It is the the first public version of amedyn module for the new usb_atm. Test by me whit Zyxel 630-11 and kernel 2.6.10 in FC3. And by Emmanuel Counasse whit Asus AAM600UG and kernel 2.6.9 in Debian Sid.
CVS at : http://cvs.sourceforge.net/viewcvs.py/zyxel630-11/amedyn2/module/ For full package: - cvs -d:pserver:[EMAIL PROTECTED]:/cvsroot/zyxel630-11 login - cvs -z3 -d:pserver:[EMAIL PROTECTED]:/cvsroot/zyxel630-11 co -P amedyn2 --------------------------- /****************************************************************************** * amedyn.c - Zyxel 630-11/13 and Asus AAM600UG ALC USB xDSL modem driver * * Copyright (C) 2001, Alcatel * Copyright (C) 2003, Duncan Sands * Copyright (C) 2004, David Woodhouse * * 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., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ******************************************************************************/ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/gfp.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/errno.h> #include <linux/proc_fs.h> #include <linux/slab.h> #include <linux/wait.h> #include <linux/list.h> #include <asm/processor.h> #include <asm/uaccess.h> #include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/atm.h> #include <linux/atmdev.h> #include <linux/crc32.h> #include <linux/init.h> #include <linux/firmware.h> #include "usb_atm.h" #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) # define USE_FW_LOADER #endif #define DRIVER_AUTHOR "Aurelio Arroyo, Josep Comas, David Woodhouse, Duncan Sands" #define DRIVER_VERSION "0.8" #define DRIVER_DESC "Zyxel 630-11/13 and Asus AAM600UG ALC USB driver version " DRIVER_VERSION static const char amedyn_driver_name[] = "amedyn"; /* Alcatel Microelectronics new reference design */ #define AME_VENDORID2 0x06b9 /* Vendor = Zyxel */ #define AME_PRODUCTID2 0xa5a5 /* Product = 630-11 & 630-13 */ #define AME_VENDORID3 0x0b05 /* Vendor = Asustek */ #define AME_PRODUCTID3 0x6206 /* Product = AAM6000UG with Alcatel chipset */ /* Modem types */ #define UDSL_MODEM_TYPE1 0 /* Alcatel reference design */ #define UDSL_MODEM_TYPE2 1 /* Conexant reference design */ #define UDSL_MODEM_TYPE3 2 /* 3Com reference design (Alcatel DSP) */ /* Timeout in jiffies */ #define CTRL_TIMEOUT (2*HZ) #define DATA_TIMEOUT (2*HZ) #define ANALOG 0x15 #define ISDN 0x11 static int linetype = 0; module_param(linetype, uint, 0444); MODULE_PARM_DESC(linetype, "Set phone line type code"); #define UDSL_IOCTL_LINE_UP 1 #define UDSL_IOCTL_LINE_DOWN 2 #define AMEDYN_ENDPOINT_DATA_OUT 0x07 #define AMEDYN_ENDPOINT_DATA_IN 0x87 #define AMEDYN_ENDPOINT_FIRMWARE_OUT 0x05 // from the userspace tool #define AMEDYN_USB_IN_INFO 0x81 // IN endpoint address, read modem status / #define AMEDYN_USB_OUT_INFO 0x01 // OUT endpoint address / #define AMEDYN_USB_IN_FIRM 0x85 // IN endpoint address, read config / #define AMEDYN_USB_OUT_FIRM 0x05 // OUT endpoint address, send firmware / #define AMEDYN_VENDOR_REQUEST_OUT 0x40 /* Vendor specific requests, OUT direction */ #define AMEDYN_VENDOR_REQUEST_IN 0xC0 /* Vendor specific requets, IN direction */ #define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) static struct usb_device_id amedyn_usb_ids[] = { {USB_DEVICE(AME_VENDORID2, AME_PRODUCTID2)}, {USB_DEVICE(AME_VENDORID3, AME_PRODUCTID3)}, {} }; MODULE_DEVICE_TABLE(usb, amedyn_usb_ids); struct amedyn_instance_data { struct udsl_instance_data u; int datamax; /* maximum data that we can send in a block */ char *initfirmfile; /* init firmware file name */ char *firmfile; /* firmware file name */ char linetype; /* 0x15 = analog line, 0x11 ISDN line */ unsigned char bufconf[8]; /* buffer to save config bytes */ int resynconlost; /* Resync line if it is lost?*/ /* Status */ struct work_struct poll_work; struct timer_list poll_timer; }; /* USB */ static int amedyn_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); static void amedyn_usb_disconnect(struct usb_interface *intf); static int amedyn_usb_ioctl(struct usb_interface *intf, unsigned int code, void *user_data); static void amedyn_poll_status(struct amedyn_instance_data *instance); static struct usb_driver amedyn_usb_driver = { .owner = THIS_MODULE, .name = amedyn_driver_name, .probe = amedyn_usb_probe, .disconnect = amedyn_usb_disconnect, .ioctl = amedyn_usb_ioctl, .id_table = amedyn_usb_ids, }; /*************** ** firmware ** ***************/ static void amedyn_got_firmware(struct amedyn_instance_data *instance, int got_it) { int err; down(&instance->u.serialize); /* vs self, amedyn_firmware_start */ if (instance->u.status == UDSL_LOADED_FIRMWARE) goto out; if (!got_it) { instance->u.status = UDSL_NO_FIRMWARE; goto out; } if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) { dbg("amedyn_got_firmware: usb_set_interface returned %d!", err); instance->u.status = UDSL_NO_FIRMWARE; goto out; } /* Start status polling */ mod_timer(&instance->poll_timer, jiffies + (1 * HZ)); instance->u.status = UDSL_LOADED_FIRMWARE; tasklet_schedule(&instance->u.receive_tasklet); out: up(&instance->u.serialize); wake_up_interruptible(&instance->u.firmware_waiters); } static int amedyn_line_down_signal (struct amedyn_instance_data *instance) { struct usb_device *dev = instance->u.usb_dev; unsigned char buf[0x1ff]; int ret; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x03, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x00, buf, 0x00, CTRL_TIMEOUT); if (ret < 0) { dbg("amedyn: Failed sync down command: %d\n", ret); return ret; } dbg("amedyn: OK sync dowm command"); return 0; } static int amedyn_get_status(struct amedyn_instance_data *instance, unsigned char *buf) { struct usb_device *dev = instance->u.usb_dev; int ret; memset(buf, 0, 0x10); ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev, AMEDYN_USB_IN_INFO), buf, 0x10, NULL, DATA_TIMEOUT); if (ret < 0) { dbg("Error retrieving info!"); return ret; } /* If buf isn't line status info or a error code try again*/ if ( buf[0] != 0x01 && ret > 2 && instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND ) { ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev, AMEDYN_USB_IN_INFO), buf, 0x10, NULL, DATA_TIMEOUT); if (ret < 0) { dbg("Error retrieving info!"); return ret; } } /* If buf isn't line status info or a error code try again, again */ if ( buf[0] != 0x01 && ret > 2 && instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND ) { ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev, AMEDYN_USB_IN_INFO), buf, 0x10, NULL, DATA_TIMEOUT); if (ret < 0) { dbg("Error retrieving info!"); return ret; } } /* If buf isn't line status info or a error code try again, again, again*/ if ( buf[0] != 0x01 && ret > 2 && instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND ) { ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev, AMEDYN_USB_IN_INFO), buf, 0x10, NULL, DATA_TIMEOUT); if (ret < 0) { dbg("Error retrieving info!"); return ret; } } /* If buf isn't line status info or a error code try again, again, again, again*/ if ( buf[0] != 0x01 && ret > 2 && instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND ) { ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev, AMEDYN_USB_IN_INFO), buf, 0x10, NULL, DATA_TIMEOUT); if (ret < 0) { dbg("Error retrieving info!"); return ret; } } /* If buf isn't line status info or a error code try again, again, again, again, again*/ if ( buf[0] != 0x01 && ret > 2 && instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND ) { ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev, AMEDYN_USB_IN_INFO), buf, 0x10, NULL, DATA_TIMEOUT); if (ret < 0) { dbg("Error retrieving info!"); return ret; } } return 0; } static int amedyn_start_synchro(struct amedyn_instance_data *instance) { struct usb_device *dev = instance->u.usb_dev; unsigned char buf[0x1ff]; /* buffer */ int ret, i; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x0b, AMEDYN_VENDOR_REQUEST_OUT, 0x0c, 0x00, NULL, 0x00, CTRL_TIMEOUT); if (ret < 0) { printk(KERN_WARNING "%s failed on local urb 1: %d\n", __func__, ret); return ret; } for (i = 0xba; i <= 0xc1; i++) { ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, i, &(instance->bufconf[i-0xba]), 0x01, CTRL_TIMEOUT); if (ret < 0) { printk(KERN_WARNING "%s failed on local urb 2: %d\n", __func__, ret); return ret; } } /* set AFE value, R_Function_Code = 0x15 (adjust Alcatel DSP for our configuration) */ /* 0x1fd in CTRLE protocol */ /* 0x15 = analog line, 0x11 ISDN line */ buf[0] = instance->linetype; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x1fd, buf, 0x01, CTRL_TIMEOUT); if (ret < 0) { printk(KERN_WARNING "%s failed on local urb 3: %d\n", __func__, ret); return ret; } buf[0] = 0x01; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x4a, buf, 0x01, CTRL_TIMEOUT); if (ret < 0) { printk(KERN_WARNING "%s failed on local urb 4: %d\n", __func__, ret); return ret; } buf[0] = 0x00; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x4b, buf, 0x01, CTRL_TIMEOUT); if (ret < 0) { printk(KERN_WARNING "%s failed on local urb 5: %d\n", __func__, ret); return ret; } buf[0] = 0x00; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x4c, buf, 0x01, CTRL_TIMEOUT); if (ret < 0) { printk(KERN_WARNING "%s failed on local urb 6: %d\n", __func__, ret); return ret; } /* Only this command seem to be necesary to resync. */ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x02, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x00, NULL, 0x00, CTRL_TIMEOUT); if (ret < 0) { printk(KERN_WARNING "%s failed on local urb 7: %d\n", __func__, ret); return ret; } ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x0e, AMEDYN_VENDOR_REQUEST_IN, 0x03, 0x00, buf, 0x0c, CTRL_TIMEOUT); if (ret < 0) { printk(KERN_WARNING "%s failed on local urb 8: %d\n", __func__, ret); return ret; } dbg("amedyn_start_synchro: send sync signals."); return 0; } static void amedyn_poll_status(struct amedyn_instance_data *instance) { unsigned char buf[0x10]; int ret; ret = amedyn_get_status(instance, buf); if (ret) { printk(KERN_WARNING "Zyxel: Error %d fetching device status\n", ret); return; } if ( buf[0] == 0x02 ) { if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; printk(KERN_NOTICE "ADSL line is down\n"); } dbg("Line sync lost?"); if ( instance->resynconlost == 1 ) { amedyn_line_down_signal (instance); amedyn_start_synchro(instance); } } if ( buf[0] == 0x40 ) { if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; printk(KERN_NOTICE "ADSL line is down\n"); } dbg("Line sync faild with code: %02x", buf[1]); if ( instance->resynconlost == 1 ) { amedyn_line_down_signal (instance); amedyn_start_synchro(instance); } } if ( buf[0] == 0x01 ) dbg("Line state %02x", buf[1]); else { // printk(KERN_NOTICE "amedyn_get_status return useless info\n"); return ; } switch (buf[1]) { case 0x00: if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; printk(KERN_NOTICE "ADSL line is down\n"); } if ( instance->resynconlost == 1 ) { amedyn_line_down_signal (instance); amedyn_start_synchro(instance); } break; case 0x08: if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; printk(KERN_NOTICE "ADSL line is blocked?\n"); } break; case 0x10: if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; printk(KERN_NOTICE "ADSL line is synchronising\n"); } break; case 0x20: if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) { instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; mod_timer(&instance->poll_timer, jiffies + (5 * HZ)); printk(KERN_NOTICE "ADSL line is up\n"); } break; default: if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; printk(KERN_NOTICE "Unknown line state %02x\n", buf[1]); } break; } } static void amedyn_timer_poll(unsigned long data) { struct amedyn_instance_data *instance = (void *)data; schedule_work(&instance->poll_work); mod_timer(&instance->poll_timer, jiffies + (1 * HZ)); } #ifdef USE_FW_LOADER /* From userspace tool */ int jump_to_address(struct amedyn_instance_data *instance, unsigned int place) { struct usb_device *dev = instance->u.usb_dev; unsigned char buf[6]; /* buffer */ buf[0] = 0x08; // Command (= set base address) buf[1] = 0x04; // Length (= 4 bytes) // Value (base address = place) buf[2] = (place >> 24) & 0xff; buf[3] = (place >> 16) & 0xff; buf[4] = (place >> 8) & 0xff; buf[5] = place & 0xff; if (usb_bulk_msg (dev, usb_sndbulkpipe(dev, AMEDYN_USB_OUT_FIRM), buf, 6, NULL, DATA_TIMEOUT)) return -1; buf[0] = 0x00; // Command (= jump?) buf[1] = 0x01; // Length (= 1 byte) buf[2] = 0x14; // Value (= jump to base address) if (usb_bulk_msg (dev, usb_sndbulkpipe(dev, AMEDYN_USB_OUT_FIRM), buf, 3, NULL, DATA_TIMEOUT)) return -1; return 0; } /* From userspace tool */ /* format a message */ void format_message(int cmd, int ldata, int address, char *bufin) { char buf[8]; /* initial bytes of a message */ memset(buf, 0, sizeof(buf)); buf[0] = cmd & 0xff; /* usb command */ /* address */ buf[2] = address & 0xff; buf[3] = (address >> 8) & 0xff; buf[4] = (address >> 16) & 0xff; buf[5] = (address >> 24) & 0xff; /* data length */ buf[6] = ldata & 0xff; buf[7] = (ldata >> 8) & 0xff; buf[1] = buf[6] + 6; memcpy(bufin, buf, sizeof(buf)); } /* From userspace tool */ int send_block(struct amedyn_instance_data *instance, int place, char *bufin, int len) { struct usb_device *dev = instance->u.usb_dev; char buf[0x1ff]; /* = modem_char.datamax + 8 */ if ((bufin == NULL) || (len > instance->datamax)) return -1; memset(buf, 0, sizeof(buf)); format_message(0x88, len, place, buf); memcpy(buf+8, bufin, len); if (usb_bulk_msg (dev, usb_sndbulkpipe(dev, AMEDYN_USB_OUT_FIRM), buf, len+8, NULL, DATA_TIMEOUT)) return -1; return 0; } static void amedyn_upload_firmware(struct amedyn_instance_data *instance, const struct firmware *fw1, const struct firmware *fw2) { unsigned char *buffer; struct usb_device *usb_dev = instance->u.usb_dev; struct usb_interface *intf; int ret; int offset; unsigned char buf[0x1ff]; char value; /* returned byte */ int i; dbg("amedyn_upload_firmware"); if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { dbg("amedyn_upload_firmware: interface not found!"); goto fail; } if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { dbg("amedyn_upload_firmware: no memory for buffer!"); goto fail; } /* A user-space firmware loader may already have claimed interface #2 */ if ((ret = usb_driver_claim_interface(&amedyn_usb_driver, intf, NULL)) < 0) { dbg("amedyn_upload_firmware: interface in use (%d)!", ret); goto fail_free; } usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, AMEDYN_USB_OUT_FIRM)); usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, AMEDYN_USB_IN_FIRM)); /* Init firmware upload*/ offset = 0; do { int thislen = min_t(int, instance->datamax, fw1->size - offset); memcpy(buf, fw1->data + offset, thislen); if (send_block(instance, offset, buf, thislen)) goto fail_release; offset+=thislen; buf[0] = 0x40; buf[1] = 0x01; buf[2] = 0x12; ret = usb_bulk_msg (usb_dev, usb_sndbulkpipe(usb_dev, AMEDYN_USB_OUT_FIRM), buf, 3, NULL, DATA_TIMEOUT); if (ret < 0) { goto fail_release; dbg("amedyn_upload_firmware: write Init firmware to modem failed (%d)!", ret); } } while (offset < fw1->size ); dbg("amedyn_upload_firmware: Init load"); if (jump_to_address(instance, 0x00000000)) goto fail_release; /* read something needed */ ret = usb_bulk_msg (usb_dev, usb_rcvbulkpipe(usb_dev, AMEDYN_USB_IN_FIRM), buf, 0x1ff, NULL, DATA_TIMEOUT); if (ret < 0) { goto fail_release; dbg("amedyn_upload_firmware: read bufconf failed (%d)!", ret); } memcpy(instance->bufconf, buf+0xb9, 8); dbg("amedyn_upload_firmware: Read bufconf OK"); /* Firmware upload */ offset = 0; do { int thislen = min_t(int, instance->datamax, fw2->size - offset); memcpy(buf, fw2->data + offset, thislen); if (send_block(instance, offset, buf, thislen)) goto fail_release; offset+=thislen; buf[0] = 0x40; buf[1] = 0x01; buf[2] = 0x12; ret = usb_bulk_msg (usb_dev, usb_sndbulkpipe(usb_dev, AMEDYN_USB_OUT_FIRM), buf, 3, NULL, DATA_TIMEOUT); if (ret < 0) { dbg("amedyn_upload_firmware: write Init firmware to modem failed (%d)!", ret); goto fail_release; } } while (offset < fw2->size ); dbg("amedyn_upload_firmware: Firmware load"); if (jump_to_address(instance, 0x00000000)) goto fail_release; msleep(2000); dbg("PostInit..."); /* configure something */ ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 0x0a, AMEDYN_VENDOR_REQUEST_IN, 0x0c, 0x08, buf, 0x01, CTRL_TIMEOUT); if (ret < 1) { dbg("amedyn_upload_firmware: PostInit fail at local urb 1: %d\n", ret); goto fail_release; } value = buf[0]; usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, AMEDYN_USB_IN_INFO)); ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 0x40, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x00, NULL, 0x00, CTRL_TIMEOUT); if (ret < 0) { dbg("amedyn_upload_firmware: PostInit fail at local urb 2: %d\n", ret); goto fail_release; } for (i = 0xc2; i <= 0xcd; i++) { ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), value, AMEDYN_VENDOR_REQUEST_IN, 0x03, i, buf, 0x03, CTRL_TIMEOUT); if (ret < 3) { dbg("amedyn_upload_firmware: PostInit fail at local urb 3: %d\n", ret); goto fail_release; } msleep(100); } /* success */ dbg("amedyn_upload_firmware: firmware upload OK"); /* Delay to allow firmware to start up. We can do this here because we're in our own kernel thread anyway. */ msleep(1000); /* Start modem synchronisation */ if (amedyn_start_synchro(instance)) dbg("amedyn_start_synchro: failed"); amedyn_got_firmware(instance, 1); free_page((unsigned long)buffer); return; fail_release: /* Only release interface #2 if uploading failed; we don't release it we succeeded. This prevents the userspace tools from trying to load the firmware themselves */ usb_driver_release_interface(&amedyn_usb_driver, intf); fail_free: free_page((unsigned long)buffer); fail: amedyn_got_firmware(instance, 0); } static int amedyn_find_firmware(struct amedyn_instance_data *instance, char* phase, const struct firmware **fw_p) { char buf[16]; sprintf(buf, "%s", phase); dbg("amedyn_find_firmware: looking for %s", phase); if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { dev_warn(&instance->u.usb_dev->dev, "no stage %s firmware found\n", phase); return -ENOENT; } dev_info(&instance->u.usb_dev->dev, "found firmware %s\n", buf); return 0; } static int amedyn_load_firmware(void *arg) { const struct firmware *fw1, *fw2; struct amedyn_instance_data *instance = arg; dbg("amedyn_load_firmware"); BUG_ON(!instance); daemonize("firmware/amedyn"); if (!amedyn_find_firmware(instance, instance->initfirmfile, &fw1)) { if (!amedyn_find_firmware(instance, instance->firmfile, &fw2)) { amedyn_upload_firmware(instance, fw1, fw2); release_firmware(fw2); } release_firmware(fw1); } /* In case we failed, set state back to NO_FIRMWARE so that another later attempt may work. Otherwise, we never actually manage to recover if, for example, the firmware is on /usr and we look for it too early. */ amedyn_got_firmware(instance, 0); module_put(THIS_MODULE); udsl_put_instance(&instance->u); return 0; } #endif /* USE_FW_LOADER */ static void amedyn_firmware_start(struct amedyn_instance_data *instance) { #ifdef USE_FW_LOADER int ret; #endif dbg("amedyn_firmware_start"); down(&instance->u.serialize); /* vs self, amedyn_got_firmware */ if (instance->u.status >= UDSL_LOADING_FIRMWARE) { up(&instance->u.serialize); dbg("amedyn_firmware_start: instance->u.status >= UDSL_LOADING_FIRMWARE"); return; } instance->u.status = UDSL_LOADING_FIRMWARE; up(&instance->u.serialize); #ifdef USE_FW_LOADER dbg("amedyn_firmware_start: looking for firmware"); udsl_get_instance(&instance->u); dbg("amedyn_firmware_start: looking for firmware."); try_module_get(THIS_MODULE); dbg("amedyn_firmware_start: looking for firmware.."); ret = kernel_thread(amedyn_load_firmware, instance, CLONE_FS | CLONE_FILES); dbg("amedyn_firmware_start: looking for firmware..."); if (ret >= 0) return; /* OK */ dbg("amedyn_firmware_start: kernel_thread failed (%d)!", ret); module_put(THIS_MODULE); udsl_put_instance(&instance->u); /* Just pretend it never happened... hope modem_run happens */ #endif /* USE_FW_LOADER */ amedyn_got_firmware(instance, 0); } static int amedyn_firmware_wait(struct udsl_instance_data *instance) { amedyn_firmware_start((void *)instance); if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) return -ERESTARTSYS; return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN; } /********** ** USB ** **********/ static int amedyn_usb_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) { struct amedyn_instance_data *instance = usb_get_intfdata(intf); dbg("amedyn_usb_ioctl entered"); if (!instance) { dbg("amedyn_usb_ioctl: NULL instance!"); return -ENODEV; } switch (code) { case UDSL_IOCTL_LINE_UP: dbg("amedyn_usb_ioctl: UDSL_IOCTL_LINE_UP"); instance->resynconlost = 1; amedyn_got_firmware(instance, 1); return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO; case UDSL_IOCTL_LINE_DOWN: dbg("amedyn_usb_ioctl: UDSL_IOCTL_LINE_DOWN"); instance->resynconlost = 0; return amedyn_line_down_signal (instance); default: return -ENOTTY; } } static int amedyn_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); int ifnum = intf->altsetting->desc.bInterfaceNumber; struct amedyn_instance_data *instance; unsigned char mac_str[13]; int i; char *buf[0x20]; int ret; dbg("amedyn_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || (ifnum != 1)) return -ENODEV; if ( !((dev->descriptor.idVendor == AME_VENDORID2) && (dev->descriptor.idProduct == AME_PRODUCTID2)) && !((dev->descriptor.idVendor == AME_VENDORID3) && (dev->descriptor.idProduct == AME_PRODUCTID3)) ) return -ENODEV; dbg("amedyn_usb_probe: device accepted"); /* instance init */ instance = kmalloc(sizeof(*instance), GFP_KERNEL); if (!instance) { dbg("amedyn_usb_probe: no memory for instance data!"); return -ENOMEM; } memset(instance, 0, sizeof(struct amedyn_instance_data)); if ((ret = usb_set_interface(dev, 0, 0)) < 0) goto fail; if ((ret = usb_set_interface(dev, 2, 0)) < 0) goto fail; instance->u.data_endpoint = AMEDYN_ENDPOINT_DATA_IN; instance->u.firmware_wait = amedyn_firmware_wait; instance->u.driver_name = amedyn_driver_name; if ( linetype == ANALOG || linetype == ISDN) instance->linetype = linetype; else { dbg("using default line type - 0x15 (analog)"); instance->linetype = ANALOG; } instance->resynconlost = 1; instance->initfirmfile = "Init-usb.bin"; if (dev->descriptor.idProduct == AME_PRODUCTID2) { dbg("Config for modem type 2 (Zyxel)"); instance->datamax = 0x1a0; instance->firmfile = "fw-usb.bin"; } if (dev->descriptor.idProduct == AME_PRODUCTID3) { dbg("Config for modem type 3 (AAM600UG)"); instance->datamax = 0x1f2; instance->firmfile = "Fw-usb_A.bin"; } ret = udsl_instance_setup(dev, &instance->u); if (ret) goto fail; init_timer(&instance->poll_timer); instance->poll_timer.function = amedyn_timer_poll; instance->poll_timer.data = (unsigned long)instance; INIT_WORK(&instance->poll_work, (void *)amedyn_poll_status, instance); /* set MAC address, it is stored in the serial number */ memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi)); if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { for (i = 0; i < 6; i++) instance->u.atm_dev->esi[i] = (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1])); } /* First check whether the modem already seems to be alive */ ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, AMEDYN_USB_IN_INFO), buf, 0x10, NULL, DATA_TIMEOUT); if (ret >= 0) { dbg("firmware appears to be already loaded"); amedyn_got_firmware(instance, 1); amedyn_poll_status(instance); } else { dbg("firmware appears to NOT be already loaded"); amedyn_firmware_start(instance); } usb_set_intfdata(intf, instance); return 0; fail: kfree(instance); return -ENOMEM; } static void amedyn_usb_disconnect(struct usb_interface *intf) { struct amedyn_instance_data *instance = usb_get_intfdata(intf); dbg("amedyn_usb_disconnect entered"); if (!instance) { dbg("amedyn_usb_disconnect: NULL instance!"); return; } del_timer_sync(&instance->poll_timer); wmb(); flush_scheduled_work(); udsl_instance_disconnect(&instance->u); /* clean up */ usb_set_intfdata(intf, NULL); udsl_put_instance(&instance->u); } /*********** ** init ** ***********/ static int __init amedyn_usb_init(void) { dbg("amedyn_usb_init: driver version " DRIVER_VERSION); #ifdef USE_FW_LOADER dbg("amedyn_usb_init: In kernel firmware upload"); #else dbg("amedyn_usb_init: NOT in kernel firmware upload"); #endif return usb_register(&amedyn_usb_driver); } static void __exit amedyn_usb_cleanup(void) { dbg("amedyn_usb_cleanup entered"); usb_deregister(&amedyn_usb_driver); } module_init(amedyn_usb_init); module_exit(amedyn_usb_cleanup); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(DRIVER_VERSION); ______________________________________________ Renovamos el Correo Yahoo!: ˇ250 MB GRATIS! Nuevos servicios, más seguridad http://correo.yahoo.es ------------------------------------------------------- The SF.Net email is sponsored by: Beat the post-holiday blues Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek. It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel