mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-07-06 06:21:31 +00:00
HID: hid, make parsing event driven
Next step for complete hid bus, this patch includes: - call parser either from probe or from hid-core if there is no probe. - add ll_driver structure and centralize some stuff there (open, close...) - split and merge usb_hid_configure and hid_probe into several functions to allow hooks/fixes between them Signed-off-by: Jiri Slaby <jslaby@suse.cz> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
85cdaf524b
commit
c500c97140
7 changed files with 440 additions and 241 deletions
|
@ -648,6 +648,9 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
|
||||||
hid_parser_reserved
|
hid_parser_reserved
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (device->driver->report_fixup)
|
||||||
|
device->driver->report_fixup(device, start, size);
|
||||||
|
|
||||||
device->rdesc = kmalloc(size, GFP_KERNEL);
|
device->rdesc = kmalloc(size, GFP_KERNEL);
|
||||||
if (device->rdesc == NULL)
|
if (device->rdesc == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1152,15 +1155,20 @@ static int hid_device_probe(struct device *dev)
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!hdev->driver) {
|
if (!hdev->driver) {
|
||||||
if (hdrv->probe) {
|
id = hid_match_id(hdev, hdrv->id_table);
|
||||||
ret = -ENODEV;
|
if (id == NULL)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
id = hid_match_id(hdev, hdrv->id_table);
|
hdev->driver = hdrv;
|
||||||
if (id)
|
if (hdrv->probe) {
|
||||||
ret = hdrv->probe(hdev, id);
|
ret = hdrv->probe(hdev, id);
|
||||||
|
} else { /* default probe */
|
||||||
|
ret = hid_parse(hdev);
|
||||||
|
if (!ret)
|
||||||
|
ret = hid_hw_start(hdev);
|
||||||
}
|
}
|
||||||
if (!ret)
|
if (ret)
|
||||||
hdev->driver = hdrv;
|
hdev->driver = NULL;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1173,6 +1181,8 @@ static int hid_device_remove(struct device *dev)
|
||||||
if (hdrv) {
|
if (hdrv) {
|
||||||
if (hdrv->remove)
|
if (hdrv->remove)
|
||||||
hdrv->remove(hdev);
|
hdrv->remove(hdev);
|
||||||
|
else /* default remove */
|
||||||
|
hid_hw_stop(hdev);
|
||||||
hdev->driver = NULL;
|
hdev->driver = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -390,6 +390,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||||
if (ret)
|
if (ret)
|
||||||
goto mapped;
|
goto mapped;
|
||||||
|
|
||||||
|
if (device->driver->input_mapping) {
|
||||||
|
int ret = device->driver->input_mapping(device, hidinput, field,
|
||||||
|
usage, &bit, &max);
|
||||||
|
if (ret > 0)
|
||||||
|
goto mapped;
|
||||||
|
if (ret < 0)
|
||||||
|
goto ignore;
|
||||||
|
}
|
||||||
|
|
||||||
switch (usage->hid & HID_USAGE_PAGE) {
|
switch (usage->hid & HID_USAGE_PAGE) {
|
||||||
|
|
||||||
case HID_UP_UNDEFINED:
|
case HID_UP_UNDEFINED:
|
||||||
|
@ -755,6 +764,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped:
|
mapped:
|
||||||
|
if (device->driver->input_mapped && device->driver->input_mapped(device,
|
||||||
|
hidinput, field, usage, &bit, &max) < 0)
|
||||||
|
goto ignore;
|
||||||
|
|
||||||
if (device->quirks & HID_QUIRK_MIGHTYMOUSE) {
|
if (device->quirks & HID_QUIRK_MIGHTYMOUSE) {
|
||||||
if (usage->hid == HID_GD_Z)
|
if (usage->hid == HID_GD_Z)
|
||||||
map_rel(REL_HWHEEL);
|
map_rel(REL_HWHEEL);
|
||||||
|
@ -961,14 +974,14 @@ static int hidinput_open(struct input_dev *dev)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = input_get_drvdata(dev);
|
struct hid_device *hid = input_get_drvdata(dev);
|
||||||
|
|
||||||
return hid->hid_open(hid);
|
return hid->ll_driver->open(hid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hidinput_close(struct input_dev *dev)
|
static void hidinput_close(struct input_dev *dev)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = input_get_drvdata(dev);
|
struct hid_device *hid = input_get_drvdata(dev);
|
||||||
|
|
||||||
hid->hid_close(hid);
|
hid->ll_driver->close(hid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1019,7 +1032,8 @@ int hidinput_connect(struct hid_device *hid)
|
||||||
}
|
}
|
||||||
|
|
||||||
input_set_drvdata(input_dev, hid);
|
input_set_drvdata(input_dev, hid);
|
||||||
input_dev->event = hid->hidinput_input_event;
|
input_dev->event =
|
||||||
|
hid->ll_driver->hidinput_input_event;
|
||||||
input_dev->open = hidinput_open;
|
input_dev->open = hidinput_open;
|
||||||
input_dev->close = hidinput_close;
|
input_dev->close = hidinput_close;
|
||||||
input_dev->setkeycode = hidinput_setkeycode;
|
input_dev->setkeycode = hidinput_setkeycode;
|
||||||
|
|
|
@ -181,7 +181,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
|
||||||
|
|
||||||
dev = hidraw_table[minor];
|
dev = hidraw_table[minor];
|
||||||
if (!dev->open++)
|
if (!dev->open++)
|
||||||
dev->hid->hid_open(dev->hid);
|
dev->hid->ll_driver->open(dev->hid);
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
spin_unlock(&minors_lock);
|
spin_unlock(&minors_lock);
|
||||||
|
@ -207,7 +207,7 @@ static int hidraw_release(struct inode * inode, struct file * file)
|
||||||
dev = hidraw_table[minor];
|
dev = hidraw_table[minor];
|
||||||
if (!dev->open--) {
|
if (!dev->open--) {
|
||||||
if (list->hidraw->exist)
|
if (list->hidraw->exist)
|
||||||
dev->hid->hid_close(dev->hid);
|
dev->hid->ll_driver->close(dev->hid);
|
||||||
else
|
else
|
||||||
kfree(list->hidraw);
|
kfree(list->hidraw);
|
||||||
}
|
}
|
||||||
|
@ -367,7 +367,7 @@ void hidraw_disconnect(struct hid_device *hid)
|
||||||
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
||||||
|
|
||||||
if (hidraw->open) {
|
if (hidraw->open) {
|
||||||
hid->hid_close(hid);
|
hid->ll_driver->close(hid);
|
||||||
wake_up_interruptible(&hidraw->wait);
|
wake_up_interruptible(&hidraw->wait);
|
||||||
} else {
|
} else {
|
||||||
kfree(hidraw);
|
kfree(hidraw);
|
||||||
|
|
|
@ -701,17 +701,84 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
static int usbhid_start_finish(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
|
struct usb_interface *intf = to_usb_interface(hid->dev.parent);
|
||||||
|
char path[64], *type;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
usbhid_init_reports(hid);
|
||||||
|
hid_dump_device(hid);
|
||||||
|
if (hid->quirks & HID_QUIRK_RESET_LEDS)
|
||||||
|
usbhid_set_leds(hid);
|
||||||
|
|
||||||
|
if (!hidinput_connect(hid))
|
||||||
|
hid->claimed |= HID_CLAIMED_INPUT;
|
||||||
|
if (!hiddev_connect(hid))
|
||||||
|
hid->claimed |= HID_CLAIMED_HIDDEV;
|
||||||
|
if (!hidraw_connect(hid))
|
||||||
|
hid->claimed |= HID_CLAIMED_HIDRAW;
|
||||||
|
|
||||||
|
if (!hid->claimed) {
|
||||||
|
printk(KERN_ERR "HID device claimed by neither input, hiddev "
|
||||||
|
"nor hidraw\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((hid->claimed & HID_CLAIMED_INPUT))
|
||||||
|
hid_ff_init(hid);
|
||||||
|
|
||||||
|
if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
|
||||||
|
hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
|
||||||
|
intf->cur_altsetting->desc.bInterfaceNumber);
|
||||||
|
|
||||||
|
printk(KERN_INFO);
|
||||||
|
|
||||||
|
if (hid->claimed & HID_CLAIMED_INPUT)
|
||||||
|
printk("input");
|
||||||
|
if ((hid->claimed & HID_CLAIMED_INPUT) &&
|
||||||
|
((hid->claimed & HID_CLAIMED_HIDDEV) ||
|
||||||
|
hid->claimed & HID_CLAIMED_HIDRAW))
|
||||||
|
printk(",");
|
||||||
|
if (hid->claimed & HID_CLAIMED_HIDDEV)
|
||||||
|
printk("hiddev%d", hid->minor);
|
||||||
|
if ((hid->claimed & HID_CLAIMED_INPUT) &&
|
||||||
|
(hid->claimed & HID_CLAIMED_HIDDEV) &&
|
||||||
|
(hid->claimed & HID_CLAIMED_HIDRAW))
|
||||||
|
printk(",");
|
||||||
|
if (hid->claimed & HID_CLAIMED_HIDRAW)
|
||||||
|
printk("hidraw%d", ((struct hidraw *)hid->hidraw)->minor);
|
||||||
|
|
||||||
|
type = "Device";
|
||||||
|
for (i = 0; i < hid->maxcollection; i++) {
|
||||||
|
if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
|
||||||
|
(hid->collection[i].usage & HID_USAGE_PAGE) ==
|
||||||
|
HID_UP_GENDESK &&
|
||||||
|
(hid->collection[i].usage & 0xffff) <
|
||||||
|
ARRAY_SIZE(hid_types)) {
|
||||||
|
type = hid_types[hid->collection[i].usage & 0xffff];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_make_path(interface_to_usbdev(intf), path, 63);
|
||||||
|
|
||||||
|
printk(": USB HID v%x.%02x %s [%s] on %s\n",
|
||||||
|
hid->version >> 8, hid->version & 0xff, type, hid->name, path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usbhid_parse(struct hid_device *hid)
|
||||||
|
{
|
||||||
|
struct usb_interface *intf = to_usb_interface(hid->dev.parent);
|
||||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||||
struct usb_device *dev = interface_to_usbdev (intf);
|
struct usb_device *dev = interface_to_usbdev (intf);
|
||||||
struct hid_descriptor *hdesc;
|
struct hid_descriptor *hdesc;
|
||||||
struct hid_device *hid;
|
|
||||||
u32 quirks = 0;
|
u32 quirks = 0;
|
||||||
unsigned int insize = 0, rsize = 0;
|
unsigned int rsize = 0;
|
||||||
char *rdesc;
|
char *rdesc;
|
||||||
int n, len;
|
int ret, n;
|
||||||
struct usbhid_device *usbhid;
|
|
||||||
|
|
||||||
quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
|
quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
|
||||||
le16_to_cpu(dev->descriptor.idProduct));
|
le16_to_cpu(dev->descriptor.idProduct));
|
||||||
|
@ -725,40 +792,44 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quirks & HID_QUIRK_IGNORE)
|
if (quirks & HID_QUIRK_IGNORE)
|
||||||
return NULL;
|
return -ENODEV;
|
||||||
|
|
||||||
if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
|
if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
|
||||||
(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
|
(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
|
||||||
return NULL;
|
return -ENODEV;
|
||||||
|
|
||||||
|
|
||||||
if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
|
if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
|
||||||
(!interface->desc.bNumEndpoints ||
|
(!interface->desc.bNumEndpoints ||
|
||||||
usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
|
usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
|
||||||
dbg_hid("class descriptor not present\n");
|
dbg_hid("class descriptor not present\n");
|
||||||
return NULL;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hid->version = le16_to_cpu(hdesc->bcdHID);
|
||||||
|
hid->country = hdesc->bCountryCode;
|
||||||
|
|
||||||
for (n = 0; n < hdesc->bNumDescriptors; n++)
|
for (n = 0; n < hdesc->bNumDescriptors; n++)
|
||||||
if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
|
if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
|
||||||
rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
|
rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
|
||||||
|
|
||||||
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
|
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
|
||||||
dbg_hid("weird size of report descriptor (%u)\n", rsize);
|
dbg_hid("weird size of report descriptor (%u)\n", rsize);
|
||||||
return NULL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
|
if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
|
||||||
dbg_hid("couldn't allocate rdesc memory\n");
|
dbg_hid("couldn't allocate rdesc memory\n");
|
||||||
return NULL;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
|
hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
|
||||||
|
|
||||||
if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
|
ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber,
|
||||||
|
HID_DT_REPORT, rdesc, rsize);
|
||||||
|
if (ret < 0) {
|
||||||
dbg_hid("reading report descriptor failed\n");
|
dbg_hid("reading report descriptor failed\n");
|
||||||
kfree(rdesc);
|
kfree(rdesc);
|
||||||
return NULL;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
|
usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
|
||||||
|
@ -770,24 +841,36 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
||||||
dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
|
dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
|
||||||
dbg_hid_line("\n");
|
dbg_hid_line("\n");
|
||||||
|
|
||||||
hid = hid_allocate_device();
|
ret = hid_parse_report(hid, rdesc, rsize);
|
||||||
if (IS_ERR(hid)) {
|
|
||||||
kfree(rdesc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hid_parse_report(hid, rdesc, n)) {
|
|
||||||
dbg_hid("parsing report descriptor failed\n");
|
|
||||||
hid_destroy_device(hid);
|
|
||||||
kfree(rdesc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(rdesc);
|
kfree(rdesc);
|
||||||
|
if (ret) {
|
||||||
|
dbg_hid("parsing report descriptor failed\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
hid->quirks = quirks;
|
hid->quirks = quirks;
|
||||||
|
|
||||||
if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
|
return 0;
|
||||||
goto fail_no_usbhid;
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usbhid_start(struct hid_device *hid)
|
||||||
|
{
|
||||||
|
struct usb_interface *intf = to_usb_interface(hid->dev.parent);
|
||||||
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||||
|
struct usb_device *dev = interface_to_usbdev(intf);
|
||||||
|
struct usbhid_device *usbhid;
|
||||||
|
unsigned int n, insize = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
WARN_ON(hid->driver_data);
|
||||||
|
|
||||||
|
usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL);
|
||||||
|
if (usbhid == NULL) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
hid->driver_data = usbhid;
|
hid->driver_data = usbhid;
|
||||||
usbhid->hid = hid;
|
usbhid->hid = hid;
|
||||||
|
@ -805,27 +888,12 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
||||||
if (insize > HID_MAX_BUFFER_SIZE)
|
if (insize > HID_MAX_BUFFER_SIZE)
|
||||||
insize = HID_MAX_BUFFER_SIZE;
|
insize = HID_MAX_BUFFER_SIZE;
|
||||||
|
|
||||||
if (hid_alloc_buffers(dev, hid))
|
if (hid_alloc_buffers(dev, hid)) {
|
||||||
|
ret = -ENOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
hid->name[0] = 0;
|
|
||||||
|
|
||||||
if (dev->manufacturer)
|
|
||||||
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
|
|
||||||
|
|
||||||
if (dev->product) {
|
|
||||||
if (dev->manufacturer)
|
|
||||||
strlcat(hid->name, " ", sizeof(hid->name));
|
|
||||||
strlcat(hid->name, dev->product, sizeof(hid->name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strlen(hid->name))
|
|
||||||
snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
|
|
||||||
le16_to_cpu(dev->descriptor.idVendor),
|
|
||||||
le16_to_cpu(dev->descriptor.idProduct));
|
|
||||||
|
|
||||||
for (n = 0; n < interface->desc.bNumEndpoints; n++) {
|
for (n = 0; n < interface->desc.bNumEndpoints; n++) {
|
||||||
|
|
||||||
struct usb_endpoint_descriptor *endpoint;
|
struct usb_endpoint_descriptor *endpoint;
|
||||||
int pipe;
|
int pipe;
|
||||||
int interval;
|
int interval;
|
||||||
|
@ -837,7 +905,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
||||||
interval = endpoint->bInterval;
|
interval = endpoint->bInterval;
|
||||||
|
|
||||||
/* Some vendors give fullspeed interval on highspeed devides */
|
/* Some vendors give fullspeed interval on highspeed devides */
|
||||||
if (quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
|
if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
|
||||||
dev->speed == USB_SPEED_HIGH) {
|
dev->speed == USB_SPEED_HIGH) {
|
||||||
interval = fls(endpoint->bInterval*8);
|
interval = fls(endpoint->bInterval*8);
|
||||||
printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
|
printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
|
||||||
|
@ -848,6 +916,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
||||||
if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
|
if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
|
||||||
interval = hid_mousepoll_interval;
|
interval = hid_mousepoll_interval;
|
||||||
|
|
||||||
|
ret = -ENOMEM;
|
||||||
if (usb_endpoint_dir_in(endpoint)) {
|
if (usb_endpoint_dir_in(endpoint)) {
|
||||||
if (usbhid->urbin)
|
if (usbhid->urbin)
|
||||||
continue;
|
continue;
|
||||||
|
@ -873,6 +942,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
||||||
|
|
||||||
if (!usbhid->urbin) {
|
if (!usbhid->urbin) {
|
||||||
err_hid("couldn't find an input interrupt endpoint");
|
err_hid("couldn't find an input interrupt endpoint");
|
||||||
|
ret = -ENODEV;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,44 +954,26 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
||||||
spin_lock_init(&usbhid->outlock);
|
spin_lock_init(&usbhid->outlock);
|
||||||
spin_lock_init(&usbhid->ctrllock);
|
spin_lock_init(&usbhid->ctrllock);
|
||||||
|
|
||||||
hid->version = le16_to_cpu(hdesc->bcdHID);
|
|
||||||
hid->country = hdesc->bCountryCode;
|
|
||||||
hid->dev.parent = &intf->dev;
|
|
||||||
usbhid->intf = intf;
|
usbhid->intf = intf;
|
||||||
usbhid->ifnum = interface->desc.bInterfaceNumber;
|
usbhid->ifnum = interface->desc.bInterfaceNumber;
|
||||||
|
|
||||||
hid->bus = BUS_USB;
|
|
||||||
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
|
|
||||||
hid->product = le16_to_cpu(dev->descriptor.idProduct);
|
|
||||||
|
|
||||||
usb_make_path(dev, hid->phys, sizeof(hid->phys));
|
|
||||||
strlcat(hid->phys, "/input", sizeof(hid->phys));
|
|
||||||
len = strlen(hid->phys);
|
|
||||||
if (len < sizeof(hid->phys) - 1)
|
|
||||||
snprintf(hid->phys + len, sizeof(hid->phys) - len,
|
|
||||||
"%d", intf->altsetting[0].desc.bInterfaceNumber);
|
|
||||||
|
|
||||||
if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
|
|
||||||
hid->uniq[0] = 0;
|
|
||||||
|
|
||||||
usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
|
usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
|
||||||
if (!usbhid->urbctrl)
|
if (!usbhid->urbctrl) {
|
||||||
|
ret = -ENOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
|
usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
|
||||||
usbhid->ctrlbuf, 1, hid_ctrl, hid);
|
usbhid->ctrlbuf, 1, hid_ctrl, hid);
|
||||||
usbhid->urbctrl->setup_dma = usbhid->cr_dma;
|
usbhid->urbctrl->setup_dma = usbhid->cr_dma;
|
||||||
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
|
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
|
||||||
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
||||||
hid->hidinput_input_event = usb_hidinput_input_event;
|
|
||||||
hid->hid_open = usbhid_open;
|
ret = usbhid_start_finish(hid);
|
||||||
hid->hid_close = usbhid_close;
|
if (ret)
|
||||||
#ifdef CONFIG_USB_HIDDEV
|
goto fail;
|
||||||
hid->hiddev_hid_event = hiddev_hid_event;
|
|
||||||
hid->hiddev_report_event = hiddev_report_event;
|
return 0;
|
||||||
#endif
|
|
||||||
hid->hid_output_raw_report = usbhid_output_raw_report;
|
|
||||||
return hid;
|
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
usb_free_urb(usbhid->urbin);
|
usb_free_urb(usbhid->urbin);
|
||||||
|
@ -929,24 +981,18 @@ fail:
|
||||||
usb_free_urb(usbhid->urbctrl);
|
usb_free_urb(usbhid->urbctrl);
|
||||||
hid_free_buffers(dev, hid);
|
hid_free_buffers(dev, hid);
|
||||||
kfree(usbhid);
|
kfree(usbhid);
|
||||||
fail_no_usbhid:
|
err:
|
||||||
hid_destroy_device(hid);
|
return ret;
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hid_disconnect(struct usb_interface *intf)
|
static void usbhid_stop(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = usb_get_intfdata (intf);
|
struct usbhid_device *usbhid = hid->driver_data;
|
||||||
struct usbhid_device *usbhid;
|
|
||||||
|
|
||||||
if (!hid)
|
if (WARN_ON(!usbhid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
usbhid = hid->driver_data;
|
|
||||||
|
|
||||||
spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
|
spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
|
||||||
usb_set_intfdata(intf, NULL);
|
|
||||||
set_bit(HID_DISCONNECTED, &usbhid->iofl);
|
set_bit(HID_DISCONNECTED, &usbhid->iofl);
|
||||||
spin_unlock_irq(&usbhid->inlock);
|
spin_unlock_irq(&usbhid->inlock);
|
||||||
usb_kill_urb(usbhid->urbin);
|
usb_kill_urb(usbhid->urbin);
|
||||||
|
@ -963,93 +1009,99 @@ static void hid_disconnect(struct usb_interface *intf)
|
||||||
if (hid->claimed & HID_CLAIMED_HIDRAW)
|
if (hid->claimed & HID_CLAIMED_HIDRAW)
|
||||||
hidraw_disconnect(hid);
|
hidraw_disconnect(hid);
|
||||||
|
|
||||||
|
hid->claimed = 0;
|
||||||
|
|
||||||
usb_free_urb(usbhid->urbin);
|
usb_free_urb(usbhid->urbin);
|
||||||
usb_free_urb(usbhid->urbctrl);
|
usb_free_urb(usbhid->urbctrl);
|
||||||
usb_free_urb(usbhid->urbout);
|
usb_free_urb(usbhid->urbout);
|
||||||
|
|
||||||
hid_free_buffers(hid_to_usb_dev(hid), hid);
|
hid_free_buffers(hid_to_usb_dev(hid), hid);
|
||||||
kfree(usbhid);
|
kfree(usbhid);
|
||||||
hid_destroy_device(hid);
|
hid->driver_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct hid_ll_driver usb_hid_driver = {
|
||||||
|
.parse = usbhid_parse,
|
||||||
|
.start = usbhid_start,
|
||||||
|
.stop = usbhid_stop,
|
||||||
|
.open = usbhid_open,
|
||||||
|
.close = usbhid_close,
|
||||||
|
.hidinput_input_event = usb_hidinput_input_event,
|
||||||
|
};
|
||||||
|
|
||||||
static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
|
struct usb_device *dev = interface_to_usbdev(intf);
|
||||||
struct hid_device *hid;
|
struct hid_device *hid;
|
||||||
char path[64];
|
size_t len;
|
||||||
int i, ret;
|
int ret;
|
||||||
char *c;
|
|
||||||
|
|
||||||
dbg_hid("HID probe called for ifnum %d\n",
|
dbg_hid("HID probe called for ifnum %d\n",
|
||||||
intf->altsetting->desc.bInterfaceNumber);
|
intf->altsetting->desc.bInterfaceNumber);
|
||||||
|
|
||||||
if (!(hid = usb_hid_configure(intf)))
|
hid = hid_allocate_device();
|
||||||
return -ENODEV;
|
if (IS_ERR(hid))
|
||||||
|
return PTR_ERR(hid);
|
||||||
usbhid_init_reports(hid);
|
|
||||||
hid_dump_device(hid);
|
|
||||||
if (hid->quirks & HID_QUIRK_RESET_LEDS)
|
|
||||||
usbhid_set_leds(hid);
|
|
||||||
|
|
||||||
if (!hidinput_connect(hid))
|
|
||||||
hid->claimed |= HID_CLAIMED_INPUT;
|
|
||||||
if (!hiddev_connect(hid))
|
|
||||||
hid->claimed |= HID_CLAIMED_HIDDEV;
|
|
||||||
if (!hidraw_connect(hid))
|
|
||||||
hid->claimed |= HID_CLAIMED_HIDRAW;
|
|
||||||
|
|
||||||
usb_set_intfdata(intf, hid);
|
usb_set_intfdata(intf, hid);
|
||||||
|
hid->ll_driver = &usb_hid_driver;
|
||||||
|
hid->hid_output_raw_report = usbhid_output_raw_report;
|
||||||
|
#ifdef CONFIG_USB_HIDDEV
|
||||||
|
hid->hiddev_hid_event = hiddev_hid_event;
|
||||||
|
hid->hiddev_report_event = hiddev_report_event;
|
||||||
|
#endif
|
||||||
|
hid->dev.parent = &intf->dev;
|
||||||
|
hid->bus = BUS_USB;
|
||||||
|
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||||
|
hid->product = le16_to_cpu(dev->descriptor.idProduct);
|
||||||
|
hid->name[0] = 0;
|
||||||
|
|
||||||
if (!hid->claimed) {
|
if (dev->manufacturer)
|
||||||
printk ("HID device claimed by neither input, hiddev nor hidraw\n");
|
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
|
||||||
hid_disconnect(intf);
|
|
||||||
return -ENODEV;
|
if (dev->product) {
|
||||||
|
if (dev->manufacturer)
|
||||||
|
strlcat(hid->name, " ", sizeof(hid->name));
|
||||||
|
strlcat(hid->name, dev->product, sizeof(hid->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((hid->claimed & HID_CLAIMED_INPUT))
|
if (!strlen(hid->name))
|
||||||
hid_ff_init(hid);
|
snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
|
||||||
|
le16_to_cpu(dev->descriptor.idVendor),
|
||||||
|
le16_to_cpu(dev->descriptor.idProduct));
|
||||||
|
|
||||||
if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
|
usb_make_path(dev, hid->phys, sizeof(hid->phys));
|
||||||
hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
|
strlcat(hid->phys, "/input", sizeof(hid->phys));
|
||||||
intf->cur_altsetting->desc.bInterfaceNumber);
|
len = strlen(hid->phys);
|
||||||
|
if (len < sizeof(hid->phys) - 1)
|
||||||
|
snprintf(hid->phys + len, sizeof(hid->phys) - len,
|
||||||
|
"%d", intf->altsetting[0].desc.bInterfaceNumber);
|
||||||
|
|
||||||
printk(KERN_INFO);
|
if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
|
||||||
|
hid->uniq[0] = 0;
|
||||||
if (hid->claimed & HID_CLAIMED_INPUT)
|
|
||||||
printk("input");
|
|
||||||
if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
|
|
||||||
hid->claimed & HID_CLAIMED_HIDRAW))
|
|
||||||
printk(",");
|
|
||||||
if (hid->claimed & HID_CLAIMED_HIDDEV)
|
|
||||||
printk("hiddev%d", hid->minor);
|
|
||||||
if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
|
|
||||||
(hid->claimed & HID_CLAIMED_HIDRAW))
|
|
||||||
printk(",");
|
|
||||||
if (hid->claimed & HID_CLAIMED_HIDRAW)
|
|
||||||
printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
|
|
||||||
|
|
||||||
c = "Device";
|
|
||||||
for (i = 0; i < hid->maxcollection; i++) {
|
|
||||||
if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
|
|
||||||
(hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
|
|
||||||
(hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
|
|
||||||
c = hid_types[hid->collection[i].usage & 0xffff];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usb_make_path(interface_to_usbdev(intf), path, 63);
|
|
||||||
|
|
||||||
printk(": USB HID v%x.%02x %s [%s] on %s\n",
|
|
||||||
hid->version >> 8, hid->version & 0xff, c, hid->name, path);
|
|
||||||
|
|
||||||
ret = hid_add_device(hid);
|
ret = hid_add_device(hid);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&intf->dev, "can't add hid device: %d\n", ret);
|
dev_err(&intf->dev, "can't add hid device: %d\n", ret);
|
||||||
hid_disconnect(intf);
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
hid_destroy_device(hid);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hid_disconnect(struct usb_interface *intf)
|
||||||
|
{
|
||||||
|
struct hid_device *hid = usb_get_intfdata(intf);
|
||||||
|
|
||||||
|
if (WARN_ON(!hid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hid_destroy_device(hid);
|
||||||
|
}
|
||||||
|
|
||||||
static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = usb_get_intfdata (intf);
|
struct hid_device *hid = usb_get_intfdata (intf);
|
||||||
|
|
|
@ -419,6 +419,7 @@ struct hid_control_fifo {
|
||||||
#define HID_CLAIMED_HIDRAW 4
|
#define HID_CLAIMED_HIDRAW 4
|
||||||
|
|
||||||
#define HID_STAT_ADDED 1
|
#define HID_STAT_ADDED 1
|
||||||
|
#define HID_STAT_PARSED 2
|
||||||
|
|
||||||
#define HID_CTRL_RUNNING 1
|
#define HID_CTRL_RUNNING 1
|
||||||
#define HID_OUT_RUNNING 2
|
#define HID_OUT_RUNNING 2
|
||||||
|
@ -435,6 +436,7 @@ struct hid_input {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hid_driver;
|
struct hid_driver;
|
||||||
|
struct hid_ll_driver;
|
||||||
|
|
||||||
struct hid_device { /* device report descriptor */
|
struct hid_device { /* device report descriptor */
|
||||||
__u8 *rdesc;
|
__u8 *rdesc;
|
||||||
|
@ -452,6 +454,7 @@ struct hid_device { /* device report descriptor */
|
||||||
|
|
||||||
struct device dev; /* device */
|
struct device dev; /* device */
|
||||||
struct hid_driver *driver;
|
struct hid_driver *driver;
|
||||||
|
struct hid_ll_driver *ll_driver;
|
||||||
|
|
||||||
unsigned int status; /* see STAT flags above */
|
unsigned int status; /* see STAT flags above */
|
||||||
unsigned claimed; /* Claimed by hidinput, hiddev? */
|
unsigned claimed; /* Claimed by hidinput, hiddev? */
|
||||||
|
@ -471,11 +474,6 @@ struct hid_device { /* device report descriptor */
|
||||||
|
|
||||||
__s32 delayed_value; /* For A4 Tech mice hwheel quirk */
|
__s32 delayed_value; /* For A4 Tech mice hwheel quirk */
|
||||||
|
|
||||||
/* device-specific function pointers */
|
|
||||||
int (*hidinput_input_event) (struct input_dev *, unsigned int, unsigned int, int);
|
|
||||||
int (*hid_open) (struct hid_device *);
|
|
||||||
void (*hid_close) (struct hid_device *);
|
|
||||||
|
|
||||||
/* hiddev event handler */
|
/* hiddev event handler */
|
||||||
void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field,
|
void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field,
|
||||||
struct hid_usage *, __s32);
|
struct hid_usage *, __s32);
|
||||||
|
@ -561,9 +559,22 @@ struct hid_usage_id {
|
||||||
* @raw_event: if report in report_table, this hook is called (NULL means nop)
|
* @raw_event: if report in report_table, this hook is called (NULL means nop)
|
||||||
* @usage_table: on which events to call event (NULL means all)
|
* @usage_table: on which events to call event (NULL means all)
|
||||||
* @event: if usage in usage_table, this hook is called (NULL means nop)
|
* @event: if usage in usage_table, this hook is called (NULL means nop)
|
||||||
|
* @report_fixup: called before report descriptor parsing (NULL means nop)
|
||||||
|
* @input_mapping: invoked on input registering before mapping an usage
|
||||||
|
* @input_mapped: invoked on input registering after mapping an usage
|
||||||
*
|
*
|
||||||
* raw_event and event should return 0 on no action performed, 1 when no
|
* raw_event and event should return 0 on no action performed, 1 when no
|
||||||
* further processing should be done and negative on error
|
* further processing should be done and negative on error
|
||||||
|
*
|
||||||
|
* input_mapping shall return a negative value to completely ignore this usage
|
||||||
|
* (e.g. doubled or invalid usage), zero to continue with parsing of this
|
||||||
|
* usage by generic code (no special handling needed) or positive to skip
|
||||||
|
* generic parsing (needed special handling which was done in the hook already)
|
||||||
|
* input_mapped shall return negative to inform the layer that this usage
|
||||||
|
* should not be considered for further processing or zero to notify that
|
||||||
|
* no processing was performed and should be done in a generic manner
|
||||||
|
* Both these functions may be NULL which means the same behavior as returning
|
||||||
|
* zero from them.
|
||||||
*/
|
*/
|
||||||
struct hid_driver {
|
struct hid_driver {
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -578,10 +589,43 @@ struct hid_driver {
|
||||||
const struct hid_usage_id *usage_table;
|
const struct hid_usage_id *usage_table;
|
||||||
int (*event)(struct hid_device *hdev, struct hid_field *field,
|
int (*event)(struct hid_device *hdev, struct hid_field *field,
|
||||||
struct hid_usage *usage, __s32 value);
|
struct hid_usage *usage, __s32 value);
|
||||||
|
|
||||||
|
void (*report_fixup)(struct hid_device *hdev, __u8 *buf,
|
||||||
|
unsigned int size);
|
||||||
|
|
||||||
|
int (*input_mapping)(struct hid_device *hdev,
|
||||||
|
struct hid_input *hidinput, struct hid_field *field,
|
||||||
|
struct hid_usage *usage, unsigned long **bit, int *max);
|
||||||
|
int (*input_mapped)(struct hid_device *hdev,
|
||||||
|
struct hid_input *hidinput, struct hid_field *field,
|
||||||
|
struct hid_usage *usage, unsigned long **bit, int *max);
|
||||||
/* private: */
|
/* private: */
|
||||||
struct device_driver driver;
|
struct device_driver driver;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hid_ll_driver - low level driver callbacks
|
||||||
|
* @start: called on probe to start the device
|
||||||
|
* @stop: called on remove
|
||||||
|
* @open: called by input layer on open
|
||||||
|
* @close: called by input layer on close
|
||||||
|
* @hidinput_input_event: event input event (e.g. ff or leds)
|
||||||
|
* @parse: this method is called only once to parse the device data,
|
||||||
|
* shouldn't allocate anything to not leak memory
|
||||||
|
*/
|
||||||
|
struct hid_ll_driver {
|
||||||
|
int (*start)(struct hid_device *hdev);
|
||||||
|
void (*stop)(struct hid_device *hdev);
|
||||||
|
|
||||||
|
int (*open)(struct hid_device *hdev);
|
||||||
|
void (*close)(struct hid_device *hdev);
|
||||||
|
|
||||||
|
int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
|
||||||
|
unsigned int code, int value);
|
||||||
|
|
||||||
|
int (*parse)(struct hid_device *hdev);
|
||||||
|
};
|
||||||
|
|
||||||
/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
|
/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
|
||||||
/* We ignore a few input applications that are not widely used */
|
/* We ignore a few input applications that are not widely used */
|
||||||
#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002))
|
#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002))
|
||||||
|
@ -618,6 +662,56 @@ void hid_output_report(struct hid_report *report, __u8 *data);
|
||||||
struct hid_device *hid_allocate_device(void);
|
struct hid_device *hid_allocate_device(void);
|
||||||
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
|
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hid_parse - parse HW reports
|
||||||
|
*
|
||||||
|
* @hdev: hid device
|
||||||
|
*
|
||||||
|
* Call this from probe after you set up the device (if needed). Your
|
||||||
|
* report_fixup will be called (if non-NULL) after reading raw report from
|
||||||
|
* device before passing it to hid layer for real parsing.
|
||||||
|
*/
|
||||||
|
static inline int __must_check hid_parse(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (hdev->status & HID_STAT_PARSED)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = hdev->ll_driver->parse(hdev);
|
||||||
|
if (!ret)
|
||||||
|
hdev->status |= HID_STAT_PARSED;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hid_hw_start - start underlaying HW
|
||||||
|
*
|
||||||
|
* @hdev: hid device
|
||||||
|
*
|
||||||
|
* Call this in probe function *after* hid_parse. This will setup HW buffers
|
||||||
|
* and start the device (if not deffered to device open). hid_hw_stop must be
|
||||||
|
* called if this was successfull.
|
||||||
|
*/
|
||||||
|
static inline int __must_check hid_hw_start(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
return hdev->ll_driver->start(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hid_hw_stop - stop underlaying HW
|
||||||
|
*
|
||||||
|
* @hdev: hid device
|
||||||
|
*
|
||||||
|
* This is usually called from remove function or from probe when something
|
||||||
|
* failed and hid_hw_start was called already.
|
||||||
|
*/
|
||||||
|
static inline void hid_hw_stop(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
hdev->ll_driver->stop(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
|
void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
|
||||||
int interrupt);
|
int interrupt);
|
||||||
|
|
||||||
|
|
|
@ -623,9 +623,15 @@ static struct device *hidp_get_device(struct hidp_session *session)
|
||||||
static int hidp_setup_input(struct hidp_session *session,
|
static int hidp_setup_input(struct hidp_session *session,
|
||||||
struct hidp_connadd_req *req)
|
struct hidp_connadd_req *req)
|
||||||
{
|
{
|
||||||
struct input_dev *input = session->input;
|
struct input_dev *input;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
input = input_allocate_device();
|
||||||
|
if (!input)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
session->input = input;
|
||||||
|
|
||||||
input_set_drvdata(input, session);
|
input_set_drvdata(input, session);
|
||||||
|
|
||||||
input->name = "Bluetooth HID Boot Protocol Device";
|
input->name = "Bluetooth HID Boot Protocol Device";
|
||||||
|
@ -698,55 +704,117 @@ static void hidp_setup_quirks(struct hid_device *hid)
|
||||||
hid->quirks = hidp_blacklist[n].quirks;
|
hid->quirks = hidp_blacklist[n].quirks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hidp_parse(struct hid_device *hid)
|
||||||
|
{
|
||||||
|
struct hidp_session *session = hid->driver_data;
|
||||||
|
struct hidp_connadd_req *req = session->req;
|
||||||
|
unsigned char *buf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
buf = kmalloc(req->rd_size, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (copy_from_user(buf, req->rd_data, req->rd_size)) {
|
||||||
|
kfree(buf);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hid_parse_report(session->hid, buf, req->rd_size);
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
session->req = NULL;
|
||||||
|
|
||||||
|
hidp_setup_quirks(hid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hidp_start(struct hid_device *hid)
|
||||||
|
{
|
||||||
|
struct hidp_session *session = hid->driver_data;
|
||||||
|
struct hid_report *report;
|
||||||
|
|
||||||
|
list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
|
||||||
|
report_list, list)
|
||||||
|
hidp_send_report(session, report);
|
||||||
|
|
||||||
|
list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
|
||||||
|
report_list, list)
|
||||||
|
hidp_send_report(session, report);
|
||||||
|
|
||||||
|
if (hidinput_connect(hid) == 0)
|
||||||
|
hid->claimed |= HID_CLAIMED_INPUT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hidp_stop(struct hid_device *hid)
|
||||||
|
{
|
||||||
|
struct hidp_session *session = hid->driver_data;
|
||||||
|
|
||||||
|
skb_queue_purge(&session->ctrl_transmit);
|
||||||
|
skb_queue_purge(&session->intr_transmit);
|
||||||
|
|
||||||
|
if (hid->claimed & HID_CLAIMED_INPUT)
|
||||||
|
hidinput_disconnect(hid);
|
||||||
|
hid->claimed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hid_ll_driver hidp_hid_driver = {
|
||||||
|
.parse = hidp_parse,
|
||||||
|
.start = hidp_start,
|
||||||
|
.stop = hidp_stop,
|
||||||
|
.open = hidp_open,
|
||||||
|
.close = hidp_close,
|
||||||
|
.hidinput_input_event = hidp_hidinput_event,
|
||||||
|
};
|
||||||
|
|
||||||
static int hidp_setup_hid(struct hidp_session *session,
|
static int hidp_setup_hid(struct hidp_session *session,
|
||||||
struct hidp_connadd_req *req)
|
struct hidp_connadd_req *req)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = session->hid;
|
struct hid_device *hid;
|
||||||
struct hid_report *report;
|
|
||||||
bdaddr_t src, dst;
|
bdaddr_t src, dst;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
baswap(&src, &bt_sk(session->ctrl_sock->sk)->src);
|
hid = hid_allocate_device();
|
||||||
baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst);
|
if (IS_ERR(hid)) {
|
||||||
|
ret = PTR_ERR(session->hid);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->hid = hid;
|
||||||
|
session->req = req;
|
||||||
hid->driver_data = session;
|
hid->driver_data = session;
|
||||||
|
|
||||||
hid->country = req->country;
|
baswap(&src, &bt_sk(session->ctrl_sock->sk)->src);
|
||||||
|
baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst);
|
||||||
|
|
||||||
hid->bus = BUS_BLUETOOTH;
|
hid->bus = BUS_BLUETOOTH;
|
||||||
hid->vendor = req->vendor;
|
hid->vendor = req->vendor;
|
||||||
hid->product = req->product;
|
hid->product = req->product;
|
||||||
hid->version = req->version;
|
hid->version = req->version;
|
||||||
|
hid->country = req->country;
|
||||||
|
|
||||||
strncpy(hid->name, req->name, 128);
|
strncpy(hid->name, req->name, 128);
|
||||||
strncpy(hid->phys, batostr(&src), 64);
|
strncpy(hid->phys, batostr(&src), 64);
|
||||||
strncpy(hid->uniq, batostr(&dst), 64);
|
strncpy(hid->uniq, batostr(&dst), 64);
|
||||||
|
|
||||||
hid->dev.parent = hidp_get_device(session);
|
hid->dev.parent = hidp_get_device(session);
|
||||||
|
hid->ll_driver = &hidp_hid_driver;
|
||||||
hid->hid_open = hidp_open;
|
|
||||||
hid->hid_close = hidp_close;
|
|
||||||
|
|
||||||
hid->hidinput_input_event = hidp_hidinput_event;
|
|
||||||
|
|
||||||
hidp_setup_quirks(hid);
|
|
||||||
|
|
||||||
list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
|
|
||||||
hidp_send_report(session, report);
|
|
||||||
|
|
||||||
list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
|
|
||||||
hidp_send_report(session, report);
|
|
||||||
|
|
||||||
if (hidinput_connect(hid) == 0)
|
|
||||||
hid->claimed |= HID_CLAIMED_INPUT;
|
|
||||||
|
|
||||||
ret = hid_add_device(hid);
|
ret = hid_add_device(hid);
|
||||||
if (ret) {
|
if (ret)
|
||||||
if (hid->claimed & HID_CLAIMED_INPUT)
|
goto err_hid;
|
||||||
hidinput_disconnect(hid);
|
|
||||||
skb_queue_purge(&session->intr_transmit);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err_hid:
|
||||||
|
hid_destroy_device(hid);
|
||||||
|
session->hid = NULL;
|
||||||
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,46 +835,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
||||||
|
|
||||||
BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
|
BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
|
||||||
|
|
||||||
if (req->rd_size > 0) {
|
|
||||||
unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL);
|
|
||||||
|
|
||||||
if (!buf) {
|
|
||||||
kfree(session);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copy_from_user(buf, req->rd_data, req->rd_size)) {
|
|
||||||
kfree(buf);
|
|
||||||
kfree(session);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
session->hid = hid_allocate_device();
|
|
||||||
if (IS_ERR(session->hid)) {
|
|
||||||
kfree(buf);
|
|
||||||
kfree(session);
|
|
||||||
return PTR_ERR(session->hid);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = hid_parse_report(session->hid, buf, req->rd_size);
|
|
||||||
|
|
||||||
kfree(buf);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
hid_destroy_device(session->hid);
|
|
||||||
kfree(session);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!session->hid) {
|
|
||||||
session->input = input_allocate_device();
|
|
||||||
if (!session->input) {
|
|
||||||
kfree(session);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
down_write(&hidp_session_sem);
|
down_write(&hidp_session_sem);
|
||||||
|
|
||||||
s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
|
s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
|
||||||
|
@ -834,16 +862,16 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
||||||
session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
|
session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
|
||||||
session->idle_to = req->idle_to;
|
session->idle_to = req->idle_to;
|
||||||
|
|
||||||
if (session->input) {
|
if (req->rd_size > 0) {
|
||||||
err = hidp_setup_input(session, req);
|
|
||||||
if (err < 0)
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session->hid) {
|
|
||||||
err = hidp_setup_hid(session, req);
|
err = hidp_setup_hid(session, req);
|
||||||
if (err)
|
if (err)
|
||||||
goto failed;
|
goto err_skb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session->hid) {
|
||||||
|
err = hidp_setup_input(session, req);
|
||||||
|
if (err < 0)
|
||||||
|
goto err_skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
__hidp_link_session(session);
|
__hidp_link_session(session);
|
||||||
|
@ -871,16 +899,15 @@ unlink:
|
||||||
|
|
||||||
__hidp_unlink_session(session);
|
__hidp_unlink_session(session);
|
||||||
|
|
||||||
if (session->input) {
|
if (session->input)
|
||||||
input_unregister_device(session->input);
|
input_unregister_device(session->input);
|
||||||
session->input = NULL; /* don't try to free it here */
|
|
||||||
}
|
|
||||||
|
|
||||||
failed:
|
|
||||||
up_write(&hidp_session_sem);
|
|
||||||
|
|
||||||
if (session->hid)
|
if (session->hid)
|
||||||
hid_destroy_device(session->hid);
|
hid_destroy_device(session->hid);
|
||||||
|
err_skb:
|
||||||
|
skb_queue_purge(&session->ctrl_transmit);
|
||||||
|
skb_queue_purge(&session->intr_transmit);
|
||||||
|
failed:
|
||||||
|
up_write(&hidp_session_sem);
|
||||||
|
|
||||||
input_free_device(session->input);
|
input_free_device(session->input);
|
||||||
kfree(session);
|
kfree(session);
|
||||||
|
|
|
@ -151,6 +151,8 @@ struct hidp_session {
|
||||||
|
|
||||||
struct sk_buff_head ctrl_transmit;
|
struct sk_buff_head ctrl_transmit;
|
||||||
struct sk_buff_head intr_transmit;
|
struct sk_buff_head intr_transmit;
|
||||||
|
|
||||||
|
struct hidp_connadd_req *req;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void hidp_schedule(struct hidp_session *session)
|
static inline void hidp_schedule(struct hidp_session *session)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue