18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001, Alcatel 68c2ecf20Sopenharmony_ci * Copyright (C) 2003, Duncan Sands 78c2ecf20Sopenharmony_ci * Copyright (C) 2004, David Woodhouse 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on "modem_run.c", copyright (C) 2001, Benoit Papillault 108c2ecf20Sopenharmony_ci ******************************************************************************/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/page.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/firmware.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/stat.h> 218c2ecf20Sopenharmony_ci#include <linux/timer.h> 228c2ecf20Sopenharmony_ci#include <linux/types.h> 238c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h> 248c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "usbatm.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>" 298c2ecf20Sopenharmony_ci#define DRIVER_DESC "Alcatel SpeedTouch USB driver" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic const char speedtch_driver_name[] = "speedtch"; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define CTRL_TIMEOUT 2000 /* milliseconds */ 348c2ecf20Sopenharmony_ci#define DATA_TIMEOUT 2000 /* milliseconds */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define OFFSET_7 0 /* size 1 */ 378c2ecf20Sopenharmony_ci#define OFFSET_b 1 /* size 8 */ 388c2ecf20Sopenharmony_ci#define OFFSET_d 9 /* size 4 */ 398c2ecf20Sopenharmony_ci#define OFFSET_e 13 /* size 1 */ 408c2ecf20Sopenharmony_ci#define OFFSET_f 14 /* size 1 */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define SIZE_7 1 438c2ecf20Sopenharmony_ci#define SIZE_b 8 448c2ecf20Sopenharmony_ci#define SIZE_d 4 458c2ecf20Sopenharmony_ci#define SIZE_e 1 468c2ecf20Sopenharmony_ci#define SIZE_f 1 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define MIN_POLL_DELAY 5000 /* milliseconds */ 498c2ecf20Sopenharmony_ci#define MAX_POLL_DELAY 60000 /* milliseconds */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define RESUBMIT_DELAY 1000 /* milliseconds */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define DEFAULT_BULK_ALTSETTING 1 548c2ecf20Sopenharmony_ci#define DEFAULT_ISOC_ALTSETTING 3 558c2ecf20Sopenharmony_ci#define DEFAULT_DL_512_FIRST 0 568c2ecf20Sopenharmony_ci#define DEFAULT_ENABLE_ISOC 0 578c2ecf20Sopenharmony_ci#define DEFAULT_SW_BUFFERING 0 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic unsigned int altsetting = 0; /* zero means: use the default */ 608c2ecf20Sopenharmony_cistatic bool dl_512_first = DEFAULT_DL_512_FIRST; 618c2ecf20Sopenharmony_cistatic bool enable_isoc = DEFAULT_ENABLE_ISOC; 628c2ecf20Sopenharmony_cistatic bool sw_buffering = DEFAULT_SW_BUFFERING; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define DEFAULT_B_MAX_DSL 8128 658c2ecf20Sopenharmony_ci#define DEFAULT_MODEM_MODE 11 668c2ecf20Sopenharmony_ci#define MODEM_OPTION_LENGTH 16 678c2ecf20Sopenharmony_cistatic const unsigned char DEFAULT_MODEM_OPTION[MODEM_OPTION_LENGTH] = { 688c2ecf20Sopenharmony_ci 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic unsigned int BMaxDSL = DEFAULT_B_MAX_DSL; 728c2ecf20Sopenharmony_cistatic unsigned char ModemMode = DEFAULT_MODEM_MODE; 738c2ecf20Sopenharmony_cistatic unsigned char ModemOption[MODEM_OPTION_LENGTH]; 748c2ecf20Sopenharmony_cistatic unsigned int num_ModemOption; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cimodule_param(altsetting, uint, S_IRUGO | S_IWUSR); 778c2ecf20Sopenharmony_ciMODULE_PARM_DESC(altsetting, 788c2ecf20Sopenharmony_ci "Alternative setting for data interface (bulk_default: " 798c2ecf20Sopenharmony_ci __MODULE_STRING(DEFAULT_BULK_ALTSETTING) "; isoc_default: " 808c2ecf20Sopenharmony_ci __MODULE_STRING(DEFAULT_ISOC_ALTSETTING) ")"); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cimodule_param(dl_512_first, bool, S_IRUGO | S_IWUSR); 838c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dl_512_first, 848c2ecf20Sopenharmony_ci "Read 512 bytes before sending firmware (default: " 858c2ecf20Sopenharmony_ci __MODULE_STRING(DEFAULT_DL_512_FIRST) ")"); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cimodule_param(enable_isoc, bool, S_IRUGO | S_IWUSR); 888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable_isoc, 898c2ecf20Sopenharmony_ci "Use isochronous transfers if available (default: " 908c2ecf20Sopenharmony_ci __MODULE_STRING(DEFAULT_ENABLE_ISOC) ")"); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cimodule_param(sw_buffering, bool, S_IRUGO | S_IWUSR); 938c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sw_buffering, 948c2ecf20Sopenharmony_ci "Enable software buffering (default: " 958c2ecf20Sopenharmony_ci __MODULE_STRING(DEFAULT_SW_BUFFERING) ")"); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cimodule_param(BMaxDSL, uint, S_IRUGO | S_IWUSR); 988c2ecf20Sopenharmony_ciMODULE_PARM_DESC(BMaxDSL, 998c2ecf20Sopenharmony_ci "default: " __MODULE_STRING(DEFAULT_B_MAX_DSL)); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cimodule_param(ModemMode, byte, S_IRUGO | S_IWUSR); 1028c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ModemMode, 1038c2ecf20Sopenharmony_ci "default: " __MODULE_STRING(DEFAULT_MODEM_MODE)); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cimodule_param_array(ModemOption, byte, &num_ModemOption, S_IRUGO); 1068c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ModemOption, "default: 0x10,0x00,0x00,0x00,0x20"); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define INTERFACE_DATA 1 1098c2ecf20Sopenharmony_ci#define ENDPOINT_INT 0x81 1108c2ecf20Sopenharmony_ci#define ENDPOINT_BULK_DATA 0x07 1118c2ecf20Sopenharmony_ci#define ENDPOINT_ISOC_DATA 0x07 1128c2ecf20Sopenharmony_ci#define ENDPOINT_FIRMWARE 0x05 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistruct speedtch_params { 1158c2ecf20Sopenharmony_ci unsigned int altsetting; 1168c2ecf20Sopenharmony_ci unsigned int BMaxDSL; 1178c2ecf20Sopenharmony_ci unsigned char ModemMode; 1188c2ecf20Sopenharmony_ci unsigned char ModemOption[MODEM_OPTION_LENGTH]; 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistruct speedtch_instance_data { 1228c2ecf20Sopenharmony_ci struct usbatm_data *usbatm; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci struct speedtch_params params; /* set in probe, constant afterwards */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci struct timer_list status_check_timer; 1278c2ecf20Sopenharmony_ci struct work_struct status_check_work; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci unsigned char last_status; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci int poll_delay; /* milliseconds */ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci struct timer_list resubmit_timer; 1348c2ecf20Sopenharmony_ci struct urb *int_urb; 1358c2ecf20Sopenharmony_ci unsigned char int_data[16]; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci unsigned char scratch_buffer[16]; 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/*************** 1418c2ecf20Sopenharmony_ci** firmware ** 1428c2ecf20Sopenharmony_ci***************/ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void speedtch_set_swbuff(struct speedtch_instance_data *instance, int state) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 1478c2ecf20Sopenharmony_ci struct usb_device *usb_dev = usbatm->usb_dev; 1488c2ecf20Sopenharmony_ci int ret; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 1518c2ecf20Sopenharmony_ci 0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, CTRL_TIMEOUT); 1528c2ecf20Sopenharmony_ci if (ret < 0) 1538c2ecf20Sopenharmony_ci usb_warn(usbatm, 1548c2ecf20Sopenharmony_ci "%sabling SW buffering: usb_control_msg returned %d\n", 1558c2ecf20Sopenharmony_ci state ? "En" : "Dis", ret); 1568c2ecf20Sopenharmony_ci else 1578c2ecf20Sopenharmony_ci usb_dbg(usbatm, "speedtch_set_swbuff: %sbled SW buffering\n", state ? "En" : "Dis"); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void speedtch_test_sequence(struct speedtch_instance_data *instance) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 1638c2ecf20Sopenharmony_ci struct usb_device *usb_dev = usbatm->usb_dev; 1648c2ecf20Sopenharmony_ci unsigned char *buf = instance->scratch_buffer; 1658c2ecf20Sopenharmony_ci int ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* URB 147 */ 1688c2ecf20Sopenharmony_ci buf[0] = 0x1c; 1698c2ecf20Sopenharmony_ci buf[1] = 0x50; 1708c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 1718c2ecf20Sopenharmony_ci 0x01, 0x40, 0x0b, 0x00, buf, 2, CTRL_TIMEOUT); 1728c2ecf20Sopenharmony_ci if (ret < 0) 1738c2ecf20Sopenharmony_ci usb_warn(usbatm, "%s failed on URB147: %d\n", __func__, ret); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* URB 148 */ 1768c2ecf20Sopenharmony_ci buf[0] = 0x32; 1778c2ecf20Sopenharmony_ci buf[1] = 0x00; 1788c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 1798c2ecf20Sopenharmony_ci 0x01, 0x40, 0x02, 0x00, buf, 2, CTRL_TIMEOUT); 1808c2ecf20Sopenharmony_ci if (ret < 0) 1818c2ecf20Sopenharmony_ci usb_warn(usbatm, "%s failed on URB148: %d\n", __func__, ret); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* URB 149 */ 1848c2ecf20Sopenharmony_ci buf[0] = 0x01; 1858c2ecf20Sopenharmony_ci buf[1] = 0x00; 1868c2ecf20Sopenharmony_ci buf[2] = 0x01; 1878c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 1888c2ecf20Sopenharmony_ci 0x01, 0x40, 0x03, 0x00, buf, 3, CTRL_TIMEOUT); 1898c2ecf20Sopenharmony_ci if (ret < 0) 1908c2ecf20Sopenharmony_ci usb_warn(usbatm, "%s failed on URB149: %d\n", __func__, ret); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* URB 150 */ 1938c2ecf20Sopenharmony_ci buf[0] = 0x01; 1948c2ecf20Sopenharmony_ci buf[1] = 0x00; 1958c2ecf20Sopenharmony_ci buf[2] = 0x01; 1968c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 1978c2ecf20Sopenharmony_ci 0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT); 1988c2ecf20Sopenharmony_ci if (ret < 0) 1998c2ecf20Sopenharmony_ci usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Extra initialisation in recent drivers - gives higher speeds */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* URBext1 */ 2048c2ecf20Sopenharmony_ci buf[0] = instance->params.ModemMode; 2058c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 2068c2ecf20Sopenharmony_ci 0x01, 0x40, 0x11, 0x00, buf, 1, CTRL_TIMEOUT); 2078c2ecf20Sopenharmony_ci if (ret < 0) 2088c2ecf20Sopenharmony_ci usb_warn(usbatm, "%s failed on URBext1: %d\n", __func__, ret); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* URBext2 */ 2118c2ecf20Sopenharmony_ci /* This seems to be the one which actually triggers the higher sync 2128c2ecf20Sopenharmony_ci rate -- it does require the new firmware too, although it works OK 2138c2ecf20Sopenharmony_ci with older firmware */ 2148c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 2158c2ecf20Sopenharmony_ci 0x01, 0x40, 0x14, 0x00, 2168c2ecf20Sopenharmony_ci instance->params.ModemOption, 2178c2ecf20Sopenharmony_ci MODEM_OPTION_LENGTH, CTRL_TIMEOUT); 2188c2ecf20Sopenharmony_ci if (ret < 0) 2198c2ecf20Sopenharmony_ci usb_warn(usbatm, "%s failed on URBext2: %d\n", __func__, ret); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* URBext3 */ 2228c2ecf20Sopenharmony_ci buf[0] = instance->params.BMaxDSL & 0xff; 2238c2ecf20Sopenharmony_ci buf[1] = instance->params.BMaxDSL >> 8; 2248c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), 2258c2ecf20Sopenharmony_ci 0x01, 0x40, 0x12, 0x00, buf, 2, CTRL_TIMEOUT); 2268c2ecf20Sopenharmony_ci if (ret < 0) 2278c2ecf20Sopenharmony_ci usb_warn(usbatm, "%s failed on URBext3: %d\n", __func__, ret); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int speedtch_upload_firmware(struct speedtch_instance_data *instance, 2318c2ecf20Sopenharmony_ci const struct firmware *fw1, 2328c2ecf20Sopenharmony_ci const struct firmware *fw2) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci unsigned char *buffer; 2358c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 2368c2ecf20Sopenharmony_ci struct usb_device *usb_dev = usbatm->usb_dev; 2378c2ecf20Sopenharmony_ci int actual_length; 2388c2ecf20Sopenharmony_ci int ret = 0; 2398c2ecf20Sopenharmony_ci int offset; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s entered\n", __func__); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci buffer = (unsigned char *)__get_free_page(GFP_KERNEL); 2448c2ecf20Sopenharmony_ci if (!buffer) { 2458c2ecf20Sopenharmony_ci ret = -ENOMEM; 2468c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__); 2478c2ecf20Sopenharmony_ci goto out; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (!usb_ifnum_to_if(usb_dev, 2)) { 2518c2ecf20Sopenharmony_ci ret = -ENODEV; 2528c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: interface not found!\n", __func__); 2538c2ecf20Sopenharmony_ci goto out_free; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* URB 7 */ 2578c2ecf20Sopenharmony_ci if (dl_512_first) { /* some modems need a read before writing the firmware */ 2588c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2598c2ecf20Sopenharmony_ci buffer, 0x200, &actual_length, 2000); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ETIMEDOUT) 2628c2ecf20Sopenharmony_ci usb_warn(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret); 2638c2ecf20Sopenharmony_ci else 2648c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* URB 8 : both leds are static green */ 2688c2ecf20Sopenharmony_ci for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { 2698c2ecf20Sopenharmony_ci int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); 2708c2ecf20Sopenharmony_ci memcpy(buffer, fw1->data + offset, thislen); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2738c2ecf20Sopenharmony_ci buffer, thislen, &actual_length, DATA_TIMEOUT); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (ret < 0) { 2768c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret); 2778c2ecf20Sopenharmony_ci goto out_free; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* USB led blinking green, ADSL led off */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* URB 11 */ 2858c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 2868c2ecf20Sopenharmony_ci buffer, 0x200, &actual_length, DATA_TIMEOUT); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (ret < 0) { 2898c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret); 2908c2ecf20Sopenharmony_ci goto out_free; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* URBs 12 to 139 - USB led blinking green, ADSL led off */ 2958c2ecf20Sopenharmony_ci for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { 2968c2ecf20Sopenharmony_ci int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); 2978c2ecf20Sopenharmony_ci memcpy(buffer, fw2->data + offset, thislen); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 3008c2ecf20Sopenharmony_ci buffer, thislen, &actual_length, DATA_TIMEOUT); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (ret < 0) { 3038c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret); 3048c2ecf20Sopenharmony_ci goto out_free; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: BLOCK3 uploaded (%zu bytes)\n", __func__, fw2->size); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* USB led static green, ADSL led static red */ 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* URB 142 */ 3128c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE), 3138c2ecf20Sopenharmony_ci buffer, 0x200, &actual_length, DATA_TIMEOUT); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (ret < 0) { 3168c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret); 3178c2ecf20Sopenharmony_ci goto out_free; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* success */ 3218c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: BLOCK4 downloaded (%d bytes)\n", __func__, actual_length); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Delay to allow firmware to start up. We can do this here 3248c2ecf20Sopenharmony_ci because we're in our own kernel thread anyway. */ 3258c2ecf20Sopenharmony_ci msleep_interruptible(1000); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) { 3288c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: setting interface to %d failed (%d)!\n", __func__, instance->params.altsetting, ret); 3298c2ecf20Sopenharmony_ci goto out_free; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Enable software buffering, if requested */ 3338c2ecf20Sopenharmony_ci if (sw_buffering) 3348c2ecf20Sopenharmony_ci speedtch_set_swbuff(instance, 1); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Magic spell; don't ask us what this does */ 3378c2ecf20Sopenharmony_ci speedtch_test_sequence(instance); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ciout_free: 3428c2ecf20Sopenharmony_ci free_page((unsigned long)buffer); 3438c2ecf20Sopenharmony_ciout: 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int speedtch_find_firmware(struct usbatm_data *usbatm, struct usb_interface *intf, 3488c2ecf20Sopenharmony_ci int phase, const struct firmware **fw_p) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct device *dev = &intf->dev; 3518c2ecf20Sopenharmony_ci const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice); 3528c2ecf20Sopenharmony_ci const u8 major_revision = bcdDevice >> 8; 3538c2ecf20Sopenharmony_ci const u8 minor_revision = bcdDevice & 0xff; 3548c2ecf20Sopenharmony_ci char buf[24]; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); 3578c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (request_firmware(fw_p, buf, dev)) { 3608c2ecf20Sopenharmony_ci sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); 3618c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (request_firmware(fw_p, buf, dev)) { 3648c2ecf20Sopenharmony_ci sprintf(buf, "speedtch-%d.bin", phase); 3658c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (request_firmware(fw_p, buf, dev)) { 3688c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: no stage %d firmware found!\n", __func__, phase); 3698c2ecf20Sopenharmony_ci return -ENOENT; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci usb_info(usbatm, "found stage %d firmware %s\n", phase, buf); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface *intf) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci const struct firmware *fw1, *fw2; 3828c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance = usbatm->driver_data; 3838c2ecf20Sopenharmony_ci int ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if ((ret = speedtch_find_firmware(usbatm, intf, 1, &fw1)) < 0) 3868c2ecf20Sopenharmony_ci return ret; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if ((ret = speedtch_find_firmware(usbatm, intf, 2, &fw2)) < 0) { 3898c2ecf20Sopenharmony_ci release_firmware(fw1); 3908c2ecf20Sopenharmony_ci return ret; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if ((ret = speedtch_upload_firmware(instance, fw1, fw2)) < 0) 3948c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: firmware upload failed (%d)!\n", __func__, ret); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci release_firmware(fw2); 3978c2ecf20Sopenharmony_ci release_firmware(fw1); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return ret; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/********** 4048c2ecf20Sopenharmony_ci** ATM ** 4058c2ecf20Sopenharmony_ci**********/ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int speedtch_read_status(struct speedtch_instance_data *instance) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 4108c2ecf20Sopenharmony_ci struct usb_device *usb_dev = usbatm->usb_dev; 4118c2ecf20Sopenharmony_ci unsigned char *buf = instance->scratch_buffer; 4128c2ecf20Sopenharmony_ci int ret; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci memset(buf, 0, 16); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 4178c2ecf20Sopenharmony_ci 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7, 4188c2ecf20Sopenharmony_ci CTRL_TIMEOUT); 4198c2ecf20Sopenharmony_ci if (ret < 0) { 4208c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: MSG 7 failed\n", __func__); 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 4258c2ecf20Sopenharmony_ci 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b, 4268c2ecf20Sopenharmony_ci CTRL_TIMEOUT); 4278c2ecf20Sopenharmony_ci if (ret < 0) { 4288c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: MSG B failed\n", __func__); 4298c2ecf20Sopenharmony_ci return ret; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 4338c2ecf20Sopenharmony_ci 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d, 4348c2ecf20Sopenharmony_ci CTRL_TIMEOUT); 4358c2ecf20Sopenharmony_ci if (ret < 0) { 4368c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: MSG D failed\n", __func__); 4378c2ecf20Sopenharmony_ci return ret; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 4418c2ecf20Sopenharmony_ci 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e, 4428c2ecf20Sopenharmony_ci CTRL_TIMEOUT); 4438c2ecf20Sopenharmony_ci if (ret < 0) { 4448c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: MSG E failed\n", __func__); 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 4498c2ecf20Sopenharmony_ci 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f, 4508c2ecf20Sopenharmony_ci CTRL_TIMEOUT); 4518c2ecf20Sopenharmony_ci if (ret < 0) { 4528c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: MSG F failed\n", __func__); 4538c2ecf20Sopenharmony_ci return ret; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int speedtch_start_synchro(struct speedtch_instance_data *instance) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 4628c2ecf20Sopenharmony_ci struct usb_device *usb_dev = usbatm->usb_dev; 4638c2ecf20Sopenharmony_ci unsigned char *buf = instance->scratch_buffer; 4648c2ecf20Sopenharmony_ci int ret; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s entered\n", __func__); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci memset(buf, 0, 2); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 4718c2ecf20Sopenharmony_ci 0x12, 0xc0, 0x04, 0x00, 4728c2ecf20Sopenharmony_ci buf, 2, CTRL_TIMEOUT); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (ret < 0) 4758c2ecf20Sopenharmony_ci atm_warn(usbatm, "failed to start ADSL synchronisation: %d\n", ret); 4768c2ecf20Sopenharmony_ci else 4778c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: modem prodded. %d bytes returned: %02x %02x\n", 4788c2ecf20Sopenharmony_ci __func__, ret, buf[0], buf[1]); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic void speedtch_check_status(struct work_struct *work) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance = 4868c2ecf20Sopenharmony_ci container_of(work, struct speedtch_instance_data, 4878c2ecf20Sopenharmony_ci status_check_work); 4888c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 4898c2ecf20Sopenharmony_ci struct atm_dev *atm_dev = usbatm->atm_dev; 4908c2ecf20Sopenharmony_ci unsigned char *buf = instance->scratch_buffer; 4918c2ecf20Sopenharmony_ci int down_speed, up_speed, ret; 4928c2ecf20Sopenharmony_ci unsigned char status; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci#ifdef VERBOSE_DEBUG 4958c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s entered\n", __func__); 4968c2ecf20Sopenharmony_ci#endif 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ret = speedtch_read_status(instance); 4998c2ecf20Sopenharmony_ci if (ret < 0) { 5008c2ecf20Sopenharmony_ci atm_warn(usbatm, "error %d fetching device status\n", ret); 5018c2ecf20Sopenharmony_ci instance->poll_delay = min(2 * instance->poll_delay, MAX_POLL_DELAY); 5028c2ecf20Sopenharmony_ci return; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci instance->poll_delay = max(instance->poll_delay / 2, MIN_POLL_DELAY); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci status = buf[OFFSET_7]; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if ((status != instance->last_status) || !status) { 5108c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: line state 0x%02x\n", __func__, status); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci switch (status) { 5138c2ecf20Sopenharmony_ci case 0: 5148c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 5158c2ecf20Sopenharmony_ci if (instance->last_status) 5168c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line is down\n"); 5178c2ecf20Sopenharmony_ci /* It may never resync again unless we ask it to... */ 5188c2ecf20Sopenharmony_ci ret = speedtch_start_synchro(instance); 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci case 0x08: 5228c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN); 5238c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line is blocked?\n"); 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci case 0x10: 5278c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 5288c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line is synchronising\n"); 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci case 0x20: 5328c2ecf20Sopenharmony_ci down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8) 5338c2ecf20Sopenharmony_ci | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24); 5348c2ecf20Sopenharmony_ci up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8) 5358c2ecf20Sopenharmony_ci | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (!(down_speed & 0x0000ffff) && !(up_speed & 0x0000ffff)) { 5388c2ecf20Sopenharmony_ci down_speed >>= 16; 5398c2ecf20Sopenharmony_ci up_speed >>= 16; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci atm_dev->link_rate = down_speed * 1000 / 424; 5438c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_FOUND); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci atm_info(usbatm, 5468c2ecf20Sopenharmony_ci "ADSL line is up (%d kb/s down | %d kb/s up)\n", 5478c2ecf20Sopenharmony_ci down_speed, up_speed); 5488c2ecf20Sopenharmony_ci break; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci default: 5518c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN); 5528c2ecf20Sopenharmony_ci atm_info(usbatm, "unknown line state %02x\n", status); 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci instance->last_status = status; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic void speedtch_status_poll(struct timer_list *t) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance = from_timer(instance, t, 5638c2ecf20Sopenharmony_ci status_check_timer); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci schedule_work(&instance->status_check_work); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* The following check is racy, but the race is harmless */ 5688c2ecf20Sopenharmony_ci if (instance->poll_delay < MAX_POLL_DELAY) 5698c2ecf20Sopenharmony_ci mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(instance->poll_delay)); 5708c2ecf20Sopenharmony_ci else 5718c2ecf20Sopenharmony_ci atm_warn(instance->usbatm, "Too many failures - disabling line status polling\n"); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic void speedtch_resubmit_int(struct timer_list *t) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance = from_timer(instance, t, 5778c2ecf20Sopenharmony_ci resubmit_timer); 5788c2ecf20Sopenharmony_ci struct urb *int_urb = instance->int_urb; 5798c2ecf20Sopenharmony_ci int ret; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci atm_dbg(instance->usbatm, "%s entered\n", __func__); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (int_urb) { 5848c2ecf20Sopenharmony_ci ret = usb_submit_urb(int_urb, GFP_ATOMIC); 5858c2ecf20Sopenharmony_ci if (!ret) 5868c2ecf20Sopenharmony_ci schedule_work(&instance->status_check_work); 5878c2ecf20Sopenharmony_ci else { 5888c2ecf20Sopenharmony_ci atm_dbg(instance->usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret); 5898c2ecf20Sopenharmony_ci mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic void speedtch_handle_int(struct urb *int_urb) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance = int_urb->context; 5978c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 5988c2ecf20Sopenharmony_ci unsigned int count = int_urb->actual_length; 5998c2ecf20Sopenharmony_ci int status = int_urb->status; 6008c2ecf20Sopenharmony_ci int ret; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* The magic interrupt for "up state" */ 6038c2ecf20Sopenharmony_ci static const unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; 6048c2ecf20Sopenharmony_ci /* The magic interrupt for "down state" */ 6058c2ecf20Sopenharmony_ci static const unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 }; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s entered\n", __func__); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (status < 0) { 6108c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, status); 6118c2ecf20Sopenharmony_ci goto fail; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) { 6158c2ecf20Sopenharmony_ci del_timer(&instance->status_check_timer); 6168c2ecf20Sopenharmony_ci atm_info(usbatm, "DSL line goes up\n"); 6178c2ecf20Sopenharmony_ci } else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) { 6188c2ecf20Sopenharmony_ci atm_info(usbatm, "DSL line goes down\n"); 6198c2ecf20Sopenharmony_ci } else { 6208c2ecf20Sopenharmony_ci int i; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: unknown interrupt packet of length %d:", __func__, count); 6238c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 6248c2ecf20Sopenharmony_ci printk(" %02x", instance->int_data[i]); 6258c2ecf20Sopenharmony_ci printk("\n"); 6268c2ecf20Sopenharmony_ci goto fail; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci int_urb = instance->int_urb; 6308c2ecf20Sopenharmony_ci if (int_urb) { 6318c2ecf20Sopenharmony_ci ret = usb_submit_urb(int_urb, GFP_ATOMIC); 6328c2ecf20Sopenharmony_ci schedule_work(&instance->status_check_work); 6338c2ecf20Sopenharmony_ci if (ret < 0) { 6348c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret); 6358c2ecf20Sopenharmony_ci goto fail; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cifail: 6428c2ecf20Sopenharmony_ci int_urb = instance->int_urb; 6438c2ecf20Sopenharmony_ci if (int_urb) 6448c2ecf20Sopenharmony_ci mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_dev) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct usb_device *usb_dev = usbatm->usb_dev; 6508c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance = usbatm->driver_data; 6518c2ecf20Sopenharmony_ci int i, ret; 6528c2ecf20Sopenharmony_ci unsigned char mac_str[13]; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s entered\n", __func__); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Set MAC address, it is stored in the serial number */ 6578c2ecf20Sopenharmony_ci memset(atm_dev->esi, 0, sizeof(atm_dev->esi)); 6588c2ecf20Sopenharmony_ci if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { 6598c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 6608c2ecf20Sopenharmony_ci atm_dev->esi[i] = (hex_to_bin(mac_str[i * 2]) << 4) + 6618c2ecf20Sopenharmony_ci hex_to_bin(mac_str[i * 2 + 1]); 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* Start modem synchronisation */ 6658c2ecf20Sopenharmony_ci ret = speedtch_start_synchro(instance); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* Set up interrupt endpoint */ 6688c2ecf20Sopenharmony_ci if (instance->int_urb) { 6698c2ecf20Sopenharmony_ci ret = usb_submit_urb(instance->int_urb, GFP_KERNEL); 6708c2ecf20Sopenharmony_ci if (ret < 0) { 6718c2ecf20Sopenharmony_ci /* Doesn't matter; we'll poll anyway */ 6728c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s: submission of interrupt URB failed (%d)!\n", __func__, ret); 6738c2ecf20Sopenharmony_ci usb_free_urb(instance->int_urb); 6748c2ecf20Sopenharmony_ci instance->int_urb = NULL; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* Start status polling */ 6798c2ecf20Sopenharmony_ci mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(1000)); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci return 0; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_dev) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance = usbatm->driver_data; 6878c2ecf20Sopenharmony_ci struct urb *int_urb = instance->int_urb; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s entered\n", __func__); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci del_timer_sync(&instance->status_check_timer); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* 6948c2ecf20Sopenharmony_ci * Since resubmit_timer and int_urb can schedule themselves and 6958c2ecf20Sopenharmony_ci * each other, shutting them down correctly takes some care 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_ci instance->int_urb = NULL; /* signal shutdown */ 6988c2ecf20Sopenharmony_ci mb(); 6998c2ecf20Sopenharmony_ci usb_kill_urb(int_urb); 7008c2ecf20Sopenharmony_ci del_timer_sync(&instance->resubmit_timer); 7018c2ecf20Sopenharmony_ci /* 7028c2ecf20Sopenharmony_ci * At this point, speedtch_handle_int and speedtch_resubmit_int 7038c2ecf20Sopenharmony_ci * can run or be running, but instance->int_urb == NULL means that 7048c2ecf20Sopenharmony_ci * they will not reschedule 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci usb_kill_urb(int_urb); 7078c2ecf20Sopenharmony_ci del_timer_sync(&instance->resubmit_timer); 7088c2ecf20Sopenharmony_ci usb_free_urb(int_urb); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci flush_work(&instance->status_check_work); 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic int speedtch_pre_reset(struct usb_interface *intf) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int speedtch_post_reset(struct usb_interface *intf) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci return 0; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci/********** 7258c2ecf20Sopenharmony_ci** USB ** 7268c2ecf20Sopenharmony_ci**********/ 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic const struct usb_device_id speedtch_usb_ids[] = { 7298c2ecf20Sopenharmony_ci {USB_DEVICE(0x06b9, 0x4061)}, 7308c2ecf20Sopenharmony_ci {} 7318c2ecf20Sopenharmony_ci}; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, speedtch_usb_ids); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic struct usb_driver speedtch_usb_driver = { 7388c2ecf20Sopenharmony_ci .name = speedtch_driver_name, 7398c2ecf20Sopenharmony_ci .probe = speedtch_usb_probe, 7408c2ecf20Sopenharmony_ci .disconnect = usbatm_usb_disconnect, 7418c2ecf20Sopenharmony_ci .pre_reset = speedtch_pre_reset, 7428c2ecf20Sopenharmony_ci .post_reset = speedtch_post_reset, 7438c2ecf20Sopenharmony_ci .id_table = speedtch_usb_ids 7448c2ecf20Sopenharmony_ci}; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic void speedtch_release_interfaces(struct usb_device *usb_dev, 7478c2ecf20Sopenharmony_ci int num_interfaces) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci struct usb_interface *cur_intf; 7508c2ecf20Sopenharmony_ci int i; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci for (i = 0; i < num_interfaces; i++) { 7538c2ecf20Sopenharmony_ci cur_intf = usb_ifnum_to_if(usb_dev, i); 7548c2ecf20Sopenharmony_ci if (cur_intf) { 7558c2ecf20Sopenharmony_ci usb_set_intfdata(cur_intf, NULL); 7568c2ecf20Sopenharmony_ci usb_driver_release_interface(&speedtch_usb_driver, cur_intf); 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic int speedtch_bind(struct usbatm_data *usbatm, 7628c2ecf20Sopenharmony_ci struct usb_interface *intf, 7638c2ecf20Sopenharmony_ci const struct usb_device_id *id) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 7668c2ecf20Sopenharmony_ci struct usb_interface *cur_intf, *data_intf; 7678c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance; 7688c2ecf20Sopenharmony_ci int ifnum = intf->altsetting->desc.bInterfaceNumber; 7698c2ecf20Sopenharmony_ci int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces; 7708c2ecf20Sopenharmony_ci int i, ret; 7718c2ecf20Sopenharmony_ci int use_isoc; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s entered\n", __func__); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* sanity checks */ 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { 7788c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass); 7798c2ecf20Sopenharmony_ci return -ENODEV; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA); 7838c2ecf20Sopenharmony_ci if (!data_intf) { 7848c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: data interface not found!\n", __func__); 7858c2ecf20Sopenharmony_ci return -ENODEV; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* claim all interfaces */ 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci for (i = 0; i < num_interfaces; i++) { 7918c2ecf20Sopenharmony_ci cur_intf = usb_ifnum_to_if(usb_dev, i); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if ((i != ifnum) && cur_intf) { 7948c2ecf20Sopenharmony_ci ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (ret < 0) { 7978c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, i, ret); 7988c2ecf20Sopenharmony_ci speedtch_release_interfaces(usb_dev, i); 7998c2ecf20Sopenharmony_ci return ret; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci instance = kzalloc(sizeof(*instance), GFP_KERNEL); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (!instance) { 8078c2ecf20Sopenharmony_ci ret = -ENOMEM; 8088c2ecf20Sopenharmony_ci goto fail_release; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci instance->usbatm = usbatm; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* module parameters may change at any moment, so take a snapshot */ 8148c2ecf20Sopenharmony_ci instance->params.altsetting = altsetting; 8158c2ecf20Sopenharmony_ci instance->params.BMaxDSL = BMaxDSL; 8168c2ecf20Sopenharmony_ci instance->params.ModemMode = ModemMode; 8178c2ecf20Sopenharmony_ci memcpy(instance->params.ModemOption, DEFAULT_MODEM_OPTION, MODEM_OPTION_LENGTH); 8188c2ecf20Sopenharmony_ci memcpy(instance->params.ModemOption, ModemOption, num_ModemOption); 8198c2ecf20Sopenharmony_ci use_isoc = enable_isoc; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (instance->params.altsetting) 8228c2ecf20Sopenharmony_ci if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) { 8238c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, instance->params.altsetting, ret); 8248c2ecf20Sopenharmony_ci instance->params.altsetting = 0; /* fall back to default */ 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (!instance->params.altsetting && use_isoc) 8288c2ecf20Sopenharmony_ci if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_ISOC_ALTSETTING)) < 0) { 8298c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_ISOC_ALTSETTING, ret); 8308c2ecf20Sopenharmony_ci use_isoc = 0; /* fall back to bulk */ 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (use_isoc) { 8348c2ecf20Sopenharmony_ci const struct usb_host_interface *desc = data_intf->cur_altsetting; 8358c2ecf20Sopenharmony_ci const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci use_isoc = 0; /* fall back to bulk if endpoint not found */ 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci for (i = 0; i < desc->desc.bNumEndpoints; i++) { 8408c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if ((endpoint_desc->bEndpointAddress == target_address)) { 8438c2ecf20Sopenharmony_ci use_isoc = 8448c2ecf20Sopenharmony_ci usb_endpoint_xfer_isoc(endpoint_desc); 8458c2ecf20Sopenharmony_ci break; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (!use_isoc) 8508c2ecf20Sopenharmony_ci usb_info(usbatm, "isochronous transfer not supported - using bulk\n"); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (!use_isoc && !instance->params.altsetting) 8548c2ecf20Sopenharmony_ci if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_BULK_ALTSETTING)) < 0) { 8558c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_BULK_ALTSETTING, ret); 8568c2ecf20Sopenharmony_ci goto fail_free; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (!instance->params.altsetting) 8608c2ecf20Sopenharmony_ci instance->params.altsetting = use_isoc ? DEFAULT_ISOC_ALTSETTING : DEFAULT_BULK_ALTSETTING; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci INIT_WORK(&instance->status_check_work, speedtch_check_status); 8658c2ecf20Sopenharmony_ci timer_setup(&instance->status_check_timer, speedtch_status_poll, 0); 8668c2ecf20Sopenharmony_ci instance->last_status = 0xff; 8678c2ecf20Sopenharmony_ci instance->poll_delay = MIN_POLL_DELAY; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci timer_setup(&instance->resubmit_timer, speedtch_resubmit_int, 0); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (instance->int_urb) 8748c2ecf20Sopenharmony_ci usb_fill_int_urb(instance->int_urb, usb_dev, 8758c2ecf20Sopenharmony_ci usb_rcvintpipe(usb_dev, ENDPOINT_INT), 8768c2ecf20Sopenharmony_ci instance->int_data, sizeof(instance->int_data), 8778c2ecf20Sopenharmony_ci speedtch_handle_int, instance, 16); 8788c2ecf20Sopenharmony_ci else 8798c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* check whether the modem already seems to be alive */ 8828c2ecf20Sopenharmony_ci ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 8838c2ecf20Sopenharmony_ci 0x12, 0xc0, 0x07, 0x00, 8848c2ecf20Sopenharmony_ci instance->scratch_buffer + OFFSET_7, SIZE_7, 500); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci usbatm->flags |= (ret == SIZE_7 ? UDSL_SKIP_HEAVY_INIT : 0); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, usbatm->flags & UDSL_SKIP_HEAVY_INIT ? "already" : "not"); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (!(usbatm->flags & UDSL_SKIP_HEAVY_INIT)) 8918c2ecf20Sopenharmony_ci if ((ret = usb_reset_device(usb_dev)) < 0) { 8928c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: device reset failed (%d)!\n", __func__, ret); 8938c2ecf20Sopenharmony_ci goto fail_free; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci usbatm->driver_data = instance; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci return 0; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cifail_free: 9018c2ecf20Sopenharmony_ci usb_free_urb(instance->int_urb); 9028c2ecf20Sopenharmony_ci kfree(instance); 9038c2ecf20Sopenharmony_cifail_release: 9048c2ecf20Sopenharmony_ci speedtch_release_interfaces(usb_dev, num_interfaces); 9058c2ecf20Sopenharmony_ci return ret; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *intf) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 9118c2ecf20Sopenharmony_ci struct speedtch_instance_data *instance = usbatm->driver_data; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s entered\n", __func__); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci speedtch_release_interfaces(usb_dev, usb_dev->actconfig->desc.bNumInterfaces); 9168c2ecf20Sopenharmony_ci usb_free_urb(instance->int_urb); 9178c2ecf20Sopenharmony_ci kfree(instance); 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci/*********** 9228c2ecf20Sopenharmony_ci** init ** 9238c2ecf20Sopenharmony_ci***********/ 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic struct usbatm_driver speedtch_usbatm_driver = { 9268c2ecf20Sopenharmony_ci .driver_name = speedtch_driver_name, 9278c2ecf20Sopenharmony_ci .bind = speedtch_bind, 9288c2ecf20Sopenharmony_ci .heavy_init = speedtch_heavy_init, 9298c2ecf20Sopenharmony_ci .unbind = speedtch_unbind, 9308c2ecf20Sopenharmony_ci .atm_start = speedtch_atm_start, 9318c2ecf20Sopenharmony_ci .atm_stop = speedtch_atm_stop, 9328c2ecf20Sopenharmony_ci .bulk_in = ENDPOINT_BULK_DATA, 9338c2ecf20Sopenharmony_ci .bulk_out = ENDPOINT_BULK_DATA, 9348c2ecf20Sopenharmony_ci .isoc_in = ENDPOINT_ISOC_DATA 9358c2ecf20Sopenharmony_ci}; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cimodule_usb_driver(speedtch_usb_driver); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 9458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 9468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 947