/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* is a device-specific driver (DSD) working with USB generic serial driver
* (GSD). It implements the USB-to-serial device-specific driver interface
* (DSDI) which is offered by GSD. The interface is defined by ds_ops_t
* structure.
*
* For USA49WLC, it's necessary to download firmware every time the device is
* plugged. Before the firmware is downloaded, we say that the device is in
* "firmware mode", and the attach routin is keyspan_pre_attach(). After
* downloading, the device's product id will change to 0x12a. Then the device
* will be enumerated again and another attach for the new product id will
* begin. No firmware is included in the driver. The functions of USA49WLC is
* disabled.
*
* For USA49WG and USA19HS, no need to download firmware since it can be kept
* in the device's memory.
*
* For USA49WLC and USA19HS, it's necessary to check and switch their
* configrations at the beginning of attach, since each of them has two
* configrations. This driver uses the one whose endpoints are all bulk.
*
* For USA49WG, this driver uses the third configuration which has 6 endpoints,
* 3 bulk out eps, 1 bulk in ep, 1 intr in ep, 1 intr out ep. Bulk in ep is
* shared by 4 ports for receiving data.
*
* Some of Keyspan adapters have only one port, some have two or four ports.
* This driver supports up to four ports. Each port has its own states (traced
* by keyspan_port structure) and can be operated independently.
*
* port_state:
*
* KEYSPAN_PORT_NOT_INIT
* |
* |
* attach_ports
* |
* |
* |
* v
* KEYSPAN_PORT_CLOSED <-----close-------<---- +
* | |
* | |
* | |
* open_port |
* | |
* | |
* v |
* KEYSPAN_PORT_OPENING ---open_hw_port---> USBSER_PORT_OPEN
*
* (traced by keyspan_pipe structure). The pipe states is as following:
*
* pipe_state:
*
* KEYSPAN_PIPE_NOT_INIT
* | ^
* | |
* keyspan_init_pipes keyspan_fini_pipes
* | |
* v |
* KEYSPAN_PIPE_CLOSED ------------->-----------+
* ^ |
* | |
* | v
* +---------<------------------ KEYSPAN_PIPE_OPEN
*
* To control the device and get its status in a timely way, this driver makes
* use of two global bulk endpoints for cmd and status on the device. The pipes
*
* This driver can be easily extended to support more Keyspan adapter models.
* You need the following steps to reach the aim:
* 2. If the device need firmware downloaded, add the firmware code as a header
* file, and add code to keyspan_pre_attach() as what were done for USA49WLC.
* 3. Add several model specific functions, like keyspan_build_cmd_msg_*,
* keyspan_default_port_params_*, keyspan_save_port_params_*, etc. The functions
* for USA19HS and USA49WLC can be taken as examples.
* 4. Add model specific code to the "switch (id_product) {...}" sentences.
*/
/*
*
* keyspan driver glue code
*
*/
#define USBDRV_MINOR_VER 0
#include <sys/byteorder.h>
/* configuration entry points */
void **);
/* functions related with set config or firmware download */
static int keyspan_download_firmware(keyspan_pre_state_t *);
/*
* STREAMS structures
*/
0, /* module id */
"usbsksp", /* module name */
USBSER_MIN_PKTSZ, /* min pkt size */
USBSER_MAX_PKTSZ, /* max pkt size */
USBSER_HIWAT, /* hi watermark */
USBSER_LOWAT /* low watermark */
};
putq,
NULL,
};
NULL,
NULL,
NULL,
};
};
nodev, /* cb_open */
nodev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&usbser_keyspan_str_info, /* cb_stream */
};
/*
* auto configuration ops
*/
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
usbser_keyspan_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
usbser_keyspan_attach, /* devo_attach */
usbser_keyspan_detach, /* devo_detach */
nodev, /* devo_reset */
&usbser_keyspan_cb_ops, /* devo_cb_ops */
usbser_power, /* devo_power */
ddi_quiesce_not_needed, /* devo_quiesce */
};
extern struct mod_ops mod_driverops;
&mod_driverops, /* type of module - driver */
"USB keyspan usb2serial driver",
};
};
/* debug support */
/* firmware support for usa49wlc model */
extern usbser_keyspan_fw_record_t *keyspan_usa49wlc_fw(void);
/*
* configuration entry points
* --------------------------
*/
int
_init(void)
{
int error;
sizeof (keyspan_pre_state_t)), 1);
}
return (error);
}
int
_fini(void)
{
int error;
}
return (error);
}
int
{
}
/*ARGSUSED*/
int
void **result)
{
}
static int
{
int rval;
/*
* Once the device is plugged, we need set its cfg. And need download
* firmware for some of them.
*/
/*
* After the cfg is set, and the firmware is downloaded,
* do the real attach.
*/
if (rval == DDI_ECONTEXT) {
&keyspan_ds_ops));
} else {
return (rval);
}
}
static int
{
} else {
}
}
static int
{
}
/*
* Switch config or download firmware
*/
/*ARGSUSED*/
static int
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* attach driver to USBA */
}
goto fail;
}
/*
* If 19HS or 49WG, needn't download firmware, but need check the
* current cfg.
* If 49WLC, need check the current cfg before download fw. And after
* download, the product id will change to KEYSPAN_USA49WLC_PID.
*/
/* Go to keyspan_attach() by return DDI_ECONTEXT. */
rval = DDI_ECONTEXT;
}
goto fail;
/* Go to keyspan_attach() by return DDI_ECONTEXT. */
rval = DDI_ECONTEXT;
}
goto fail;
}
/*
* By checking KEYSPAN_FW_FLAG, we can check whether the firmware
* has been downloaded.
* If firmware is already there, then do normal attach.
*/
if (!keyspan_need_fw(dev_data)) {
/* Go to keyspan_attach() by return DDI_ECONTEXT. */
rval = DDI_ECONTEXT;
goto fail;
}
/* Go on to download firmware. */
}
if (kbp) {
"keyspan_pre_attach: completed.");
/* keyspan download firmware done. */
return (DDI_SUCCESS);
}
}
fail:
if (kbp) {
}
return (rval);
}
static int
{
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* Set cfg for the device which has more than one cfg */
static int
{
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/* Return TRUE if need download firmware to the device. */
static boolean_t
{
/* need to convert to Little-Endian */
/*
* According to Keyspan's interface spec, this flag indicates
* if need download fw.
*/
return (bcd_descr_change == KEYSPAN_FW_FLAG);
}
/* Set the device's register. */
static int
{
int rval;
/*
* (0x7f92) is the reg addr we want to set.
*/
return (rval);
}
/*
* Download firmware or set register to the device by default ctrl pipe
*/
static int
{
/* reuse previous mblk if possible */
return (USB_FAILURE);
}
/* This is a req defined by hardware vendor. */
/* KEYSPAN_RETRY */
if (++retry > 3) {
if (data) {
}
return (USB_FAILURE);
}
}
if (data) {
}
return (USB_SUCCESS);
}
/* Download firmware into device */
static int
{
/* If the firmware module exists, then download it to device. */
if (&keyspan_usa49wlc_fw) {
}
if (!record) {
"No firmware available for Keyspan usa49wlc"
" usb-to-serial adapter. Refer to usbsksp(7D)"
" for details.");
return (USB_FAILURE);
}
/* Set bit 1 before downloading firmware. */
"keyspan_pre_attach: Set register failed.");
return (USB_FAILURE);
}
/* Write until the last record of the firmware */
"keyspan_pre_attach: download firmware failed.");
return (USB_FAILURE);
}
record++;
}
/*
* Set bit 0, device will be enumerated again after a while,
* and then go to keyspan_attach()
*/
return (USB_FAILURE);
}
return (USB_SUCCESS);
}