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