xref: /illumos-gate/usr/src/uts/common/io/usb/clients/usbser/usbser_keyspan/usbser_keyspan.c (revision f6f4cb8ada400367a1921f6b93fb9e02f53ac5e6)
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 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 };
227 
228 extern struct mod_ops mod_driverops;
229 
230 static struct modldrv modldrv = {
231 	&mod_driverops,		/* type of module - driver */
232 	"USB keyspan usb2serial driver",
233 	&usbser_keyspan_ops,
234 };
235 
236 static struct modlinkage modlinkage = {
237 	MODREV_1, &modldrv, 0
238 };
239 
240 /* debug support */
241 static uint_t	keyspan_pre_errlevel = USB_LOG_L4;
242 static uint_t	keyspan_pre_errmask = DPRINT_MASK_ALL;
243 static uint_t	keyspan_pre_instance_debug = (uint_t)-1;
244 
245 /* firmware support for usa49wlc model */
246 extern usbser_keyspan_fw_record_t *keyspan_usa49wlc_fw(void);
247 #pragma weak keyspan_usa49wlc_fw
248 
249 /*
250  * configuration entry points
251  * --------------------------
252  */
253 int
254 _init(void)
255 {
256 	int    error;
257 
258 	if ((error = mod_install(&modlinkage)) == 0) {
259 		error = ddi_soft_state_init(&usbser_keyspan_statep,
260 		    max(usbser_soft_state_size(),
261 		    sizeof (keyspan_pre_state_t)), 1);
262 	}
263 
264 	return (error);
265 }
266 
267 
268 int
269 _fini(void)
270 {
271 	int    error;
272 
273 	if ((error = mod_remove(&modlinkage)) == 0) {
274 		ddi_soft_state_fini(&usbser_keyspan_statep);
275 	}
276 
277 	return (error);
278 }
279 
280 
281 int
282 _info(struct modinfo *modinfop)
283 {
284 	return (mod_info(&modlinkage, modinfop));
285 }
286 
287 
288 /*ARGSUSED*/
289 int
290 usbser_keyspan_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
291 		void **result)
292 {
293 	return (usbser_getinfo(dip, infocmd, arg, result,
294 	    usbser_keyspan_statep));
295 }
296 
297 
298 static int
299 usbser_keyspan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
300 {
301 	int	rval;
302 
303 	/*
304 	 * Once the device is plugged, we need set its cfg. And need download
305 	 * firmware for some of them.
306 	 */
307 	rval = keyspan_pre_attach(dip, cmd, usbser_keyspan_statep);
308 
309 	/*
310 	 * After the cfg is set, and the firmware is downloaded,
311 	 * do the real attach.
312 	 */
313 	if (rval == DDI_ECONTEXT) {
314 
315 		return (usbser_attach(dip, cmd, usbser_keyspan_statep,
316 		    &ds_ops));
317 	} else {
318 
319 		return (rval);
320 	}
321 }
322 
323 
324 static int
325 usbser_keyspan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
326 {
327 
328 	if (ddi_get_driver_private(dip) == NULL) {
329 
330 		return (keyspan_pre_detach(dip, cmd, usbser_keyspan_statep));
331 	} else {
332 
333 
334 		return (usbser_detach(dip, cmd, usbser_keyspan_statep));
335 
336 
337 	}
338 
339 }
340 
341 
342 static int
343 usbser_keyspan_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
344 {
345 	return (usbser_open(rq, dev, flag, sflag, cr, usbser_keyspan_statep));
346 }
347 
348 /*
349  * Switch config or download firmware
350  */
351 /*ARGSUSED*/
352 static int
353 keyspan_pre_attach(dev_info_t *dip, ddi_attach_cmd_t cmd, void *statep)
354 {
355 
356 	int			instance = ddi_get_instance(dip);
357 	keyspan_pre_state_t	*kbp = NULL;
358 	usb_client_dev_data_t	*dev_data = NULL;
359 	int			rval = DDI_FAILURE;
360 
361 	switch (cmd) {
362 	case DDI_ATTACH:
363 
364 		break;
365 	case DDI_RESUME:
366 
367 		return (DDI_SUCCESS);
368 	default:
369 
370 		return (DDI_FAILURE);
371 	}
372 
373 	/* attach driver to USBA */
374 	if (usb_client_attach(dip, USBDRV_VERSION, 0) == USB_SUCCESS) {
375 		(void) usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0);
376 	}
377 	if (dev_data == NULL) {
378 
379 		goto fail;
380 	}
381 
382 	/*
383 	 * If 19HS or 49WG, needn't download firmware, but need check the
384 	 * current cfg.
385 	 * If 49WLC, need check the current cfg before download fw. And after
386 	 * download, the product id will change to KEYSPAN_USA49WLC_PID.
387 	 */
388 	if (dev_data->dev_descr->idProduct == KEYSPAN_USA19HS_PID ||
389 	    dev_data->dev_descr->idProduct == KEYSPAN_USA49WLC_PID) {
390 		if (keyspan_set_cfg(dip, 1) == USB_SUCCESS) {
391 			/* Go to keyspan_attach() by return DDI_ECONTEXT. */
392 			rval =	DDI_ECONTEXT;
393 		}
394 
395 		goto fail;
396 	} else if (dev_data->dev_descr->idProduct == KEYSPAN_USA49WG_PID) {
397 		if (keyspan_set_cfg(dip, 2) == USB_SUCCESS) {
398 			/* Go to keyspan_attach() by return DDI_ECONTEXT. */
399 			rval =	DDI_ECONTEXT;
400 		}
401 
402 		goto fail;
403 	}
404 
405 
406 	/*
407 	 * By checking KEYSPAN_FW_FLAG,  we can check whether the firmware
408 	 * has been downloaded.
409 	 * If firmware is already there, then do normal attach.
410 	 */
411 	if (!keyspan_need_fw(dev_data)) {
412 		/* Go to keyspan_attach() by return DDI_ECONTEXT. */
413 		rval =	DDI_ECONTEXT;
414 
415 		goto fail;
416 	}
417 
418 	/* Go on to download firmware. */
419 
420 	if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) {
421 		kbp = ddi_get_soft_state(statep, instance);
422 	}
423 	if (kbp) {
424 		kbp->kb_dip = dip;
425 		kbp->kb_instance = instance;
426 		kbp->kb_dev_data = dev_data;
427 		kbp->kb_def_pipe.pipe_handle = kbp->kb_dev_data->dev_default_ph;
428 		kbp->kb_lh = usb_alloc_log_hdl(kbp->kb_dip, "keyspan[*].",
429 		    &keyspan_pre_errlevel, &keyspan_pre_errmask,
430 		    &keyspan_pre_instance_debug, 0);
431 
432 		kbp->kb_def_pipe.pipe_lh = kbp->kb_lh;
433 
434 		if (keyspan_download_firmware(kbp) == USB_SUCCESS) {
435 			USB_DPRINTF_L4(DPRINT_ATTACH, kbp->kb_lh,
436 			    "keyspan_pre_attach: completed.");
437 
438 			/* keyspan download firmware done. */
439 
440 			return (DDI_SUCCESS);
441 		}
442 	}
443 fail:
444 	if (kbp) {
445 		usb_free_log_hdl(kbp->kb_lh);
446 		ddi_soft_state_free(statep, instance);
447 	}
448 	usb_client_detach(dip, dev_data);
449 
450 	return (rval);
451 }
452 
453 
454 static int
455 keyspan_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep)
456 {
457 	int		instance = ddi_get_instance(dip);
458 	keyspan_pre_state_t	*kbp;
459 
460 	kbp = ddi_get_soft_state(statep, instance);
461 
462 	switch (cmd) {
463 	case DDI_DETACH:
464 
465 		break;
466 	case DDI_SUSPEND:
467 
468 		return (DDI_SUCCESS);
469 	default:
470 
471 		return (DDI_FAILURE);
472 	}
473 
474 	usb_free_log_hdl(kbp->kb_lh);
475 	usb_client_detach(dip, kbp->kb_dev_data);
476 	ddi_soft_state_free(statep, instance);
477 
478 	return (DDI_SUCCESS);
479 }
480 
481 
482 /* Set cfg for the device which has more than one cfg */
483 static int
484 keyspan_set_cfg(dev_info_t *dip, uint8_t cfg_num)
485 {
486 
487 	if (usb_set_cfg(dip, cfg_num, USB_FLAGS_SLEEP,
488 	    NULL, NULL) != USB_SUCCESS) {
489 
490 		return (USB_FAILURE);
491 	}
492 
493 	return (USB_SUCCESS);
494 }
495 
496 
497 /* Return TRUE if need download firmware to the device. */
498 static boolean_t
499 keyspan_need_fw(usb_client_dev_data_t *dev_data)
500 {
501 	uint16_t	bcd_descr;
502 	uint16_t	bcd_descr_change;
503 
504 	/* need to convert to Little-Endian */
505 	bcd_descr = dev_data->dev_descr->bcdDevice;
506 
507 	/*
508 	 * According to Keyspan's interface spec, this flag indicates
509 	 * if need download fw.
510 	 */
511 	bcd_descr_change = bcd_descr & KEYSPAN_FW_FLAG;
512 
513 	return (bcd_descr_change == KEYSPAN_FW_FLAG);
514 }
515 
516 /* Set the device's register. */
517 static int
518 keyspan_set_reg(keyspan_pipe_t *pipe, uchar_t bit)
519 {
520 	int	rval;
521 
522 	/*
523 	 * (0x7f92) is the reg addr we want to set.
524 	 * We set this reg before/after downloading firmware.
525 	 */
526 	rval = keyspan_write_memory(pipe, 0x7f92, &bit, 1, KEYSPAN_REQ_SET);
527 
528 	return (rval);
529 }
530 
531 /*
532  * Download firmware or set register to the device by default ctrl pipe
533  */
534 static int
535 keyspan_write_memory(keyspan_pipe_t *pipe, uint16_t addr, uchar_t *buf,
536     uint16_t len, uint8_t bRequest)
537 {
538 	mblk_t *data;
539 	usb_ctrl_setup_t setup;
540 
541 	usb_cb_flags_t	cb_flags;
542 	usb_cr_t	cr;
543 	uint8_t		retry = 0;
544 
545 	/* reuse previous mblk if possible */
546 	if ((data = allocb(len, BPRI_HI)) == NULL) {
547 
548 		return (USB_FAILURE);
549 	}
550 
551 	bcopy(buf, data->b_rptr, len);
552 
553 	setup.bmRequestType = USB_DEV_REQ_TYPE_VENDOR;
554 
555 	/* This is a req defined by hardware vendor. */
556 	setup.bRequest = bRequest;
557 	setup.wValue = addr;
558 	setup.wIndex = 0;
559 	setup.wLength = len;
560 	setup.attrs = 0;
561 
562 	while (usb_pipe_ctrl_xfer_wait(pipe->pipe_handle, &setup, &data,
563 	    &cr, &cb_flags, 0) != USB_SUCCESS) {
564 
565 		/* KEYSPAN_RETRY */
566 		if (++retry > 3) {
567 			if (data) {
568 				freemsg(data);
569 			}
570 
571 			return (USB_FAILURE);
572 		}
573 	}
574 
575 	if (data) {
576 		freemsg(data);
577 	}
578 
579 	return (USB_SUCCESS);
580 }
581 
582 /* Download firmware into device */
583 static int
584 keyspan_download_firmware(keyspan_pre_state_t *kbp)
585 {
586 	usbser_keyspan_fw_record_t *record = NULL;
587 
588 	/* If the firmware module exists, then download it to device. */
589 	if (&keyspan_usa49wlc_fw) {
590 
591 		record = keyspan_usa49wlc_fw();
592 	}
593 
594 	if (!record) {
595 		USB_DPRINTF_L1(DPRINT_ATTACH, kbp->kb_lh,
596 		    "No firmware available for Keyspan usa49wlc"
597 		    " usb-to-serial adapter. Refer to usbsksp(7D)"
598 		    " for details.");
599 
600 		return (USB_FAILURE);
601 	}
602 
603 	/* Set bit 1 before downloading firmware. */
604 	if (keyspan_set_reg(&kbp->kb_def_pipe, 1) != USB_SUCCESS) {
605 		USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh,
606 		    "keyspan_pre_attach: Set register failed.");
607 
608 		return (USB_FAILURE);
609 	}
610 
611 	/* Write until the last record of the firmware */
612 	while (record->address != 0xffff) {
613 		if (keyspan_write_memory(&kbp->kb_def_pipe,
614 		    record->address, (uchar_t *)record->data,
615 		    record->data_len, KEYSPAN_REQ_SET) != USB_SUCCESS) {
616 			USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh,
617 			    "keyspan_pre_attach: download firmware failed.");
618 
619 			return (USB_FAILURE);
620 		}
621 		record++;
622 	}
623 
624 	/*
625 	 * Set bit 0, device will be enumerated again after a while,
626 	 * and then go to keyspan_attach()
627 	 */
628 	if (keyspan_set_reg(&kbp->kb_def_pipe, 0) != USB_SUCCESS) {
629 
630 		return (USB_FAILURE);
631 	}
632 
633 	return (USB_SUCCESS);
634 }
635