Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] USB ATM: driver for the Conexant AccessRunner chipset cxacru

Driver for modems based on the Conexant AccessRunner chipset.
Original patch by Josep Comas, much reworked by Roman Kagan.

Signed-off-by: Duncan Sands <baldrick@free.fr>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Duncan Sands and committed by
Greg Kroah-Hartman
1b0e6146 48da7267

+878
+878
drivers/usb/atm/cxacru.c
··· 1 + /****************************************************************************** 2 + * cxacru.c - driver for USB ADSL modems based on 3 + * Conexant AccessRunner chipset 4 + * 5 + * Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan 6 + * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) 7 + * 8 + * This program is free software; you can redistribute it and/or modify it 9 + * under the terms of the GNU General Public License as published by the Free 10 + * Software Foundation; either version 2 of the License, or (at your option) 11 + * any later version. 12 + * 13 + * This program is distributed in the hope that it will be useful, but WITHOUT 14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 + * more details. 17 + * 18 + * You should have received a copy of the GNU General Public License along with 19 + * this program; if not, write to the Free Software Foundation, Inc., 59 20 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 + * 22 + ******************************************************************************/ 23 + 24 + /* 25 + * Credit is due for Josep Comas, who created the original patch to speedtch.c 26 + * to support the different padding used by the AccessRunner (now generalized 27 + * into usbatm), and the userspace firmware loading utility. 28 + */ 29 + 30 + #include <linux/module.h> 31 + #include <linux/moduleparam.h> 32 + #include <linux/kernel.h> 33 + #include <linux/timer.h> 34 + #include <linux/errno.h> 35 + #include <linux/slab.h> 36 + #include <linux/init.h> 37 + #include <linux/device.h> /* FIXME: linux/firmware.h should include it itself */ 38 + #include <linux/firmware.h> 39 + 40 + #include "usbatm.h" 41 + 42 + #define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands" 43 + #define DRIVER_VERSION "0.2" 44 + #define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver" 45 + 46 + static const char cxacru_driver_name[] = "cxacru"; 47 + 48 + #define CXACRU_EP_CMD 0x01 /* Bulk/interrupt in/out */ 49 + #define CXACRU_EP_DATA 0x02 /* Bulk in/out */ 50 + 51 + #define CMD_PACKET_SIZE 64 /* Should be maxpacket(ep)? */ 52 + 53 + /* Addresses */ 54 + #define PLLFCLK_ADDR 0x00350068 55 + #define PLLBCLK_ADDR 0x0035006c 56 + #define SDRAMEN_ADDR 0x00350010 57 + #define FW_ADDR 0x00801000 58 + #define BR_ADDR 0x00180600 59 + #define SIG_ADDR 0x00180500 60 + #define BR_STACK_ADDR 0x00187f10 61 + 62 + /* Values */ 63 + #define SDRAM_ENA 0x1 64 + 65 + #define CMD_TIMEOUT 2000 /* msecs */ 66 + #define POLL_INTERVAL 5000 /* msecs */ 67 + 68 + /* commands for interaction with the modem through the control channel before 69 + * firmware is loaded */ 70 + enum cxacru_fw_request { 71 + FW_CMD_ERR, 72 + FW_GET_VER, 73 + FW_READ_MEM, 74 + FW_WRITE_MEM, 75 + FW_RMW_MEM, 76 + FW_CHECKSUM_MEM, 77 + FW_GOTO_MEM, 78 + }; 79 + 80 + /* commands for interaction with the modem through the control channel once 81 + * firmware is loaded */ 82 + enum cxacru_cm_request { 83 + CM_REQUEST_UNDEFINED = 0x80, 84 + CM_REQUEST_TEST, 85 + CM_REQUEST_CHIP_GET_MAC_ADDRESS, 86 + CM_REQUEST_CHIP_GET_DP_VERSIONS, 87 + CM_REQUEST_CHIP_ADSL_LINE_START, 88 + CM_REQUEST_CHIP_ADSL_LINE_STOP, 89 + CM_REQUEST_CHIP_ADSL_LINE_GET_STATUS, 90 + CM_REQUEST_CHIP_ADSL_LINE_GET_SPEED, 91 + CM_REQUEST_CARD_INFO_GET, 92 + CM_REQUEST_CARD_DATA_GET, 93 + CM_REQUEST_CARD_DATA_SET, 94 + CM_REQUEST_COMMAND_HW_IO, 95 + CM_REQUEST_INTERFACE_HW_IO, 96 + CM_REQUEST_CARD_SERIAL_DATA_PATH_GET, 97 + CM_REQUEST_CARD_SERIAL_DATA_PATH_SET, 98 + CM_REQUEST_CARD_CONTROLLER_VERSION_GET, 99 + CM_REQUEST_CARD_GET_STATUS, 100 + CM_REQUEST_CARD_GET_MAC_ADDRESS, 101 + CM_REQUEST_CARD_GET_DATA_LINK_STATUS, 102 + CM_REQUEST_MAX, 103 + }; 104 + 105 + /* reply codes to the commands above */ 106 + enum cxacru_cm_status { 107 + CM_STATUS_UNDEFINED, 108 + CM_STATUS_SUCCESS, 109 + CM_STATUS_ERROR, 110 + CM_STATUS_UNSUPPORTED, 111 + CM_STATUS_UNIMPLEMENTED, 112 + CM_STATUS_PARAMETER_ERROR, 113 + CM_STATUS_DBG_LOOPBACK, 114 + CM_STATUS_MAX, 115 + }; 116 + 117 + /* indices into CARD_INFO_GET return array */ 118 + enum cxacru_info_idx { 119 + CXINF_DOWNSTREAM_RATE, 120 + CXINF_UPSTREAM_RATE, 121 + CXINF_LINK_STATUS, 122 + CXINF_LINE_STATUS, 123 + CXINF_MAC_ADDRESS_HIGH, 124 + CXINF_MAC_ADDRESS_LOW, 125 + CXINF_UPSTREAM_SNR_MARGIN, 126 + CXINF_DOWNSTREAM_SNR_MARGIN, 127 + CXINF_UPSTREAM_ATTENUATION, 128 + CXINF_DOWNSTREAM_ATTENUATION, 129 + CXINF_TRANSMITTER_POWER, 130 + CXINF_UPSTREAM_BITS_PER_FRAME, 131 + CXINF_DOWNSTREAM_BITS_PER_FRAME, 132 + CXINF_STARTUP_ATTEMPTS, 133 + CXINF_UPSTREAM_CRC_ERRORS, 134 + CXINF_DOWNSTREAM_CRC_ERRORS, 135 + CXINF_UPSTREAM_FEC_ERRORS, 136 + CXINF_DOWNSTREAM_FEC_ERRORS, 137 + CXINF_UPSTREAM_HEC_ERRORS, 138 + CXINF_DOWNSTREAM_HEC_ERRORS, 139 + CXINF_LINE_STARTABLE, 140 + CXINF_MODULATION, 141 + CXINF_ADSL_HEADEND, 142 + CXINF_ADSL_HEADEND_ENVIRONMENT, 143 + CXINF_CONTROLLER_VERSION, 144 + /* dunno what the missing two mean */ 145 + CXINF_MAX = 0x1c, 146 + }; 147 + 148 + struct cxacru_modem_type { 149 + u32 pll_f_clk; 150 + u32 pll_b_clk; 151 + int boot_rom_patch; 152 + }; 153 + 154 + struct cxacru_data { 155 + struct usbatm_data *usbatm; 156 + 157 + const struct cxacru_modem_type *modem_type; 158 + 159 + int line_status; 160 + struct work_struct poll_work; 161 + 162 + /* contol handles */ 163 + struct semaphore cm_serialize; 164 + u8 *rcv_buf; 165 + u8 *snd_buf; 166 + struct urb *rcv_urb; 167 + struct urb *snd_urb; 168 + struct completion rcv_done; 169 + struct completion snd_done; 170 + }; 171 + 172 + /* the following three functions are stolen from drivers/usb/core/message.c */ 173 + static void cxacru_blocking_completion(struct urb *urb, struct pt_regs *regs) 174 + { 175 + complete((struct completion *)urb->context); 176 + } 177 + 178 + static void cxacru_timeout_kill(unsigned long data) 179 + { 180 + usb_unlink_urb((struct urb *) data); 181 + } 182 + 183 + static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, 184 + int* actual_length) 185 + { 186 + struct timer_list timer; 187 + int status; 188 + 189 + init_timer(&timer); 190 + timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT); 191 + timer.data = (unsigned long) urb; 192 + timer.function = cxacru_timeout_kill; 193 + add_timer(&timer); 194 + wait_for_completion(done); 195 + status = urb->status; 196 + if (status == -ECONNRESET) 197 + status = -ETIMEDOUT; 198 + del_timer_sync(&timer); 199 + 200 + if (actual_length) 201 + *actual_length = urb->actual_length; 202 + return status; 203 + } 204 + 205 + static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, 206 + u8 *wdata, int wsize, u8 *rdata, int rsize) 207 + { 208 + int ret, actlen; 209 + int offb, offd; 210 + const int stride = CMD_PACKET_SIZE - 4; 211 + u8 *wbuf = instance->snd_buf; 212 + u8 *rbuf = instance->rcv_buf; 213 + int wbuflen = ((wsize - 1) / stride + 1) * CMD_PACKET_SIZE; 214 + int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE; 215 + 216 + if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) { 217 + dbg("too big transfer requested"); 218 + ret = -ENOMEM; 219 + goto fail; 220 + } 221 + 222 + down(&instance->cm_serialize); 223 + 224 + /* submit reading urb before the writing one */ 225 + init_completion(&instance->rcv_done); 226 + ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL); 227 + if (ret < 0) { 228 + dbg("submitting read urb for cm %#x failed", cm); 229 + ret = ret; 230 + goto fail; 231 + } 232 + 233 + memset(wbuf, 0, wbuflen); 234 + /* handle wsize == 0 */ 235 + wbuf[0] = cm; 236 + for (offb = offd = 0; offd < wsize; offd += stride, offb += CMD_PACKET_SIZE) { 237 + wbuf[offb] = cm; 238 + memcpy(wbuf + offb + 4, wdata + offd, min_t(int, stride, wsize - offd)); 239 + } 240 + 241 + instance->snd_urb->transfer_buffer_length = wbuflen; 242 + init_completion(&instance->snd_done); 243 + ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL); 244 + if (ret < 0) { 245 + dbg("submitting write urb for cm %#x failed", cm); 246 + ret = ret; 247 + goto fail; 248 + } 249 + 250 + ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL); 251 + if (ret < 0) { 252 + dbg("sending cm %#x failed", cm); 253 + ret = ret; 254 + goto fail; 255 + } 256 + 257 + ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen); 258 + if (ret < 0) { 259 + dbg("receiving cm %#x failed", cm); 260 + ret = ret; 261 + goto fail; 262 + } 263 + if (actlen % CMD_PACKET_SIZE || !actlen) { 264 + dbg("response is not a positive multiple of %d: %#x", 265 + CMD_PACKET_SIZE, actlen); 266 + ret = -EIO; 267 + goto fail; 268 + } 269 + 270 + /* check the return status and copy the data to the output buffer, if needed */ 271 + for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) { 272 + if (rbuf[offb] != cm) { 273 + dbg("wrong cm %#x in response", rbuf[offb]); 274 + ret = -EIO; 275 + goto fail; 276 + } 277 + if (rbuf[offb + 1] != CM_STATUS_SUCCESS) { 278 + dbg("response failed: %#x", rbuf[offb + 1]); 279 + ret = -EIO; 280 + goto fail; 281 + } 282 + if (offd >= rsize) 283 + break; 284 + memcpy(rdata + offd, rbuf + offb + 4, min_t(int, stride, rsize - offd)); 285 + offd += stride; 286 + } 287 + 288 + ret = offd; 289 + dbg("cm %#x", cm); 290 + fail: 291 + up(&instance->cm_serialize); 292 + return ret; 293 + } 294 + 295 + static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_request cm, 296 + u32 *data, int size) 297 + { 298 + int ret, len; 299 + u32 *buf; 300 + int offb, offd; 301 + const int stride = CMD_PACKET_SIZE / (4 * 2) - 1; 302 + int buflen = ((size - 1) / stride + 1 + size * 2) * 4; 303 + 304 + buf = kmalloc(buflen, GFP_KERNEL); 305 + if (!buf) 306 + return -ENOMEM; 307 + 308 + ret = cxacru_cm(instance, cm, NULL, 0, (u8 *) buf, buflen); 309 + if (ret < 0) 310 + goto cleanup; 311 + 312 + /* len > 0 && len % 4 == 0 guaranteed by cxacru_cm() */ 313 + len = ret / 4; 314 + for (offb = 0; offb < len; ) { 315 + int l = le32_to_cpu(buf[offb++]); 316 + if (l > stride || l > (len - offb) / 2) { 317 + dbg("wrong data length %#x in response", l); 318 + ret = -EIO; 319 + goto cleanup; 320 + } 321 + while (l--) { 322 + offd = le32_to_cpu(buf[offb++]); 323 + if (offd >= size) { 324 + dbg("wrong index %#x in response", offd); 325 + ret = -EIO; 326 + goto cleanup; 327 + } 328 + data[offd] = le32_to_cpu(buf[offb++]); 329 + } 330 + } 331 + 332 + ret = 0; 333 + 334 + cleanup: 335 + kfree(buf); 336 + return ret; 337 + } 338 + 339 + static int cxacru_card_status(struct cxacru_data *instance) 340 + { 341 + int ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0); 342 + if (ret < 0) { /* firmware not loaded */ 343 + dbg("cxacru_adsl_start: CARD_GET_STATUS returned %d", ret); 344 + return ret; 345 + } 346 + return 0; 347 + } 348 + 349 + static void cxacru_poll_status(struct cxacru_data *instance); 350 + 351 + static int cxacru_atm_start(struct usbatm_data *usbatm_instance, 352 + struct atm_dev *atm_dev) 353 + { 354 + struct cxacru_data *instance = usbatm_instance->driver_data; 355 + struct device *dev = &usbatm_instance->usb_intf->dev; 356 + /* 357 + struct atm_dev *atm_dev = usbatm_instance->atm_dev; 358 + */ 359 + int ret; 360 + 361 + dbg("cxacru_atm_start"); 362 + 363 + /* Read MAC address */ 364 + ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0, 365 + atm_dev->esi, sizeof(atm_dev->esi)); 366 + if (ret < 0) { 367 + dev_err(dev, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret); 368 + return ret; 369 + } 370 + 371 + /* start ADSL */ 372 + ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); 373 + if (ret < 0) { 374 + dev_err(dev, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret); 375 + return ret; 376 + } 377 + 378 + /* Start status polling */ 379 + cxacru_poll_status(instance); 380 + return 0; 381 + } 382 + 383 + static void cxacru_poll_status(struct cxacru_data *instance) 384 + { 385 + u32 buf[CXINF_MAX] = {}; 386 + struct device *dev = &instance->usbatm->usb_intf->dev; 387 + struct atm_dev *atm_dev = instance->usbatm->atm_dev; 388 + int ret; 389 + 390 + ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX); 391 + if (ret < 0) { 392 + dev_warn(dev, "poll status: error %d\n", ret); 393 + goto reschedule; 394 + } 395 + 396 + if (instance->line_status == buf[CXINF_LINE_STATUS]) 397 + goto reschedule; 398 + 399 + instance->line_status = buf[CXINF_LINE_STATUS]; 400 + switch (instance->line_status) { 401 + case 0: 402 + atm_dev->signal = ATM_PHY_SIG_LOST; 403 + dev_info(dev, "ADSL line: down\n"); 404 + break; 405 + 406 + case 1: 407 + atm_dev->signal = ATM_PHY_SIG_LOST; 408 + dev_info(dev, "ADSL line: attemtping to activate\n"); 409 + break; 410 + 411 + case 2: 412 + atm_dev->signal = ATM_PHY_SIG_LOST; 413 + dev_info(dev, "ADSL line: training\n"); 414 + break; 415 + 416 + case 3: 417 + atm_dev->signal = ATM_PHY_SIG_LOST; 418 + dev_info(dev, "ADSL line: channel analysis\n"); 419 + break; 420 + 421 + case 4: 422 + atm_dev->signal = ATM_PHY_SIG_LOST; 423 + dev_info(dev, "ADSL line: exchange\n"); 424 + break; 425 + 426 + case 5: 427 + atm_dev->link_rate = buf[CXINF_DOWNSTREAM_RATE] * 1000 / 424; 428 + atm_dev->signal = ATM_PHY_SIG_FOUND; 429 + 430 + dev_info(dev, "ADSL line: up (%d Kib/s down | %d Kib/s up)\n", 431 + buf[CXINF_DOWNSTREAM_RATE], buf[CXINF_UPSTREAM_RATE]); 432 + break; 433 + 434 + case 6: 435 + atm_dev->signal = ATM_PHY_SIG_LOST; 436 + dev_info(dev, "ADSL line: waiting\n"); 437 + break; 438 + 439 + case 7: 440 + atm_dev->signal = ATM_PHY_SIG_LOST; 441 + dev_info(dev, "ADSL line: initializing\n"); 442 + break; 443 + 444 + default: 445 + atm_dev->signal = ATM_PHY_SIG_UNKNOWN; 446 + dev_info(dev, "Unknown line state %02x\n", instance->line_status); 447 + break; 448 + } 449 + reschedule: 450 + schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL)); 451 + } 452 + 453 + static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, 454 + u8 code1, u8 code2, u32 addr, u8 *data, int size) 455 + { 456 + int ret; 457 + u8 *buf; 458 + int offd, offb; 459 + const int stride = CMD_PACKET_SIZE - 8; 460 + 461 + buf = (u8 *) __get_free_page(GFP_KERNEL); 462 + if (!buf) 463 + return -ENOMEM; 464 + 465 + offb = offd = 0; 466 + do { 467 + int l = min_t(int, stride, size - offd); 468 + buf[offb++] = fw; 469 + buf[offb++] = l; 470 + buf[offb++] = code1; 471 + buf[offb++] = code2; 472 + *((u32 *) (buf + offb)) = cpu_to_le32(addr); 473 + offb += 4; 474 + addr += l; 475 + if(l) 476 + memcpy(buf + offb, data + offd, l); 477 + if (l < stride) 478 + memset(buf + offb + l, 0, stride - l); 479 + offb += stride; 480 + offd += stride; 481 + if ((offb >= PAGE_SIZE) || (offd >= size)) { 482 + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD), 483 + buf, offb, NULL, CMD_TIMEOUT); 484 + if (ret < 0) { 485 + dbg("sending fw %#x failed", fw); 486 + goto cleanup; 487 + } 488 + offb = 0; 489 + } 490 + } while(offd < size); 491 + dbg("sent fw %#x", fw); 492 + 493 + ret = 0; 494 + 495 + cleanup: 496 + free_page((unsigned long) buf); 497 + return ret; 498 + } 499 + 500 + static void cxacru_upload_firmware(struct cxacru_data *instance, 501 + const struct firmware *fw, 502 + const struct firmware *bp, 503 + const struct firmware *cf) 504 + { 505 + int ret; 506 + int off; 507 + struct usb_device *usb_dev = instance->usbatm->usb_dev; 508 + struct device *dev = &instance->usbatm->usb_intf->dev; 509 + u16 signature[] = { usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct }; 510 + u32 val; 511 + 512 + dbg("cxacru_upload_firmware"); 513 + 514 + /* FirmwarePllFClkValue */ 515 + val = cpu_to_le32(instance->modem_type->pll_f_clk); 516 + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLFCLK_ADDR, (u8 *) &val, 4); 517 + if (ret) { 518 + dev_err(dev, "FirmwarePllFClkValue failed: %d\n", ret); 519 + return; 520 + } 521 + 522 + /* FirmwarePllBClkValue */ 523 + val = cpu_to_le32(instance->modem_type->pll_b_clk); 524 + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLBCLK_ADDR, (u8 *) &val, 4); 525 + if (ret) { 526 + dev_err(dev, "FirmwarePllBClkValue failed: %d\n", ret); 527 + return; 528 + } 529 + 530 + /* Enable SDRAM */ 531 + val = cpu_to_le32(SDRAM_ENA); 532 + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SDRAMEN_ADDR, (u8 *) &val, 4); 533 + if (ret) { 534 + dev_err(dev, "Enable SDRAM failed: %d\n", ret); 535 + return; 536 + } 537 + 538 + /* Firmware */ 539 + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size); 540 + if (ret) { 541 + dev_err(dev, "Firmware upload failed: %d\n", ret); 542 + return; 543 + } 544 + 545 + /* Boot ROM patch */ 546 + if (instance->modem_type->boot_rom_patch) { 547 + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size); 548 + if (ret) { 549 + dev_err(dev, "Boot ROM patching failed: %d\n", ret); 550 + return; 551 + } 552 + } 553 + 554 + /* Signature */ 555 + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SIG_ADDR, (u8 *) signature, 4); 556 + if (ret) { 557 + dev_err(dev, "Signature storing failed: %d\n", ret); 558 + return; 559 + } 560 + 561 + if (instance->modem_type->boot_rom_patch) { 562 + val = cpu_to_le32(BR_ADDR); 563 + ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4); 564 + } 565 + else { 566 + ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0); 567 + } 568 + if (ret) { 569 + dev_err(dev, "Passing control to firmware failed: %d\n", ret); 570 + return; 571 + } 572 + 573 + /* Delay to allow firmware to start up. */ 574 + msleep_interruptible(1000); 575 + 576 + usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD)); 577 + usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_CMD)); 578 + usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_DATA)); 579 + usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_DATA)); 580 + 581 + ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0); 582 + if (ret < 0) { 583 + dev_err(dev, "modem failed to initialize: %d\n", ret); 584 + return; 585 + } 586 + 587 + /* Load config data (le32), doing one packet at a time */ 588 + if (cf) 589 + for (off = 0; off < cf->size / 4; ) { 590 + u32 buf[CMD_PACKET_SIZE / 4 - 1]; 591 + int i, len = min_t(int, cf->size / 4 - off, CMD_PACKET_SIZE / 4 / 2 - 1); 592 + buf[0] = cpu_to_le32(len); 593 + for (i = 0; i < len; i++, off++) { 594 + buf[i * 2 + 1] = cpu_to_le32(off); 595 + memcpy(buf + i * 2 + 2, cf->data + off * 4, 4); 596 + } 597 + ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET, 598 + (u8 *) buf, len, NULL, 0); 599 + if (ret < 0) { 600 + dev_err(dev, "load config data failed: %d\n", ret); 601 + return; 602 + } 603 + } 604 + 605 + msleep_interruptible(4000); 606 + } 607 + 608 + static int cxacru_find_firmware(struct cxacru_data *instance, 609 + char* phase, const struct firmware **fw_p) 610 + { 611 + struct device *dev = &instance->usbatm->usb_intf->dev; 612 + char buf[16]; 613 + 614 + sprintf(buf, "cxacru-%s.bin", phase); 615 + dbg("cxacru_find_firmware: looking for %s", buf); 616 + 617 + if (request_firmware(fw_p, buf, dev)) { 618 + dev_dbg(dev, "no stage %s firmware found\n", phase); 619 + return -ENOENT; 620 + } 621 + 622 + dev_info(dev, "found firmware %s\n", buf); 623 + 624 + return 0; 625 + } 626 + 627 + static int cxacru_heavy_init(struct usbatm_data *usbatm_instance, 628 + struct usb_interface *usb_intf) 629 + { 630 + struct device *dev = &usbatm_instance->usb_intf->dev; 631 + const struct firmware *fw, *bp, *cf; 632 + struct cxacru_data *instance = usbatm_instance->driver_data; 633 + 634 + int ret = cxacru_find_firmware(instance, "fw", &fw); 635 + if (ret) { 636 + dev_warn(dev, "firmware (cxacru-fw.bin) unavailable (hotplug misconfiguration?)\n"); 637 + return ret; 638 + } 639 + 640 + if (instance->modem_type->boot_rom_patch) { 641 + ret = cxacru_find_firmware(instance, "bp", &bp); 642 + if (ret) { 643 + dev_warn(dev, "boot ROM patch (cxacru-bp.bin) unavailable (hotplug misconfiguration?)\n"); 644 + release_firmware(fw); 645 + return ret; 646 + } 647 + } 648 + 649 + if (cxacru_find_firmware(instance, "cf", &cf)) /* optional */ 650 + cf = NULL; 651 + 652 + cxacru_upload_firmware(instance, fw, bp, cf); 653 + 654 + if (cf) 655 + release_firmware(cf); 656 + if (instance->modem_type->boot_rom_patch) 657 + release_firmware(bp); 658 + release_firmware(fw); 659 + 660 + ret = cxacru_card_status(instance); 661 + if (ret) 662 + dbg("modem initialisation failed"); 663 + else 664 + dbg("done setting up the modem"); 665 + 666 + return ret; 667 + } 668 + 669 + static int cxacru_bind(struct usbatm_data *usbatm_instance, 670 + struct usb_interface *intf, const struct usb_device_id *id, 671 + int *need_heavy_init) 672 + { 673 + struct cxacru_data *instance; 674 + struct usb_device *usb_dev = interface_to_usbdev(intf); 675 + int ret; 676 + 677 + /* instance init */ 678 + instance = kmalloc(sizeof(*instance), GFP_KERNEL); 679 + if (!instance) { 680 + dbg("cxacru_bind: no memory for instance data"); 681 + return -ENOMEM; 682 + } 683 + 684 + memset(instance, 0, sizeof(*instance)); 685 + 686 + instance->usbatm = usbatm_instance; 687 + instance->modem_type = (struct cxacru_modem_type *) id->driver_info; 688 + 689 + instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); 690 + if (!instance->rcv_buf) { 691 + dbg("cxacru_bind: no memory for rcv_buf"); 692 + ret = -ENOMEM; 693 + goto fail; 694 + } 695 + instance->snd_buf = (u8 *) __get_free_page(GFP_KERNEL); 696 + if (!instance->snd_buf) { 697 + dbg("cxacru_bind: no memory for snd_buf"); 698 + ret = -ENOMEM; 699 + goto fail; 700 + } 701 + instance->rcv_urb = usb_alloc_urb(0, GFP_KERNEL); 702 + if (!instance->rcv_urb) { 703 + dbg("cxacru_bind: no memory for rcv_urb"); 704 + ret = -ENOMEM; 705 + goto fail; 706 + } 707 + instance->snd_urb = usb_alloc_urb(0, GFP_KERNEL); 708 + if (!instance->snd_urb) { 709 + dbg("cxacru_bind: no memory for snd_urb"); 710 + ret = -ENOMEM; 711 + goto fail; 712 + } 713 + 714 + usb_fill_int_urb(instance->rcv_urb, 715 + usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD), 716 + instance->rcv_buf, PAGE_SIZE, 717 + cxacru_blocking_completion, &instance->rcv_done, 1); 718 + instance->rcv_urb->transfer_flags |= URB_ASYNC_UNLINK; 719 + 720 + usb_fill_int_urb(instance->snd_urb, 721 + usb_dev, usb_sndintpipe(usb_dev, CXACRU_EP_CMD), 722 + instance->snd_buf, PAGE_SIZE, 723 + cxacru_blocking_completion, &instance->snd_done, 4); 724 + instance->snd_urb->transfer_flags |= URB_ASYNC_UNLINK; 725 + 726 + init_MUTEX(&instance->cm_serialize); 727 + 728 + INIT_WORK(&instance->poll_work, (void *)cxacru_poll_status, instance); 729 + 730 + usbatm_instance->driver_data = instance; 731 + 732 + *need_heavy_init = cxacru_card_status(instance); 733 + 734 + return 0; 735 + 736 + fail: 737 + free_page((unsigned long) instance->snd_buf); 738 + free_page((unsigned long) instance->rcv_buf); 739 + usb_free_urb(instance->snd_urb); 740 + usb_free_urb(instance->rcv_urb); 741 + kfree(instance); 742 + 743 + return ret; 744 + } 745 + 746 + static void cxacru_unbind(struct usbatm_data *usbatm_instance, 747 + struct usb_interface *intf) 748 + { 749 + struct cxacru_data *instance = usbatm_instance->driver_data; 750 + 751 + dbg("cxacru_unbind entered"); 752 + 753 + if (!instance) { 754 + dbg("cxacru_unbind: NULL instance!"); 755 + return; 756 + } 757 + 758 + while (!cancel_delayed_work(&instance->poll_work)) 759 + flush_scheduled_work(); 760 + 761 + usb_kill_urb(instance->snd_urb); 762 + usb_kill_urb(instance->rcv_urb); 763 + usb_free_urb(instance->snd_urb); 764 + usb_free_urb(instance->rcv_urb); 765 + 766 + free_page((unsigned long) instance->snd_buf); 767 + free_page((unsigned long) instance->rcv_buf); 768 + kfree(instance); 769 + 770 + usbatm_instance->driver_data = NULL; 771 + } 772 + 773 + static const struct cxacru_modem_type cxacru_cafe = { 774 + .pll_f_clk = 0x02d874df, 775 + .pll_b_clk = 0x0196a51a, 776 + .boot_rom_patch = 1, 777 + }; 778 + 779 + static const struct cxacru_modem_type cxacru_cb00 = { 780 + .pll_f_clk = 0x5, 781 + .pll_b_clk = 0x3, 782 + .boot_rom_patch = 0, 783 + }; 784 + 785 + static const struct usb_device_id cxacru_usb_ids[] = { 786 + { /* V = Conexant P = ADSL modem (Euphrates project) */ 787 + USB_DEVICE(0x0572, 0xcafe), .driver_info = (unsigned long) &cxacru_cafe 788 + }, 789 + { /* V = Conexant P = ADSL modem (Hasbani project) */ 790 + USB_DEVICE(0x0572, 0xcb00), .driver_info = (unsigned long) &cxacru_cb00 791 + }, 792 + { /* V = Conexant P = ADSL modem */ 793 + USB_DEVICE(0x0572, 0xcb01), .driver_info = (unsigned long) &cxacru_cb00 794 + }, 795 + { /* V = Conexant P = ADSL modem */ 796 + USB_DEVICE(0x0572, 0xcb06), .driver_info = (unsigned long) &cxacru_cb00 797 + }, 798 + { /* V = Olitec P = ADSL modem version 2 */ 799 + USB_DEVICE(0x08e3, 0x0100), .driver_info = (unsigned long) &cxacru_cafe 800 + }, 801 + { /* V = Olitec P = ADSL modem version 3 */ 802 + USB_DEVICE(0x08e3, 0x0102), .driver_info = (unsigned long) &cxacru_cb00 803 + }, 804 + { /* V = Trust/Amigo Technology Co. P = AMX-CA86U */ 805 + USB_DEVICE(0x0eb0, 0x3457), .driver_info = (unsigned long) &cxacru_cafe 806 + }, 807 + { /* V = Zoom P = 5510 */ 808 + USB_DEVICE(0x1803, 0x5510), .driver_info = (unsigned long) &cxacru_cb00 809 + }, 810 + { /* V = Draytek P = Vigor 318 */ 811 + USB_DEVICE(0x0675, 0x0200), .driver_info = (unsigned long) &cxacru_cb00 812 + }, 813 + { /* V = Zyxel P = 630-C1 aka OMNI ADSL USB (Annex A) */ 814 + USB_DEVICE(0x0586, 0x330a), .driver_info = (unsigned long) &cxacru_cb00 815 + }, 816 + { /* V = Zyxel P = 630-C3 aka OMNI ADSL USB (Annex B) */ 817 + USB_DEVICE(0x0586, 0x330b), .driver_info = (unsigned long) &cxacru_cb00 818 + }, 819 + { /* V = Aethra P = Starmodem UM1020 */ 820 + USB_DEVICE(0x0659, 0x0020), .driver_info = (unsigned long) &cxacru_cb00 821 + }, 822 + { /* V = Aztech Systems P = ? AKA Pirelli AUA-010 */ 823 + USB_DEVICE(0x0509, 0x0812), .driver_info = (unsigned long) &cxacru_cb00 824 + }, 825 + { /* V = Netopia P = Cayman 3341(Annex A)/3351(Annex B) */ 826 + USB_DEVICE(0x100d, 0xcb01), .driver_info = (unsigned long) &cxacru_cb00 827 + }, 828 + { /* V = Netopia P = Cayman 3342(Annex A)/3352(Annex B) */ 829 + USB_DEVICE(0x100d, 0x3342), .driver_info = (unsigned long) &cxacru_cb00 830 + }, 831 + {} 832 + }; 833 + 834 + MODULE_DEVICE_TABLE(usb, cxacru_usb_ids); 835 + 836 + static struct usbatm_driver cxacru_driver = { 837 + .owner = THIS_MODULE, 838 + .driver_name = cxacru_driver_name, 839 + .bind = cxacru_bind, 840 + .heavy_init = cxacru_heavy_init, 841 + .unbind = cxacru_unbind, 842 + .atm_start = cxacru_atm_start, 843 + .in = CXACRU_EP_DATA, 844 + .out = CXACRU_EP_DATA, 845 + .rx_padding = 3, 846 + .tx_padding = 11, 847 + }; 848 + 849 + static int cxacru_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) 850 + { 851 + return usbatm_usb_probe(intf, id, &cxacru_driver); 852 + } 853 + 854 + static struct usb_driver cxacru_usb_driver = { 855 + .owner = THIS_MODULE, 856 + .name = cxacru_driver_name, 857 + .probe = cxacru_usb_probe, 858 + .disconnect = usbatm_usb_disconnect, 859 + .id_table = cxacru_usb_ids 860 + }; 861 + 862 + static int __init cxacru_init(void) 863 + { 864 + return usb_register(&cxacru_usb_driver); 865 + } 866 + 867 + static void __exit cxacru_cleanup(void) 868 + { 869 + usb_deregister(&cxacru_usb_driver); 870 + } 871 + 872 + module_init(cxacru_init); 873 + module_exit(cxacru_cleanup); 874 + 875 + MODULE_AUTHOR(DRIVER_AUTHOR); 876 + MODULE_DESCRIPTION(DRIVER_DESC); 877 + MODULE_LICENSE("GPL"); 878 + MODULE_VERSION(DRIVER_VERSION);