#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER 0
#include <sys/usb/usba.h>
#include <sys/usb/clients/usbser/usbser.h>
#include <sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h>
#include <sys/byteorder.h>
#include <sys/strsun.h>
static int usbser_keyspan_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
void **);
static int usbser_keyspan_attach(dev_info_t *, ddi_attach_cmd_t);
static int usbser_keyspan_detach(dev_info_t *, ddi_detach_cmd_t);
static int usbser_keyspan_open(queue_t *, dev_t *, int, int, cred_t *);
static int keyspan_pre_attach(dev_info_t *, ddi_attach_cmd_t, void *);
static int keyspan_set_cfg(dev_info_t *, uint8_t);
static int keyspan_pre_detach(dev_info_t *, ddi_detach_cmd_t, void *);
static boolean_t keyspan_need_fw(usb_client_dev_data_t *);
static int keyspan_set_reg(keyspan_pipe_t *, uchar_t);
static int keyspan_write_memory(keyspan_pipe_t *, uint16_t, uchar_t *,
uint16_t, uint8_t);
static int keyspan_download_firmware(keyspan_pre_state_t *);
static void *usbser_keyspan_statep;
extern ds_ops_t keyspan_ds_ops;
struct module_info usbser_keyspan_modinfo = {
0,
"usbsksp",
USBSER_MIN_PKTSZ,
USBSER_MAX_PKTSZ,
USBSER_HIWAT,
USBSER_LOWAT
};
static struct qinit usbser_keyspan_rinit = {
putq,
usbser_rsrv,
usbser_keyspan_open,
usbser_close,
NULL,
&usbser_keyspan_modinfo,
NULL
};
static struct qinit usbser_keyspan_winit = {
usbser_wput,
usbser_wsrv,
NULL,
NULL,
NULL,
&usbser_keyspan_modinfo,
NULL
};
struct streamtab usbser_keyspan_str_info = {
&usbser_keyspan_rinit, &usbser_keyspan_winit, NULL, NULL
};
static struct cb_ops usbser_keyspan_cb_ops = {
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
&usbser_keyspan_str_info,
(int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG)
};
struct dev_ops usbser_keyspan_ops = {
DEVO_REV,
0,
usbser_keyspan_getinfo,
nulldev,
nulldev,
usbser_keyspan_attach,
usbser_keyspan_detach,
nodev,
&usbser_keyspan_cb_ops,
(struct bus_ops *)NULL,
usbser_power,
ddi_quiesce_not_needed,
};
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"USB keyspan usb2serial driver",
&usbser_keyspan_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, &modldrv, 0
};
static uint_t keyspan_pre_errlevel = USB_LOG_L4;
static uint_t keyspan_pre_errmask = DPRINT_MASK_ALL;
static uint_t keyspan_pre_instance_debug = (uint_t)-1;
extern usbser_keyspan_fw_record_t *keyspan_usa49wlc_fw(void);
#pragma weak keyspan_usa49wlc_fw
int
_init(void)
{
int error;
if ((error = mod_install(&modlinkage)) == 0) {
error = ddi_soft_state_init(&usbser_keyspan_statep,
max(usbser_soft_state_size(),
sizeof (keyspan_pre_state_t)), 1);
}
return (error);
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) == 0) {
ddi_soft_state_fini(&usbser_keyspan_statep);
}
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
usbser_keyspan_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result)
{
return (usbser_getinfo(dip, infocmd, arg, result,
usbser_keyspan_statep));
}
static int
usbser_keyspan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int rval;
rval = keyspan_pre_attach(dip, cmd, usbser_keyspan_statep);
if (rval == DDI_ECONTEXT) {
return (usbser_attach(dip, cmd, usbser_keyspan_statep,
&keyspan_ds_ops));
} else {
return (rval);
}
}
static int
usbser_keyspan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
if (ddi_get_driver_private(dip) == NULL) {
return (keyspan_pre_detach(dip, cmd, usbser_keyspan_statep));
} else {
return (usbser_detach(dip, cmd, usbser_keyspan_statep));
}
}
static int
usbser_keyspan_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
{
return (usbser_open(rq, dev, flag, sflag, cr, usbser_keyspan_statep));
}
static int
keyspan_pre_attach(dev_info_t *dip, ddi_attach_cmd_t cmd, void *statep)
{
int instance = ddi_get_instance(dip);
keyspan_pre_state_t *kbp = NULL;
usb_client_dev_data_t *dev_data = NULL;
int rval = DDI_FAILURE;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (usb_client_attach(dip, USBDRV_VERSION, 0) == USB_SUCCESS) {
(void) usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0);
}
if (dev_data == NULL) {
goto fail;
}
if (dev_data->dev_descr->idProduct == KEYSPAN_USA19HS_PID ||
dev_data->dev_descr->idProduct == KEYSPAN_USA49WLC_PID) {
if (keyspan_set_cfg(dip, 1) == USB_SUCCESS) {
rval = DDI_ECONTEXT;
}
goto fail;
} else if (dev_data->dev_descr->idProduct == KEYSPAN_USA49WG_PID) {
if (keyspan_set_cfg(dip, 2) == USB_SUCCESS) {
rval = DDI_ECONTEXT;
}
goto fail;
}
if (!keyspan_need_fw(dev_data)) {
rval = DDI_ECONTEXT;
goto fail;
}
if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) {
kbp = ddi_get_soft_state(statep, instance);
}
if (kbp) {
kbp->kb_dip = dip;
kbp->kb_instance = instance;
kbp->kb_dev_data = dev_data;
kbp->kb_def_pipe.pipe_handle = kbp->kb_dev_data->dev_default_ph;
kbp->kb_lh = usb_alloc_log_hdl(kbp->kb_dip, "keyspan[*].",
&keyspan_pre_errlevel, &keyspan_pre_errmask,
&keyspan_pre_instance_debug, 0);
kbp->kb_def_pipe.pipe_lh = kbp->kb_lh;
if (keyspan_download_firmware(kbp) == USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_ATTACH, kbp->kb_lh,
"keyspan_pre_attach: completed.");
return (DDI_SUCCESS);
}
}
fail:
if (kbp) {
usb_free_log_hdl(kbp->kb_lh);
ddi_soft_state_free(statep, instance);
}
usb_client_detach(dip, dev_data);
return (rval);
}
static int
keyspan_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep)
{
int instance = ddi_get_instance(dip);
keyspan_pre_state_t *kbp;
kbp = ddi_get_soft_state(statep, instance);
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
usb_free_log_hdl(kbp->kb_lh);
usb_client_detach(dip, kbp->kb_dev_data);
ddi_soft_state_free(statep, instance);
return (DDI_SUCCESS);
}
static int
keyspan_set_cfg(dev_info_t *dip, uint8_t cfg_num)
{
if (usb_set_cfg(dip, cfg_num, USB_FLAGS_SLEEP,
NULL, NULL) != USB_SUCCESS) {
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static boolean_t
keyspan_need_fw(usb_client_dev_data_t *dev_data)
{
uint16_t bcd_descr;
uint16_t bcd_descr_change;
bcd_descr = dev_data->dev_descr->bcdDevice;
bcd_descr_change = bcd_descr & KEYSPAN_FW_FLAG;
return (bcd_descr_change == KEYSPAN_FW_FLAG);
}
static int
keyspan_set_reg(keyspan_pipe_t *pipe, uchar_t bit)
{
int rval;
rval = keyspan_write_memory(pipe, 0x7f92, &bit, 1, KEYSPAN_REQ_SET);
return (rval);
}
static int
keyspan_write_memory(keyspan_pipe_t *pipe, uint16_t addr, uchar_t *buf,
uint16_t len, uint8_t bRequest)
{
mblk_t *data;
usb_ctrl_setup_t setup;
usb_cb_flags_t cb_flags;
usb_cr_t cr;
uint8_t retry = 0;
if ((data = allocb(len, BPRI_HI)) == NULL) {
return (USB_FAILURE);
}
bcopy(buf, data->b_rptr, len);
setup.bmRequestType = USB_DEV_REQ_TYPE_VENDOR;
setup.bRequest = bRequest;
setup.wValue = addr;
setup.wIndex = 0;
setup.wLength = len;
setup.attrs = 0;
while (usb_pipe_ctrl_xfer_wait(pipe->pipe_handle, &setup, &data,
&cr, &cb_flags, 0) != USB_SUCCESS) {
if (++retry > 3) {
if (data) {
freemsg(data);
}
return (USB_FAILURE);
}
}
if (data) {
freemsg(data);
}
return (USB_SUCCESS);
}
static int
keyspan_download_firmware(keyspan_pre_state_t *kbp)
{
usbser_keyspan_fw_record_t *record = NULL;
if (&keyspan_usa49wlc_fw) {
record = keyspan_usa49wlc_fw();
}
if (!record) {
USB_DPRINTF_L1(DPRINT_ATTACH, kbp->kb_lh,
"No firmware available for Keyspan usa49wlc"
" usb-to-serial adapter. Refer to usbsksp(4D)"
" for details.");
return (USB_FAILURE);
}
if (keyspan_set_reg(&kbp->kb_def_pipe, 1) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh,
"keyspan_pre_attach: Set register failed.");
return (USB_FAILURE);
}
while (record->address != 0xffff) {
if (keyspan_write_memory(&kbp->kb_def_pipe,
record->address, (uchar_t *)record->data,
record->data_len, KEYSPAN_REQ_SET) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_ATTACH, kbp->kb_lh,
"keyspan_pre_attach: download firmware failed.");
return (USB_FAILURE);
}
record++;
}
if (keyspan_set_reg(&kbp->kb_def_pipe, 0) != USB_SUCCESS) {
return (USB_FAILURE);
}
return (USB_SUCCESS);
}