lkml.org 
[lkml]   [2008]   [Apr]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC v2 3/8] HID: hid, make parsing event driven
    Date
    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>
    ---
    drivers/hid/hid-core.c | 14 ++-
    drivers/hid/hid-input.c | 16 ++-
    drivers/hid/hidraw.c | 6 +-
    drivers/hid/usbhid/hid-core.c | 330 ++++++++++++++++++++++++-----------------
    include/linux/hid.h | 94 +++++++++++-
    net/bluetooth/hidp/core.c | 191 ++++++++++++++----------
    net/bluetooth/hidp/hidp.h | 2 +
    7 files changed, 419 insertions(+), 234 deletions(-)

    diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
    index a444c65..7d35e04 100644
    --- a/drivers/hid/hid-core.c
    +++ b/drivers/hid/hid-core.c
    @@ -648,6 +648,9 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
    hid_parser_reserved
    };

    + if (device->driver->report_fixup)
    + device->driver->report_fixup(device, start, size);
    +
    device->rdesc = kmalloc(size, GFP_KERNEL);
    if (device->rdesc == NULL)
    return -ENOMEM;
    @@ -1154,15 +1157,20 @@ static int hid_device_probe(struct device *dev)
    int ret = 0;

    if (!hdev->driver) {
    + hdev->driver = hdrv;
    if (hdrv->probe) {
    ret = -ENODEV;

    id = hid_match_id(hdev, hdrv->id_table);
    if (id)
    ret = hdrv->probe(hdev, id);
    + } else { /* default probe */
    + ret = hid_parse(hdev);
    + if (!ret)
    + ret = hid_hw_start(hdev);
    }
    - if (!ret)
    - hdev->driver = hdrv;
    + if (ret)
    + hdev->driver = NULL;
    }
    return ret;
    }
    @@ -1175,6 +1183,8 @@ static int hid_device_remove(struct device *dev)
    if (hdrv) {
    if (hdrv->remove)
    hdrv->remove(hdev);
    + else /* default remove */
    + hid_hw_stop(hdev);
    hdev->driver = NULL;
    }

    diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
    index f300c7b..5f1a91a 100644
    --- a/drivers/hid/hid-input.c
    +++ b/drivers/hid/hid-input.c
    @@ -389,6 +389,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
    if (ret)
    goto mapped;

    + if (device->driver->input_mapping &&
    + device->driver->input_mapping(device, hidinput, usage,
    + &bit, &max))
    + goto mapped;
    +
    switch (usage->hid & HID_USAGE_PAGE) {

    case HID_UP_UNDEFINED:
    @@ -753,6 +758,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
    }

    mapped:
    + if (device->driver->input_mapped)
    + device->driver->input_mapped(device, hidinput, usage,
    + &bit, &max);
    +
    if (device->quirks & HID_QUIRK_MIGHTYMOUSE) {
    if (usage->hid == HID_GD_Z)
    map_rel(REL_HWHEEL);
    @@ -959,14 +968,14 @@ static int hidinput_open(struct input_dev *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)
    {
    struct hid_device *hid = input_get_drvdata(dev);

    - hid->hid_close(hid);
    + hid->ll_driver->close(hid);
    }

    /*
    @@ -1017,7 +1026,8 @@ int hidinput_connect(struct hid_device *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->close = hidinput_close;
    input_dev->setkeycode = hidinput_setkeycode;
    diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
    index 1ca6f46..19081d0 100644
    --- a/drivers/hid/hidraw.c
    +++ b/drivers/hid/hidraw.c
    @@ -178,7 +178,7 @@ static int hidraw_open(struct inode *inode, struct file *file)

    dev = hidraw_table[minor];
    if (!dev->open++)
    - dev->hid->hid_open(dev->hid);
    + dev->hid->ll_driver->open(dev->hid);

    out_unlock:
    spin_unlock(&minors_lock);
    @@ -203,7 +203,7 @@ static int hidraw_release(struct inode * inode, struct file * file)
    dev = hidraw_table[minor];
    if (!dev->open--) {
    if (list->hidraw->exist)
    - dev->hid->hid_close(dev->hid);
    + dev->hid->ll_driver->close(dev->hid);
    else
    kfree(list->hidraw);
    }
    @@ -359,7 +359,7 @@ void hidraw_disconnect(struct hid_device *hid)
    device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));

    if (hidraw->open) {
    - hid->hid_close(hid);
    + hid->ll_driver->close(hid);
    wake_up_interruptible(&hidraw->wait);
    } else {
    kfree(hidraw);
    diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
    index cf45b03..a07a931 100644
    --- a/drivers/hid/usbhid/hid-core.c
    +++ b/drivers/hid/usbhid/hid-core.c
    @@ -701,17 +701,84 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
    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_device *dev = interface_to_usbdev (intf);
    struct hid_descriptor *hdesc;
    - struct hid_device *hid;
    u32 quirks = 0;
    - unsigned int insize = 0, rsize = 0;
    + unsigned int rsize = 0;
    char *rdesc;
    - int n, len;
    - struct usbhid_device *usbhid;
    + int ret, n;

    quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
    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)
    - return NULL;
    + return -ENODEV;

    if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
    (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
    - return NULL;
    -
    + return -ENODEV;

    if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
    (!interface->desc.bNumEndpoints ||
    usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
    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++)
    if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
    rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);

    if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
    dbg_hid("weird size of report descriptor (%u)\n", rsize);
    - return NULL;
    + return -EINVAL;
    }

    if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
    dbg_hid("couldn't allocate rdesc memory\n");
    - return NULL;
    + return -ENOMEM;
    }

    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");
    kfree(rdesc);
    - return NULL;
    + goto err;
    }

    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("\n");

    - hid = hid_allocate_device();
    - if (IS_ERR(hid)) {
    - kfree(rdesc);
    - return NULL;
    - }
    -
    - if (hid_parse_report(hid, rdesc, n)) {
    + ret = hid_parse_report(hid, rdesc, rsize);
    + kfree(rdesc);
    + if (ret) {
    dbg_hid("parsing report descriptor failed\n");
    - hid_destroy_device(hid);
    - kfree(rdesc);
    - return NULL;
    + goto err;
    }

    - kfree(rdesc);
    hid->quirks = quirks;

    - if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
    - goto fail_no_usbhid;
    + return 0;
    +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;
    usbhid->hid = hid;
    @@ -805,27 +888,12 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
    if (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;
    -
    - 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++) {
    -
    struct usb_endpoint_descriptor *endpoint;
    int pipe;
    int interval;
    @@ -837,7 +905,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
    interval = endpoint->bInterval;

    /* 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) {
    interval = fls(endpoint->bInterval*8);
    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)
    interval = hid_mousepoll_interval;

    + ret = -ENOMEM;
    if (usb_endpoint_dir_in(endpoint)) {
    if (usbhid->urbin)
    continue;
    @@ -873,6 +942,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)

    if (!usbhid->urbin) {
    err_hid("couldn't find an input interrupt endpoint");
    + ret = -ENODEV;
    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->ctrllock);

    - hid->version = le16_to_cpu(hdesc->bcdHID);
    - hid->country = hdesc->bCountryCode;
    - hid->dev.parent = &intf->dev;
    usbhid->intf = intf;
    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);
    - if (!usbhid->urbctrl)
    + if (!usbhid->urbctrl) {
    + ret = -ENOMEM;
    goto fail;
    + }

    usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
    usbhid->ctrlbuf, 1, hid_ctrl, hid);
    usbhid->urbctrl->setup_dma = usbhid->cr_dma;
    usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
    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;
    - hid->hid_close = usbhid_close;
    -#ifdef CONFIG_USB_HIDDEV
    - hid->hiddev_hid_event = hiddev_hid_event;
    - hid->hiddev_report_event = hiddev_report_event;
    -#endif
    - hid->hid_output_raw_report = usbhid_output_raw_report;
    - return hid;
    +
    + ret = usbhid_start_finish(hid);
    + if (ret)
    + goto fail;
    +
    + return 0;

    fail:
    usb_free_urb(usbhid->urbin);
    @@ -929,24 +981,18 @@ fail:
    usb_free_urb(usbhid->urbctrl);
    hid_free_buffers(dev, hid);
    kfree(usbhid);
    -fail_no_usbhid:
    - hid_destroy_device(hid);
    -
    - return NULL;
    +err:
    + return ret;
    }

    -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;
    + struct usbhid_device *usbhid = hid->driver_data;

    - if (!hid)
    + if (WARN_ON(!usbhid))
    return;

    - usbhid = hid->driver_data;
    -
    spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
    - usb_set_intfdata(intf, NULL);
    set_bit(HID_DISCONNECTED, &usbhid->iofl);
    spin_unlock_irq(&usbhid->inlock);
    usb_kill_urb(usbhid->urbin);
    @@ -963,93 +1009,99 @@ static void hid_disconnect(struct usb_interface *intf)
    if (hid->claimed & HID_CLAIMED_HIDRAW)
    hidraw_disconnect(hid);

    + hid->claimed = 0;
    +
    usb_free_urb(usbhid->urbin);
    usb_free_urb(usbhid->urbctrl);
    usb_free_urb(usbhid->urbout);

    hid_free_buffers(hid_to_usb_dev(hid), hid);
    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)
    {
    + struct usb_device *dev = interface_to_usbdev(intf);
    struct hid_device *hid;
    - char path[64];
    - int i, ret;
    - char *c;
    + size_t len;
    + int ret;

    dbg_hid("HID probe called for ifnum %d\n",
    intf->altsetting->desc.bInterfaceNumber);

    - if (!(hid = usb_hid_configure(intf)))
    - return -ENODEV;
    -
    - 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;
    + hid = hid_allocate_device();
    + if (IS_ERR(hid))
    + return PTR_ERR(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) {
    - printk ("HID device claimed by neither input, hiddev nor hidraw\n");
    - hid_disconnect(intf);
    - 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);
    + if (dev->manufacturer)
    + strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));

    - 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;
    - }
    + if (dev->product) {
    + if (dev->manufacturer)
    + strlcat(hid->name, " ", sizeof(hid->name));
    + strlcat(hid->name, dev->product, sizeof(hid->name));
    }

    - usb_make_path(interface_to_usbdev(intf), path, 63);
    + 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));

    - printk(": USB HID v%x.%02x %s [%s] on %s\n",
    - hid->version >> 8, hid->version & 0xff, c, hid->name, path);
    + 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;

    ret = hid_add_device(hid);
    if (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;
    }

    +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)
    {
    struct hid_device *hid = usb_get_intfdata (intf);
    diff --git a/include/linux/hid.h b/include/linux/hid.h
    index 68efdcc..9acf77f 100644
    --- a/include/linux/hid.h
    +++ b/include/linux/hid.h
    @@ -420,6 +420,7 @@ struct hid_control_fifo {
    #define HID_CLAIMED_HIDRAW 4

    #define HID_STAT_ADDED 1
    +#define HID_STAT_PARSED 2

    #define HID_CTRL_RUNNING 1
    #define HID_OUT_RUNNING 2
    @@ -436,6 +437,7 @@ struct hid_input {
    };

    struct hid_driver;
    +struct hid_ll_driver;

    struct hid_device { /* device report descriptor */
    __u8 *rdesc;
    @@ -453,6 +455,7 @@ struct hid_device { /* device report descriptor */

    struct device dev; /* device */
    struct hid_driver *driver;
    + struct hid_ll_driver *ll_driver;

    unsigned int status; /* see STAT flags above */
    unsigned claimed; /* Claimed by hidinput, hiddev? */
    @@ -472,11 +475,6 @@ struct hid_device { /* device report descriptor */

    __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 */
    void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field,
    struct hid_usage *, __s32);
    @@ -561,6 +559,9 @@ struct hid_usage_id {
    * @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)
    * @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
    */
    struct hid_driver {
    char *name;
    @@ -575,10 +576,43 @@ struct hid_driver {
    const struct hid_usage_id *usage_table;
    int (*event)(struct hid_device *hdev, struct hid_field *field,
    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_usage *usage,
    + unsigned long **bit, int *max);
    + void (*input_mapped)(struct hid_device *hdev,
    + struct hid_input *hidinput, struct hid_usage *usage,
    + unsigned long **bit, int *max);
    /* private: */
    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 */
    /* We ignore a few input applications that are not widely used */
    #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001))
    @@ -615,6 +649,56 @@ void hid_output_report(struct hid_report *report, __u8 *data);
    struct hid_device *hid_allocate_device(void);
    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,
    int interrupt);

    diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
    index afedb24..1ead6da 100644
    --- a/net/bluetooth/hidp/core.c
    +++ b/net/bluetooth/hidp/core.c
    @@ -617,9 +617,15 @@ static struct device *hidp_get_device(struct hidp_session *session)
    static int hidp_setup_input(struct hidp_session *session,
    struct hidp_connadd_req *req)
    {
    - struct input_dev *input = session->input;
    + struct input_dev *input;
    int i;

    + input = input_allocate_device();
    + if (!input)
    + return -ENOMEM;
    +
    + session->input = input;
    +
    input_set_drvdata(input, session);

    input->name = "Bluetooth HID Boot Protocol Device";
    @@ -692,55 +698,117 @@ static void hidp_setup_quirks(struct hid_device *hid)
    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_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,
    struct hidp_connadd_req *req)
    {
    - struct hid_device *hid = session->hid;
    - struct hid_report *report;
    + struct hid_device *hid;
    bdaddr_t src, dst;
    int ret;

    - baswap(&src, &bt_sk(session->ctrl_sock->sk)->src);
    - baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst);
    + hid = hid_allocate_device();
    + if (IS_ERR(hid)) {
    + ret = PTR_ERR(session->hid);
    + goto err;
    + }

    + session->hid = hid;
    + session->req = req;
    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->vendor = req->vendor;
    hid->product = req->product;
    hid->version = req->version;
    + hid->country = req->country;

    strncpy(hid->name, req->name, 128);
    strncpy(hid->phys, batostr(&src), 64);
    strncpy(hid->uniq, batostr(&dst), 64);

    hid->dev.parent = hidp_get_device(session);
    -
    - 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;
    + hid->ll_driver = &hidp_driver;

    ret = hid_add_device(hid);
    - if (ret) {
    - if (hid->claimed & HID_CLAIMED_INPUT)
    - hidinput_disconnect(hid);
    - skb_queue_purge(&session->intr_transmit);
    - }
    + if (ret)
    + goto err_hid;

    + return 0;
    +err_hid:
    + hid_destroy_device(hid);
    + session->hid = NULL;
    +err:
    return ret;
    }

    @@ -761,46 +829,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);

    - 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);

    s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
    @@ -828,16 +856,16 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
    session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
    session->idle_to = req->idle_to;

    - if (session->input) {
    - err = hidp_setup_input(session, req);
    - if (err < 0)
    - goto failed;
    - }
    -
    - if (session->hid) {
    + if (req->rd_size > 0) {
    err = hidp_setup_hid(session, req);
    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);
    @@ -865,16 +893,15 @@ unlink:

    __hidp_unlink_session(session);

    - if (session->input) {
    + if (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)
    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);
    kfree(session);
    diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
    index 343fb05..e503c89 100644
    --- a/net/bluetooth/hidp/hidp.h
    +++ b/net/bluetooth/hidp/hidp.h
    @@ -151,6 +151,8 @@ struct hidp_session {

    struct sk_buff_head ctrl_transmit;
    struct sk_buff_head intr_transmit;
    +
    + struct hidp_connadd_req *req;
    };

    static inline void hidp_schedule(struct hidp_session *session)
    --
    1.5.4.5


    \
     
     \ /
      Last update: 2008-04-27 13:53    [W:0.089 / U:0.968 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site