Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/hid/hid.c
Show All 26 Lines | |||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
* POSSIBILITY OF SUCH DAMAGE. | * POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
#ifdef USB_GLOBAL_INCLUDE_FILE | #include "opt_hid.h" | ||||
#include USB_GLOBAL_INCLUDE_FILE | |||||
#else | |||||
#include <sys/stdint.h> | |||||
#include <sys/stddef.h> | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/queue.h> | |||||
#include <sys/types.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/kdb.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/lock.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/condvar.h> | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/sx.h> | |||||
#include <sys/unistd.h> | |||||
#include <sys/callout.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/priv.h> | |||||
#include <dev/usb/usb.h> | #define HID_DEBUG_VAR hid_debug | ||||
#include <dev/usb/usbdi.h> | #include <dev/hid/hid.h> | ||||
#include <dev/usb/usbdi_util.h> | #include <dev/hid/hidquirk.h> | ||||
#include <dev/usb/usbhid.h> | |||||
#define USB_DEBUG_VAR usb_debug | /* | ||||
* Define this unconditionally in case a kernel module is loaded that | |||||
* has been compiled with debugging options. | |||||
*/ | |||||
int hid_debug = 0; | |||||
#include <dev/usb/usb_core.h> | SYSCTL_NODE(_hw, OID_AUTO, hid, CTLFLAG_RW, 0, "HID debugging"); | ||||
#include <dev/usb/usb_debug.h> | SYSCTL_INT(_hw_hid, OID_AUTO, debug, CTLFLAG_RWTUN, | ||||
#include <dev/usb/usb_process.h> | &hid_debug, 0, "Debug level"); | ||||
#include <dev/usb/usb_device.h> | |||||
#include <dev/usb/usb_request.h> | |||||
#endif /* USB_GLOBAL_INCLUDE_FILE */ | |||||
static void hid_clear_local(struct hid_item *); | static void hid_clear_local(struct hid_item *); | ||||
static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); | static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); | ||||
static hid_test_quirk_t hid_test_quirk_w; | |||||
hid_test_quirk_t *hid_test_quirk_p = &hid_test_quirk_w; | |||||
#define MAXUSAGE 64 | #define MAXUSAGE 64 | ||||
#define MAXPUSH 4 | #define MAXPUSH 4 | ||||
#define MAXID 16 | #define MAXID 16 | ||||
#define MAXLOCCNT 2048 | #define MAXLOCCNT 2048 | ||||
struct hid_pos_data { | struct hid_pos_data { | ||||
int32_t rid; | int32_t rid; | ||||
uint32_t pos; | uint32_t pos; | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | if (i != MAXID) { | ||||
c->loc.pos = 0; | c->loc.pos = 0; | ||||
} | } | ||||
} | } | ||||
/*------------------------------------------------------------------------* | /*------------------------------------------------------------------------* | ||||
* hid_start_parse | * hid_start_parse | ||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
struct hid_data * | struct hid_data * | ||||
hid_start_parse(const void *d, usb_size_t len, int kindset) | hid_start_parse(const void *d, hid_size_t len, int kindset) | ||||
{ | { | ||||
struct hid_data *s; | struct hid_data *s; | ||||
if ((kindset-1) & kindset) { | if ((kindset-1) & kindset) { | ||||
DPRINTFN(0, "Only one bit can be " | DPRINTFN(0, "Only one bit can be " | ||||
"set in the kindset\n"); | "set in the kindset\n"); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 377 Lines • ▼ Show 20 Lines | top: | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/*------------------------------------------------------------------------* | /*------------------------------------------------------------------------* | ||||
* hid_report_size | * hid_report_size | ||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
int | int | ||||
hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) | hid_report_size(const void *buf, hid_size_t len, enum hid_kind k, uint8_t id) | ||||
{ | { | ||||
struct hid_data *d; | struct hid_data *d; | ||||
struct hid_item h; | struct hid_item h; | ||||
uint32_t temp; | uint32_t temp; | ||||
uint32_t hpos; | uint32_t hpos; | ||||
uint32_t lpos; | uint32_t lpos; | ||||
int report_id = 0; | |||||
hpos = 0; | |||||
lpos = 0xFFFFFFFF; | |||||
for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { | |||||
if (h.kind == k && h.report_ID == id) { | |||||
/* compute minimum */ | |||||
if (lpos > h.loc.pos) | |||||
lpos = h.loc.pos; | |||||
/* compute end position */ | |||||
temp = h.loc.pos + (h.loc.size * h.loc.count); | |||||
/* compute maximum */ | |||||
if (hpos < temp) | |||||
hpos = temp; | |||||
if (h.report_ID != 0) | |||||
report_id = 1; | |||||
} | |||||
} | |||||
hid_end_parse(d); | |||||
/* safety check - can happen in case of currupt descriptors */ | |||||
if (lpos > hpos) | |||||
temp = 0; | |||||
else | |||||
temp = hpos - lpos; | |||||
/* return length in bytes rounded up */ | |||||
return ((temp + 7) / 8 + report_id); | |||||
} | |||||
int | |||||
hid_report_size_max(const void *buf, hid_size_t len, enum hid_kind k, | |||||
uint8_t *id) | |||||
{ | |||||
struct hid_data *d; | |||||
struct hid_item h; | |||||
uint32_t temp; | |||||
uint32_t hpos; | |||||
uint32_t lpos; | |||||
uint8_t any_id; | uint8_t any_id; | ||||
any_id = 0; | any_id = 0; | ||||
hpos = 0; | hpos = 0; | ||||
lpos = 0xFFFFFFFF; | lpos = 0xFFFFFFFF; | ||||
for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { | for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { | ||||
if (h.kind == k) { | if (h.kind == k) { | ||||
Show All 30 Lines | hid_report_size_max(const void *buf, hid_size_t len, enum hid_kind k, | ||||
/* return length in bytes rounded up */ | /* return length in bytes rounded up */ | ||||
return ((temp + 7) / 8); | return ((temp + 7) / 8); | ||||
} | } | ||||
/*------------------------------------------------------------------------* | /*------------------------------------------------------------------------* | ||||
* hid_locate | * hid_locate | ||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
int | int | ||||
hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k, | hid_locate(const void *desc, hid_size_t size, int32_t u, enum hid_kind k, | ||||
uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) | uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) | ||||
{ | { | ||||
struct hid_data *d; | struct hid_data *d; | ||||
struct hid_item h; | struct hid_item h; | ||||
int i; | int i; | ||||
for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { | for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { | ||||
for (i = 0; i < h.nusages; i++) { | for (i = 0; i < h.nusages; i++) { | ||||
Show All 20 Lines | hid_locate(const void *desc, hid_size_t size, int32_t u, enum hid_kind k, | ||||
hid_end_parse(d); | hid_end_parse(d); | ||||
return (0); | return (0); | ||||
} | } | ||||
/*------------------------------------------------------------------------* | /*------------------------------------------------------------------------* | ||||
* hid_get_data | * hid_get_data | ||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
static uint32_t | static uint32_t | ||||
hid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc, | hid_get_data_sub(const uint8_t *buf, hid_size_t len, struct hid_location *loc, | ||||
int is_signed) | int is_signed) | ||||
{ | { | ||||
uint32_t hpos = loc->pos; | uint32_t hpos = loc->pos; | ||||
uint32_t hsize = loc->size; | uint32_t hsize = loc->size; | ||||
uint32_t data; | uint32_t data; | ||||
uint32_t rpos; | uint32_t rpos; | ||||
uint8_t n; | uint8_t n; | ||||
Show All 27 Lines | else | ||||
data = (uint32_t)((uint32_t)data << n) >> n; | data = (uint32_t)((uint32_t)data << n) >> n; | ||||
DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", | DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", | ||||
loc->pos, loc->size, (long)data); | loc->pos, loc->size, (long)data); | ||||
return (data); | return (data); | ||||
} | } | ||||
int32_t | int32_t | ||||
hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc) | hid_get_data(const uint8_t *buf, hid_size_t len, struct hid_location *loc) | ||||
{ | { | ||||
return (hid_get_data_sub(buf, len, loc, 1)); | return (hid_get_data_sub(buf, len, loc, 1)); | ||||
} | } | ||||
uint32_t | uint32_t | ||||
hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc) | hid_get_udata(const uint8_t *buf, hid_size_t len, struct hid_location *loc) | ||||
{ | { | ||||
return (hid_get_data_sub(buf, len, loc, 0)); | return (hid_get_data_sub(buf, len, loc, 0)); | ||||
} | } | ||||
/*------------------------------------------------------------------------* | /*------------------------------------------------------------------------* | ||||
* hid_put_data | * hid_put_data | ||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
void | void | ||||
hid_put_data_unsigned(uint8_t *buf, usb_size_t len, | hid_put_udata(uint8_t *buf, hid_size_t len, | ||||
struct hid_location *loc, unsigned int value) | struct hid_location *loc, unsigned int value) | ||||
{ | { | ||||
uint32_t hpos = loc->pos; | uint32_t hpos = loc->pos; | ||||
uint32_t hsize = loc->size; | uint32_t hsize = loc->size; | ||||
uint64_t data; | uint64_t data; | ||||
uint64_t mask; | uint64_t mask; | ||||
uint32_t rpos; | uint32_t rpos; | ||||
uint8_t n; | uint8_t n; | ||||
Show All 20 Lines | while (n--) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/*------------------------------------------------------------------------* | /*------------------------------------------------------------------------* | ||||
* hid_is_collection | * hid_is_collection | ||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
int | int | ||||
hid_is_collection(const void *desc, usb_size_t size, int32_t usage) | hid_is_collection(const void *desc, hid_size_t size, int32_t usage) | ||||
{ | { | ||||
struct hid_data *hd; | struct hid_data *hd; | ||||
struct hid_item hi; | struct hid_item hi; | ||||
int err; | int err; | ||||
hd = hid_start_parse(desc, size, hid_input); | hd = hid_start_parse(desc, size, hid_input); | ||||
if (hd == NULL) | if (hd == NULL) | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 150 Lines • ▼ Show 20 Lines | |||||
*------------------------------------------------------------------------*/ | *------------------------------------------------------------------------*/ | ||||
int | int | ||||
hid_is_keyboard(const void *d_ptr, uint16_t d_len) | hid_is_keyboard(const void *d_ptr, uint16_t d_len) | ||||
{ | { | ||||
if (hid_is_collection(d_ptr, d_len, | if (hid_is_collection(d_ptr, d_len, | ||||
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) | HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) | ||||
return (1); | return (1); | ||||
return (0); | return (0); | ||||
} | |||||
/*------------------------------------------------------------------------* | |||||
* hid_test_quirk - test a device for a given quirk | |||||
* | |||||
* Return values: | |||||
* false: The HID device does not have the given quirk. | |||||
* true: The HID device has the given quirk. | |||||
*------------------------------------------------------------------------*/ | |||||
bool | |||||
hid_test_quirk(const struct hid_device_info *dev_info, uint16_t quirk) | |||||
{ | |||||
bool found; | |||||
uint8_t x; | |||||
if (quirk == HQ_NONE) | |||||
return (false); | |||||
/* search the automatic per device quirks first */ | |||||
for (x = 0; x != HID_MAX_AUTO_QUIRK; x++) { | |||||
if (dev_info->autoQuirk[x] == quirk) | |||||
return (true); | |||||
} | |||||
/* search global quirk table, if any */ | |||||
found = (hid_test_quirk_p) (dev_info, quirk); | |||||
return (found); | |||||
} | |||||
static bool | |||||
hid_test_quirk_w(const struct hid_device_info *dev_info, uint16_t quirk) | |||||
{ | |||||
return (false); /* no match */ | |||||
} | |||||
int | |||||
hid_add_dynamic_quirk(struct hid_device_info *dev_info, uint16_t quirk) | |||||
{ | |||||
uint8_t x; | |||||
for (x = 0; x != HID_MAX_AUTO_QUIRK; x++) { | |||||
if (dev_info->autoQuirk[x] == 0 || | |||||
dev_info->autoQuirk[x] == quirk) { | |||||
dev_info->autoQuirk[x] = quirk; | |||||
return (0); /* success */ | |||||
} | |||||
} | |||||
return (ENOSPC); | |||||
} | |||||
void | |||||
hid_quirk_unload(void *arg) | |||||
{ | |||||
/* reset function pointer */ | |||||
hid_test_quirk_p = &hid_test_quirk_w; | |||||
#ifdef NOT_YET | |||||
hidquirk_ioctl_p = &hidquirk_ioctl_w; | |||||
#endif | |||||
/* wait for CPU to exit the loaded functions, if any */ | |||||
/* XXX this is a tradeoff */ | |||||
pause("WAIT", hz); | |||||
} | } | ||||
MODULE_VERSION(hid, 1); | MODULE_VERSION(hid, 1); |