11da177e4SLinus Torvalds /****************************************************************************** 21da177e4SLinus Torvalds * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2001, Alcatel 51da177e4SLinus Torvalds * Copyright (C) 2003, Duncan Sands 61da177e4SLinus Torvalds * Copyright (C) 2004, David Woodhouse 71da177e4SLinus Torvalds * 848da7267SDuncan Sands * Based on "modem_run.c", copyright (C) 2001, Benoit Papillault 948da7267SDuncan Sands * 101da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify it 111da177e4SLinus Torvalds * under the terms of the GNU General Public License as published by the Free 121da177e4SLinus Torvalds * Software Foundation; either version 2 of the License, or (at your option) 131da177e4SLinus Torvalds * any later version. 141da177e4SLinus Torvalds * 151da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, but WITHOUT 161da177e4SLinus Torvalds * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 171da177e4SLinus Torvalds * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 181da177e4SLinus Torvalds * more details. 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License along with 211da177e4SLinus Torvalds * this program; if not, write to the Free Software Foundation, Inc., 59 221da177e4SLinus Torvalds * Temple Place - Suite 330, Boston, MA 02111-1307, USA. 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds ******************************************************************************/ 251da177e4SLinus Torvalds 2648da7267SDuncan Sands #include <asm/page.h> 2748da7267SDuncan Sands #include <linux/device.h> 2848da7267SDuncan Sands #include <linux/errno.h> 2948da7267SDuncan Sands #include <linux/firmware.h> 3048da7267SDuncan Sands #include <linux/kernel.h> 311da177e4SLinus Torvalds #include <linux/module.h> 321da177e4SLinus Torvalds #include <linux/moduleparam.h> 331da177e4SLinus Torvalds #include <linux/slab.h> 3448da7267SDuncan Sands #include <linux/stat.h> 3548da7267SDuncan Sands #include <linux/timer.h> 3680aae7a1SDuncan Sands #include <linux/types.h> 375f848137SDavid Brownell #include <linux/usb/ch9.h> 3848da7267SDuncan Sands #include <linux/workqueue.h> 391da177e4SLinus Torvalds 4048da7267SDuncan Sands #include "usbatm.h" 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds #define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>" 439acd6b2aSGreg Kroah-Hartman #define DRIVER_DESC "Alcatel SpeedTouch USB driver" 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds static const char speedtch_driver_name[] = "speedtch"; 461da177e4SLinus Torvalds 4748da7267SDuncan Sands #define CTRL_TIMEOUT 2000 /* milliseconds */ 4848da7267SDuncan Sands #define DATA_TIMEOUT 2000 /* milliseconds */ 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds #define OFFSET_7 0 /* size 1 */ 511da177e4SLinus Torvalds #define OFFSET_b 1 /* size 8 */ 521da177e4SLinus Torvalds #define OFFSET_d 9 /* size 4 */ 531da177e4SLinus Torvalds #define OFFSET_e 13 /* size 1 */ 541da177e4SLinus Torvalds #define OFFSET_f 14 /* size 1 */ 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds #define SIZE_7 1 571da177e4SLinus Torvalds #define SIZE_b 8 581da177e4SLinus Torvalds #define SIZE_d 4 591da177e4SLinus Torvalds #define SIZE_e 1 601da177e4SLinus Torvalds #define SIZE_f 1 611da177e4SLinus Torvalds 6248da7267SDuncan Sands #define MIN_POLL_DELAY 5000 /* milliseconds */ 6348da7267SDuncan Sands #define MAX_POLL_DELAY 60000 /* milliseconds */ 641da177e4SLinus Torvalds 6548da7267SDuncan Sands #define RESUBMIT_DELAY 1000 /* milliseconds */ 661da177e4SLinus Torvalds 6780aae7a1SDuncan Sands #define DEFAULT_BULK_ALTSETTING 1 6867c752b4SDuncan Sands #define DEFAULT_ISOC_ALTSETTING 3 6948da7267SDuncan Sands #define DEFAULT_DL_512_FIRST 0 7080aae7a1SDuncan Sands #define DEFAULT_ENABLE_ISOC 0 7148da7267SDuncan Sands #define DEFAULT_SW_BUFFERING 0 721da177e4SLinus Torvalds 7380aae7a1SDuncan Sands static unsigned int altsetting = 0; /* zero means: use the default */ 7490ab5ee9SRusty Russell static bool dl_512_first = DEFAULT_DL_512_FIRST; 7590ab5ee9SRusty Russell static bool enable_isoc = DEFAULT_ENABLE_ISOC; 7690ab5ee9SRusty Russell static bool sw_buffering = DEFAULT_SW_BUFFERING; 771da177e4SLinus Torvalds 786a4f1b41SDuncan Sands #define DEFAULT_B_MAX_DSL 8128 796a4f1b41SDuncan Sands #define DEFAULT_MODEM_MODE 11 806a4f1b41SDuncan Sands #define MODEM_OPTION_LENGTH 16 816a4f1b41SDuncan Sands static const unsigned char DEFAULT_MODEM_OPTION[MODEM_OPTION_LENGTH] = { 826a4f1b41SDuncan Sands 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 836a4f1b41SDuncan Sands }; 846a4f1b41SDuncan Sands 856a4f1b41SDuncan Sands static unsigned int BMaxDSL = DEFAULT_B_MAX_DSL; 866a4f1b41SDuncan Sands static unsigned char ModemMode = DEFAULT_MODEM_MODE; 876a4f1b41SDuncan Sands static unsigned char ModemOption[MODEM_OPTION_LENGTH]; 8864a6f950SAl Viro static unsigned int num_ModemOption; 896a4f1b41SDuncan Sands 9080aae7a1SDuncan Sands module_param(altsetting, uint, S_IRUGO | S_IWUSR); 9148da7267SDuncan Sands MODULE_PARM_DESC(altsetting, 9280aae7a1SDuncan Sands "Alternative setting for data interface (bulk_default: " 9380aae7a1SDuncan Sands __MODULE_STRING(DEFAULT_BULK_ALTSETTING) "; isoc_default: " 9480aae7a1SDuncan Sands __MODULE_STRING(DEFAULT_ISOC_ALTSETTING) ")"); 9548da7267SDuncan Sands 9648da7267SDuncan Sands module_param(dl_512_first, bool, S_IRUGO | S_IWUSR); 9748da7267SDuncan Sands MODULE_PARM_DESC(dl_512_first, 9848da7267SDuncan Sands "Read 512 bytes before sending firmware (default: " 9948da7267SDuncan Sands __MODULE_STRING(DEFAULT_DL_512_FIRST) ")"); 10048da7267SDuncan Sands 10180aae7a1SDuncan Sands module_param(enable_isoc, bool, S_IRUGO | S_IWUSR); 10280aae7a1SDuncan Sands MODULE_PARM_DESC(enable_isoc, 10380aae7a1SDuncan Sands "Use isochronous transfers if available (default: " 10480aae7a1SDuncan Sands __MODULE_STRING(DEFAULT_ENABLE_ISOC) ")"); 10580aae7a1SDuncan Sands 10648da7267SDuncan Sands module_param(sw_buffering, bool, S_IRUGO | S_IWUSR); 10748da7267SDuncan Sands MODULE_PARM_DESC(sw_buffering, 10848da7267SDuncan Sands "Enable software buffering (default: " 10948da7267SDuncan Sands __MODULE_STRING(DEFAULT_SW_BUFFERING) ")"); 11048da7267SDuncan Sands 1116a4f1b41SDuncan Sands module_param(BMaxDSL, uint, S_IRUGO | S_IWUSR); 1126a4f1b41SDuncan Sands MODULE_PARM_DESC(BMaxDSL, 1136a4f1b41SDuncan Sands "default: " __MODULE_STRING(DEFAULT_B_MAX_DSL)); 1146a4f1b41SDuncan Sands 1156a4f1b41SDuncan Sands module_param(ModemMode, byte, S_IRUGO | S_IWUSR); 1166a4f1b41SDuncan Sands MODULE_PARM_DESC(ModemMode, 1176a4f1b41SDuncan Sands "default: " __MODULE_STRING(DEFAULT_MODEM_MODE)); 1186a4f1b41SDuncan Sands 1196a4f1b41SDuncan Sands module_param_array(ModemOption, byte, &num_ModemOption, S_IRUGO); 1206a4f1b41SDuncan Sands MODULE_PARM_DESC(ModemOption, "default: 0x10,0x00,0x00,0x00,0x20"); 1216a4f1b41SDuncan Sands 1226f749475SDuncan Sands #define INTERFACE_DATA 1 12348da7267SDuncan Sands #define ENDPOINT_INT 0x81 12480aae7a1SDuncan Sands #define ENDPOINT_BULK_DATA 0x07 12580aae7a1SDuncan Sands #define ENDPOINT_ISOC_DATA 0x07 12648da7267SDuncan Sands #define ENDPOINT_FIRMWARE 0x05 1271da177e4SLinus Torvalds 1286a4f1b41SDuncan Sands struct speedtch_params { 1296a4f1b41SDuncan Sands unsigned int altsetting; 1306a4f1b41SDuncan Sands unsigned int BMaxDSL; 1316a4f1b41SDuncan Sands unsigned char ModemMode; 1326a4f1b41SDuncan Sands unsigned char ModemOption[MODEM_OPTION_LENGTH]; 1336a4f1b41SDuncan Sands }; 1346a4f1b41SDuncan Sands 1351da177e4SLinus Torvalds struct speedtch_instance_data { 13648da7267SDuncan Sands struct usbatm_data *usbatm; 1371da177e4SLinus Torvalds 1386a4f1b41SDuncan Sands struct speedtch_params params; /* set in probe, constant afterwards */ 1396f749475SDuncan Sands 14037c95bfeSTejun Heo struct timer_list status_check_timer; 14137c95bfeSTejun Heo struct work_struct status_check_work; 14248da7267SDuncan Sands 1431a7aad15SDuncan Sands unsigned char last_status; 1441a7aad15SDuncan Sands 14548da7267SDuncan Sands int poll_delay; /* milliseconds */ 14648da7267SDuncan Sands 14748da7267SDuncan Sands struct timer_list resubmit_timer; 1481da177e4SLinus Torvalds struct urb *int_urb; 1491da177e4SLinus Torvalds unsigned char int_data[16]; 1501da177e4SLinus Torvalds 1516a4f1b41SDuncan Sands unsigned char scratch_buffer[16]; 1521da177e4SLinus Torvalds }; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds /*************** 1551da177e4SLinus Torvalds ** firmware ** 1561da177e4SLinus Torvalds ***************/ 1571da177e4SLinus Torvalds 15848da7267SDuncan Sands static void speedtch_set_swbuff(struct speedtch_instance_data *instance, int state) 1591da177e4SLinus Torvalds { 16048da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 16148da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 1621da177e4SLinus Torvalds int ret; 1631da177e4SLinus Torvalds 16448da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 16548da7267SDuncan Sands 0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, CTRL_TIMEOUT); 16648da7267SDuncan Sands if (ret < 0) 16748da7267SDuncan Sands usb_warn(usbatm, 16848da7267SDuncan Sands "%sabling SW buffering: usb_control_msg returned %d\n", 1691da177e4SLinus Torvalds state ? "En" : "Dis", ret); 17048da7267SDuncan Sands else 171d8995425SGreg Kroah-Hartman usb_dbg(usbatm, "speedtch_set_swbuff: %sbled SW buffering\n", state ? "En" : "Dis"); 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds static void speedtch_test_sequence(struct speedtch_instance_data *instance) 1751da177e4SLinus Torvalds { 17648da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 17748da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 17848da7267SDuncan Sands unsigned char *buf = instance->scratch_buffer; 1791da177e4SLinus Torvalds int ret; 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds /* URB 147 */ 1821da177e4SLinus Torvalds buf[0] = 0x1c; 1831da177e4SLinus Torvalds buf[1] = 0x50; 18448da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 18548da7267SDuncan Sands 0x01, 0x40, 0x0b, 0x00, buf, 2, CTRL_TIMEOUT); 1861da177e4SLinus Torvalds if (ret < 0) 18748da7267SDuncan Sands usb_warn(usbatm, "%s failed on URB147: %d\n", __func__, ret); 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds /* URB 148 */ 1901da177e4SLinus Torvalds buf[0] = 0x32; 1911da177e4SLinus Torvalds buf[1] = 0x00; 19248da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 19348da7267SDuncan Sands 0x01, 0x40, 0x02, 0x00, buf, 2, CTRL_TIMEOUT); 1941da177e4SLinus Torvalds if (ret < 0) 19548da7267SDuncan Sands usb_warn(usbatm, "%s failed on URB148: %d\n", __func__, ret); 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds /* URB 149 */ 1981da177e4SLinus Torvalds buf[0] = 0x01; 1991da177e4SLinus Torvalds buf[1] = 0x00; 2001da177e4SLinus Torvalds buf[2] = 0x01; 20148da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 20248da7267SDuncan Sands 0x01, 0x40, 0x03, 0x00, buf, 3, CTRL_TIMEOUT); 2031da177e4SLinus Torvalds if (ret < 0) 20448da7267SDuncan Sands usb_warn(usbatm, "%s failed on URB149: %d\n", __func__, ret); 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds /* URB 150 */ 2071da177e4SLinus Torvalds buf[0] = 0x01; 2081da177e4SLinus Torvalds buf[1] = 0x00; 2091da177e4SLinus Torvalds buf[2] = 0x01; 21048da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 21148da7267SDuncan Sands 0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT); 2121da177e4SLinus Torvalds if (ret < 0) 21348da7267SDuncan Sands usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret); 2146a4f1b41SDuncan Sands 2156a4f1b41SDuncan Sands /* Extra initialisation in recent drivers - gives higher speeds */ 2166a4f1b41SDuncan Sands 2176a4f1b41SDuncan Sands /* URBext1 */ 2186a4f1b41SDuncan Sands buf[0] = instance->params.ModemMode; 2196a4f1b41SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 2206a4f1b41SDuncan Sands 0x01, 0x40, 0x11, 0x00, buf, 1, CTRL_TIMEOUT); 2216a4f1b41SDuncan Sands if (ret < 0) 2226a4f1b41SDuncan Sands usb_warn(usbatm, "%s failed on URBext1: %d\n", __func__, ret); 2236a4f1b41SDuncan Sands 2246a4f1b41SDuncan Sands /* URBext2 */ 2256a4f1b41SDuncan Sands /* This seems to be the one which actually triggers the higher sync 2266a4f1b41SDuncan Sands rate -- it does require the new firmware too, although it works OK 2276a4f1b41SDuncan Sands with older firmware */ 2286a4f1b41SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 2296a4f1b41SDuncan Sands 0x01, 0x40, 0x14, 0x00, 2306a4f1b41SDuncan Sands instance->params.ModemOption, 2316a4f1b41SDuncan Sands MODEM_OPTION_LENGTH, CTRL_TIMEOUT); 2326a4f1b41SDuncan Sands if (ret < 0) 2336a4f1b41SDuncan Sands usb_warn(usbatm, "%s failed on URBext2: %d\n", __func__, ret); 2346a4f1b41SDuncan Sands 2356a4f1b41SDuncan Sands /* URBext3 */ 2366a4f1b41SDuncan Sands buf[0] = instance->params.BMaxDSL & 0xff; 2376a4f1b41SDuncan Sands buf[1] = instance->params.BMaxDSL >> 8; 2386a4f1b41SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 2396a4f1b41SDuncan Sands 0x01, 0x40, 0x12, 0x00, buf, 2, CTRL_TIMEOUT); 2406a4f1b41SDuncan Sands if (ret < 0) 2416a4f1b41SDuncan Sands usb_warn(usbatm, "%s failed on URBext3: %d\n", __func__, ret); 2421da177e4SLinus Torvalds } 2431da177e4SLinus Torvalds 24448da7267SDuncan Sands static int speedtch_upload_firmware(struct speedtch_instance_data *instance, 2451da177e4SLinus Torvalds const struct firmware *fw1, 2461da177e4SLinus Torvalds const struct firmware *fw2) 2471da177e4SLinus Torvalds { 2481da177e4SLinus Torvalds unsigned char *buffer; 24948da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 25048da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 25148da7267SDuncan Sands int actual_length; 25248da7267SDuncan Sands int ret = 0; 2531da177e4SLinus Torvalds int offset; 2541da177e4SLinus Torvalds 25548da7267SDuncan Sands usb_dbg(usbatm, "%s entered\n", __func__); 2561da177e4SLinus Torvalds 2573383ee4cSGreg Kroah-Hartman buffer = (unsigned char *)__get_free_page(GFP_KERNEL); 2583383ee4cSGreg Kroah-Hartman if (!buffer) { 25948da7267SDuncan Sands ret = -ENOMEM; 26048da7267SDuncan Sands usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__); 26148da7267SDuncan Sands goto out; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 264011db815SMicah Gruber if (!usb_ifnum_to_if(usb_dev, 2)) { 26548da7267SDuncan Sands ret = -ENODEV; 26648da7267SDuncan Sands usb_dbg(usbatm, "%s: interface not found!\n", __func__); 26748da7267SDuncan Sands goto out_free; 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds /* URB 7 */ 2711da177e4SLinus Torvalds if (dl_512_first) { /* some modems need a read before writing the firmware */ 27248da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2731da177e4SLinus Torvalds buffer, 0x200, &actual_length, 2000); 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds if (ret < 0 && ret != -ETIMEDOUT) 2760ec3c7e8SDuncan Sands usb_warn(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret); 2771da177e4SLinus Torvalds else 27848da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret); 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds /* URB 8 : both leds are static green */ 2821da177e4SLinus Torvalds for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { 2831da177e4SLinus Torvalds int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); 2841da177e4SLinus Torvalds memcpy(buffer, fw1->data + offset, thislen); 2851da177e4SLinus Torvalds 28648da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2871da177e4SLinus Torvalds buffer, thislen, &actual_length, DATA_TIMEOUT); 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds if (ret < 0) { 2900ec3c7e8SDuncan Sands usb_err(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret); 29148da7267SDuncan Sands goto out_free; 2921da177e4SLinus Torvalds } 29348da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size); 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds /* USB led blinking green, ADSL led off */ 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds /* URB 11 */ 29948da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 3001da177e4SLinus Torvalds buffer, 0x200, &actual_length, DATA_TIMEOUT); 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds if (ret < 0) { 3030ec3c7e8SDuncan Sands usb_err(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret); 30448da7267SDuncan Sands goto out_free; 3051da177e4SLinus Torvalds } 30648da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length); 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds /* URBs 12 to 139 - USB led blinking green, ADSL led off */ 3091da177e4SLinus Torvalds for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { 3101da177e4SLinus Torvalds int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); 3111da177e4SLinus Torvalds memcpy(buffer, fw2->data + offset, thislen); 3121da177e4SLinus Torvalds 31348da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 3141da177e4SLinus Torvalds buffer, thislen, &actual_length, DATA_TIMEOUT); 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds if (ret < 0) { 3170ec3c7e8SDuncan Sands usb_err(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret); 31848da7267SDuncan Sands goto out_free; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds } 32148da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK3 uploaded (%zu bytes)\n", __func__, fw2->size); 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds /* USB led static green, ADSL led static red */ 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds /* URB 142 */ 32648da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 3271da177e4SLinus Torvalds buffer, 0x200, &actual_length, DATA_TIMEOUT); 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds if (ret < 0) { 3300ec3c7e8SDuncan Sands usb_err(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret); 33148da7267SDuncan Sands goto out_free; 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds /* success */ 33548da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK4 downloaded (%d bytes)\n", __func__, actual_length); 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds /* Delay to allow firmware to start up. We can do this here 3381da177e4SLinus Torvalds because we're in our own kernel thread anyway. */ 33948da7267SDuncan Sands msleep_interruptible(1000); 3401da177e4SLinus Torvalds 3416a4f1b41SDuncan Sands if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) { 3426a4f1b41SDuncan Sands usb_err(usbatm, "%s: setting interface to %d failed (%d)!\n", __func__, instance->params.altsetting, ret); 3436f749475SDuncan Sands goto out_free; 3446f749475SDuncan Sands } 3456f749475SDuncan Sands 3461da177e4SLinus Torvalds /* Enable software buffering, if requested */ 3471da177e4SLinus Torvalds if (sw_buffering) 3481da177e4SLinus Torvalds speedtch_set_swbuff(instance, 1); 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds /* Magic spell; don't ask us what this does */ 3511da177e4SLinus Torvalds speedtch_test_sequence(instance); 3521da177e4SLinus Torvalds 35348da7267SDuncan Sands ret = 0; 3541da177e4SLinus Torvalds 35548da7267SDuncan Sands out_free: 3561da177e4SLinus Torvalds free_page((unsigned long)buffer); 35748da7267SDuncan Sands out: 35848da7267SDuncan Sands return ret; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 3610ec3c7e8SDuncan Sands static int speedtch_find_firmware(struct usbatm_data *usbatm, struct usb_interface *intf, 3620ec3c7e8SDuncan Sands int phase, const struct firmware **fw_p) 3631da177e4SLinus Torvalds { 36448da7267SDuncan Sands struct device *dev = &intf->dev; 36548da7267SDuncan Sands const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice); 3661da177e4SLinus Torvalds const u8 major_revision = bcdDevice >> 8; 3671da177e4SLinus Torvalds const u8 minor_revision = bcdDevice & 0xff; 36848da7267SDuncan Sands char buf[24]; 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); 3710ec3c7e8SDuncan Sands usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 3721da177e4SLinus Torvalds 37348da7267SDuncan Sands if (request_firmware(fw_p, buf, dev)) { 3741da177e4SLinus Torvalds sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); 3750ec3c7e8SDuncan Sands usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 3761da177e4SLinus Torvalds 37748da7267SDuncan Sands if (request_firmware(fw_p, buf, dev)) { 3781da177e4SLinus Torvalds sprintf(buf, "speedtch-%d.bin", phase); 3790ec3c7e8SDuncan Sands usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 3801da177e4SLinus Torvalds 38148da7267SDuncan Sands if (request_firmware(fw_p, buf, dev)) { 3820ec3c7e8SDuncan Sands usb_err(usbatm, "%s: no stage %d firmware found!\n", __func__, phase); 3831da177e4SLinus Torvalds return -ENOENT; 3841da177e4SLinus Torvalds } 3851da177e4SLinus Torvalds } 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds 3880ec3c7e8SDuncan Sands usb_info(usbatm, "found stage %d firmware %s\n", phase, buf); 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds return 0; 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds 39348da7267SDuncan Sands static int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface *intf) 3941da177e4SLinus Torvalds { 3951da177e4SLinus Torvalds const struct firmware *fw1, *fw2; 39648da7267SDuncan Sands struct speedtch_instance_data *instance = usbatm->driver_data; 39748da7267SDuncan Sands int ret; 3981da177e4SLinus Torvalds 3990ec3c7e8SDuncan Sands if ((ret = speedtch_find_firmware(usbatm, intf, 1, &fw1)) < 0) 40048da7267SDuncan Sands return ret; 4011da177e4SLinus Torvalds 4020ec3c7e8SDuncan Sands if ((ret = speedtch_find_firmware(usbatm, intf, 2, &fw2)) < 0) { 4031da177e4SLinus Torvalds release_firmware(fw1); 40448da7267SDuncan Sands return ret; 4051da177e4SLinus Torvalds } 4061da177e4SLinus Torvalds 4070ec3c7e8SDuncan Sands if ((ret = speedtch_upload_firmware(instance, fw1, fw2)) < 0) 4080ec3c7e8SDuncan Sands usb_err(usbatm, "%s: firmware upload failed (%d)!\n", __func__, ret); 4091da177e4SLinus Torvalds 41048da7267SDuncan Sands release_firmware(fw2); 41148da7267SDuncan Sands release_firmware(fw1); 41248da7267SDuncan Sands 41348da7267SDuncan Sands return ret; 41448da7267SDuncan Sands } 41548da7267SDuncan Sands 41648da7267SDuncan Sands 41748da7267SDuncan Sands /********** 41848da7267SDuncan Sands ** ATM ** 41948da7267SDuncan Sands **********/ 42048da7267SDuncan Sands 42148da7267SDuncan Sands static int speedtch_read_status(struct speedtch_instance_data *instance) 42248da7267SDuncan Sands { 42348da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 42448da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 42548da7267SDuncan Sands unsigned char *buf = instance->scratch_buffer; 42648da7267SDuncan Sands int ret; 42748da7267SDuncan Sands 4286a4f1b41SDuncan Sands memset(buf, 0, 16); 42948da7267SDuncan Sands 43048da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 43148da7267SDuncan Sands 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7, 43248da7267SDuncan Sands CTRL_TIMEOUT); 43348da7267SDuncan Sands if (ret < 0) { 43448da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG 7 failed\n", __func__); 43548da7267SDuncan Sands return ret; 43648da7267SDuncan Sands } 43748da7267SDuncan Sands 43848da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 43948da7267SDuncan Sands 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b, 44048da7267SDuncan Sands CTRL_TIMEOUT); 44148da7267SDuncan Sands if (ret < 0) { 44248da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG B failed\n", __func__); 44348da7267SDuncan Sands return ret; 44448da7267SDuncan Sands } 44548da7267SDuncan Sands 44648da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 44748da7267SDuncan Sands 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d, 44848da7267SDuncan Sands CTRL_TIMEOUT); 44948da7267SDuncan Sands if (ret < 0) { 45048da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG D failed\n", __func__); 45148da7267SDuncan Sands return ret; 45248da7267SDuncan Sands } 45348da7267SDuncan Sands 45448da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 45548da7267SDuncan Sands 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e, 45648da7267SDuncan Sands CTRL_TIMEOUT); 45748da7267SDuncan Sands if (ret < 0) { 45848da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG E failed\n", __func__); 45948da7267SDuncan Sands return ret; 46048da7267SDuncan Sands } 46148da7267SDuncan Sands 46248da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 46348da7267SDuncan Sands 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f, 46448da7267SDuncan Sands CTRL_TIMEOUT); 46548da7267SDuncan Sands if (ret < 0) { 46648da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG F failed\n", __func__); 46748da7267SDuncan Sands return ret; 46848da7267SDuncan Sands } 46948da7267SDuncan Sands 4701da177e4SLinus Torvalds return 0; 4711da177e4SLinus Torvalds } 4721da177e4SLinus Torvalds 47348da7267SDuncan Sands static int speedtch_start_synchro(struct speedtch_instance_data *instance) 4741da177e4SLinus Torvalds { 47548da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 47648da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 47748da7267SDuncan Sands unsigned char *buf = instance->scratch_buffer; 4781da177e4SLinus Torvalds int ret; 4791da177e4SLinus Torvalds 48048da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 4811da177e4SLinus Torvalds 48248da7267SDuncan Sands memset(buf, 0, 2); 4831da177e4SLinus Torvalds 48448da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 48548da7267SDuncan Sands 0x12, 0xc0, 0x04, 0x00, 48648da7267SDuncan Sands buf, 2, CTRL_TIMEOUT); 48748da7267SDuncan Sands 48848da7267SDuncan Sands if (ret < 0) 48948da7267SDuncan Sands atm_warn(usbatm, "failed to start ADSL synchronisation: %d\n", ret); 49048da7267SDuncan Sands else 49148da7267SDuncan Sands atm_dbg(usbatm, "%s: modem prodded. %d bytes returned: %02x %02x\n", 49248da7267SDuncan Sands __func__, ret, buf[0], buf[1]); 49348da7267SDuncan Sands 49448da7267SDuncan Sands return ret; 49548da7267SDuncan Sands } 49648da7267SDuncan Sands 497c4028958SDavid Howells static void speedtch_check_status(struct work_struct *work) 49848da7267SDuncan Sands { 499c4028958SDavid Howells struct speedtch_instance_data *instance = 500c4028958SDavid Howells container_of(work, struct speedtch_instance_data, 50137c95bfeSTejun Heo status_check_work); 50248da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 50348da7267SDuncan Sands struct atm_dev *atm_dev = usbatm->atm_dev; 50448da7267SDuncan Sands unsigned char *buf = instance->scratch_buffer; 5051a7aad15SDuncan Sands int down_speed, up_speed, ret; 5061a7aad15SDuncan Sands unsigned char status; 50748da7267SDuncan Sands 5080ec3c7e8SDuncan Sands #ifdef VERBOSE_DEBUG 50948da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 5100ec3c7e8SDuncan Sands #endif 51148da7267SDuncan Sands 51248da7267SDuncan Sands ret = speedtch_read_status(instance); 51348da7267SDuncan Sands if (ret < 0) { 51448da7267SDuncan Sands atm_warn(usbatm, "error %d fetching device status\n", ret); 515cd5c08fbSDuncan Sands instance->poll_delay = min(2 * instance->poll_delay, MAX_POLL_DELAY); 5161da177e4SLinus Torvalds return; 5171da177e4SLinus Torvalds } 5181da177e4SLinus Torvalds 519cd5c08fbSDuncan Sands instance->poll_delay = max(instance->poll_delay / 2, MIN_POLL_DELAY); 5201da177e4SLinus Torvalds 5211a7aad15SDuncan Sands status = buf[OFFSET_7]; 5221da177e4SLinus Torvalds 5231a7aad15SDuncan Sands if ((status != instance->last_status) || !status) { 5240ec3c7e8SDuncan Sands atm_dbg(usbatm, "%s: line state 0x%02x\n", __func__, status); 5250ec3c7e8SDuncan Sands 5261a7aad15SDuncan Sands switch (status) { 52748da7267SDuncan Sands case 0: 52823f89f04SKarl Hiramoto atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 5291a7aad15SDuncan Sands if (instance->last_status) 53052fbae2aSDavid S. Miller atm_info(usbatm, "ADSL line is down\n"); 5311a7aad15SDuncan Sands /* It may never resync again unless we ask it to... */ 53248da7267SDuncan Sands ret = speedtch_start_synchro(instance); 53348da7267SDuncan Sands break; 5341da177e4SLinus Torvalds 53548da7267SDuncan Sands case 0x08: 53623f89f04SKarl Hiramoto atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN); 53752fbae2aSDavid S. Miller atm_info(usbatm, "ADSL line is blocked?\n"); 53848da7267SDuncan Sands break; 5391da177e4SLinus Torvalds 54048da7267SDuncan Sands case 0x10: 54123f89f04SKarl Hiramoto atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 54252fbae2aSDavid S. Miller atm_info(usbatm, "ADSL line is synchronising\n"); 54348da7267SDuncan Sands break; 5441da177e4SLinus Torvalds 54548da7267SDuncan Sands case 0x20: 5461a7aad15SDuncan Sands down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8) 54748da7267SDuncan Sands | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24); 5481a7aad15SDuncan Sands up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8) 54948da7267SDuncan Sands | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24); 5501da177e4SLinus Torvalds 55148da7267SDuncan Sands if (!(down_speed & 0x0000ffff) && !(up_speed & 0x0000ffff)) { 55248da7267SDuncan Sands down_speed >>= 16; 55348da7267SDuncan Sands up_speed >>= 16; 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds 55648da7267SDuncan Sands atm_dev->link_rate = down_speed * 1000 / 424; 55723f89f04SKarl Hiramoto atm_dev_signal_change(atm_dev, ATM_PHY_SIG_FOUND); 55848da7267SDuncan Sands 55948da7267SDuncan Sands atm_info(usbatm, 560322a95bcSDuncan Sands "ADSL line is up (%d kb/s down | %d kb/s up)\n", 56148da7267SDuncan Sands down_speed, up_speed); 56248da7267SDuncan Sands break; 56348da7267SDuncan Sands 56448da7267SDuncan Sands default: 56523f89f04SKarl Hiramoto atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN); 5660ec3c7e8SDuncan Sands atm_info(usbatm, "unknown line state %02x\n", status); 56748da7267SDuncan Sands break; 56848da7267SDuncan Sands } 5691a7aad15SDuncan Sands 5701a7aad15SDuncan Sands instance->last_status = status; 5711a7aad15SDuncan Sands } 57248da7267SDuncan Sands } 57348da7267SDuncan Sands 57448da7267SDuncan Sands static void speedtch_status_poll(unsigned long data) 5751da177e4SLinus Torvalds { 57648da7267SDuncan Sands struct speedtch_instance_data *instance = (void *)data; 5771da177e4SLinus Torvalds 57837c95bfeSTejun Heo schedule_work(&instance->status_check_work); 5791da177e4SLinus Torvalds 58048da7267SDuncan Sands /* The following check is racy, but the race is harmless */ 58148da7267SDuncan Sands if (instance->poll_delay < MAX_POLL_DELAY) 58237c95bfeSTejun Heo mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(instance->poll_delay)); 58348da7267SDuncan Sands else 58452fbae2aSDavid S. Miller atm_warn(instance->usbatm, "Too many failures - disabling line status polling\n"); 5851da177e4SLinus Torvalds } 5861da177e4SLinus Torvalds 58748da7267SDuncan Sands static void speedtch_resubmit_int(unsigned long data) 58848da7267SDuncan Sands { 58948da7267SDuncan Sands struct speedtch_instance_data *instance = (void *)data; 59048da7267SDuncan Sands struct urb *int_urb = instance->int_urb; 59148da7267SDuncan Sands int ret; 59248da7267SDuncan Sands 59348da7267SDuncan Sands atm_dbg(instance->usbatm, "%s entered\n", __func__); 59448da7267SDuncan Sands 59548da7267SDuncan Sands if (int_urb) { 59648da7267SDuncan Sands ret = usb_submit_urb(int_urb, GFP_ATOMIC); 59748da7267SDuncan Sands if (!ret) 59837c95bfeSTejun Heo schedule_work(&instance->status_check_work); 59948da7267SDuncan Sands else { 60048da7267SDuncan Sands atm_dbg(instance->usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret); 60148da7267SDuncan Sands mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); 60248da7267SDuncan Sands } 60348da7267SDuncan Sands } 60448da7267SDuncan Sands } 60548da7267SDuncan Sands 6067d12e780SDavid Howells static void speedtch_handle_int(struct urb *int_urb) 60748da7267SDuncan Sands { 60848da7267SDuncan Sands struct speedtch_instance_data *instance = int_urb->context; 60948da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 61048da7267SDuncan Sands unsigned int count = int_urb->actual_length; 6119a5a3e95SGreg Kroah-Hartman int status = int_urb->status; 6129a5a3e95SGreg Kroah-Hartman int ret; 61348da7267SDuncan Sands 61448da7267SDuncan Sands /* The magic interrupt for "up state" */ 6153c6bee1dSJesper Juhl static const unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; 61648da7267SDuncan Sands /* The magic interrupt for "down state" */ 6173c6bee1dSJesper Juhl static const unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 }; 61848da7267SDuncan Sands 61948da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 62048da7267SDuncan Sands 6219a5a3e95SGreg Kroah-Hartman if (status < 0) { 6229a5a3e95SGreg Kroah-Hartman atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, status); 62348da7267SDuncan Sands goto fail; 62448da7267SDuncan Sands } 62548da7267SDuncan Sands 62648da7267SDuncan Sands if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) { 62737c95bfeSTejun Heo del_timer(&instance->status_check_timer); 62852fbae2aSDavid S. Miller atm_info(usbatm, "DSL line goes up\n"); 62948da7267SDuncan Sands } else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) { 63052fbae2aSDavid S. Miller atm_info(usbatm, "DSL line goes down\n"); 63148da7267SDuncan Sands } else { 63248da7267SDuncan Sands int i; 63348da7267SDuncan Sands 63448da7267SDuncan Sands atm_dbg(usbatm, "%s: unknown interrupt packet of length %d:", __func__, count); 63548da7267SDuncan Sands for (i = 0; i < count; i++) 63648da7267SDuncan Sands printk(" %02x", instance->int_data[i]); 63748da7267SDuncan Sands printk("\n"); 63848da7267SDuncan Sands goto fail; 63948da7267SDuncan Sands } 64048da7267SDuncan Sands 6413383ee4cSGreg Kroah-Hartman int_urb = instance->int_urb; 6423383ee4cSGreg Kroah-Hartman if (int_urb) { 64348da7267SDuncan Sands ret = usb_submit_urb(int_urb, GFP_ATOMIC); 64437c95bfeSTejun Heo schedule_work(&instance->status_check_work); 64548da7267SDuncan Sands if (ret < 0) { 64648da7267SDuncan Sands atm_dbg(usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret); 64748da7267SDuncan Sands goto fail; 64848da7267SDuncan Sands } 64948da7267SDuncan Sands } 65048da7267SDuncan Sands 65148da7267SDuncan Sands return; 65248da7267SDuncan Sands 65348da7267SDuncan Sands fail: 6543383ee4cSGreg Kroah-Hartman int_urb = instance->int_urb; 6553383ee4cSGreg Kroah-Hartman if (int_urb) 65648da7267SDuncan Sands mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); 65748da7267SDuncan Sands } 65848da7267SDuncan Sands 65948da7267SDuncan Sands static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_dev) 66048da7267SDuncan Sands { 66148da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 66248da7267SDuncan Sands struct speedtch_instance_data *instance = usbatm->driver_data; 66348da7267SDuncan Sands int i, ret; 66448da7267SDuncan Sands unsigned char mac_str[13]; 66548da7267SDuncan Sands 66648da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 66748da7267SDuncan Sands 66848da7267SDuncan Sands /* Set MAC address, it is stored in the serial number */ 66948da7267SDuncan Sands memset(atm_dev->esi, 0, sizeof(atm_dev->esi)); 67048da7267SDuncan Sands if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { 67148da7267SDuncan Sands for (i = 0; i < 6; i++) 67296b89f32SAndy Shevchenko atm_dev->esi[i] = (hex_to_bin(mac_str[i * 2]) << 4) + 67396b89f32SAndy Shevchenko hex_to_bin(mac_str[i * 2 + 1]); 67448da7267SDuncan Sands } 67548da7267SDuncan Sands 67648da7267SDuncan Sands /* Start modem synchronisation */ 67748da7267SDuncan Sands ret = speedtch_start_synchro(instance); 67848da7267SDuncan Sands 67948da7267SDuncan Sands /* Set up interrupt endpoint */ 68048da7267SDuncan Sands if (instance->int_urb) { 68148da7267SDuncan Sands ret = usb_submit_urb(instance->int_urb, GFP_KERNEL); 68248da7267SDuncan Sands if (ret < 0) { 68348da7267SDuncan Sands /* Doesn't matter; we'll poll anyway */ 68448da7267SDuncan Sands atm_dbg(usbatm, "%s: submission of interrupt URB failed (%d)!\n", __func__, ret); 68548da7267SDuncan Sands usb_free_urb(instance->int_urb); 68648da7267SDuncan Sands instance->int_urb = NULL; 68748da7267SDuncan Sands } 68848da7267SDuncan Sands } 68948da7267SDuncan Sands 69048da7267SDuncan Sands /* Start status polling */ 69137c95bfeSTejun Heo mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(1000)); 69248da7267SDuncan Sands 69348da7267SDuncan Sands return 0; 69448da7267SDuncan Sands } 69548da7267SDuncan Sands 69648da7267SDuncan Sands static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_dev) 69748da7267SDuncan Sands { 69848da7267SDuncan Sands struct speedtch_instance_data *instance = usbatm->driver_data; 69948da7267SDuncan Sands struct urb *int_urb = instance->int_urb; 70048da7267SDuncan Sands 70148da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 70248da7267SDuncan Sands 70337c95bfeSTejun Heo del_timer_sync(&instance->status_check_timer); 70448da7267SDuncan Sands 70548da7267SDuncan Sands /* 70648da7267SDuncan Sands * Since resubmit_timer and int_urb can schedule themselves and 70748da7267SDuncan Sands * each other, shutting them down correctly takes some care 70848da7267SDuncan Sands */ 70948da7267SDuncan Sands instance->int_urb = NULL; /* signal shutdown */ 71048da7267SDuncan Sands mb(); 71148da7267SDuncan Sands usb_kill_urb(int_urb); 71248da7267SDuncan Sands del_timer_sync(&instance->resubmit_timer); 71348da7267SDuncan Sands /* 71448da7267SDuncan Sands * At this point, speedtch_handle_int and speedtch_resubmit_int 71548da7267SDuncan Sands * can run or be running, but instance->int_urb == NULL means that 71648da7267SDuncan Sands * they will not reschedule 71748da7267SDuncan Sands */ 71848da7267SDuncan Sands usb_kill_urb(int_urb); 71948da7267SDuncan Sands del_timer_sync(&instance->resubmit_timer); 72048da7267SDuncan Sands usb_free_urb(int_urb); 72148da7267SDuncan Sands 72243829731STejun Heo flush_work(&instance->status_check_work); 72348da7267SDuncan Sands } 72448da7267SDuncan Sands 7258fc7aeabSAlan Stern static int speedtch_pre_reset(struct usb_interface *intf) 7268fc7aeabSAlan Stern { 7278fc7aeabSAlan Stern return 0; 7288fc7aeabSAlan Stern } 7298fc7aeabSAlan Stern 7308fc7aeabSAlan Stern static int speedtch_post_reset(struct usb_interface *intf) 7318fc7aeabSAlan Stern { 7328fc7aeabSAlan Stern return 0; 7338fc7aeabSAlan Stern } 7348fc7aeabSAlan Stern 73548da7267SDuncan Sands 7361da177e4SLinus Torvalds /********** 7371da177e4SLinus Torvalds ** USB ** 7381da177e4SLinus Torvalds **********/ 7391da177e4SLinus Torvalds 740*1e44f54bSArvind Yadav static const struct usb_device_id speedtch_usb_ids[] = { 74148da7267SDuncan Sands {USB_DEVICE(0x06b9, 0x4061)}, 74248da7267SDuncan Sands {} 74348da7267SDuncan Sands }; 74448da7267SDuncan Sands 74548da7267SDuncan Sands MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); 74648da7267SDuncan Sands 74748da7267SDuncan Sands static int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *); 74848da7267SDuncan Sands 74948da7267SDuncan Sands static struct usb_driver speedtch_usb_driver = { 75048da7267SDuncan Sands .name = speedtch_driver_name, 75148da7267SDuncan Sands .probe = speedtch_usb_probe, 75248da7267SDuncan Sands .disconnect = usbatm_usb_disconnect, 7538fc7aeabSAlan Stern .pre_reset = speedtch_pre_reset, 7548fc7aeabSAlan Stern .post_reset = speedtch_post_reset, 75548da7267SDuncan Sands .id_table = speedtch_usb_ids 75648da7267SDuncan Sands }; 75748da7267SDuncan Sands 7586c4b7f70SNicolas Kaiser static void speedtch_release_interfaces(struct usb_device *usb_dev, 7596c4b7f70SNicolas Kaiser int num_interfaces) 7606c4b7f70SNicolas Kaiser { 76148da7267SDuncan Sands struct usb_interface *cur_intf; 76248da7267SDuncan Sands int i; 76348da7267SDuncan Sands 7643383ee4cSGreg Kroah-Hartman for (i = 0; i < num_interfaces; i++) { 7653383ee4cSGreg Kroah-Hartman cur_intf = usb_ifnum_to_if(usb_dev, i); 7663383ee4cSGreg Kroah-Hartman if (cur_intf) { 76748da7267SDuncan Sands usb_set_intfdata(cur_intf, NULL); 76848da7267SDuncan Sands usb_driver_release_interface(&speedtch_usb_driver, cur_intf); 76948da7267SDuncan Sands } 77048da7267SDuncan Sands } 7713383ee4cSGreg Kroah-Hartman } 77248da7267SDuncan Sands 77348da7267SDuncan Sands static int speedtch_bind(struct usbatm_data *usbatm, 77448da7267SDuncan Sands struct usb_interface *intf, 77535644b0cSDuncan Sands const struct usb_device_id *id) 7761da177e4SLinus Torvalds { 77748da7267SDuncan Sands struct usb_device *usb_dev = interface_to_usbdev(intf); 77880aae7a1SDuncan Sands struct usb_interface *cur_intf, *data_intf; 7791da177e4SLinus Torvalds struct speedtch_instance_data *instance; 78048da7267SDuncan Sands int ifnum = intf->altsetting->desc.bInterfaceNumber; 78148da7267SDuncan Sands int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces; 78248da7267SDuncan Sands int i, ret; 78380aae7a1SDuncan Sands int use_isoc; 7841da177e4SLinus Torvalds 78548da7267SDuncan Sands usb_dbg(usbatm, "%s entered\n", __func__); 7861da177e4SLinus Torvalds 7870ec3c7e8SDuncan Sands /* sanity checks */ 7880ec3c7e8SDuncan Sands 78948da7267SDuncan Sands if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { 7900ec3c7e8SDuncan Sands usb_err(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass); 7911da177e4SLinus Torvalds return -ENODEV; 79248da7267SDuncan Sands } 7931da177e4SLinus Torvalds 7943383ee4cSGreg Kroah-Hartman data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA); 7953383ee4cSGreg Kroah-Hartman if (!data_intf) { 79680aae7a1SDuncan Sands usb_err(usbatm, "%s: data interface not found!\n", __func__); 79780aae7a1SDuncan Sands return -ENODEV; 79880aae7a1SDuncan Sands } 79980aae7a1SDuncan Sands 80048da7267SDuncan Sands /* claim all interfaces */ 8011da177e4SLinus Torvalds 80248da7267SDuncan Sands for (i = 0; i < num_interfaces; i++) { 80348da7267SDuncan Sands cur_intf = usb_ifnum_to_if(usb_dev, i); 80448da7267SDuncan Sands 80548da7267SDuncan Sands if ((i != ifnum) && cur_intf) { 80648da7267SDuncan Sands ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm); 80748da7267SDuncan Sands 80848da7267SDuncan Sands if (ret < 0) { 8090ec3c7e8SDuncan Sands usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, i, ret); 81048da7267SDuncan Sands speedtch_release_interfaces(usb_dev, i); 81148da7267SDuncan Sands return ret; 81248da7267SDuncan Sands } 81348da7267SDuncan Sands } 81448da7267SDuncan Sands } 81548da7267SDuncan Sands 8169a734efeSDuncan Sands instance = kzalloc(sizeof(*instance), GFP_KERNEL); 81748da7267SDuncan Sands 8181da177e4SLinus Torvalds if (!instance) { 81948da7267SDuncan Sands ret = -ENOMEM; 82048da7267SDuncan Sands goto fail_release; 8211da177e4SLinus Torvalds } 8221da177e4SLinus Torvalds 82348da7267SDuncan Sands instance->usbatm = usbatm; 8241da177e4SLinus Torvalds 8256a4f1b41SDuncan Sands /* module parameters may change at any moment, so take a snapshot */ 8266a4f1b41SDuncan Sands instance->params.altsetting = altsetting; 8276a4f1b41SDuncan Sands instance->params.BMaxDSL = BMaxDSL; 8286a4f1b41SDuncan Sands instance->params.ModemMode = ModemMode; 8296a4f1b41SDuncan Sands memcpy(instance->params.ModemOption, DEFAULT_MODEM_OPTION, MODEM_OPTION_LENGTH); 8306a4f1b41SDuncan Sands memcpy(instance->params.ModemOption, ModemOption, num_ModemOption); 83180aae7a1SDuncan Sands use_isoc = enable_isoc; 8326f749475SDuncan Sands 8336a4f1b41SDuncan Sands if (instance->params.altsetting) 8346a4f1b41SDuncan Sands if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) { 8356a4f1b41SDuncan Sands usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, instance->params.altsetting, ret); 8366a4f1b41SDuncan Sands instance->params.altsetting = 0; /* fall back to default */ 8376f749475SDuncan Sands } 8386f749475SDuncan Sands 8396a4f1b41SDuncan Sands if (!instance->params.altsetting && use_isoc) 84080aae7a1SDuncan Sands if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_ISOC_ALTSETTING)) < 0) { 84180aae7a1SDuncan Sands usb_dbg(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_ISOC_ALTSETTING, ret); 84280aae7a1SDuncan Sands use_isoc = 0; /* fall back to bulk */ 84380aae7a1SDuncan Sands } 84480aae7a1SDuncan Sands 84580aae7a1SDuncan Sands if (use_isoc) { 84680aae7a1SDuncan Sands const struct usb_host_interface *desc = data_intf->cur_altsetting; 84780aae7a1SDuncan Sands const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in; 84880aae7a1SDuncan Sands 84980aae7a1SDuncan Sands use_isoc = 0; /* fall back to bulk if endpoint not found */ 85080aae7a1SDuncan Sands 85180aae7a1SDuncan Sands for (i = 0; i < desc->desc.bNumEndpoints; i++) { 85280aae7a1SDuncan Sands const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc; 85380aae7a1SDuncan Sands 85480aae7a1SDuncan Sands if ((endpoint_desc->bEndpointAddress == target_address)) { 855c5dd1f94SLuiz Fernando N. Capitulino use_isoc = 856c5dd1f94SLuiz Fernando N. Capitulino usb_endpoint_xfer_isoc(endpoint_desc); 85780aae7a1SDuncan Sands break; 85880aae7a1SDuncan Sands } 85980aae7a1SDuncan Sands } 86080aae7a1SDuncan Sands 86180aae7a1SDuncan Sands if (!use_isoc) 86280aae7a1SDuncan Sands usb_info(usbatm, "isochronous transfer not supported - using bulk\n"); 86380aae7a1SDuncan Sands } 86480aae7a1SDuncan Sands 8656a4f1b41SDuncan Sands if (!use_isoc && !instance->params.altsetting) 86680aae7a1SDuncan Sands if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_BULK_ALTSETTING)) < 0) { 86780aae7a1SDuncan Sands usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_BULK_ALTSETTING, ret); 8686f749475SDuncan Sands goto fail_free; 8696f749475SDuncan Sands } 87080aae7a1SDuncan Sands 8716a4f1b41SDuncan Sands if (!instance->params.altsetting) 8726a4f1b41SDuncan Sands instance->params.altsetting = use_isoc ? DEFAULT_ISOC_ALTSETTING : DEFAULT_BULK_ALTSETTING; 87380aae7a1SDuncan Sands 87480aae7a1SDuncan Sands usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0); 8756f749475SDuncan Sands 87637c95bfeSTejun Heo INIT_WORK(&instance->status_check_work, speedtch_check_status); 87737c95bfeSTejun Heo init_timer(&instance->status_check_timer); 8781da177e4SLinus Torvalds 87937c95bfeSTejun Heo instance->status_check_timer.function = speedtch_status_poll; 88037c95bfeSTejun Heo instance->status_check_timer.data = (unsigned long)instance; 8811a7aad15SDuncan Sands instance->last_status = 0xff; 88248da7267SDuncan Sands instance->poll_delay = MIN_POLL_DELAY; 8831da177e4SLinus Torvalds 88448da7267SDuncan Sands init_timer(&instance->resubmit_timer); 88548da7267SDuncan Sands instance->resubmit_timer.function = speedtch_resubmit_int; 88648da7267SDuncan Sands instance->resubmit_timer.data = (unsigned long)instance; 8871da177e4SLinus Torvalds 88848da7267SDuncan Sands instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); 8891da177e4SLinus Torvalds 89048da7267SDuncan Sands if (instance->int_urb) 89148da7267SDuncan Sands usb_fill_int_urb(instance->int_urb, usb_dev, 89248da7267SDuncan Sands usb_rcvintpipe(usb_dev, ENDPOINT_INT), 89348da7267SDuncan Sands instance->int_data, sizeof(instance->int_data), 894afb8aae8SFelipe Balbi speedtch_handle_int, instance, 16); 89548da7267SDuncan Sands else 89648da7267SDuncan Sands usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__); 8971da177e4SLinus Torvalds 89848da7267SDuncan Sands /* check whether the modem already seems to be alive */ 89948da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 90048da7267SDuncan Sands 0x12, 0xc0, 0x07, 0x00, 90148da7267SDuncan Sands instance->scratch_buffer + OFFSET_7, SIZE_7, 500); 9021da177e4SLinus Torvalds 90380aae7a1SDuncan Sands usbatm->flags |= (ret == SIZE_7 ? UDSL_SKIP_HEAVY_INIT : 0); 9041da177e4SLinus Torvalds 90535644b0cSDuncan Sands usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, usbatm->flags & UDSL_SKIP_HEAVY_INIT ? "already" : "not"); 9061da177e4SLinus Torvalds 90735644b0cSDuncan Sands if (!(usbatm->flags & UDSL_SKIP_HEAVY_INIT)) 9080ec3c7e8SDuncan Sands if ((ret = usb_reset_device(usb_dev)) < 0) { 9090ec3c7e8SDuncan Sands usb_err(usbatm, "%s: device reset failed (%d)!\n", __func__, ret); 91048da7267SDuncan Sands goto fail_free; 9110ec3c7e8SDuncan Sands } 91248da7267SDuncan Sands 91348da7267SDuncan Sands usbatm->driver_data = instance; 9141da177e4SLinus Torvalds 9151da177e4SLinus Torvalds return 0; 9161da177e4SLinus Torvalds 91748da7267SDuncan Sands fail_free: 91848da7267SDuncan Sands usb_free_urb(instance->int_urb); 9191da177e4SLinus Torvalds kfree(instance); 92048da7267SDuncan Sands fail_release: 92148da7267SDuncan Sands speedtch_release_interfaces(usb_dev, num_interfaces); 92248da7267SDuncan Sands return ret; 9231da177e4SLinus Torvalds } 9241da177e4SLinus Torvalds 92548da7267SDuncan Sands static void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *intf) 9261da177e4SLinus Torvalds { 92748da7267SDuncan Sands struct usb_device *usb_dev = interface_to_usbdev(intf); 92848da7267SDuncan Sands struct speedtch_instance_data *instance = usbatm->driver_data; 9291da177e4SLinus Torvalds 93048da7267SDuncan Sands usb_dbg(usbatm, "%s entered\n", __func__); 9311da177e4SLinus Torvalds 93248da7267SDuncan Sands speedtch_release_interfaces(usb_dev, usb_dev->actconfig->desc.bNumInterfaces); 93348da7267SDuncan Sands usb_free_urb(instance->int_urb); 93448da7267SDuncan Sands kfree(instance); 9351da177e4SLinus Torvalds } 9361da177e4SLinus Torvalds 9371da177e4SLinus Torvalds 9381da177e4SLinus Torvalds /*********** 9391da177e4SLinus Torvalds ** init ** 9401da177e4SLinus Torvalds ***********/ 9411da177e4SLinus Torvalds 94248da7267SDuncan Sands static struct usbatm_driver speedtch_usbatm_driver = { 94348da7267SDuncan Sands .driver_name = speedtch_driver_name, 94448da7267SDuncan Sands .bind = speedtch_bind, 94548da7267SDuncan Sands .heavy_init = speedtch_heavy_init, 94648da7267SDuncan Sands .unbind = speedtch_unbind, 94748da7267SDuncan Sands .atm_start = speedtch_atm_start, 94848da7267SDuncan Sands .atm_stop = speedtch_atm_stop, 94980aae7a1SDuncan Sands .bulk_in = ENDPOINT_BULK_DATA, 95080aae7a1SDuncan Sands .bulk_out = ENDPOINT_BULK_DATA, 95180aae7a1SDuncan Sands .isoc_in = ENDPOINT_ISOC_DATA 95248da7267SDuncan Sands }; 95348da7267SDuncan Sands 95448da7267SDuncan Sands static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) 95548da7267SDuncan Sands { 95648da7267SDuncan Sands return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver); 95748da7267SDuncan Sands } 95848da7267SDuncan Sands 95965db4305SGreg Kroah-Hartman module_usb_driver(speedtch_usb_driver); 9601da177e4SLinus Torvalds 9611da177e4SLinus Torvalds MODULE_AUTHOR(DRIVER_AUTHOR); 9621da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC); 9631da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 964