Skip to content

Commit

Permalink
OS-5887 want ugen nodes for devices attached by hid
Browse files Browse the repository at this point in the history
Reviewed by: Robert Mustacchi <rm@joyent.com>
Approved by: Joshua M. Clulow <jmc@joyent.com>
  • Loading branch information
arekinath committed Jan 10, 2017
1 parent a20de2b commit ef863da
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 15 deletions.
212 changes: 204 additions & 8 deletions usr/src/uts/common/io/usb/clients/hid/hid.c
Expand Up @@ -21,7 +21,7 @@

/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2016 Joyent, Inc.
* Copyright 2017 Joyent, Inc.
*/


Expand Down Expand Up @@ -139,6 +139,12 @@ 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);
/* These are to enable ugen support: */
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 **);

/*
* Warlock is not aware of the automatic locking mechanisms for
Expand Down Expand Up @@ -198,18 +204,18 @@ struct streamtab hid_streamtab = {
};

struct cb_ops hid_cb_ops = {
nulldev, /* open */
nulldev, /* close */
hid_chropen, /* open */
hid_chrclose, /* close */
nulldev, /* strategy */
nulldev, /* print */
nulldev, /* dump */
nulldev, /* read */
nulldev, /* write */
hid_read, /* read */
hid_write, /* write */
nulldev, /* ioctl */
nulldev, /* devmap */
nulldev, /* mmap */
nulldev, /* segmap */
nochpoll, /* poll */
hid_poll, /* poll */
ddi_prop_op, /* cb_prop_op */
&hid_streamtab, /* streamtab */
D_MP | D_MTPERQ
Expand Down Expand Up @@ -349,6 +355,7 @@ hid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
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:
Expand Down Expand Up @@ -490,6 +497,28 @@ hid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
usb_free_dev_data(dip, dev_data);
hidp->hid_dev_data = NULL;

if (usb_owns_device(dip)) {
/* Get a ugen handle. */
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;
}
}

/*
* Don't get the report descriptor if parsing hid descriptor earlier
* failed since device probably won't return valid report descriptor
Expand Down Expand Up @@ -768,6 +797,149 @@ hid_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
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);
}

/*
* hid_open :
* Open entry point: Opens the interrupt pipe. Sets up queues.
Expand All @@ -786,13 +958,21 @@ hid_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)

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 this is a ugen device, return ENOSTR (no streams). This will
* cause spec_open to try hid_chropen from our regular ops_cb instead
* (and thus treat us as a plain character device).
*/
if (HID_IS_UGEN_OPEN(minor)) {
return (ENOSTR);
}

if (sflag) {
/* clone open NOT supported here */
return (ENXIO);
Expand All @@ -802,6 +982,8 @@ hid_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
return (EIO);
}

mutex_enter(&hidp->hid_mutex);

/*
* This is a workaround:
* Currently, if we open an already disconnected device, and send
Expand All @@ -811,7 +993,6 @@ hid_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
* The consconfig_dacf module need this interface to detect if the
* device is already disconnnected.
*/
mutex_enter(&hidp->hid_mutex);
if (HID_IS_INTERNAL_OPEN(minor) &&
(hidp->hid_dev_state == USB_DEV_DISCONNECTED)) {
mutex_exit(&hidp->hid_mutex);
Expand Down Expand Up @@ -1687,6 +1868,11 @@ hid_cpr_suspend(hid_state_t *hidp)
}
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);
}

Expand All @@ -1698,6 +1884,10 @@ hid_cpr_resume(hid_state_t *hidp)
"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);
}
}


Expand Down Expand Up @@ -2135,6 +2325,12 @@ hid_detach_cleanup(dev_info_t *dip, hid_state_t *hidp)
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) {
Expand Down
19 changes: 13 additions & 6 deletions usr/src/uts/common/sys/usb/clients/hid/hidminor.h
Expand Up @@ -20,7 +20,7 @@
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2017 Joyent, Inc.
*/

#ifndef _SYS_USB_HIDMINOR_H
Expand All @@ -44,21 +44,28 @@ extern "C" {
* transparent.
*
* So we change minor node numbering scheme to be:
* external node minor num == instance << 1
* internal node minor num == instance << 1 | 0x1
* external node minor num == instance << 9
* internal node minor num == instance << 9 | 0x100
* (There are only internal nodes for keyboard/mouse now.)
*
* The 8 bits of the LSB are used for ugen minor numbering (hence the use
* of the first bit of the next byte for the "internal" flag)
*/
#define HID_MINOR_BITS_MASK 0x1
#define HID_MINOR_BITS_MASK 0x1ff
#define HID_MINOR_UGEN_BITS_MASK 0xff
#define HID_MINOR_INSTANCE_MASK ~HID_MINOR_BITS_MASK
#define HID_MINOR_INSTANCE_SHIFT 1
#define HID_MINOR_INSTANCE_SHIFT 9

#define HID_MINOR_INTERNAL 0x1
#define HID_MINOR_INTERNAL 0x100
#define HID_MINOR_MAKE_INTERNAL(minor) \
((minor) | HID_MINOR_INTERNAL)

#define HID_IS_INTERNAL_OPEN(minor) \
(((minor) & HID_MINOR_INTERNAL))

#define HID_IS_UGEN_OPEN(minor) \
(((minor) & HID_MINOR_UGEN_BITS_MASK))

#define HID_MINOR_TO_INSTANCE(minor) \
(((minor) & HID_MINOR_INSTANCE_MASK) >> \
HID_MINOR_INSTANCE_SHIFT)
Expand Down
5 changes: 4 additions & 1 deletion usr/src/uts/common/sys/usb/clients/hid/hidvar.h
Expand Up @@ -21,7 +21,7 @@

/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2017 Joyent, Inc.
*/

#ifndef _SYS_USB_HIDVAR_H
Expand All @@ -33,6 +33,7 @@ extern "C" {
#endif

#include <sys/usb/usba/usbai_private.h>
#include <sys/usb/usba/usba_ugen.h>

/*
* HID : This header file contains the internal structures
Expand Down Expand Up @@ -222,6 +223,8 @@ typedef struct hid_state {
queue_t *hid_inuse_rq;
int hid_internal_flag; /* see below */
int hid_external_flag; /* see below */

usb_ugen_hdl_t hid_ugen_hdl; /* ugen support */
} hid_state_t;

/* warlock directives, stable data */
Expand Down

0 comments on commit ef863da

Please sign in to comment.