1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * This driver includes code for Keyspan USA49WG/USA49WLC/USA19HS adapters. It 30 * is a device-specific driver (DSD) working with USB generic serial driver 31 * (GSD). It implements the USB-to-serial device-specific driver interface 32 * (DSDI) which is offered by GSD. The interface is defined by ds_ops_t 33 * structure. 34 * 35 * For USA49WLC, it's necessary to download firmware every time the device is 36 * plugged. Before the firmware is downloaded, we say that the device is in 37 * "firmware mode", and the attach routin is keyspan_pre_attach(). After 38 * downloading, the device's product id will change to 0x12a. Then the device 39 * will be enumerated again and another attach for the new product id will 40 * begin. No firmware is included in the driver. The functions of USA49WLC is 41 * disabled. 42 * 43 * For USA49WG and USA19HS, no need to download firmware since it can be kept 44 * in the device's memory. 45 * 46 * For USA49WLC and USA19HS, it's necessary to check and switch their 47 * configrations at the beginning of attach, since each of them has two 48 * configrations. This driver uses the one whose endpoints are all bulk. 49 * 50 * For USA49WG, this driver uses the third configuration which has 6 endpoints, 51 * 3 bulk out eps, 1 bulk in ep, 1 intr in ep, 1 intr out ep. Bulk in ep is 52 * shared by 4 ports for receiving data. 53 * 54 * Some of Keyspan adapters have only one port, some have two or four ports. 55 * This driver supports up to four ports. Each port has its own states (traced 56 * by keyspan_port structure) and can be operated independently. 57 * 58 * port_state: 59 * 60 * KEYSPAN_PORT_NOT_INIT 61 * | 62 * | 63 * attach_ports 64 * | 65 * | 66 * | 67 * v 68 * KEYSPAN_PORT_CLOSED <-----close-------<---- + 69 * | | 70 * | | 71 * | | 72 * open_port | 73 * | | 74 * | | 75 * v | 76 * KEYSPAN_PORT_OPENING ---open_hw_port---> USBSER_PORT_OPEN 77 * 78 * Each port has its own data in/out pipes and each pipe also has its own states 79 * (traced by keyspan_pipe structure). The pipe states is as following: 80 * 81 * pipe_state: 82 * 83 * KEYSPAN_PIPE_NOT_INIT 84 * | ^ 85 * | | 86 * keyspan_init_pipes keyspan_fini_pipes 87 * | | 88 * v | 89 * KEYSPAN_PIPE_CLOSED ------------->-----------+ 90 * ^ | 91 * | reconnect/resume/open_port 92 * | | 93 * disconnect/suspend/close_port | 94 * | v 95 * +---------<------------------ KEYSPAN_PIPE_OPEN 96 * 97 * To control the device and get its status in a timely way, this driver makes 98 * use of two global bulk endpoints for cmd and status on the device. The pipes 99 * for cmd/status will be opened during attach. For multi-port devices, one of 100 * the cmd/status message fields will designate which port this message is for. 101 * 102 * This driver can be easily extended to support more Keyspan adapter models. 103 * You need the following steps to reach the aim: 104 * 1. Add the model specific data structures, like cmd/status message structure. 105 * 2. If the device need firmware downloaded, add the firmware code as a header 106 * file, and add code to keyspan_pre_attach() as what were done for USA49WLC. 107 * 3. Add several model specific functions, like keyspan_build_cmd_msg_*, 108 * keyspan_default_port_params_*, keyspan_save_port_params_*, etc. The functions 109 * for USA19HS and USA49WLC can be taken as examples. 110 * 4. Add model specific code to the "switch (id_product) {...}" sentences. 111 */ 112 113 /* 114 * 115 * keyspan driver glue code 116 * 117 */ 118 #include <sys/types.h> 119 #include <sys/param.h> 120 #include <sys/stream.h> 121 #include <sys/conf.h> 122 #include <sys/ddi.h> 123 #include <sys/sunddi.h> 124 125 #define USBDRV_MAJOR_VER 2 126 #define USBDRV_MINOR_VER 0 127 128 #include <sys/usb/usba.h> 129 130 #include <sys/usb/clients/usbser/usbser.h> 131 #include <sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h> 132 133 #include <sys/byteorder.h> 134 #include <sys/strsun.h> 135 136 /* configuration entry points */ 137 static int usbser_keyspan_getinfo(dev_info_t *, ddi_info_cmd_t, void *, 138 void **); 139 static int usbser_keyspan_attach(dev_info_t *, ddi_attach_cmd_t); 140 static int usbser_keyspan_detach(dev_info_t *, ddi_detach_cmd_t); 141 static int usbser_keyspan_open(queue_t *, dev_t *, int, int, cred_t *); 142 143 /* functions related with set config or firmware download */ 144 static int keyspan_pre_attach(dev_info_t *, ddi_attach_cmd_t, void *); 145 static int keyspan_set_cfg(dev_info_t *, uint8_t); 146 static int keyspan_pre_detach(dev_info_t *, ddi_detach_cmd_t, void *); 147 static boolean_t keyspan_need_fw(usb_client_dev_data_t *); 148 static int keyspan_set_reg(keyspan_pipe_t *, uchar_t); 149 static int keyspan_write_memory(keyspan_pipe_t *, uint16_t, uchar_t *, 150 uint16_t, uint8_t); 151 static int keyspan_download_firmware(keyspan_pre_state_t *); 152 153 static void *usbser_keyspan_statep; /* soft state */ 154 155 extern ds_ops_t keyspan_ds_ops; /* DSD operations */ 156 157 /* 158 * STREAMS structures 159 */ 160 struct module_info usbser_keyspan_modinfo = { 161 0, /* module id */ 162 "usbsksp", /* module name */ 163 USBSER_MIN_PKTSZ, /* min pkt size */ 164 USBSER_MAX_PKTSZ, /* max pkt size */ 165 USBSER_HIWAT, /* hi watermark */ 166 USBSER_LOWAT /* low watermark */ 167 }; 168 169 static struct qinit usbser_keyspan_rinit = { 170 putq, 171 usbser_rsrv, 172 usbser_keyspan_open, 173 usbser_close, 174 NULL, 175 &usbser_keyspan_modinfo, 176 NULL 177 }; 178 179 static struct qinit usbser_keyspan_winit = { 180 usbser_wput, 181 usbser_wsrv, 182 NULL, 183 NULL, 184 NULL, 185 &usbser_keyspan_modinfo, 186 NULL 187 }; 188 189 struct streamtab usbser_keyspan_str_info = { 190 &usbser_keyspan_rinit, &usbser_keyspan_winit, NULL, NULL 191 }; 192 193 static struct cb_ops usbser_keyspan_cb_ops = { 194 nodev, /* cb_open */ 195 nodev, /* cb_close */ 196 nodev, /* cb_strategy */ 197 nodev, /* cb_print */ 198 nodev, /* cb_dump */ 199 nodev, /* cb_read */ 200 nodev, /* cb_write */ 201 nodev, /* cb_ioctl */ 202 nodev, /* cb_devmap */ 203 nodev, /* cb_mmap */ 204 nodev, /* cb_segmap */ 205 nochpoll, /* cb_chpoll */ 206 ddi_prop_op, /* cb_prop_op */ 207 &usbser_keyspan_str_info, /* cb_stream */ 208 (int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG) /* cb_flag */ 209 }; 210 211 /* 212 * auto configuration ops 213 */ 214 struct dev_ops usbser_keyspan_ops = { 215 DEVO_REV, /* devo_rev */ 216 0, /* devo_refcnt */ 217 usbser_keyspan_getinfo, /* devo_getinfo */ 218 nulldev, /* devo_identify */ 219 nulldev, /* devo_probe */ 220 usbser_keyspan_attach, /* devo_attach */ 221 usbser_keyspan_detach, /* devo_detach */ 222 nodev, /* devo_reset */ 223 &usbser_keyspan_cb_ops, /* devo_cb_ops */ 224 (struct bus_ops *)NULL, /* devo_bus_ops */ 225 usbser_power, /* devo_power */ 226 ddi_quiesce_not_needed, /* devo_quiesce */ 227 }; 228 229 extern struct mod_ops mod_driverops; 230 231 static struct modldrv modldrv = { 232 &mod_driverops, /* type of module - driver */ 233 "USB keyspan usb2serial driver", 234 &usbser_keyspan_ops, 235 }; 236 237 static struct modlinkage modlinkage = { 238 MODREV_1, &modldrv, 0 239 }; 240 241 /* debug support */ 242 static uint_t keyspan_pre_errlevel = USB_LOG_L4; 243 static uint_t keyspan_pre_errmask = DPRINT_MASK_ALL; 244 static uint_t keyspan_pre_instance_debug = (uint_t)-1; 245 246 /* firmware support for usa49wlc model */ 247 extern usbser_keyspan_fw_record_t *keyspan_usa49wlc_fw(void); 248 #pragma weak keyspan_usa49wlc_fw 249 250 /* 251 * configuration entry points 252 * -------------------------- 253 */ 254 int 255 _init(void) 256 { 257 int error; 258 259 if ((error = mod_install(&modlinkage)) == 0) { 260 error = ddi_soft_state_init(&usbser_keyspan_statep, 261 max(usbser_soft_state_size(), 262 sizeof (keyspan_pre_state_t)), 1); 263 } 264 265 return (error); 266 } 267 268 269 int 270 _fini(void) 271 { 272 int error; 273 274 if ((error = mod_remove(&modlinkage)) == 0) { 275 ddi_soft_state_fini(&usbser_keyspan_statep); 276 } 277 278 return (error); 279 } 280 281 282 int 283 _info(struct modinfo *modinfop) 284 { 285 return (mod_info(&modlinkage, modinfop)); 286 } 287 288 289 /*ARGSUSED*/ 290 int 291 usbser_keyspan_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 292 void **result) 293 { 294 return (usbser_getinfo(dip, infocmd, arg, result, 295 usbser_keyspan_statep)); 296 } 297 298 299 static int 300 usbser_keyspan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 301 { 302 int rval; 303 304 /* 305 * Once the device is plugged, we need set its cfg. And need download 306 * firmware for some of them. 307 */ 308 rval = keyspan_pre_attach(dip, cmd, usbser_keyspan_statep); 309 310 /* 311 * After the cfg is set, and the firmware is downloaded, 312 * do the real attach. 313 */ 314 if (rval == DDI_ECONTEXT) { 315 316 return (usbser_attach(dip, cmd, usbser_keyspan_statep, 317 &keyspan_ds_ops)); 318 } else { 319 320 return (rval); 321 } 322 } 323 324 325 static int 326 usbser_keyspan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 327 { 328 329 if (ddi_get_driver_private(dip) == NULL) { 330 331 return (keyspan_pre_detach(dip, cmd, usbser_keyspan_statep)); 332 } else { 333 334 335 return (usbser_detach(dip, cmd, usbser_keyspan_statep)); 336 337 338 } 339 340 } 341 342 343 static int 344 usbser_keyspan_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) 345 { 346 return (usbser_open(rq, dev, flag, sflag, cr, usbser_keyspan_statep)); 347 } 348 349 /* 350 * Switch config or download firmware 351 */ 352 /*ARGSUSED*/ 353 static int 354 keyspan_pre_attach(dev_info_t *dip, ddi_attach_cmd_t cmd, void *statep) 355 { 356 357 int instance = ddi_get_instance(dip); 358 keyspan_pre_state_t *kbp = NULL; 359 usb_client_dev_data_t *dev_data = NULL; 360 int rval = DDI_FAILURE; 361 362 switch (cmd) { 363 case DDI_ATTACH: 364 365 break; 366 case DDI_RESUME: 367 368 return (DDI_SUCCESS); 369 default: 370 371 return (DDI_FAILURE); 372 } 373 374 /* attach driver to USBA */ 375 if (usb_client_attach(dip, USBDRV_VERSION, 0) == USB_SUCCESS) { 376 (void) usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0); 377 } 378 if (dev_data == NULL) { 379 380 goto fail; 381 } 382 383 /* 384 * If 19HS or 49WG, needn't download firmware, but need check the 385 * current cfg. 386 * If 49WLC, need check the current cfg before download fw. And after 387 * download, the product id will change to KEYSPAN_USA49WLC_PID. 388 */ 389 if (dev_data->dev_descr->idProduct == KEYSPAN_USA19HS_PID || 390 dev_data->dev_descr->idProduct == KEYSPAN_USA49WLC_PID) { 391 if (keyspan_set_cfg(dip, 1) == USB_SUCCESS) { 392 /* Go to keyspan_attach() by return DDI_ECONTEXT. */ 393 rval = DDI_ECONTEXT; 394 } 395 396 goto fail; 397 } else if (dev_data->dev_descr->idProduct == KEYSPAN_USA49WG_PID) { 398 if (keyspan_set_cfg(dip, 2) == USB_SUCCESS) { 399 /* Go to keyspan_attach() by return DDI_ECONTEXT. */ 400 rval = DDI_ECONTEXT; 401 } 402 403 goto fail; 404 } 405 406 407 /* 408 * By checking KEYSPAN_FW_FLAG, we can check whether the firmware 409 * has been downloaded. 410 * If firmware is already there, then do normal attach. 411 */ 412 if (!keyspan_need_fw(dev_data)) { 413 /* Go to keyspan_attach() by return DDI_ECONTEXT. */ 414 rval = DDI_ECONTEXT; 415 416 goto fail; 417 } 418 419 /* Go on to download firmware. */ 420 421 if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) { 422 kbp = ddi_get_soft_state(statep, instance); 423 } 424 if (kbp) { 425 kbp->kb_dip = dip; 426 kbp->kb_instance = instance; 427 kbp->kb_dev_data = dev_data; 428 kbp->kb_def_pipe.pipe_handle = kbp->kb_dev_data->dev_default_ph; 429 kbp->kb_lh = usb_alloc_log_hdl(kbp->kb_dip, "keyspan[*].", 430 &keyspan_pre_errlevel, &keyspan_pre_errmask, 431 &keyspan_pre_instance_debug, 0); 432 433 kbp->kb_def_pipe.pipe_lh = kbp->kb_lh; 434 435 if (keyspan_download_firmware(kbp) == USB_SUCCESS) { 436 USB_DPRINTF_L4(DPRINT_ATTACH, kbp->kb_lh, 437 "keyspan_pre_attach: completed."); 438 439 /* keyspan download firmware done. */ 440 441 return (DDI_SUCCESS); 442 } 443 } 444 fail: 445 if (kbp) { 446 usb_free_log_hdl(kbp->kb_lh); 447 ddi_soft_state_free(statep, instance); 448 } 449 usb_client_detach(dip, dev_data); 450 451 return (rval); 452 } 453 454 455 static int 456 keyspan_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep) 457 { 458 int instance = ddi_get_instance(dip); 459 keyspan_pre_state_t *kbp; 460 461 kbp = ddi_get_soft_state(statep, instance); 462 463 switch (cmd) { 464 case DDI_DETACH: 465 466 break; 467 case DDI_SUSPEND: 468 469 return (DDI_SUCCESS); 470 default: 471 472 return (DDI_FAILURE); 473 } 474 475 usb_free_log_hdl(kbp->kb_lh); 476 usb_client_detach(dip, kbp->kb_dev_data); 477 ddi_soft_state_free(statep, instance); 478 479 return (DDI_SUCCESS); 480 } 481 482 483 /* Set cfg for the device which has more than one cfg */ 484 static int 485 keyspan_set_cfg(dev_info_t *dip, uint8_t cfg_num) 486 { 487 488 if (usb_set_cfg(dip, cfg_num, USB_FLAGS_SLEEP, 489 NULL, NULL) != USB_SUCCESS) { 490 491 return (USB_FAILURE); 492 } 493 494 return (USB_SUCCESS); 495 } 496 497 498 /* Return TRUE if need download firmware to the device. */ 499 static boolean_t 500 keyspan_need_fw(usb_client_dev_data_t *dev_data) 501 { 502 uint16_t bcd_descr; 503 uint16_t bcd_descr_change; 504 505 /* need to convert to Little-Endian */ 506 bcd_descr = dev_data->dev_descr->bcdDevice; 507 508 /* 509 * According to Keyspan's interface spec, this flag indicates 510 * if need download fw. 511 */ 512 bcd_descr_change = bcd_descr & KEYSPAN_FW_FLAG; 513 514 return (bcd_descr_change == KEYSPAN_FW_FLAG); 515 } 516 517 /* Set the device's register. */ 518 static int 519 keyspan_set_reg(keyspan_pipe_t *pipe, uchar_t bit) 520 { 521 int rval; 522 523 /* 524 * (0x7f92) is the reg addr we want to set. 525 * We set this reg before/after downloading firmware. 526 */ 527 rval = keyspan_write_memory(pipe, 0x7f92, &bit, 1, KEYSPAN_REQ_SET); 528 529 return (rval); 530 } 531 532 /* 533 * Download firmware or set register to the device by default ctrl pipe 534 */ 535 static int 536 keyspan_write_memory(keyspan_pipe_t *pipe, uint16_t addr, uchar_t *buf, 537 uint16_t len, uint8_t bRequest) 538 { 539 mblk_t *data; 540 usb_ctrl_setup_t setup; 541 542 usb_cb_flags_t cb_flags; 543 usb_cr_t cr; 544 uint8_t retry = 0; 545 546 /* reuse previous mblk if possible */ 547 if ((data = allocb(len, BPRI_HI)) == NULL) { 548 549 return (USB_FAILURE); 550 } 551 552 bcopy(buf, data->b_rptr, len); 553 554 setup.bmRequestType = USB_DEV_REQ_TYPE_VENDOR; 555 556 /* This is a req defined by hardware vendor. */ 557 setup.bRequest = bRequest; 558 setup.wValue = addr; 559 setup.wIndex = 0; 560 setup.wLength = len; 561 setup.attrs = 0; 562 563 while (usb_pipe_ctrl_xfer_wait(pipe->pipe_handle, &setup, &data, 564 &cr, &cb_flags, 0) != USB_SUCCESS) { 565 566 /* KEYSPAN_RETRY */ 567 if (++retry > 3) { 568 if (data) { 569 freemsg(data); 570 } 571 572 return (USB_FAILURE); 573 } 574 } 575 576 if (data) { 577 freemsg(data); 578 } 579 580 return (USB_SUCCESS); 581 } 582 583 /* Download firmware into device */ 584 static int 585 keyspan_download_firmware(keyspan_pre_state_t *kbp) 586 { 587 usbser_keyspan_fw_record_t *record = NULL; 588 589 /* If the firmware module exists, then download it to device. */ 590 if (&keyspan_usa49wlc_fw) { 591 592 record = keyspan_usa49wlc_fw(); 593 } 594 595 if (!record) { 596 USB_DPRINTF_L1(DPRINT_ATTACH, kbp->kb_lh, 597 "No firmware available for Keyspan usa49wlc" 598 " usb-to-serial adapter. Refer to usbsksp(7D)" 599 " for details."); 600 601 return (USB_FAILURE); 602 } 603 604 /* Set bit 1 before downloading firmware. */ 605 if (keyspan_set_reg(&kbp->kb_def_pipe, 1) != USB_SUCCESS) { 606 USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh, 607 "keyspan_pre_attach: Set register failed."); 608 609 return (USB_FAILURE); 610 } 611 612 /* Write until the last record of the firmware */ 613 while (record->address != 0xffff) { 614 if (keyspan_write_memory(&kbp->kb_def_pipe, 615 record->address, (uchar_t *)record->data, 616 record->data_len, KEYSPAN_REQ_SET) != USB_SUCCESS) { 617 USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh, 618 "keyspan_pre_attach: download firmware failed."); 619 620 return (USB_FAILURE); 621 } 622 record++; 623 } 624 625 /* 626 * Set bit 0, device will be enumerated again after a while, 627 * and then go to keyspan_attach() 628 */ 629 if (keyspan_set_reg(&kbp->kb_def_pipe, 0) != USB_SUCCESS) { 630 631 return (USB_FAILURE); 632 } 633 634 return (USB_SUCCESS); 635 }