160b08185Syz /*
260b08185Syz  * CDDL HEADER START
360b08185Syz  *
460b08185Syz  * The contents of this file are subject to the terms of the
502dd2108Slg  * Common Development and Distribution License (the "License").
602dd2108Slg  * You may not use this file except in compliance with the License.
760b08185Syz  *
860b08185Syz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
960b08185Syz  * or http://www.opensolaris.org/os/licensing.
1060b08185Syz  * See the License for the specific language governing permissions
1160b08185Syz  * and limitations under the License.
1260b08185Syz  *
1360b08185Syz  * When distributing Covered Code, include this CDDL HEADER in each
1460b08185Syz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1560b08185Syz  * If applicable, add the following below this CDDL HEADER, with the
1660b08185Syz  * fields enclosed by brackets "[]" replaced with your own identifying
1760b08185Syz  * information: Portions Copyright [yyyy] [name of copyright owner]
1860b08185Syz  *
1960b08185Syz  * CDDL HEADER END
2060b08185Syz  */
2160b08185Syz 
2260b08185Syz /*
2377e51571Sgongtian zhao - Sun Microsystems - Beijing China  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2460b08185Syz  * Use is subject to license terms.
2560b08185Syz  */
2660b08185Syz 
2760b08185Syz 
2860b08185Syz /*
2902dd2108Slg  * This driver includes code for Keyspan USA49WG/USA49WLC/USA19HS adapters. It
3002dd2108Slg  * is a device-specific driver (DSD) working with USB generic serial driver
3102dd2108Slg  * (GSD). It implements the USB-to-serial device-specific driver interface
3202dd2108Slg  * (DSDI) which is offered by GSD. The interface is defined by ds_ops_t
3302dd2108Slg  * structure.
3460b08185Syz  *
3560b08185Syz  * For USA49WLC, it's necessary to download firmware every time the device is
3660b08185Syz  * plugged. Before the firmware is downloaded, we say that the device is in
3760b08185Syz  * "firmware mode", and the attach routin is keyspan_pre_attach(). After
3860b08185Syz  * downloading, the device's product id will change to 0x12a. Then the device
3960b08185Syz  * will be enumerated again and another attach for the new product id will
4060b08185Syz  * begin. No firmware is included in the driver. The functions of USA49WLC is
4160b08185Syz  * disabled.
4260b08185Syz  *
4302dd2108Slg  * For USA49WG and USA19HS, no need to download firmware since it can be kept
4402dd2108Slg  * in the device's memory.
4560b08185Syz  *
4602dd2108Slg  * For USA49WLC and USA19HS, it's necessary to check and switch their
4702dd2108Slg  * configrations at the beginning of attach, since each of them has two
4802dd2108Slg  * configrations. This driver uses the one whose endpoints are all bulk.
4902dd2108Slg  *
5002dd2108Slg  * For USA49WG, this driver uses the third configuration which has 6 endpoints,
5102dd2108Slg  * 3 bulk out eps, 1 bulk in ep, 1 intr in ep, 1 intr out ep. Bulk in ep is
5202dd2108Slg  * shared by 4 ports for receiving data.
5360b08185Syz  *
5460b08185Syz  * Some of Keyspan adapters have only one port, some have two or four ports.
5560b08185Syz  * This driver supports up to four ports. Each port has its own states (traced
5660b08185Syz  * by keyspan_port structure) and can be operated independently.
5760b08185Syz  *
5860b08185Syz  * port_state:
5960b08185Syz  *
6060b08185Syz  *   KEYSPAN_PORT_NOT_INIT
6160b08185Syz  *	    |
6260b08185Syz  *	    |
6360b08185Syz  *     attach_ports
6460b08185Syz  *	    |
6560b08185Syz  *	    |
6660b08185Syz  *	    |
6760b08185Syz  *	    v
6860b08185Syz  *   KEYSPAN_PORT_CLOSED <-----close-------<---- +
6960b08185Syz  *	|					 |
7060b08185Syz  *	|					 |
7160b08185Syz  *	|					 |
7260b08185Syz  *  open_port					 |
7360b08185Syz  *	|					 |
7460b08185Syz  *	|					 |
7560b08185Syz  *	v					 |
7660b08185Syz  * KEYSPAN_PORT_OPENING ---open_hw_port---> USBSER_PORT_OPEN
7760b08185Syz  *
7860b08185Syz  * Each port has its own data in/out pipes and each pipe also has its own states
7960b08185Syz  * (traced by keyspan_pipe structure). The pipe states is as following:
8060b08185Syz  *
8160b08185Syz  * pipe_state:
8260b08185Syz  *
8360b08185Syz  *	  KEYSPAN_PIPE_NOT_INIT
8460b08185Syz  *		|	^
8560b08185Syz  *		|	|
8660b08185Syz  * keyspan_init_pipes  keyspan_fini_pipes
8760b08185Syz  *		|	|
8860b08185Syz  *		v	|
8960b08185Syz  *	   KEYSPAN_PIPE_CLOSED ------------->-----------+
9060b08185Syz  *		  ^					|
9160b08185Syz  *		  |			  reconnect/resume/open_port
9260b08185Syz  *		  |					|
9360b08185Syz  *    disconnect/suspend/close_port			|
9460b08185Syz  *		  |					v
9560b08185Syz  *		  +---------<------------------ KEYSPAN_PIPE_OPEN
9660b08185Syz  *
9760b08185Syz  * To control the device and get its status in a timely way, this driver makes
9860b08185Syz  * use of two global bulk endpoints for cmd and status on the device. The pipes
9960b08185Syz  * for cmd/status will be opened during attach. For multi-port devices, one of
10060b08185Syz  * the cmd/status message fields will designate which port this message is for.
10160b08185Syz  *
10260b08185Syz  * This driver can be easily extended to support more Keyspan adapter models.
10360b08185Syz  * You need the following steps to reach the aim:
10460b08185Syz  * 1. Add the model specific data structures, like cmd/status message structure.
10560b08185Syz  * 2. If the device need firmware downloaded, add the firmware code as a header
10660b08185Syz  * file, and add code to keyspan_pre_attach() as what were done for USA49WLC.
10760b08185Syz  * 3. Add several model specific functions, like keyspan_build_cmd_msg_*,
10860b08185Syz  * keyspan_default_port_params_*, keyspan_save_port_params_*, etc. The functions
10960b08185Syz  * for USA19HS and USA49WLC can be taken as examples.
11060b08185Syz  * 4. Add model specific code to the "switch (id_product) {...}" sentences.
11160b08185Syz  */
11260b08185Syz 
11360b08185Syz /*
11460b08185Syz  *
11560b08185Syz  * keyspan driver glue code
11660b08185Syz  *
11760b08185Syz  */
11860b08185Syz #include <sys/types.h>
11960b08185Syz #include <sys/param.h>
12060b08185Syz #include <sys/stream.h>
12160b08185Syz #include <sys/conf.h>
12260b08185Syz #include <sys/ddi.h>
12360b08185Syz #include <sys/sunddi.h>
12460b08185Syz 
12560b08185Syz #define	USBDRV_MAJOR_VER	2
12660b08185Syz #define	USBDRV_MINOR_VER	0
12760b08185Syz 
12860b08185Syz #include <sys/usb/usba.h>
12960b08185Syz 
13060b08185Syz #include <sys/usb/clients/usbser/usbser.h>
13160b08185Syz #include <sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h>
13260b08185Syz 
13360b08185Syz #include <sys/byteorder.h>
13460b08185Syz #include <sys/strsun.h>
13560b08185Syz 
13660b08185Syz /* configuration entry points */
13760b08185Syz static int	usbser_keyspan_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
13860b08185Syz 		void **);
13960b08185Syz static int	usbser_keyspan_attach(dev_info_t *, ddi_attach_cmd_t);
14060b08185Syz static int	usbser_keyspan_detach(dev_info_t *, ddi_detach_cmd_t);
14160b08185Syz static int	usbser_keyspan_open(queue_t *, dev_t *, int, int, cred_t *);
14260b08185Syz 
14360b08185Syz /* functions related with set config or firmware download */
14460b08185Syz static int	keyspan_pre_attach(dev_info_t *, ddi_attach_cmd_t, void *);
14502dd2108Slg static int	keyspan_set_cfg(dev_info_t *, uint8_t);
14660b08185Syz static int	keyspan_pre_detach(dev_info_t *, ddi_detach_cmd_t, void *);
14760b08185Syz static boolean_t keyspan_need_fw(usb_client_dev_data_t *);
14860b08185Syz static int	keyspan_set_reg(keyspan_pipe_t *, uchar_t);
14960b08185Syz static int	keyspan_write_memory(keyspan_pipe_t *, uint16_t, uchar_t *,
15060b08185Syz 		uint16_t, uint8_t);
151c138f478Syz static int	keyspan_download_firmware(keyspan_pre_state_t *);
15260b08185Syz 
15360b08185Syz static void    *usbser_keyspan_statep;	/* soft state */
15460b08185Syz 
155e8ed0869SJohn Beck extern ds_ops_t keyspan_ds_ops;		/* DSD operations */
15660b08185Syz 
15760b08185Syz /*
15860b08185Syz  * STREAMS structures
15960b08185Syz  */
16060b08185Syz struct module_info usbser_keyspan_modinfo = {
16160b08185Syz 	0,			/* module id */
16260b08185Syz 	"usbsksp",		/* module name */
16360b08185Syz 	USBSER_MIN_PKTSZ,	/* min pkt size */
16460b08185Syz 	USBSER_MAX_PKTSZ,	/* max pkt size */
16560b08185Syz 	USBSER_HIWAT,		/* hi watermark */
16660b08185Syz 	USBSER_LOWAT		/* low watermark */
16760b08185Syz };
16860b08185Syz 
16960b08185Syz static struct qinit usbser_keyspan_rinit = {
17060b08185Syz 	putq,
17160b08185Syz 	usbser_rsrv,
17260b08185Syz 	usbser_keyspan_open,
17360b08185Syz 	usbser_close,
17460b08185Syz 	NULL,
17560b08185Syz 	&usbser_keyspan_modinfo,
17660b08185Syz 	NULL
17760b08185Syz };
17860b08185Syz 
17960b08185Syz static struct qinit usbser_keyspan_winit = {
18060b08185Syz 	usbser_wput,
18160b08185Syz 	usbser_wsrv,
18260b08185Syz 	NULL,
18360b08185Syz 	NULL,
18460b08185Syz 	NULL,
18560b08185Syz 	&usbser_keyspan_modinfo,
18660b08185Syz 	NULL
18760b08185Syz };
18860b08185Syz 
18960b08185Syz struct streamtab usbser_keyspan_str_info = {
19060b08185Syz 	&usbser_keyspan_rinit, &usbser_keyspan_winit, NULL, NULL
19160b08185Syz };
19260b08185Syz 
19360b08185Syz static struct cb_ops usbser_keyspan_cb_ops = {
19460b08185Syz 	nodev,			/* cb_open */
19560b08185Syz 	nodev,			/* cb_close */
19660b08185Syz 	nodev,			/* cb_strategy */
19760b08185Syz 	nodev,			/* cb_print */
19860b08185Syz 	nodev,			/* cb_dump */
19960b08185Syz 	nodev,			/* cb_read */
20060b08185Syz 	nodev,			/* cb_write */
20160b08185Syz 	nodev,			/* cb_ioctl */
20260b08185Syz 	nodev,			/* cb_devmap */
20360b08185Syz 	nodev,			/* cb_mmap */
20460b08185Syz 	nodev,			/* cb_segmap */
20560b08185Syz 	nochpoll,		/* cb_chpoll */
20660b08185Syz 	ddi_prop_op,		/* cb_prop_op */
20760b08185Syz 	&usbser_keyspan_str_info,	/* cb_stream */
20860b08185Syz 	(int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG)	/* cb_flag */
20960b08185Syz };
21060b08185Syz 
21160b08185Syz /*
21260b08185Syz  * auto configuration ops
21360b08185Syz  */
21460b08185Syz struct dev_ops usbser_keyspan_ops = {
21560b08185Syz 	DEVO_REV,		/* devo_rev */
21660b08185Syz 	0,			/* devo_refcnt */
21760b08185Syz 	usbser_keyspan_getinfo,	/* devo_getinfo */
21860b08185Syz 	nulldev,		/* devo_identify */
21960b08185Syz 	nulldev,		/* devo_probe */
22060b08185Syz 	usbser_keyspan_attach,	/* devo_attach */
22160b08185Syz 	usbser_keyspan_detach,	/* devo_detach */
22260b08185Syz 	nodev,			/* devo_reset */
22360b08185Syz 	&usbser_keyspan_cb_ops,	/* devo_cb_ops */
22460b08185Syz 	(struct bus_ops *)NULL,	/* devo_bus_ops */
22519397407SSherry Moore 	usbser_power,		/* devo_power */
22619397407SSherry Moore 	ddi_quiesce_not_needed,		/* devo_quiesce */
22760b08185Syz };
22860b08185Syz 
22960b08185Syz extern struct mod_ops mod_driverops;
23060b08185Syz 
23160b08185Syz static struct modldrv modldrv = {
23260b08185Syz 	&mod_driverops,		/* type of module - driver */
23377e51571Sgongtian zhao - Sun Microsystems - Beijing China 	"USB keyspan usb2serial driver",
23460b08185Syz 	&usbser_keyspan_ops,
23560b08185Syz };
23660b08185Syz 
23760b08185Syz static struct modlinkage modlinkage = {
23860b08185Syz 	MODREV_1, &modldrv, 0
23960b08185Syz };
24060b08185Syz 
241c138f478Syz /* debug support */
242c138f478Syz static uint_t	keyspan_pre_errlevel = USB_LOG_L4;
243c138f478Syz static uint_t	keyspan_pre_errmask = DPRINT_MASK_ALL;
244c138f478Syz static uint_t	keyspan_pre_instance_debug = (uint_t)-1;
245c138f478Syz 
246c138f478Syz /* firmware support for usa49wlc model */
247c138f478Syz extern usbser_keyspan_fw_record_t *keyspan_usa49wlc_fw(void);
248c138f478Syz #pragma weak keyspan_usa49wlc_fw
24960b08185Syz 
25060b08185Syz /*
25160b08185Syz  * configuration entry points
25260b08185Syz  * --------------------------
25360b08185Syz  */
25460b08185Syz int
_init(void)25560b08185Syz _init(void)
25660b08185Syz {
25760b08185Syz 	int    error;
25860b08185Syz 
25960b08185Syz 	if ((error = mod_install(&modlinkage)) == 0) {
26060b08185Syz 		error = ddi_soft_state_init(&usbser_keyspan_statep,
26160b08185Syz 		    max(usbser_soft_state_size(),
26260b08185Syz 		    sizeof (keyspan_pre_state_t)), 1);
26360b08185Syz 	}
26460b08185Syz 
26560b08185Syz 	return (error);
26660b08185Syz }
26760b08185Syz 
26860b08185Syz 
26960b08185Syz int
_fini(void)27060b08185Syz _fini(void)
27160b08185Syz {
27260b08185Syz 	int    error;
27360b08185Syz 
27460b08185Syz 	if ((error = mod_remove(&modlinkage)) == 0) {
27560b08185Syz 		ddi_soft_state_fini(&usbser_keyspan_statep);
27660b08185Syz 	}
27760b08185Syz 
27860b08185Syz 	return (error);
27960b08185Syz }
28060b08185Syz 
28160b08185Syz 
28260b08185Syz int
_info(struct modinfo * modinfop)28360b08185Syz _info(struct modinfo *modinfop)
28460b08185Syz {
28560b08185Syz 	return (mod_info(&modlinkage, modinfop));
28660b08185Syz }
28760b08185Syz 
28860b08185Syz 
28960b08185Syz /*ARGSUSED*/
29060b08185Syz int
usbser_keyspan_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)29160b08185Syz usbser_keyspan_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
29260b08185Syz 		void **result)
29360b08185Syz {
29460b08185Syz 	return (usbser_getinfo(dip, infocmd, arg, result,
29560b08185Syz 	    usbser_keyspan_statep));
29660b08185Syz }
29760b08185Syz 
29860b08185Syz 
29960b08185Syz static int
usbser_keyspan_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)30060b08185Syz usbser_keyspan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
30160b08185Syz {
30260b08185Syz 	int	rval;
30360b08185Syz 
30460b08185Syz 	/*
30560b08185Syz 	 * Once the device is plugged, we need set its cfg. And need download
30660b08185Syz 	 * firmware for some of them.
30760b08185Syz 	 */
30860b08185Syz 	rval = keyspan_pre_attach(dip, cmd, usbser_keyspan_statep);
30960b08185Syz 
31060b08185Syz 	/*
31160b08185Syz 	 * After the cfg is set, and the firmware is downloaded,
31260b08185Syz 	 * do the real attach.
31360b08185Syz 	 */
31460b08185Syz 	if (rval == DDI_ECONTEXT) {
31560b08185Syz 
31660b08185Syz 		return (usbser_attach(dip, cmd, usbser_keyspan_statep,
317e8ed0869SJohn Beck 		    &keyspan_ds_ops));
31860b08185Syz 	} else {
31960b08185Syz 
32060b08185Syz 		return (rval);
32160b08185Syz 	}
32260b08185Syz }
32360b08185Syz 
32460b08185Syz 
32560b08185Syz static int
usbser_keyspan_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)32660b08185Syz usbser_keyspan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
32760b08185Syz {
328c138f478Syz 
32960b08185Syz 	if (ddi_get_driver_private(dip) == NULL) {
33060b08185Syz 
33160b08185Syz 		return (keyspan_pre_detach(dip, cmd, usbser_keyspan_statep));
33260b08185Syz 	} else {
333c138f478Syz 
33460b08185Syz 
33560b08185Syz 		return (usbser_detach(dip, cmd, usbser_keyspan_statep));
33660b08185Syz 
337c138f478Syz 
33860b08185Syz 	}
339c138f478Syz 
34060b08185Syz }
34160b08185Syz 
34260b08185Syz 
34360b08185Syz static int
usbser_keyspan_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)34460b08185Syz usbser_keyspan_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
34560b08185Syz {
34660b08185Syz 	return (usbser_open(rq, dev, flag, sflag, cr, usbser_keyspan_statep));
34760b08185Syz }
34860b08185Syz 
34960b08185Syz /*
35060b08185Syz  * Switch config or download firmware
35160b08185Syz  */
35260b08185Syz /*ARGSUSED*/
35360b08185Syz static int
keyspan_pre_attach(dev_info_t * dip,ddi_attach_cmd_t cmd,void * statep)35460b08185Syz keyspan_pre_attach(dev_info_t *dip, ddi_attach_cmd_t cmd, void *statep)
35560b08185Syz {
35660b08185Syz 
357c138f478Syz 	int			instance = ddi_get_instance(dip);
358c138f478Syz 	keyspan_pre_state_t	*kbp = NULL;
359c138f478Syz 	usb_client_dev_data_t	*dev_data = NULL;
360c138f478Syz 	int			rval = DDI_FAILURE;
36160b08185Syz 
36260b08185Syz 	switch (cmd) {
36360b08185Syz 	case DDI_ATTACH:
36460b08185Syz 
36560b08185Syz 		break;
36660b08185Syz 	case DDI_RESUME:
36760b08185Syz 
36860b08185Syz 		return (DDI_SUCCESS);
36960b08185Syz 	default:
37060b08185Syz 
37160b08185Syz 		return (DDI_FAILURE);
37260b08185Syz 	}
37360b08185Syz 
37460b08185Syz 	/* attach driver to USBA */
375c138f478Syz 	if (usb_client_attach(dip, USBDRV_VERSION, 0) == USB_SUCCESS) {
376c138f478Syz 		(void) usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0);
37760b08185Syz 	}
378c138f478Syz 	if (dev_data == NULL) {
37960b08185Syz 
380c138f478Syz 		goto fail;
38160b08185Syz 	}
38260b08185Syz 
38360b08185Syz 	/*
38402dd2108Slg 	 * If 19HS or 49WG, needn't download firmware, but need check the
38502dd2108Slg 	 * current cfg.
38660b08185Syz 	 * If 49WLC, need check the current cfg before download fw. And after
387c138f478Syz 	 * download, the product id will change to KEYSPAN_USA49WLC_PID.
38860b08185Syz 	 */
38960b08185Syz 	if (dev_data->dev_descr->idProduct == KEYSPAN_USA19HS_PID ||
39060b08185Syz 	    dev_data->dev_descr->idProduct == KEYSPAN_USA49WLC_PID) {
39102dd2108Slg 		if (keyspan_set_cfg(dip, 1) == USB_SUCCESS) {
39202dd2108Slg 			/* Go to keyspan_attach() by return DDI_ECONTEXT. */
39302dd2108Slg 			rval =	DDI_ECONTEXT;
39402dd2108Slg 		}
39502dd2108Slg 
39602dd2108Slg 		goto fail;
39702dd2108Slg 	} else if (dev_data->dev_descr->idProduct == KEYSPAN_USA49WG_PID) {
39802dd2108Slg 		if (keyspan_set_cfg(dip, 2) == USB_SUCCESS) {
399c138f478Syz 			/* Go to keyspan_attach() by return DDI_ECONTEXT. */
400c138f478Syz 			rval =	DDI_ECONTEXT;
40160b08185Syz 		}
40260b08185Syz 
403c138f478Syz 		goto fail;
40460b08185Syz 	}
40560b08185Syz 
40602dd2108Slg 
40760b08185Syz 	/*
408c138f478Syz 	 * By checking KEYSPAN_FW_FLAG,  we can check whether the firmware
409c138f478Syz 	 * has been downloaded.
410c138f478Syz 	 * If firmware is already there, then do normal attach.
41160b08185Syz 	 */
41260b08185Syz 	if (!keyspan_need_fw(dev_data)) {
41360b08185Syz 		/* Go to keyspan_attach() by return DDI_ECONTEXT. */
414c138f478Syz 		rval =	DDI_ECONTEXT;
41560b08185Syz 
416c138f478Syz 		goto fail;
41760b08185Syz 	}
41860b08185Syz 
419c138f478Syz 	/* Go on to download firmware. */
42060b08185Syz 
421c138f478Syz 	if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) {
422c138f478Syz 		kbp = ddi_get_soft_state(statep, instance);
42360b08185Syz 	}
424c138f478Syz 	if (kbp) {
425c138f478Syz 		kbp->kb_dip = dip;
426c138f478Syz 		kbp->kb_instance = instance;
427c138f478Syz 		kbp->kb_dev_data = dev_data;
428c138f478Syz 		kbp->kb_def_pipe.pipe_handle = kbp->kb_dev_data->dev_default_ph;
429c138f478Syz 		kbp->kb_lh = usb_alloc_log_hdl(kbp->kb_dip, "keyspan[*].",
430c138f478Syz 		    &keyspan_pre_errlevel, &keyspan_pre_errmask,
431c138f478Syz 		    &keyspan_pre_instance_debug, 0);
43260b08185Syz 
433c138f478Syz 		kbp->kb_def_pipe.pipe_lh = kbp->kb_lh;
43460b08185Syz 
435c138f478Syz 		if (keyspan_download_firmware(kbp) == USB_SUCCESS) {
436c138f478Syz 			USB_DPRINTF_L4(DPRINT_ATTACH, kbp->kb_lh,
437c138f478Syz 			    "keyspan_pre_attach: completed.");
43860b08185Syz 
439c138f478Syz 			/* keyspan download firmware done. */
44060b08185Syz 
441c138f478Syz 			return (DDI_SUCCESS);
44260b08185Syz 		}
44360b08185Syz 	}
444c138f478Syz fail:
445c138f478Syz 	if (kbp) {
446c138f478Syz 		usb_free_log_hdl(kbp->kb_lh);
447c138f478Syz 		ddi_soft_state_free(statep, instance);
44860b08185Syz 	}
44960b08185Syz 	usb_client_detach(dip, dev_data);
45060b08185Syz 
451c138f478Syz 	return (rval);
45260b08185Syz }
45360b08185Syz 
45460b08185Syz 
45560b08185Syz static int
keyspan_pre_detach(dev_info_t * dip,ddi_detach_cmd_t cmd,void * statep)45660b08185Syz keyspan_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep)
45760b08185Syz {
45860b08185Syz 	int		instance = ddi_get_instance(dip);
45960b08185Syz 	keyspan_pre_state_t	*kbp;
46060b08185Syz 
46160b08185Syz 	kbp = ddi_get_soft_state(statep, instance);
46260b08185Syz 
46360b08185Syz 	switch (cmd) {
46460b08185Syz 	case DDI_DETACH:
46560b08185Syz 
46660b08185Syz 		break;
46760b08185Syz 	case DDI_SUSPEND:
46860b08185Syz 
46960b08185Syz 		return (DDI_SUCCESS);
47060b08185Syz 	default:
47160b08185Syz 
47260b08185Syz 		return (DDI_FAILURE);
47360b08185Syz 	}
47460b08185Syz 
475c138f478Syz 	usb_free_log_hdl(kbp->kb_lh);
47660b08185Syz 	usb_client_detach(dip, kbp->kb_dev_data);
47760b08185Syz 	ddi_soft_state_free(statep, instance);
47860b08185Syz 
47960b08185Syz 	return (DDI_SUCCESS);
48060b08185Syz }
48160b08185Syz 
48260b08185Syz 
48360b08185Syz /* Set cfg for the device which has more than one cfg */
48460b08185Syz static int
keyspan_set_cfg(dev_info_t * dip,uint8_t cfg_num)48502dd2108Slg keyspan_set_cfg(dev_info_t *dip, uint8_t cfg_num)
48660b08185Syz {
48760b08185Syz 
48802dd2108Slg 	if (usb_set_cfg(dip, cfg_num, USB_FLAGS_SLEEP,
48977e51571Sgongtian zhao - Sun Microsystems - Beijing China 	    NULL, NULL) != USB_SUCCESS) {
49060b08185Syz 
49160b08185Syz 		return (USB_FAILURE);
49260b08185Syz 	}
49360b08185Syz 
49460b08185Syz 	return (USB_SUCCESS);
49560b08185Syz }
49660b08185Syz 
49760b08185Syz 
49860b08185Syz /* Return TRUE if need download firmware to the device. */
49960b08185Syz static boolean_t
keyspan_need_fw(usb_client_dev_data_t * dev_data)50060b08185Syz keyspan_need_fw(usb_client_dev_data_t *dev_data)
50160b08185Syz {
50260b08185Syz 	uint16_t	bcd_descr;
50360b08185Syz 	uint16_t	bcd_descr_change;
50460b08185Syz 
50560b08185Syz 	/* need to convert to Little-Endian */
50660b08185Syz 	bcd_descr = dev_data->dev_descr->bcdDevice;
50760b08185Syz 
50860b08185Syz 	/*
50960b08185Syz 	 * According to Keyspan's interface spec, this flag indicates
51060b08185Syz 	 * if need download fw.
51160b08185Syz 	 */
51260b08185Syz 	bcd_descr_change = bcd_descr & KEYSPAN_FW_FLAG;
51360b08185Syz 
51460b08185Syz 	return (bcd_descr_change == KEYSPAN_FW_FLAG);
51560b08185Syz }
51660b08185Syz 
51760b08185Syz /* Set the device's register. */
51860b08185Syz static int
keyspan_set_reg(keyspan_pipe_t * pipe,uchar_t bit)51960b08185Syz keyspan_set_reg(keyspan_pipe_t *pipe, uchar_t bit)
52060b08185Syz {
52160b08185Syz 	int	rval;
52260b08185Syz 
52360b08185Syz 	/*
52460b08185Syz 	 * (0x7f92) is the reg addr we want to set.
52560b08185Syz 	 * We set this reg before/after downloading firmware.
52660b08185Syz 	 */
52760b08185Syz 	rval = keyspan_write_memory(pipe, 0x7f92, &bit, 1, KEYSPAN_REQ_SET);
52860b08185Syz 
52960b08185Syz 	return (rval);
53060b08185Syz }
53160b08185Syz 
53260b08185Syz /*
53360b08185Syz  * Download firmware or set register to the device by default ctrl pipe
53460b08185Syz  */
53560b08185Syz static int
keyspan_write_memory(keyspan_pipe_t * pipe,uint16_t addr,uchar_t * buf,uint16_t len,uint8_t bRequest)53660b08185Syz keyspan_write_memory(keyspan_pipe_t *pipe, uint16_t addr, uchar_t *buf,
53760b08185Syz     uint16_t len, uint8_t bRequest)
53860b08185Syz {
53960b08185Syz 	mblk_t *data;
54060b08185Syz 	usb_ctrl_setup_t setup;
54160b08185Syz 
54260b08185Syz 	usb_cb_flags_t	cb_flags;
54360b08185Syz 	usb_cr_t	cr;
54460b08185Syz 	uint8_t		retry = 0;
54560b08185Syz 
54660b08185Syz 	/* reuse previous mblk if possible */
54760b08185Syz 	if ((data = allocb(len, BPRI_HI)) == NULL) {
54860b08185Syz 
54960b08185Syz 		return (USB_FAILURE);
55060b08185Syz 	}
55160b08185Syz 
55260b08185Syz 	bcopy(buf, data->b_rptr, len);
55360b08185Syz 
55460b08185Syz 	setup.bmRequestType = USB_DEV_REQ_TYPE_VENDOR;
55560b08185Syz 
55660b08185Syz 	/* This is a req defined by hardware vendor. */
55760b08185Syz 	setup.bRequest = bRequest;
55860b08185Syz 	setup.wValue = addr;
55960b08185Syz 	setup.wIndex = 0;
56060b08185Syz 	setup.wLength = len;
56160b08185Syz 	setup.attrs = 0;
56260b08185Syz 
56360b08185Syz 	while (usb_pipe_ctrl_xfer_wait(pipe->pipe_handle, &setup, &data,
56477e51571Sgongtian zhao - Sun Microsystems - Beijing China 	    &cr, &cb_flags, 0) != USB_SUCCESS) {
56560b08185Syz 
56660b08185Syz 		/* KEYSPAN_RETRY */
56760b08185Syz 		if (++retry > 3) {
56860b08185Syz 			if (data) {
56960b08185Syz 				freemsg(data);
57060b08185Syz 			}
57160b08185Syz 
57260b08185Syz 			return (USB_FAILURE);
57360b08185Syz 		}
57460b08185Syz 	}
57560b08185Syz 
57660b08185Syz 	if (data) {
57760b08185Syz 		freemsg(data);
57860b08185Syz 	}
57960b08185Syz 
58060b08185Syz 	return (USB_SUCCESS);
58160b08185Syz }
582c138f478Syz 
583c138f478Syz /* Download firmware into device */
584c138f478Syz static int
keyspan_download_firmware(keyspan_pre_state_t * kbp)585c138f478Syz keyspan_download_firmware(keyspan_pre_state_t *kbp)
586c138f478Syz {
587c138f478Syz 	usbser_keyspan_fw_record_t *record = NULL;
588c138f478Syz 
589c138f478Syz 	/* If the firmware module exists, then download it to device. */
590c138f478Syz 	if (&keyspan_usa49wlc_fw) {
591c138f478Syz 
592c138f478Syz 		record = keyspan_usa49wlc_fw();
593c138f478Syz 	}
594c138f478Syz 
595c138f478Syz 	if (!record) {
596c138f478Syz 		USB_DPRINTF_L1(DPRINT_ATTACH, kbp->kb_lh,
597c138f478Syz 		    "No firmware available for Keyspan usa49wlc"
598*bbf21555SRichard Lowe 		    " usb-to-serial adapter. Refer to usbsksp(4D)"
599c138f478Syz 		    " for details.");
600c138f478Syz 
601c138f478Syz 		return (USB_FAILURE);
602c138f478Syz 	}
603c138f478Syz 
604c138f478Syz 	/* Set bit 1 before downloading firmware. */
605c138f478Syz 	if (keyspan_set_reg(&kbp->kb_def_pipe, 1) != USB_SUCCESS) {
606c138f478Syz 		USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh,
607c138f478Syz 		    "keyspan_pre_attach: Set register failed.");
608c138f478Syz 
609c138f478Syz 		return (USB_FAILURE);
610c138f478Syz 	}
611c138f478Syz 
612c138f478Syz 	/* Write until the last record of the firmware */
613c138f478Syz 	while (record->address != 0xffff) {
614c138f478Syz 		if (keyspan_write_memory(&kbp->kb_def_pipe,
615c138f478Syz 		    record->address, (uchar_t *)record->data,
616c138f478Syz 		    record->data_len, KEYSPAN_REQ_SET) != USB_SUCCESS) {
617c138f478Syz 			USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh,
618c138f478Syz 			    "keyspan_pre_attach: download firmware failed.");
619c138f478Syz 
620c138f478Syz 			return (USB_FAILURE);
621c138f478Syz 		}
622c138f478Syz 		record++;
623c138f478Syz 	}
624c138f478Syz 
625c138f478Syz 	/*
626c138f478Syz 	 * Set bit 0, device will be enumerated again after a while,
627c138f478Syz 	 * and then go to keyspan_attach()
628c138f478Syz 	 */
629c138f478Syz 	if (keyspan_set_reg(&kbp->kb_def_pipe, 0) != USB_SUCCESS) {
630c138f478Syz 
631c138f478Syz 		return (USB_FAILURE);
632c138f478Syz 	}
633c138f478Syz 
634c138f478Syz 	return (USB_SUCCESS);
635c138f478Syz }
636