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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)283 _info(struct modinfo *modinfop)
284 {
285 return (mod_info(&modlinkage, modinfop));
286 }
287
288
289 /*ARGSUSED*/
290 int
usbser_keyspan_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
usbser_keyspan_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
usbser_keyspan_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
usbser_keyspan_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)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
keyspan_pre_attach(dev_info_t * dip,ddi_attach_cmd_t cmd,void * statep)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
keyspan_pre_detach(dev_info_t * dip,ddi_detach_cmd_t cmd,void * statep)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
keyspan_set_cfg(dev_info_t * dip,uint8_t cfg_num)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
keyspan_need_fw(usb_client_dev_data_t * dev_data)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
keyspan_set_reg(keyspan_pipe_t * pipe,uchar_t bit)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
keyspan_write_memory(keyspan_pipe_t * pipe,uint16_t addr,uchar_t * buf,uint16_t len,uint8_t bRequest)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
keyspan_download_firmware(keyspan_pre_state_t * kbp)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(4D)"
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 }
636