diff options
Diffstat (limited to 'drivers/hid/hid-core.c')
| -rw-r--r-- | drivers/hid/hid-core.c | 647 |
1 files changed, 435 insertions, 212 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 3e1803592bd4..a5b3a8ca2fcb 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -20,7 +20,7 @@ #include <linux/list.h> #include <linux/mm.h> #include <linux/spinlock.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <asm/byteorder.h> #include <linux/input.h> #include <linux/wait.h> @@ -41,16 +41,43 @@ #define DRIVER_DESC "HID core driver" -int hid_debug = 0; -module_param_named(debug, hid_debug, int, 0600); -MODULE_PARM_DESC(debug, "toggle HID debugging messages"); -EXPORT_SYMBOL_GPL(hid_debug); - static int hid_ignore_special_drivers = 0; module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600); MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle all devices by generic driver"); /* + * Convert a signed n-bit integer to signed 32-bit integer. + */ + +static s32 snto32(__u32 value, unsigned int n) +{ + if (!value || !n) + return 0; + + if (n > 32) + n = 32; + + return sign_extend32(value, n - 1); +} + +/* + * Convert a signed 32-bit integer to a signed n-bit integer. + */ + +static u32 s32ton(__s32 value, unsigned int n) +{ + s32 a; + + if (!value || !n) + return 0; + + a = value >> (n - 1); + if (a && a != -1) + return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; + return value & ((1 << n) - 1); +} + +/* * Register a new report for a device. */ @@ -100,9 +127,9 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned return NULL; } - field = kzalloc((sizeof(struct hid_field) + - usages * sizeof(struct hid_usage) + - 3 * usages * sizeof(unsigned int)), GFP_KERNEL); + field = kvzalloc((sizeof(struct hid_field) + + usages * sizeof(struct hid_usage) + + 3 * usages * sizeof(unsigned int)), GFP_KERNEL); if (!field) return NULL; @@ -261,6 +288,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign { struct hid_report *report; struct hid_field *field; + unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; unsigned int usages; unsigned int offset; unsigned int i; @@ -291,8 +319,11 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign offset = report->size; report->size += parser->global.report_size * parser->global.report_count; + if (parser->device->ll_driver->max_buffer_size) + max_buffer_size = parser->device->ll_driver->max_buffer_size; + /* Total size check: Allow for possible report index byte */ - if (report->size > (HID_MAX_BUFFER_SIZE - 1) << 3) { + if (report->size > (max_buffer_size - 1) << 3) { hid_err(parser->device, "report is too long\n"); return -1; } @@ -426,7 +457,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) * both this and the standard encoding. */ raw_value = item_sdata(item); if (!(raw_value & 0xfffffff0)) - parser->global.unit_exponent = hid_snto32(raw_value, 4); + parser->global.unit_exponent = snto32(raw_value, 4); else parser->global.unit_exponent = raw_value; return 0; @@ -630,7 +661,11 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) ret = hid_add_field(parser, HID_FEATURE_REPORT, data); break; default: - hid_warn(parser->device, "unknown main item tag 0x%x\n", item->tag); + if (item->tag >= HID_MAIN_ITEM_TAG_RESERVED_MIN && + item->tag <= HID_MAIN_ITEM_TAG_RESERVED_MAX) + hid_warn_ratelimited(parser->device, "reserved main item tag 0x%x\n", item->tag); + else + hid_warn_ratelimited(parser->device, "unknown main item tag 0x%x\n", item->tag); ret = 0; } @@ -662,7 +697,7 @@ static void hid_free_report(struct hid_report *report) kfree(report->field_entries); for (n = 0; n < report->maxfield; n++) - kfree(report->field[n]); + kvfree(report->field[n]); kfree(report); } @@ -686,7 +721,14 @@ static void hid_close_report(struct hid_device *device) INIT_LIST_HEAD(&report_enum->report_list); } - kfree(device->rdesc); + /* + * If the HID driver had a rdesc_fixup() callback, dev->rdesc + * will be allocated by hid-core and needs to be freed. + * Otherwise, it is either equal to dev_rdesc or bpf_rdesc, in + * which cases it'll be freed later on device removal or destroy. + */ + if (device->rdesc != device->dev_rdesc && device->rdesc != device->bpf_rdesc) + kfree(device->rdesc); device->rdesc = NULL; device->rsize = 0; @@ -699,25 +741,41 @@ static void hid_close_report(struct hid_device *device) device->status &= ~HID_STAT_PARSED; } +static inline void hid_free_bpf_rdesc(struct hid_device *hdev) +{ + /* bpf_rdesc is either equal to dev_rdesc or allocated by call_hid_bpf_rdesc_fixup() */ + if (hdev->bpf_rdesc != hdev->dev_rdesc) + kfree(hdev->bpf_rdesc); + hdev->bpf_rdesc = NULL; +} + /* * Free a device structure, all reports, and all fields. */ -static void hid_device_release(struct device *dev) +void hiddev_free(struct kref *ref) { - struct hid_device *hid = to_hid_device(dev); + struct hid_device *hid = container_of(ref, struct hid_device, ref); hid_close_report(hid); + hid_free_bpf_rdesc(hid); kfree(hid->dev_rdesc); kfree(hid); } +static void hid_device_release(struct device *dev) +{ + struct hid_device *hid = to_hid_device(dev); + + kref_put(&hid->ref, hiddev_free); +} + /* * Fetch a report description item from the data stream. We support long * items, though they are not used yet. */ -static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) +static const u8 *fetch_item(const __u8 *start, const __u8 *end, struct hid_item *item) { u8 b; @@ -748,35 +806,29 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) } item->format = HID_ITEM_FORMAT_SHORT; - item->size = b & 3; + item->size = BIT(b & 3) >> 1; /* 0, 1, 2, 3 -> 0, 1, 2, 4 */ + + if (end - start < item->size) + return NULL; switch (item->size) { case 0: - return start; + break; case 1: - if ((end - start) < 1) - return NULL; - item->data.u8 = *start++; - return start; + item->data.u8 = *start; + break; case 2: - if ((end - start) < 2) - return NULL; item->data.u16 = get_unaligned_le16(start); - start = (__u8 *)((__le16 *)start + 1); - return start; + break; - case 3: - item->size++; - if ((end - start) < 4) - return NULL; + case 4: item->data.u32 = get_unaligned_le32(start); - start = (__u8 *)((__le32 *)start + 1); - return start; + break; } - return NULL; + return start + item->size; } static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) @@ -804,7 +856,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) int i; if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && - type == HID_COLLECTION_PHYSICAL) + (type == HID_COLLECTION_PHYSICAL || + type == HID_COLLECTION_APPLICATION)) hid->group = HID_GROUP_SENSOR_HUB; if (hid->vendor == USB_VENDOR_ID_MICROSOFT && @@ -873,8 +926,8 @@ static int hid_scan_report(struct hid_device *hid) { struct hid_parser *parser; struct hid_item item; - __u8 *start = hid->dev_rdesc; - __u8 *end = start + hid->dev_rsize; + const __u8 *start = hid->dev_rdesc; + const __u8 *end = start + hid->dev_rsize; static int (*dispatch_type[])(struct hid_parser *parser, struct hid_item *item) = { hid_scan_main, @@ -891,6 +944,15 @@ static int hid_scan_report(struct hid_device *hid) hid->group = HID_GROUP_GENERIC; /* + * In case we are re-scanning after a BPF has been loaded, + * we need to use the bpf report descriptor, not the original one. + */ + if (hid->bpf_rdesc && hid->bpf_rsize) { + start = hid->bpf_rdesc; + end = start + hid->bpf_rsize; + } + + /* * The parsing is simpler than the one in hid_open_report() as we should * be robust against hid errors. Those errors will be raised by * hid_open_report() anyway. @@ -939,7 +1001,7 @@ static int hid_scan_report(struct hid_device *hid) * Allocate the device report as read by the bus driver. This function should * only be called from parse() in ll drivers. */ -int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) +int hid_parse_report(struct hid_device *hid, const __u8 *start, unsigned size) { hid->dev_rdesc = kmemdup(start, size, GFP_KERNEL); if (!hid->dev_rdesc) @@ -1118,6 +1180,8 @@ static void hid_apply_multiplier(struct hid_device *hid, while (multiplier_collection->parent_idx != -1 && multiplier_collection->type != HID_COLLECTION_LOGICAL) multiplier_collection = &hid->collection[multiplier_collection->parent_idx]; + if (multiplier_collection->type != HID_COLLECTION_LOGICAL) + multiplier_collection = NULL; effective_multiplier = hid_calculate_multiplier(hid, multiplier); @@ -1197,11 +1261,11 @@ int hid_open_report(struct hid_device *device) struct hid_parser *parser; struct hid_item item; unsigned int size; - __u8 *start; - __u8 *buf; - __u8 *end; - __u8 *next; + const __u8 *start; + const __u8 *end; + const __u8 *next; int ret; + int i; static int (*dispatch_type[])(struct hid_parser *parser, struct hid_item *item) = { hid_parser_main, @@ -1213,24 +1277,34 @@ int hid_open_report(struct hid_device *device) if (WARN_ON(device->status & HID_STAT_PARSED)) return -EBUSY; - start = device->dev_rdesc; + start = device->bpf_rdesc; if (WARN_ON(!start)) return -ENODEV; - size = device->dev_rsize; + size = device->bpf_rsize; - buf = kmemdup(start, size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; + if (device->driver->report_fixup) { + /* + * device->driver->report_fixup() needs to work + * on a copy of our report descriptor so it can + * change it. + */ + __u8 *buf = kmemdup(start, size, GFP_KERNEL); + + if (buf == NULL) + return -ENOMEM; - if (device->driver->report_fixup) start = device->driver->report_fixup(device, buf, &size); - else - start = buf; - start = kmemdup(start, size, GFP_KERNEL); - kfree(buf); - if (start == NULL) - return -ENOMEM; + /* + * The second kmemdup is required in case report_fixup() returns + * a static read-only memory, but we have no idea if that memory + * needs to be cleaned up or not at the end. + */ + start = kmemdup(start, size, GFP_KERNEL); + kfree(buf); + if (start == NULL) + return -ENOMEM; + } device->rdesc = start; device->rsize = size; @@ -1252,6 +1326,8 @@ int hid_open_report(struct hid_device *device) goto err; } device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + for (i = 0; i < HID_DEFAULT_NUM_COLLECTIONS; i++) + device->collection[i].parent_idx = -1; ret = -EINVAL; while ((next = fetch_item(start, end, &item)) != NULL) { @@ -1305,46 +1381,6 @@ alloc_err: EXPORT_SYMBOL_GPL(hid_open_report); /* - * Convert a signed n-bit integer to signed 32-bit integer. Common - * cases are done through the compiler, the screwed things has to be - * done by hand. - */ - -static s32 snto32(__u32 value, unsigned n) -{ - if (!value || !n) - return 0; - - if (n > 32) - n = 32; - - switch (n) { - case 8: return ((__s8)value); - case 16: return ((__s16)value); - case 32: return ((__s32)value); - } - return value & (1 << (n - 1)) ? value | (~0U << n) : value; -} - -s32 hid_snto32(__u32 value, unsigned n) -{ - return snto32(value, n); -} -EXPORT_SYMBOL_GPL(hid_snto32); - -/* - * Convert a signed 32-bit integer to a signed n-bit integer. - */ - -static u32 s32ton(__s32 value, unsigned n) -{ - s32 a = value >> (n - 1); - if (a && a != -1) - return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; - return value & ((1 << n) - 1); -} - -/* * Extract/implement a data field from/to a little endian report (bit array). * * Code sort-of follows HID spec: @@ -1437,7 +1473,6 @@ static void implement(const struct hid_device *hid, u8 *report, hid_warn(hid, "%s() called with too large value %d (n: %d)! (%s)\n", __func__, value, n, current->comm); - WARN_ON(1); value &= m; } } @@ -1861,11 +1896,14 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) /* * 7 extra bytes are necessary to achieve proper functionality * of implement() working on 8 byte chunks + * 1 extra byte for the report ID if it is null (not used) so + * we can reserve that extra byte in the first position of the buffer + * when sending it to .raw_request() */ - u32 len = hid_report_len(report) + 7; + u32 len = hid_report_len(report) + 7 + (report->id == 0); - return kmalloc(len, flags); + return kzalloc(len, flags); } EXPORT_SYMBOL_GPL(hid_alloc_report_buf); @@ -1902,6 +1940,31 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) } EXPORT_SYMBOL_GPL(hid_set_field); +struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type, + unsigned int application, unsigned int usage) +{ + struct list_head *report_list = &hdev->report_enum[report_type].report_list; + struct hid_report *report; + int i, j; + + list_for_each_entry(report, report_list, list) { + if (report->application != application) + continue; + + for (i = 0; i < report->maxfield; i++) { + struct hid_field *field = report->field[i]; + + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == usage) + return field; + } + } + } + + return NULL; +} +EXPORT_SYMBOL_GPL(hid_find_field); + static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, const u8 *data) { @@ -1926,7 +1989,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, int __hid_request(struct hid_device *hid, struct hid_report *report, enum hid_class_request reqtype) { - char *buf; + char *buf, *data_buf; int ret; u32 len; @@ -1934,13 +1997,19 @@ int __hid_request(struct hid_device *hid, struct hid_report *report, if (!buf) return -ENOMEM; + data_buf = buf; len = hid_report_len(report); + if (report->id == 0) { + /* reserve the first byte for the report ID */ + data_buf++; + len++; + } + if (reqtype == HID_REQ_SET_REPORT) - hid_output_report(report, buf); + hid_output_report(report, data_buf); - ret = hid->ll_driver->raw_request(hid, report->id, buf, len, - report->type, reqtype); + ret = hid_hw_raw_request(hid, report->id, buf, len, report->type, reqtype); if (ret < 0) { dbg_hid("unable to complete request: %d\n", ret); goto out; @@ -1963,6 +2032,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; struct hid_driver *hdrv; + int max_buffer_size = HID_MAX_BUFFER_SIZE; u32 rsize, csize = size; u8 *cdata = data; int ret = 0; @@ -1978,10 +2048,13 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * rsize = hid_compute_report_size(report); - if (report_enum->numbered && rsize >= HID_MAX_BUFFER_SIZE) - rsize = HID_MAX_BUFFER_SIZE - 1; - else if (rsize > HID_MAX_BUFFER_SIZE) - rsize = HID_MAX_BUFFER_SIZE; + if (hid->ll_driver->max_buffer_size) + max_buffer_size = hid->ll_driver->max_buffer_size; + + if (report_enum->numbered && rsize >= max_buffer_size) + rsize = max_buffer_size - 1; + else if (rsize > max_buffer_size) + rsize = max_buffer_size; if (csize < rsize) { dbg_hid("report %d is too short, (%d < %d)\n", report->id, @@ -2011,19 +2084,10 @@ out: } EXPORT_SYMBOL_GPL(hid_report_raw_event); -/** - * hid_input_report - report data from lower layer (usb, bt...) - * - * @hid: hid device - * @type: HID report type (HID_*_REPORT) - * @data: report contents - * @size: size of data parameter - * @interrupt: distinguish between interrupt and control transfers - * - * This is data entry for lower layers. - */ -int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, - int interrupt) + +static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, + u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, + bool lock_already_taken) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; @@ -2033,8 +2097,13 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data if (!hid) return -ENODEV; - if (down_trylock(&hid->driver_input_lock)) + ret = down_trylock(&hid->driver_input_lock); + if (lock_already_taken && !ret) { + up(&hid->driver_input_lock); + return -EINVAL; + } else if (!lock_already_taken && ret) { return -EBUSY; + } if (!hid->driver) { ret = -ENODEV; @@ -2043,6 +2112,12 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data report_enum = hid->report_enum + type; hdrv = hid->driver; + data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + goto unlock; + } + if (!size) { dbg_hid("empty report\n"); ret = -1; @@ -2069,9 +2144,29 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data ret = hid_report_raw_event(hid, type, data, size, interrupt); unlock: - up(&hid->driver_input_lock); + if (!lock_already_taken) + up(&hid->driver_input_lock); return ret; } + +/** + * hid_input_report - report data from lower layer (usb, bt...) + * + * @hid: hid device + * @type: HID report type (HID_*_REPORT) + * @data: report contents + * @size: size of data parameter + * @interrupt: distinguish between interrupt and control transfers + * + * This is data entry for lower layers. + */ +int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) +{ + return __hid_input_report(hid, type, data, size, interrupt, 0, + false, /* from_bpf */ + false /* lock_already_taken */); +} EXPORT_SYMBOL_GPL(hid_input_report); bool hid_match_one_id(const struct hid_device *hdev, @@ -2107,9 +2202,9 @@ static bool hid_hiddev(struct hid_device *hdev) static ssize_t -read_report_descriptor(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +report_descriptor_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct hid_device *hdev = to_hid_device(dev); @@ -2126,24 +2221,17 @@ read_report_descriptor(struct file *filp, struct kobject *kobj, } static ssize_t -show_country(struct device *dev, struct device_attribute *attr, - char *buf) +country_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct hid_device *hdev = to_hid_device(dev); return sprintf(buf, "%02x\n", hdev->country & 0xff); } -static struct bin_attribute dev_bin_attr_report_desc = { - .attr = { .name = "report_descriptor", .mode = 0444 }, - .read = read_report_descriptor, - .size = HID_MAX_DESCRIPTOR_SIZE, -}; +static const BIN_ATTR_RO(report_descriptor, HID_MAX_DESCRIPTOR_SIZE); -static const struct device_attribute dev_attr_country = { - .attr = { .name = "country", .mode = 0444 }, - .show = show_country, -}; +static const DEVICE_ATTR_RO(country); int hid_connect(struct hid_device *hdev, unsigned int connect_mask) { @@ -2157,6 +2245,10 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) int len; int ret; + ret = hid_bpf_connect_device(hdev); + if (ret) + return ret; + if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE) @@ -2224,6 +2316,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) case BUS_I2C: bus = "I2C"; break; + case BUS_SDW: + bus = "SOUNDWIRE"; + break; case BUS_VIRTUAL: bus = "VIRTUAL"; break; @@ -2258,6 +2353,8 @@ void hid_disconnect(struct hid_device *hdev) if (hdev->claimed & HID_CLAIMED_HIDRAW) hidraw_disconnect(hdev); hdev->claimed = 0; + + hid_bpf_disconnect_device(hdev); } EXPORT_SYMBOL_GPL(hid_disconnect); @@ -2324,6 +2421,9 @@ int hid_hw_open(struct hid_device *hdev) ret = hdev->ll_driver->open(hdev); if (ret) hdev->ll_open_count--; + + if (hdev->driver->on_hid_hw_open) + hdev->driver->on_hid_hw_open(hdev); } mutex_unlock(&hdev->ll_open_lock); @@ -2343,8 +2443,12 @@ EXPORT_SYMBOL_GPL(hid_hw_open); void hid_hw_close(struct hid_device *hdev) { mutex_lock(&hdev->ll_open_lock); - if (!--hdev->ll_open_count) + if (!--hdev->ll_open_count) { hdev->ll_driver->close(hdev); + + if (hdev->driver->on_hid_hw_close) + hdev->driver->on_hid_hw_close(hdev); + } mutex_unlock(&hdev->ll_open_lock); } EXPORT_SYMBOL_GPL(hid_hw_close); @@ -2366,6 +2470,30 @@ void hid_hw_request(struct hid_device *hdev, } EXPORT_SYMBOL_GPL(hid_hw_request); +int __hid_hw_raw_request(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + size_t len, enum hid_report_type rtype, + enum hid_class_request reqtype, + u64 source, bool from_bpf) +{ + unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + int ret; + + if (hdev->ll_driver->max_buffer_size) + max_buffer_size = hdev->ll_driver->max_buffer_size; + + if (len < 1 || len > max_buffer_size || !buf) + return -EINVAL; + + ret = dispatch_hid_bpf_raw_requests(hdev, reportnum, buf, len, rtype, + reqtype, source, from_bpf); + if (ret) + return ret; + + return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, + rtype, reqtype); +} + /** * hid_hw_raw_request - send report request to device * @@ -2384,13 +2512,31 @@ int hid_hw_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, enum hid_report_type rtype, enum hid_class_request reqtype) { - if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf) + return __hid_hw_raw_request(hdev, reportnum, buf, len, rtype, reqtype, 0, false); +} +EXPORT_SYMBOL_GPL(hid_hw_raw_request); + +int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, u64 source, + bool from_bpf) +{ + unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + int ret; + + if (hdev->ll_driver->max_buffer_size) + max_buffer_size = hdev->ll_driver->max_buffer_size; + + if (len < 1 || len > max_buffer_size || !buf) return -EINVAL; - return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, - rtype, reqtype); + ret = dispatch_hid_bpf_output_report(hdev, buf, len, source, from_bpf); + if (ret) + return ret; + + if (hdev->ll_driver->output_report) + return hdev->ll_driver->output_report(hdev, buf, len); + + return -ENOSYS; } -EXPORT_SYMBOL_GPL(hid_hw_raw_request); /** * hid_hw_output_report - send output report to device @@ -2403,13 +2549,7 @@ EXPORT_SYMBOL_GPL(hid_hw_raw_request); */ int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len) { - if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf) - return -EINVAL; - - if (hdev->ll_driver->output_report) - return hdev->ll_driver->output_report(hdev, buf, len); - - return -ENOSYS; + return __hid_hw_output_report(hdev, buf, len, 0, false); } EXPORT_SYMBOL_GPL(hid_hw_output_report); @@ -2526,7 +2666,7 @@ const struct hid_device_id *hid_match_device(struct hid_device *hdev, } EXPORT_SYMBOL_GPL(hid_match_device); -static int hid_bus_match(struct device *dev, struct device_driver *drv) +static int hid_bus_match(struct device *dev, const struct device_driver *drv) { struct hid_driver *hdrv = to_hid_driver(drv); struct hid_device *hdev = to_hid_device(dev); @@ -2557,64 +2697,123 @@ bool hid_compare_device_paths(struct hid_device *hdev_a, } EXPORT_SYMBOL_GPL(hid_compare_device_paths); -static int hid_device_probe(struct device *dev) +static bool hid_check_device_match(struct hid_device *hdev, + struct hid_driver *hdrv, + const struct hid_device_id **id) { - struct hid_driver *hdrv = to_hid_driver(dev->driver); - struct hid_device *hdev = to_hid_device(dev); - const struct hid_device_id *id; - int ret = 0; + *id = hid_match_device(hdev, hdrv); + if (!*id) + return false; + + if (hdrv->match) + return hdrv->match(hdev, hid_ignore_special_drivers); + + /* + * hid-generic implements .match(), so we must be dealing with a + * different HID driver here, and can simply check if + * hid_ignore_special_drivers or HID_QUIRK_IGNORE_SPECIAL_DRIVER + * are set or not. + */ + return !hid_ignore_special_drivers && !(hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER); +} + +static void hid_set_group(struct hid_device *hdev) +{ + int ret; - if (down_interruptible(&hdev->driver_input_lock)) { - ret = -EINTR; - goto end; + if (hid_ignore_special_drivers) { + hdev->group = HID_GROUP_GENERIC; + } else if (!hdev->group && + !(hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)) { + ret = hid_scan_report(hdev); + if (ret) + hid_warn(hdev, "bad device descriptor (%d)\n", ret); } - hdev->io_started = false; +} - clear_bit(ffs(HID_STAT_REPROBED), &hdev->status); +static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) +{ + const struct hid_device_id *id; + int ret; - if (!hdev->driver) { - id = hid_match_device(hdev, hdrv); - if (id == NULL) { - ret = -ENODEV; - goto unlock; - } + if (!hdev->bpf_rsize) { + /* we keep a reference to the currently scanned report descriptor */ + const __u8 *original_rdesc = hdev->bpf_rdesc; - if (hdrv->match) { - if (!hdrv->match(hdev, hid_ignore_special_drivers)) { - ret = -ENODEV; - goto unlock; - } - } else { - /* - * hid-generic implements .match(), so if - * hid_ignore_special_drivers is set, we can safely - * return. - */ - if (hid_ignore_special_drivers) { - ret = -ENODEV; - goto unlock; - } - } + if (!original_rdesc) + original_rdesc = hdev->dev_rdesc; - /* reset the quirks that has been previously set */ - hdev->quirks = hid_lookup_quirk(hdev); - hdev->driver = hdrv; - if (hdrv->probe) { - ret = hdrv->probe(hdev, id); - } else { /* default probe */ - ret = hid_open_report(hdev); - if (!ret) - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - } - if (ret) { - hid_close_report(hdev); - hdev->driver = NULL; + /* in case a bpf program gets detached, we need to free the old one */ + hid_free_bpf_rdesc(hdev); + + /* keep this around so we know we called it once */ + hdev->bpf_rsize = hdev->dev_rsize; + + /* call_hid_bpf_rdesc_fixup will always return a valid pointer */ + hdev->bpf_rdesc = call_hid_bpf_rdesc_fixup(hdev, hdev->dev_rdesc, + &hdev->bpf_rsize); + + /* the report descriptor changed, we need to re-scan it */ + if (original_rdesc != hdev->bpf_rdesc) { + hdev->group = 0; + hid_set_group(hdev); } } -unlock: + + if (!hid_check_device_match(hdev, hdrv, &id)) + return -ENODEV; + + hdev->devres_group_id = devres_open_group(&hdev->dev, NULL, GFP_KERNEL); + if (!hdev->devres_group_id) + return -ENOMEM; + + /* reset the quirks that has been previously set */ + hdev->quirks = hid_lookup_quirk(hdev); + hdev->driver = hdrv; + + if (hdrv->probe) { + ret = hdrv->probe(hdev, id); + } else { /* default probe */ + ret = hid_open_report(hdev); + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + } + + /* + * Note that we are not closing the devres group opened above so + * even resources that were attached to the device after probe is + * run are released when hid_device_remove() is executed. This is + * needed as some drivers would allocate additional resources, + * for example when updating firmware. + */ + + if (ret) { + devres_release_group(&hdev->dev, hdev->devres_group_id); + hid_close_report(hdev); + hdev->driver = NULL; + } + + return ret; +} + +static int hid_device_probe(struct device *dev) +{ + struct hid_device *hdev = to_hid_device(dev); + struct hid_driver *hdrv = to_hid_driver(dev->driver); + int ret = 0; + + if (down_interruptible(&hdev->driver_input_lock)) + return -EINTR; + + hdev->io_started = false; + clear_bit(ffs(HID_STAT_REPROBED), &hdev->status); + + if (!hdev->driver) + ret = __hid_device_probe(hdev, hdrv); + if (!hdev->io_started) up(&hdev->driver_input_lock); -end: + return ret; } @@ -2632,6 +2831,10 @@ static void hid_device_remove(struct device *dev) hdrv->remove(hdev); else /* default remove */ hid_hw_stop(hdev); + + /* Release all devres resources allocated by the driver */ + devres_release_group(&hdev->dev, hdev->devres_group_id); + hid_close_report(hdev); hdev->driver = NULL; } @@ -2645,7 +2848,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, { struct hid_device *hdev = container_of(dev, struct hid_device, dev); - return scnprintf(buf, PAGE_SIZE, "hid:b%04Xg%04Xv%08Xp%08X\n", + return sysfs_emit(buf, "hid:b%04Xg%04Xv%08Xp%08X\n", hdev->bus, hdev->group, hdev->vendor, hdev->product); } static DEVICE_ATTR_RO(modalias); @@ -2654,8 +2857,8 @@ static struct attribute *hid_dev_attrs[] = { &dev_attr_modalias.attr, NULL, }; -static struct bin_attribute *hid_dev_bin_attrs[] = { - &dev_bin_attr_report_desc, +static const struct bin_attribute *hid_dev_bin_attrs[] = { + &bin_attr_report_descriptor, NULL }; static const struct attribute_group hid_dev_group = { @@ -2664,9 +2867,9 @@ static const struct attribute_group hid_dev_group = { }; __ATTRIBUTE_GROUPS(hid_dev); -static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) +static int hid_uevent(const struct device *dev, struct kobj_uevent_env *env) { - struct hid_device *hdev = to_hid_device(dev); + const struct hid_device *hdev = to_hid_device(dev); if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X", hdev->bus, hdev->vendor, hdev->product)) @@ -2688,7 +2891,7 @@ static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -struct bus_type hid_bus_type = { +const struct bus_type hid_bus_type = { .name = "hid", .dev_groups = hid_dev_groups, .drv_groups = hid_drv_groups, @@ -2735,14 +2938,7 @@ int hid_add_device(struct hid_device *hdev) /* * Scan generic devices for group information */ - if (hid_ignore_special_drivers) { - hdev->group = HID_GROUP_GENERIC; - } else if (!hdev->group && - !(hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)) { - ret = hid_scan_report(hdev); - if (ret) - hid_warn(hdev, "bad device descriptor (%d)\n", ret); - } + hid_set_group(hdev); hdev->id = atomic_inc_return(&id); @@ -2792,8 +2988,17 @@ struct hid_device *hid_allocate_device(void) spin_lock_init(&hdev->debug_list_lock); sema_init(&hdev->driver_input_lock, 1); mutex_init(&hdev->ll_open_lock); + kref_init(&hdev->ref); + + ret = hid_bpf_device_init(hdev); + if (ret) + goto out_err; return hdev; + +out_err: + hid_destroy_device(hdev); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(hid_allocate_device); @@ -2804,9 +3009,11 @@ static void hid_remove_device(struct hid_device *hdev) hid_debug_unregister(hdev); hdev->status &= ~HID_STAT_ADDED; } + hid_free_bpf_rdesc(hdev); kfree(hdev->dev_rdesc); hdev->dev_rdesc = NULL; hdev->dev_rsize = 0; + hdev->bpf_rsize = 0; } /** @@ -2819,6 +3026,7 @@ static void hid_remove_device(struct hid_device *hdev) */ void hid_destroy_device(struct hid_device *hdev) { + hid_bpf_destroy_device(hdev); hid_remove_device(hdev); put_device(&hdev->dev); } @@ -2905,20 +3113,31 @@ int hid_check_keys_pressed(struct hid_device *hid) } EXPORT_SYMBOL_GPL(hid_check_keys_pressed); +#ifdef CONFIG_HID_BPF +static const struct hid_ops __hid_ops = { + .hid_get_report = hid_get_report, + .hid_hw_raw_request = __hid_hw_raw_request, + .hid_hw_output_report = __hid_hw_output_report, + .hid_input_report = __hid_input_report, + .owner = THIS_MODULE, + .bus_type = &hid_bus_type, +}; +#endif + static int __init hid_init(void) { int ret; - if (hid_debug) - pr_warn("hid_debug is now used solely for parser and driver debugging.\n" - "debugfs is now used for inspecting the device (report descriptor, reports)\n"); - ret = bus_register(&hid_bus_type); if (ret) { pr_err("can't register hid bus\n"); goto err; } +#ifdef CONFIG_HID_BPF + hid_ops = &__hid_ops; +#endif + ret = hidraw_init(); if (ret) goto err_bus; @@ -2934,6 +3153,9 @@ err: static void __exit hid_exit(void) { +#ifdef CONFIG_HID_BPF + hid_ops = NULL; +#endif hid_debug_exit(); hidraw_exit(); bus_unregister(&hid_bus_type); @@ -2946,4 +3168,5 @@ module_exit(hid_exit); MODULE_AUTHOR("Andreas Gal"); MODULE_AUTHOR("Vojtech Pavlik"); MODULE_AUTHOR("Jiri Kosina"); +MODULE_DESCRIPTION("HID support for Linux"); MODULE_LICENSE("GPL"); |
