2006-12-08 20:40:44 +03:00
/*
2006-12-08 20:40:53 +03:00
* HID support for Linux
2006-12-08 20:40:44 +03:00
*
* 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
2007-04-16 13:29:28 +04:00
* Copyright ( c ) 2006 - 2007 Jiri Kosina
2006-12-08 20:40:44 +03:00
*/
/*
* 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>
2007-03-12 16:55:12 +03:00
# include <linux/vmalloc.h>
2007-11-30 13:12:58 +03:00
# include <linux/sched.h>
2006-12-08 20:40:44 +03:00
# include <linux/hid.h>
# include <linux/hiddev.h>
2007-01-25 13:43:31 +03:00
# include <linux/hid-debug.h>
2007-05-14 11:57:40 +04:00
# include <linux/hidraw.h>
2006-12-08 20:40:44 +03:00
2008-05-16 13:49:19 +04:00
# include "hid-ids.h"
2006-12-08 20:40:44 +03:00
/*
* Version Information
*/
# define DRIVER_VERSION "v2.6"
2007-04-16 13:29:28 +04:00
# define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
2007-01-09 15:24:25 +03:00
# define DRIVER_DESC "HID core driver"
2006-12-08 20:40:44 +03:00
# define DRIVER_LICENSE "GPL"
2007-05-30 17:07:13 +04:00
# ifdef CONFIG_HID_DEBUG
int hid_debug = 0 ;
2008-03-23 01:50:13 +03:00
module_param_named ( debug , hid_debug , int , 0600 ) ;
MODULE_PARM_DESC ( debug , " HID debugging (0=off, 1=probing info, 2=continuous data dumping) " ) ;
2007-05-30 17:07:13 +04:00
EXPORT_SYMBOL_GPL ( hid_debug ) ;
# endif
2006-12-08 20:40:44 +03:00
/*
* 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 ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " too many fields in report \n " ) ;
2006-12-08 20:40:44 +03:00
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 ) ;
2008-03-28 19:06:41 +03:00
field - > value = ( s32 * ) ( field - > usage + usages ) ;
2006-12-08 20:40:44 +03:00
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 ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " collection stack overflow \n " ) ;
2006-12-08 20:40:44 +03:00
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 ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " failed to reallocate collection array \n " ) ;
2006-12-08 20:40:44 +03:00
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 ;
collection - > level = parser - > collection_stack_ptr - 1 ;
if ( type = = HID_COLLECTION_APPLICATION )
parser - > device - > maxapplication + + ;
return 0 ;
}
/*
* Close a collection .
*/
static int close_collection ( struct hid_parser * parser )
{
if ( ! parser - > collection_stack_ptr ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " collection stack underflow \n " ) ;
2006-12-08 20:40:44 +03:00
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 - > device - > collection [ parser - > collection_stack [ n ] ] . type = = type )
return parser - > device - > collection [ 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 ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " usage index exceeded \n " ) ;
2006-12-08 20:40:44 +03:00
return - 1 ;
}
parser - > local . usage [ parser - > local . usage_index ] = usage ;
parser - > local . collection_index [ parser - > local . usage_index ] =
parser - > collection_stack_ptr ?
parser - > collection_stack [ parser - > collection_stack_ptr - 1 ] : 0 ;
parser - > local . usage_index + + ;
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 ) ) ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " hid_register_report failed \n " ) ;
2006-12-08 20:40:44 +03:00
return - 1 ;
}
if ( parser - > global . logical_maximum < parser - > global . logical_minimum ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " logical range invalid %d %d \n " , parser - > global . logical_minimum , parser - > global . logical_maximum ) ;
2006-12-08 20:40:44 +03:00
return - 1 ;
}
offset = report - > size ;
report - > size + = parser - > global . report_size * parser - > global . report_count ;
if ( ! parser - > local . usage_index ) /* Ignore padding fields */
return 0 ;
usages = max_t ( int , parser - > local . usage_index , parser - > global . report_count ) ;
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 + + ) {
int j = i ;
/* Duplicate the last usage we parsed if we have excess values */
if ( i > = parser - > local . usage_index )
j = parser - > local . usage_index - 1 ;
field - > usage [ i ] . hid = parser - > local . usage [ j ] ;
field - > usage [ i ] . collection_index =
parser - > local . collection_index [ j ] ;
}
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 u32 item_udata ( struct hid_item * item )
{
switch ( item - > size ) {
2008-06-19 01:55:41 +04:00
case 1 : return item - > data . u8 ;
case 2 : return item - > data . u16 ;
case 4 : return item - > data . u32 ;
2006-12-08 20:40:44 +03:00
}
return 0 ;
}
static s32 item_sdata ( struct hid_item * item )
{
switch ( item - > size ) {
2008-06-19 01:55:41 +04:00
case 1 : return item - > data . s8 ;
case 2 : return item - > data . s16 ;
case 4 : return item - > data . s32 ;
2006-12-08 20:40:44 +03:00
}
return 0 ;
}
/*
* Process a global item .
*/
static int hid_parser_global ( struct hid_parser * parser , struct hid_item * item )
{
switch ( item - > tag ) {
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_PUSH :
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( parser - > global_stack_ptr = = HID_GLOBAL_STACK_SIZE ) {
dbg_hid ( " global enviroment stack overflow \n " ) ;
return - 1 ;
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
memcpy ( parser - > global_stack + parser - > global_stack_ptr + + ,
& parser - > global , sizeof ( struct hid_global ) ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_POP :
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( ! parser - > global_stack_ptr ) {
dbg_hid ( " global enviroment stack underflow \n " ) ;
return - 1 ;
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
memcpy ( & parser - > global , parser - > global_stack +
- - parser - > global_stack_ptr , sizeof ( struct hid_global ) ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_USAGE_PAGE :
parser - > global . usage_page = item_udata ( item ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM :
parser - > global . logical_minimum = item_sdata ( item ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM :
if ( parser - > global . logical_minimum < 0 )
parser - > global . logical_maximum = item_sdata ( item ) ;
else
parser - > global . logical_maximum = item_udata ( item ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM :
parser - > global . physical_minimum = item_sdata ( item ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM :
if ( parser - > global . physical_minimum < 0 )
parser - > global . physical_maximum = item_sdata ( item ) ;
else
parser - > global . physical_maximum = item_udata ( item ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT :
parser - > global . unit_exponent = item_sdata ( item ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_UNIT :
parser - > global . unit = item_udata ( item ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE :
parser - > global . report_size = item_udata ( item ) ;
if ( parser - > global . report_size > 32 ) {
dbg_hid ( " invalid report_size %d \n " ,
parser - > global . report_size ) ;
return - 1 ;
}
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_REPORT_COUNT :
parser - > global . report_count = item_udata ( item ) ;
if ( parser - > global . report_count > HID_MAX_USAGES ) {
dbg_hid ( " invalid report_count %d \n " ,
parser - > global . report_count ) ;
return - 1 ;
}
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GLOBAL_ITEM_TAG_REPORT_ID :
parser - > global . report_id = item_udata ( item ) ;
if ( parser - > global . report_id = = 0 ) {
dbg_hid ( " report_id 0 is invalid \n " ) ;
2006-12-08 20:40:44 +03:00
return - 1 ;
2008-06-19 01:55:41 +04:00
}
return 0 ;
default :
dbg_hid ( " unknown global tag 0x%x \n " , item - > tag ) ;
return - 1 ;
2006-12-08 20:40:44 +03:00
}
}
/*
* 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 ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " item data expected for local item \n " ) ;
2006-12-08 20:40:44 +03:00
return - 1 ;
}
data = item_udata ( item ) ;
switch ( item - > tag ) {
2008-06-19 01:55:41 +04:00
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_hid ( " nested delimiters \n " ) ;
return - 1 ;
2006-12-08 20:40:44 +03:00
}
2008-06-19 01:55:41 +04:00
parser - > local . delimiter_depth + + ;
parser - > local . delimiter_branch + + ;
} else {
if ( parser - > local . delimiter_depth < 1 ) {
dbg_hid ( " bogus close delimiter \n " ) ;
return - 1 ;
2006-12-08 20:40:44 +03:00
}
2008-06-19 01:55:41 +04:00
parser - > local . delimiter_depth - - ;
}
return 1 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_LOCAL_ITEM_TAG_USAGE :
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( parser - > local . delimiter_branch > 1 ) {
dbg_hid ( " alternative usage ignored \n " ) ;
return 0 ;
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( item - > size < = 2 )
data = ( parser - > global . usage_page < < 16 ) + data ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
return hid_add_usage ( parser , data ) ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM :
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( parser - > local . delimiter_branch > 1 ) {
dbg_hid ( " alternative usage ignored \n " ) ;
2006-12-08 20:40:44 +03:00
return 0 ;
2008-06-19 01:55:41 +04:00
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( item - > size < = 2 )
data = ( parser - > global . usage_page < < 16 ) + data ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
parser - > local . usage_minimum = data ;
return 0 ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM :
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( parser - > local . delimiter_branch > 1 ) {
dbg_hid ( " alternative usage ignored \n " ) ;
2006-12-08 20:40:44 +03:00
return 0 ;
2008-06-19 01:55:41 +04:00
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( item - > size < = 2 )
data = ( parser - > global . usage_page < < 16 ) + data ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
for ( n = parser - > local . usage_minimum ; n < = data ; n + + )
if ( hid_add_usage ( parser , n ) ) {
dbg_hid ( " hid_add_usage failed \n " ) ;
return - 1 ;
}
return 0 ;
default :
dbg_hid ( " unknown local item tag 0x%x \n " , item - > tag ) ;
return 0 ;
2006-12-08 20:40:44 +03:00
}
return 0 ;
}
/*
* Process a main item .
*/
static int hid_parser_main ( struct hid_parser * parser , struct hid_item * item )
{
__u32 data ;
int ret ;
data = item_udata ( item ) ;
switch ( item - > tag ) {
2008-06-19 01:55:41 +04:00
case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION :
ret = open_collection ( parser , data & 0xff ) ;
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_hid ( " unknown main item tag 0x%x \n " , item - > tag ) ;
ret = 0 ;
2006-12-08 20:40:44 +03:00
}
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 )
{
2007-05-30 17:07:13 +04:00
dbg_hid ( " reserved item type, tag 0x%x \n " , item - > tag ) ;
2006-12-08 20:40:44 +03:00
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 .
*/
2008-05-16 13:49:15 +04:00
static void hid_device_release ( struct device * dev )
2006-12-08 20:40:44 +03:00
{
2008-05-16 13:49:15 +04:00
struct hid_device * device = container_of ( dev , struct hid_device , dev ) ;
unsigned i , j ;
2006-12-08 20:40:44 +03:00
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 ) ;
2007-01-25 01:05:07 +03:00
kfree ( device - > collection ) ;
2006-12-08 20:40:44 +03:00
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 ) {
2008-06-19 01:55:41 +04:00
case 0 :
return start ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
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 = get_unaligned_le16 ( start ) ;
start = ( __u8 * ) ( ( __le16 * ) start + 1 ) ;
return start ;
case 3 :
item - > size + + ;
if ( ( end - start ) < 4 )
return NULL ;
item - > data . u32 = get_unaligned_le32 ( start ) ;
start = ( __u8 * ) ( ( __le32 * ) start + 1 ) ;
return start ;
2006-12-08 20:40:44 +03:00
}
return NULL ;
}
2008-05-16 13:49:15 +04:00
/**
* hid_parse_report - parse device report
*
* @ device : hid device
* @ start : report start
* @ size : report size
*
2006-12-08 20:40:44 +03:00
* Parse a report description into a hid_device structure . Reports are
* enumerated , fields are attached to these reports .
2008-05-16 13:49:15 +04:00
* 0 returned on success , otherwise nonzero error value .
2006-12-08 20:40:44 +03:00
*/
2008-05-16 13:49:15 +04:00
int hid_parse_report ( struct hid_device * device , __u8 * start ,
unsigned size )
2006-12-08 20:40:44 +03:00
{
struct hid_parser * parser ;
struct hid_item item ;
__u8 * end ;
2008-05-16 13:49:15 +04:00
int ret ;
2006-12-08 20:40:44 +03:00
static int ( * dispatch_type [ ] ) ( struct hid_parser * parser ,
struct hid_item * item ) = {
hid_parser_main ,
hid_parser_global ,
hid_parser_local ,
hid_parser_reserved
} ;
2008-05-16 13:49:16 +04:00
if ( device - > driver - > report_fixup )
device - > driver - > report_fixup ( device , start , size ) ;
2008-05-16 13:49:15 +04:00
device - > rdesc = kmalloc ( size , GFP_KERNEL ) ;
if ( device - > rdesc = = NULL )
return - ENOMEM ;
2006-12-08 20:40:44 +03:00
memcpy ( device - > rdesc , start , size ) ;
device - > rsize = size ;
2008-05-16 13:49:15 +04:00
parser = vmalloc ( sizeof ( struct hid_parser ) ) ;
if ( ! parser ) {
ret = - ENOMEM ;
goto err ;
2006-12-08 20:40:44 +03:00
}
2008-05-16 13:49:15 +04:00
2007-03-12 16:55:12 +03:00
memset ( parser , 0 , sizeof ( struct hid_parser ) ) ;
2006-12-08 20:40:44 +03:00
parser - > device = device ;
end = start + size ;
2008-05-16 13:49:15 +04:00
ret = - EINVAL ;
2006-12-08 20:40:44 +03:00
while ( ( start = fetch_item ( start , end , & item ) ) ! = NULL ) {
if ( item . format ! = HID_ITEM_FORMAT_SHORT ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " unexpected long global item \n " ) ;
2008-05-16 13:49:15 +04:00
goto err ;
2006-12-08 20:40:44 +03:00
}
if ( dispatch_type [ item . type ] ( parser , & item ) ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " item %u %u %u %u parsing failed \n " ,
2006-12-08 20:40:44 +03:00
item . format , ( unsigned ) item . size , ( unsigned ) item . type , ( unsigned ) item . tag ) ;
2008-05-16 13:49:15 +04:00
goto err ;
2006-12-08 20:40:44 +03:00
}
if ( start = = end ) {
if ( parser - > collection_stack_ptr ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " unbalanced collection at end of report description \n " ) ;
2008-05-16 13:49:15 +04:00
goto err ;
2006-12-08 20:40:44 +03:00
}
if ( parser - > local . delimiter_depth ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " unbalanced delimiter at end of report description \n " ) ;
2008-05-16 13:49:15 +04:00
goto err ;
2006-12-08 20:40:44 +03:00
}
2007-03-12 16:55:12 +03:00
vfree ( parser ) ;
2008-05-16 13:49:15 +04:00
return 0 ;
2006-12-08 20:40:44 +03:00
}
}
2007-05-30 17:07:13 +04:00
dbg_hid ( " item fetching failed at offset %d \n " , ( int ) ( end - start ) ) ;
2008-05-16 13:49:15 +04:00
err :
2007-03-12 16:55:12 +03:00
vfree ( parser ) ;
2008-05-16 13:49:15 +04:00
return ret ;
2006-12-08 20:40:44 +03:00
}
2006-12-08 20:40:53 +03:00
EXPORT_SYMBOL_GPL ( hid_parse_report ) ;
2006-12-08 20:40:44 +03:00
/*
* 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 ) {
2008-06-19 01:55:41 +04:00
case 8 : return ( ( __s8 ) value ) ;
case 16 : return ( ( __s16 ) value ) ;
case 32 : return ( ( __s32 ) value ) ;
2006-12-08 20:40:44 +03:00
}
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 ;
2007-11-30 13:12:58 +03:00
if ( n > 32 )
printk ( KERN_WARNING " HID: extract() called with n (%d) > 32! (%s) \n " ,
n , current - > comm ) ;
2006-12-08 20:40:44 +03:00
report + = offset > > 3 ; /* adjust byte index */
2006-12-08 20:40:53 +03:00
offset & = 7 ; /* now only need bit offset into one byte */
2008-04-29 12:03:31 +04:00
x = get_unaligned_le64 ( report ) ;
2006-12-08 20:40:53 +03:00
x = ( x > > offset ) & ( ( 1ULL < < n ) - 1 ) ; /* extract bit field */
2006-12-08 20:40:44 +03:00
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 )
{
2008-05-16 13:00:23 +04:00
u64 x ;
2006-12-08 20:40:44 +03:00
u64 m = ( 1ULL < < n ) - 1 ;
2007-11-30 13:12:58 +03:00
if ( n > 32 )
printk ( KERN_WARNING " HID: implement() called with n (%d) > 32! (%s) \n " ,
n , current - > comm ) ;
2006-12-08 20:40:44 +03:00
2007-11-30 13:12:58 +03:00
if ( value > m )
printk ( KERN_WARNING " HID: implement() called with too large value %d! (%s) \n " ,
value , current - > comm ) ;
2006-12-08 20:40:44 +03:00
WARN_ON ( value > m ) ;
value & = m ;
report + = offset > > 3 ;
offset & = 7 ;
2008-05-16 13:00:23 +04:00
x = get_unaligned_le64 ( report ) ;
x & = ~ ( m < < offset ) ;
x | = ( ( u64 ) value ) < < offset ;
put_unaligned_le64 ( x , report ) ;
2006-12-08 20:40:44 +03:00
}
/*
* 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 ;
}
2008-05-16 13:49:15 +04:00
/**
* hid_match_report - check if driver ' s raw_event should be called
*
* @ hid : hid device
* @ report_type : type to match against
*
* compare hid - > driver - > report_table - > report_type to report - > type
*/
static int hid_match_report ( struct hid_device * hid , struct hid_report * report )
2006-12-08 20:40:44 +03:00
{
2008-05-16 13:49:15 +04:00
const struct hid_report_id * id = hid - > driver - > report_table ;
if ( ! id ) /* NULL means all */
return 1 ;
for ( ; id - > report_type ! = HID_TERMINATOR ; id + + )
if ( id - > report_type = = HID_ANY_ID | |
id - > report_type = = report - > type )
return 1 ;
return 0 ;
}
/**
* hid_match_usage - check if driver ' s event should be called
*
* @ hid : hid device
* @ usage : usage to match against
*
* compare hid - > driver - > usage_table - > usage_ { type , code } to
* usage - > usage_ { type , code }
*/
static int hid_match_usage ( struct hid_device * hid , struct hid_usage * usage )
{
const struct hid_usage_id * id = hid - > driver - > usage_table ;
if ( ! id ) /* NULL means all */
return 1 ;
for ( ; id - > usage_type ! = HID_ANY_ID - 1 ; id + + )
if ( ( id - > usage_hid = = HID_ANY_ID | |
id - > usage_hid = = usage - > hid ) & &
( id - > usage_type = = HID_ANY_ID | |
id - > usage_type = = usage - > type ) & &
( id - > usage_code = = HID_ANY_ID | |
id - > usage_code = = usage - > code ) )
return 1 ;
return 0 ;
}
static void hid_process_event ( struct hid_device * hid , struct hid_field * field ,
struct hid_usage * usage , __s32 value , int interrupt )
{
struct hid_driver * hdrv = hid - > driver ;
int ret ;
2006-12-08 20:40:44 +03:00
hid_dump_input ( usage , value ) ;
2008-05-16 13:49:15 +04:00
if ( hdrv & & hdrv - > event & & hid_match_usage ( hid , usage ) ) {
ret = hdrv - > event ( hid , field , usage , value ) ;
if ( ret ! = 0 ) {
if ( ret < 0 )
dbg_hid ( " %s's event failed with %d \n " ,
hdrv - > name , ret ) ;
return ;
}
}
2006-12-08 20:40:44 +03:00
if ( hid - > claimed & HID_CLAIMED_INPUT )
hidinput_hid_event ( hid , field , usage , value ) ;
2006-12-08 20:41:10 +03:00
if ( hid - > claimed & HID_CLAIMED_HIDDEV & & interrupt & & hid - > hiddev_hid_event )
hid - > hiddev_hid_event ( hid , field , usage , value ) ;
2006-12-08 20:40:44 +03:00
}
/*
* 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 ) .
*/
2008-03-31 03:53:56 +04:00
static void hid_input_field ( struct hid_device * hid , struct hid_field * field ,
__u8 * data , int interrupt )
2006-12-08 20:40:44 +03:00
{
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 ) ;
}
/*
* 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 ;
2007-03-12 15:52:04 +03:00
unsigned bitsused = offset + count * size ;
2006-12-08 20:40:44 +03:00
unsigned n ;
2007-03-12 15:52:04 +03:00
/* 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 ;
2006-12-08 20:40:44 +03:00
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 .
*/
2006-12-08 20:40:53 +03:00
void hid_output_report ( struct hid_report * report , __u8 * data )
2006-12-08 20:40:44 +03:00
{
unsigned n ;
if ( report - > id > 0 )
* data + + = report - > id ;
for ( n = 0 ; n < report - > maxfield ; n + + )
hid_output_field ( report - > field [ n ] , data ) ;
}
2006-12-08 20:40:53 +03:00
EXPORT_SYMBOL_GPL ( hid_output_report ) ;
2006-12-08 20:40:44 +03:00
/*
* 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 ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " offset (%d) exceeds report_count (%d) \n " , offset , field - > report_count ) ;
2006-12-08 20:40:44 +03:00
hid_dump_field ( field , 8 ) ;
return - 1 ;
}
if ( field - > logical_minimum < 0 ) {
if ( value ! = snto32 ( s32ton ( value , size ) , size ) ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " value %d is out of range \n " , value ) ;
2006-12-08 20:40:44 +03:00
return - 1 ;
}
}
field - > value [ offset ] = value ;
return 0 ;
}
2006-12-08 20:40:53 +03:00
EXPORT_SYMBOL_GPL ( hid_set_field ) ;
2006-12-08 20:40:44 +03:00
2008-05-16 13:49:15 +04:00
static struct hid_report * hid_get_report ( struct hid_report_enum * report_enum ,
const u8 * data )
2006-12-08 20:41:17 +03:00
{
struct hid_report * report ;
2008-05-16 13:49:15 +04:00
unsigned int n = 0 ; /* Normally report number is 0 */
2006-12-08 20:41:17 +03:00
2008-05-16 13:49:15 +04:00
/* Device uses numbered reports, data[0] is report number */
if ( report_enum - > numbered )
n = * data ;
2006-12-08 20:41:17 +03:00
2008-05-16 13:49:15 +04:00
report = report_enum - > report_id_hash [ n ] ;
if ( report = = NULL )
dbg_hid ( " undefined report_id %u received \n " , n ) ;
2006-12-08 20:41:17 +03:00
2008-05-16 13:49:15 +04:00
return report ;
}
2006-12-08 20:41:17 +03:00
2008-05-16 13:49:15 +04:00
void hid_report_raw_event ( 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 ;
unsigned int a ;
int rsize , csize = size ;
u8 * cdata = data ;
2006-12-08 20:41:17 +03:00
2008-05-16 13:49:15 +04:00
report = hid_get_report ( report_enum , data ) ;
if ( ! report )
return ;
2006-12-08 20:41:17 +03:00
2008-05-16 13:49:15 +04:00
if ( report_enum - > numbered ) {
cdata + + ;
csize - - ;
2006-12-08 20:41:17 +03:00
}
rsize = ( ( report - > size - 1 ) > > 3 ) + 1 ;
2008-05-16 13:49:15 +04:00
if ( csize < rsize ) {
dbg_hid ( " report %d is too short, (%d < %d) \n " , report - > id ,
csize , rsize ) ;
memset ( cdata + csize , 0 , rsize - csize ) ;
2006-12-08 20:41:17 +03:00
}
if ( ( hid - > claimed & HID_CLAIMED_HIDDEV ) & & hid - > hiddev_report_event )
hid - > hiddev_report_event ( hid , report ) ;
2008-03-28 16:11:22 +03:00
if ( hid - > claimed & HID_CLAIMED_HIDRAW ) {
/* numbered reports need to be passed with the report num */
if ( report_enum - > numbered )
hidraw_report_event ( hid , data - 1 , size + 1 ) ;
else
hidraw_report_event ( hid , data , size ) ;
}
2006-12-08 20:41:17 +03:00
2008-05-16 13:49:15 +04:00
for ( a = 0 ; a < report - > maxfield ; a + + )
hid_input_field ( hid , report - > field [ a ] , cdata , interrupt ) ;
2006-12-08 20:41:17 +03:00
if ( hid - > claimed & HID_CLAIMED_INPUT )
hidinput_report_event ( hid , report ) ;
2008-05-16 13:49:15 +04:00
}
EXPORT_SYMBOL_GPL ( hid_report_raw_event ) ;
/**
* hid_input_report - report data from lower layer ( usb , bt . . . )
*
* @ hid : hid device
* @ type : HID report type ( HID_ * _REPORT )
* @ data : report contents
* @ size : size of data parameter
* @ interrupt : called from atomic ?
*
* This is data entry for lower layers .
*/
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_driver * hdrv = hid - > driver ;
struct hid_report * report ;
unsigned int i ;
int ret ;
if ( ! hid | | ! hid - > driver )
return - ENODEV ;
if ( ! size ) {
dbg_hid ( " empty report \n " ) ;
return - 1 ;
}
dbg_hid ( " report (size %u) (%snumbered) \n " , size , report_enum - > numbered ? " " : " un " ) ;
report = hid_get_report ( report_enum , data ) ;
if ( ! report )
return - 1 ;
/* dump the report */
dbg_hid ( " report %d (size %u) = " , report - > id , size ) ;
for ( i = 0 ; i < size ; i + + )
dbg_hid_line ( " %02x " , data [ i ] ) ;
dbg_hid_line ( " \n " ) ;
if ( hdrv & & hdrv - > raw_event & & hid_match_report ( hid , report ) ) {
ret = hdrv - > raw_event ( hid , report , data , size ) ;
if ( ret ! = 0 )
return ret < 0 ? ret : 0 ;
}
hid_report_raw_event ( hid , type , data , size , interrupt ) ;
2006-12-08 20:41:17 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( hid_input_report ) ;
2008-05-16 13:49:15 +04:00
static bool hid_match_one_id ( struct hid_device * hdev ,
const struct hid_device_id * id )
{
return id - > bus = = hdev - > bus & &
( id - > vendor = = HID_ANY_ID | | id - > vendor = = hdev - > vendor ) & &
( id - > product = = HID_ANY_ID | | id - > product = = hdev - > product ) ;
}
static const struct hid_device_id * hid_match_id ( struct hid_device * hdev ,
const struct hid_device_id * id )
{
for ( ; id - > bus ; id + + )
if ( hid_match_one_id ( hdev , id ) )
return id ;
return NULL ;
}
static const struct hid_device_id hid_blacklist [ ] = {
2008-06-19 01:36:49 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_IRCONTROL4 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_MIGHTYMOUSE ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_FOUNTAIN_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER_JIS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER3_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER3_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER3_JIS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER4_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER4_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER4_JIS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_ALU_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_ALU_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_ALU_JIS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_WELLSPRING_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_WELLSPRING_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_WELLSPRING_JIS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_WELLSPRING2_ISO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_WELLSPRING2_JIS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_APPLE , USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY ) } ,
2008-05-16 13:49:19 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_MX3000_RECEIVER ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_S510_RECEIVER ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_S510_RECEIVER_2 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_LOGITECH_RECEIVER ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_DINOVO_DESKTOP ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_DINOVO_EDGE ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_DINOVO_MINI ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_LOGITECH_KBD ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_LOGITECH_ELITE_KBD ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_LOGITECH_LX3 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_LOGITECH_V150 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_LOGITECH_EXTREME_3D ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LOGITECH , USB_DEVICE_ID_LOGITECH_WHEEL ) } ,
2008-06-20 23:26:11 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_SIDEWINDER_GV ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_NE4K ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_LK6K ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_PRESENTER_8K_USB ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 ) } ,
2008-06-23 23:56:07 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_SUNPLUS , USB_DEVICE_ID_SUNPLUS_WDESKTOP ) } ,
2008-06-19 01:36:49 +04:00
{ HID_BLUETOOTH_DEVICE ( USB_VENDOR_ID_APPLE , 0x030c ) } ,
2008-06-20 23:26:11 +04:00
{ HID_BLUETOOTH_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_PRESENTER_8K_BT ) } ,
2008-05-16 13:49:15 +04:00
{ }
} ;
static int hid_bus_match ( struct device * dev , struct device_driver * drv )
{
struct hid_driver * hdrv = container_of ( drv , struct hid_driver , driver ) ;
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
if ( ! hid_match_id ( hdev , hdrv - > id_table ) )
return 0 ;
/* generic wants all non-blacklisted */
if ( ! strncmp ( hdrv - > name , " generic- " , 8 ) )
return ! hid_match_id ( hdev , hid_blacklist ) ;
return 1 ;
}
static int hid_device_probe ( struct device * dev )
{
struct hid_driver * hdrv = container_of ( dev - > driver ,
struct hid_driver , driver ) ;
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
const struct hid_device_id * id ;
int ret = 0 ;
if ( ! hdev - > driver ) {
2008-05-16 13:49:16 +04:00
id = hid_match_id ( hdev , hdrv - > id_table ) ;
if ( id = = NULL )
return - ENODEV ;
2008-05-16 13:49:15 +04:00
2008-05-16 13:49:16 +04:00
hdev - > driver = hdrv ;
if ( hdrv - > probe ) {
ret = hdrv - > probe ( hdev , id ) ;
} else { /* default probe */
ret = hid_parse ( hdev ) ;
if ( ! ret )
ret = hid_hw_start ( hdev ) ;
2008-05-16 13:49:15 +04:00
}
2008-05-16 13:49:16 +04:00
if ( ret )
hdev - > driver = NULL ;
2008-05-16 13:49:15 +04:00
}
return ret ;
}
static int hid_device_remove ( struct device * dev )
{
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
struct hid_driver * hdrv = hdev - > driver ;
if ( hdrv ) {
if ( hdrv - > remove )
hdrv - > remove ( hdev ) ;
2008-05-16 13:49:16 +04:00
else /* default remove */
hid_hw_stop ( hdev ) ;
2008-05-16 13:49:15 +04:00
hdev - > driver = NULL ;
}
return 0 ;
}
static int hid_uevent ( struct device * dev , struct kobj_uevent_env * env )
{
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
if ( add_uevent_var ( env , " HID_ID=%04X:%08X:%08X " ,
hdev - > bus , hdev - > vendor , hdev - > product ) )
return - ENOMEM ;
if ( add_uevent_var ( env , " HID_NAME=%s " , hdev - > name ) )
return - ENOMEM ;
if ( add_uevent_var ( env , " HID_PHYS=%s " , hdev - > phys ) )
return - ENOMEM ;
if ( add_uevent_var ( env , " HID_UNIQ=%s " , hdev - > uniq ) )
return - ENOMEM ;
if ( add_uevent_var ( env , " MODALIAS=hid:b%04Xv%08Xp%08X " ,
hdev - > bus , hdev - > vendor , hdev - > product ) )
return - ENOMEM ;
return 0 ;
}
static struct bus_type hid_bus_type = {
. name = " hid " ,
. match = hid_bus_match ,
. probe = hid_device_probe ,
. remove = hid_device_remove ,
. uevent = hid_uevent ,
} ;
2008-05-16 13:49:20 +04:00
static const struct hid_device_id hid_ignore_list [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_ACECAD , USB_DEVICE_ID_ACECAD_FLAIR ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ACECAD , USB_DEVICE_ID_ACECAD_302 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ADS_TECH , USB_DEVICE_ID_ADS_TECH_RADIO_SI470X ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_AIPTEK , USB_DEVICE_ID_AIPTEK_01 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_AIPTEK , USB_DEVICE_ID_AIPTEK_10 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_AIPTEK , USB_DEVICE_ID_AIPTEK_20 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_AIPTEK , USB_DEVICE_ID_AIPTEK_21 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_AIPTEK , USB_DEVICE_ID_AIPTEK_22 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_AIPTEK , USB_DEVICE_ID_AIPTEK_23 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_AIPTEK , USB_DEVICE_ID_AIPTEK_24 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_AIRCABLE , USB_DEVICE_ID_AIRCABLE1 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ALCOR , USB_DEVICE_ID_ALCOR_USBRS232 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ASUS , USB_DEVICE_ID_ASUS_LCM ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_BERKSHIRE , USB_DEVICE_ID_BERKSHIRE_PCWD ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_CIDC , 0x0103 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_CYGNAL , USB_DEVICE_ID_CYGNAL_RADIO_SI470X ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_CMEDIA , USB_DEVICE_ID_CM109 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_CYPRESS , USB_DEVICE_ID_CYPRESS_HIDCOM ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_CYPRESS , USB_DEVICE_ID_CYPRESS_ULTRAMOUSE ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_DELORME , USB_DEVICE_ID_DELORME_EARTHMATE ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_DELORME , USB_DEVICE_ID_DELORME_EM_LT20 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ESSENTIAL_REALITY , USB_DEVICE_ID_ESSENTIAL_REALITY_P5 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GENERAL_TOUCH , 0x0001 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GENERAL_TOUCH , 0x0002 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GENERAL_TOUCH , 0x0003 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GENERAL_TOUCH , 0x0004 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GLAB , USB_DEVICE_ID_4_PHIDGETSERVO_30 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GLAB , USB_DEVICE_ID_1_PHIDGETSERVO_30 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GLAB , USB_DEVICE_ID_0_0_4_IF_KIT ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GLAB , USB_DEVICE_ID_0_16_16_IF_KIT ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GLAB , USB_DEVICE_ID_8_8_8_IF_KIT ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GLAB , USB_DEVICE_ID_0_8_7_IF_KIT ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GLAB , USB_DEVICE_ID_0_8_8_IF_KIT ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GLAB , USB_DEVICE_ID_PHIDGET_MOTORCONTROL ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GOTOP , USB_DEVICE_ID_SUPER_Q2 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GOTOP , USB_DEVICE_ID_GOGOPEN ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GOTOP , USB_DEVICE_ID_PENPOWER ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GRETAGMACBETH , USB_DEVICE_ID_GRETAGMACBETH_HUEY ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GRIFFIN , USB_DEVICE_ID_POWERMATE ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GRIFFIN , USB_DEVICE_ID_SOUNDKNOB ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_90 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_100 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_101 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_103 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_104 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_105 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_106 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_107 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_108 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_200 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_201 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_202 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_203 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_204 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_205 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_206 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_207 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_300 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_301 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_302 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_303 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_304 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_305 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_306 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_307 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_308 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_309 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_400 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_401 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_402 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_403 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_404 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_405 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_500 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_501 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_502 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_503 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_504 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_1000 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_1001 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_1002 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_1003 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_1004 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_1005 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_1006 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_GTCO , USB_DEVICE_ID_GTCO_1007 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_IMATION , USB_DEVICE_ID_DISC_STAKKA ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_KBGEAR , USB_DEVICE_ID_KBGEAR_JAMSTUDIO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_KYE , USB_DEVICE_ID_KYE_GPEN_560 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_CASSY ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_POCKETCASSY ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_MOBILECASSY ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_JWM ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_DMMP ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_UMIP ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_XRAY1 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_XRAY2 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_VIDEOCOM ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_COM3LAB ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_TELEPORT ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_NETWORKANALYSER ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_POWERCONTROL ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LD , USB_DEVICE_ID_LD_MACHINETEST ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MCC , USB_DEVICE_ID_MCC_PMD1024LS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MCC , USB_DEVICE_ID_MCC_PMD1208LS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MGE , USB_DEVICE_ID_MGE_UPS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MGE , USB_DEVICE_ID_MGE_UPS1 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROCHIP , USB_DEVICE_ID_PICKIT1 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROCHIP , USB_DEVICE_ID_PICKIT2 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR , USB_DEVICE_ID_N_S_HARMONY ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 20 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 30 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 100 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 108 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 118 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 200 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 300 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 400 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ONTRAK , USB_DEVICE_ID_ONTRAK_ADU100 + 500 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_PANJIT , 0x0001 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_PANJIT , 0x0002 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_PANJIT , 0x0003 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_PANJIT , 0x0004 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_SOUNDGRAPH , USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_VERNIER , USB_DEVICE_ID_VERNIER_LABPRO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_VERNIER , USB_DEVICE_ID_VERNIER_GOTEMP ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_VERNIER , USB_DEVICE_ID_VERNIER_SKIP ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_VERNIER , USB_DEVICE_ID_VERNIER_CYCLOPS ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_VERNIER , USB_DEVICE_ID_VERNIER_LCSPEC ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_WACOM , HID_ANY_ID ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_WISEGROUP , USB_DEVICE_ID_4_PHIDGETSERVO_20 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_WISEGROUP , USB_DEVICE_ID_1_PHIDGETSERVO_20 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_WISEGROUP , USB_DEVICE_ID_8_8_4_IF_KIT ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_YEALINK , USB_DEVICE_ID_YEALINK_P1K_P4K_B2K ) } ,
{ }
} ;
static bool hid_ignore ( struct hid_device * hdev )
{
switch ( hdev - > vendor ) {
case USB_VENDOR_ID_CODEMERCS :
/* ignore all Code Mercenaries IOWarrior devices */
if ( hdev - > product > = USB_DEVICE_ID_CODEMERCS_IOW_FIRST & &
hdev - > product < = USB_DEVICE_ID_CODEMERCS_IOW_LAST )
return true ;
break ;
case USB_VENDOR_ID_LOGITECH :
if ( hdev - > product > = USB_DEVICE_ID_LOGITECH_HARMONY_FIRST & &
hdev - > product < = USB_DEVICE_ID_LOGITECH_HARMONY_LAST )
return true ;
break ;
}
return ! ! hid_match_id ( hdev , hid_ignore_list ) ;
}
2008-05-16 13:49:15 +04:00
int hid_add_device ( struct hid_device * hdev )
{
static atomic_t id = ATOMIC_INIT ( 0 ) ;
int ret ;
if ( WARN_ON ( hdev - > status & HID_STAT_ADDED ) )
return - EBUSY ;
2008-05-16 13:49:20 +04:00
/* we need to kill them here, otherwise they will stay allocated to
* wait for coming driver */
if ( hid_ignore ( hdev ) )
return - ENODEV ;
2008-05-16 13:49:15 +04:00
/* XXX hack, any other cleaner solution < 20 bus_id bytes? */
sprintf ( hdev - > dev . bus_id , " %04X:%04X:%04X.%04X " , hdev - > bus ,
hdev - > vendor , hdev - > product , atomic_inc_return ( & id ) ) ;
ret = device_add ( & hdev - > dev ) ;
if ( ! ret )
hdev - > status | = HID_STAT_ADDED ;
return ret ;
}
EXPORT_SYMBOL_GPL ( hid_add_device ) ;
/**
* hid_allocate_device - allocate new hid device descriptor
*
* Allocate and initialize hid device , so that hid_destroy_device might be
* used to free it .
*
* New hid_device pointer is returned on success , otherwise ERR_PTR encoded
* error value .
*/
struct hid_device * hid_allocate_device ( void )
{
struct hid_device * hdev ;
unsigned int i ;
int ret = - ENOMEM ;
hdev = kzalloc ( sizeof ( * hdev ) , GFP_KERNEL ) ;
if ( hdev = = NULL )
return ERR_PTR ( ret ) ;
device_initialize ( & hdev - > dev ) ;
hdev - > dev . release = hid_device_release ;
hdev - > dev . bus = & hid_bus_type ;
hdev - > collection = kcalloc ( HID_DEFAULT_NUM_COLLECTIONS ,
sizeof ( struct hid_collection ) , GFP_KERNEL ) ;
if ( hdev - > collection = = NULL )
goto err ;
hdev - > collection_size = HID_DEFAULT_NUM_COLLECTIONS ;
for ( i = 0 ; i < HID_REPORT_TYPES ; i + + )
INIT_LIST_HEAD ( & hdev - > report_enum [ i ] . report_list ) ;
return hdev ;
err :
put_device ( & hdev - > dev ) ;
return ERR_PTR ( ret ) ;
}
EXPORT_SYMBOL_GPL ( hid_allocate_device ) ;
static void hid_remove_device ( struct hid_device * hdev )
{
if ( hdev - > status & HID_STAT_ADDED ) {
device_del ( & hdev - > dev ) ;
hdev - > status & = ~ HID_STAT_ADDED ;
}
}
/**
* hid_destroy_device - free previously allocated device
*
* @ hdev : hid device
*
* If you allocate hid_device through hid_allocate_device , you should ever
* free by this function .
*/
void hid_destroy_device ( struct hid_device * hdev )
{
hid_remove_device ( hdev ) ;
put_device ( & hdev - > dev ) ;
}
EXPORT_SYMBOL_GPL ( hid_destroy_device ) ;
int __hid_register_driver ( struct hid_driver * hdrv , struct module * owner ,
const char * mod_name )
{
hdrv - > driver . name = hdrv - > name ;
hdrv - > driver . bus = & hid_bus_type ;
hdrv - > driver . owner = owner ;
hdrv - > driver . mod_name = mod_name ;
return driver_register ( & hdrv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( __hid_register_driver ) ;
void hid_unregister_driver ( struct hid_driver * hdrv )
{
driver_unregister ( & hdrv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( hid_unregister_driver ) ;
2008-05-16 13:49:22 +04:00
# ifdef CONFIG_HID_COMPAT
static void hid_compat_load ( struct work_struct * ws )
{
request_module ( " hid-dummy " ) ;
}
static DECLARE_WORK ( hid_compat_work , hid_compat_load ) ;
# endif
2007-05-14 11:57:40 +04:00
static int __init hid_init ( void )
{
2008-05-16 13:49:15 +04:00
int ret ;
ret = bus_register ( & hid_bus_type ) ;
if ( ret ) {
printk ( KERN_ERR " HID: can't register hid bus \n " ) ;
goto err ;
}
ret = hidraw_init ( ) ;
if ( ret )
goto err_bus ;
2008-05-16 13:49:22 +04:00
# ifdef CONFIG_HID_COMPAT
schedule_work ( & hid_compat_work ) ;
# endif
2008-05-16 13:49:15 +04:00
return 0 ;
err_bus :
bus_unregister ( & hid_bus_type ) ;
err :
return ret ;
2007-05-14 11:57:40 +04:00
}
static void __exit hid_exit ( void )
{
hidraw_exit ( ) ;
2008-05-16 13:49:15 +04:00
bus_unregister ( & hid_bus_type ) ;
2007-05-14 11:57:40 +04:00
}
module_init ( hid_init ) ;
module_exit ( hid_exit ) ;
2006-12-08 20:41:10 +03:00
MODULE_LICENSE ( DRIVER_LICENSE ) ;