📄 hid-core.c
字号:
break; case HID_MAIN_ITEM_TAG_END_COLLECTION: ret = close_collection(parser); break; case HID_MAIN_ITEM_TAG_INPUT: ret = hid_add_field(parser, HID_INPUT_REPORT, data); break; case HID_MAIN_ITEM_TAG_OUTPUT: ret = hid_add_field(parser, HID_OUTPUT_REPORT, data); break; case HID_MAIN_ITEM_TAG_FEATURE: ret = hid_add_field(parser, HID_FEATURE_REPORT, data); break; default: dbg("unknown main item tag 0x%x", item->tag); ret = 0; } memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */ return ret;}/* * Process a reserved item. */static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item){ dbg("reserved item type, tag 0x%x", item->tag); return 0;}/* * Free a report and all registered fields. The field->usage and * field->value table's are allocated behind the field, so we need * only to free(field) itself. */static void hid_free_report(struct hid_report *report){ unsigned n; for (n = 0; n < report->maxfield; n++) kfree(report->field[n]); kfree(report);}/* * Free a device structure, all reports, and all fields. */static void hid_free_device(struct hid_device *device){ unsigned i,j; hid_ff_exit(device); for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; for (j = 0; j < 256; j++) { struct hid_report *report = report_enum->report_id_hash[j]; if (report) hid_free_report(report); } } kfree(device->rdesc); kfree(device);}/* * 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){ u8 b; if ((end - start) <= 0) return NULL; b = *start++; item->type = (b >> 2) & 3; item->tag = (b >> 4) & 15; if (item->tag == HID_ITEM_TAG_LONG) { item->format = HID_ITEM_FORMAT_LONG; if ((end - start) < 2) return NULL; item->size = *start++; item->tag = *start++; if ((end - start) < item->size) return NULL; item->data.longdata = start; start += item->size; return start; } item->format = HID_ITEM_FORMAT_SHORT; item->size = b & 3; switch (item->size) { case 0: return start; case 1: if ((end - start) < 1) return NULL; item->data.u8 = *start++; return start; case 2: if ((end - start) < 2) return NULL; item->data.u16 = le16_to_cpu(get_unaligned((__le16*)start)); start = (__u8 *)((__le16 *)start + 1); return start; case 3: item->size++; if ((end - start) < 4) return NULL; item->data.u32 = le32_to_cpu(get_unaligned((__le32*)start)); start = (__u8 *)((__le32 *)start + 1); return start; } return NULL;}/* * Parse a report description into a hid_device structure. Reports are * enumerated, fields are attached to these reports. */static struct hid_device *hid_parse_report(__u8 *start, unsigned size){ struct hid_device *device; struct hid_parser *parser; struct hid_item item; __u8 *end; unsigned i; static int (*dispatch_type[])(struct hid_parser *parser, struct hid_item *item) = { hid_parser_main, hid_parser_global, hid_parser_local, hid_parser_reserved }; if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL))) return NULL; memset(device, 0, sizeof(struct hid_device)); if (!(device->collection = kmalloc(sizeof(struct hid_collection) * HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) { kfree(device); return NULL; } memset(device->collection, 0, sizeof(struct hid_collection) * HID_DEFAULT_NUM_COLLECTIONS); device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; for (i = 0; i < HID_REPORT_TYPES; i++) INIT_LIST_HEAD(&device->report_enum[i].report_list); if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) { kfree(device->collection); kfree(device); return NULL; } memcpy(device->rdesc, start, size); device->rsize = size; if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) { kfree(device->rdesc); kfree(device->collection); kfree(device); return NULL; } memset(parser, 0, sizeof(struct hid_parser)); parser->device = device; end = start + size; while ((start = fetch_item(start, end, &item)) != NULL) { if (item.format != HID_ITEM_FORMAT_SHORT) { dbg("unexpected long global item"); kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; } if (dispatch_type[item.type](parser, &item)) { dbg("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; } if (start == end) { if (parser->collection_stack_ptr) { dbg("unbalanced collection at end of report description"); kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; } if (parser->local.delimiter_depth) { dbg("unbalanced delimiter at end of report description"); kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; } kfree(parser); return device; } } dbg("item fetching failed at offset %d\n", (int)(end - start)); kfree(device->collection); hid_free_device(device); kfree(parser); return NULL;}/* * 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 __inline__ __s32 snto32(__u32 value, unsigned n){ switch (n) { case 8: return ((__s8)value); case 16: return ((__s16)value); case 32: return ((__s32)value); } return value & (1 << (n - 1)) ? value | (-1 << n) : value;}/* * Convert a signed 32-bit integer to a signed n-bit integer. */static __inline__ __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 report. */static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n){ report += (offset >> 5) << 2; offset &= 31; return (le64_to_cpu(get_unaligned((__le64*)report)) >> offset) & ((1ULL << n) - 1);}static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value){ report += (offset >> 5) << 2; offset &= 31; put_unaligned((get_unaligned((__le64*)report) & cpu_to_le64(~((((__u64) 1 << n) - 1) << offset))) | cpu_to_le64((__u64)value << offset), (__le64*)report);}/* * Search an array for a value. */static __inline__ int search(__s32 *array, __s32 value, unsigned n){ while (n--) { if (*array++ == value) return 0; } return -1;}static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt, struct pt_regs *regs){ hid_dump_input(usage, value); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_hid_event(hid, field, usage, value, regs); if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt) hiddev_hid_event(hid, field, usage, value, regs);}/* * Analyse a received field, and fetch the data from it. The field * content is stored for next report processing (we do differential * reporting to the layer). */static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, int interrupt, struct pt_regs *regs){ unsigned n; unsigned count = field->report_count; unsigned offset = field->report_offset; unsigned size = field->report_size; __s32 min = field->logical_minimum; __s32 max = field->logical_maximum; __s32 *value; if (!(value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC))) return; for (n = 0; n < count; n++) { value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) : extract(data, offset + n * size, size); if (!(field->flags & HID_MAIN_ITEM_VARIABLE) /* Ignore report if ErrorRollOver */ && value[n] >= min && value[n] <= max && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) goto exit; } for (n = 0; n < count; n++) { if (HID_MAIN_ITEM_VARIABLE & field->flags) { hid_process_event(hid, field, &field->usage[n], value[n], interrupt, regs); continue; } if (field->value[n] >= min && field->value[n] <= max && field->usage[field->value[n] - min].hid && search(value, field->value[n], count)) hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt, regs); if (value[n] >= min && value[n] <= max && field->usage[value[n] - min].hid && search(field->value, value[n], count)) hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt, regs); } memcpy(field->value, value, count * sizeof(__s32));exit: kfree(value);}static int hid_input_report(int type, struct urb *urb, int interrupt, struct pt_regs *regs){ struct hid_device *hid = urb->context; struct hid_report_enum *report_enum = hid->report_enum + type; u8 *data = urb->transfer_buffer; int len = urb->actual_length; struct hid_report *report; int n, size; if (!len) { dbg("empty report"); return -1; }#ifdef DEBUG_DATA printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered)\n", len, report_enum->numbered ? "" : "un");#endif n = 0; /* Normally report number is 0 */ if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ n = *data++; len--; }#ifdef DEBUG_DATA { int i; printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, len); for (i = 0; i < len; i++) printk(" %02x", data[i]); printk("\n"); }#endif if (!(report = report_enum->report_id_hash[n])) { dbg("undefined report_id %d received", n); return -1; } size = ((report->size - 1) >> 3) + 1; if (len < size) { dbg("report %d is too short, (%d < %d)", report->id, len, size); memset(data + len, 0, size - len); } if (hid->claimed & HID_CLAIMED_HIDDEV) hiddev_report_event(hid, report); for (n = 0; n < report->maxfield; n++) hid_input_field(hid, report->field[n], data, interrupt, regs); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_report_event(hid, report); return 0;}/* * Input interrupt completion handler. */static void hid_irq_in(struct urb *urb, struct pt_regs *regs){ struct hid_device *hid = urb->context; int status; switch (urb->status) { case 0: /* success */ hid_input_report(HID_INPUT_REPORT, urb, 1, regs); break; case -ECONNRESET: /* unlink */ case -ENOENT: case -EPERM: case -ESHUTDOWN: /* unplug */ case -EILSEQ: /* unplug timeout on uhci */ return; case -ETIMEDOUT: /* NAK */ break; default: /* error */ warn("input irq status %d received", urb->status); } status = usb_submit_urb(urb, SLAB_ATOMIC); if (status) err("can't resubmit intr, %s-%s/input%d, status %d", hid->dev->bus->bus_name, hid->dev->devpath, hid->ifnum, status);}/* * Output the field into the report. */static void hid_output_field(struct hid_field *field, __u8 *data){ unsigned count = field->report_count; unsigned offset = field->report_offset; unsigned size = field->report_size; unsigned n; for (n = 0; n < count; n++) { if (field->logical_minimum < 0) /* signed values */ implement(data, offset + n * size, size, s32ton(field->value[n], size)); else /* unsigned values */ implement(data, offset + n * size, size, field->value[n]); }}/* * Create a report. */static void hid_output_report(struct hid_report *report, __u8 *data){ unsigned n; if (report->id > 0) *data++ = report->id; for (n = 0; n < report->maxfield; n++) hid_output_field(report->field[n], data);}/* * Set a field value. The report this field belongs to has to be * created and transferred to the device, to set this value in the * device. */int hid_set_field(struct hid_field *field, unsigned offset, __s32 value){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -