From 288379f050284087578b77e04f040b57db3db3f8 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 19 Jan 2009 16:43:59 -0800 Subject: net: Remove redundant NAPI functions Following the removal of the unused struct net_device * parameter from the NAPI functions named *netif_rx_* in commit 908a7a1, they are exactly equivalent to the corresponding *napi_* functions and are therefore redundant. Signed-off-by: Ben Hutchings Acked-by: Neil Horman Signed-off-by: David S. Miller --- drivers/net/typhoon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/typhoon.c') diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 3af9a9516cc..dcff5ade6d0 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -1783,7 +1783,7 @@ typhoon_poll(struct napi_struct *napi, int budget) } if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); iowrite32(TYPHOON_INTR_NONE, tp->ioaddr + TYPHOON_REG_INTR_MASK); typhoon_post_pci_writes(tp->ioaddr); @@ -1806,10 +1806,10 @@ typhoon_interrupt(int irq, void *dev_instance) iowrite32(intr_status, ioaddr + TYPHOON_REG_INTR_STATUS); - if (netif_rx_schedule_prep(&tp->napi)) { + if (napi_schedule_prep(&tp->napi)) { iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK); typhoon_post_pci_writes(ioaddr); - __netif_rx_schedule(&tp->napi); + __napi_schedule(&tp->napi); } else { printk(KERN_ERR "%s: Error, poll already scheduled\n", dev->name); -- cgit v1.2.3 From 649aa95d75cbadb9f440c1b8d04c666461de326f Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sun, 18 Jan 2009 22:03:01 -0800 Subject: typhoon: replace users of __constant_{endian} The base versions handle constant folding just fine, use them directly. Signed-off-by: Harvey Harrison Acked-by: David Dillow Signed-off-by: David S. Miller --- drivers/net/typhoon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/typhoon.c') diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index dcff5ade6d0..a8e5651f316 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -1944,7 +1944,7 @@ typhoon_start_runtime(struct typhoon *tp) goto error_out; INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_VLAN_TYPE_WRITE); - xp_cmd.parm1 = __constant_cpu_to_le16(ETH_P_8021Q); + xp_cmd.parm1 = cpu_to_le16(ETH_P_8021Q); err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL); if(err < 0) goto error_out; -- cgit v1.2.3 From b775a750c3afacbfac884537d466d34d50b1023b Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 26 Feb 2009 23:21:23 -0800 Subject: typhoon: Use request_firmware() Based on a patch by Jaswinder Singh . Compile-tested only. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/typhoon.c | 131 ++++++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 57 deletions(-) (limited to 'drivers/net/typhoon.c') diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index a8e5651f316..cd3283f766d 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -129,16 +129,18 @@ static const int multicast_filter_limit = 32; #include #include #include +#include #include "typhoon.h" -#include "typhoon-firmware.h" static char version[] __devinitdata = "typhoon.c: version " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; +#define FIRMWARE_NAME "3com/typhoon.bin" MODULE_AUTHOR("David Dillow "); MODULE_VERSION(DRV_MODULE_VERSION); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FIRMWARE_NAME); MODULE_DESCRIPTION("3Com Typhoon Family (3C990, 3CR990, and variants)"); MODULE_PARM_DESC(rx_copybreak, "Packets smaller than this are copied and " "the buffer given back to the NIC. Default " @@ -1344,45 +1346,61 @@ typhoon_init_rings(struct typhoon *tp) tp->txHiRing.lastRead = 0; } +static const struct firmware *typhoon_fw; + +static int +typhoon_request_firmware(struct typhoon *tp) +{ + int err; + + if (typhoon_fw) + return 0; + + err = request_firmware(&typhoon_fw, FIRMWARE_NAME, &tp->pdev->dev); + if (err) { + printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n", + tp->name, FIRMWARE_NAME); + return err; + } + + if (typhoon_fw->size < sizeof(struct typhoon_file_header) || + memcmp(typhoon_fw->data, "TYPHOON", 8)) { + printk(KERN_ERR "%s: Invalid firmware image\n", + tp->name); + release_firmware(typhoon_fw); + typhoon_fw = NULL; + return -EINVAL; + } + + return 0; +} + static int typhoon_download_firmware(struct typhoon *tp) { void __iomem *ioaddr = tp->ioaddr; struct pci_dev *pdev = tp->pdev; - struct typhoon_file_header *fHdr; - struct typhoon_section_header *sHdr; - u8 *image_data; - void *dpage; - dma_addr_t dpage_dma; + const struct typhoon_file_header *fHdr; + const struct typhoon_section_header *sHdr; + const u8 *image_data; + dma_addr_t image_dma; __sum16 csum; u32 irqEnabled; u32 irqMasked; u32 numSections; u32 section_len; - u32 len; u32 load_addr; u32 hmac; int i; int err; - err = -EINVAL; - fHdr = (struct typhoon_file_header *) typhoon_firmware_image; - image_data = (u8 *) fHdr; - - if(memcmp(fHdr->tag, "TYPHOON", 8)) { - printk(KERN_ERR "%s: Invalid firmware image!\n", tp->name); - goto err_out; - } + image_data = typhoon_fw->data; + fHdr = (struct typhoon_file_header *) image_data; - /* Cannot just map the firmware image using pci_map_single() as - * the firmware is part of the kernel/module image, so we allocate - * some consistent memory to copy the sections into, as it is simpler, - * and short-lived. If we ever split out and require a userland - * firmware loader, then we can revisit this. - */ err = -ENOMEM; - dpage = pci_alloc_consistent(pdev, PAGE_SIZE, &dpage_dma); - if(!dpage) { + image_dma = pci_map_single(pdev, (u8 *) typhoon_fw->data, + typhoon_fw->size, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, image_dma)) { printk(KERN_ERR "%s: no DMA mem for firmware\n", tp->name); goto err_out; } @@ -1430,41 +1448,34 @@ typhoon_download_firmware(struct typhoon *tp) load_addr = le32_to_cpu(sHdr->startAddr); section_len = le32_to_cpu(sHdr->len); - while(section_len) { - len = min_t(u32, section_len, PAGE_SIZE); + if (typhoon_wait_interrupt(ioaddr) < 0 || + ioread32(ioaddr + TYPHOON_REG_STATUS) != + TYPHOON_STATUS_WAITING_FOR_SEGMENT) { + printk(KERN_ERR "%s: segment ready timeout\n", + tp->name); + goto err_out_irq; + } - if(typhoon_wait_interrupt(ioaddr) < 0 || - ioread32(ioaddr + TYPHOON_REG_STATUS) != - TYPHOON_STATUS_WAITING_FOR_SEGMENT) { - printk(KERN_ERR "%s: segment ready timeout\n", - tp->name); - goto err_out_irq; - } + /* Do an pseudo IPv4 checksum on the data -- first + * need to convert each u16 to cpu order before + * summing. Fortunately, due to the properties of + * the checksum, we can do this once, at the end. + */ + csum = csum_fold(csum_partial(image_data, section_len, 0)); + + iowrite32(section_len, ioaddr + TYPHOON_REG_BOOT_LENGTH); + iowrite32(le16_to_cpu((__force __le16)csum), + ioaddr + TYPHOON_REG_BOOT_CHECKSUM); + iowrite32(load_addr, + ioaddr + TYPHOON_REG_BOOT_DEST_ADDR); + iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI); + iowrite32(image_dma + (image_data - typhoon_fw->data), + ioaddr + TYPHOON_REG_BOOT_DATA_LO); + typhoon_post_pci_writes(ioaddr); + iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE, + ioaddr + TYPHOON_REG_COMMAND); - /* Do an pseudo IPv4 checksum on the data -- first - * need to convert each u16 to cpu order before - * summing. Fortunately, due to the properties of - * the checksum, we can do this once, at the end. - */ - csum = csum_fold(csum_partial_copy_nocheck(image_data, - dpage, len, - 0)); - - iowrite32(len, ioaddr + TYPHOON_REG_BOOT_LENGTH); - iowrite32(le16_to_cpu((__force __le16)csum), - ioaddr + TYPHOON_REG_BOOT_CHECKSUM); - iowrite32(load_addr, - ioaddr + TYPHOON_REG_BOOT_DEST_ADDR); - iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI); - iowrite32(dpage_dma, ioaddr + TYPHOON_REG_BOOT_DATA_LO); - typhoon_post_pci_writes(ioaddr); - iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE, - ioaddr + TYPHOON_REG_COMMAND); - - image_data += len; - load_addr += len; - section_len -= len; - } + image_data += section_len; } if(typhoon_wait_interrupt(ioaddr) < 0 || @@ -1488,7 +1499,7 @@ err_out_irq: iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK); iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE); - pci_free_consistent(pdev, PAGE_SIZE, dpage, dpage_dma); + pci_unmap_single(pdev, image_dma, typhoon_fw->size, PCI_DMA_TODEVICE); err_out: return err; @@ -2086,6 +2097,10 @@ typhoon_open(struct net_device *dev) struct typhoon *tp = netdev_priv(dev); int err; + err = typhoon_request_firmware(tp); + if (err) + goto out; + err = typhoon_wakeup(tp, WaitSleep); if(err < 0) { printk(KERN_ERR "%s: unable to wakeup device\n", dev->name); @@ -2624,6 +2639,8 @@ typhoon_init(void) static void __exit typhoon_cleanup(void) { + if (typhoon_fw) + release_firmware(typhoon_fw); pci_unregister_driver(&typhoon_driver); } -- cgit v1.2.3 From d517c4a1da590a7fa50325a5e5cd18f07e8fb5a7 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 1 Mar 2009 20:24:32 -0800 Subject: typhoon: Need non-vmalloc memory to DMA firmware to the card. request_firmware() gives vmalloc'd memory, which is not suitable for pci_map_single() and friends. Use a kmalloc()'d copy of the firmware for this DMA operation. Signed-off-by: David S. Miller --- drivers/net/typhoon.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'drivers/net/typhoon.c') diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index cd3283f766d..ec2541c8c22 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -1347,6 +1347,7 @@ typhoon_init_rings(struct typhoon *tp) } static const struct firmware *typhoon_fw; +static u8 *typhoon_fw_image; static int typhoon_request_firmware(struct typhoon *tp) @@ -1367,12 +1368,22 @@ typhoon_request_firmware(struct typhoon *tp) memcmp(typhoon_fw->data, "TYPHOON", 8)) { printk(KERN_ERR "%s: Invalid firmware image\n", tp->name); - release_firmware(typhoon_fw); - typhoon_fw = NULL; - return -EINVAL; + err = -EINVAL; + goto out_err; + } + + typhoon_fw_image = kmalloc(typhoon_fw->size, GFP_KERNEL); + if (!typhoon_fw_image) { + err = -ENOMEM; + goto out_err; } return 0; + +out_err: + release_firmware(typhoon_fw); + typhoon_fw = NULL; + return err; } static int @@ -1394,11 +1405,11 @@ typhoon_download_firmware(struct typhoon *tp) int i; int err; - image_data = typhoon_fw->data; + image_data = typhoon_fw_image; fHdr = (struct typhoon_file_header *) image_data; err = -ENOMEM; - image_dma = pci_map_single(pdev, (u8 *) typhoon_fw->data, + image_dma = pci_map_single(pdev, (u8 *) image_data, typhoon_fw->size, PCI_DMA_TODEVICE); if (pci_dma_mapping_error(pdev, image_dma)) { printk(KERN_ERR "%s: no DMA mem for firmware\n", tp->name); @@ -1469,7 +1480,7 @@ typhoon_download_firmware(struct typhoon *tp) iowrite32(load_addr, ioaddr + TYPHOON_REG_BOOT_DEST_ADDR); iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI); - iowrite32(image_dma + (image_data - typhoon_fw->data), + iowrite32(image_dma + (image_data - typhoon_fw_image), ioaddr + TYPHOON_REG_BOOT_DATA_LO); typhoon_post_pci_writes(ioaddr); iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE, @@ -2639,8 +2650,10 @@ typhoon_init(void) static void __exit typhoon_cleanup(void) { - if (typhoon_fw) + if (typhoon_fw) { + kfree(typhoon_fw_image); release_firmware(typhoon_fw); + } pci_unregister_driver(&typhoon_driver); } -- cgit v1.2.3 From 093fb6968deeed46bf93266eee44baeb213bf253 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Mar 2009 01:53:03 -0800 Subject: typhoon: Add missing firmware copy. Noticed by David Dillow. Signed-off-by: David S. Miller --- drivers/net/typhoon.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/typhoon.c') diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index ec2541c8c22..9bba7871636 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -1377,6 +1377,7 @@ typhoon_request_firmware(struct typhoon *tp) err = -ENOMEM; goto out_err; } + memcpy(typhoon_fw_image, typhoon_fw->data, typhoon_fw->size); return 0; -- cgit v1.2.3 From a8c9a53c22441efcd57ad7955231b2804696b133 Mon Sep 17 00:00:00 2001 From: David Dillow Date: Mon, 2 Mar 2009 22:15:09 -0800 Subject: typhoon: repair firmware loading The conversion to avoid using pci_alloc_consistent() broke the firmware load process, as well as added an order-4 kmalloc and doubled the memory usage of the firmware image. Go back to loading a page at a time. Also, since the user can now give us utter garbage for firmware, do a cursory validation so we don't try to load just anything. Signed-off-by: David Dillow Signed-off-by: David S. Miller --- drivers/net/typhoon.c | 137 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 54 deletions(-) (limited to 'drivers/net/typhoon.c') diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 9bba7871636..9dd4f76a2ff 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -100,10 +100,11 @@ static const int multicast_filter_limit = 32; #define PKT_BUF_SZ 1536 #define DRV_MODULE_NAME "typhoon" -#define DRV_MODULE_VERSION "1.5.8" -#define DRV_MODULE_RELDATE "06/11/09" +#define DRV_MODULE_VERSION "1.5.9" +#define DRV_MODULE_RELDATE "Mar 2, 2009" #define PFX DRV_MODULE_NAME ": " #define ERR_PFX KERN_ERR PFX +#define FIRMWARE_NAME "3com/typhoon.bin" #include #include @@ -136,7 +137,6 @@ static const int multicast_filter_limit = 32; static char version[] __devinitdata = "typhoon.c: version " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; -#define FIRMWARE_NAME "3com/typhoon.bin" MODULE_AUTHOR("David Dillow "); MODULE_VERSION(DRV_MODULE_VERSION); MODULE_LICENSE("GPL"); @@ -1347,11 +1347,16 @@ typhoon_init_rings(struct typhoon *tp) } static const struct firmware *typhoon_fw; -static u8 *typhoon_fw_image; static int typhoon_request_firmware(struct typhoon *tp) { + const struct typhoon_file_header *fHdr; + const struct typhoon_section_header *sHdr; + const u8 *image_data; + u32 numSections; + u32 section_len; + u32 remaining; int err; if (typhoon_fw) @@ -1360,31 +1365,45 @@ typhoon_request_firmware(struct typhoon *tp) err = request_firmware(&typhoon_fw, FIRMWARE_NAME, &tp->pdev->dev); if (err) { printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n", - tp->name, FIRMWARE_NAME); + tp->name, FIRMWARE_NAME); return err; } - if (typhoon_fw->size < sizeof(struct typhoon_file_header) || - memcmp(typhoon_fw->data, "TYPHOON", 8)) { - printk(KERN_ERR "%s: Invalid firmware image\n", - tp->name); - err = -EINVAL; - goto out_err; - } + image_data = (u8 *) typhoon_fw->data; + remaining = typhoon_fw->size; + if (remaining < sizeof(struct typhoon_file_header)) + goto invalid_fw; - typhoon_fw_image = kmalloc(typhoon_fw->size, GFP_KERNEL); - if (!typhoon_fw_image) { - err = -ENOMEM; - goto out_err; + fHdr = (struct typhoon_file_header *) image_data; + if (memcmp(fHdr->tag, "TYPHOON", 8)) + goto invalid_fw; + + numSections = le32_to_cpu(fHdr->numSections); + image_data += sizeof(struct typhoon_file_header); + remaining -= sizeof(struct typhoon_file_header); + + while (numSections--) { + if (remaining < sizeof(struct typhoon_section_header)) + goto invalid_fw; + + sHdr = (struct typhoon_section_header *) image_data; + image_data += sizeof(struct typhoon_section_header); + section_len = le32_to_cpu(sHdr->len); + + if (remaining < section_len) + goto invalid_fw; + + image_data += section_len; + remaining -= section_len; } - memcpy(typhoon_fw_image, typhoon_fw->data, typhoon_fw->size); return 0; -out_err: +invalid_fw: + printk(KERN_ERR "%s: Invalid firmware image\n", tp->name); release_firmware(typhoon_fw); typhoon_fw = NULL; - return err; + return -EINVAL; } static int @@ -1395,24 +1414,29 @@ typhoon_download_firmware(struct typhoon *tp) const struct typhoon_file_header *fHdr; const struct typhoon_section_header *sHdr; const u8 *image_data; - dma_addr_t image_dma; + void *dpage; + dma_addr_t dpage_dma; __sum16 csum; u32 irqEnabled; u32 irqMasked; u32 numSections; u32 section_len; + u32 len; u32 load_addr; u32 hmac; int i; int err; - image_data = typhoon_fw_image; + image_data = (u8 *) typhoon_fw->data; fHdr = (struct typhoon_file_header *) image_data; + /* Cannot just map the firmware image using pci_map_single() as + * the firmware is vmalloc()'d and may not be physically contiguous, + * so we allocate some consistent memory to copy the sections into. + */ err = -ENOMEM; - image_dma = pci_map_single(pdev, (u8 *) image_data, - typhoon_fw->size, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(pdev, image_dma)) { + dpage = pci_alloc_consistent(pdev, PAGE_SIZE, &dpage_dma); + if(!dpage) { printk(KERN_ERR "%s: no DMA mem for firmware\n", tp->name); goto err_out; } @@ -1460,34 +1484,41 @@ typhoon_download_firmware(struct typhoon *tp) load_addr = le32_to_cpu(sHdr->startAddr); section_len = le32_to_cpu(sHdr->len); - if (typhoon_wait_interrupt(ioaddr) < 0 || - ioread32(ioaddr + TYPHOON_REG_STATUS) != - TYPHOON_STATUS_WAITING_FOR_SEGMENT) { - printk(KERN_ERR "%s: segment ready timeout\n", - tp->name); - goto err_out_irq; - } + while(section_len) { + len = min_t(u32, section_len, PAGE_SIZE); - /* Do an pseudo IPv4 checksum on the data -- first - * need to convert each u16 to cpu order before - * summing. Fortunately, due to the properties of - * the checksum, we can do this once, at the end. - */ - csum = csum_fold(csum_partial(image_data, section_len, 0)); - - iowrite32(section_len, ioaddr + TYPHOON_REG_BOOT_LENGTH); - iowrite32(le16_to_cpu((__force __le16)csum), - ioaddr + TYPHOON_REG_BOOT_CHECKSUM); - iowrite32(load_addr, - ioaddr + TYPHOON_REG_BOOT_DEST_ADDR); - iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI); - iowrite32(image_dma + (image_data - typhoon_fw_image), - ioaddr + TYPHOON_REG_BOOT_DATA_LO); - typhoon_post_pci_writes(ioaddr); - iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE, - ioaddr + TYPHOON_REG_COMMAND); + if(typhoon_wait_interrupt(ioaddr) < 0 || + ioread32(ioaddr + TYPHOON_REG_STATUS) != + TYPHOON_STATUS_WAITING_FOR_SEGMENT) { + printk(KERN_ERR "%s: segment ready timeout\n", + tp->name); + goto err_out_irq; + } - image_data += section_len; + /* Do an pseudo IPv4 checksum on the data -- first + * need to convert each u16 to cpu order before + * summing. Fortunately, due to the properties of + * the checksum, we can do this once, at the end. + */ + csum = csum_fold(csum_partial_copy_nocheck(image_data, + dpage, len, + 0)); + + iowrite32(len, ioaddr + TYPHOON_REG_BOOT_LENGTH); + iowrite32(le16_to_cpu((__force __le16)csum), + ioaddr + TYPHOON_REG_BOOT_CHECKSUM); + iowrite32(load_addr, + ioaddr + TYPHOON_REG_BOOT_DEST_ADDR); + iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI); + iowrite32(dpage_dma, ioaddr + TYPHOON_REG_BOOT_DATA_LO); + typhoon_post_pci_writes(ioaddr); + iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE, + ioaddr + TYPHOON_REG_COMMAND); + + image_data += len; + load_addr += len; + section_len -= len; + } } if(typhoon_wait_interrupt(ioaddr) < 0 || @@ -1511,7 +1542,7 @@ err_out_irq: iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK); iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE); - pci_unmap_single(pdev, image_dma, typhoon_fw->size, PCI_DMA_TODEVICE); + pci_free_consistent(pdev, PAGE_SIZE, dpage, dpage_dma); err_out: return err; @@ -2651,10 +2682,8 @@ typhoon_init(void) static void __exit typhoon_cleanup(void) { - if (typhoon_fw) { - kfree(typhoon_fw_image); + if (typhoon_fw) release_firmware(typhoon_fw); - } pci_unregister_driver(&typhoon_driver); } -- cgit v1.2.3