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