/*
* HID support for Linux
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
*/
/*
* 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.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <linux/input.h>
#include <linux/wait.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/hid.h>
#include <linux/hiddev.h>
#include <linux/hid-debug.h>
#include <linux/hidraw.h>
/*
* Version Information
*/
#define DRIVER_VERSION "v2.6"
#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
#define DRIVER_DESC "HID core driver"
#define DRIVER_LICENSE "GPL"
#ifdef CONFIG_HID_DEBUG
int hid_debug = 0;
module_param_named(debug, hid_debug, bool, 0600);
MODULE_PARM_DESC(debug, "Turn HID debugging mode on and off");
EXPORT_SYMBOL_GPL(hid_debug);
#endif
/*
* 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 = kzalloc(sizeof(struct hid_report), GFP_KERNEL)))
return NULL;
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_hid("too many fields in report\n");
return NULL;
}
if (!(field = kzalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ values * sizeof(unsigned), GFP_KERNEL))) return NULL;
field->index = report->maxfield++;
report->field[field->index] = 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 (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
dbg_hid("collection stack overflow\n");
return -1;
}
if (parser->device->maxcollection == parser->device->collection_size) {
collection = kmalloc(sizeof(struct hid_collection) *
parser->device->collection_size * 2, GFP_KERNEL);
if (collection == NULL) {
dbg_hid("failed to reallocate collection array\n");
return -1;
}
memcpy(collection, parser->device->collection,
sizeof(struct hid_collection) *
parser->device->collection_size);
memset(collection + parser->device->collection_size, 0,
sizeof(struct hid_collection) *
parser->device->collection_size);
kfree(parser->device->collection);
parser->device->collection = collection;
parser->device->collection_size *= 2;
}
parser->collection_stack[parser->collection_stack_ptr++] =
parser->device->maxcollection;
collection = parser->device->collection +
parser->device->maxcollection++;
collection->type = type;
collection->usage = usage;