xref: /illumos-gate/usr/src/uts/common/io/usb/clients/usbser/usbser_keyspan/usbser_keyspan.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This driver includes code for Keyspan USA49WG/USA49WLC/USA19HS adapters. It
31  * is a device-specific driver (DSD) working with USB generic serial driver
32  * (GSD). It implements the USB-to-serial device-specific driver interface
33  * (DSDI) which is offered by GSD. The interface is defined by ds_ops_t
34  * 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 USA49WG and USA19HS, no need to download firmware since it can be kept
45  * in the device's memory.
46  *
47  * For USA49WLC and USA19HS, it's necessary to check and switch their
48  * configrations at the beginning of attach, since each of them has two
49  * configrations. This driver uses the one whose endpoints are all bulk.
50  *
51  * For USA49WG, this driver uses the third configuration which has 6 endpoints,
52  * 3 bulk out eps, 1 bulk in ep, 1 intr in ep, 1 intr out ep. Bulk in ep is
53  * shared by 4 ports for receiving data.
54  *
55  * Some of Keyspan adapters have only one port, some have two or four ports.
56  * This driver supports up to four ports. Each port has its own states (traced
57  * by keyspan_port structure) and can be operated independently.
58  *
59  * port_state:
60  *
61  *   KEYSPAN_PORT_NOT_INIT
62  *	    |
63  *	    |
64  *     attach_ports
65  *	    |
66  *	    |
67  *	    |
68  *	    v
69  *   KEYSPAN_PORT_CLOSED <-----close-------<---- +
70  *	|					 |
71  *	|					 |
72  *	|					 |
73  *  open_port					 |
74  *	|					 |
75  *	|					 |
76  *	v					 |
77  * KEYSPAN_PORT_OPENING ---open_hw_port---> USBSER_PORT_OPEN
78  *
79  * Each port has its own data in/out pipes and each pipe also has its own states
80  * (traced by keyspan_pipe structure). The pipe states is as following:
81  *
82  * pipe_state:
83  *
84  *	  KEYSPAN_PIPE_NOT_INIT
85  *		|	^
86  *		|	|
87  * keyspan_init_pipes  keyspan_fini_pipes
88  *		|	|
89  *		v	|
90  *	   KEYSPAN_PIPE_CLOSED ------------->-----------+
91  *		  ^					|
92  *		  |			  reconnect/resume/open_port
93  *		  |					|
94  *    disconnect/suspend/close_port			|
95  *		  |					v
96  *		  +---------<------------------ KEYSPAN_PIPE_OPEN
97  *
98  * To control the device and get its status in a timely way, this driver makes
99  * use of two global bulk endpoints for cmd and status on the device. The pipes
100  * for cmd/status will be opened during attach. For multi-port devices, one of
101  * the cmd/status message fields will designate which port this message is for.
102  *
103  * This driver can be easily extended to support more Keyspan adapter models.
104  * You need the following steps to reach the aim:
105  * 1. Add the model specific data structures, like cmd/status message structure.
106  * 2. If the device need firmware downloaded, add the firmware code as a header
107  * file, and add code to keyspan_pre_attach() as what were done for USA49WLC.
108  * 3. Add several model specific functions, like keyspan_build_cmd_msg_*,
109  * keyspan_default_port_params_*, keyspan_save_port_params_*, etc. The functions
110  * for USA19HS and USA49WLC can be taken as examples.
111  * 4. Add model specific code to the "switch (id_product) {...}" sentences.
112  */
113 
114 /*
115  *
116  * keyspan driver glue code
117  *
118  */
119 #include <sys/types.h>
120 #include <sys/param.h>
121 #include <sys/stream.h>
122 #include <sys/conf.h>
123 #include <sys/ddi.h>
124 #include <sys/sunddi.h>
125 
126 #define	USBDRV_MAJOR_VER	2
127 #define	USBDRV_MINOR_VER	0
128 
129 #include <sys/usb/usba.h>
130 
131 #include <sys/usb/clients/usbser/usbser.h>
132 #include <sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h>
133 
134 #include <sys/byteorder.h>
135 #include <sys/strsun.h>
136 
137 /* configuration entry points */
138 static int	usbser_keyspan_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
139 		void **);
140 static int	usbser_keyspan_attach(dev_info_t *, ddi_attach_cmd_t);
141 static int	usbser_keyspan_detach(dev_info_t *, ddi_detach_cmd_t);
142 static int	usbser_keyspan_open(queue_t *, dev_t *, int, int, cred_t *);
143 
144 /* functions related with set config or firmware download */
145 static int	keyspan_pre_attach(dev_info_t *, ddi_attach_cmd_t, void *);
146 static int	keyspan_set_cfg(dev_info_t *, uint8_t);
147 static int	keyspan_pre_detach(dev_info_t *, ddi_detach_cmd_t, void *);
148 static boolean_t keyspan_need_fw(usb_client_dev_data_t *);
149 static int	keyspan_set_reg(keyspan_pipe_t *, uchar_t);
150 static int	keyspan_write_memory(keyspan_pipe_t *, uint16_t, uchar_t *,
151 		uint16_t, uint8_t);
152 static int	keyspan_download_firmware(keyspan_pre_state_t *);
153 
154 static void    *usbser_keyspan_statep;	/* soft state */
155 
156 extern ds_ops_t ds_ops;		/* DSD operations */
157 
158 /*
159  * STREAMS structures
160  */
161 struct module_info usbser_keyspan_modinfo = {
162 	0,			/* module id */
163 	"usbsksp",		/* module name */
164 	USBSER_MIN_PKTSZ,	/* min pkt size */
165 	USBSER_MAX_PKTSZ,	/* max pkt size */
166 	USBSER_HIWAT,		/* hi watermark */
167 	USBSER_LOWAT		/* low watermark */
168 };
169 
170 static struct qinit usbser_keyspan_rinit = {
171 	putq,
172 	usbser_rsrv,
173 	usbser_keyspan_open,
174 	usbser_close,
175 	NULL,
176 	&usbser_keyspan_modinfo,
177 	NULL
178 };
179 
180 static struct qinit usbser_keyspan_winit = {
181 	usbser_wput,
182 	usbser_wsrv,
183 	NULL,
184 	NULL,
185 	NULL,
186 	&usbser_keyspan_modinfo,
187 	NULL
188 };
189 
190 struct streamtab usbser_keyspan_str_info = {
191 	&usbser_keyspan_rinit, &usbser_keyspan_winit, NULL, NULL
192 };
193 
194 static struct cb_ops usbser_keyspan_cb_ops = {
195 	nodev,			/* cb_open */
196 	nodev,			/* cb_close */
197 	nodev,			/* cb_strategy */
198 	nodev,			/* cb_print */
199 	nodev,			/* cb_dump */
200 	nodev,			/* cb_read */
201 	nodev,			/* cb_write */
202 	nodev,			/* cb_ioctl */
203 	nodev,			/* cb_devmap */
204 	nodev,			/* cb_mmap */
205 	nodev,			/* cb_segmap */
206 	nochpoll,		/* cb_chpoll */
207 	ddi_prop_op,		/* cb_prop_op */
208 	&usbser_keyspan_str_info,	/* cb_stream */
209 	(int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG)	/* cb_flag */
210 };
211 
212 /*
213  * auto configuration ops
214  */
215 struct dev_ops usbser_keyspan_ops = {
216 	DEVO_REV,		/* devo_rev */
217 	0,			/* devo_refcnt */
218 	usbser_keyspan_getinfo,	/* devo_getinfo */
219 	nulldev,		/* devo_identify */
220 	nulldev,		/* devo_probe */
221 	usbser_keyspan_attach,	/* devo_attach */
222 	usbser_keyspan_detach,	/* devo_detach */
223 	nodev,			/* devo_reset */
224 	&usbser_keyspan_cb_ops,	/* devo_cb_ops */
225 	(struct bus_ops *)NULL,	/* devo_bus_ops */
226 	usbser_power		/* devo_power */
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 %I%",
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 		    &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 }
636