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/gfp.h> 3148da7267SDuncan Sands #include <linux/init.h> 3248da7267SDuncan Sands #include <linux/kernel.h> 331da177e4SLinus Torvalds #include <linux/module.h> 341da177e4SLinus Torvalds #include <linux/moduleparam.h> 351da177e4SLinus Torvalds #include <linux/slab.h> 3648da7267SDuncan Sands #include <linux/stat.h> 3748da7267SDuncan Sands #include <linux/timer.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>" 4348da7267SDuncan Sands #define DRIVER_VERSION "1.9" 441da177e4SLinus Torvalds #define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds static const char speedtch_driver_name[] = "speedtch"; 471da177e4SLinus Torvalds 4848da7267SDuncan Sands #define CTRL_TIMEOUT 2000 /* milliseconds */ 4948da7267SDuncan Sands #define DATA_TIMEOUT 2000 /* milliseconds */ 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds #define OFFSET_7 0 /* size 1 */ 521da177e4SLinus Torvalds #define OFFSET_b 1 /* size 8 */ 531da177e4SLinus Torvalds #define OFFSET_d 9 /* size 4 */ 541da177e4SLinus Torvalds #define OFFSET_e 13 /* size 1 */ 551da177e4SLinus Torvalds #define OFFSET_f 14 /* size 1 */ 561da177e4SLinus Torvalds #define TOTAL 15 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds #define SIZE_7 1 591da177e4SLinus Torvalds #define SIZE_b 8 601da177e4SLinus Torvalds #define SIZE_d 4 611da177e4SLinus Torvalds #define SIZE_e 1 621da177e4SLinus Torvalds #define SIZE_f 1 631da177e4SLinus Torvalds 6448da7267SDuncan Sands #define MIN_POLL_DELAY 5000 /* milliseconds */ 6548da7267SDuncan Sands #define MAX_POLL_DELAY 60000 /* milliseconds */ 661da177e4SLinus Torvalds 6748da7267SDuncan Sands #define RESUBMIT_DELAY 1000 /* milliseconds */ 681da177e4SLinus Torvalds 6948da7267SDuncan Sands #define DEFAULT_ALTSETTING 1 7048da7267SDuncan Sands #define DEFAULT_DL_512_FIRST 0 7148da7267SDuncan Sands #define DEFAULT_SW_BUFFERING 0 721da177e4SLinus Torvalds 7348da7267SDuncan Sands static int altsetting = DEFAULT_ALTSETTING; 7448da7267SDuncan Sands static int dl_512_first = DEFAULT_DL_512_FIRST; 7548da7267SDuncan Sands static int sw_buffering = DEFAULT_SW_BUFFERING; 761da177e4SLinus Torvalds 7748da7267SDuncan Sands module_param(altsetting, int, S_IRUGO | S_IWUSR); 7848da7267SDuncan Sands MODULE_PARM_DESC(altsetting, 7948da7267SDuncan Sands "Alternative setting for data interface (default: " 8048da7267SDuncan Sands __MODULE_STRING(DEFAULT_ALTSETTING) ")"); 8148da7267SDuncan Sands 8248da7267SDuncan Sands module_param(dl_512_first, bool, S_IRUGO | S_IWUSR); 8348da7267SDuncan Sands MODULE_PARM_DESC(dl_512_first, 8448da7267SDuncan Sands "Read 512 bytes before sending firmware (default: " 8548da7267SDuncan Sands __MODULE_STRING(DEFAULT_DL_512_FIRST) ")"); 8648da7267SDuncan Sands 8748da7267SDuncan Sands module_param(sw_buffering, bool, S_IRUGO | S_IWUSR); 8848da7267SDuncan Sands MODULE_PARM_DESC(sw_buffering, 8948da7267SDuncan Sands "Enable software buffering (default: " 9048da7267SDuncan Sands __MODULE_STRING(DEFAULT_SW_BUFFERING) ")"); 9148da7267SDuncan Sands 9248da7267SDuncan Sands #define ENDPOINT_INT 0x81 9348da7267SDuncan Sands #define ENDPOINT_DATA 0x07 9448da7267SDuncan Sands #define ENDPOINT_FIRMWARE 0x05 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds #define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds struct speedtch_instance_data { 9948da7267SDuncan Sands struct usbatm_data *usbatm; 1001da177e4SLinus Torvalds 10148da7267SDuncan Sands struct work_struct status_checker; 10248da7267SDuncan Sands 1031a7aad15SDuncan Sands unsigned char last_status; 1041a7aad15SDuncan Sands 10548da7267SDuncan Sands int poll_delay; /* milliseconds */ 10648da7267SDuncan Sands 10748da7267SDuncan Sands struct timer_list resubmit_timer; 1081da177e4SLinus Torvalds struct urb *int_urb; 1091da177e4SLinus Torvalds unsigned char int_data[16]; 1101da177e4SLinus Torvalds 11148da7267SDuncan Sands unsigned char scratch_buffer[TOTAL]; 1121da177e4SLinus Torvalds }; 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds /*************** 1151da177e4SLinus Torvalds ** firmware ** 1161da177e4SLinus Torvalds ***************/ 1171da177e4SLinus Torvalds 11848da7267SDuncan Sands static void speedtch_set_swbuff(struct speedtch_instance_data *instance, int state) 1191da177e4SLinus Torvalds { 12048da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 12148da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 1221da177e4SLinus Torvalds int ret; 1231da177e4SLinus Torvalds 12448da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 12548da7267SDuncan Sands 0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, CTRL_TIMEOUT); 12648da7267SDuncan Sands if (ret < 0) 12748da7267SDuncan Sands usb_warn(usbatm, 12848da7267SDuncan Sands "%sabling SW buffering: usb_control_msg returned %d\n", 1291da177e4SLinus Torvalds state ? "En" : "Dis", ret); 13048da7267SDuncan Sands else 1311da177e4SLinus Torvalds dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis"); 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds static void speedtch_test_sequence(struct speedtch_instance_data *instance) 1351da177e4SLinus Torvalds { 13648da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 13748da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 13848da7267SDuncan Sands unsigned char *buf = instance->scratch_buffer; 1391da177e4SLinus Torvalds int ret; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds /* URB 147 */ 1421da177e4SLinus Torvalds buf[0] = 0x1c; 1431da177e4SLinus Torvalds buf[1] = 0x50; 14448da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 14548da7267SDuncan Sands 0x01, 0x40, 0x0b, 0x00, buf, 2, CTRL_TIMEOUT); 1461da177e4SLinus Torvalds if (ret < 0) 14748da7267SDuncan Sands usb_warn(usbatm, "%s failed on URB147: %d\n", __func__, ret); 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds /* URB 148 */ 1501da177e4SLinus Torvalds buf[0] = 0x32; 1511da177e4SLinus Torvalds buf[1] = 0x00; 15248da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 15348da7267SDuncan Sands 0x01, 0x40, 0x02, 0x00, buf, 2, CTRL_TIMEOUT); 1541da177e4SLinus Torvalds if (ret < 0) 15548da7267SDuncan Sands usb_warn(usbatm, "%s failed on URB148: %d\n", __func__, ret); 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds /* URB 149 */ 1581da177e4SLinus Torvalds buf[0] = 0x01; 1591da177e4SLinus Torvalds buf[1] = 0x00; 1601da177e4SLinus Torvalds buf[2] = 0x01; 16148da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 16248da7267SDuncan Sands 0x01, 0x40, 0x03, 0x00, buf, 3, CTRL_TIMEOUT); 1631da177e4SLinus Torvalds if (ret < 0) 16448da7267SDuncan Sands usb_warn(usbatm, "%s failed on URB149: %d\n", __func__, ret); 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds /* URB 150 */ 1671da177e4SLinus Torvalds buf[0] = 0x01; 1681da177e4SLinus Torvalds buf[1] = 0x00; 1691da177e4SLinus Torvalds buf[2] = 0x01; 17048da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 17148da7267SDuncan Sands 0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT); 1721da177e4SLinus Torvalds if (ret < 0) 17348da7267SDuncan Sands usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret); 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds 17648da7267SDuncan Sands static int speedtch_upload_firmware(struct speedtch_instance_data *instance, 1771da177e4SLinus Torvalds const struct firmware *fw1, 1781da177e4SLinus Torvalds const struct firmware *fw2) 1791da177e4SLinus Torvalds { 1801da177e4SLinus Torvalds unsigned char *buffer; 18148da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 1821da177e4SLinus Torvalds struct usb_interface *intf; 18348da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 18448da7267SDuncan Sands int actual_length; 18548da7267SDuncan Sands int ret = 0; 1861da177e4SLinus Torvalds int offset; 1871da177e4SLinus Torvalds 18848da7267SDuncan Sands usb_dbg(usbatm, "%s entered\n", __func__); 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { 19148da7267SDuncan Sands ret = -ENOMEM; 19248da7267SDuncan Sands usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__); 19348da7267SDuncan Sands goto out; 1941da177e4SLinus Torvalds } 1951da177e4SLinus Torvalds 19648da7267SDuncan Sands if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { 19748da7267SDuncan Sands ret = -ENODEV; 19848da7267SDuncan Sands usb_dbg(usbatm, "%s: interface not found!\n", __func__); 19948da7267SDuncan Sands goto out_free; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds /* URB 7 */ 2031da177e4SLinus Torvalds if (dl_512_first) { /* some modems need a read before writing the firmware */ 20448da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2051da177e4SLinus Torvalds buffer, 0x200, &actual_length, 2000); 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds if (ret < 0 && ret != -ETIMEDOUT) 2080ec3c7e8SDuncan Sands usb_warn(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret); 2091da177e4SLinus Torvalds else 21048da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret); 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds /* URB 8 : both leds are static green */ 2141da177e4SLinus Torvalds for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { 2151da177e4SLinus Torvalds int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); 2161da177e4SLinus Torvalds memcpy(buffer, fw1->data + offset, thislen); 2171da177e4SLinus Torvalds 21848da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2191da177e4SLinus Torvalds buffer, thislen, &actual_length, DATA_TIMEOUT); 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds if (ret < 0) { 2220ec3c7e8SDuncan Sands usb_err(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret); 22348da7267SDuncan Sands goto out_free; 2241da177e4SLinus Torvalds } 22548da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size); 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds /* USB led blinking green, ADSL led off */ 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds /* URB 11 */ 23148da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2321da177e4SLinus Torvalds buffer, 0x200, &actual_length, DATA_TIMEOUT); 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds if (ret < 0) { 2350ec3c7e8SDuncan Sands usb_err(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret); 23648da7267SDuncan Sands goto out_free; 2371da177e4SLinus Torvalds } 23848da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length); 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds /* URBs 12 to 139 - USB led blinking green, ADSL led off */ 2411da177e4SLinus Torvalds for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { 2421da177e4SLinus Torvalds int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); 2431da177e4SLinus Torvalds memcpy(buffer, fw2->data + offset, thislen); 2441da177e4SLinus Torvalds 24548da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2461da177e4SLinus Torvalds buffer, thislen, &actual_length, DATA_TIMEOUT); 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds if (ret < 0) { 2490ec3c7e8SDuncan Sands usb_err(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret); 25048da7267SDuncan Sands goto out_free; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds } 25348da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK3 uploaded (%zu bytes)\n", __func__, fw2->size); 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds /* USB led static green, ADSL led static red */ 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds /* URB 142 */ 25848da7267SDuncan Sands ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2591da177e4SLinus Torvalds buffer, 0x200, &actual_length, DATA_TIMEOUT); 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds if (ret < 0) { 2620ec3c7e8SDuncan Sands usb_err(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret); 26348da7267SDuncan Sands goto out_free; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds /* success */ 26748da7267SDuncan Sands usb_dbg(usbatm, "%s: BLOCK4 downloaded (%d bytes)\n", __func__, actual_length); 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds /* Delay to allow firmware to start up. We can do this here 2701da177e4SLinus Torvalds because we're in our own kernel thread anyway. */ 27148da7267SDuncan Sands msleep_interruptible(1000); 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds /* Enable software buffering, if requested */ 2741da177e4SLinus Torvalds if (sw_buffering) 2751da177e4SLinus Torvalds speedtch_set_swbuff(instance, 1); 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds /* Magic spell; don't ask us what this does */ 2781da177e4SLinus Torvalds speedtch_test_sequence(instance); 2791da177e4SLinus Torvalds 28048da7267SDuncan Sands ret = 0; 2811da177e4SLinus Torvalds 28248da7267SDuncan Sands out_free: 2831da177e4SLinus Torvalds free_page((unsigned long)buffer); 28448da7267SDuncan Sands out: 28548da7267SDuncan Sands return ret; 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds 2880ec3c7e8SDuncan Sands static int speedtch_find_firmware(struct usbatm_data *usbatm, struct usb_interface *intf, 2890ec3c7e8SDuncan Sands int phase, const struct firmware **fw_p) 2901da177e4SLinus Torvalds { 29148da7267SDuncan Sands struct device *dev = &intf->dev; 29248da7267SDuncan Sands const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice); 2931da177e4SLinus Torvalds const u8 major_revision = bcdDevice >> 8; 2941da177e4SLinus Torvalds const u8 minor_revision = bcdDevice & 0xff; 29548da7267SDuncan Sands char buf[24]; 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); 2980ec3c7e8SDuncan Sands usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 2991da177e4SLinus Torvalds 30048da7267SDuncan Sands if (request_firmware(fw_p, buf, dev)) { 3011da177e4SLinus Torvalds sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); 3020ec3c7e8SDuncan Sands usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 3031da177e4SLinus Torvalds 30448da7267SDuncan Sands if (request_firmware(fw_p, buf, dev)) { 3051da177e4SLinus Torvalds sprintf(buf, "speedtch-%d.bin", phase); 3060ec3c7e8SDuncan Sands usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 3071da177e4SLinus Torvalds 30848da7267SDuncan Sands if (request_firmware(fw_p, buf, dev)) { 3090ec3c7e8SDuncan Sands usb_err(usbatm, "%s: no stage %d firmware found!\n", __func__, phase); 3101da177e4SLinus Torvalds return -ENOENT; 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 3150ec3c7e8SDuncan Sands usb_info(usbatm, "found stage %d firmware %s\n", phase, buf); 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds return 0; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 32048da7267SDuncan Sands static int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface *intf) 3211da177e4SLinus Torvalds { 3221da177e4SLinus Torvalds const struct firmware *fw1, *fw2; 32348da7267SDuncan Sands struct speedtch_instance_data *instance = usbatm->driver_data; 32448da7267SDuncan Sands int ret; 3251da177e4SLinus Torvalds 3260ec3c7e8SDuncan Sands if ((ret = speedtch_find_firmware(usbatm, intf, 1, &fw1)) < 0) 32748da7267SDuncan Sands return ret; 3281da177e4SLinus Torvalds 3290ec3c7e8SDuncan Sands if ((ret = speedtch_find_firmware(usbatm, intf, 2, &fw2)) < 0) { 3301da177e4SLinus Torvalds release_firmware(fw1); 33148da7267SDuncan Sands return ret; 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds 3340ec3c7e8SDuncan Sands if ((ret = speedtch_upload_firmware(instance, fw1, fw2)) < 0) 3350ec3c7e8SDuncan Sands usb_err(usbatm, "%s: firmware upload failed (%d)!\n", __func__, ret); 3361da177e4SLinus Torvalds 33748da7267SDuncan Sands release_firmware(fw2); 33848da7267SDuncan Sands release_firmware(fw1); 33948da7267SDuncan Sands 34048da7267SDuncan Sands return ret; 34148da7267SDuncan Sands } 34248da7267SDuncan Sands 34348da7267SDuncan Sands 34448da7267SDuncan Sands /********** 34548da7267SDuncan Sands ** ATM ** 34648da7267SDuncan Sands **********/ 34748da7267SDuncan Sands 34848da7267SDuncan Sands static int speedtch_read_status(struct speedtch_instance_data *instance) 34948da7267SDuncan Sands { 35048da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 35148da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 35248da7267SDuncan Sands unsigned char *buf = instance->scratch_buffer; 35348da7267SDuncan Sands int ret; 35448da7267SDuncan Sands 35548da7267SDuncan Sands memset(buf, 0, TOTAL); 35648da7267SDuncan Sands 35748da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 35848da7267SDuncan Sands 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7, 35948da7267SDuncan Sands CTRL_TIMEOUT); 36048da7267SDuncan Sands if (ret < 0) { 36148da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG 7 failed\n", __func__); 36248da7267SDuncan Sands return ret; 36348da7267SDuncan Sands } 36448da7267SDuncan Sands 36548da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 36648da7267SDuncan Sands 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b, 36748da7267SDuncan Sands CTRL_TIMEOUT); 36848da7267SDuncan Sands if (ret < 0) { 36948da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG B failed\n", __func__); 37048da7267SDuncan Sands return ret; 37148da7267SDuncan Sands } 37248da7267SDuncan Sands 37348da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 37448da7267SDuncan Sands 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d, 37548da7267SDuncan Sands CTRL_TIMEOUT); 37648da7267SDuncan Sands if (ret < 0) { 37748da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG D failed\n", __func__); 37848da7267SDuncan Sands return ret; 37948da7267SDuncan Sands } 38048da7267SDuncan Sands 38148da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 38248da7267SDuncan Sands 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e, 38348da7267SDuncan Sands CTRL_TIMEOUT); 38448da7267SDuncan Sands if (ret < 0) { 38548da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG E failed\n", __func__); 38648da7267SDuncan Sands return ret; 38748da7267SDuncan Sands } 38848da7267SDuncan Sands 38948da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 39048da7267SDuncan Sands 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f, 39148da7267SDuncan Sands CTRL_TIMEOUT); 39248da7267SDuncan Sands if (ret < 0) { 39348da7267SDuncan Sands atm_dbg(usbatm, "%s: MSG F failed\n", __func__); 39448da7267SDuncan Sands return ret; 39548da7267SDuncan Sands } 39648da7267SDuncan Sands 3971da177e4SLinus Torvalds return 0; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 40048da7267SDuncan Sands static int speedtch_start_synchro(struct speedtch_instance_data *instance) 4011da177e4SLinus Torvalds { 40248da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 40348da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 40448da7267SDuncan Sands unsigned char *buf = instance->scratch_buffer; 4051da177e4SLinus Torvalds int ret; 4061da177e4SLinus Torvalds 40748da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 4081da177e4SLinus Torvalds 40948da7267SDuncan Sands memset(buf, 0, 2); 4101da177e4SLinus Torvalds 41148da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 41248da7267SDuncan Sands 0x12, 0xc0, 0x04, 0x00, 41348da7267SDuncan Sands buf, 2, CTRL_TIMEOUT); 41448da7267SDuncan Sands 41548da7267SDuncan Sands if (ret < 0) 41648da7267SDuncan Sands atm_warn(usbatm, "failed to start ADSL synchronisation: %d\n", ret); 41748da7267SDuncan Sands else 41848da7267SDuncan Sands atm_dbg(usbatm, "%s: modem prodded. %d bytes returned: %02x %02x\n", 41948da7267SDuncan Sands __func__, ret, buf[0], buf[1]); 42048da7267SDuncan Sands 42148da7267SDuncan Sands return ret; 42248da7267SDuncan Sands } 42348da7267SDuncan Sands 42448da7267SDuncan Sands static void speedtch_check_status(struct speedtch_instance_data *instance) 42548da7267SDuncan Sands { 42648da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 42748da7267SDuncan Sands struct atm_dev *atm_dev = usbatm->atm_dev; 42848da7267SDuncan Sands unsigned char *buf = instance->scratch_buffer; 4291a7aad15SDuncan Sands int down_speed, up_speed, ret; 4301a7aad15SDuncan Sands unsigned char status; 43148da7267SDuncan Sands 4320ec3c7e8SDuncan Sands #ifdef VERBOSE_DEBUG 43348da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 4340ec3c7e8SDuncan Sands #endif 43548da7267SDuncan Sands 43648da7267SDuncan Sands ret = speedtch_read_status(instance); 43748da7267SDuncan Sands if (ret < 0) { 43848da7267SDuncan Sands atm_warn(usbatm, "error %d fetching device status\n", ret); 439cd5c08fbSDuncan Sands instance->poll_delay = min(2 * instance->poll_delay, MAX_POLL_DELAY); 4401da177e4SLinus Torvalds return; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 443cd5c08fbSDuncan Sands instance->poll_delay = max(instance->poll_delay / 2, MIN_POLL_DELAY); 4441da177e4SLinus Torvalds 4451a7aad15SDuncan Sands status = buf[OFFSET_7]; 4461da177e4SLinus Torvalds 4471a7aad15SDuncan Sands if ((status != instance->last_status) || !status) { 4480ec3c7e8SDuncan Sands atm_dbg(usbatm, "%s: line state 0x%02x\n", __func__, status); 4490ec3c7e8SDuncan Sands 4501a7aad15SDuncan Sands switch (status) { 45148da7267SDuncan Sands case 0: 45248da7267SDuncan Sands atm_dev->signal = ATM_PHY_SIG_LOST; 4531a7aad15SDuncan Sands if (instance->last_status) 45452fbae2aSDavid S. Miller atm_info(usbatm, "ADSL line is down\n"); 4551a7aad15SDuncan Sands /* It may never resync again unless we ask it to... */ 45648da7267SDuncan Sands ret = speedtch_start_synchro(instance); 45748da7267SDuncan Sands break; 4581da177e4SLinus Torvalds 45948da7267SDuncan Sands case 0x08: 46048da7267SDuncan Sands atm_dev->signal = ATM_PHY_SIG_UNKNOWN; 46152fbae2aSDavid S. Miller atm_info(usbatm, "ADSL line is blocked?\n"); 46248da7267SDuncan Sands break; 4631da177e4SLinus Torvalds 46448da7267SDuncan Sands case 0x10: 46548da7267SDuncan Sands atm_dev->signal = ATM_PHY_SIG_LOST; 46652fbae2aSDavid S. Miller atm_info(usbatm, "ADSL line is synchronising\n"); 46748da7267SDuncan Sands break; 4681da177e4SLinus Torvalds 46948da7267SDuncan Sands case 0x20: 4701a7aad15SDuncan Sands down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8) 47148da7267SDuncan Sands | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24); 4721a7aad15SDuncan Sands up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8) 47348da7267SDuncan Sands | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24); 4741da177e4SLinus Torvalds 47548da7267SDuncan Sands if (!(down_speed & 0x0000ffff) && !(up_speed & 0x0000ffff)) { 47648da7267SDuncan Sands down_speed >>= 16; 47748da7267SDuncan Sands up_speed >>= 16; 4781da177e4SLinus Torvalds } 4791da177e4SLinus Torvalds 48048da7267SDuncan Sands atm_dev->link_rate = down_speed * 1000 / 424; 48148da7267SDuncan Sands atm_dev->signal = ATM_PHY_SIG_FOUND; 48248da7267SDuncan Sands 48348da7267SDuncan Sands atm_info(usbatm, 484322a95bcSDuncan Sands "ADSL line is up (%d kb/s down | %d kb/s up)\n", 48548da7267SDuncan Sands down_speed, up_speed); 48648da7267SDuncan Sands break; 48748da7267SDuncan Sands 48848da7267SDuncan Sands default: 48948da7267SDuncan Sands atm_dev->signal = ATM_PHY_SIG_UNKNOWN; 4900ec3c7e8SDuncan Sands atm_info(usbatm, "unknown line state %02x\n", status); 49148da7267SDuncan Sands break; 49248da7267SDuncan Sands } 4931a7aad15SDuncan Sands 4941a7aad15SDuncan Sands instance->last_status = status; 4951a7aad15SDuncan Sands } 49648da7267SDuncan Sands } 49748da7267SDuncan Sands 49848da7267SDuncan Sands static void speedtch_status_poll(unsigned long data) 4991da177e4SLinus Torvalds { 50048da7267SDuncan Sands struct speedtch_instance_data *instance = (void *)data; 5011da177e4SLinus Torvalds 50248da7267SDuncan Sands schedule_work(&instance->status_checker); 5031da177e4SLinus Torvalds 50448da7267SDuncan Sands /* The following check is racy, but the race is harmless */ 50548da7267SDuncan Sands if (instance->poll_delay < MAX_POLL_DELAY) 50648da7267SDuncan Sands mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(instance->poll_delay)); 50748da7267SDuncan Sands else 50852fbae2aSDavid S. Miller atm_warn(instance->usbatm, "Too many failures - disabling line status polling\n"); 5091da177e4SLinus Torvalds } 5101da177e4SLinus Torvalds 51148da7267SDuncan Sands static void speedtch_resubmit_int(unsigned long data) 51248da7267SDuncan Sands { 51348da7267SDuncan Sands struct speedtch_instance_data *instance = (void *)data; 51448da7267SDuncan Sands struct urb *int_urb = instance->int_urb; 51548da7267SDuncan Sands int ret; 51648da7267SDuncan Sands 51748da7267SDuncan Sands atm_dbg(instance->usbatm, "%s entered\n", __func__); 51848da7267SDuncan Sands 51948da7267SDuncan Sands if (int_urb) { 52048da7267SDuncan Sands ret = usb_submit_urb(int_urb, GFP_ATOMIC); 52148da7267SDuncan Sands if (!ret) 52248da7267SDuncan Sands schedule_work(&instance->status_checker); 52348da7267SDuncan Sands else { 52448da7267SDuncan Sands atm_dbg(instance->usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret); 52548da7267SDuncan Sands mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); 52648da7267SDuncan Sands } 52748da7267SDuncan Sands } 52848da7267SDuncan Sands } 52948da7267SDuncan Sands 53048da7267SDuncan Sands static void speedtch_handle_int(struct urb *int_urb, struct pt_regs *regs) 53148da7267SDuncan Sands { 53248da7267SDuncan Sands struct speedtch_instance_data *instance = int_urb->context; 53348da7267SDuncan Sands struct usbatm_data *usbatm = instance->usbatm; 53448da7267SDuncan Sands unsigned int count = int_urb->actual_length; 53548da7267SDuncan Sands int ret = int_urb->status; 53648da7267SDuncan Sands 53748da7267SDuncan Sands /* The magic interrupt for "up state" */ 5383c6bee1dSJesper Juhl static const unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; 53948da7267SDuncan Sands /* The magic interrupt for "down state" */ 5403c6bee1dSJesper Juhl static const unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 }; 54148da7267SDuncan Sands 54248da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 54348da7267SDuncan Sands 54448da7267SDuncan Sands if (ret < 0) { 54548da7267SDuncan Sands atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, ret); 54648da7267SDuncan Sands goto fail; 54748da7267SDuncan Sands } 54848da7267SDuncan Sands 54948da7267SDuncan Sands if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) { 55048da7267SDuncan Sands del_timer(&instance->status_checker.timer); 55152fbae2aSDavid S. Miller atm_info(usbatm, "DSL line goes up\n"); 55248da7267SDuncan Sands } else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) { 55352fbae2aSDavid S. Miller atm_info(usbatm, "DSL line goes down\n"); 55448da7267SDuncan Sands } else { 55548da7267SDuncan Sands int i; 55648da7267SDuncan Sands 55748da7267SDuncan Sands atm_dbg(usbatm, "%s: unknown interrupt packet of length %d:", __func__, count); 55848da7267SDuncan Sands for (i = 0; i < count; i++) 55948da7267SDuncan Sands printk(" %02x", instance->int_data[i]); 56048da7267SDuncan Sands printk("\n"); 56148da7267SDuncan Sands goto fail; 56248da7267SDuncan Sands } 56348da7267SDuncan Sands 56448da7267SDuncan Sands if ((int_urb = instance->int_urb)) { 56548da7267SDuncan Sands ret = usb_submit_urb(int_urb, GFP_ATOMIC); 56648da7267SDuncan Sands schedule_work(&instance->status_checker); 56748da7267SDuncan Sands if (ret < 0) { 56848da7267SDuncan Sands atm_dbg(usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret); 56948da7267SDuncan Sands goto fail; 57048da7267SDuncan Sands } 57148da7267SDuncan Sands } 57248da7267SDuncan Sands 57348da7267SDuncan Sands return; 57448da7267SDuncan Sands 57548da7267SDuncan Sands fail: 57648da7267SDuncan Sands if ((int_urb = instance->int_urb)) 57748da7267SDuncan Sands mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); 57848da7267SDuncan Sands } 57948da7267SDuncan Sands 58048da7267SDuncan Sands static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_dev) 58148da7267SDuncan Sands { 58248da7267SDuncan Sands struct usb_device *usb_dev = usbatm->usb_dev; 58348da7267SDuncan Sands struct speedtch_instance_data *instance = usbatm->driver_data; 58448da7267SDuncan Sands int i, ret; 58548da7267SDuncan Sands unsigned char mac_str[13]; 58648da7267SDuncan Sands 58748da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 58848da7267SDuncan Sands 58948da7267SDuncan Sands if ((ret = usb_set_interface(usb_dev, 1, altsetting)) < 0) { 59048da7267SDuncan Sands atm_dbg(usbatm, "%s: usb_set_interface returned %d!\n", __func__, ret); 59148da7267SDuncan Sands return ret; 59248da7267SDuncan Sands } 59348da7267SDuncan Sands 59448da7267SDuncan Sands /* Set MAC address, it is stored in the serial number */ 59548da7267SDuncan Sands memset(atm_dev->esi, 0, sizeof(atm_dev->esi)); 59648da7267SDuncan Sands if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { 59748da7267SDuncan Sands for (i = 0; i < 6; i++) 59848da7267SDuncan Sands atm_dev->esi[i] = (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1])); 59948da7267SDuncan Sands } 60048da7267SDuncan Sands 60148da7267SDuncan Sands /* Start modem synchronisation */ 60248da7267SDuncan Sands ret = speedtch_start_synchro(instance); 60348da7267SDuncan Sands 60448da7267SDuncan Sands /* Set up interrupt endpoint */ 60548da7267SDuncan Sands if (instance->int_urb) { 60648da7267SDuncan Sands ret = usb_submit_urb(instance->int_urb, GFP_KERNEL); 60748da7267SDuncan Sands if (ret < 0) { 60848da7267SDuncan Sands /* Doesn't matter; we'll poll anyway */ 60948da7267SDuncan Sands atm_dbg(usbatm, "%s: submission of interrupt URB failed (%d)!\n", __func__, ret); 61048da7267SDuncan Sands usb_free_urb(instance->int_urb); 61148da7267SDuncan Sands instance->int_urb = NULL; 61248da7267SDuncan Sands } 61348da7267SDuncan Sands } 61448da7267SDuncan Sands 61548da7267SDuncan Sands /* Start status polling */ 61648da7267SDuncan Sands mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(1000)); 61748da7267SDuncan Sands 61848da7267SDuncan Sands return 0; 61948da7267SDuncan Sands } 62048da7267SDuncan Sands 62148da7267SDuncan Sands static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_dev) 62248da7267SDuncan Sands { 62348da7267SDuncan Sands struct speedtch_instance_data *instance = usbatm->driver_data; 62448da7267SDuncan Sands struct urb *int_urb = instance->int_urb; 62548da7267SDuncan Sands 62648da7267SDuncan Sands atm_dbg(usbatm, "%s entered\n", __func__); 62748da7267SDuncan Sands 62848da7267SDuncan Sands del_timer_sync(&instance->status_checker.timer); 62948da7267SDuncan Sands 63048da7267SDuncan Sands /* 63148da7267SDuncan Sands * Since resubmit_timer and int_urb can schedule themselves and 63248da7267SDuncan Sands * each other, shutting them down correctly takes some care 63348da7267SDuncan Sands */ 63448da7267SDuncan Sands instance->int_urb = NULL; /* signal shutdown */ 63548da7267SDuncan Sands mb(); 63648da7267SDuncan Sands usb_kill_urb(int_urb); 63748da7267SDuncan Sands del_timer_sync(&instance->resubmit_timer); 63848da7267SDuncan Sands /* 63948da7267SDuncan Sands * At this point, speedtch_handle_int and speedtch_resubmit_int 64048da7267SDuncan Sands * can run or be running, but instance->int_urb == NULL means that 64148da7267SDuncan Sands * they will not reschedule 64248da7267SDuncan Sands */ 64348da7267SDuncan Sands usb_kill_urb(int_urb); 64448da7267SDuncan Sands del_timer_sync(&instance->resubmit_timer); 64548da7267SDuncan Sands usb_free_urb(int_urb); 64648da7267SDuncan Sands 64748da7267SDuncan Sands flush_scheduled_work(); 64848da7267SDuncan Sands } 64948da7267SDuncan Sands 65048da7267SDuncan Sands 6511da177e4SLinus Torvalds /********** 6521da177e4SLinus Torvalds ** USB ** 6531da177e4SLinus Torvalds **********/ 6541da177e4SLinus Torvalds 65548da7267SDuncan Sands static struct usb_device_id speedtch_usb_ids[] = { 65648da7267SDuncan Sands {USB_DEVICE(0x06b9, 0x4061)}, 65748da7267SDuncan Sands {} 65848da7267SDuncan Sands }; 65948da7267SDuncan Sands 66048da7267SDuncan Sands MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); 66148da7267SDuncan Sands 66248da7267SDuncan Sands static int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *); 66348da7267SDuncan Sands 66448da7267SDuncan Sands static struct usb_driver speedtch_usb_driver = { 66548da7267SDuncan Sands .name = speedtch_driver_name, 66648da7267SDuncan Sands .probe = speedtch_usb_probe, 66748da7267SDuncan Sands .disconnect = usbatm_usb_disconnect, 66848da7267SDuncan Sands .id_table = speedtch_usb_ids 66948da7267SDuncan Sands }; 67048da7267SDuncan Sands 67148da7267SDuncan Sands static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_interfaces) { 67248da7267SDuncan Sands struct usb_interface *cur_intf; 67348da7267SDuncan Sands int i; 67448da7267SDuncan Sands 67548da7267SDuncan Sands for(i = 0; i < num_interfaces; i++) 67648da7267SDuncan Sands if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) { 67748da7267SDuncan Sands usb_set_intfdata(cur_intf, NULL); 67848da7267SDuncan Sands usb_driver_release_interface(&speedtch_usb_driver, cur_intf); 67948da7267SDuncan Sands } 68048da7267SDuncan Sands } 68148da7267SDuncan Sands 68248da7267SDuncan Sands static int speedtch_bind(struct usbatm_data *usbatm, 68348da7267SDuncan Sands struct usb_interface *intf, 68435644b0cSDuncan Sands const struct usb_device_id *id) 6851da177e4SLinus Torvalds { 68648da7267SDuncan Sands struct usb_device *usb_dev = interface_to_usbdev(intf); 68748da7267SDuncan Sands struct usb_interface *cur_intf; 6881da177e4SLinus Torvalds struct speedtch_instance_data *instance; 68948da7267SDuncan Sands int ifnum = intf->altsetting->desc.bInterfaceNumber; 69048da7267SDuncan Sands int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces; 69148da7267SDuncan Sands int i, ret; 6921da177e4SLinus Torvalds 69348da7267SDuncan Sands usb_dbg(usbatm, "%s entered\n", __func__); 6941da177e4SLinus Torvalds 6950ec3c7e8SDuncan Sands /* sanity checks */ 6960ec3c7e8SDuncan Sands 69748da7267SDuncan Sands if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { 6980ec3c7e8SDuncan Sands usb_err(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass); 6991da177e4SLinus Torvalds return -ENODEV; 70048da7267SDuncan Sands } 7011da177e4SLinus Torvalds 70248da7267SDuncan Sands /* claim all interfaces */ 7031da177e4SLinus Torvalds 70448da7267SDuncan Sands for (i=0; i < num_interfaces; i++) { 70548da7267SDuncan Sands cur_intf = usb_ifnum_to_if(usb_dev, i); 70648da7267SDuncan Sands 70748da7267SDuncan Sands if ((i != ifnum) && cur_intf) { 70848da7267SDuncan Sands ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm); 70948da7267SDuncan Sands 71048da7267SDuncan Sands if (ret < 0) { 7110ec3c7e8SDuncan Sands usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, i, ret); 71248da7267SDuncan Sands speedtch_release_interfaces(usb_dev, i); 71348da7267SDuncan Sands return ret; 71448da7267SDuncan Sands } 71548da7267SDuncan Sands } 71648da7267SDuncan Sands } 71748da7267SDuncan Sands 718*9a734efeSDuncan Sands instance = kzalloc(sizeof(*instance), GFP_KERNEL); 71948da7267SDuncan Sands 7201da177e4SLinus Torvalds if (!instance) { 7210ec3c7e8SDuncan Sands usb_err(usbatm, "%s: no memory for instance data!\n", __func__); 72248da7267SDuncan Sands ret = -ENOMEM; 72348da7267SDuncan Sands goto fail_release; 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds 72648da7267SDuncan Sands instance->usbatm = usbatm; 7271da177e4SLinus Torvalds 72848da7267SDuncan Sands INIT_WORK(&instance->status_checker, (void *)speedtch_check_status, instance); 7291da177e4SLinus Torvalds 73048da7267SDuncan Sands instance->status_checker.timer.function = speedtch_status_poll; 73148da7267SDuncan Sands instance->status_checker.timer.data = (unsigned long)instance; 7321a7aad15SDuncan Sands instance->last_status = 0xff; 73348da7267SDuncan Sands instance->poll_delay = MIN_POLL_DELAY; 7341da177e4SLinus Torvalds 73548da7267SDuncan Sands init_timer(&instance->resubmit_timer); 73648da7267SDuncan Sands instance->resubmit_timer.function = speedtch_resubmit_int; 73748da7267SDuncan Sands instance->resubmit_timer.data = (unsigned long)instance; 7381da177e4SLinus Torvalds 73948da7267SDuncan Sands instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); 7401da177e4SLinus Torvalds 74148da7267SDuncan Sands if (instance->int_urb) 74248da7267SDuncan Sands usb_fill_int_urb(instance->int_urb, usb_dev, 74348da7267SDuncan Sands usb_rcvintpipe(usb_dev, ENDPOINT_INT), 74448da7267SDuncan Sands instance->int_data, sizeof(instance->int_data), 74548da7267SDuncan Sands speedtch_handle_int, instance, 50); 74648da7267SDuncan Sands else 74748da7267SDuncan Sands usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__); 7481da177e4SLinus Torvalds 74948da7267SDuncan Sands /* check whether the modem already seems to be alive */ 75048da7267SDuncan Sands ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 75148da7267SDuncan Sands 0x12, 0xc0, 0x07, 0x00, 75248da7267SDuncan Sands instance->scratch_buffer + OFFSET_7, SIZE_7, 500); 7531da177e4SLinus Torvalds 75435644b0cSDuncan Sands usbatm->flags = (ret == SIZE_7 ? UDSL_SKIP_HEAVY_INIT : 0); 7551da177e4SLinus Torvalds 75635644b0cSDuncan Sands usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, usbatm->flags & UDSL_SKIP_HEAVY_INIT ? "already" : "not"); 7571da177e4SLinus Torvalds 75835644b0cSDuncan Sands if (!(usbatm->flags & UDSL_SKIP_HEAVY_INIT)) 7590ec3c7e8SDuncan Sands if ((ret = usb_reset_device(usb_dev)) < 0) { 7600ec3c7e8SDuncan Sands usb_err(usbatm, "%s: device reset failed (%d)!\n", __func__, ret); 76148da7267SDuncan Sands goto fail_free; 7620ec3c7e8SDuncan Sands } 76348da7267SDuncan Sands 76448da7267SDuncan Sands usbatm->driver_data = instance; 7651da177e4SLinus Torvalds 7661da177e4SLinus Torvalds return 0; 7671da177e4SLinus Torvalds 76848da7267SDuncan Sands fail_free: 76948da7267SDuncan Sands usb_free_urb(instance->int_urb); 7701da177e4SLinus Torvalds kfree(instance); 77148da7267SDuncan Sands fail_release: 77248da7267SDuncan Sands speedtch_release_interfaces(usb_dev, num_interfaces); 77348da7267SDuncan Sands return ret; 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds 77648da7267SDuncan Sands static void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *intf) 7771da177e4SLinus Torvalds { 77848da7267SDuncan Sands struct usb_device *usb_dev = interface_to_usbdev(intf); 77948da7267SDuncan Sands struct speedtch_instance_data *instance = usbatm->driver_data; 7801da177e4SLinus Torvalds 78148da7267SDuncan Sands usb_dbg(usbatm, "%s entered\n", __func__); 7821da177e4SLinus Torvalds 78348da7267SDuncan Sands speedtch_release_interfaces(usb_dev, usb_dev->actconfig->desc.bNumInterfaces); 78448da7267SDuncan Sands usb_free_urb(instance->int_urb); 78548da7267SDuncan Sands kfree(instance); 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds 7891da177e4SLinus Torvalds /*********** 7901da177e4SLinus Torvalds ** init ** 7911da177e4SLinus Torvalds ***********/ 7921da177e4SLinus Torvalds 79348da7267SDuncan Sands static struct usbatm_driver speedtch_usbatm_driver = { 79448da7267SDuncan Sands .driver_name = speedtch_driver_name, 79548da7267SDuncan Sands .bind = speedtch_bind, 79648da7267SDuncan Sands .heavy_init = speedtch_heavy_init, 79748da7267SDuncan Sands .unbind = speedtch_unbind, 79848da7267SDuncan Sands .atm_start = speedtch_atm_start, 79948da7267SDuncan Sands .atm_stop = speedtch_atm_stop, 80048da7267SDuncan Sands .in = ENDPOINT_DATA, 80148da7267SDuncan Sands .out = ENDPOINT_DATA 80248da7267SDuncan Sands }; 80348da7267SDuncan Sands 80448da7267SDuncan Sands static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) 80548da7267SDuncan Sands { 80648da7267SDuncan Sands return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver); 80748da7267SDuncan Sands } 80848da7267SDuncan Sands 8091da177e4SLinus Torvalds static int __init speedtch_usb_init(void) 8101da177e4SLinus Torvalds { 81148da7267SDuncan Sands dbg("%s: driver version %s", __func__, DRIVER_VERSION); 8121da177e4SLinus Torvalds 8131da177e4SLinus Torvalds return usb_register(&speedtch_usb_driver); 8141da177e4SLinus Torvalds } 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds static void __exit speedtch_usb_cleanup(void) 8171da177e4SLinus Torvalds { 81848da7267SDuncan Sands dbg("%s", __func__); 8191da177e4SLinus Torvalds 8201da177e4SLinus Torvalds usb_deregister(&speedtch_usb_driver); 8211da177e4SLinus Torvalds } 8221da177e4SLinus Torvalds 8231da177e4SLinus Torvalds module_init(speedtch_usb_init); 8241da177e4SLinus Torvalds module_exit(speedtch_usb_cleanup); 8251da177e4SLinus Torvalds 8261da177e4SLinus Torvalds MODULE_AUTHOR(DRIVER_AUTHOR); 8271da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC); 8281da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 8291da177e4SLinus Torvalds MODULE_VERSION(DRIVER_VERSION); 830