diff options
Diffstat (limited to 'drivers/net/irda')
-rw-r--r-- | drivers/net/irda/Kconfig | 15 | ||||
-rw-r--r-- | drivers/net/irda/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/irda/ali-ircc.c | 106 | ||||
-rw-r--r-- | drivers/net/irda/irda-usb.c | 327 | ||||
-rw-r--r-- | drivers/net/irda/irda-usb.h | 10 | ||||
-rw-r--r-- | drivers/net/irda/mcs7780.c | 1009 | ||||
-rw-r--r-- | drivers/net/irda/mcs7780.h | 167 | ||||
-rw-r--r-- | drivers/net/irda/stir4200.c | 38 | ||||
-rw-r--r-- | drivers/net/irda/vlsi_ir.c | 2 |
9 files changed, 1403 insertions, 272 deletions
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index cff8598aa800..d2ce4896abff 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -417,5 +417,20 @@ config PXA_FICP available capabilities may vary from one PXA2xx target to another. +config MCS_FIR + tristate "MosChip MCS7780 IrDA-USB dongle" + depends on IRDA && USB && EXPERIMENTAL + help + Say Y or M here if you want to build support for the MosChip + MCS7780 IrDA-USB bridge device driver. + + USB bridge based on the MosChip MCS7780 don't conform to the + IrDA-USB device class specification, and therefore need their + own specific driver. Those dongles support SIR and FIR (4Mbps) + speeds. + + To compile it as a module, choose M here: the module will be called + mcs7780. + endmenu diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile index c1ce2398efea..5be09f1b9ee2 100644 --- a/drivers/net/irda/Makefile +++ b/drivers/net/irda/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ALI_FIR) += ali-ircc.o obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o obj-$(CONFIG_VIA_FIR) += via-ircc.o obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o +obj-$(CONFIG_MCS_FIR) += mcs7780.o # Old dongle drivers for old SIR drivers obj-$(CONFIG_ESI_DONGLE_OLD) += esi.o obj-$(CONFIG_TEKRAM_DONGLE_OLD) += tekram.o diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index 2e7882eb7d6f..bf1fca5a3fa0 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -34,14 +34,12 @@ #include <linux/rtnetlink.h> #include <linux/serial_reg.h> #include <linux/dma-mapping.h> +#include <linux/platform_device.h> #include <asm/io.h> #include <asm/dma.h> #include <asm/byteorder.h> -#include <linux/pm.h> -#include <linux/pm_legacy.h> - #include <net/irda/wrapper.h> #include <net/irda/irda.h> #include <net/irda/irda_device.h> @@ -51,7 +49,19 @@ #define CHIP_IO_EXTENT 8 #define BROKEN_DONGLE_ID -static char *driver_name = "ali-ircc"; +#define ALI_IRCC_DRIVER_NAME "ali-ircc" + +/* Power Management */ +static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state); +static int ali_ircc_resume(struct platform_device *dev); + +static struct platform_driver ali_ircc_driver = { + .suspend = ali_ircc_suspend, + .resume = ali_ircc_resume, + .driver = { + .name = ALI_IRCC_DRIVER_NAME, + }, +}; /* Module parameters */ static int qos_mtt_bits = 0x07; /* 1 ms or more */ @@ -97,10 +107,7 @@ static int ali_ircc_is_receiving(struct ali_ircc_cb *self); static int ali_ircc_net_open(struct net_device *dev); static int ali_ircc_net_close(struct net_device *dev); static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int ali_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data); static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud); -static void ali_ircc_suspend(struct ali_ircc_cb *self); -static void ali_ircc_wakeup(struct ali_ircc_cb *self); static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev); /* SIR function */ @@ -145,6 +152,14 @@ static int __init ali_ircc_init(void) int i = 0; IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + ret = platform_driver_register(&ali_ircc_driver); + if (ret) { + IRDA_ERROR("%s, Can't register driver!\n", + ALI_IRCC_DRIVER_NAME); + return ret; + } + /* Probe for all the ALi chipsets we know about */ for (chip= chips; chip->name; chip++, i++) @@ -214,6 +229,10 @@ static int __init ali_ircc_init(void) } IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); + + if (ret) + platform_driver_unregister(&ali_ircc_driver); + return ret; } @@ -228,14 +247,14 @@ static void __exit ali_ircc_cleanup(void) int i; IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); - - pm_unregister_all(ali_ircc_pmproc); for (i=0; i < 4; i++) { if (dev_self[i]) ali_ircc_close(dev_self[i]); } + platform_driver_unregister(&ali_ircc_driver); + IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); } @@ -249,7 +268,6 @@ static int ali_ircc_open(int i, chipio_t *info) { struct net_device *dev; struct ali_ircc_cb *self; - struct pm_dev *pmdev; int dongle_id; int err; @@ -284,7 +302,8 @@ static int ali_ircc_open(int i, chipio_t *info) self->io.fifo_size = 16; /* SIR: 16, FIR: 32 Benjamin 2000/11/1 */ /* Reserve the ioports that we need */ - if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) { + if (!request_region(self->io.fir_base, self->io.fir_ext, + ALI_IRCC_DRIVER_NAME)) { IRDA_WARNING("%s(), can't get iobase of 0x%03x\n", __FUNCTION__, self->io.fir_base); err = -ENODEV; @@ -354,13 +373,10 @@ static int ali_ircc_open(int i, chipio_t *info) /* Check dongle id */ dongle_id = ali_ircc_read_dongle_id(i, info); - IRDA_MESSAGE("%s(), %s, Found dongle: %s\n", __FUNCTION__, driver_name, dongle_types[dongle_id]); + IRDA_MESSAGE("%s(), %s, Found dongle: %s\n", __FUNCTION__, + ALI_IRCC_DRIVER_NAME, dongle_types[dongle_id]); self->io.dongle_id = dongle_id; - - pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ali_ircc_pmproc); - if (pmdev) - pmdev->data = self; IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); @@ -548,12 +564,11 @@ static int ali_ircc_setup(chipio_t *info) /* Should be 0x00 in the M1535/M1535D */ if(version != 0x00) { - IRDA_ERROR("%s, Wrong chip version %02x\n", driver_name, version); + IRDA_ERROR("%s, Wrong chip version %02x\n", + ALI_IRCC_DRIVER_NAME, version); return -1; } - // IRDA_MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, info->cfg_base); - /* Set FIR FIFO Threshold Register */ switch_bank(iobase, BANK1); outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); @@ -583,7 +598,8 @@ static int ali_ircc_setup(chipio_t *info) /* Switch to SIR space */ FIR2SIR(iobase); - IRDA_MESSAGE("%s, driver loaded (Benjamin Kong)\n", driver_name); + IRDA_MESSAGE("%s, driver loaded (Benjamin Kong)\n", + ALI_IRCC_DRIVER_NAME); /* Enable receive interrupts */ // outb(UART_IER_RDI, iobase+UART_IER); //benjamin 2000/11/23 01:25PM @@ -647,7 +663,8 @@ static irqreturn_t ali_ircc_interrupt(int irq, void *dev_id, IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); if (!dev) { - IRDA_WARNING("%s: irq %d for unknown device.\n", driver_name, irq); + IRDA_WARNING("%s: irq %d for unknown device.\n", + ALI_IRCC_DRIVER_NAME, irq); return IRQ_NONE; } @@ -1328,7 +1345,8 @@ static int ali_ircc_net_open(struct net_device *dev) /* Request IRQ and install Interrupt Handler */ if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev)) { - IRDA_WARNING("%s, unable to allocate irq=%d\n", driver_name, + IRDA_WARNING("%s, unable to allocate irq=%d\n", + ALI_IRCC_DRIVER_NAME, self->io.irq); return -EAGAIN; } @@ -1338,7 +1356,8 @@ static int ali_ircc_net_open(struct net_device *dev) * failure. */ if (request_dma(self->io.dma, dev->name)) { - IRDA_WARNING("%s, unable to allocate dma=%d\n", driver_name, + IRDA_WARNING("%s, unable to allocate dma=%d\n", + ALI_IRCC_DRIVER_NAME, self->io.dma); free_irq(self->io.irq, self); return -EAGAIN; @@ -2108,61 +2127,38 @@ static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev) return &self->stats; } -static void ali_ircc_suspend(struct ali_ircc_cb *self) +static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state) { - IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + struct ali_ircc_cb *self = platform_get_drvdata(dev); - IRDA_MESSAGE("%s, Suspending\n", driver_name); + IRDA_MESSAGE("%s, Suspending\n", ALI_IRCC_DRIVER_NAME); if (self->io.suspended) - return; + return 0; ali_ircc_net_close(self->netdev); self->io.suspended = 1; - IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + return 0; } -static void ali_ircc_wakeup(struct ali_ircc_cb *self) +static int ali_ircc_resume(struct platform_device *dev) { - IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + struct ali_ircc_cb *self = platform_get_drvdata(dev); if (!self->io.suspended) - return; + return 0; ali_ircc_net_open(self->netdev); - IRDA_MESSAGE("%s, Waking up\n", driver_name); + IRDA_MESSAGE("%s, Waking up\n", ALI_IRCC_DRIVER_NAME); self->io.suspended = 0; - - IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); -} -static int ali_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct ali_ircc_cb *self = (struct ali_ircc_cb*) dev->data; - - IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); - - if (self) { - switch (rqst) { - case PM_SUSPEND: - ali_ircc_suspend(self); - break; - case PM_RESUME: - ali_ircc_wakeup(self); - break; - } - } - - IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); - return 0; } - /* ALi Chip Function */ static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable) diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index cd87593e4e8a..844fa74ac9ec 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -83,9 +83,9 @@ static struct usb_device_id dongles[] = { /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ { USB_DEVICE(0x8e9, 0x100), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, /* SigmaTel STIR4210/4220/4116 USB IrDA (VFIR) Bridge */ - { USB_DEVICE(0x66f, 0x4210), .driver_info = IUC_STIR_4210 | IUC_SPEED_BUG }, - { USB_DEVICE(0x66f, 0x4220), .driver_info = IUC_STIR_4210 | IUC_SPEED_BUG }, - { USB_DEVICE(0x66f, 0x4116), .driver_info = IUC_STIR_4210 | IUC_SPEED_BUG }, + { USB_DEVICE(0x66f, 0x4210), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, + { USB_DEVICE(0x66f, 0x4220), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, + { USB_DEVICE(0x66f, 0x4116), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS, .bInterfaceClass = USB_CLASS_APP_SPEC, @@ -154,7 +154,7 @@ static void irda_usb_build_header(struct irda_usb_cb *self, * and if either speed or xbofs (or both) needs * to be changed. */ - if (self->capability & IUC_STIR_4210 && + if (self->capability & IUC_STIR421X && ((self->new_speed != -1) || (self->new_xbofs != -1))) { /* With STIR421x, speed and xBOFs must be set at the same @@ -318,7 +318,7 @@ static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self) /* Set the new speed and xbofs in this fake frame */ irda_usb_build_header(self, frame, 1); - if ( self->capability & IUC_STIR_4210 ) { + if (self->capability & IUC_STIR421X) { if (frame[0] == 0) return ; // do nothing if no change frame[1] = 0; // other parameters don't change here frame[2] = 0; @@ -455,7 +455,7 @@ static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev) /* Change setting for next frame */ - if ( self->capability & IUC_STIR_4210 ) { + if (self->capability & IUC_STIR421X) { __u8 turnaround_time; __u8* frame; turnaround_time = get_turnaround_time( skb ); @@ -897,10 +897,13 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs) docopy = (urb->actual_length < IRDA_RX_COPY_THRESHOLD); /* Allocate a new skb */ - if ( self->capability & IUC_STIR_4210 ) - newskb = dev_alloc_skb(docopy ? urb->actual_length : IRDA_SKB_MAX_MTU + USB_IRDA_SIGMATEL_HEADER); + if (self->capability & IUC_STIR421X) + newskb = dev_alloc_skb(docopy ? urb->actual_length : + IRDA_SKB_MAX_MTU + + USB_IRDA_STIR421X_HEADER); else - newskb = dev_alloc_skb(docopy ? urb->actual_length : IRDA_SKB_MAX_MTU); + newskb = dev_alloc_skb(docopy ? urb->actual_length : + IRDA_SKB_MAX_MTU); if (!newskb) { self->stats.rx_dropped++; @@ -1022,188 +1025,140 @@ static int irda_usb_is_receiving(struct irda_usb_cb *self) return 0; /* For now */ } - -#define STIR421X_PATCH_PRODUCT_VERSION_STR "Product Version: " -#define STIR421X_PATCH_COMPONENT_VERSION_STR "Component Version: " -#define STIR421X_PATCH_DATA_TAG_STR "STMP" -#define STIR421X_PATCH_FILE_VERSION_MAX_OFFSET 512 /* version info is before here */ -#define STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET 512 /* patch image starts before here */ -#define STIR421X_PATCH_FILE_END_OF_HEADER_TAG 0x1A /* marks end of patch file header (PC DOS text file EOF character) */ +#define STIR421X_PATCH_PRODUCT_VER "Product Version: " +#define STIR421X_PATCH_STMP_TAG "STMP" +#define STIR421X_PATCH_CODE_OFFSET 512 /* patch image starts before here */ +/* marks end of patch file header (PC DOS text file EOF character) */ +#define STIR421X_PATCH_END_OF_HDR_TAG 0x1A +#define STIR421X_PATCH_BLOCK_SIZE 1023 /* - * Known firmware patches for STIR421x dongles + * Function stir421x_fwupload (struct irda_usb_cb *self, + * unsigned char *patch, + * const unsigned int patch_len) + * + * Upload firmware code to SigmaTel 421X IRDA-USB dongle */ -static char * stir421x_patches[] = { - "42101001.sb", - "42101002.sb", -}; - -static int stir421x_get_patch_version(unsigned char * patch, const unsigned long patch_len) +static int stir421x_fw_upload(struct irda_usb_cb *self, + unsigned char *patch, + const unsigned int patch_len) { - unsigned int version_offset; - unsigned long version_major, version_minor, version_build; - unsigned char * version_start; - int version_found = 0; - - for (version_offset = 0; - version_offset < STIR421X_PATCH_FILE_END_OF_HEADER_TAG; - version_offset++) { - if (!memcmp(patch + version_offset, - STIR421X_PATCH_PRODUCT_VERSION_STR, - sizeof(STIR421X_PATCH_PRODUCT_VERSION_STR) - 1)) { - version_found = 1; - version_start = patch + - version_offset + - sizeof(STIR421X_PATCH_PRODUCT_VERSION_STR) - 1; - break; - } + int ret = -ENOMEM; + int actual_len = 0; + unsigned int i; + unsigned int block_size = 0; + unsigned char *patch_block; + + patch_block = kzalloc(STIR421X_PATCH_BLOCK_SIZE, GFP_KERNEL); + if (patch_block == NULL) + return -ENOMEM; + + /* break up patch into 1023-byte sections */ + for (i = 0; i < patch_len; i += block_size) { + block_size = patch_len - i; + + if (block_size > STIR421X_PATCH_BLOCK_SIZE) + block_size = STIR421X_PATCH_BLOCK_SIZE; + + /* upload the patch section */ + memcpy(patch_block, patch + i, block_size); + + ret = usb_bulk_msg(self->usbdev, + usb_sndbulkpipe(self->usbdev, + self->bulk_out_ep), + patch_block, block_size, + &actual_len, msecs_to_jiffies(500)); + IRDA_DEBUG(3,"%s(): Bulk send %u bytes, ret=%d\n", + __FUNCTION__, actual_len, ret); + + if (ret < 0) + break; } - /* We couldn't find a product version on this patch */ - if (!version_found) - return -EINVAL; - - /* Let's check if the product version is dotted */ - if (version_start[3] != '.' || - version_start[7] != '.') - return -EINVAL; - - version_major = simple_strtoul(version_start, NULL, 10); - version_minor = simple_strtoul(version_start + 4, NULL, 10); - version_build = simple_strtoul(version_start + 8, NULL, 10); - - IRDA_DEBUG(2, "%s(), Major: %ld Minor: %ld Build: %ld\n", - __FUNCTION__, - version_major, version_minor, version_build); - - return (((version_major) << 12) + - ((version_minor) << 8) + - ((version_build / 10) << 4) + - (version_build % 10)); - -} - - -static int stir421x_upload_patch (struct irda_usb_cb *self, - unsigned char * patch, - const unsigned int patch_len) -{ - int retval = 0; - int actual_len; - unsigned int i = 0, download_amount = 0; - unsigned char * patch_chunk; - - IRDA_DEBUG (2, "%s(), Uploading STIR421x Patch\n", __FUNCTION__); - - patch_chunk = kzalloc(STIR421X_MAX_PATCH_DOWNLOAD_SIZE, GFP_KERNEL); - if (patch_chunk == NULL) - return -ENOMEM; - - /* break up patch into 1023-byte sections */ - for (i = 0; retval >= 0 && i < patch_len; i += download_amount) { - download_amount = patch_len - i; - if (download_amount > STIR421X_MAX_PATCH_DOWNLOAD_SIZE) - download_amount = STIR421X_MAX_PATCH_DOWNLOAD_SIZE; - - /* download the patch section */ - memcpy(patch_chunk, patch + i, download_amount); - - retval = usb_bulk_msg (self->usbdev, - usb_sndbulkpipe (self->usbdev, - self->bulk_out_ep), - patch_chunk, download_amount, - &actual_len, msecs_to_jiffies (500)); - IRDA_DEBUG (2, "%s(), Sent %u bytes\n", __FUNCTION__, - actual_len); - if (retval == 0) - mdelay(10); - } - - kfree(patch_chunk); - - if (i != patch_len) { - IRDA_ERROR ("%s(), Pushed %d bytes (!= patch_len (%d))\n", - __FUNCTION__, i, patch_len); - retval = -EIO; - } - - if (retval < 0) - /* todo - mark device as not ready */ - IRDA_ERROR ("%s(), STIR421x patch upload failed (%d)\n", - __FUNCTION__, retval); - - return retval; -} + kfree(patch_block); + return ret; + } +/* + * Function stir421x_patch_device(struct irda_usb_cb *self) + * + * Get a firmware code from userspase using hotplug request_firmware() call + */ static int stir421x_patch_device(struct irda_usb_cb *self) { - unsigned int i, patch_found = 0, data_found = 0, data_offset; - int patch_version, ret = 0; - const struct firmware *fw_entry; - - for (i = 0; i < ARRAY_SIZE(stir421x_patches); i++) { - if(request_firmware(&fw_entry, stir421x_patches[i], &self->usbdev->dev) != 0) { - IRDA_ERROR( "%s(), Patch %s is not available\n", __FUNCTION__, stir421x_patches[i]); - continue; - } - - /* We found a patch from userspace */ - patch_version = stir421x_get_patch_version (fw_entry->data, fw_entry->size); - - if (patch_version < 0) { - /* Couldn't fetch a version, let's move on to the next file */ - IRDA_ERROR("%s(), version parsing failed\n", __FUNCTION__); - ret = patch_version; - release_firmware(fw_entry); - continue; - } - - if (patch_version != self->usbdev->descriptor.bcdDevice) { - /* Patch version and device don't match */ - IRDA_ERROR ("%s(), wrong patch version (%d <-> %d)\n", - __FUNCTION__, - patch_version, self->usbdev->descriptor.bcdDevice); - ret = -EINVAL; - release_firmware(fw_entry); - continue; - } - - /* If we're here, we've found a correct patch */ - patch_found = 1; - break; - - } - - /* We couldn't find a valid firmware, let's leave */ - if (!patch_found) - return ret; - - /* The actual image starts after the "STMP" keyword */ - for (data_offset = 0; data_offset < STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET; data_offset++) { - if (!memcmp(fw_entry->data + data_offset, - STIR421X_PATCH_DATA_TAG_STR, - sizeof(STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET))) { - IRDA_DEBUG(2, "%s(), found patch data for STIR421x at offset %d\n", - __FUNCTION__, data_offset); - data_found = 1; - break; - } - } - - /* We couldn't find "STMP" from the header */ - if (!data_found) - return -EINVAL; - - /* Let's upload the patch to the target */ - ret = stir421x_upload_patch(self, - &fw_entry->data[data_offset + sizeof(STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET)], - fw_entry->size - (data_offset + sizeof(STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET))); - - release_firmware(fw_entry); - - return ret; - + unsigned int i; + int ret; + char stir421x_fw_name[11]; + const struct firmware *fw; + unsigned char *fw_version_ptr; /* pointer to version string */ + unsigned long fw_version = 0; + + /* + * Known firmware patch file names for STIR421x dongles + * are "42101001.sb" or "42101002.sb" + */ + sprintf(stir421x_fw_name, "4210%4X.sb", + self->usbdev->descriptor.bcdDevice); + ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev); + if (ret < 0) + return ret; + + /* We get a patch from userspace */ + IRDA_MESSAGE("%s(): Received firmware %s (%u bytes)\n", + __FUNCTION__, stir421x_fw_name, fw->size); + + ret = -EINVAL; + + /* Get the bcd product version */ + if (!memcmp(fw->data, STIR421X_PATCH_PRODUCT_VER, + sizeof(STIR421X_PATCH_PRODUCT_VER) - 1)) { + fw_version_ptr = fw->data + + sizeof(STIR421X_PATCH_PRODUCT_VER) - 1; + + /* Let's check if the product version is dotted */ + if (fw_version_ptr[3] == '.' && + fw_version_ptr[7] == '.') { + unsigned long major, minor, build; + major = simple_strtoul(fw_version_ptr, NULL, 10); + minor = simple_strtoul(fw_version_ptr + 4, NULL, 10); + build = simple_strtoul(fw_version_ptr + 8, NULL, 10); + + fw_version = (major << 12) + + (minor << 8) + + ((build / 10) << 4) + + (build % 10); + + IRDA_DEBUG(3, "%s(): Firmware Product version %ld\n", + __FUNCTION__, fw_version); + } + } + + if (self->usbdev->descriptor.bcdDevice == fw_version) { + /* + * If we're here, we've found a correct patch + * The actual image starts after the "STMP" keyword + * so forward to the firmware header tag + */ + for (i = 0; (fw->data[i] != STIR421X_PATCH_END_OF_HDR_TAG) + && (i < fw->size); i++) ; + /* here we check for the out of buffer case */ + if ((STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i]) + && (i < STIR421X_PATCH_CODE_OFFSET)) { + if (!memcmp(fw->data + i + 1, STIR421X_PATCH_STMP_TAG, + sizeof(STIR421X_PATCH_STMP_TAG) - 1)) { + + /* We can upload the patch to the target */ + i += sizeof(STIR421X_PATCH_STMP_TAG); + ret = stir421x_fw_upload(self, &fw->data[i], + fw->size - i); + } + } + } + + release_firmware(fw); + + return ret; } @@ -1702,12 +1657,12 @@ static int irda_usb_probe(struct usb_interface *intf, init_timer(&self->rx_defer_timer); self->capability = id->driver_info; - self->needspatch = ((self->capability & IUC_STIR_4210) != 0) ; + self->needspatch = ((self->capability & IUC_STIR421X) != 0); /* Create all of the needed urbs */ - if (self->capability & IUC_STIR_4210) { + if (self->capability & IUC_STIR421X) { self->max_rx_urb = IU_SIGMATEL_MAX_RX_URBS; - self->header_length = USB_IRDA_SIGMATEL_HEADER; + self->header_length = USB_IRDA_STIR421X_HEADER; } else { self->max_rx_urb = IU_MAX_RX_URBS; self->header_length = USB_IRDA_HEADER; @@ -1813,8 +1768,8 @@ static int irda_usb_probe(struct usb_interface *intf, /* Now we fetch and upload the firmware patch */ ret = stir421x_patch_device(self); self->needspatch = (ret < 0); - if (ret < 0) { - printk("patch_device failed\n"); + if (self->needspatch) { + IRDA_ERROR("STIR421X: Couldn't upload patch\n"); goto err_out_5; } diff --git a/drivers/net/irda/irda-usb.h b/drivers/net/irda/irda-usb.h index d833db52cebf..6b2271f18e77 100644 --- a/drivers/net/irda/irda-usb.h +++ b/drivers/net/irda/irda-usb.h @@ -34,9 +34,6 @@ #include <net/irda/irda.h> #include <net/irda/irda_device.h> /* struct irlap_cb */ -#define PATCH_FILE_SIZE_MAX 65536 -#define PATCH_FILE_SIZE_MIN 80 - #define RX_COPY_THRESHOLD 200 #define IRDA_USB_MAX_MTU 2051 #define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */ @@ -107,14 +104,15 @@ #define IUC_SMALL_PKT 0x10 /* Device doesn't behave with big Rx packets */ #define IUC_MAX_WINDOW 0x20 /* Device underestimate the Rx window */ #define IUC_MAX_XBOFS 0x40 /* Device need more xbofs than advertised */ -#define IUC_STIR_4210 0x80 /* SigmaTel 4210/4220/4116 VFIR */ +#define IUC_STIR421X 0x80 /* SigmaTel 4210/4220/4116 VFIR */ /* USB class definitions */ #define USB_IRDA_HEADER 0x01 #define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */ #define USB_DT_IRDA 0x21 -#define USB_IRDA_SIGMATEL_HEADER 0x03 -#define IU_SIGMATEL_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + USB_IRDA_SIGMATEL_HEADER) +#define USB_IRDA_STIR421X_HEADER 0x03 +#define IU_SIGMATEL_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + \ + USB_IRDA_STIR421X_HEADER) struct irda_class_desc { __u8 bLength; diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c new file mode 100644 index 000000000000..754297fc8f22 --- /dev/null +++ b/drivers/net/irda/mcs7780.c @@ -0,0 +1,1009 @@ +/***************************************************************************** +* +* Filename: mcs7780.c +* Version: 0.4-alpha +* Description: Irda MosChip USB Dongle Driver +* Authors: Lukasz Stelmach <stlman@poczta.fm> +* Brian Pugh <bpugh@cs.pdx.edu> +* Judy Fischbach <jfisch@cs.pdx.edu> +* +* Based on stir4200 driver, but some things done differently. +* Based on earlier driver by Paul Stewart <stewart@parc.com> +* +* Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at> +* Copyright (C) 2001, Dag Brattli <dag@brattli.net> +* Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com> +* Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org> +* Copyright (C) 2005, Lukasz Stelmach <stlman@poczta.fm> +* Copyright (C) 2005, Brian Pugh <bpugh@cs.pdx.edu> +* Copyright (C) 2005, Judy Fischbach <jfisch@cs.pdx.edu> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * MCS7780 is a simple USB to IrDA bridge by MosChip. It is neither + * compatibile with irda-usb nor with stir4200. Although it is quite + * similar to the later as far as general idea of operation is concerned. + * That is it requires the software to do all the framing job at SIR speeds. + * The hardware does take care of the framing at MIR and FIR speeds. + * It supports all speeds from 2400 through 4Mbps + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/usb.h> +#include <linux/device.h> +#include <linux/crc32.h> + +#include <asm/unaligned.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/crc.h> + +#include "mcs7780.h" + +#define MCS_VENDOR_ID 0x9710 +#define MCS_PRODUCT_ID 0x7780 + +static struct usb_device_id mcs_table[] = { + /* MosChip Corp., MCS7780 FIR-USB Adapter */ + {USB_DEVICE(MCS_VENDOR_ID, MCS_PRODUCT_ID)}, + {}, +}; + +MODULE_AUTHOR("Brian Pugh <bpugh@cs.pdx.edu>"); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for MosChip MCS7780"); +MODULE_VERSION("0.3alpha"); +MODULE_LICENSE("GPL"); + +MODULE_DEVICE_TABLE(usb, mcs_table); + +static int qos_mtt_bits = 0x07 /* > 1ms */ ; +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); + +static int receive_mode = 0x1; +module_param(receive_mode, int, 0); +MODULE_PARM_DESC(receive_mode, + "Receive mode of the device (1:fast, 0:slow, default:1)"); + +static int sir_tweak = 1; +module_param(sir_tweak, int, 0444); +MODULE_PARM_DESC(sir_tweak, + "Default pulse width (1:1.6us, 0:3/16 bit, default:1)."); + +static int transceiver_type = MCS_TSC_VISHAY; +module_param(transceiver_type, int, 0444); +MODULE_PARM_DESC(transceiver_type, "IR transceiver type, see mcs7780.h."); + +struct usb_driver mcs_driver = { + .name = "mcs7780", + .probe = mcs_probe, + .disconnect = mcs_disconnect, + .id_table = mcs_table, +}; + +/* speed flag selection by direct addressing. +addr = (speed >> 8) & 0x0f + +0x1 57600 0x2 115200 0x4 1152000 0x5 9600 +0x6 38400 0x9 2400 0xa 576000 0xb 19200 + +4Mbps (or 2400) must be checked separately. Since it also has +to be programmed in a different manner that is not a big problem. +*/ +static __u16 mcs_speed_set[16] = { 0, + MCS_SPEED_57600, + MCS_SPEED_115200, + 0, + MCS_SPEED_1152000, + MCS_SPEED_9600, + MCS_SPEED_38400, + 0, 0, + MCS_SPEED_2400, + MCS_SPEED_576000, + MCS_SPEED_19200, + 0, 0, 0, +}; + +/* Set given 16 bit register with a 16 bit value. Send control message + * to set dongle register. */ +static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val) +{ + struct usb_device *dev = mcs->usbdev; + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, + MCS_WR_RTYPE, val, reg, NULL, 0, + msecs_to_jiffies(MCS_CTRL_TIMEOUT)); +} + +/* Get 16 bit register value. Send contol message to read dongle register. */ +static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val) +{ + struct usb_device *dev = mcs->usbdev; + int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, + MCS_RD_RTYPE, 0, reg, val, 2, + msecs_to_jiffies(MCS_CTRL_TIMEOUT)); + + return ret; +} + +/* Setup a communication between mcs7780 and TFDU chips. It is described + * in more detail in the data sheet. The setup sequence puts the the + * vishay tranceiver into high speed mode. It will also receive SIR speed + * packets but at reduced sensitivity. + */ + +/* 0: OK 1:ERROR */ +static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs) +{ + int ret = 0; + __u16 rval; + + /* mcs_get_reg should read exactly two bytes from the dongle */ + ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); + if (unlikely(ret != 2)) { + ret = -EIO; + goto error; + } + + /* The MCS_XCVR_CONF bit puts the transceiver into configuration + * mode. The MCS_MODE0 bit must start out high (1) and then + * transition to low and the MCS_STFIR and MCS_MODE1 bits must + * be low. + */ + rval |= (MCS_MODE0 | MCS_XCVR_CONF); + rval &= ~MCS_STFIR; + rval &= ~MCS_MODE1; + ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); + if (unlikely(ret)) + goto error; + + rval &= ~MCS_MODE0; + ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); + if (unlikely(ret)) + goto error; + + rval &= ~MCS_XCVR_CONF; + ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); + if (unlikely(ret)) + goto error; + + ret = 0; + error: + return ret; +} + +/* Setup a communication between mcs7780 and agilent chip. */ +static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs) +{ + IRDA_WARNING("This transceiver type is not supported yet."); + return 1; +} + +/* Setup a communication between mcs7780 and sharp chip. */ +static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs) +{ + IRDA_WARNING("This transceiver type is not supported yet."); + return 1; +} + +/* Common setup for all transceivers */ +static inline int mcs_setup_transceiver(struct mcs_cb *mcs) +{ + int ret = 0; + __u16 rval; + char *msg; + + msg = "Basic transceiver setup error."; + + /* read value of MODE Register, set the DRIVER and RESET bits + * and write value back out to MODE Register + */ + ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); + if(unlikely(ret != 2)) + goto error; + rval |= MCS_DRIVER; /* put the mcs7780 into configuration mode. */ + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + if(unlikely(ret)) + goto error; + + rval = 0; /* set min pulse width to 0 initially. */ + ret = mcs_set_reg(mcs, MCS_MINRXPW_REG, rval); + if(unlikely(ret)) + goto error; + + ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); + if(unlikely(ret != 2)) + goto error; + + rval &= ~MCS_FIR; /* turn off fir mode. */ + if(mcs->sir_tweak) + rval |= MCS_SIR16US; /* 1.6us pulse width */ + else + rval &= ~MCS_SIR16US; /* 3/16 bit time pulse width */ + + /* make sure ask mode and back to back packets are off. */ + rval &= ~(MCS_BBTG | MCS_ASK); + + rval &= ~MCS_SPEED_MASK; + rval |= MCS_SPEED_9600; /* make sure initial speed is 9600. */ + mcs->speed = 9600; + mcs->new_speed = 0; /* new_speed is set to 0 */ + rval &= ~MCS_PLLPWDN; /* disable power down. */ + + /* make sure device determines direction and that the auto send sip + * pulse are on. + */ + rval |= MCS_DTD | MCS_SIPEN; + + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + if(unlikely(ret)) + goto error; + + msg = "transceiver model specific setup error."; + switch (mcs->transceiver_type) { + case MCS_TSC_VISHAY: + ret = mcs_setup_transceiver_vishay(mcs); + break; + + case MCS_TSC_SHARP: + ret = mcs_setup_transceiver_sharp(mcs); + break; + + case MCS_TSC_AGILENT: + ret = mcs_setup_transceiver_agilent(mcs); + break; + + default: + IRDA_WARNING("Unknown transceiver type: %d", + mcs->transceiver_type); + ret = 1; + } + if (unlikely(ret)) + goto error; + + /* If transceiver is not SHARP, then if receive mode set + * on the RXFAST bit in the XCVR Register otherwise unset it + */ + if (mcs->transceiver_type != MCS_TSC_SHARP) { + + ret = mcs_get_reg(mcs, MCS_XCVR_REG, &rval); + if (unlikely(ret != 2)) + goto error; + if (mcs->receive_mode) + rval |= MCS_RXFAST; + else + rval &= ~MCS_RXFAST; + ret = mcs_set_reg(mcs, MCS_XCVR_REG, rval); + if (unlikely(ret)) + goto error; + } + + msg = "transceiver reset."; + + ret = mcs_get_reg(mcs, MCS_MODE_REG, &rval); + if (unlikely(ret != 2)) + goto error; + + /* reset the mcs7780 so all changes take effect. */ + rval &= ~MCS_RESET; + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + if (unlikely(ret)) + goto error; + else + return ret; + +error: + IRDA_ERROR("%s", msg); + return ret; +} + +/* Wraps the data in format for SIR */ +static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf) +{ + int wraplen; + + /* 2: full frame length, including "the length" */ + wraplen = async_wrap_skb(skb, buf + 2, 4094); + + wraplen += 2; + buf[0] = wraplen & 0xff; + buf[1] = (wraplen >> 8) & 0xff; + + return wraplen; +} + +/* Wraps the data in format for FIR */ +static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) +{ + unsigned int len = 0; + __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); + + /* add 2 bytes for length value and 4 bytes for fcs. */ + len = skb->len + 6; + + /* The mcs7780 requires that the first two bytes are the packet + * length in little endian order. Note: the length value includes + * the two bytes for the length value itself. + */ + buf[0] = len & 0xff; + buf[1] = (len >> 8) & 0xff; + /* copy the data into the tx buffer. */ + memcpy(buf+2, skb->data, skb->len); + /* put the fcs in the last four bytes in little endian order. */ + buf[len - 4] = fcs & 0xff; + buf[len - 3] = (fcs >> 8) & 0xff; + buf[len - 2] = (fcs >> 16) & 0xff; + buf[len - 1] = (fcs >> 24) & 0xff; + + return len; +} + +/* Wraps the data in format for MIR */ +static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf) +{ + __u16 fcs = 0; + int len = skb->len + 4; + + fcs = ~(irda_calc_crc16(~fcs, skb->data, skb->len)); + /* put the total packet length in first. Note: packet length + * value includes the two bytes that hold the packet length + * itself. + */ + buf[0] = len & 0xff; + buf[1] = (len >> 8) & 0xff; + /* copy the data */ + memcpy(buf+2, skb->data, skb->len); + /* put the fcs in last two bytes in little endian order. */ + buf[len - 2] = fcs & 0xff; + buf[len - 1] = (fcs >> 8) & 0xff; + + return len; +} + +/* Unwrap received packets at MIR speed. A 16 bit crc_ccitt checksum is + * used for the fcs. When performed over the entire packet the result + * should be GOOD_FCS = 0xf0b8. Hands the unwrapped data off to the IrDA + * layer via a sk_buff. + */ +static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len) +{ + __u16 fcs; + int new_len; + struct sk_buff *skb; + + /* Assume that the frames are going to fill a single packet + * rather than span multiple packets. + */ + + new_len = len - 2; + if(unlikely(new_len <= 0)) { + IRDA_ERROR("%s short frame length %d\n", + mcs->netdev->name, new_len); + ++mcs->stats.rx_errors; + ++mcs->stats.rx_length_errors; + return; + } + fcs = 0; + fcs = irda_calc_crc16(~fcs, buf, len); + + if(fcs != GOOD_FCS) { + IRDA_ERROR("crc error calc 0x%x len %d\n", + fcs, new_len); + mcs->stats.rx_errors++; + mcs->stats.rx_crc_errors++; + return; + } + + skb = dev_alloc_skb(new_len + 1); + if(unlikely(!skb)) { + ++mcs->stats.rx_dropped; + return; + } + + skb_reserve(skb, 1); + memcpy(skb->data, buf, new_len); + skb_put(skb, new_len); + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + skb->dev = mcs->netdev; + + netif_rx(skb); + + mcs->stats.rx_packets++; + mcs->stats.rx_bytes += new_len; + + return; +} + +/* Unwrap received packets at FIR speed. A 32 bit crc_ccitt checksum is + * used for the fcs. Hands the unwrapped data off to the IrDA + * layer via a sk_buff. + */ +static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len) +{ + __u32 fcs; + int new_len; + struct sk_buff *skb; + + /* Assume that the frames are going to fill a single packet + * rather than span multiple packets. This is most likely a false + * assumption. + */ + + new_len = len - 4; + if(unlikely(new_len <= 0)) { + IRDA_ERROR("%s short frame length %d\n", + mcs->netdev->name, new_len); + ++mcs->stats.rx_errors; + ++mcs->stats.rx_length_errors; + return; + } + + fcs = ~(crc32_le(~0, buf, new_len)); + if(fcs != le32_to_cpu(get_unaligned((u32 *)(buf+new_len)))) { + IRDA_ERROR("crc error calc 0x%x len %d\n", fcs, new_len); + mcs->stats.rx_errors++; + mcs->stats.rx_crc_errors++; + return; + } + + skb = dev_alloc_skb(new_len + 1); + if(unlikely(!skb)) { + ++mcs->stats.rx_dropped; + return; + } + + skb_reserve(skb, 1); + memcpy(skb->data, buf, new_len); + skb_put(skb, new_len); + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + skb->dev = mcs->netdev; + + netif_rx(skb); + + mcs->stats.rx_packets++; + mcs->stats.rx_bytes += new_len; + + return; +} + + +/* Allocates urbs for both receive and transmit. + * If alloc fails return error code 0 (fail) otherwise + * return error code 1 (success). + */ +static inline int mcs_setup_urbs(struct mcs_cb *mcs) +{ + mcs->rx_urb = NULL; + + mcs->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mcs->tx_urb) + return 0; + + mcs->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mcs->rx_urb) + return 0; + + return 1; +} + +/* Sets up state to be initially outside frame, gets receive urb, + * sets status to successful and then submits the urb to start + * receiving the data. + */ +static inline int mcs_receive_start(struct mcs_cb *mcs) +{ + mcs->rx_buff.in_frame = FALSE; + mcs->rx_buff.state = OUTSIDE_FRAME; + + usb_fill_bulk_urb(mcs->rx_urb, mcs->usbdev, + usb_rcvbulkpipe(mcs->usbdev, mcs->ep_in), + mcs->in_buf, 4096, mcs_receive_irq, mcs); + + mcs->rx_urb->status = 0; + return usb_submit_urb(mcs->rx_urb, GFP_KERNEL); +} + +/* Finds the in and out endpoints for the mcs control block */ +static inline int mcs_find_endpoints(struct mcs_cb *mcs, + struct usb_host_endpoint *ep, int epnum) +{ + int i; + int ret = 0; + + /* If no place to store the endpoints just return */ + if (!ep) + return ret; + + /* cycle through all endpoints, find the first two that are DIR_IN */ + for (i = 0; i < epnum; i++) { + if (ep[i].desc.bEndpointAddress & USB_DIR_IN) + mcs->ep_in = ep[i].desc.bEndpointAddress; + else + mcs->ep_out = ep[i].desc.bEndpointAddress; + + /* MosChip says that the chip has only two bulk + * endpoints. Find one for each direction and move on. + */ + if ((mcs->ep_in != 0) && (mcs->ep_out != 0)) { + ret = 1; + break; + } + } + + return ret; +} + +static void mcs_speed_work(void *arg) +{ + struct mcs_cb *mcs = arg; + struct net_device *netdev = mcs->netdev; + + mcs_speed_change(mcs); + netif_wake_queue(netdev); +} + +/* Function to change the speed of the mcs7780. Fully supports SIR, + * MIR, and FIR speeds. + */ +static int mcs_speed_change(struct mcs_cb *mcs) +{ + int ret = 0; + int rst = 0; + int cnt = 0; + __u16 nspeed; + __u16 rval; + + nspeed = mcs_speed_set[(mcs->new_speed >> 8) & 0x0f]; + + do { + mcs_get_reg(mcs, MCS_RESV_REG, &rval); + } while(cnt++ < 100 && (rval & MCS_IRINTX)); + + if(cnt >= 100) { + IRDA_ERROR("unable to change speed"); + ret = -EIO; + goto error; + } + + mcs_get_reg(mcs, MCS_MODE_REG, &rval); + + /* MINRXPW values recomended by MosChip */ + if (mcs->new_speed <= 115200) { + rval &= ~MCS_FIR; + + if ((rst = (mcs->speed > 115200))) + mcs_set_reg(mcs, MCS_MINRXPW_REG, 0); + + } else if (mcs->new_speed <= 1152000) { + rval &= ~MCS_FIR; + + if ((rst = !(mcs->speed == 576000 || mcs->speed == 1152000))) + mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); + + } else { + rval |= MCS_FIR; + + if ((rst = (mcs->speed != 4000000))) + mcs_set_reg(mcs, MCS_MINRXPW_REG, 5); + + } + + rval &= ~MCS_SPEED_MASK; + rval |= nspeed; + + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + if (unlikely(ret)) + goto error; + + if (rst) + switch (mcs->transceiver_type) { + case MCS_TSC_VISHAY: + ret = mcs_setup_transceiver_vishay(mcs); + break; + + case MCS_TSC_SHARP: + ret = mcs_setup_transceiver_sharp(mcs); + break; + + case MCS_TSC_AGILENT: + ret = mcs_setup_transceiver_agilent(mcs); + break; + + default: + ret = 1; + IRDA_WARNING("Unknown transceiver type: %d", + mcs->transceiver_type); + } + if (unlikely(ret)) + goto error; + + mcs_get_reg(mcs, MCS_MODE_REG, &rval); + rval &= ~MCS_RESET; + ret = mcs_set_reg(mcs, MCS_MODE_REG, rval); + + mcs->speed = mcs->new_speed; + error: + mcs->new_speed = 0; + return ret; +} + +/* Ioctl calls not supported at this time. Can be an area of future work. */ +static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) +{ + /* struct if_irda_req *irq = (struct if_irda_req *)rq; */ + /* struct mcs_cb *mcs = netdev_priv(netdev); */ + int ret = 0; + + switch (cmd) { + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* Network device is taken down, done by "ifconfig irda0 down" */ +static int mcs_net_close(struct net_device *netdev) +{ + int ret = 0; + struct mcs_cb *mcs = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* kill and free the receive and transmit URBs */ + usb_kill_urb(mcs->rx_urb); + usb_free_urb(mcs->rx_urb); + usb_kill_urb(mcs->tx_urb); + usb_free_urb(mcs->tx_urb); + + /* Stop and remove instance of IrLAP */ + if (mcs->irlap) + irlap_close(mcs->irlap); + + mcs->irlap = NULL; + return ret; +} + +/* Network device is taken up, done by "ifconfig irda0 up" */ +static int mcs_net_open(struct net_device *netdev) +{ + struct mcs_cb *mcs = netdev_priv(netdev); + char hwname[16]; + int ret = 0; + + ret = usb_clear_halt(mcs->usbdev, + usb_sndbulkpipe(mcs->usbdev, mcs->ep_in)); + if (ret) + goto error1; + ret = usb_clear_halt(mcs->usbdev, + usb_rcvbulkpipe(mcs->usbdev, mcs->ep_out)); + if (ret) + goto error1; + + ret = mcs_setup_transceiver(mcs); + if (ret) + goto error1; + + ret = -ENOMEM; + + /* Initialize for SIR/FIR to copy data directly into skb. */ + mcs->receiving = 0; + mcs->rx_buff.truesize = IRDA_SKB_MAX_MTU; + mcs->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!mcs->rx_buff.skb) + goto error1; + + skb_reserve(mcs->rx_buff.skb, 1); + mcs->rx_buff.head = mcs->rx_buff.skb->data; + do_gettimeofday(&mcs->rx_time); + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + * Note : will send immediately a speed change... + */ + sprintf(hwname, "usb#%d", mcs->usbdev->devnum); + mcs->irlap = irlap_open(netdev, &mcs->qos, hwname); + if (!mcs->irlap) { + IRDA_ERROR("mcs7780: irlap_open failed"); + goto error2; + } + + if (!mcs_setup_urbs(mcs)) + goto error3; + + ret = mcs_receive_start(mcs); + if (ret) + goto error3; + + netif_start_queue(netdev); + return 0; + + error3: + irlap_close(mcs->irlap); + error2: + kfree_skb(mcs->rx_buff.skb); + error1: + return ret; +} + + +/* Get device stats for /proc/net/dev and ifconfig */ +static struct net_device_stats *mcs_net_get_stats(struct net_device *netdev) +{ + struct mcs_cb *mcs = netdev_priv(netdev); + return &mcs->stats; +} + +/* Receive callback function. */ +static void mcs_receive_irq(struct urb *urb, struct pt_regs *regs) +{ + __u8 *bytes; + struct mcs_cb *mcs = urb->context; + int i; + int ret; + + if (!netif_running(mcs->netdev)) + return; + + if (urb->status) + return; + + if (urb->actual_length > 0) { + bytes = urb->transfer_buffer; + + /* MCS returns frames without BOF and EOF + * I assume it returns whole frames. + */ + /* SIR speed */ + if(mcs->speed < 576000) { + async_unwrap_char(mcs->netdev, &mcs->stats, + &mcs->rx_buff, 0xc0); + + for (i = 0; i < urb->actual_length; i++) + async_unwrap_char(mcs->netdev, &mcs->stats, + &mcs->rx_buff, bytes[i]); + + async_unwrap_char(mcs->netdev, &mcs->stats, + &mcs->rx_buff, 0xc1); + } + /* MIR speed */ + else if(mcs->speed == 576000 || mcs->speed == 1152000) { + mcs_unwrap_mir(mcs, urb->transfer_buffer, + urb->actual_length); + } + /* FIR speed */ + else { + mcs_unwrap_fir(mcs, urb->transfer_buffer, + urb->actual_length); + } + mcs->netdev->last_rx = jiffies; + do_gettimeofday(&mcs->rx_time); + } + + ret = usb_submit_urb(urb, GFP_ATOMIC); +} + +/* Transmit callback funtion. */ +static void mcs_send_irq(struct urb *urb, struct pt_regs *regs) +{ + struct mcs_cb *mcs = urb->context; + struct net_device *ndev = mcs->netdev; + + if (unlikely(mcs->new_speed)) + schedule_work(&mcs->work); + else + netif_wake_queue(ndev); +} + +/* Transmit callback funtion. */ +static int mcs_hard_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + unsigned long flags; + struct mcs_cb *mcs; + int wraplen; + int ret = 0; + + + if (skb == NULL || ndev == NULL) + return -EINVAL; + + netif_stop_queue(ndev); + mcs = netdev_priv(ndev); + + spin_lock_irqsave(&mcs->lock, flags); + + mcs->new_speed = irda_get_next_speed(skb); + if (likely(mcs->new_speed == mcs->speed)) + mcs->new_speed = 0; + + /* SIR speed */ + if(mcs->speed < 576000) { + wraplen = mcs_wrap_sir_skb(skb, mcs->out_buf); + } + /* MIR speed */ + else if(mcs->speed == 576000 || mcs->speed == 1152000) { + wraplen = mcs_wrap_mir_skb(skb, mcs->out_buf); + } + /* FIR speed */ + else { + wraplen = mcs_wrap_fir_skb(skb, mcs->out_buf); + } + usb_fill_bulk_urb(mcs->tx_urb, mcs->usbdev, + usb_sndbulkpipe(mcs->usbdev, mcs->ep_out), + mcs->out_buf, wraplen, mcs_send_irq, mcs); + + if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) { + IRDA_ERROR("failed tx_urb: %d", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + mcs->stats.tx_errors++; + netif_start_queue(ndev); + } + } else { + mcs->stats.tx_packets++; + mcs->stats.tx_bytes += skb->len; + } + + dev_kfree_skb(skb); + spin_unlock_irqrestore(&mcs->lock, flags); + return ret; +} + +/* + * This function is called by the USB subsystem for each new device in the + * system. Need to verify the device and if it is, then start handling it. + */ +static int mcs_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct net_device *ndev = NULL; + struct mcs_cb *mcs; + int ret = -ENOMEM; + + ndev = alloc_irdadev(sizeof(*mcs)); + if (!ndev) + goto error1; + + IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.", udev->devnum); + + /* what is it realy for? */ + SET_MODULE_OWNER(ndev); + SET_NETDEV_DEV(ndev, &intf->dev); + + ret = usb_reset_configuration(udev); + if (ret != 0) { + IRDA_ERROR("mcs7780: usb reset configuration failed"); + goto error2; + } + + mcs = netdev_priv(ndev); + mcs->usbdev = udev; + mcs->netdev = ndev; + spin_lock_init(&mcs->lock); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&mcs->qos); + + /* That's the Rx capability. */ + mcs->qos.baud_rate.bits &= + IR_2400 | IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 + | IR_576000 | IR_1152000 | (IR_4000000 << 8); + + + mcs->qos.min_turn_time.bits &= qos_mtt_bits; + irda_qos_bits_to_value(&mcs->qos); + + /* Speed change work initialisation*/ + INIT_WORK(&mcs->work, mcs_speed_work, mcs); + + /* Override the network functions we need to use */ + ndev->hard_start_xmit = mcs_hard_xmit; + ndev->open = mcs_net_open; + ndev->stop = mcs_net_close; + ndev->get_stats = mcs_net_get_stats; + ndev->do_ioctl = mcs_net_ioctl; + + if (!intf->cur_altsetting) + goto error2; + + ret = mcs_find_endpoints(mcs, intf->cur_altsetting->endpoint, + intf->cur_altsetting->desc.bNumEndpoints); + if (!ret) { + ret = -ENODEV; + goto error2; + } + + ret = register_netdev(ndev); + if (ret != 0) + goto error2; + + IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s", + ndev->name); + + mcs->transceiver_type = transceiver_type; + mcs->sir_tweak = sir_tweak; + mcs->receive_mode = receive_mode; + + usb_set_intfdata(intf, mcs); + return 0; + + error2: + free_netdev(ndev); + + error1: + return ret; +} + +/* The current device is removed, the USB layer tells us to shut down. */ +static void mcs_disconnect(struct usb_interface *intf) +{ + struct mcs_cb *mcs = usb_get_intfdata(intf); + + if (!mcs) + return; + + flush_scheduled_work(); + + unregister_netdev(mcs->netdev); + free_netdev(mcs->netdev); + + usb_set_intfdata(intf, NULL); + IRDA_DEBUG(0, "MCS7780 now disconnected."); +} + +/* Module insertion */ +static int __init mcs_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&mcs_driver); + if (result) + IRDA_ERROR("usb_register failed. Error number %d", result); + + return result; +} +module_init(mcs_init); + +/* Module removal */ +static void __exit mcs_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&mcs_driver); +} +module_exit(mcs_exit); + diff --git a/drivers/net/irda/mcs7780.h b/drivers/net/irda/mcs7780.h new file mode 100644 index 000000000000..1a723d725c2a --- /dev/null +++ b/drivers/net/irda/mcs7780.h @@ -0,0 +1,167 @@ +/***************************************************************************** +* +* Filename: mcs7780.h +* Version: 0.2-alpha +* Description: Irda MosChip USB Dongle +* Status: Experimental +* Authors: Lukasz Stelmach <stlman@poczta.fm> +* Brian Pugh <bpugh@cs.pdx.edu> +* +* Copyright (C) 2005, Lukasz Stelmach <stlman@poczta.fm> +* Copyright (C) 2005, Brian Pugh <bpugh@cs.pdx.edu> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ +#ifndef _MCS7780_H +#define _MCS7780_H + +#define MCS_MODE_SIR 0 +#define MCS_MODE_MIR 1 +#define MCS_MODE_FIR 2 + +#define MCS_CTRL_TIMEOUT 500 +#define MCS_XMIT_TIMEOUT 500 +/* Possible transceiver types */ +#define MCS_TSC_VISHAY 0 /* Vishay TFD, default choice */ +#define MCS_TSC_AGILENT 1 /* Agilent 3602/3600 */ +#define MCS_TSC_SHARP 2 /* Sharp GP2W1000YP */ + +/* Requests */ +#define MCS_RD_RTYPE 0xC0 +#define MCS_WR_RTYPE 0x40 +#define MCS_RDREQ 0x0F +#define MCS_WRREQ 0x0E + +/* Register 0x00 */ +#define MCS_MODE_REG 0 +#define MCS_FIR ((__u16)0x0001) +#define MCS_SIR16US ((__u16)0x0002) +#define MCS_BBTG ((__u16)0x0004) +#define MCS_ASK ((__u16)0x0008) +#define MCS_PARITY ((__u16)0x0010) + +/* SIR/MIR speed constants */ +#define MCS_SPEED_SHIFT 5 +#define MCS_SPEED_MASK ((__u16)0x00E0) +#define MCS_SPEED(x) ((x & MCS_SPEED_MASK) >> MCS_SPEED_SHIFT) +#define MCS_SPEED_2400 ((0 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_9600 ((1 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_19200 ((2 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_38400 ((3 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_57600 ((4 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_115200 ((5 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_576000 ((6 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) +#define MCS_SPEED_1152000 ((7 << MCS_SPEED_SHIFT) & MCS_SPEED_MASK) + +#define MCS_PLLPWDN ((__u16)0x0100) +#define MCS_DRIVER ((__u16)0x0200) +#define MCS_DTD ((__u16)0x0400) +#define MCS_DIR ((__u16)0x0800) +#define MCS_SIPEN ((__u16)0x1000) +#define MCS_SENDSIP ((__u16)0x2000) +#define MCS_CHGDIR ((__u16)0x4000) +#define MCS_RESET ((__u16)0x8000) + +/* Register 0x02 */ +#define MCS_XCVR_REG 2 +#define MCS_MODE0 ((__u16)0x0001) +#define MCS_STFIR ((__u16)0x0002) +#define MCS_XCVR_CONF ((__u16)0x0004) +#define MCS_RXFAST ((__u16)0x0008) +/* TXCUR [6:4] */ +#define MCS_TXCUR_SHIFT 4 +#define MCS_TXCUR_MASK ((__u16)0x0070) +#define MCS_TXCUR(x) ((x & MCS_TXCUR_MASK) >> MCS_TXCUR_SHIFT) +#define MCS_SETTXCUR(x,y) \ + ((x & ~MCS_TXCUR_MASK) | (y << MCS_TXCUR_SHIFT) & MCS_TXCUR_MASK) + +#define MCS_MODE1 ((__u16)0x0080) +#define MCS_SMODE0 ((__u16)0x0100) +#define MCS_SMODE1 ((__u16)0x0200) +#define MCS_INVTX ((__u16)0x0400) +#define MCS_INVRX ((__u16)0x0800) + +#define MCS_MINRXPW_REG 4 + +#define MCS_RESV_REG 7 +#define MCS_IRINTX ((__u16)0x0001) +#define MCS_IRINRX ((__u16)0x0002) + +struct mcs_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct net_device_stats stats; /* network statistics */ + struct qos_info qos; + unsigned int speed; /* Current speed */ + unsigned int new_speed; /* new speed */ + + struct work_struct work; /* Change speed work */ + + struct sk_buff *tx_pending; + char in_buf[4096]; /* transmit/receive buffer */ + char out_buf[4096]; /* transmit/receive buffer */ + __u8 *fifo_status; + + iobuff_t rx_buff; /* receive unwrap state machine */ + struct timeval rx_time; + spinlock_t lock; + int receiving; + + __u8 ep_in; + __u8 ep_out; + + struct urb *rx_urb; + struct urb *tx_urb; + + int transceiver_type; + int sir_tweak; + int receive_mode; +}; + +static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val); +static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val); + +static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs); +static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs); +static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs); +static inline int mcs_setup_transceiver(struct mcs_cb *mcs); +static inline int mcs_wrap_sir_skb(struct sk_buff *skb, __u8 * buf); +static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf); +static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf); +static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len); +static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len); +static inline int mcs_setup_urbs(struct mcs_cb *mcs); +static inline int mcs_receive_start(struct mcs_cb *mcs); +static inline int mcs_find_endpoints(struct mcs_cb *mcs, + struct usb_host_endpoint *ep, int epnum); + +static int mcs_speed_change(struct mcs_cb *mcs); + +static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd); +static int mcs_net_close(struct net_device *netdev); +static int mcs_net_open(struct net_device *netdev); +static struct net_device_stats *mcs_net_get_stats(struct net_device *netdev); + +static void mcs_receive_irq(struct urb *urb, struct pt_regs *regs); +static void mcs_send_irq(struct urb *urb, struct pt_regs *regs); +static int mcs_hard_xmit(struct sk_buff *skb, struct net_device *netdev); + +static int mcs_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void mcs_disconnect(struct usb_interface *intf); + +#endif /* _MCS7780_H */ diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 31867e4b891b..d61b208b52a2 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -50,6 +50,7 @@ #include <linux/delay.h> #include <linux/usb.h> #include <linux/crc32.h> +#include <linux/kthread.h> #include <net/irda/irda.h> #include <net/irda/irlap.h> #include <net/irda/irda_device.h> @@ -173,9 +174,7 @@ struct stir_cb { struct qos_info qos; unsigned speed; /* Current speed */ - wait_queue_head_t thr_wait; /* transmit thread wakeup */ - struct completion thr_exited; - pid_t thr_pid; + struct task_struct *thread; /* transmit thread */ struct sk_buff *tx_pending; void *io_buf; /* transmit/receive buffer */ @@ -577,7 +576,7 @@ static int stir_hard_xmit(struct sk_buff *skb, struct net_device *netdev) SKB_LINEAR_ASSERT(skb); skb = xchg(&stir->tx_pending, skb); - wake_up(&stir->thr_wait); + wake_up_process(stir->thread); /* this should never happen unless stop/wakeup problem */ if (unlikely(skb)) { @@ -753,13 +752,7 @@ static int stir_transmit_thread(void *arg) struct net_device *dev = stir->netdev; struct sk_buff *skb; - daemonize("%s", dev->name); - allow_signal(SIGTERM); - - while (netif_running(dev) - && netif_device_present(dev) - && !signal_pending(current)) - { + while (!kthread_should_stop()) { #ifdef CONFIG_PM /* if suspending, then power off and wait */ if (unlikely(freezing(current))) { @@ -813,10 +806,11 @@ static int stir_transmit_thread(void *arg) } /* sleep if nothing to send */ - wait_event_interruptible(stir->thr_wait, stir->tx_pending); - } + set_current_state(TASK_INTERRUPTIBLE); + schedule(); - complete_and_exit (&stir->thr_exited, 0); + } + return 0; } @@ -859,7 +853,7 @@ static void stir_rcv_irq(struct urb *urb, struct pt_regs *regs) warn("%s: usb receive submit error: %d", stir->netdev->name, err); stir->receiving = 0; - wake_up(&stir->thr_wait); + wake_up_process(stir->thread); } } @@ -928,10 +922,10 @@ static int stir_net_open(struct net_device *netdev) } /** Start kernel thread for transmit. */ - stir->thr_pid = kernel_thread(stir_transmit_thread, stir, - CLONE_FS|CLONE_FILES); - if (stir->thr_pid < 0) { - err = stir->thr_pid; + stir->thread = kthread_run(stir_transmit_thread, stir, + "%s", stir->netdev->name); + if (IS_ERR(stir->thread)) { + err = PTR_ERR(stir->thread); err("stir4200: unable to start kernel thread"); goto err_out6; } @@ -968,8 +962,7 @@ static int stir_net_close(struct net_device *netdev) netif_stop_queue(netdev); /* Kill transmit thread */ - kill_proc(stir->thr_pid, SIGTERM, 1); - wait_for_completion(&stir->thr_exited); + kthread_stop(stir->thread); kfree(stir->fifo_status); /* Mop up receive urb's */ @@ -1084,9 +1077,6 @@ static int stir_probe(struct usb_interface *intf, stir->qos.min_turn_time.bits &= qos_mtt_bits; irda_qos_bits_to_value(&stir->qos); - init_completion (&stir->thr_exited); - init_waitqueue_head (&stir->thr_wait); - /* Override the network functions we need to use */ net->hard_start_xmit = stir_hard_xmit; net->open = stir_net_open; diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index 97a49e0be76b..d70b9e8d6e60 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -959,7 +959,7 @@ static int vlsi_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) || (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec)) break; udelay(100); - /* must not sleep here - we are called under xmit_lock! */ + /* must not sleep here - called under netif_tx_lock! */ } } |