?? hid-core.c
字號(hào):
/* * Process a reserved item. */static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item){ dbg_hid("reserved item type, tag 0x%x\n", 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. */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); } } kfree(device->rdesc); kfree(device->collection); kfree(device);}EXPORT_SYMBOL_GPL(hid_free_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. */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 = kzalloc(sizeof(struct hid_device), GFP_KERNEL))) return NULL; if (!(device->collection = kzalloc(sizeof(struct hid_collection) * HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) { kfree(device); return NULL; } 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 = kmalloc(size, GFP_KERNEL))) { kfree(device->collection); kfree(device); return NULL; } memcpy(device->rdesc, start, size); device->rsize = size; if (!(parser = vmalloc(sizeof(struct hid_parser)))) { 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_hid("unexpected long global item\n"); hid_free_device(device); vfree(parser); return NULL; } if (dispatch_type[item.type](parser, &item)) { dbg_hid("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); hid_free_device(device); vfree(parser); return NULL; } if (start == end) { if (parser->collection_stack_ptr) { dbg_hid("unbalanced collection at end of report description\n"); hid_free_device(device); vfree(parser); return NULL; } if (parser->local.delimiter_depth) { dbg_hid("unbalanced delimiter at end of report description\n"); hid_free_device(device); vfree(parser); return NULL; } vfree(parser); return device; } } dbg_hid("item fetching failed at offset %d\n", (int)(end - start)); hid_free_device(device); vfree(parser); return NULL;}EXPORT_SYMBOL_GPL(hid_parse_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){ 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 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: * http://www.usb.org/developers/devclass_docs/HID1_11.pdf * * While the USB HID spec allows unlimited length bit fields in "report * descriptors", most devices never use more than 16 bits. * One model of UPS is claimed to report "LINEV" as a 32-bit field. * Search linux-kernel and linux-usb-devel archives for "hid-core extract". */static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n){ u64 x; WARN_ON(n > 32); report += offset >> 3; /* adjust byte index */ offset &= 7; /* now only need bit offset into one byte */ x = le64_to_cpu(get_unaligned((__le64 *) report)); x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */ return (u32) x;}/* * "implement" : set bits in a little endian bit stream. * Same concepts as "extract" (see comments above). * The data mangled in the bit stream remains in little endian * order the whole time. It make more sense to talk about * endianness of register values by considering a register * a "cached" copy of the little endiad bit stream. */static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value){ __le64 x; u64 m = (1ULL << n) - 1; WARN_ON(n > 32); WARN_ON(value > m); value &= m; report += offset >> 3; offset &= 7; x = get_unaligned((__le64 *)report); x &= cpu_to_le64(~(m << offset)); x |= cpu_to_le64(((u64) value) << offset); put_unaligned(x, (__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){ hid_dump_input(usage, value); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_hid_event(hid, field, usage, value); if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event) hid->hiddev_hid_event(hid, field, usage, value);}/* * 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). */void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, int interrupt){ 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); 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); 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); } memcpy(field->value, value, count * sizeof(__s32));exit: kfree(value);}EXPORT_SYMBOL_GPL(hid_input_field);/* * 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 bitsused = offset + count * size; unsigned n; /* make sure the unused bits in the last byte are zeros */ if (count > 0 && size > 0 && (bitsused % 8) != 0) data[(bitsused-1)/8] &= (1 << (bitsused % 8)) - 1; 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; if (report->id > 0) *data++ = report->id; for (n = 0; n < report->maxfield; n++) hid_output_field(report->field[n], data);}EXPORT_SYMBOL_GPL(hid_output_report);/* * 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){ unsigned size = field->report_size; hid_dump_input(field->usage + offset, value); if (offset >= field->report_count) { dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); hid_dump_field(field, 8); return -1; } if (field->logical_minimum < 0) { if (value != snto32(s32ton(value, size), size)) { dbg_hid("value %d is out of range\n", value); return -1; } } field->value[offset] = value; return 0;}EXPORT_SYMBOL_GPL(hid_set_field);int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt){ struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; int n, rsize, i; if (!hid) return -ENODEV; if (!size) { dbg_hid("empty report\n"); return -1; } dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); n = 0; /* Normally report number is 0 */ if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ n = *data++; size--; } /* dump the report descriptor */ dbg_hid("report %d (size %u) = ", n, size); for (i = 0; i < size; i++) dbg_hid_line(" %02x", data[i]); dbg_hid_line("\n"); if (!(report = report_enum->report_id_hash[n])) { dbg_hid("undefined report_id %d received\n", n); return -1; } rsize = ((report->size - 1) >> 3) + 1; if (size < rsize) { dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize); memset(data + size, 0, rsize - size); } if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) hid->hiddev_report_event(hid, report); if (hid->claimed & HID_CLAIMED_HIDRAW) hidraw_report_event(hid, data, size); for (n = 0; n < report->maxfield; n++) hid_input_field(hid, report->field[n], data, interrupt); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_report_event(hid, report); return 0;}EXPORT_SYMBOL_GPL(hid_input_report);static int __init hid_init(void){ return hidraw_init();}static void __exit hid_exit(void){ hidraw_exit();}module_init(hid_init);module_exit(hid_exit);MODULE_LICENSE(DRIVER_LICENSE);
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -