Home | History | Annotate | Download | only in usbser_keyspan
      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
    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 		    &keyspan_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