#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER 0
#include <sys/usb/usba.h>
#include <sys/usb/usba/genconsole.h>
#include <sys/usb/clients/hid/hid.h>
#include <sys/usb/clients/hid/hid_polled.h>
#include <sys/usb/clients/hidparser/hidparser.h>
#include <sys/usb/clients/hid/hidvar.h>
#include <sys/usb/clients/hid/hidminor.h>
#include <sys/usb/clients/hidparser/hid_parser_driver.h>
#include <sys/stropts.h>
#include <sys/sunddi.h>
#include <sys/stream.h>
#include <sys/strsun.h>
extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
uint_t hid_errmask = (uint_t)PRINT_MASK_ALL;
uint_t hid_errlevel = USB_LOG_L4;
uint_t hid_instance_debug = (uint_t)-1;
int hid_default_pipe_drain_timeout = HID_DEFAULT_PIPE_DRAIN_TIMEOUT;
int hid_pm_mouse = 1;
#define HID_INITIAL_SOFT_SPACE 4
static void *hid_statep;
static void hid_interrupt_pipe_callback(usb_pipe_handle_t,
usb_intr_req_t *);
static void hid_default_pipe_callback(usb_pipe_handle_t, usb_ctrl_req_t *);
static void hid_interrupt_pipe_exception_callback(usb_pipe_handle_t,
usb_intr_req_t *);
static void hid_default_pipe_exception_callback(usb_pipe_handle_t,
usb_ctrl_req_t *);
static int hid_restore_state_event_callback(dev_info_t *);
static int hid_disconnect_event_callback(dev_info_t *);
static int hid_cpr_suspend(hid_state_t *hidp);
static void hid_cpr_resume(hid_state_t *hidp);
static void hid_power_change_callback(void *arg, int rval);
static size_t hid_parse_hid_descr(usb_hid_descr_t *, size_t,
usb_alt_if_data_t *, usb_ep_data_t *);
static int hid_parse_hid_descr_failure(hid_state_t *);
static int hid_handle_report_descriptor(hid_state_t *, int);
static void hid_set_idle(hid_state_t *);
static void hid_set_protocol(hid_state_t *, int);
static void hid_detach_cleanup(dev_info_t *, hid_state_t *);
static int hid_start_intr_polling(hid_state_t *);
static void hid_close_intr_pipe(hid_state_t *);
static int hid_mctl_execute_cmd(queue_t *, int, hid_req_t *,
mblk_t *);
static int hid_mctl_receive(queue_t *, mblk_t *);
static int hid_send_async_ctrl_request(hid_default_pipe_arg_t *, hid_req_t *,
uchar_t, int, ushort_t);
static void hid_create_pm_components(dev_info_t *, hid_state_t *);
static int hid_is_pm_enabled(dev_info_t *);
static void hid_restore_device_state(dev_info_t *, hid_state_t *);
static void hid_save_device_state(hid_state_t *);
static void hid_qreply_merror(queue_t *, mblk_t *, uchar_t);
static mblk_t *hid_data2mblk(uchar_t *, int);
static void hid_flush(queue_t *);
static int hid_pwrlvl0(hid_state_t *);
static int hid_pwrlvl1(hid_state_t *);
static int hid_pwrlvl2(hid_state_t *);
static int hid_pwrlvl3(hid_state_t *);
static void hid_pm_busy_component(hid_state_t *);
static void hid_pm_idle_component(hid_state_t *);
static int hid_polled_read(hid_polled_handle_t, uchar_t **);
static int hid_polled_input_enter(hid_polled_handle_t);
static int hid_polled_input_exit(hid_polled_handle_t);
static int hid_polled_input_init(hid_state_t *);
static int hid_polled_input_fini(hid_state_t *);
static int hid_open(queue_t *, dev_t *, int, int, cred_t *);
static int hid_close(queue_t *, int, cred_t *);
static int hid_wput(queue_t *, mblk_t *);
static int hid_wsrv(queue_t *);
static int hid_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int hid_attach(dev_info_t *, ddi_attach_cmd_t);
static int hid_detach(dev_info_t *, ddi_detach_cmd_t);
static int hid_power(dev_info_t *, int, int);
static int hid_chropen(dev_t *, int, int, cred_t *);
static int hid_chrclose(dev_t, int, int, cred_t *);
static int hid_read(dev_t, struct uio *, cred_t *);
static int hid_write(dev_t, struct uio *, cred_t *);
static int hid_poll(dev_t, short, int, short *, struct pollhead **);
_NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", datab))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", msgb))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", queue))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_ctrl_req))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_intr_req))
static struct module_info hid_mod_info = {
0x0ffff,
"hid",
0,
INFPSZ,
512,
128
};
static struct qinit rinit = {
NULL,
NULL,
hid_open,
hid_close,
NULL,
&hid_mod_info,
NULL
};
static struct qinit winit = {
hid_wput,
hid_wsrv,
NULL,
NULL,
NULL,
&hid_mod_info,
NULL
};
struct streamtab hid_streamtab = {
&rinit,
&winit,
NULL,
NULL
};
struct cb_ops hid_cb_ops = {
hid_chropen,
hid_chrclose,
nulldev,
nulldev,
nulldev,
hid_read,
hid_write,
nulldev,
nulldev,
nulldev,
nulldev,
hid_poll,
ddi_prop_op,
&hid_streamtab,
D_MP | D_MTPERQ
};
static struct dev_ops hid_ops = {
DEVO_REV,
0,
hid_info,
nulldev,
nulldev,
hid_attach,
hid_detach,
nodev,
&hid_cb_ops,
NULL,
hid_power,
ddi_quiesce_not_needed,
};
static struct modldrv hidmodldrv = {
&mod_driverops,
"USB HID Client Driver",
&hid_ops
};
static struct modlinkage modlinkage = {
MODREV_1,
&hidmodldrv,
NULL,
};
static usb_event_t hid_events = {
hid_disconnect_event_callback,
hid_restore_state_event_callback,
NULL,
NULL,
};
int
_init(void)
{
int rval;
if (((rval = ddi_soft_state_init(&hid_statep, sizeof (hid_state_t),
HID_INITIAL_SOFT_SPACE)) != 0)) {
return (rval);
}
if ((rval = mod_install(&modlinkage)) != 0) {
ddi_soft_state_fini(&hid_statep);
}
return (rval);
}
int
_fini(void)
{
int rval;
if ((rval = mod_remove(&modlinkage)) != 0) {
return (rval);
}
ddi_soft_state_fini(&hid_statep);
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
hid_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
hid_state_t *hidp = NULL;
int error = DDI_FAILURE;
minor_t minor = getminor((dev_t)arg);
int instance = HID_MINOR_TO_INSTANCE(minor);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if ((hidp = ddi_get_soft_state(hid_statep, instance)) != NULL) {
*result = hidp->hid_dip;
if (*result != NULL) {
error = DDI_SUCCESS;
}
} else
*result = NULL;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(uintptr_t)instance;
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
static int
hid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
int parse_hid_descr_error = 0;
hid_state_t *hidp = NULL;
uint32_t usage_page;
uint32_t usage;
usb_client_dev_data_t *dev_data;
usb_alt_if_data_t *altif_data;
char minor_name[HID_MINOR_NAME_LEN];
usb_ep_data_t *ep_data;
usb_ugen_info_t usb_ugen_info;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
hidp = ddi_get_soft_state(hid_statep, instance);
hid_cpr_resume(hidp);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_soft_state_zalloc(hid_statep, instance) == DDI_SUCCESS) {
hidp = ddi_get_soft_state(hid_statep, instance);
}
if (hidp == NULL) {
goto fail;
}
hidp->hid_log_handle = usb_alloc_log_hdl(dip, NULL, &hid_errlevel,
&hid_errmask, &hid_instance_debug, 0);
hidp->hid_instance = instance;
hidp->hid_dip = dip;
if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_attach: client attach failed");
goto fail;
}
if (usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_attach: usb_get_dev_data() failed");
goto fail;
}
mutex_init(&hidp->hid_mutex, NULL, MUTEX_DRIVER,
dev_data->dev_iblock_cookie);
hidp->hid_attach_flags |= HID_LOCK_INIT;
altif_data = &dev_data->dev_curr_cfg->
cfg_if[dev_data->dev_curr_if].if_alt[0];
mutex_enter(&hidp->hid_mutex);
hidp->hid_dev_data = dev_data;
hidp->hid_dev_descr = dev_data->dev_descr;
hidp->hid_interfaceno = dev_data->dev_curr_if;
hidp->hid_if_descr = altif_data->altif_descr;
if (hidp->hid_if_descr.bInterfaceSubClass != BOOT_INTERFACE)
hidp->hid_if_descr.bInterfaceProtocol = NONE_PROTOCOL;
mutex_exit(&hidp->hid_mutex);
if ((ep_data = usb_lookup_ep_data(dip, dev_data,
hidp->hid_interfaceno, 0, 0,
(uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"no interrupt IN endpoint found");
goto fail;
}
mutex_enter(&hidp->hid_mutex);
if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION, dip, ep_data,
&hidp->hid_ep_intr_xdescr) != USB_SUCCESS) {
mutex_exit(&hidp->hid_mutex);
goto fail;
}
if (hid_parse_hid_descr(&hidp->hid_hid_descr, USB_HID_DESCR_SIZE,
altif_data, ep_data) != USB_HID_DESCR_SIZE) {
if (hid_parse_hid_descr_failure(hidp) == USB_FAILURE) {
mutex_exit(&hidp->hid_mutex);
goto fail;
}
parse_hid_descr_error = HID_BAD_DESCR;
} else {
USB_DPRINTF_L3(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Hid descriptor:\n\t"
"bLength = 0x%x bDescriptorType = 0x%x "
"bcdHID = 0x%x\n\t"
"bCountryCode = 0x%x bNumDescriptors = 0x%x\n\t"
"bReportDescriptorType = 0x%x\n\t"
"wReportDescriptorLength = 0x%x",
hidp->hid_hid_descr.bLength,
hidp->hid_hid_descr.bDescriptorType,
hidp->hid_hid_descr.bcdHID,
hidp->hid_hid_descr.bCountryCode,
hidp->hid_hid_descr.bNumDescriptors,
hidp->hid_hid_descr.bReportDescriptorType,
hidp->hid_hid_descr.wReportDescriptorLength);
}
hidp->hid_default_pipe = hidp->hid_dev_data->dev_default_ph;
usb_free_dev_data(dip, dev_data);
hidp->hid_dev_data = NULL;
if (usb_owns_device(dip)) {
bzero(&usb_ugen_info, sizeof (usb_ugen_info));
usb_ugen_info.usb_ugen_flags = 0;
usb_ugen_info.usb_ugen_minor_node_ugen_bits_mask =
(dev_t)HID_MINOR_UGEN_BITS_MASK;
usb_ugen_info.usb_ugen_minor_node_instance_mask =
(dev_t)HID_MINOR_INSTANCE_MASK;
hidp->hid_ugen_hdl = usb_ugen_get_hdl(dip, &usb_ugen_info);
if (usb_ugen_attach(hidp->hid_ugen_hdl, cmd) !=
USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA,
hidp->hid_log_handle,
"usb_ugen_attach failed");
usb_ugen_release_hdl(hidp->hid_ugen_hdl);
hidp->hid_ugen_hdl = NULL;
}
}
if (parse_hid_descr_error != HID_BAD_DESCR) {
int retry;
mutex_exit(&hidp->hid_mutex);
for (retry = 1; retry < HID_RETRY; retry++) {
if (hid_handle_report_descriptor(hidp,
hidp->hid_interfaceno) == USB_SUCCESS) {
break;
}
delay(retry * drv_usectohz(1000));
}
if (retry >= HID_RETRY) {
goto fail;
}
mutex_enter(&hidp->hid_mutex);
if (hidp->hid_packet_size == 0) {
if (hidp->hid_if_descr.bInterfaceProtocol ==
KEYBOARD_PROTOCOL) {
hidp->hid_packet_size = USBKPSZ;
} else if (hidp->
hid_if_descr.bInterfaceProtocol ==
MOUSE_PROTOCOL) {
hidp->hid_packet_size = USBMSSZ;
} else {
USB_DPRINTF_L2(PRINT_MASK_ATTA,
hidp->hid_log_handle,
"Failed to find hid packet size");
mutex_exit(&hidp->hid_mutex);
goto fail;
}
}
}
hidp->hid_intr_pipe_policy.pp_max_async_reqs = 1;
mutex_exit(&hidp->hid_mutex);
hid_set_idle(hidp);
hid_set_protocol(hidp, SET_REPORT_PROTOCOL);
mutex_enter(&hidp->hid_mutex);
switch (hidp->hid_if_descr.bInterfaceProtocol) {
case KEYBOARD_PROTOCOL:
(void) strcpy(minor_name, "keyboard");
break;
case MOUSE_PROTOCOL:
(void) strcpy(minor_name, "mouse");
break;
default:
if (hidparser_lookup_usage_collection(
hidp->hid_report_descr, HID_GENERIC_DESKTOP,
HID_GD_MOUSE) != HIDPARSER_FAILURE) {
(void) strcpy(minor_name, "mouse");
break;
}
if (hidparser_get_top_level_collection_usage(
hidp->hid_report_descr, &usage_page, &usage) !=
HIDPARSER_FAILURE) {
switch (usage_page) {
case HID_CONSUMER:
switch (usage) {
case HID_CONSUMER_CONTROL:
(void) strcpy(minor_name,
"consumer_control");
break;
default:
(void) sprintf(minor_name,
"hid_%d_%d", usage_page, usage);
break;
}
break;
case HID_GENERIC_DESKTOP:
switch (usage) {
case HID_GD_POINTER:
(void) strcpy(minor_name,
"pointer");
break;
case HID_GD_MOUSE:
(void) strcpy(minor_name,
"mouse");
break;
case HID_GD_KEYBOARD:
(void) strcpy(minor_name,
"keyboard");
break;
default:
(void) sprintf(minor_name,
"hid_%d_%d", usage_page, usage);
break;
}
break;
default:
(void) sprintf(minor_name,
"hid_%d_%d", usage_page, usage);
break;
}
} else {
USB_DPRINTF_L1(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_attach: Unsupported HID device");
mutex_exit(&hidp->hid_mutex);
goto fail;
}
break;
}
mutex_exit(&hidp->hid_mutex);
if ((ddi_create_minor_node(dip, minor_name, S_IFCHR,
HID_CONSTRUCT_EXTERNAL_MINOR(instance),
DDI_PSEUDO, 0)) != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_attach: Could not create minor node");
goto fail;
}
if (strcmp(minor_name, "mouse") == 0) {
if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
HID_CONSTRUCT_INTERNAL_MINOR(instance)) != DDI_SUCCESS) {
goto fail;
}
}
if (strcmp(minor_name, "keyboard") == 0) {
if (ddi_create_internal_pathname(dip, "internal_keyboard",
S_IFCHR, HID_CONSTRUCT_INTERNAL_MINOR(instance)) !=
DDI_SUCCESS) {
goto fail;
}
}
mutex_enter(&hidp->hid_mutex);
hidp->hid_attach_flags |= HID_MINOR_NODES;
hidp->hid_dev_state = USB_DEV_ONLINE;
mutex_exit(&hidp->hid_mutex);
if (usb_register_event_cbs(dip, &hid_events, 0) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"usb_register_event_cbs failed");
goto fail;
}
hid_create_pm_components(dip, hidp);
hid_pm_busy_component(hidp);
(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
hid_pm_idle_component(hidp);
hidp->hid_internal_rq = hidp->hid_external_rq = NULL;
hidp->hid_internal_flag = hidp->hid_external_flag = 0;
hidp->hid_inuse_rq = NULL;
ddi_report_dev(dip);
USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_attach: End");
return (DDI_SUCCESS);
fail:
if (hidp) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_attach: fail");
hid_detach_cleanup(dip, hidp);
}
return (DDI_FAILURE);
}
static int
hid_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
hid_state_t *hidp;
int rval = DDI_FAILURE;
hidp = ddi_get_soft_state(hid_statep, instance);
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle, "hid_detach");
switch (cmd) {
case DDI_DETACH:
hid_detach_cleanup(dip, hidp);
return (DDI_SUCCESS);
case DDI_SUSPEND:
rval = hid_cpr_suspend(hidp);
return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
default:
break;
}
return (rval);
}
static int
hid_chropen(dev_t *devp, int flag, int sflag, cred_t *credp)
{
int rval;
minor_t minor = getminor(*devp);
int instance;
hid_state_t *hidp;
instance = HID_MINOR_TO_INSTANCE(minor);
hidp = ddi_get_soft_state(hid_statep, instance);
if (hidp == NULL) {
return (ENXIO);
}
if (!HID_IS_UGEN_OPEN(minor)) {
return (ENXIO);
}
hid_pm_busy_component(hidp);
(void) pm_raise_power(hidp->hid_dip, 0, USB_DEV_OS_FULL_PWR);
mutex_enter(&hidp->hid_mutex);
rval = usb_ugen_open(hidp->hid_ugen_hdl, devp, flag,
sflag, credp);
mutex_exit(&hidp->hid_mutex);
if (rval != 0) {
hid_pm_idle_component(hidp);
}
return (rval);
}
static int
hid_chrclose(dev_t dev, int flag, int otyp, cred_t *credp)
{
int rval;
minor_t minor = getminor(dev);
int instance;
hid_state_t *hidp;
instance = HID_MINOR_TO_INSTANCE(minor);
hidp = ddi_get_soft_state(hid_statep, instance);
if (hidp == NULL) {
return (ENXIO);
}
if (!HID_IS_UGEN_OPEN(minor)) {
return (ENXIO);
}
mutex_enter(&hidp->hid_mutex);
rval = usb_ugen_close(hidp->hid_ugen_hdl, dev, flag,
otyp, credp);
mutex_exit(&hidp->hid_mutex);
if (rval == 0) {
hid_pm_idle_component(hidp);
}
return (rval);
}
static int
hid_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
int rval;
minor_t minor = getminor(dev);
int instance;
hid_state_t *hidp;
instance = HID_MINOR_TO_INSTANCE(minor);
hidp = ddi_get_soft_state(hid_statep, instance);
if (hidp == NULL) {
return (ENXIO);
}
if (!HID_IS_UGEN_OPEN(minor)) {
return (ENXIO);
}
rval = usb_ugen_read(hidp->hid_ugen_hdl, dev, uiop, credp);
return (rval);
}
static int
hid_write(dev_t dev, struct uio *uiop, cred_t *credp)
{
int rval;
minor_t minor = getminor(dev);
int instance;
hid_state_t *hidp;
instance = HID_MINOR_TO_INSTANCE(minor);
hidp = ddi_get_soft_state(hid_statep, instance);
if (hidp == NULL) {
return (ENXIO);
}
if (!HID_IS_UGEN_OPEN(minor)) {
return (ENXIO);
}
rval = usb_ugen_write(hidp->hid_ugen_hdl, dev, uiop, credp);
return (rval);
}
static int
hid_poll(dev_t dev, short events, int anyyet, short *reventsp,
struct pollhead **phpp)
{
int rval;
minor_t minor = getminor(dev);
int instance;
hid_state_t *hidp;
instance = HID_MINOR_TO_INSTANCE(minor);
hidp = ddi_get_soft_state(hid_statep, instance);
if (hidp == NULL) {
return (ENXIO);
}
if (!HID_IS_UGEN_OPEN(minor)) {
return (ENXIO);
}
rval = usb_ugen_poll(hidp->hid_ugen_hdl, dev, events, anyyet,
reventsp, phpp);
return (rval);
}
static int
hid_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
int no_of_ep = 0;
int rval;
int instance;
hid_state_t *hidp;
minor_t minor = getminor(*devp);
instance = HID_MINOR_TO_INSTANCE(minor);
hidp = ddi_get_soft_state(hid_statep, instance);
if (hidp == NULL) {
return (ENXIO);
}
USB_DPRINTF_L4(PRINT_MASK_OPEN, hidp->hid_log_handle,
"hid_open: Begin");
if (HID_IS_UGEN_OPEN(minor)) {
return (ENOSTR);
}
if (sflag) {
return (ENXIO);
}
if (!(flag & FREAD)) {
return (EIO);
}
mutex_enter(&hidp->hid_mutex);
if (HID_IS_INTERNAL_OPEN(minor) &&
(hidp->hid_dev_state == USB_DEV_DISCONNECTED)) {
mutex_exit(&hidp->hid_mutex);
return (ENODEV);
}
if (HID_IS_INTERNAL_OPEN(minor) &&
(hidp->hid_internal_rq != NULL)) {
ASSERT(hidp->hid_internal_rq == q);
mutex_exit(&hidp->hid_mutex);
return (0);
}
if ((!HID_IS_INTERNAL_OPEN(minor)) &&
(hidp->hid_external_rq != NULL)) {
ASSERT(hidp->hid_external_rq == q);
mutex_exit(&hidp->hid_mutex);
return (0);
}
mutex_exit(&hidp->hid_mutex);
q->q_ptr = hidp;
WR(q)->q_ptr = hidp;
mutex_enter(&hidp->hid_mutex);
if (hidp->hid_inuse_rq != NULL) {
if (HID_IS_INTERNAL_OPEN(minor)) {
hidp->hid_internal_flag = HID_STREAMS_OPEN;
hidp->hid_inuse_rq = hidp->hid_internal_rq = q;
} else {
hidp->hid_external_flag = HID_STREAMS_OPEN;
hidp->hid_inuse_rq = hidp->hid_external_rq = q;
}
mutex_exit(&hidp->hid_mutex);
qprocson(q);
return (0);
}
hidp->hid_interrupt_pipe = NULL;
no_of_ep = hidp->hid_if_descr.bNumEndpoints;
mutex_exit(&hidp->hid_mutex);
if (no_of_ep > 0) {
if (usb_pipe_xopen(hidp->hid_dip,
&hidp->hid_ep_intr_xdescr,
&hidp->hid_intr_pipe_policy, USB_FLAGS_SLEEP,
&hidp->hid_interrupt_pipe) !=
USB_SUCCESS) {
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
return (EIO);
}
}
hid_pm_busy_component(hidp);
(void) pm_raise_power(hidp->hid_dip, 0, USB_DEV_OS_FULL_PWR);
mutex_enter(&hidp->hid_mutex);
if (HID_IS_INTERNAL_OPEN(minor)) {
hidp->hid_internal_flag = HID_STREAMS_OPEN;
hidp->hid_inuse_rq = hidp->hid_internal_rq = q;
} else {
hidp->hid_external_flag = HID_STREAMS_OPEN;
hidp->hid_inuse_rq = hidp->hid_external_rq = q;
}
mutex_exit(&hidp->hid_mutex);
qprocson(q);
mutex_enter(&hidp->hid_mutex);
if ((rval = hid_start_intr_polling(hidp)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_OPEN, hidp->hid_log_handle,
"unable to start intr pipe polling. rval = %d", rval);
if (HID_IS_INTERNAL_OPEN(minor))
hidp->hid_internal_flag = HID_STREAMS_DISMANTLING;
else
hidp->hid_external_flag = HID_STREAMS_DISMANTLING;
mutex_exit(&hidp->hid_mutex);
usb_pipe_close(hidp->hid_dip, hidp->hid_interrupt_pipe,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&hidp->hid_mutex);
hidp->hid_interrupt_pipe = NULL;
mutex_exit(&hidp->hid_mutex);
qprocsoff(q);
mutex_enter(&hidp->hid_mutex);
if (HID_IS_INTERNAL_OPEN(minor)) {
hidp->hid_internal_flag = 0;
hidp->hid_internal_rq = NULL;
if (hidp->hid_external_flag == HID_STREAMS_OPEN)
hidp->hid_inuse_rq = hidp->hid_external_rq;
else
hidp->hid_inuse_rq = NULL;
} else {
hidp->hid_external_flag = 0;
hidp->hid_external_rq = NULL;
if (hidp->hid_internal_flag == HID_STREAMS_OPEN)
hidp->hid_inuse_rq = hidp->hid_internal_rq;
else
hidp->hid_inuse_rq = NULL;
}
mutex_exit(&hidp->hid_mutex);
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
hid_pm_idle_component(hidp);
return (EIO);
}
mutex_exit(&hidp->hid_mutex);
USB_DPRINTF_L4(PRINT_MASK_OPEN, hidp->hid_log_handle, "hid_open: End");
switch (hidp->hid_pm->hid_pm_strategy) {
case HID_PM_ACTIVITY:
hid_pm_idle_component(hidp);
break;
default:
break;
}
return (0);
}
static int
hid_close(queue_t *q, int flag, cred_t *credp)
{
hid_state_t *hidp = (hid_state_t *)q->q_ptr;
queue_t *wq;
mblk_t *mp;
USB_DPRINTF_L4(PRINT_MASK_CLOSE, hidp->hid_log_handle, "hid_close:");
mutex_enter(&hidp->hid_mutex);
ASSERT((hidp->hid_internal_rq == q) ||
(hidp->hid_external_rq == q));
if (hidp->hid_internal_rq == q)
hidp->hid_internal_flag = HID_STREAMS_DISMANTLING;
else
hidp->hid_external_flag = HID_STREAMS_DISMANTLING;
mutex_exit(&hidp->hid_mutex);
(void) usb_pipe_drain_reqs(hidp->hid_dip,
hidp->hid_default_pipe, 0, USB_FLAGS_SLEEP, NULL, 0);
mutex_enter(&hidp->hid_mutex);
wq = WR(q);
while (mp = getq(wq)) {
hid_qreply_merror(wq, mp, EIO);
mutex_exit(&hidp->hid_mutex);
hid_pm_idle_component(hidp);
mutex_enter(&hidp->hid_mutex);
}
mutex_exit(&hidp->hid_mutex);
qprocsoff(q);
q->q_ptr = NULL;
wq->q_ptr = NULL;
mutex_enter(&hidp->hid_mutex);
if (hidp->hid_internal_rq == q) {
hidp->hid_internal_rq = NULL;
hidp->hid_internal_flag = 0;
if (hidp->hid_inuse_rq == q) {
if (hidp->hid_external_flag == HID_STREAMS_OPEN)
hidp->hid_inuse_rq = hidp->hid_external_rq;
else
hidp->hid_inuse_rq = NULL;
}
} else {
hidp->hid_external_rq = NULL;
hidp->hid_external_flag = 0;
if (hidp->hid_inuse_rq == q) {
if (hidp->hid_internal_flag == HID_STREAMS_OPEN)
hidp->hid_inuse_rq = hidp->hid_internal_rq;
else
hidp->hid_inuse_rq = NULL;
}
}
if (hidp->hid_inuse_rq != NULL) {
mutex_exit(&hidp->hid_mutex);
return (0);
}
hid_close_intr_pipe(hidp);
mutex_exit(&hidp->hid_mutex);
switch (hidp->hid_pm->hid_pm_strategy) {
case HID_PM_ACTIVITY:
break;
default:
hid_pm_idle_component(hidp);
break;
}
USB_DPRINTF_L4(PRINT_MASK_CLOSE, hidp->hid_log_handle,
"hid_close: End");
return (0);
}
static int
hid_wput(queue_t *q, mblk_t *mp)
{
hid_state_t *hidp = (hid_state_t *)q->q_ptr;
int error = USB_SUCCESS;
struct iocblk *iocbp;
mblk_t *datap;
int direction;
struct copyresp *crp;
queue_t *tmpq;
int flag;
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_wput: Begin");
ASSERT(mp != NULL);
ASSERT(mp->b_datap != NULL);
switch (mp->b_datap->db_type) {
case M_FLUSH:
if (*mp->b_rptr & FLUSHW) {
flushq(q, FLUSHDATA);
}
if (*mp->b_rptr & FLUSHR) {
*mp->b_rptr &= ~FLUSHW;
qreply(q, mp);
} else {
freemsg(mp);
}
break;
case M_IOCTL:
iocbp = (struct iocblk *)mp->b_rptr;
if (iocbp->ioc_count != TRANSPARENT) {
miocnak(q, mp, 0, EINVAL);
break;
}
switch (iocbp->ioc_cmd) {
case HIDIOCKMGDIRECT:
mutex_enter(&hidp->hid_mutex);
ASSERT(hidp->hid_inuse_rq != NULL);
mutex_exit(&hidp->hid_mutex);
if ((datap = allocb(sizeof (int), BPRI_MED)) == NULL) {
miocnak(q, mp, 0, ENOMEM);
break;
}
mutex_enter(&hidp->hid_mutex);
if (hidp->hid_inuse_rq == hidp->hid_internal_rq) {
*(int *)datap->b_wptr = 0;
datap->b_wptr += sizeof (int);
} else {
ASSERT(hidp->hid_inuse_rq ==
hidp->hid_external_rq);
*(int *)datap->b_wptr = 1;
datap->b_wptr += sizeof (int);
}
mutex_exit(&hidp->hid_mutex);
mcopyout(mp, NULL, sizeof (int), NULL, datap);
qreply(q, mp);
break;
case HIDIOCKMSDIRECT:
mcopyin(mp, NULL, sizeof (int), NULL);
qreply(q, mp);
break;
default:
miocnak(q, mp, 0, ENOTTY);
}
break;
case M_IOCDATA:
crp = (void *)mp->b_rptr;
if (crp->cp_rval != 0) {
miocnak(q, mp, 0, EIO);
break;
}
switch (crp->cp_cmd) {
case HIDIOCKMGDIRECT:
miocack(q, mp, 0, 0);
break;
case HIDIOCKMSDIRECT:
direction = *(int *)mp->b_cont->b_rptr;
if ((direction != 0) && (direction != 1)) {
miocnak(q, mp, 0, EINVAL);
break;
}
mutex_enter(&hidp->hid_mutex);
if (direction == 0) {
flag = hidp->hid_internal_flag;
tmpq = hidp->hid_internal_rq;
} else {
flag = hidp->hid_external_flag;
tmpq = hidp->hid_external_rq;
}
if (flag != HID_STREAMS_OPEN) {
mutex_exit(&hidp->hid_mutex);
miocnak(q, mp, 0, EIO);
break;
}
hidp->hid_inuse_rq = tmpq;
mutex_exit(&hidp->hid_mutex);
miocack(q, mp, 0, 0);
break;
default:
miocnak(q, mp, 0, ENOTTY);
break;
}
break;
case M_CTL:
hid_pm_busy_component(hidp);
if (q->q_first) {
(void) putq(q, mp);
} else {
error = hid_mctl_receive(q, mp);
switch (error) {
case HID_ENQUEUE:
(void) putq(q, mp);
break;
case HID_INPROGRESS:
break;
case HID_SUCCESS:
case HID_FAILURE:
default:
hid_pm_idle_component(hidp);
break;
}
}
break;
default:
hid_qreply_merror(q, mp, EINVAL);
error = USB_FAILURE;
break;
}
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_wput: End");
return (DDI_SUCCESS);
}
static int
hid_wsrv(queue_t *q)
{
hid_state_t *hidp = (hid_state_t *)q->q_ptr;
int error;
mblk_t *mp;
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_wsrv: Begin");
mutex_enter(&hidp->hid_mutex);
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_wsrv: dev_state: %s",
usb_str_dev_state(hidp->hid_dev_state));
if (hidp->hid_dev_state == USB_DEV_PWRED_DOWN) {
mutex_exit(&hidp->hid_mutex);
(void) pm_raise_power(hidp->hid_dip, 0, USB_DEV_OS_FULL_PWR);
mutex_enter(&hidp->hid_mutex);
}
while ((hidp->hid_dev_state == USB_DEV_ONLINE) &&
(HID_STREAMS_FLAG(q, hidp) != HID_STREAMS_DISMANTLING) &&
((mp = getq(q)) != NULL)) {
mutex_exit(&hidp->hid_mutex);
error = hid_mctl_receive(q, mp);
switch (error) {
case HID_ENQUEUE:
(void) putbq(q, mp);
break;
case HID_INPROGRESS:
break;
case HID_SUCCESS:
case HID_FAILURE:
default:
hid_pm_idle_component(hidp);
break;
}
mutex_enter(&hidp->hid_mutex);
}
mutex_exit(&hidp->hid_mutex);
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_wsrv: End");
return (DDI_SUCCESS);
}
static int
hid_power(dev_info_t *dip, int comp, int level)
{
int instance = ddi_get_instance(dip);
hid_state_t *hidp;
hid_power_t *hidpm;
int retval;
hidp = ddi_get_soft_state(hid_statep, instance);
USB_DPRINTF_L3(PRINT_MASK_PM, hidp->hid_log_handle, "hid_power:"
" hid_state: comp=%d level=%d", comp, level);
mutex_enter(&hidp->hid_mutex);
hidpm = hidp->hid_pm;
if (USB_DEV_PWRSTATE_OK(hidpm->hid_pwr_states, level)) {
USB_DPRINTF_L2(PRINT_MASK_PM, hidp->hid_log_handle,
"hid_power: illegal level=%d hid_pwr_states=%d",
level, hidpm->hid_pwr_states);
mutex_exit(&hidp->hid_mutex);
return (DDI_FAILURE);
}
switch (level) {
case USB_DEV_OS_PWR_OFF:
retval = hid_pwrlvl0(hidp);
break;
case USB_DEV_OS_PWR_1:
retval = hid_pwrlvl1(hidp);
break;
case USB_DEV_OS_PWR_2:
retval = hid_pwrlvl2(hidp);
break;
case USB_DEV_OS_FULL_PWR:
retval = hid_pwrlvl3(hidp);
break;
default:
retval = USB_FAILURE;
break;
}
mutex_exit(&hidp->hid_mutex);
return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
}
static void
hid_interrupt_pipe_callback(usb_pipe_handle_t pipe, usb_intr_req_t *req)
{
hid_state_t *hidp = (hid_state_t *)req->intr_client_private;
queue_t *q;
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_interrupt_pipe_callback: ph = 0x%p req = 0x%p",
(void *)pipe, (void *)req);
hid_pm_busy_component(hidp);
mutex_enter(&hidp->hid_mutex);
if (HID_STREAMS_FLAG(hidp->hid_inuse_rq, hidp) ==
HID_STREAMS_OPEN) {
if (!canputnext(hidp->hid_inuse_rq)) {
USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
"Buffer flushed when overflowed.");
hid_flush(hidp->hid_inuse_rq);
mutex_exit(&hidp->hid_mutex);
} else {
q = hidp->hid_inuse_rq;
mutex_exit(&hidp->hid_mutex);
putnext(q, req->intr_data);
req->intr_data = NULL;
}
} else {
mutex_exit(&hidp->hid_mutex);
}
usb_free_intr_req(req);
hid_pm_idle_component(hidp);
}
static void
hid_default_pipe_callback(usb_pipe_handle_t pipe, usb_ctrl_req_t *req)
{
hid_default_pipe_arg_t *hid_default_pipe_arg =
(hid_default_pipe_arg_t *)req->ctrl_client_private;
queue_t *wq = hid_default_pipe_arg->hid_default_pipe_arg_queue;
queue_t *rq = RD(wq);
hid_state_t *hidp = (hid_state_t *)rq->q_ptr;
mblk_t *mctl_mp;
mblk_t *data = NULL;
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_default_pipe_callback: "
"ph = 0x%p, req = 0x%p, data= 0x%p",
(void *)pipe, (void *)req, (void *)data);
ASSERT((req->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
if (req->ctrl_data) {
data = req->ctrl_data;
req->ctrl_data = NULL;
}
mctl_mp = hid_default_pipe_arg->hid_default_pipe_arg_mblk;
freemsg(mctl_mp->b_cont);
mctl_mp->b_cont = data;
if (canputnext(rq)) {
putnext(rq, mctl_mp);
} else {
freemsg(mctl_mp);
}
kmem_free(hid_default_pipe_arg, sizeof (hid_default_pipe_arg_t));
usb_free_ctrl_req(req);
mutex_enter(&hidp->hid_mutex);
hidp->hid_default_pipe_req--;
ASSERT(hidp->hid_default_pipe_req >= 0);
mutex_exit(&hidp->hid_mutex);
hid_pm_idle_component(hidp);
qenable(wq);
}
static void
hid_interrupt_pipe_exception_callback(usb_pipe_handle_t pipe,
usb_intr_req_t *req)
{
hid_state_t *hidp = (hid_state_t *)req->intr_client_private;
mblk_t *data = req->intr_data;
usb_cb_flags_t flags = req->intr_cb_flags;
int rval;
USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_interrupt_pipe_exception_callback: "
"completion_reason = 0x%x, data = 0x%p, flag = 0x%x",
req->intr_completion_reason, (void *)data, req->intr_cb_flags);
ASSERT((req->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
if (((flags & USB_CB_FUNCTIONAL_STALL) != 0) &&
((flags & USB_CB_STALL_CLEARED) == 0)) {
USB_DPRINTF_L2(PRINT_MASK_ALL,
hidp->hid_log_handle,
"hid_interrupt_pipe_exception_callback: "
"unable to clear stall. flags = 0x%x",
req->intr_cb_flags);
}
mutex_enter(&hidp->hid_mutex);
switch (req->intr_completion_reason) {
case USB_CR_STOPPED_POLLING:
case USB_CR_PIPE_CLOSING:
default:
break;
case USB_CR_PIPE_RESET:
case USB_CR_NO_RESOURCES:
if ((hidp->hid_dev_state == USB_DEV_ONLINE) &&
((rval = hid_start_intr_polling(hidp)) !=
USB_SUCCESS)) {
USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
"unable to restart interrupt poll. rval = %d",
rval);
}
break;
}
mutex_exit(&hidp->hid_mutex);
usb_free_intr_req(req);
}
static void
hid_default_pipe_exception_callback(usb_pipe_handle_t pipe,
usb_ctrl_req_t *req)
{
hid_default_pipe_arg_t *hid_default_pipe_arg =
(hid_default_pipe_arg_t *)req->ctrl_client_private;
queue_t *wq = hid_default_pipe_arg->hid_default_pipe_arg_queue;
queue_t *rq = RD(wq);
hid_state_t *hidp = (hid_state_t *)rq->q_ptr;
usb_cr_t ctrl_completion_reason = req->ctrl_completion_reason;
mblk_t *mp, *data = NULL;
USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_default_pipe_exception_callback: "
"completion_reason = 0x%x, data = 0x%p, flag = 0x%x",
ctrl_completion_reason, (void *)data, req->ctrl_cb_flags);
ASSERT((req->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
mp = hid_default_pipe_arg->hid_default_pipe_arg_mblk;
if (canputnext(rq)) {
mp->b_datap->db_type = M_ERROR;
mp->b_rptr = mp->b_datap->db_base;
mp->b_wptr = mp->b_rptr + sizeof (char);
*mp->b_rptr = EIO;
putnext(rq, mp);
} else {
freemsg(mp);
}
kmem_free(hid_default_pipe_arg, sizeof (hid_default_pipe_arg_t));
mutex_enter(&hidp->hid_mutex);
hidp->hid_default_pipe_req--;
ASSERT(hidp->hid_default_pipe_req >= 0);
mutex_exit(&hidp->hid_mutex);
qenable(wq);
usb_free_ctrl_req(req);
hid_pm_idle_component(hidp);
}
static int
hid_restore_state_event_callback(dev_info_t *dip)
{
hid_state_t *hidp = (hid_state_t *)ddi_get_soft_state(hid_statep,
ddi_get_instance(dip));
ASSERT(hidp != NULL);
USB_DPRINTF_L3(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_restore_state_event_callback: dip=0x%p", (void *)dip);
hid_restore_device_state(dip, hidp);
return (USB_SUCCESS);
}
static int
hid_cpr_suspend(hid_state_t *hidp)
{
int rval, prev_state;
int retval = USB_FAILURE;
USB_DPRINTF_L4(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_cpr_suspend: dip=0x%p", (void *)hidp->hid_dip);
mutex_enter(&hidp->hid_mutex);
switch (hidp->hid_dev_state) {
case USB_DEV_ONLINE:
case USB_DEV_PWRED_DOWN:
prev_state = hidp->hid_dev_state;
hidp->hid_dev_state = USB_DEV_SUSPENDED;
mutex_exit(&hidp->hid_mutex);
rval = usb_pipe_drain_reqs(hidp->hid_dip,
hidp->hid_default_pipe, hid_default_pipe_drain_timeout,
USB_FLAGS_SLEEP, NULL, 0);
mutex_enter(&hidp->hid_mutex);
if ((rval != USB_SUCCESS) || (hidp->hid_default_pipe_req > 0)) {
USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_cpr_suspend: "
"device busy - can't checkpoint");
hidp->hid_dev_state = prev_state;
} else {
retval = USB_SUCCESS;
hid_save_device_state(hidp);
}
break;
case USB_DEV_DISCONNECTED:
hidp->hid_dev_state = USB_DEV_SUSPENDED;
hid_save_device_state(hidp);
retval = USB_SUCCESS;
break;
case USB_DEV_SUSPENDED:
default:
USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_cpr_suspend: Illegal dev state: %d",
hidp->hid_dev_state);
break;
}
mutex_exit(&hidp->hid_mutex);
if ((retval == USB_SUCCESS) && hidp->hid_ugen_hdl != NULL) {
retval = usb_ugen_detach(hidp->hid_ugen_hdl,
DDI_SUSPEND);
}
return (retval);
}
static void
hid_cpr_resume(hid_state_t *hidp)
{
USB_DPRINTF_L4(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_cpr_resume: dip=0x%p", (void *)hidp->hid_dip);
hid_restore_device_state(hidp->hid_dip, hidp);
if (hidp->hid_ugen_hdl != NULL) {
(void) usb_ugen_attach(hidp->hid_ugen_hdl, DDI_RESUME);
}
}
static int
hid_disconnect_event_callback(dev_info_t *dip)
{
hid_state_t *hidp;
mblk_t *mp;
hidp = (hid_state_t *)ddi_get_soft_state(hid_statep,
ddi_get_instance(dip));
ASSERT(hidp != NULL);
USB_DPRINTF_L4(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_disconnect_event_callback: dip=0x%p", (void *)dip);
mutex_enter(&hidp->hid_mutex);
switch (hidp->hid_dev_state) {
case USB_DEV_ONLINE:
case USB_DEV_PWRED_DOWN:
hidp->hid_dev_state = USB_DEV_DISCONNECTED;
if (HID_IS_OPEN(hidp)) {
USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"busy device has been disconnected");
}
hid_save_device_state(hidp);
if (hidp->hid_external_flag == HID_STREAMS_OPEN) {
queue_t *q = hidp->hid_external_rq;
mutex_exit(&hidp->hid_mutex);
mp = allocb(sizeof (uchar_t), BPRI_HI);
if (mp != NULL) {
mp->b_datap->db_type = M_ERROR;
mp->b_rptr = mp->b_datap->db_base;
mp->b_wptr = mp->b_rptr + sizeof (char);
*mp->b_rptr = ENODEV;
putnext(q, mp);
}
mutex_enter(&hidp->hid_mutex);
}
break;
case USB_DEV_SUSPENDED:
break;
default:
USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_disconnect_event_callback: Illegal dev state: %d",
hidp->hid_dev_state);
break;
}
mutex_exit(&hidp->hid_mutex);
return (USB_SUCCESS);
}
static void
hid_power_change_callback(void *arg, int rval)
{
hid_state_t *hidp;
queue_t *wq;
hidp = (hid_state_t *)arg;
USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
"hid_power_change_callback - rval: %d", rval);
mutex_enter(&hidp->hid_mutex);
hidp->hid_pm->hid_raise_power = B_FALSE;
if (hidp->hid_dev_state == USB_DEV_ONLINE) {
wq = WR(hidp->hid_inuse_rq);
mutex_exit(&hidp->hid_mutex);
qenable(wq);
} else {
mutex_exit(&hidp->hid_mutex);
}
}
static size_t
hid_parse_hid_descr(usb_hid_descr_t *ret_descr, size_t ret_buf_len,
usb_alt_if_data_t *altif_data, usb_ep_data_t *ep_data)
{
usb_cvs_data_t *cvs;
int which_cvs;
for (which_cvs = 0; which_cvs < altif_data->altif_n_cvs; which_cvs++) {
cvs = &altif_data->altif_cvs[which_cvs];
if (cvs->cvs_buf == NULL) {
continue;
}
if (cvs->cvs_buf[1] == USB_DESCR_TYPE_HID) {
return (usb_parse_data("ccscccs",
cvs->cvs_buf, cvs->cvs_buf_len,
(void *)ret_descr,
(size_t)ret_buf_len));
}
}
for (which_cvs = 0; which_cvs < ep_data->ep_n_cvs; which_cvs++) {
cvs = &ep_data->ep_cvs[which_cvs];
if (cvs->cvs_buf == NULL) {
continue;
}
if (cvs->cvs_buf[1] == USB_DESCR_TYPE_HID) {
return (usb_parse_data("ccscccs",
cvs->cvs_buf, cvs->cvs_buf_len,
(void *)ret_descr,
(size_t)ret_buf_len));
}
}
return (USB_PARSE_ERROR);
}
static int
hid_parse_hid_descr_failure(hid_state_t *hidp)
{
USB_DPRINTF_L1(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Parsing of hid descriptor failed");
if (hidp->hid_if_descr.bInterfaceProtocol == KEYBOARD_PROTOCOL) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Set hid descriptor length to predefined "
"USB_KB_HID_DESCR_LENGTH for keyboard.");
hidp->hid_hid_descr.wReportDescriptorLength =
USB_KB_HID_DESCR_LENGTH;
hidp->hid_packet_size = USBKPSZ;
} else if (hidp->hid_if_descr.bInterfaceProtocol ==
MOUSE_PROTOCOL) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Set hid descriptor length to predefined "
"USB_MS_HID_DESCR_LENGTH for mouse.");
hidp->hid_hid_descr.wReportDescriptorLength =
USB_MS_HID_DESCR_LENGTH;
hidp->hid_packet_size = USBMSSZ;
} else {
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static int
hid_handle_report_descriptor(hid_state_t *hidp, int interface)
{
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
mblk_t *data = NULL;
hidparser_packet_info_t hpack;
int i;
usb_ctrl_setup_t setup = {
USB_DEV_REQ_DEV_TO_HOST |
USB_DEV_REQ_RCPT_IF,
USB_REQ_GET_DESCR,
USB_CLASS_DESCR_TYPE_REPORT,
0,
0,
0
};
setup.wIndex = (uint16_t)interface;
setup.wLength = hidp->hid_hid_descr.wReportDescriptorLength;
if (usb_pipe_ctrl_xfer_wait(hidp->hid_default_pipe,
&setup,
&data,
&completion_reason, &cb_flags, 0) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Failed to receive the Report Descriptor");
freemsg(data);
return (USB_FAILURE);
} else {
int n = hidp->hid_hid_descr.wReportDescriptorLength;
ASSERT(data);
for (i = 0; i < n; i++) {
USB_DPRINTF_L3(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Index = %d\tvalue =0x%x", i,
(int)(data->b_rptr[i]));
}
if (hidparser_parse_report_descriptor(
data->b_rptr,
hidp->hid_hid_descr.wReportDescriptorLength,
&hidp->hid_hid_descr,
&hidp->hid_report_descr) == HIDPARSER_SUCCESS) {
hidparser_find_max_packet_size_from_report_descriptor(
hidp->hid_report_descr, &hpack);
hidp->hid_packet_size = (hpack.max_packet_size + 7) / 8;
if (hpack.report_id != HID_REPORT_ID_UNDEFINED) {
hidp->hid_packet_size++;
}
} else {
USB_DPRINTF_L1(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Invalid Report Descriptor");
freemsg(data);
return (USB_FAILURE);
}
freemsg(data);
return (USB_SUCCESS);
}
}
static void
hid_set_idle(hid_state_t *hidp)
{
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
usb_ctrl_setup_t setup = {
USB_DEV_REQ_HOST_TO_DEV |
USB_DEV_REQ_TYPE_CLASS |
USB_DEV_REQ_RCPT_IF,
SET_IDLE,
DURATION,
0,
0,
0
};
USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_set_idle: Begin");
setup.wIndex = hidp->hid_if_descr.bInterfaceNumber;
if (usb_pipe_ctrl_xfer_wait(
hidp->hid_default_pipe,
&setup,
NULL,
&completion_reason, &cb_flags, 0) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Failed while trying to set idle,"
"cr = %d, cb_flags = 0x%x\n",
completion_reason, cb_flags);
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_set_idle: End");
}
static void
hid_set_protocol(hid_state_t *hidp, int protocol)
{
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
usb_ctrl_setup_t setup;
USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_set_protocol(%d): Begin", protocol);
setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
setup.bRequest = SET_PROTOCOL;
setup.wValue = (uint16_t)protocol;
setup.wIndex = hidp->hid_if_descr.bInterfaceNumber;
setup.wLength = 0;
setup.attrs = 0;
if (usb_pipe_ctrl_xfer_wait(
hidp->hid_default_pipe,
&setup,
NULL,
&completion_reason, &cb_flags, 0) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
"Failed while trying to set protocol:%d,"
"cr = %d cb_flags = 0x%x\n",
completion_reason, cb_flags, protocol);
}
USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_set_protocol: End");
}
static void
hid_detach_cleanup(dev_info_t *dip, hid_state_t *hidp)
{
int flags = hidp->hid_attach_flags;
int rval;
hid_power_t *hidpm;
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_detach_cleanup: Begin");
if ((hidp->hid_attach_flags & HID_LOCK_INIT) == 0) {
goto done;
}
usb_unregister_event_cbs(dip, &hid_events);
mutex_enter(&hidp->hid_mutex);
hidpm = hidp->hid_pm;
USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_detach_cleanup: hidpm=0x%p", (void *)hidpm);
if (hidpm && (hidp->hid_dev_state != USB_DEV_DISCONNECTED)) {
mutex_exit(&hidp->hid_mutex);
hid_pm_busy_component(hidp);
if (hid_is_pm_enabled(dip) == USB_SUCCESS) {
if (hidpm->hid_wakeup_enabled) {
(void) pm_raise_power(dip, 0,
USB_DEV_OS_FULL_PWR);
rval = usb_handle_remote_wakeup(dip,
USB_REMOTE_WAKEUP_DISABLE);
if (rval != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ALL,
hidp->hid_log_handle,
"hid_detach_cleanup: "
"disble remote wakeup failed, "
"rval= %d", rval);
}
}
(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
}
hid_pm_idle_component(hidp);
mutex_enter(&hidp->hid_mutex);
}
if (hidpm) {
freemsg(hidpm->hid_pm_pwrup);
kmem_free(hidpm, sizeof (hid_power_t));
hidp->hid_pm = NULL;
}
if (hidp->hid_ugen_hdl != NULL) {
rval = usb_ugen_detach(hidp->hid_ugen_hdl, DDI_DETACH);
VERIFY0(rval);
usb_ugen_release_hdl(hidp->hid_ugen_hdl);
}
mutex_exit(&hidp->hid_mutex);
if (hidp->hid_report_descr != NULL) {
(void) hidparser_free_report_descriptor_handle(
hidp->hid_report_descr);
}
if (flags & HID_MINOR_NODES) {
ddi_remove_minor_node(dip, NULL);
}
mutex_destroy(&hidp->hid_mutex);
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_detach_cleanup: End");
done:
usb_client_detach(dip, hidp->hid_dev_data);
usb_free_log_hdl(hidp->hid_log_handle);
ddi_soft_state_free(hid_statep, hidp->hid_instance);
ddi_prop_remove_all(dip);
}
static int
hid_start_intr_polling(hid_state_t *hidp)
{
usb_intr_req_t *req;
int rval = USB_SUCCESS;
USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
"hid_start_intr_polling: "
"dev_state=%s internal_str_flag=%d external_str_flag=%d ph=0x%p",
usb_str_dev_state(hidp->hid_dev_state), hidp->hid_internal_flag,
hidp->hid_external_flag, (void *)hidp->hid_interrupt_pipe);
if (HID_IS_OPEN(hidp) && (hidp->hid_interrupt_pipe != NULL)) {
req = usb_alloc_intr_req(hidp->hid_dip, 0, USB_FLAGS_SLEEP);
req->intr_client_private = (usb_opaque_t)hidp;
req->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
USB_ATTRS_AUTOCLEARING;
req->intr_len = hidp->hid_packet_size;
req->intr_cb = hid_interrupt_pipe_callback;
req->intr_exc_cb = hid_interrupt_pipe_exception_callback;
mutex_exit(&hidp->hid_mutex);
if ((rval = usb_pipe_intr_xfer(hidp->hid_interrupt_pipe, req,
USB_FLAGS_SLEEP)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_PM, hidp->hid_log_handle,
"hid_start_intr_polling failed: rval = %d",
rval);
usb_free_intr_req(req);
}
mutex_enter(&hidp->hid_mutex);
}
USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
"hid_start_intr_polling: done, rval = %d", rval);
return (rval);
}
static void
hid_close_intr_pipe(hid_state_t *hidp)
{
USB_DPRINTF_L4(PRINT_MASK_CLOSE, hidp->hid_log_handle,
"hid_close_intr_pipe: Begin");
if (hidp->hid_interrupt_pipe) {
mutex_exit(&hidp->hid_mutex);
usb_pipe_close(hidp->hid_dip, hidp->hid_interrupt_pipe,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&hidp->hid_mutex);
hidp->hid_interrupt_pipe = NULL;
}
USB_DPRINTF_L4(PRINT_MASK_CLOSE, hidp->hid_log_handle,
"hid_close_intr_pipe: End");
}
static int
hid_mctl_receive(register queue_t *q, register mblk_t *mp)
{
hid_state_t *hidp = (hid_state_t *)q->q_ptr;
struct iocblk *iocp;
int error = HID_FAILURE;
uchar_t request_type;
hid_req_t *hid_req_data = NULL;
hid_polled_input_callback_t hid_polled_input;
hid_vid_pid_t hid_vid_pid;
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_mctl_receive");
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case HID_SET_REPORT:
case HID_SET_IDLE:
case HID_SET_PROTOCOL:
request_type = USB_DEV_REQ_HOST_TO_DEV |
USB_DEV_REQ_RCPT_IF | USB_DEV_REQ_TYPE_CLASS;
break;
case HID_GET_REPORT:
case HID_GET_IDLE:
case HID_GET_PROTOCOL:
request_type = USB_DEV_REQ_DEV_TO_HOST |
USB_DEV_REQ_RCPT_IF | USB_DEV_REQ_TYPE_CLASS;
break;
case HID_GET_PARSER_HANDLE:
if (canputnext(RD(q))) {
freemsg(mp->b_cont);
mp->b_cont = hid_data2mblk(
(uchar_t *)&hidp->hid_report_descr,
sizeof (hidp->hid_report_descr));
if (mp->b_cont == NULL) {
iocp->ioc_count = 0;
} else {
iocp->ioc_count =
sizeof (hidp->hid_report_descr);
}
qreply(q, mp);
return (HID_SUCCESS);
} else {
return (HID_ENQUEUE);
}
case HID_GET_VID_PID:
if (canputnext(RD(q))) {
freemsg(mp->b_cont);
hid_vid_pid.VendorId =
hidp->hid_dev_descr->idVendor;
hid_vid_pid.ProductId =
hidp->hid_dev_descr->idProduct;
mp->b_cont = hid_data2mblk(
(uchar_t *)&hid_vid_pid, sizeof (hid_vid_pid_t));
if (mp->b_cont == NULL) {
iocp->ioc_count = 0;
} else {
iocp->ioc_count =
sizeof (hid_vid_pid_t);
}
qreply(q, mp);
return (HID_SUCCESS);
} else {
return (HID_ENQUEUE);
}
case HID_OPEN_POLLED_INPUT:
if (canputnext(RD(q))) {
freemsg(mp->b_cont);
hid_polled_input.hid_polled_version =
HID_POLLED_INPUT_V0;
hid_polled_input.hid_polled_read = hid_polled_read;
hid_polled_input.hid_polled_input_enter =
hid_polled_input_enter;
hid_polled_input.hid_polled_input_exit =
hid_polled_input_exit;
hid_polled_input.hid_polled_input_handle =
(hid_polled_handle_t)hidp;
mp->b_cont = hid_data2mblk(
(uchar_t *)&hid_polled_input,
sizeof (hid_polled_input_callback_t));
if (mp->b_cont == NULL) {
iocp->ioc_count = 0;
} else {
(void) hid_polled_input_init(hidp);
iocp->ioc_count =
sizeof (hid_polled_input_callback_t);
}
qreply(q, mp);
return (HID_SUCCESS);
} else {
return (HID_ENQUEUE);
}
case HID_CLOSE_POLLED_INPUT:
(void) hid_polled_input_fini(hidp);
iocp->ioc_count = 0;
qreply(q, mp);
return (HID_SUCCESS);
default:
hid_qreply_merror(q, mp, EINVAL);
return (HID_FAILURE);
}
if (mp->b_cont == NULL) {
hid_qreply_merror(q, mp, EINVAL);
return (error);
} else {
hid_req_data = (hid_req_t *)mp->b_cont->b_rptr;
if ((iocp->ioc_cmd == HID_SET_REPORT) &&
(hid_req_data->hid_req_wLength == 0)) {
hid_qreply_merror(q, mp, EINVAL);
return (error);
}
}
if (hid_req_data->hid_req_version_no != HID_VERSION_V_0) {
hid_qreply_merror(q, mp, EINVAL);
return (error);
}
mutex_enter(&hidp->hid_mutex);
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_mctl_receive: dev_state=%s",
usb_str_dev_state(hidp->hid_dev_state));
switch (hidp->hid_dev_state) {
case USB_DEV_PWRED_DOWN:
hidp->hid_dev_state = USB_DEV_HID_POWER_CHANGE;
mutex_exit(&hidp->hid_mutex);
if (usb_req_raise_power(hidp->hid_dip, 0,
USB_DEV_OS_FULL_PWR, hid_power_change_callback,
hidp, 0) != USB_SUCCESS) {
mutex_enter(&hidp->hid_mutex);
hidp->hid_dev_state = USB_DEV_PWRED_DOWN;
mutex_exit(&hidp->hid_mutex);
}
error = HID_ENQUEUE;
break;
case USB_DEV_HID_POWER_CHANGE:
mutex_exit(&hidp->hid_mutex);
error = HID_ENQUEUE;
break;
case USB_DEV_ONLINE:
if (HID_STREAMS_FLAG(q, hidp) != HID_STREAMS_DISMANTLING) {
mutex_exit(&hidp->hid_mutex);
error = hid_mctl_execute_cmd(q, request_type,
hid_req_data, mp);
if (error == HID_FAILURE) {
hid_qreply_merror(q, mp, EIO);
}
} else {
mutex_exit(&hidp->hid_mutex);
hid_qreply_merror(q, mp, EIO);
}
break;
default:
mutex_exit(&hidp->hid_mutex);
hid_qreply_merror(q, mp, EIO);
break;
}
return (error);
}
static int
hid_mctl_execute_cmd(queue_t *q, int request_type, hid_req_t *hid_req_data,
mblk_t *mp)
{
int request_index;
struct iocblk *iocp;
hid_default_pipe_arg_t *def_pipe_arg;
hid_state_t *hidp = (hid_state_t *)q->q_ptr;
iocp = (struct iocblk *)mp->b_rptr;
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_mctl_execute_cmd: iocp=0x%p", (void *)iocp);
request_index = hidp->hid_if_descr.bInterfaceNumber;
def_pipe_arg = kmem_zalloc(sizeof (hid_default_pipe_arg_t), 0);
if (def_pipe_arg == NULL) {
return (HID_FAILURE);
}
def_pipe_arg->hid_default_pipe_arg_queue = q;
def_pipe_arg->hid_default_pipe_arg_mctlmsg.ioc_cmd = iocp->ioc_cmd;
def_pipe_arg->hid_default_pipe_arg_mctlmsg.ioc_count = 0;
def_pipe_arg->hid_default_pipe_arg_mblk = mp;
if (hid_send_async_ctrl_request(def_pipe_arg, hid_req_data,
request_type, iocp->ioc_cmd, request_index) != USB_SUCCESS) {
kmem_free(def_pipe_arg, sizeof (hid_default_pipe_arg_t));
return (HID_FAILURE);
}
return (HID_INPROGRESS);
}
static int
hid_send_async_ctrl_request(hid_default_pipe_arg_t *hid_default_pipe_arg,
hid_req_t *hid_request, uchar_t request_type, int request_request,
ushort_t request_index)
{
queue_t *q = hid_default_pipe_arg->hid_default_pipe_arg_queue;
hid_state_t *hidp = (hid_state_t *)q->q_ptr;
usb_ctrl_req_t *ctrl_req;
int rval;
size_t length = 0;
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_send_async_ctrl_request: "
"rq_type=%d rq_rq=%d index=%d",
request_type, request_request, request_index);
mutex_enter(&hidp->hid_mutex);
hidp->hid_default_pipe_req++;
mutex_exit(&hidp->hid_mutex);
if (hid_request->hid_req_wLength >= MAX_REPORT_DATA) {
USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_req_wLength is exceeded");
return (USB_FAILURE);
}
if ((request_type & USB_DEV_REQ_DIR_MASK) == USB_DEV_REQ_DEV_TO_HOST) {
length = hid_request->hid_req_wLength;
}
if ((ctrl_req = usb_alloc_ctrl_req(hidp->hid_dip, length, 0)) == NULL) {
USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
"unable to alloc ctrl req. async trans failed");
mutex_enter(&hidp->hid_mutex);
hidp->hid_default_pipe_req--;
ASSERT(hidp->hid_default_pipe_req >= 0);
mutex_exit(&hidp->hid_mutex);
return (USB_FAILURE);
}
if ((request_type & USB_DEV_REQ_DIR_MASK) == USB_DEV_REQ_HOST_TO_DEV) {
ASSERT((length == 0) && (ctrl_req->ctrl_data == NULL));
}
ctrl_req->ctrl_bmRequestType = request_type;
ctrl_req->ctrl_bRequest = (uint8_t)request_request;
ctrl_req->ctrl_wValue = hid_request->hid_req_wValue;
ctrl_req->ctrl_wIndex = request_index;
ctrl_req->ctrl_wLength = hid_request->hid_req_wLength;
if ((request_type & USB_DEV_REQ_DIR_MASK) == USB_DEV_REQ_HOST_TO_DEV) {
mblk_t *pblk = allocb(hid_request->hid_req_wLength, BPRI_HI);
if (pblk == NULL) {
usb_free_ctrl_req(ctrl_req);
return (USB_FAILURE);
}
bcopy(hid_request->hid_req_data, pblk->b_wptr,
hid_request->hid_req_wLength);
pblk->b_wptr += hid_request->hid_req_wLength;
ctrl_req->ctrl_data = pblk;
}
ctrl_req->ctrl_attributes = USB_ATTRS_AUTOCLEARING;
ctrl_req->ctrl_client_private = (usb_opaque_t)hid_default_pipe_arg;
ctrl_req->ctrl_cb = hid_default_pipe_callback;
ctrl_req->ctrl_exc_cb = hid_default_pipe_exception_callback;
if ((rval = usb_pipe_ctrl_xfer(hidp->hid_default_pipe,
ctrl_req, 0)) != USB_SUCCESS) {
mutex_enter(&hidp->hid_mutex);
hidp->hid_default_pipe_req--;
ASSERT(hidp->hid_default_pipe_req >= 0);
mutex_exit(&hidp->hid_mutex);
usb_free_ctrl_req(ctrl_req);
USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
"usb_pipe_ctrl_xfer() failed. rval = %d", rval);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static void
hid_create_pm_components(dev_info_t *dip, hid_state_t *hidp)
{
hid_power_t *hidpm;
uint_t pwr_states;
USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
"hid_create_pm_components: Begin");
hidpm = kmem_zalloc(sizeof (hid_power_t), KM_SLEEP);
hidp->hid_pm = hidpm;
hidpm->hid_state = hidp;
hidpm->hid_raise_power = B_FALSE;
hidpm->hid_pm_capabilities = 0;
hidpm->hid_current_power = USB_DEV_OS_FULL_PWR;
switch (hidp->hid_if_descr.bInterfaceProtocol) {
case KEYBOARD_PROTOCOL:
case MOUSE_PROTOCOL:
hidpm->hid_pm_strategy = HID_PM_ACTIVITY;
if ((hid_is_pm_enabled(dip) == USB_SUCCESS) &&
(usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) ==
USB_SUCCESS)) {
USB_DPRINTF_L3(PRINT_MASK_PM, hidp->hid_log_handle,
"hid_create_pm_components: Remote Wakeup Enabled");
if (usb_create_pm_components(dip, &pwr_states) ==
USB_SUCCESS) {
hidpm->hid_wakeup_enabled = 1;
hidpm->hid_pwr_states = (uint8_t)pwr_states;
}
}
break;
default:
hidpm->hid_pm_strategy = HID_PM_OPEN_CLOSE;
if ((hid_is_pm_enabled(dip) == USB_SUCCESS) &&
(usb_create_pm_components(dip, &pwr_states) ==
USB_SUCCESS)) {
hidpm->hid_wakeup_enabled = 0;
hidpm->hid_pwr_states = (uint8_t)pwr_states;
}
break;
}
USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
"hid_create_pm_components: END");
}
static int
hid_is_pm_enabled(dev_info_t *dip)
{
hid_state_t *hidp = ddi_get_soft_state(hid_statep,
ddi_get_instance(dip));
if (strcmp(ddi_node_name(dip), "mouse") == 0) {
if (hid_pm_mouse ||
(ddi_prop_exists(DDI_DEV_T_ANY, dip,
(DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
"hid-mouse-pm-enable") == 1)) {
return (USB_SUCCESS);
}
if ((hidp->hid_dev_descr->idVendor ==
HID_SUN_MOUSE_VENDOR_ID) &&
(hidp->hid_dev_descr->idProduct ==
HID_SUN_MOUSE_PROD_ID) &&
(hidp->hid_dev_descr->bcdDevice >=
HID_SUN_MOUSE_BCDDEVICE)) {
return (USB_SUCCESS);
}
} else {
return (USB_SUCCESS);
}
return (USB_FAILURE);
}
static void
hid_save_device_state(hid_state_t *hidp)
{
struct iocblk *mctlmsg;
mblk_t *mp;
queue_t *q;
USB_DPRINTF_L4(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_save_device_state");
if (!(HID_IS_OPEN(hidp)))
return;
if (hidp->hid_internal_flag == HID_STREAMS_OPEN) {
q = hidp->hid_internal_rq;
mutex_exit(&hidp->hid_mutex);
if (canputnext(q)) {
mp = allocb(sizeof (struct iocblk), BPRI_HI);
if (mp != NULL) {
mp->b_datap->db_type = M_CTL;
mctlmsg = (struct iocblk *)
mp->b_datap->db_base;
mctlmsg->ioc_cmd = HID_DISCONNECT_EVENT;
mctlmsg->ioc_count = 0;
putnext(q, mp);
}
}
mutex_enter(&hidp->hid_mutex);
}
if (hidp->hid_external_flag == HID_STREAMS_OPEN) {
q = hidp->hid_external_rq;
mutex_exit(&hidp->hid_mutex);
if (canputnext(q)) {
mp = allocb(sizeof (struct iocblk), BPRI_HI);
if (mp != NULL) {
mp->b_datap->db_type = M_CTL;
mctlmsg = (struct iocblk *)
mp->b_datap->db_base;
mctlmsg->ioc_cmd = HID_DISCONNECT_EVENT;
mctlmsg->ioc_count = 0;
putnext(q, mp);
}
}
mutex_enter(&hidp->hid_mutex);
}
mutex_exit(&hidp->hid_mutex);
usb_pipe_stop_intr_polling(hidp->hid_interrupt_pipe, USB_FLAGS_SLEEP);
mutex_enter(&hidp->hid_mutex);
}
static void
hid_restore_device_state(dev_info_t *dip, hid_state_t *hidp)
{
int rval;
hid_power_t *hidpm;
struct iocblk *mctlmsg;
mblk_t *mp;
queue_t *q;
hid_pm_busy_component(hidp);
mutex_enter(&hidp->hid_mutex);
USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_restore_device_state: %s",
usb_str_dev_state(hidp->hid_dev_state));
hidpm = hidp->hid_pm;
mutex_exit(&hidp->hid_mutex);
(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
mutex_enter(&hidp->hid_mutex);
if (hidp->hid_dev_state == USB_DEV_ONLINE) {
mutex_exit(&hidp->hid_mutex);
hid_pm_idle_component(hidp);
return;
}
mutex_exit(&hidp->hid_mutex);
if (usb_check_same_device(dip, hidp->hid_log_handle, USB_LOG_L2,
PRINT_MASK_ALL, USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS) {
mutex_enter(&hidp->hid_mutex);
hidp->hid_dev_state = USB_DEV_DISCONNECTED;
mutex_exit(&hidp->hid_mutex);
hid_pm_idle_component(hidp);
goto nodev;
}
hid_set_idle(hidp);
hid_set_protocol(hidp, SET_REPORT_PROTOCOL);
mutex_enter(&hidp->hid_mutex);
if (hidpm->hid_wakeup_enabled) {
mutex_exit(&hidp->hid_mutex);
if ((rval = usb_handle_remote_wakeup(hidp->hid_dip,
USB_REMOTE_WAKEUP_ENABLE)) != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ATTA,
hidp->hid_log_handle,
"usb_handle_remote_wakeup failed (%d)", rval);
}
mutex_enter(&hidp->hid_mutex);
}
if (HID_IS_OPEN(hidp)) {
if ((rval = hid_start_intr_polling(hidp)) != USB_SUCCESS) {
USB_DPRINTF_L3(PRINT_MASK_ATTA, hidp->hid_log_handle,
"hid_restore_device_state:"
"unable to restart intr pipe poll"
" rval = %d ", rval);
hidp->hid_dev_state = USB_DEV_DISCONNECTED;
mutex_exit(&hidp->hid_mutex);
hid_pm_idle_component(hidp);
goto nodev;
}
if (hidp->hid_dev_state == USB_DEV_DISCONNECTED) {
USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"device is being re-connected");
}
hidp->hid_dev_state = USB_DEV_ONLINE;
if (hidp->hid_internal_flag == HID_STREAMS_OPEN) {
q = hidp->hid_internal_rq;
mutex_exit(&hidp->hid_mutex);
if (canputnext(q)) {
mp = allocb(sizeof (struct iocblk), BPRI_HI);
if (mp != NULL) {
mp->b_datap->db_type = M_CTL;
mctlmsg = (struct iocblk *)
mp->b_datap->db_base;
mctlmsg->ioc_cmd = HID_CONNECT_EVENT;
mctlmsg->ioc_count = 0;
putnext(q, mp);
}
}
qenable(WR(q));
mutex_enter(&hidp->hid_mutex);
}
if (hidp->hid_external_flag == HID_STREAMS_OPEN) {
q = hidp->hid_external_rq;
mutex_exit(&hidp->hid_mutex);
if (canputnext(q)) {
mp = allocb(sizeof (struct iocblk), BPRI_HI);
if (mp != NULL) {
mp->b_datap->db_type = M_CTL;
mctlmsg = (struct iocblk *)
mp->b_datap->db_base;
mctlmsg->ioc_cmd = HID_CONNECT_EVENT;
mctlmsg->ioc_count = 0;
putnext(q, mp);
}
}
qenable(WR(q));
mutex_enter(&hidp->hid_mutex);
}
} else {
hidp->hid_dev_state = USB_DEV_ONLINE;
}
mutex_exit(&hidp->hid_mutex);
hid_pm_idle_component(hidp);
return;
nodev:
mutex_enter(&hidp->hid_mutex);
if ((q = hidp->hid_external_rq) == NULL) {
mutex_exit(&hidp->hid_mutex);
return;
}
mutex_exit(&hidp->hid_mutex);
mp = allocb(sizeof (uchar_t), BPRI_HI);
if (mp != NULL) {
mp->b_datap->db_type = M_ERROR;
mp->b_rptr = mp->b_datap->db_base;
mp->b_wptr = mp->b_rptr + sizeof (char);
*mp->b_rptr = ENODEV;
putnext(q, mp);
}
}
static void
hid_qreply_merror(queue_t *q, mblk_t *mp, uchar_t errval)
{
mp->b_datap->db_type = M_ERROR;
if (mp->b_cont) {
freemsg(mp->b_cont);
mp->b_cont = NULL;
}
mp->b_rptr = mp->b_datap->db_base;
mp->b_wptr = mp->b_rptr + sizeof (char);
*mp->b_rptr = errval;
qreply(q, mp);
}
static mblk_t *
hid_data2mblk(uchar_t *buf, int len)
{
mblk_t *mp = NULL;
if (len >= 0) {
mp = allocb(len, BPRI_HI);
if (mp) {
bcopy(buf, mp->b_datap->db_base, len);
mp->b_wptr += len;
}
}
return (mp);
}
static void
hid_flush(queue_t *q)
{
if ((q != NULL) && (q->q_next != NULL)) {
(void) putnextctl1(q, M_FLUSH, FLUSHR);
}
}
static void
hid_pm_busy_component(hid_state_t *hid_statep)
{
ASSERT(!mutex_owned(&hid_statep->hid_mutex));
if (hid_statep->hid_pm != NULL) {
mutex_enter(&hid_statep->hid_mutex);
hid_statep->hid_pm->hid_pm_busy++;
USB_DPRINTF_L4(PRINT_MASK_PM, hid_statep->hid_log_handle,
"hid_pm_busy_component: %d",
hid_statep->hid_pm->hid_pm_busy);
mutex_exit(&hid_statep->hid_mutex);
if (pm_busy_component(hid_statep->hid_dip, 0) != DDI_SUCCESS) {
mutex_enter(&hid_statep->hid_mutex);
hid_statep->hid_pm->hid_pm_busy--;
USB_DPRINTF_L2(PRINT_MASK_PM,
hid_statep->hid_log_handle,
"hid_pm_busy_component failed: %d",
hid_statep->hid_pm->hid_pm_busy);
mutex_exit(&hid_statep->hid_mutex);
}
}
}
static void
hid_pm_idle_component(hid_state_t *hid_statep)
{
ASSERT(!mutex_owned(&hid_statep->hid_mutex));
if (hid_statep->hid_pm != NULL) {
if (pm_idle_component(hid_statep->hid_dip, 0) == DDI_SUCCESS) {
mutex_enter(&hid_statep->hid_mutex);
ASSERT(hid_statep->hid_pm->hid_pm_busy > 0);
hid_statep->hid_pm->hid_pm_busy--;
USB_DPRINTF_L4(PRINT_MASK_PM,
hid_statep->hid_log_handle,
"hid_pm_idle_component: %d",
hid_statep->hid_pm->hid_pm_busy);
mutex_exit(&hid_statep->hid_mutex);
}
}
}
static int
hid_pwrlvl0(hid_state_t *hidp)
{
hid_power_t *hidpm;
int rval;
struct iocblk *mctlmsg;
mblk_t *mp_lowpwr, *mp_fullpwr;
queue_t *q;
hidpm = hidp->hid_pm;
switch (hidp->hid_dev_state) {
case USB_DEV_ONLINE:
if (hidpm->hid_pm_busy != 0) {
return (USB_FAILURE);
}
if (HID_IS_OPEN(hidp)) {
q = hidp->hid_inuse_rq;
mutex_exit(&hidp->hid_mutex);
if (canputnext(q)) {
mp_lowpwr = allocb(
(int)sizeof (struct iocblk), BPRI_HI);
mp_fullpwr = allocb(
(int)sizeof (struct iocblk), BPRI_HI);
if ((mp_lowpwr != NULL) &&
(mp_fullpwr != NULL)) {
usb_pipe_stop_intr_polling(
hidp->hid_interrupt_pipe,
USB_FLAGS_SLEEP);
mp_lowpwr->b_datap->db_type = M_CTL;
mctlmsg = (struct iocblk *)
mp_lowpwr->b_datap->db_base;
mctlmsg->ioc_cmd = HID_POWER_OFF;
mctlmsg->ioc_count = 0;
putnext(q, mp_lowpwr);
mutex_enter(&hidp->hid_mutex);
hidpm->hid_pm_pwrup = mp_fullpwr;
} else {
freemsg(mp_lowpwr);
freemsg(mp_fullpwr);
mutex_enter(&hidp->hid_mutex);
return (USB_FAILURE);
}
} else {
mutex_enter(&hidp->hid_mutex);
return (USB_FAILURE);
}
}
mutex_exit(&hidp->hid_mutex);
rval = usb_set_device_pwrlvl3(hidp->hid_dip);
ASSERT(rval == USB_SUCCESS);
mutex_enter(&hidp->hid_mutex);
hidp->hid_dev_state = USB_DEV_PWRED_DOWN;
hidpm->hid_current_power = USB_DEV_OS_PWR_OFF;
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
case USB_DEV_PWRED_DOWN:
default:
break;
}
return (USB_SUCCESS);
}
static int
hid_pwrlvl1(hid_state_t *hidp)
{
int rval;
rval = usb_set_device_pwrlvl2(hidp->hid_dip);
ASSERT(rval == USB_SUCCESS);
return (USB_FAILURE);
}
static int
hid_pwrlvl2(hid_state_t *hidp)
{
int rval;
rval = usb_set_device_pwrlvl1(hidp->hid_dip);
ASSERT(rval == USB_SUCCESS);
return (USB_FAILURE);
}
static int
hid_pwrlvl3(hid_state_t *hidp)
{
hid_power_t *hidpm;
int rval;
struct iocblk *mctlmsg;
mblk_t *mp;
queue_t *q;
hidpm = hidp->hid_pm;
switch (hidp->hid_dev_state) {
case USB_DEV_HID_POWER_CHANGE:
case USB_DEV_PWRED_DOWN:
rval = usb_set_device_pwrlvl0(hidp->hid_dip);
ASSERT(rval == USB_SUCCESS);
if (HID_IS_OPEN(hidp)) {
rval = hid_start_intr_polling(hidp);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_EVENTS,
hidp->hid_log_handle,
"unable to restart intr polling rval = %d",
rval);
return (USB_FAILURE);
}
q = hidp->hid_inuse_rq;
mp = hidpm->hid_pm_pwrup;
hidpm->hid_pm_pwrup = NULL;
mutex_exit(&hidp->hid_mutex);
if (canputnext(q)) {
mp->b_datap->db_type = M_CTL;
mctlmsg = (struct iocblk *)
mp->b_datap->db_base;
mctlmsg->ioc_cmd = HID_FULL_POWER;
mctlmsg->ioc_count = 0;
putnext(q, mp);
} else {
freemsg(mp);
}
mutex_enter(&hidp->hid_mutex);
}
hidp->hid_dev_state = USB_DEV_ONLINE;
hidpm->hid_current_power = USB_DEV_OS_FULL_PWR;
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
case USB_DEV_ONLINE:
return (USB_SUCCESS);
default:
USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
"hid_pwrlvl3: Improper State");
return (USB_FAILURE);
}
}
static int
hid_polled_input_init(hid_state_t *hidp)
{
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_polled_input_init");
if (usb_console_input_init(hidp->hid_dip, hidp->hid_interrupt_pipe,
&hidp->hid_polled_raw_buf,
&hidp->hid_polled_console_info) != USB_SUCCESS) {
(void) hid_polled_input_fini(hidp);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static int
hid_polled_input_fini(hid_state_t *hidp)
{
USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
"hid_polled_input_fini");
if ((hidp->hid_polled_console_info) &&
(usb_console_input_fini(hidp->hid_polled_console_info) !=
USB_SUCCESS)) {
return (USB_FAILURE);
}
hidp->hid_polled_console_info = NULL;
return (USB_SUCCESS);
}
static int
hid_polled_input_enter(hid_polled_handle_t hid_polled_inputp)
{
hid_state_t *hidp = (hid_state_t *)hid_polled_inputp;
(void) usb_console_input_enter(hidp->hid_polled_console_info);
return (USB_SUCCESS);
}
static int
hid_polled_read(hid_polled_handle_t hid_polled_input, uchar_t **buffer)
{
hid_state_t *hidp = (hid_state_t *)hid_polled_input;
uint_t num_bytes;
if (usb_console_read(hidp->hid_polled_console_info,
&num_bytes) != USB_SUCCESS) {
return (0);
}
_NOTE(NO_COMPETING_THREADS_NOW);
*buffer = hidp->hid_polled_raw_buf;
_NOTE(COMPETING_THREADS_NOW);
return (num_bytes);
}
static int
hid_polled_input_exit(hid_polled_handle_t hid_polled_inputp)
{
hid_state_t *hidp = (hid_state_t *)hid_polled_inputp;
(void) usb_console_input_exit(hidp->hid_polled_console_info);
return (0);
}