📄 hid-core.c
字号:
/* * Free a device structure, all reports, and all fields. */static void hid_free_device(struct hid_device *device){ unsigned i,j; 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); } } if (device->rdesc) kfree(device->rdesc);}/* * 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(((__u16*)start)++)); return start; case 3: item->size++; if ((end - start) < 4) return NULL; item->data.u32 = le32_to_cpu(get_unaligned(((__u32*)start)++)); 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)); 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); return NULL; } memcpy(device->rdesc, start, size); if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) { kfree(device->rdesc); kfree(device); return NULL; } memset(parser, 0, sizeof(struct hid_parser)); parser->device = device; end = start + size; while ((start = fetch_item(start, end, &item)) != 0) { if (item.format != HID_ITEM_FORMAT_SHORT) { dbg("unexpected long global item"); 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); hid_free_device(device); kfree(parser); return NULL; } if (start == end) { if (parser->collection_stack_ptr) { dbg("unbalanced collection at end of report description"); hid_free_device(device); kfree(parser); return NULL; } if (parser->local.delimiter_depth) { dbg("unbalanced delimiter at end of report description"); hid_free_device(device); kfree(parser); return NULL; } kfree(parser); return device; } } dbg("item fetching failed at offset %d\n", (int)(end - start)); 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((__u64*)report)) >> offset) & ((1 << n) - 1);}static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value){ report += (offset >> 5) << 2; offset &= 31; put_unaligned((get_unaligned((__u64*)report) & cpu_to_le64(~((((__u64) 1 << n) - 1) << offset))) | cpu_to_le64((__u64)value << offset), (__u64*)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){ hid_dump_input(usage, value); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_hid_event(hid, field, usage, value);#ifdef CONFIG_USB_HIDDEV if (hid->claimed & HID_CLAIMED_HIDDEV) { struct hiddev_usage_ref uref; unsigned type = field->report_type; uref.report_type = (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); uref.report_id = field->report->id; uref.field_index = field->index; uref.usage_index = (usage - field->usage); uref.usage_code = usage->hid; uref.value = value; hiddev_hid_event(hid, &uref); }#endif}/* * 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){ 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[count]; /* WARNING: gcc specific */ 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) return; } for (n = 0; n < count; n++) { if (HID_MAIN_ITEM_VARIABLE & field->flags) { if (field->flags & HID_MAIN_ITEM_RELATIVE) { if (!value[n]) continue; } else { if (value[n] == field->value[n]) continue; } hid_process_event(hid, field, &field->usage[n], value[n]); 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); 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); } memcpy(field->value, value, count * sizeof(__s32));}static int hid_input_report(int type, struct urb *urb){ 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 < n; 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; }#ifdef CONFIG_USB_HIDDEV /* Notify listeners that a report has been received */ if (hid->claimed & HID_CLAIMED_HIDDEV) { struct hiddev_usage_ref uref; memset(&uref, 0, sizeof(uref)); uref.report_type = (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); uref.report_id = report->id; uref.field_index = HID_FIELD_INDEX_NONE; hiddev_hid_event(hid, &uref); }#endif size = ((report->size - 1) >> 3) + 1; if (len < size) { dbg("report %d is too short, (%d < %d)", report->id, len, size); return -1; } for (n = 0; n < report->maxfield; n++) hid_input_field(hid, report->field[n], data); return 0;}/* * Input interrupt completion handler. */static void hid_irq_in(struct urb *urb){ if (urb->status) { dbg("nonzero status in input irq %d", urb->status); return; } hid_input_report(HID_INPUT_REPORT, urb);}/* * 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. */void hid_output_report(struct hid_report *report, __u8 *data){ unsigned n; 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 transfered to the device, to set this value in the * device. */int hid_set_field(struct hid_field *field, unsigned offset, __s32 value){ unsigned size = field->report_size; hid_dump_input(field->usage + offset, value); if (offset >= field->report_count) { dbg("offset (%d) exceeds report_count (%d)", offset, field->report_count); hid_dump_field(field, 8); return -1; } if (field->logical_minimum < 0) { if (value != snto32(s32ton(value, size), size)) { dbg("value %d is out of range", value); return -1; } } field->value[offset] = value; return 0;}int hid_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field){ struct hid_report_enum *report_enum = hid->report_enum + HID_OUTPUT_REPORT; struct list_head *list = report_enum->report_list.next; int i, j; while (list != &report_enum->report_list) { struct hid_report *report = (struct hid_report *) list; list = list->next; for (i = 0; i < report->maxfield; i++) { *field = report->field[i]; for (j = 0; j < (*field)->maxusage; j++) if ((*field)->usage[j].type == type && (*field)->usage[j].code == code) return j; } } return -1;}/* * Find a report with a specified HID usage. */int hid_find_report_by_usage(struct hid_device *hid, __u32 wanted_usage, struct hid_report **report, int type){ struct hid_report_enum *report_enum = hid->report_enum + type; struct list_head *list = report_enum->report_list.next; int i, j; while (list != &report_enum->report_list) { *report = (struct hid_report *) list; list = list->next; for (i = 0; i < (*report)->maxfield; i++) { struct hid_field *field = (*report)->field[i]; for (j = 0; j < field->maxusage; j++) if (field->logical == wanted_usage) return j; } } return -1;}int hid_find_field_in_report(struct hid_report *report, __u32 wanted_usage, struct hid_field **field){ int i, j; for (i = 0; i < report->maxfield; i++) { *field = report->field[i]; for (j = 0; j < (*field)->maxusage; j++) if ((*field)->usage[j].hid == wanted_usage) return j; } return -1;}static int hid_submit_out(struct hid_device *hid){ struct hid_report *report; report = hid->out[hid->outtail]; hid_output_report(report, hid->outbuf); hid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1; hid->urbout->dev = hid->dev; dbg("submitting out urb"); if (usb_submit_urb(hid->urbout, GFP_ATOMIC)) { err("usb_submit_urb(out) failed"); return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -