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