📄 hid-core.c
字号:
/* * $Id: hid-core.c,v 1.8 2001/05/23 12:02:18 vojtech Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2001 Vojtech Pavlik * * USB HID support for Linux * * Sponsored by SuSE *//* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic */#include <linux/module.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/list.h>#include <linux/mm.h>#include <linux/smp_lock.h>#include <linux/spinlock.h>#include <asm/unaligned.h>#include <linux/input.h>#undef DEBUG#undef DEBUG_DATA#include <linux/usb.h>#include "hid.h"#ifdef CONFIG_USB_HIDDEV#include <linux/hiddev.h>#endif/* * Version Information */#define DRIVER_VERSION "v1.8"#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik <vojtech@suse.cz>"#define DRIVER_DESC "USB HID support drivers"static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};/* * Register a new report for a device. */static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id){ struct hid_report_enum *report_enum = device->report_enum + type; struct hid_report *report; if (report_enum->report_id_hash[id]) return report_enum->report_id_hash[id]; if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL))) return NULL; memset(report, 0, sizeof(struct hid_report)); if (id != 0) report_enum->numbered = 1; report->id = id; report->type = type; report->size = 0; report->device = device; report_enum->report_id_hash[id] = report; list_add_tail(&report->list, &report_enum->report_list); return report;}/* * Register a new field for this report. */static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values){ struct hid_field *field; if (report->maxfield == HID_MAX_FIELDS) { dbg("too many fields in report"); return NULL; } if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + values * sizeof(unsigned), GFP_KERNEL))) return NULL; memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + values * sizeof(unsigned)); report->field[report->maxfield++] = field; field->usage = (struct hid_usage *)(field + 1); field->value = (unsigned *)(field->usage + usages); field->report = report; return field;}/* * Open a collection. The type/usage is pushed on the stack. */static int open_collection(struct hid_parser *parser, unsigned type){ struct hid_collection *collection; unsigned usage; usage = parser->local.usage[0]; if (type == HID_COLLECTION_APPLICATION && parser->device->maxapplication < HID_MAX_APPLICATIONS) parser->device->application[parser->device->maxapplication++] = usage; if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { dbg("collection stack overflow"); return -1; } collection = parser->collection_stack + parser->collection_stack_ptr++; collection->type = type; collection->usage = usage; return 0;}/* * Close a collection. */static int close_collection(struct hid_parser *parser){ if (!parser->collection_stack_ptr) { dbg("collection stack underflow"); return -1; } parser->collection_stack_ptr--; return 0;}/* * Climb up the stack, search for the specified collection type * and return the usage. */static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type){ int n; for (n = parser->collection_stack_ptr - 1; n >= 0; n--) if (parser->collection_stack[n].type == type) return parser->collection_stack[n].usage; return 0; /* we know nothing about this usage type */}/* * Add a usage to the temporary parser table. */static int hid_add_usage(struct hid_parser *parser, unsigned usage){ if (parser->local.usage_index >= HID_MAX_USAGES) { dbg("usage index exceeded"); return -1; } parser->local.usage[parser->local.usage_index++] = usage; return 0;}/* * Register a new field for this report. */static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags){ struct hid_report *report; struct hid_field *field; int usages; unsigned offset; int i; if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) { dbg("hid_register_report failed"); return -1; } if (HID_MAIN_ITEM_VARIABLE & ~flags) { /* ARRAY */ if (parser->global.logical_maximum <= parser->global.logical_minimum) { dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); return -1; } usages = parser->local.usage_index; /* Hint: we can assume usages < MAX_USAGE here */ } else { /* VARIABLE */ usages = parser->global.report_count; } offset = report->size; report->size += parser->global.report_size * parser->global.report_count; if (usages == 0) return 0; /* ignore padding fields */ if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL) return 0; field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL); field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); for (i = 0; i < usages; i++) field->usage[i].hid = parser->local.usage[i]; field->maxusage = usages; field->flags = flags; field->report_offset = offset; field->report_type = report_type; field->report_size = parser->global.report_size; field->report_count = parser->global.report_count; field->logical_minimum = parser->global.logical_minimum; field->logical_maximum = parser->global.logical_maximum; field->physical_minimum = parser->global.physical_minimum; field->physical_maximum = parser->global.physical_maximum; field->unit_exponent = parser->global.unit_exponent; field->unit = parser->global.unit; return 0;}/* * Read data value from item. */static __inline__ __u32 item_udata(struct hid_item *item){ switch (item->size) { case 1: return item->data.u8; case 2: return item->data.u16; case 4: return item->data.u32; } return 0;}static __inline__ __s32 item_sdata(struct hid_item *item){ switch (item->size) { case 1: return item->data.s8; case 2: return item->data.s16; case 4: return item->data.s32; } return 0;}/* * Process a global item. */static int hid_parser_global(struct hid_parser *parser, struct hid_item *item){ switch (item->tag) { case HID_GLOBAL_ITEM_TAG_PUSH: if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { dbg("global enviroment stack overflow"); return -1; } memcpy(parser->global_stack + parser->global_stack_ptr++, &parser->global, sizeof(struct hid_global)); return 0; case HID_GLOBAL_ITEM_TAG_POP: if (!parser->global_stack_ptr) { dbg("global enviroment stack underflow"); return -1; } memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr, sizeof(struct hid_global)); return 0; case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: parser->global.usage_page = item_udata(item); return 0; case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: parser->global.logical_minimum = item_sdata(item); return 0; case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: parser->global.logical_maximum = item_sdata(item); return 0; case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: parser->global.physical_minimum = item_sdata(item); return 0; case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: parser->global.physical_maximum = item_sdata(item); return 0; case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: parser->global.unit_exponent = item_udata(item); return 0; case HID_GLOBAL_ITEM_TAG_UNIT: parser->global.unit = item_udata(item); return 0; case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: if ((parser->global.report_size = item_udata(item)) > 32) { dbg("invalid report_size %d", parser->global.report_size); return -1; } return 0; case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) { dbg("invalid report_count %d", parser->global.report_count); return -1; } return 0; case HID_GLOBAL_ITEM_TAG_REPORT_ID: if ((parser->global.report_id = item_udata(item)) == 0) { dbg("report_id 0 is invalid"); return -1; } return 0; default: dbg("unknown global tag 0x%x", item->tag); return -1; }}/* * Process a local item. */static int hid_parser_local(struct hid_parser *parser, struct hid_item *item){ __u32 data; unsigned n; if (item->size == 0) { dbg("item data expected for local item"); return -1; } data = item_udata(item); switch (item->tag) { case HID_LOCAL_ITEM_TAG_DELIMITER: if (data) { /* * We treat items before the first delimiter * as global to all usage sets (branch 0). * In the moment we process only these global * items and the first delimiter set. */ if (parser->local.delimiter_depth != 0) { dbg("nested delimiters"); return -1; } parser->local.delimiter_depth++; parser->local.delimiter_branch++; } else { if (parser->local.delimiter_depth < 1) { dbg("bogus close delimiter"); return -1; } parser->local.delimiter_depth--; } return 1; case HID_LOCAL_ITEM_TAG_USAGE: if (parser->local.delimiter_branch > 1) { dbg("alternative usage ignored"); return 0; } if (item->size <= 2) data = (parser->global.usage_page << 16) + data; return hid_add_usage(parser, data); case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: if (parser->local.delimiter_branch > 1) { dbg("alternative usage ignored"); return 0; } if (item->size <= 2) data = (parser->global.usage_page << 16) + data; parser->local.usage_minimum = data; return 0; case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: if (parser->local.delimiter_branch > 1) { dbg("alternative usage ignored"); return 0; } if (item->size <= 2) data = (parser->global.usage_page << 16) + data; for (n = parser->local.usage_minimum; n <= data; n++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -