18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * cxacru.c - driver for USB ADSL modems based on 48c2ecf20Sopenharmony_ci * Conexant AccessRunner chipset 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) 88c2ecf20Sopenharmony_ci * Copyright (C) 2007 Simon Arlott 98c2ecf20Sopenharmony_ci * Copyright (C) 2009 Simon Arlott 108c2ecf20Sopenharmony_ci ******************************************************************************/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci * Credit is due for Josep Comas, who created the original patch to speedtch.c 148c2ecf20Sopenharmony_ci * to support the different padding used by the AccessRunner (now generalized 158c2ecf20Sopenharmony_ci * into usbatm), and the userspace firmware loading utility. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/timer.h> 228c2ecf20Sopenharmony_ci#include <linux/errno.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/device.h> 258c2ecf20Sopenharmony_ci#include <linux/firmware.h> 268c2ecf20Sopenharmony_ci#include <linux/mutex.h> 278c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "usbatm.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott" 328c2ecf20Sopenharmony_ci#define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic const char cxacru_driver_name[] = "cxacru"; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define CXACRU_EP_CMD 0x01 /* Bulk/interrupt in/out */ 378c2ecf20Sopenharmony_ci#define CXACRU_EP_DATA 0x02 /* Bulk in/out */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define CMD_PACKET_SIZE 64 /* Should be maxpacket(ep)? */ 408c2ecf20Sopenharmony_ci#define CMD_MAX_CONFIG ((CMD_PACKET_SIZE / 4 - 1) / 2) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Addresses */ 438c2ecf20Sopenharmony_ci#define PLLFCLK_ADDR 0x00350068 448c2ecf20Sopenharmony_ci#define PLLBCLK_ADDR 0x0035006c 458c2ecf20Sopenharmony_ci#define SDRAMEN_ADDR 0x00350010 468c2ecf20Sopenharmony_ci#define FW_ADDR 0x00801000 478c2ecf20Sopenharmony_ci#define BR_ADDR 0x00180600 488c2ecf20Sopenharmony_ci#define SIG_ADDR 0x00180500 498c2ecf20Sopenharmony_ci#define BR_STACK_ADDR 0x00187f10 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Values */ 528c2ecf20Sopenharmony_ci#define SDRAM_ENA 0x1 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define CMD_TIMEOUT 2000 /* msecs */ 558c2ecf20Sopenharmony_ci#define POLL_INTERVAL 1 /* secs */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* commands for interaction with the modem through the control channel before 588c2ecf20Sopenharmony_ci * firmware is loaded */ 598c2ecf20Sopenharmony_cienum cxacru_fw_request { 608c2ecf20Sopenharmony_ci FW_CMD_ERR, 618c2ecf20Sopenharmony_ci FW_GET_VER, 628c2ecf20Sopenharmony_ci FW_READ_MEM, 638c2ecf20Sopenharmony_ci FW_WRITE_MEM, 648c2ecf20Sopenharmony_ci FW_RMW_MEM, 658c2ecf20Sopenharmony_ci FW_CHECKSUM_MEM, 668c2ecf20Sopenharmony_ci FW_GOTO_MEM, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* commands for interaction with the modem through the control channel once 708c2ecf20Sopenharmony_ci * firmware is loaded */ 718c2ecf20Sopenharmony_cienum cxacru_cm_request { 728c2ecf20Sopenharmony_ci CM_REQUEST_UNDEFINED = 0x80, 738c2ecf20Sopenharmony_ci CM_REQUEST_TEST, 748c2ecf20Sopenharmony_ci CM_REQUEST_CHIP_GET_MAC_ADDRESS, 758c2ecf20Sopenharmony_ci CM_REQUEST_CHIP_GET_DP_VERSIONS, 768c2ecf20Sopenharmony_ci CM_REQUEST_CHIP_ADSL_LINE_START, 778c2ecf20Sopenharmony_ci CM_REQUEST_CHIP_ADSL_LINE_STOP, 788c2ecf20Sopenharmony_ci CM_REQUEST_CHIP_ADSL_LINE_GET_STATUS, 798c2ecf20Sopenharmony_ci CM_REQUEST_CHIP_ADSL_LINE_GET_SPEED, 808c2ecf20Sopenharmony_ci CM_REQUEST_CARD_INFO_GET, 818c2ecf20Sopenharmony_ci CM_REQUEST_CARD_DATA_GET, 828c2ecf20Sopenharmony_ci CM_REQUEST_CARD_DATA_SET, 838c2ecf20Sopenharmony_ci CM_REQUEST_COMMAND_HW_IO, 848c2ecf20Sopenharmony_ci CM_REQUEST_INTERFACE_HW_IO, 858c2ecf20Sopenharmony_ci CM_REQUEST_CARD_SERIAL_DATA_PATH_GET, 868c2ecf20Sopenharmony_ci CM_REQUEST_CARD_SERIAL_DATA_PATH_SET, 878c2ecf20Sopenharmony_ci CM_REQUEST_CARD_CONTROLLER_VERSION_GET, 888c2ecf20Sopenharmony_ci CM_REQUEST_CARD_GET_STATUS, 898c2ecf20Sopenharmony_ci CM_REQUEST_CARD_GET_MAC_ADDRESS, 908c2ecf20Sopenharmony_ci CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 918c2ecf20Sopenharmony_ci CM_REQUEST_MAX, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* commands for interaction with the flash memory 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * read: response is the contents of the first 60 bytes of flash memory 978c2ecf20Sopenharmony_ci * write: request contains the 60 bytes of data to write to flash memory 988c2ecf20Sopenharmony_ci * response is the contents of the first 60 bytes of flash memory 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * layout: PP PP VV VV MM MM MM MM MM MM ?? ?? SS SS SS SS SS SS SS SS 1018c2ecf20Sopenharmony_ci * SS SS SS SS SS SS SS SS 00 00 00 00 00 00 00 00 00 00 00 00 1028c2ecf20Sopenharmony_ci * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * P: le16 USB Product ID 1058c2ecf20Sopenharmony_ci * V: le16 USB Vendor ID 1068c2ecf20Sopenharmony_ci * M: be48 MAC Address 1078c2ecf20Sopenharmony_ci * S: le16 ASCII Serial Number 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cienum cxacru_cm_flash { 1108c2ecf20Sopenharmony_ci CM_FLASH_READ = 0xa1, 1118c2ecf20Sopenharmony_ci CM_FLASH_WRITE = 0xa2 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* reply codes to the commands above */ 1158c2ecf20Sopenharmony_cienum cxacru_cm_status { 1168c2ecf20Sopenharmony_ci CM_STATUS_UNDEFINED, 1178c2ecf20Sopenharmony_ci CM_STATUS_SUCCESS, 1188c2ecf20Sopenharmony_ci CM_STATUS_ERROR, 1198c2ecf20Sopenharmony_ci CM_STATUS_UNSUPPORTED, 1208c2ecf20Sopenharmony_ci CM_STATUS_UNIMPLEMENTED, 1218c2ecf20Sopenharmony_ci CM_STATUS_PARAMETER_ERROR, 1228c2ecf20Sopenharmony_ci CM_STATUS_DBG_LOOPBACK, 1238c2ecf20Sopenharmony_ci CM_STATUS_MAX, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* indices into CARD_INFO_GET return array */ 1278c2ecf20Sopenharmony_cienum cxacru_info_idx { 1288c2ecf20Sopenharmony_ci CXINF_DOWNSTREAM_RATE, 1298c2ecf20Sopenharmony_ci CXINF_UPSTREAM_RATE, 1308c2ecf20Sopenharmony_ci CXINF_LINK_STATUS, 1318c2ecf20Sopenharmony_ci CXINF_LINE_STATUS, 1328c2ecf20Sopenharmony_ci CXINF_MAC_ADDRESS_HIGH, 1338c2ecf20Sopenharmony_ci CXINF_MAC_ADDRESS_LOW, 1348c2ecf20Sopenharmony_ci CXINF_UPSTREAM_SNR_MARGIN, 1358c2ecf20Sopenharmony_ci CXINF_DOWNSTREAM_SNR_MARGIN, 1368c2ecf20Sopenharmony_ci CXINF_UPSTREAM_ATTENUATION, 1378c2ecf20Sopenharmony_ci CXINF_DOWNSTREAM_ATTENUATION, 1388c2ecf20Sopenharmony_ci CXINF_TRANSMITTER_POWER, 1398c2ecf20Sopenharmony_ci CXINF_UPSTREAM_BITS_PER_FRAME, 1408c2ecf20Sopenharmony_ci CXINF_DOWNSTREAM_BITS_PER_FRAME, 1418c2ecf20Sopenharmony_ci CXINF_STARTUP_ATTEMPTS, 1428c2ecf20Sopenharmony_ci CXINF_UPSTREAM_CRC_ERRORS, 1438c2ecf20Sopenharmony_ci CXINF_DOWNSTREAM_CRC_ERRORS, 1448c2ecf20Sopenharmony_ci CXINF_UPSTREAM_FEC_ERRORS, 1458c2ecf20Sopenharmony_ci CXINF_DOWNSTREAM_FEC_ERRORS, 1468c2ecf20Sopenharmony_ci CXINF_UPSTREAM_HEC_ERRORS, 1478c2ecf20Sopenharmony_ci CXINF_DOWNSTREAM_HEC_ERRORS, 1488c2ecf20Sopenharmony_ci CXINF_LINE_STARTABLE, 1498c2ecf20Sopenharmony_ci CXINF_MODULATION, 1508c2ecf20Sopenharmony_ci CXINF_ADSL_HEADEND, 1518c2ecf20Sopenharmony_ci CXINF_ADSL_HEADEND_ENVIRONMENT, 1528c2ecf20Sopenharmony_ci CXINF_CONTROLLER_VERSION, 1538c2ecf20Sopenharmony_ci /* dunno what the missing two mean */ 1548c2ecf20Sopenharmony_ci CXINF_MAX = 0x1c, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cienum cxacru_poll_state { 1588c2ecf20Sopenharmony_ci CXPOLL_STOPPING, 1598c2ecf20Sopenharmony_ci CXPOLL_STOPPED, 1608c2ecf20Sopenharmony_ci CXPOLL_POLLING, 1618c2ecf20Sopenharmony_ci CXPOLL_SHUTDOWN 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistruct cxacru_modem_type { 1658c2ecf20Sopenharmony_ci u32 pll_f_clk; 1668c2ecf20Sopenharmony_ci u32 pll_b_clk; 1678c2ecf20Sopenharmony_ci int boot_rom_patch; 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistruct cxacru_data { 1718c2ecf20Sopenharmony_ci struct usbatm_data *usbatm; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci const struct cxacru_modem_type *modem_type; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci int line_status; 1768c2ecf20Sopenharmony_ci struct mutex adsl_state_serialize; 1778c2ecf20Sopenharmony_ci int adsl_status; 1788c2ecf20Sopenharmony_ci struct delayed_work poll_work; 1798c2ecf20Sopenharmony_ci u32 card_info[CXINF_MAX]; 1808c2ecf20Sopenharmony_ci struct mutex poll_state_serialize; 1818c2ecf20Sopenharmony_ci enum cxacru_poll_state poll_state; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* contol handles */ 1848c2ecf20Sopenharmony_ci struct mutex cm_serialize; 1858c2ecf20Sopenharmony_ci u8 *rcv_buf; 1868c2ecf20Sopenharmony_ci u8 *snd_buf; 1878c2ecf20Sopenharmony_ci struct urb *rcv_urb; 1888c2ecf20Sopenharmony_ci struct urb *snd_urb; 1898c2ecf20Sopenharmony_ci struct completion rcv_done; 1908c2ecf20Sopenharmony_ci struct completion snd_done; 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, 1948c2ecf20Sopenharmony_ci u8 *wdata, int wsize, u8 *rdata, int rsize); 1958c2ecf20Sopenharmony_cistatic void cxacru_poll_status(struct work_struct *work); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* Card info exported through sysfs */ 1988c2ecf20Sopenharmony_ci#define CXACRU__ATTR_INIT(_name) \ 1998c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(_name) 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define CXACRU_CMD_INIT(_name) \ 2028c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(_name) 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci#define CXACRU_SET_INIT(_name) \ 2058c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(_name) 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#define CXACRU_ATTR_INIT(_value, _type, _name) \ 2088c2ecf20Sopenharmony_cistatic ssize_t _name##_show(struct device *dev, \ 2098c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) \ 2108c2ecf20Sopenharmony_ci{ \ 2118c2ecf20Sopenharmony_ci struct cxacru_data *instance = to_usbatm_driver_data(\ 2128c2ecf20Sopenharmony_ci to_usb_interface(dev)); \ 2138c2ecf20Sopenharmony_ci\ 2148c2ecf20Sopenharmony_ci if (instance == NULL) \ 2158c2ecf20Sopenharmony_ci return -ENODEV; \ 2168c2ecf20Sopenharmony_ci\ 2178c2ecf20Sopenharmony_ci return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \ 2188c2ecf20Sopenharmony_ci} \ 2198c2ecf20Sopenharmony_ciCXACRU__ATTR_INIT(_name) 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name) 2228c2ecf20Sopenharmony_ci#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) 2238c2ecf20Sopenharmony_ci#define CXACRU_SET_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) 2248c2ecf20Sopenharmony_ci#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name) 2278c2ecf20Sopenharmony_ci#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) 2288c2ecf20Sopenharmony_ci#define CXACRU_SET_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) 2298c2ecf20Sopenharmony_ci#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", value); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci if (likely(value >= 0)) { 2448c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%u.%02u\n", 2458c2ecf20Sopenharmony_ci value / 100, value % 100); 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci value = -value; 2488c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "-%u.%02u\n", 2498c2ecf20Sopenharmony_ci value / 100, value % 100); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci static char *str[] = { "no", "yes" }; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (unlikely(value >= ARRAY_SIZE(str))) 2588c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 2598c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", str[value]); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci static char *str[] = { NULL, "not connected", "connected", "lost" }; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL)) 2678c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 2688c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", str[value]); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci static char *str[] = { "down", "attempting to activate", 2748c2ecf20Sopenharmony_ci "training", "channel analysis", "exchange", "up", 2758c2ecf20Sopenharmony_ci "waiting", "initialising" 2768c2ecf20Sopenharmony_ci }; 2778c2ecf20Sopenharmony_ci if (unlikely(value >= ARRAY_SIZE(str))) 2788c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 2798c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", str[value]); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci static char *str[] = { 2858c2ecf20Sopenharmony_ci "", 2868c2ecf20Sopenharmony_ci "ANSI T1.413", 2878c2ecf20Sopenharmony_ci "ITU-T G.992.1 (G.DMT)", 2888c2ecf20Sopenharmony_ci "ITU-T G.992.2 (G.LITE)" 2898c2ecf20Sopenharmony_ci }; 2908c2ecf20Sopenharmony_ci if (unlikely(value >= ARRAY_SIZE(str))) 2918c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 2928c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", str[value]); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* 2968c2ecf20Sopenharmony_ci * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since 2978c2ecf20Sopenharmony_ci * this data is already in atm_dev there's no point. 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * MAC_ADDRESS_HIGH = 0x????5544 3008c2ecf20Sopenharmony_ci * MAC_ADDRESS_LOW = 0x33221100 3018c2ecf20Sopenharmony_ci * Where 00-55 are bytes 0-5 of the MAC. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic ssize_t mac_address_show(struct device *dev, 3048c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct cxacru_data *instance = to_usbatm_driver_data( 3078c2ecf20Sopenharmony_ci to_usb_interface(dev)); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (instance == NULL || instance->usbatm->atm_dev == NULL) 3108c2ecf20Sopenharmony_ci return -ENODEV; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return sprintf(buf, "%pM\n", instance->usbatm->atm_dev->esi); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic ssize_t adsl_state_show(struct device *dev, 3168c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci static char *str[] = { "running", "stopped" }; 3198c2ecf20Sopenharmony_ci struct cxacru_data *instance = to_usbatm_driver_data( 3208c2ecf20Sopenharmony_ci to_usb_interface(dev)); 3218c2ecf20Sopenharmony_ci u32 value; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (instance == NULL) 3248c2ecf20Sopenharmony_ci return -ENODEV; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci value = instance->card_info[CXINF_LINE_STARTABLE]; 3278c2ecf20Sopenharmony_ci if (unlikely(value >= ARRAY_SIZE(str))) 3288c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 3298c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", str[value]); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic ssize_t adsl_state_store(struct device *dev, 3338c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct cxacru_data *instance = to_usbatm_driver_data( 3368c2ecf20Sopenharmony_ci to_usb_interface(dev)); 3378c2ecf20Sopenharmony_ci int ret; 3388c2ecf20Sopenharmony_ci int poll = -1; 3398c2ecf20Sopenharmony_ci char str_cmd[8]; 3408c2ecf20Sopenharmony_ci int len = strlen(buf); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 3438c2ecf20Sopenharmony_ci return -EACCES; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = sscanf(buf, "%7s", str_cmd); 3468c2ecf20Sopenharmony_ci if (ret != 1) 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci ret = 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (instance == NULL) 3518c2ecf20Sopenharmony_ci return -ENODEV; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&instance->adsl_state_serialize)) 3548c2ecf20Sopenharmony_ci return -ERESTARTSYS; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) { 3578c2ecf20Sopenharmony_ci ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0); 3588c2ecf20Sopenharmony_ci if (ret < 0) { 3598c2ecf20Sopenharmony_ci atm_err(instance->usbatm, "change adsl state:" 3608c2ecf20Sopenharmony_ci " CHIP_ADSL_LINE_STOP returned %d\n", ret); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = -EIO; 3638c2ecf20Sopenharmony_ci } else { 3648c2ecf20Sopenharmony_ci ret = len; 3658c2ecf20Sopenharmony_ci poll = CXPOLL_STOPPED; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Line status is only updated every second 3708c2ecf20Sopenharmony_ci * and the device appears to only react to 3718c2ecf20Sopenharmony_ci * START/STOP every second too. Wait 1.5s to 3728c2ecf20Sopenharmony_ci * be sure that restart will have an effect. */ 3738c2ecf20Sopenharmony_ci if (!strcmp(str_cmd, "restart")) 3748c2ecf20Sopenharmony_ci msleep(1500); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) { 3778c2ecf20Sopenharmony_ci ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); 3788c2ecf20Sopenharmony_ci if (ret < 0) { 3798c2ecf20Sopenharmony_ci atm_err(instance->usbatm, "change adsl state:" 3808c2ecf20Sopenharmony_ci " CHIP_ADSL_LINE_START returned %d\n", ret); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret = -EIO; 3838c2ecf20Sopenharmony_ci } else { 3848c2ecf20Sopenharmony_ci ret = len; 3858c2ecf20Sopenharmony_ci poll = CXPOLL_POLLING; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (!strcmp(str_cmd, "poll")) { 3908c2ecf20Sopenharmony_ci ret = len; 3918c2ecf20Sopenharmony_ci poll = CXPOLL_POLLING; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (ret == 0) { 3958c2ecf20Sopenharmony_ci ret = -EINVAL; 3968c2ecf20Sopenharmony_ci poll = -1; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (poll == CXPOLL_POLLING) { 4008c2ecf20Sopenharmony_ci mutex_lock(&instance->poll_state_serialize); 4018c2ecf20Sopenharmony_ci switch (instance->poll_state) { 4028c2ecf20Sopenharmony_ci case CXPOLL_STOPPED: 4038c2ecf20Sopenharmony_ci /* start polling */ 4048c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_POLLING; 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci case CXPOLL_STOPPING: 4088c2ecf20Sopenharmony_ci /* abort stop request */ 4098c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_POLLING; 4108c2ecf20Sopenharmony_ci fallthrough; 4118c2ecf20Sopenharmony_ci case CXPOLL_POLLING: 4128c2ecf20Sopenharmony_ci case CXPOLL_SHUTDOWN: 4138c2ecf20Sopenharmony_ci /* don't start polling */ 4148c2ecf20Sopenharmony_ci poll = -1; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci mutex_unlock(&instance->poll_state_serialize); 4178c2ecf20Sopenharmony_ci } else if (poll == CXPOLL_STOPPED) { 4188c2ecf20Sopenharmony_ci mutex_lock(&instance->poll_state_serialize); 4198c2ecf20Sopenharmony_ci /* request stop */ 4208c2ecf20Sopenharmony_ci if (instance->poll_state == CXPOLL_POLLING) 4218c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_STOPPING; 4228c2ecf20Sopenharmony_ci mutex_unlock(&instance->poll_state_serialize); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci mutex_unlock(&instance->adsl_state_serialize); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (poll == CXPOLL_POLLING) 4288c2ecf20Sopenharmony_ci cxacru_poll_status(&instance->poll_work.work); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* CM_REQUEST_CARD_DATA_GET times out, so no show attribute */ 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic ssize_t adsl_config_store(struct device *dev, 4368c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct cxacru_data *instance = to_usbatm_driver_data( 4398c2ecf20Sopenharmony_ci to_usb_interface(dev)); 4408c2ecf20Sopenharmony_ci int len = strlen(buf); 4418c2ecf20Sopenharmony_ci int ret, pos, num; 4428c2ecf20Sopenharmony_ci __le32 data[CMD_PACKET_SIZE / 4]; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 4458c2ecf20Sopenharmony_ci return -EACCES; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (instance == NULL) 4488c2ecf20Sopenharmony_ci return -ENODEV; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci pos = 0; 4518c2ecf20Sopenharmony_ci num = 0; 4528c2ecf20Sopenharmony_ci while (pos < len) { 4538c2ecf20Sopenharmony_ci int tmp; 4548c2ecf20Sopenharmony_ci u32 index; 4558c2ecf20Sopenharmony_ci u32 value; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci ret = sscanf(buf + pos, "%x=%x%n", &index, &value, &tmp); 4588c2ecf20Sopenharmony_ci if (ret < 2) 4598c2ecf20Sopenharmony_ci return -EINVAL; 4608c2ecf20Sopenharmony_ci if (index > 0x7f) 4618c2ecf20Sopenharmony_ci return -EINVAL; 4628c2ecf20Sopenharmony_ci if (tmp < 0 || tmp > len - pos) 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci pos += tmp; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* skip trailing newline */ 4678c2ecf20Sopenharmony_ci if (buf[pos] == '\n' && pos == len-1) 4688c2ecf20Sopenharmony_ci pos++; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci data[num * 2 + 1] = cpu_to_le32(index); 4718c2ecf20Sopenharmony_ci data[num * 2 + 2] = cpu_to_le32(value); 4728c2ecf20Sopenharmony_ci num++; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* send config values when data buffer is full 4758c2ecf20Sopenharmony_ci * or no more data 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci if (pos >= len || num >= CMD_MAX_CONFIG) { 4788c2ecf20Sopenharmony_ci char log[CMD_MAX_CONFIG * 12 + 1]; /* %02x=%08x */ 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci data[0] = cpu_to_le32(num); 4818c2ecf20Sopenharmony_ci ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET, 4828c2ecf20Sopenharmony_ci (u8 *) data, 4 + num * 8, NULL, 0); 4838c2ecf20Sopenharmony_ci if (ret < 0) { 4848c2ecf20Sopenharmony_ci atm_err(instance->usbatm, 4858c2ecf20Sopenharmony_ci "set card data returned %d\n", ret); 4868c2ecf20Sopenharmony_ci return -EIO; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci for (tmp = 0; tmp < num; tmp++) 4908c2ecf20Sopenharmony_ci snprintf(log + tmp*12, 13, " %02x=%08x", 4918c2ecf20Sopenharmony_ci le32_to_cpu(data[tmp * 2 + 1]), 4928c2ecf20Sopenharmony_ci le32_to_cpu(data[tmp * 2 + 2])); 4938c2ecf20Sopenharmony_ci atm_info(instance->usbatm, "config%s\n", log); 4948c2ecf20Sopenharmony_ci num = 0; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return len; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/* 5028c2ecf20Sopenharmony_ci * All device attributes are included in CXACRU_ALL_FILES 5038c2ecf20Sopenharmony_ci * so that the same list can be used multiple times: 5048c2ecf20Sopenharmony_ci * INIT (define the device attributes) 5058c2ecf20Sopenharmony_ci * CREATE (create all the device files) 5068c2ecf20Sopenharmony_ci * REMOVE (remove all the device files) 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * With the last two being defined as needed in the functions 5098c2ecf20Sopenharmony_ci * they are used in before calling CXACRU_ALL_FILES() 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci#define CXACRU_ALL_FILES(_action) \ 5128c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE, u32, downstream_rate); \ 5138c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE, u32, upstream_rate); \ 5148c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_LINK_STATUS, LINK, link_status); \ 5158c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_LINE_STATUS, LINE, line_status); \ 5168c2ecf20Sopenharmony_ciCXACRU__ATTR_##_action( mac_address); \ 5178c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN, dB, upstream_snr_margin); \ 5188c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN, dB, downstream_snr_margin); \ 5198c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION, dB, upstream_attenuation); \ 5208c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION, dB, downstream_attenuation); \ 5218c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER, s8, transmitter_power); \ 5228c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME, u32, upstream_bits_per_frame); \ 5238c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32, downstream_bits_per_frame); \ 5248c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS, u32, startup_attempts); \ 5258c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS, u32, upstream_crc_errors); \ 5268c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS, u32, downstream_crc_errors); \ 5278c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS, u32, upstream_fec_errors); \ 5288c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS, u32, downstream_fec_errors); \ 5298c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS, u32, upstream_hec_errors); \ 5308c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS, u32, downstream_hec_errors); \ 5318c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \ 5328c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \ 5338c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \ 5348c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \ 5358c2ecf20Sopenharmony_ciCXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \ 5368c2ecf20Sopenharmony_ciCXACRU_CMD_##_action( adsl_state); \ 5378c2ecf20Sopenharmony_ciCXACRU_SET_##_action( adsl_config); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ciCXACRU_ALL_FILES(INIT); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic struct attribute *cxacru_attrs[] = { 5428c2ecf20Sopenharmony_ci &dev_attr_adsl_config.attr, 5438c2ecf20Sopenharmony_ci &dev_attr_adsl_state.attr, 5448c2ecf20Sopenharmony_ci &dev_attr_adsl_controller_version.attr, 5458c2ecf20Sopenharmony_ci &dev_attr_adsl_headend_environment.attr, 5468c2ecf20Sopenharmony_ci &dev_attr_adsl_headend.attr, 5478c2ecf20Sopenharmony_ci &dev_attr_modulation.attr, 5488c2ecf20Sopenharmony_ci &dev_attr_line_startable.attr, 5498c2ecf20Sopenharmony_ci &dev_attr_downstream_hec_errors.attr, 5508c2ecf20Sopenharmony_ci &dev_attr_upstream_hec_errors.attr, 5518c2ecf20Sopenharmony_ci &dev_attr_downstream_fec_errors.attr, 5528c2ecf20Sopenharmony_ci &dev_attr_upstream_fec_errors.attr, 5538c2ecf20Sopenharmony_ci &dev_attr_downstream_crc_errors.attr, 5548c2ecf20Sopenharmony_ci &dev_attr_upstream_crc_errors.attr, 5558c2ecf20Sopenharmony_ci &dev_attr_startup_attempts.attr, 5568c2ecf20Sopenharmony_ci &dev_attr_downstream_bits_per_frame.attr, 5578c2ecf20Sopenharmony_ci &dev_attr_upstream_bits_per_frame.attr, 5588c2ecf20Sopenharmony_ci &dev_attr_transmitter_power.attr, 5598c2ecf20Sopenharmony_ci &dev_attr_downstream_attenuation.attr, 5608c2ecf20Sopenharmony_ci &dev_attr_upstream_attenuation.attr, 5618c2ecf20Sopenharmony_ci &dev_attr_downstream_snr_margin.attr, 5628c2ecf20Sopenharmony_ci &dev_attr_upstream_snr_margin.attr, 5638c2ecf20Sopenharmony_ci &dev_attr_mac_address.attr, 5648c2ecf20Sopenharmony_ci &dev_attr_line_status.attr, 5658c2ecf20Sopenharmony_ci &dev_attr_link_status.attr, 5668c2ecf20Sopenharmony_ci &dev_attr_upstream_rate.attr, 5678c2ecf20Sopenharmony_ci &dev_attr_downstream_rate.attr, 5688c2ecf20Sopenharmony_ci NULL, 5698c2ecf20Sopenharmony_ci}; 5708c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(cxacru); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/* the following three functions are stolen from drivers/usb/core/message.c */ 5738c2ecf20Sopenharmony_cistatic void cxacru_blocking_completion(struct urb *urb) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci complete(urb->context); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistruct cxacru_timer { 5798c2ecf20Sopenharmony_ci struct timer_list timer; 5808c2ecf20Sopenharmony_ci struct urb *urb; 5818c2ecf20Sopenharmony_ci}; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic void cxacru_timeout_kill(struct timer_list *t) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct cxacru_timer *timer = from_timer(timer, t, timer); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci usb_unlink_urb(timer->urb); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int cxacru_start_wait_urb(struct urb *urb, struct completion *done, 5918c2ecf20Sopenharmony_ci int *actual_length) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct cxacru_timer timer = { 5948c2ecf20Sopenharmony_ci .urb = urb, 5958c2ecf20Sopenharmony_ci }; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci timer_setup_on_stack(&timer.timer, cxacru_timeout_kill, 0); 5988c2ecf20Sopenharmony_ci mod_timer(&timer.timer, jiffies + msecs_to_jiffies(CMD_TIMEOUT)); 5998c2ecf20Sopenharmony_ci wait_for_completion(done); 6008c2ecf20Sopenharmony_ci del_timer_sync(&timer.timer); 6018c2ecf20Sopenharmony_ci destroy_timer_on_stack(&timer.timer); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (actual_length) 6048c2ecf20Sopenharmony_ci *actual_length = urb->actual_length; 6058c2ecf20Sopenharmony_ci return urb->status; /* must read status after completion */ 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, 6098c2ecf20Sopenharmony_ci u8 *wdata, int wsize, u8 *rdata, int rsize) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci int ret, actlen; 6128c2ecf20Sopenharmony_ci int offb, offd; 6138c2ecf20Sopenharmony_ci const int stride = CMD_PACKET_SIZE - 4; 6148c2ecf20Sopenharmony_ci u8 *wbuf = instance->snd_buf; 6158c2ecf20Sopenharmony_ci u8 *rbuf = instance->rcv_buf; 6168c2ecf20Sopenharmony_ci int wbuflen = ((wsize - 1) / stride + 1) * CMD_PACKET_SIZE; 6178c2ecf20Sopenharmony_ci int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) { 6208c2ecf20Sopenharmony_ci if (printk_ratelimit()) 6218c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "requested transfer size too large (%d, %d)\n", 6228c2ecf20Sopenharmony_ci wbuflen, rbuflen); 6238c2ecf20Sopenharmony_ci ret = -ENOMEM; 6248c2ecf20Sopenharmony_ci goto err; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci mutex_lock(&instance->cm_serialize); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* submit reading urb before the writing one */ 6308c2ecf20Sopenharmony_ci init_completion(&instance->rcv_done); 6318c2ecf20Sopenharmony_ci ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL); 6328c2ecf20Sopenharmony_ci if (ret < 0) { 6338c2ecf20Sopenharmony_ci if (printk_ratelimit()) 6348c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "submit of read urb for cm %#x failed (%d)\n", 6358c2ecf20Sopenharmony_ci cm, ret); 6368c2ecf20Sopenharmony_ci goto fail; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci memset(wbuf, 0, wbuflen); 6408c2ecf20Sopenharmony_ci /* handle wsize == 0 */ 6418c2ecf20Sopenharmony_ci wbuf[0] = cm; 6428c2ecf20Sopenharmony_ci for (offb = offd = 0; offd < wsize; offd += stride, offb += CMD_PACKET_SIZE) { 6438c2ecf20Sopenharmony_ci wbuf[offb] = cm; 6448c2ecf20Sopenharmony_ci memcpy(wbuf + offb + 4, wdata + offd, min_t(int, stride, wsize - offd)); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci instance->snd_urb->transfer_buffer_length = wbuflen; 6488c2ecf20Sopenharmony_ci init_completion(&instance->snd_done); 6498c2ecf20Sopenharmony_ci ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL); 6508c2ecf20Sopenharmony_ci if (ret < 0) { 6518c2ecf20Sopenharmony_ci if (printk_ratelimit()) 6528c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "submit of write urb for cm %#x failed (%d)\n", 6538c2ecf20Sopenharmony_ci cm, ret); 6548c2ecf20Sopenharmony_ci goto fail; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL); 6588c2ecf20Sopenharmony_ci if (ret < 0) { 6598c2ecf20Sopenharmony_ci if (printk_ratelimit()) 6608c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "send of cm %#x failed (%d)\n", cm, ret); 6618c2ecf20Sopenharmony_ci goto fail; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen); 6658c2ecf20Sopenharmony_ci if (ret < 0) { 6668c2ecf20Sopenharmony_ci if (printk_ratelimit()) 6678c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "receive of cm %#x failed (%d)\n", cm, ret); 6688c2ecf20Sopenharmony_ci goto fail; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci if (actlen % CMD_PACKET_SIZE || !actlen) { 6718c2ecf20Sopenharmony_ci if (printk_ratelimit()) 6728c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "invalid response length to cm %#x: %d\n", 6738c2ecf20Sopenharmony_ci cm, actlen); 6748c2ecf20Sopenharmony_ci ret = -EIO; 6758c2ecf20Sopenharmony_ci goto fail; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* check the return status and copy the data to the output buffer, if needed */ 6798c2ecf20Sopenharmony_ci for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) { 6808c2ecf20Sopenharmony_ci if (rbuf[offb] != cm) { 6818c2ecf20Sopenharmony_ci if (printk_ratelimit()) 6828c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "wrong cm %#x in response to cm %#x\n", 6838c2ecf20Sopenharmony_ci rbuf[offb], cm); 6848c2ecf20Sopenharmony_ci ret = -EIO; 6858c2ecf20Sopenharmony_ci goto fail; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci if (rbuf[offb + 1] != CM_STATUS_SUCCESS) { 6888c2ecf20Sopenharmony_ci if (printk_ratelimit()) 6898c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "response to cm %#x failed: %#x\n", 6908c2ecf20Sopenharmony_ci cm, rbuf[offb + 1]); 6918c2ecf20Sopenharmony_ci ret = -EIO; 6928c2ecf20Sopenharmony_ci goto fail; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci if (offd >= rsize) 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci memcpy(rdata + offd, rbuf + offb + 4, min_t(int, stride, rsize - offd)); 6978c2ecf20Sopenharmony_ci offd += stride; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci ret = offd; 7018c2ecf20Sopenharmony_ci usb_dbg(instance->usbatm, "cm %#x\n", cm); 7028c2ecf20Sopenharmony_cifail: 7038c2ecf20Sopenharmony_ci mutex_unlock(&instance->cm_serialize); 7048c2ecf20Sopenharmony_cierr: 7058c2ecf20Sopenharmony_ci return ret; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_request cm, 7098c2ecf20Sopenharmony_ci u32 *data, int size) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci int ret, len; 7128c2ecf20Sopenharmony_ci __le32 *buf; 7138c2ecf20Sopenharmony_ci int offb; 7148c2ecf20Sopenharmony_ci unsigned int offd; 7158c2ecf20Sopenharmony_ci const int stride = CMD_PACKET_SIZE / (4 * 2) - 1; 7168c2ecf20Sopenharmony_ci int buflen = ((size - 1) / stride + 1 + size * 2) * 4; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci buf = kmalloc(buflen, GFP_KERNEL); 7198c2ecf20Sopenharmony_ci if (!buf) 7208c2ecf20Sopenharmony_ci return -ENOMEM; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci ret = cxacru_cm(instance, cm, NULL, 0, (u8 *) buf, buflen); 7238c2ecf20Sopenharmony_ci if (ret < 0) 7248c2ecf20Sopenharmony_ci goto cleanup; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* len > 0 && len % 4 == 0 guaranteed by cxacru_cm() */ 7278c2ecf20Sopenharmony_ci len = ret / 4; 7288c2ecf20Sopenharmony_ci for (offb = 0; offb < len; ) { 7298c2ecf20Sopenharmony_ci int l = le32_to_cpu(buf[offb++]); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (l < 0 || l > stride || l > (len - offb) / 2) { 7328c2ecf20Sopenharmony_ci if (printk_ratelimit()) 7338c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "invalid data length from cm %#x: %d\n", 7348c2ecf20Sopenharmony_ci cm, l); 7358c2ecf20Sopenharmony_ci ret = -EIO; 7368c2ecf20Sopenharmony_ci goto cleanup; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci while (l--) { 7398c2ecf20Sopenharmony_ci offd = le32_to_cpu(buf[offb++]); 7408c2ecf20Sopenharmony_ci if (offd >= size) { 7418c2ecf20Sopenharmony_ci if (printk_ratelimit()) 7428c2ecf20Sopenharmony_ci usb_err(instance->usbatm, "wrong index %#x in response to cm %#x\n", 7438c2ecf20Sopenharmony_ci offd, cm); 7448c2ecf20Sopenharmony_ci ret = -EIO; 7458c2ecf20Sopenharmony_ci goto cleanup; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci data[offd] = le32_to_cpu(buf[offb++]); 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ret = 0; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cicleanup: 7548c2ecf20Sopenharmony_ci kfree(buf); 7558c2ecf20Sopenharmony_ci return ret; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic int cxacru_card_status(struct cxacru_data *instance) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci int ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (ret < 0) { /* firmware not loaded */ 7638c2ecf20Sopenharmony_ci usb_dbg(instance->usbatm, "cxacru_adsl_start: CARD_GET_STATUS returned %d\n", ret); 7648c2ecf20Sopenharmony_ci return ret; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci return 0; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int cxacru_atm_start(struct usbatm_data *usbatm_instance, 7708c2ecf20Sopenharmony_ci struct atm_dev *atm_dev) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct cxacru_data *instance = usbatm_instance->driver_data; 7738c2ecf20Sopenharmony_ci struct usb_interface *intf = usbatm_instance->usb_intf; 7748c2ecf20Sopenharmony_ci int ret; 7758c2ecf20Sopenharmony_ci int start_polling = 1; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "%s\n", __func__); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* Read MAC address */ 7808c2ecf20Sopenharmony_ci ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0, 7818c2ecf20Sopenharmony_ci atm_dev->esi, sizeof(atm_dev->esi)); 7828c2ecf20Sopenharmony_ci if (ret < 0) { 7838c2ecf20Sopenharmony_ci atm_err(usbatm_instance, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret); 7848c2ecf20Sopenharmony_ci return ret; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* start ADSL */ 7888c2ecf20Sopenharmony_ci mutex_lock(&instance->adsl_state_serialize); 7898c2ecf20Sopenharmony_ci ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); 7908c2ecf20Sopenharmony_ci if (ret < 0) 7918c2ecf20Sopenharmony_ci atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* Start status polling */ 7948c2ecf20Sopenharmony_ci mutex_lock(&instance->poll_state_serialize); 7958c2ecf20Sopenharmony_ci switch (instance->poll_state) { 7968c2ecf20Sopenharmony_ci case CXPOLL_STOPPED: 7978c2ecf20Sopenharmony_ci /* start polling */ 7988c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_POLLING; 7998c2ecf20Sopenharmony_ci break; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci case CXPOLL_STOPPING: 8028c2ecf20Sopenharmony_ci /* abort stop request */ 8038c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_POLLING; 8048c2ecf20Sopenharmony_ci fallthrough; 8058c2ecf20Sopenharmony_ci case CXPOLL_POLLING: 8068c2ecf20Sopenharmony_ci case CXPOLL_SHUTDOWN: 8078c2ecf20Sopenharmony_ci /* don't start polling */ 8088c2ecf20Sopenharmony_ci start_polling = 0; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci mutex_unlock(&instance->poll_state_serialize); 8118c2ecf20Sopenharmony_ci mutex_unlock(&instance->adsl_state_serialize); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci printk(KERN_INFO "%s%d: %s %pM\n", atm_dev->type, atm_dev->number, 8148c2ecf20Sopenharmony_ci usbatm_instance->description, atm_dev->esi); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (start_polling) 8178c2ecf20Sopenharmony_ci cxacru_poll_status(&instance->poll_work.work); 8188c2ecf20Sopenharmony_ci return 0; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic void cxacru_poll_status(struct work_struct *work) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct cxacru_data *instance = 8248c2ecf20Sopenharmony_ci container_of(work, struct cxacru_data, poll_work.work); 8258c2ecf20Sopenharmony_ci u32 buf[CXINF_MAX] = {}; 8268c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 8278c2ecf20Sopenharmony_ci struct atm_dev *atm_dev = usbatm->atm_dev; 8288c2ecf20Sopenharmony_ci int keep_polling = 1; 8298c2ecf20Sopenharmony_ci int ret; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX); 8328c2ecf20Sopenharmony_ci if (ret < 0) { 8338c2ecf20Sopenharmony_ci if (ret != -ESHUTDOWN) 8348c2ecf20Sopenharmony_ci atm_warn(usbatm, "poll status: error %d\n", ret); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci mutex_lock(&instance->poll_state_serialize); 8378c2ecf20Sopenharmony_ci if (instance->poll_state != CXPOLL_SHUTDOWN) { 8388c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_STOPPED; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci if (ret != -ESHUTDOWN) 8418c2ecf20Sopenharmony_ci atm_warn(usbatm, "polling disabled, set adsl_state" 8428c2ecf20Sopenharmony_ci " to 'start' or 'poll' to resume\n"); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci mutex_unlock(&instance->poll_state_serialize); 8458c2ecf20Sopenharmony_ci goto reschedule; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci memcpy(instance->card_info, buf, sizeof(instance->card_info)); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) { 8518c2ecf20Sopenharmony_ci instance->adsl_status = buf[CXINF_LINE_STARTABLE]; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci switch (instance->adsl_status) { 8548c2ecf20Sopenharmony_ci case 0: 8558c2ecf20Sopenharmony_ci atm_printk(KERN_INFO, usbatm, "ADSL state: running\n"); 8568c2ecf20Sopenharmony_ci break; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci case 1: 8598c2ecf20Sopenharmony_ci atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n"); 8608c2ecf20Sopenharmony_ci break; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci default: 8638c2ecf20Sopenharmony_ci atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status); 8648c2ecf20Sopenharmony_ci break; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (instance->line_status == buf[CXINF_LINE_STATUS]) 8698c2ecf20Sopenharmony_ci goto reschedule; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci instance->line_status = buf[CXINF_LINE_STATUS]; 8728c2ecf20Sopenharmony_ci switch (instance->line_status) { 8738c2ecf20Sopenharmony_ci case 0: 8748c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 8758c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line: down\n"); 8768c2ecf20Sopenharmony_ci break; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci case 1: 8798c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 8808c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line: attempting to activate\n"); 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci case 2: 8848c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 8858c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line: training\n"); 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci case 3: 8898c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 8908c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line: channel analysis\n"); 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci case 4: 8948c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 8958c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line: exchange\n"); 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci case 5: 8998c2ecf20Sopenharmony_ci atm_dev->link_rate = buf[CXINF_DOWNSTREAM_RATE] * 1000 / 424; 9008c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_FOUND); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line: up (%d kb/s down | %d kb/s up)\n", 9038c2ecf20Sopenharmony_ci buf[CXINF_DOWNSTREAM_RATE], buf[CXINF_UPSTREAM_RATE]); 9048c2ecf20Sopenharmony_ci break; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci case 6: 9078c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 9088c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line: waiting\n"); 9098c2ecf20Sopenharmony_ci break; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci case 7: 9128c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); 9138c2ecf20Sopenharmony_ci atm_info(usbatm, "ADSL line: initializing\n"); 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci default: 9178c2ecf20Sopenharmony_ci atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN); 9188c2ecf20Sopenharmony_ci atm_info(usbatm, "Unknown line state %02x\n", instance->line_status); 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_cireschedule: 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci mutex_lock(&instance->poll_state_serialize); 9248c2ecf20Sopenharmony_ci if (instance->poll_state == CXPOLL_STOPPING && 9258c2ecf20Sopenharmony_ci instance->adsl_status == 1 && /* stopped */ 9268c2ecf20Sopenharmony_ci instance->line_status == 0) /* down */ 9278c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_STOPPED; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (instance->poll_state == CXPOLL_STOPPED) 9308c2ecf20Sopenharmony_ci keep_polling = 0; 9318c2ecf20Sopenharmony_ci mutex_unlock(&instance->poll_state_serialize); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (keep_polling) 9348c2ecf20Sopenharmony_ci schedule_delayed_work(&instance->poll_work, 9358c2ecf20Sopenharmony_ci round_jiffies_relative(POLL_INTERVAL*HZ)); 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, 9398c2ecf20Sopenharmony_ci u8 code1, u8 code2, u32 addr, const u8 *data, int size) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci int ret; 9428c2ecf20Sopenharmony_ci u8 *buf; 9438c2ecf20Sopenharmony_ci int offd, offb; 9448c2ecf20Sopenharmony_ci const int stride = CMD_PACKET_SIZE - 8; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci buf = (u8 *) __get_free_page(GFP_KERNEL); 9478c2ecf20Sopenharmony_ci if (!buf) 9488c2ecf20Sopenharmony_ci return -ENOMEM; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci offb = offd = 0; 9518c2ecf20Sopenharmony_ci do { 9528c2ecf20Sopenharmony_ci int l = min_t(int, stride, size - offd); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci buf[offb++] = fw; 9558c2ecf20Sopenharmony_ci buf[offb++] = l; 9568c2ecf20Sopenharmony_ci buf[offb++] = code1; 9578c2ecf20Sopenharmony_ci buf[offb++] = code2; 9588c2ecf20Sopenharmony_ci put_unaligned(cpu_to_le32(addr), (__le32 *)(buf + offb)); 9598c2ecf20Sopenharmony_ci offb += 4; 9608c2ecf20Sopenharmony_ci addr += l; 9618c2ecf20Sopenharmony_ci if (l) 9628c2ecf20Sopenharmony_ci memcpy(buf + offb, data + offd, l); 9638c2ecf20Sopenharmony_ci if (l < stride) 9648c2ecf20Sopenharmony_ci memset(buf + offb + l, 0, stride - l); 9658c2ecf20Sopenharmony_ci offb += stride; 9668c2ecf20Sopenharmony_ci offd += stride; 9678c2ecf20Sopenharmony_ci if ((offb >= PAGE_SIZE) || (offd >= size)) { 9688c2ecf20Sopenharmony_ci ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD), 9698c2ecf20Sopenharmony_ci buf, offb, NULL, CMD_TIMEOUT); 9708c2ecf20Sopenharmony_ci if (ret < 0) { 9718c2ecf20Sopenharmony_ci dev_dbg(&usb_dev->dev, "sending fw %#x failed\n", fw); 9728c2ecf20Sopenharmony_ci goto cleanup; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci offb = 0; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci } while (offd < size); 9778c2ecf20Sopenharmony_ci dev_dbg(&usb_dev->dev, "sent fw %#x\n", fw); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci ret = 0; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cicleanup: 9828c2ecf20Sopenharmony_ci free_page((unsigned long) buf); 9838c2ecf20Sopenharmony_ci return ret; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic void cxacru_upload_firmware(struct cxacru_data *instance, 9878c2ecf20Sopenharmony_ci const struct firmware *fw, 9888c2ecf20Sopenharmony_ci const struct firmware *bp) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci int ret; 9918c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 9928c2ecf20Sopenharmony_ci struct usb_device *usb_dev = usbatm->usb_dev; 9938c2ecf20Sopenharmony_ci __le16 signature[] = { usb_dev->descriptor.idVendor, 9948c2ecf20Sopenharmony_ci usb_dev->descriptor.idProduct }; 9958c2ecf20Sopenharmony_ci __le32 val; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s\n", __func__); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* FirmwarePllFClkValue */ 10008c2ecf20Sopenharmony_ci val = cpu_to_le32(instance->modem_type->pll_f_clk); 10018c2ecf20Sopenharmony_ci ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLFCLK_ADDR, (u8 *) &val, 4); 10028c2ecf20Sopenharmony_ci if (ret) { 10038c2ecf20Sopenharmony_ci usb_err(usbatm, "FirmwarePllFClkValue failed: %d\n", ret); 10048c2ecf20Sopenharmony_ci return; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* FirmwarePllBClkValue */ 10088c2ecf20Sopenharmony_ci val = cpu_to_le32(instance->modem_type->pll_b_clk); 10098c2ecf20Sopenharmony_ci ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLBCLK_ADDR, (u8 *) &val, 4); 10108c2ecf20Sopenharmony_ci if (ret) { 10118c2ecf20Sopenharmony_ci usb_err(usbatm, "FirmwarePllBClkValue failed: %d\n", ret); 10128c2ecf20Sopenharmony_ci return; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* Enable SDRAM */ 10168c2ecf20Sopenharmony_ci val = cpu_to_le32(SDRAM_ENA); 10178c2ecf20Sopenharmony_ci ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SDRAMEN_ADDR, (u8 *) &val, 4); 10188c2ecf20Sopenharmony_ci if (ret) { 10198c2ecf20Sopenharmony_ci usb_err(usbatm, "Enable SDRAM failed: %d\n", ret); 10208c2ecf20Sopenharmony_ci return; 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci /* Firmware */ 10248c2ecf20Sopenharmony_ci usb_info(usbatm, "loading firmware\n"); 10258c2ecf20Sopenharmony_ci ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size); 10268c2ecf20Sopenharmony_ci if (ret) { 10278c2ecf20Sopenharmony_ci usb_err(usbatm, "Firmware upload failed: %d\n", ret); 10288c2ecf20Sopenharmony_ci return; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* Boot ROM patch */ 10328c2ecf20Sopenharmony_ci if (instance->modem_type->boot_rom_patch) { 10338c2ecf20Sopenharmony_ci usb_info(usbatm, "loading boot ROM patch\n"); 10348c2ecf20Sopenharmony_ci ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size); 10358c2ecf20Sopenharmony_ci if (ret) { 10368c2ecf20Sopenharmony_ci usb_err(usbatm, "Boot ROM patching failed: %d\n", ret); 10378c2ecf20Sopenharmony_ci return; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci /* Signature */ 10428c2ecf20Sopenharmony_ci ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SIG_ADDR, (u8 *) signature, 4); 10438c2ecf20Sopenharmony_ci if (ret) { 10448c2ecf20Sopenharmony_ci usb_err(usbatm, "Signature storing failed: %d\n", ret); 10458c2ecf20Sopenharmony_ci return; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci usb_info(usbatm, "starting device\n"); 10498c2ecf20Sopenharmony_ci if (instance->modem_type->boot_rom_patch) { 10508c2ecf20Sopenharmony_ci val = cpu_to_le32(BR_ADDR); 10518c2ecf20Sopenharmony_ci ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4); 10528c2ecf20Sopenharmony_ci } else { 10538c2ecf20Sopenharmony_ci ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci if (ret) { 10568c2ecf20Sopenharmony_ci usb_err(usbatm, "Passing control to firmware failed: %d\n", ret); 10578c2ecf20Sopenharmony_ci return; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Delay to allow firmware to start up. */ 10618c2ecf20Sopenharmony_ci msleep_interruptible(1000); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD)); 10648c2ecf20Sopenharmony_ci usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_CMD)); 10658c2ecf20Sopenharmony_ci usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_DATA)); 10668c2ecf20Sopenharmony_ci usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_DATA)); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0); 10698c2ecf20Sopenharmony_ci if (ret < 0) { 10708c2ecf20Sopenharmony_ci usb_err(usbatm, "modem failed to initialize: %d\n", ret); 10718c2ecf20Sopenharmony_ci return; 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic int cxacru_find_firmware(struct cxacru_data *instance, 10768c2ecf20Sopenharmony_ci char *phase, const struct firmware **fw_p) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct usbatm_data *usbatm = instance->usbatm; 10798c2ecf20Sopenharmony_ci struct device *dev = &usbatm->usb_intf->dev; 10808c2ecf20Sopenharmony_ci char buf[16]; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci sprintf(buf, "cxacru-%s.bin", phase); 10838c2ecf20Sopenharmony_ci usb_dbg(usbatm, "cxacru_find_firmware: looking for %s\n", buf); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (request_firmware(fw_p, buf, dev)) { 10868c2ecf20Sopenharmony_ci usb_dbg(usbatm, "no stage %s firmware found\n", phase); 10878c2ecf20Sopenharmony_ci return -ENOENT; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci usb_info(usbatm, "found firmware %s\n", buf); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return 0; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int cxacru_heavy_init(struct usbatm_data *usbatm_instance, 10968c2ecf20Sopenharmony_ci struct usb_interface *usb_intf) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci const struct firmware *fw, *bp; 10998c2ecf20Sopenharmony_ci struct cxacru_data *instance = usbatm_instance->driver_data; 11008c2ecf20Sopenharmony_ci int ret = cxacru_find_firmware(instance, "fw", &fw); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (ret) { 11038c2ecf20Sopenharmony_ci usb_warn(usbatm_instance, "firmware (cxacru-fw.bin) unavailable (system misconfigured?)\n"); 11048c2ecf20Sopenharmony_ci return ret; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (instance->modem_type->boot_rom_patch) { 11088c2ecf20Sopenharmony_ci ret = cxacru_find_firmware(instance, "bp", &bp); 11098c2ecf20Sopenharmony_ci if (ret) { 11108c2ecf20Sopenharmony_ci usb_warn(usbatm_instance, "boot ROM patch (cxacru-bp.bin) unavailable (system misconfigured?)\n"); 11118c2ecf20Sopenharmony_ci release_firmware(fw); 11128c2ecf20Sopenharmony_ci return ret; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci cxacru_upload_firmware(instance, fw, bp); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (instance->modem_type->boot_rom_patch) 11198c2ecf20Sopenharmony_ci release_firmware(bp); 11208c2ecf20Sopenharmony_ci release_firmware(fw); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci ret = cxacru_card_status(instance); 11238c2ecf20Sopenharmony_ci if (ret) 11248c2ecf20Sopenharmony_ci usb_dbg(usbatm_instance, "modem initialisation failed\n"); 11258c2ecf20Sopenharmony_ci else 11268c2ecf20Sopenharmony_ci usb_dbg(usbatm_instance, "done setting up the modem\n"); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci return ret; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic int cxacru_bind(struct usbatm_data *usbatm_instance, 11328c2ecf20Sopenharmony_ci struct usb_interface *intf, const struct usb_device_id *id) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct cxacru_data *instance; 11358c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 11368c2ecf20Sopenharmony_ci struct usb_host_endpoint *cmd_ep = usb_dev->ep_in[CXACRU_EP_CMD]; 11378c2ecf20Sopenharmony_ci int ret; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* instance init */ 11408c2ecf20Sopenharmony_ci instance = kzalloc(sizeof(*instance), GFP_KERNEL); 11418c2ecf20Sopenharmony_ci if (!instance) 11428c2ecf20Sopenharmony_ci return -ENOMEM; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci instance->usbatm = usbatm_instance; 11458c2ecf20Sopenharmony_ci instance->modem_type = (struct cxacru_modem_type *) id->driver_info; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci mutex_init(&instance->poll_state_serialize); 11488c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_STOPPED; 11498c2ecf20Sopenharmony_ci instance->line_status = -1; 11508c2ecf20Sopenharmony_ci instance->adsl_status = -1; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci mutex_init(&instance->adsl_state_serialize); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); 11558c2ecf20Sopenharmony_ci if (!instance->rcv_buf) { 11568c2ecf20Sopenharmony_ci usb_dbg(usbatm_instance, "cxacru_bind: no memory for rcv_buf\n"); 11578c2ecf20Sopenharmony_ci ret = -ENOMEM; 11588c2ecf20Sopenharmony_ci goto fail; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci instance->snd_buf = (u8 *) __get_free_page(GFP_KERNEL); 11618c2ecf20Sopenharmony_ci if (!instance->snd_buf) { 11628c2ecf20Sopenharmony_ci usb_dbg(usbatm_instance, "cxacru_bind: no memory for snd_buf\n"); 11638c2ecf20Sopenharmony_ci ret = -ENOMEM; 11648c2ecf20Sopenharmony_ci goto fail; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci instance->rcv_urb = usb_alloc_urb(0, GFP_KERNEL); 11678c2ecf20Sopenharmony_ci if (!instance->rcv_urb) { 11688c2ecf20Sopenharmony_ci ret = -ENOMEM; 11698c2ecf20Sopenharmony_ci goto fail; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci instance->snd_urb = usb_alloc_urb(0, GFP_KERNEL); 11728c2ecf20Sopenharmony_ci if (!instance->snd_urb) { 11738c2ecf20Sopenharmony_ci ret = -ENOMEM; 11748c2ecf20Sopenharmony_ci goto fail; 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci if (!cmd_ep) { 11788c2ecf20Sopenharmony_ci usb_dbg(usbatm_instance, "cxacru_bind: no command endpoint\n"); 11798c2ecf20Sopenharmony_ci ret = -ENODEV; 11808c2ecf20Sopenharmony_ci goto fail; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if ((cmd_ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) 11848c2ecf20Sopenharmony_ci == USB_ENDPOINT_XFER_INT) { 11858c2ecf20Sopenharmony_ci usb_fill_int_urb(instance->rcv_urb, 11868c2ecf20Sopenharmony_ci usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD), 11878c2ecf20Sopenharmony_ci instance->rcv_buf, PAGE_SIZE, 11888c2ecf20Sopenharmony_ci cxacru_blocking_completion, &instance->rcv_done, 1); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci usb_fill_int_urb(instance->snd_urb, 11918c2ecf20Sopenharmony_ci usb_dev, usb_sndintpipe(usb_dev, CXACRU_EP_CMD), 11928c2ecf20Sopenharmony_ci instance->snd_buf, PAGE_SIZE, 11938c2ecf20Sopenharmony_ci cxacru_blocking_completion, &instance->snd_done, 4); 11948c2ecf20Sopenharmony_ci } else { 11958c2ecf20Sopenharmony_ci usb_fill_bulk_urb(instance->rcv_urb, 11968c2ecf20Sopenharmony_ci usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_CMD), 11978c2ecf20Sopenharmony_ci instance->rcv_buf, PAGE_SIZE, 11988c2ecf20Sopenharmony_ci cxacru_blocking_completion, &instance->rcv_done); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci usb_fill_bulk_urb(instance->snd_urb, 12018c2ecf20Sopenharmony_ci usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD), 12028c2ecf20Sopenharmony_ci instance->snd_buf, PAGE_SIZE, 12038c2ecf20Sopenharmony_ci cxacru_blocking_completion, &instance->snd_done); 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci mutex_init(&instance->cm_serialize); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&instance->poll_work, cxacru_poll_status); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci usbatm_instance->driver_data = instance; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci usbatm_instance->flags = (cxacru_card_status(instance) ? 0 : UDSL_SKIP_HEAVY_INIT); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci return 0; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci fail: 12178c2ecf20Sopenharmony_ci free_page((unsigned long) instance->snd_buf); 12188c2ecf20Sopenharmony_ci free_page((unsigned long) instance->rcv_buf); 12198c2ecf20Sopenharmony_ci usb_free_urb(instance->snd_urb); 12208c2ecf20Sopenharmony_ci usb_free_urb(instance->rcv_urb); 12218c2ecf20Sopenharmony_ci kfree(instance); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci return ret; 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic void cxacru_unbind(struct usbatm_data *usbatm_instance, 12278c2ecf20Sopenharmony_ci struct usb_interface *intf) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci struct cxacru_data *instance = usbatm_instance->driver_data; 12308c2ecf20Sopenharmony_ci int is_polling = 1; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci usb_dbg(usbatm_instance, "cxacru_unbind entered\n"); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (!instance) { 12358c2ecf20Sopenharmony_ci usb_dbg(usbatm_instance, "cxacru_unbind: NULL instance!\n"); 12368c2ecf20Sopenharmony_ci return; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci mutex_lock(&instance->poll_state_serialize); 12408c2ecf20Sopenharmony_ci BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci /* ensure that status polling continues unless 12438c2ecf20Sopenharmony_ci * it has already stopped */ 12448c2ecf20Sopenharmony_ci if (instance->poll_state == CXPOLL_STOPPED) 12458c2ecf20Sopenharmony_ci is_polling = 0; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci /* stop polling from being stopped or started */ 12488c2ecf20Sopenharmony_ci instance->poll_state = CXPOLL_SHUTDOWN; 12498c2ecf20Sopenharmony_ci mutex_unlock(&instance->poll_state_serialize); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci if (is_polling) 12528c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&instance->poll_work); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci usb_kill_urb(instance->snd_urb); 12558c2ecf20Sopenharmony_ci usb_kill_urb(instance->rcv_urb); 12568c2ecf20Sopenharmony_ci usb_free_urb(instance->snd_urb); 12578c2ecf20Sopenharmony_ci usb_free_urb(instance->rcv_urb); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci free_page((unsigned long) instance->snd_buf); 12608c2ecf20Sopenharmony_ci free_page((unsigned long) instance->rcv_buf); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci kfree(instance); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci usbatm_instance->driver_data = NULL; 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic const struct cxacru_modem_type cxacru_cafe = { 12688c2ecf20Sopenharmony_ci .pll_f_clk = 0x02d874df, 12698c2ecf20Sopenharmony_ci .pll_b_clk = 0x0196a51a, 12708c2ecf20Sopenharmony_ci .boot_rom_patch = 1, 12718c2ecf20Sopenharmony_ci}; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic const struct cxacru_modem_type cxacru_cb00 = { 12748c2ecf20Sopenharmony_ci .pll_f_clk = 0x5, 12758c2ecf20Sopenharmony_ci .pll_b_clk = 0x3, 12768c2ecf20Sopenharmony_ci .boot_rom_patch = 0, 12778c2ecf20Sopenharmony_ci}; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cistatic const struct usb_device_id cxacru_usb_ids[] = { 12808c2ecf20Sopenharmony_ci { /* V = Conexant P = ADSL modem (Euphrates project) */ 12818c2ecf20Sopenharmony_ci USB_DEVICE(0x0572, 0xcafe), .driver_info = (unsigned long) &cxacru_cafe 12828c2ecf20Sopenharmony_ci }, 12838c2ecf20Sopenharmony_ci { /* V = Conexant P = ADSL modem (Hasbani project) */ 12848c2ecf20Sopenharmony_ci USB_DEVICE(0x0572, 0xcb00), .driver_info = (unsigned long) &cxacru_cb00 12858c2ecf20Sopenharmony_ci }, 12868c2ecf20Sopenharmony_ci { /* V = Conexant P = ADSL modem */ 12878c2ecf20Sopenharmony_ci USB_DEVICE(0x0572, 0xcb01), .driver_info = (unsigned long) &cxacru_cb00 12888c2ecf20Sopenharmony_ci }, 12898c2ecf20Sopenharmony_ci { /* V = Conexant P = ADSL modem (Well PTI-800) */ 12908c2ecf20Sopenharmony_ci USB_DEVICE(0x0572, 0xcb02), .driver_info = (unsigned long) &cxacru_cb00 12918c2ecf20Sopenharmony_ci }, 12928c2ecf20Sopenharmony_ci { /* V = Conexant P = ADSL modem */ 12938c2ecf20Sopenharmony_ci USB_DEVICE(0x0572, 0xcb06), .driver_info = (unsigned long) &cxacru_cb00 12948c2ecf20Sopenharmony_ci }, 12958c2ecf20Sopenharmony_ci { /* V = Conexant P = ADSL modem (ZTE ZXDSL 852) */ 12968c2ecf20Sopenharmony_ci USB_DEVICE(0x0572, 0xcb07), .driver_info = (unsigned long) &cxacru_cb00 12978c2ecf20Sopenharmony_ci }, 12988c2ecf20Sopenharmony_ci { /* V = Olitec P = ADSL modem version 2 */ 12998c2ecf20Sopenharmony_ci USB_DEVICE(0x08e3, 0x0100), .driver_info = (unsigned long) &cxacru_cafe 13008c2ecf20Sopenharmony_ci }, 13018c2ecf20Sopenharmony_ci { /* V = Olitec P = ADSL modem version 3 */ 13028c2ecf20Sopenharmony_ci USB_DEVICE(0x08e3, 0x0102), .driver_info = (unsigned long) &cxacru_cb00 13038c2ecf20Sopenharmony_ci }, 13048c2ecf20Sopenharmony_ci { /* V = Trust/Amigo Technology Co. P = AMX-CA86U */ 13058c2ecf20Sopenharmony_ci USB_DEVICE(0x0eb0, 0x3457), .driver_info = (unsigned long) &cxacru_cafe 13068c2ecf20Sopenharmony_ci }, 13078c2ecf20Sopenharmony_ci { /* V = Zoom P = 5510 */ 13088c2ecf20Sopenharmony_ci USB_DEVICE(0x1803, 0x5510), .driver_info = (unsigned long) &cxacru_cb00 13098c2ecf20Sopenharmony_ci }, 13108c2ecf20Sopenharmony_ci { /* V = Draytek P = Vigor 318 */ 13118c2ecf20Sopenharmony_ci USB_DEVICE(0x0675, 0x0200), .driver_info = (unsigned long) &cxacru_cb00 13128c2ecf20Sopenharmony_ci }, 13138c2ecf20Sopenharmony_ci { /* V = Zyxel P = 630-C1 aka OMNI ADSL USB (Annex A) */ 13148c2ecf20Sopenharmony_ci USB_DEVICE(0x0586, 0x330a), .driver_info = (unsigned long) &cxacru_cb00 13158c2ecf20Sopenharmony_ci }, 13168c2ecf20Sopenharmony_ci { /* V = Zyxel P = 630-C3 aka OMNI ADSL USB (Annex B) */ 13178c2ecf20Sopenharmony_ci USB_DEVICE(0x0586, 0x330b), .driver_info = (unsigned long) &cxacru_cb00 13188c2ecf20Sopenharmony_ci }, 13198c2ecf20Sopenharmony_ci { /* V = Aethra P = Starmodem UM1020 */ 13208c2ecf20Sopenharmony_ci USB_DEVICE(0x0659, 0x0020), .driver_info = (unsigned long) &cxacru_cb00 13218c2ecf20Sopenharmony_ci }, 13228c2ecf20Sopenharmony_ci { /* V = Aztech Systems P = ? AKA Pirelli AUA-010 */ 13238c2ecf20Sopenharmony_ci USB_DEVICE(0x0509, 0x0812), .driver_info = (unsigned long) &cxacru_cb00 13248c2ecf20Sopenharmony_ci }, 13258c2ecf20Sopenharmony_ci { /* V = Netopia P = Cayman 3341(Annex A)/3351(Annex B) */ 13268c2ecf20Sopenharmony_ci USB_DEVICE(0x100d, 0xcb01), .driver_info = (unsigned long) &cxacru_cb00 13278c2ecf20Sopenharmony_ci }, 13288c2ecf20Sopenharmony_ci { /* V = Netopia P = Cayman 3342(Annex A)/3352(Annex B) */ 13298c2ecf20Sopenharmony_ci USB_DEVICE(0x100d, 0x3342), .driver_info = (unsigned long) &cxacru_cb00 13308c2ecf20Sopenharmony_ci }, 13318c2ecf20Sopenharmony_ci {} 13328c2ecf20Sopenharmony_ci}; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, cxacru_usb_ids); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic struct usbatm_driver cxacru_driver = { 13378c2ecf20Sopenharmony_ci .driver_name = cxacru_driver_name, 13388c2ecf20Sopenharmony_ci .bind = cxacru_bind, 13398c2ecf20Sopenharmony_ci .heavy_init = cxacru_heavy_init, 13408c2ecf20Sopenharmony_ci .unbind = cxacru_unbind, 13418c2ecf20Sopenharmony_ci .atm_start = cxacru_atm_start, 13428c2ecf20Sopenharmony_ci .bulk_in = CXACRU_EP_DATA, 13438c2ecf20Sopenharmony_ci .bulk_out = CXACRU_EP_DATA, 13448c2ecf20Sopenharmony_ci .rx_padding = 3, 13458c2ecf20Sopenharmony_ci .tx_padding = 11, 13468c2ecf20Sopenharmony_ci}; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic int cxacru_usb_probe(struct usb_interface *intf, 13498c2ecf20Sopenharmony_ci const struct usb_device_id *id) 13508c2ecf20Sopenharmony_ci{ 13518c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 13528c2ecf20Sopenharmony_ci char buf[15]; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* Avoid ADSL routers (cx82310_eth). 13558c2ecf20Sopenharmony_ci * Abort if bDeviceClass is 0xff and iProduct is "USB NET CARD". 13568c2ecf20Sopenharmony_ci */ 13578c2ecf20Sopenharmony_ci if (usb_dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC 13588c2ecf20Sopenharmony_ci && usb_string(usb_dev, usb_dev->descriptor.iProduct, 13598c2ecf20Sopenharmony_ci buf, sizeof(buf)) > 0) { 13608c2ecf20Sopenharmony_ci if (!strcmp(buf, "USB NET CARD")) { 13618c2ecf20Sopenharmony_ci dev_info(&intf->dev, "ignoring cx82310_eth device\n"); 13628c2ecf20Sopenharmony_ci return -ENODEV; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci return usbatm_usb_probe(intf, id, &cxacru_driver); 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic struct usb_driver cxacru_usb_driver = { 13708c2ecf20Sopenharmony_ci .name = cxacru_driver_name, 13718c2ecf20Sopenharmony_ci .probe = cxacru_usb_probe, 13728c2ecf20Sopenharmony_ci .disconnect = usbatm_usb_disconnect, 13738c2ecf20Sopenharmony_ci .id_table = cxacru_usb_ids, 13748c2ecf20Sopenharmony_ci .dev_groups = cxacru_groups, 13758c2ecf20Sopenharmony_ci}; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_cimodule_usb_driver(cxacru_usb_driver); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 13808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 13818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1382