mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
84
drivers/hid/usbhid/Kconfig
Normal file
84
drivers/hid/usbhid/Kconfig
Normal file
|
@ -0,0 +1,84 @@
|
|||
menu "USB HID support"
|
||||
depends on USB
|
||||
|
||||
config USB_HID
|
||||
tristate "USB HID transport layer"
|
||||
default y
|
||||
depends on USB && INPUT
|
||||
select HID
|
||||
---help---
|
||||
Say Y here if you want to connect USB keyboards,
|
||||
mice, joysticks, graphic tablets, or any other HID based devices
|
||||
to your computer via USB, as well as Uninterruptible Power Supply
|
||||
(UPS) and monitor control devices.
|
||||
|
||||
You can't use this driver and the HIDBP (Boot Protocol) keyboard
|
||||
and mouse drivers at the same time. More information is available:
|
||||
<file:Documentation/input/input.txt>.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbhid.
|
||||
|
||||
comment "Input core support is needed for USB HID input layer or HIDBP support"
|
||||
depends on USB_HID && INPUT=n
|
||||
|
||||
config HID_PID
|
||||
bool "PID device support"
|
||||
help
|
||||
Say Y here if you have a PID-compliant device and wish to enable force
|
||||
feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
|
||||
devices.
|
||||
|
||||
config USB_HIDDEV
|
||||
bool "/dev/hiddev raw HID device support"
|
||||
depends on USB_HID
|
||||
help
|
||||
Say Y here if you want to support HID devices (from the USB
|
||||
specification standpoint) that aren't strictly user interface
|
||||
devices, like monitor controls and Uninterruptable Power Supplies.
|
||||
|
||||
This module supports these devices separately using a separate
|
||||
event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
menu "USB HID Boot Protocol drivers"
|
||||
depends on USB!=n && USB_HID!=y && EXPERT
|
||||
|
||||
config USB_KBD
|
||||
tristate "USB HIDBP Keyboard (simple Boot) support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here only if you are absolutely sure that you don't want
|
||||
to use the generic HID driver for your USB keyboard and prefer
|
||||
to use the keyboard in its limited Boot Protocol mode instead.
|
||||
|
||||
This is almost certainly not what you want. This is mostly
|
||||
useful for embedded applications or simple keyboards.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbkbd.
|
||||
|
||||
If even remotely unsure, say N.
|
||||
|
||||
config USB_MOUSE
|
||||
tristate "USB HIDBP Mouse (simple Boot) support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here only if you are absolutely sure that you don't want
|
||||
to use the generic HID driver for your USB mouse and prefer
|
||||
to use the mouse in its limited Boot Protocol mode instead.
|
||||
|
||||
This is almost certainly not what you want. This is mostly
|
||||
useful for embedded applications or simple mice.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbmouse.
|
||||
|
||||
If even remotely unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
20
drivers/hid/usbhid/Makefile
Normal file
20
drivers/hid/usbhid/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
#
|
||||
# Makefile for the USB input drivers
|
||||
#
|
||||
|
||||
# Multipart objects.
|
||||
usbhid-y := hid-core.o hid-quirks.o
|
||||
|
||||
# Optional parts of multipart objects.
|
||||
|
||||
ifeq ($(CONFIG_USB_HIDDEV),y)
|
||||
usbhid-y += hiddev.o
|
||||
endif
|
||||
ifeq ($(CONFIG_HID_PID),y)
|
||||
usbhid-y += hid-pidff.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_HID) += usbhid.o
|
||||
obj-$(CONFIG_USB_KBD) += usbkbd.o
|
||||
obj-$(CONFIG_USB_MOUSE) += usbmouse.o
|
||||
|
1688
drivers/hid/usbhid/hid-core.c
Normal file
1688
drivers/hid/usbhid/hid-core.c
Normal file
File diff suppressed because it is too large
Load diff
1323
drivers/hid/usbhid/hid-pidff.c
Normal file
1323
drivers/hid/usbhid/hid-pidff.c
Normal file
File diff suppressed because it is too large
Load diff
366
drivers/hid/usbhid/hid-quirks.c
Normal file
366
drivers/hid/usbhid/hid-quirks.c
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* USB HID quirks 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
|
||||
* Copyright (c) 2007 Paul Walmsley
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/hid.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "../hid-ids.h"
|
||||
|
||||
/*
|
||||
* Alphabetically sorted blacklist by quirk type.
|
||||
*/
|
||||
|
||||
static const struct hid_blacklist {
|
||||
__u16 idVendor;
|
||||
__u16 idProduct;
|
||||
__u32 quirks;
|
||||
} hid_blacklist[] = {
|
||||
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_NATSU, USB_DEVICE_ID_NATSU_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_NEXTWINDOW, USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN, HID_QUIRK_MULTI_INPUT},
|
||||
{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
|
||||
|
||||
{ USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
|
||||
|
||||
{ USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
|
||||
{ USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
{ USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_THROTTLE, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_010c, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1610, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1640, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS9200_TOUCH, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS817_TOUCH, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS_TS, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS1030_TOUCH, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWA60, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
{ USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
{ USB_VENDOR_ID_PI_ENGINEERING, USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL, HID_QUIRK_HIDINPUT_FORCE },
|
||||
|
||||
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
/* Dynamic HID quirks list - specified at runtime */
|
||||
struct quirks_list_struct {
|
||||
struct hid_blacklist hid_bl_item;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static LIST_HEAD(dquirks_list);
|
||||
static DECLARE_RWSEM(dquirks_rwsem);
|
||||
|
||||
/* Runtime ("dynamic") quirks manipulation functions */
|
||||
|
||||
/**
|
||||
* usbhid_exists_dquirk: find any dynamic quirks for a USB HID device
|
||||
* @idVendor: the 16-bit USB vendor ID, in native byteorder
|
||||
* @idProduct: the 16-bit USB product ID, in native byteorder
|
||||
*
|
||||
* Description:
|
||||
* Scans dquirks_list for a matching dynamic quirk and returns
|
||||
* the pointer to the relevant struct hid_blacklist if found.
|
||||
* Must be called with a read lock held on dquirks_rwsem.
|
||||
*
|
||||
* Returns: NULL if no quirk found, struct hid_blacklist * if found.
|
||||
*/
|
||||
static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor,
|
||||
const u16 idProduct)
|
||||
{
|
||||
struct quirks_list_struct *q;
|
||||
struct hid_blacklist *bl_entry = NULL;
|
||||
|
||||
list_for_each_entry(q, &dquirks_list, node) {
|
||||
if (q->hid_bl_item.idVendor == idVendor &&
|
||||
q->hid_bl_item.idProduct == idProduct) {
|
||||
bl_entry = &q->hid_bl_item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bl_entry != NULL)
|
||||
dbg_hid("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
|
||||
bl_entry->quirks, bl_entry->idVendor,
|
||||
bl_entry->idProduct);
|
||||
|
||||
return bl_entry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usbhid_modify_dquirk: add/replace a HID quirk
|
||||
* @idVendor: the 16-bit USB vendor ID, in native byteorder
|
||||
* @idProduct: the 16-bit USB product ID, in native byteorder
|
||||
* @quirks: the u32 quirks value to add/replace
|
||||
*
|
||||
* Description:
|
||||
* If an dynamic quirk exists in memory for this (idVendor,
|
||||
* idProduct) pair, replace its quirks value with what was
|
||||
* provided. Otherwise, add the quirk to the dynamic quirks list.
|
||||
*
|
||||
* Returns: 0 OK, -error on failure.
|
||||
*/
|
||||
static int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
|
||||
const u32 quirks)
|
||||
{
|
||||
struct quirks_list_struct *q_new, *q;
|
||||
int list_edited = 0;
|
||||
|
||||
if (!idVendor) {
|
||||
dbg_hid("Cannot add a quirk with idVendor = 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
|
||||
if (!q_new) {
|
||||
dbg_hid("Could not allocate quirks_list_struct\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
q_new->hid_bl_item.idVendor = idVendor;
|
||||
q_new->hid_bl_item.idProduct = idProduct;
|
||||
q_new->hid_bl_item.quirks = quirks;
|
||||
|
||||
down_write(&dquirks_rwsem);
|
||||
|
||||
list_for_each_entry(q, &dquirks_list, node) {
|
||||
|
||||
if (q->hid_bl_item.idVendor == idVendor &&
|
||||
q->hid_bl_item.idProduct == idProduct) {
|
||||
|
||||
list_replace(&q->node, &q_new->node);
|
||||
kfree(q);
|
||||
list_edited = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!list_edited)
|
||||
list_add_tail(&q_new->node, &dquirks_list);
|
||||
|
||||
up_write(&dquirks_rwsem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_remove_all_dquirks: remove all runtime HID quirks from memory
|
||||
*
|
||||
* Description:
|
||||
* Free all memory associated with dynamic quirks - called before
|
||||
* module unload.
|
||||
*
|
||||
*/
|
||||
static void usbhid_remove_all_dquirks(void)
|
||||
{
|
||||
struct quirks_list_struct *q, *temp;
|
||||
|
||||
down_write(&dquirks_rwsem);
|
||||
list_for_each_entry_safe(q, temp, &dquirks_list, node) {
|
||||
list_del(&q->node);
|
||||
kfree(q);
|
||||
}
|
||||
up_write(&dquirks_rwsem);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_quirks_init: apply USB HID quirks specified at module load time
|
||||
*/
|
||||
int usbhid_quirks_init(char **quirks_param)
|
||||
{
|
||||
u16 idVendor, idProduct;
|
||||
u32 quirks;
|
||||
int n = 0, m;
|
||||
|
||||
for (; n < MAX_USBHID_BOOT_QUIRKS && quirks_param[n]; n++) {
|
||||
|
||||
m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
|
||||
&idVendor, &idProduct, &quirks);
|
||||
|
||||
if (m != 3 ||
|
||||
usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
|
||||
printk(KERN_WARNING
|
||||
"Could not parse HID quirk module param %s\n",
|
||||
quirks_param[n]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_quirks_exit: release memory associated with dynamic_quirks
|
||||
*
|
||||
* Description:
|
||||
* Release all memory associated with dynamic quirks. Called upon
|
||||
* module unload.
|
||||
*
|
||||
* Returns: nothing
|
||||
*/
|
||||
void usbhid_quirks_exit(void)
|
||||
{
|
||||
usbhid_remove_all_dquirks();
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_exists_squirk: return any static quirks for a USB HID device
|
||||
* @idVendor: the 16-bit USB vendor ID, in native byteorder
|
||||
* @idProduct: the 16-bit USB product ID, in native byteorder
|
||||
*
|
||||
* Description:
|
||||
* Given a USB vendor ID and product ID, return a pointer to
|
||||
* the hid_blacklist entry associated with that device.
|
||||
*
|
||||
* Returns: pointer if quirk found, or NULL if no quirks found.
|
||||
*/
|
||||
static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor,
|
||||
const u16 idProduct)
|
||||
{
|
||||
const struct hid_blacklist *bl_entry = NULL;
|
||||
int n = 0;
|
||||
|
||||
for (; hid_blacklist[n].idVendor; n++)
|
||||
if (hid_blacklist[n].idVendor == idVendor &&
|
||||
hid_blacklist[n].idProduct == idProduct)
|
||||
bl_entry = &hid_blacklist[n];
|
||||
|
||||
if (bl_entry != NULL)
|
||||
dbg_hid("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
|
||||
bl_entry->quirks, bl_entry->idVendor,
|
||||
bl_entry->idProduct);
|
||||
return bl_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_lookup_quirk: return any quirks associated with a USB HID device
|
||||
* @idVendor: the 16-bit USB vendor ID, in native byteorder
|
||||
* @idProduct: the 16-bit USB product ID, in native byteorder
|
||||
*
|
||||
* Description:
|
||||
* Given a USB vendor ID and product ID, return any quirks associated
|
||||
* with that device.
|
||||
*
|
||||
* Returns: a u32 quirks value.
|
||||
*/
|
||||
u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
|
||||
{
|
||||
u32 quirks = 0;
|
||||
const struct hid_blacklist *bl_entry = NULL;
|
||||
|
||||
/* NCR devices must not be queried for reports */
|
||||
if (idVendor == USB_VENDOR_ID_NCR &&
|
||||
idProduct >= USB_DEVICE_ID_NCR_FIRST &&
|
||||
idProduct <= USB_DEVICE_ID_NCR_LAST)
|
||||
return HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
down_read(&dquirks_rwsem);
|
||||
bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
|
||||
if (!bl_entry)
|
||||
bl_entry = usbhid_exists_squirk(idVendor, idProduct);
|
||||
if (bl_entry)
|
||||
quirks = bl_entry->quirks;
|
||||
up_read(&dquirks_rwsem);
|
||||
|
||||
return quirks;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(usbhid_lookup_quirk);
|
939
drivers/hid/usbhid/hiddev.c
Normal file
939
drivers/hid/usbhid/hiddev.c
Normal file
|
@ -0,0 +1,939 @@
|
|||
/*
|
||||
* Copyright (c) 2001 Paul Stewart
|
||||
* Copyright (c) 2001 Vojtech Pavlik
|
||||
*
|
||||
* HID char devices, giving access to raw HID device events.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 Paul Stewart <stewart@wetlogic.net>
|
||||
*/
|
||||
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hiddev.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
||||
#define HIDDEV_MINOR_BASE 0
|
||||
#define HIDDEV_MINORS 256
|
||||
#else
|
||||
#define HIDDEV_MINOR_BASE 96
|
||||
#define HIDDEV_MINORS 16
|
||||
#endif
|
||||
#define HIDDEV_BUFFER_SIZE 2048
|
||||
|
||||
struct hiddev {
|
||||
int exist;
|
||||
int open;
|
||||
struct mutex existancelock;
|
||||
wait_queue_head_t wait;
|
||||
struct hid_device *hid;
|
||||
struct list_head list;
|
||||
spinlock_t list_lock;
|
||||
};
|
||||
|
||||
struct hiddev_list {
|
||||
struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
|
||||
int head;
|
||||
int tail;
|
||||
unsigned flags;
|
||||
struct fasync_struct *fasync;
|
||||
struct hiddev *hiddev;
|
||||
struct list_head node;
|
||||
struct mutex thread_lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* Find a report, given the report's type and ID. The ID can be specified
|
||||
* indirectly by REPORT_ID_FIRST (which returns the first report of the given
|
||||
* type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
|
||||
* given type which follows old_id.
|
||||
*/
|
||||
static struct hid_report *
|
||||
hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
|
||||
{
|
||||
unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
|
||||
unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_report *report;
|
||||
struct list_head *list;
|
||||
|
||||
if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
|
||||
rinfo->report_type > HID_REPORT_TYPE_MAX)
|
||||
return NULL;
|
||||
|
||||
report_enum = hid->report_enum +
|
||||
(rinfo->report_type - HID_REPORT_TYPE_MIN);
|
||||
|
||||
switch (flags) {
|
||||
case 0: /* Nothing to do -- report_id is already set correctly */
|
||||
break;
|
||||
|
||||
case HID_REPORT_ID_FIRST:
|
||||
if (list_empty(&report_enum->report_list))
|
||||
return NULL;
|
||||
|
||||
list = report_enum->report_list.next;
|
||||
report = list_entry(list, struct hid_report, list);
|
||||
rinfo->report_id = report->id;
|
||||
break;
|
||||
|
||||
case HID_REPORT_ID_NEXT:
|
||||
report = report_enum->report_id_hash[rid];
|
||||
if (!report)
|
||||
return NULL;
|
||||
|
||||
list = report->list.next;
|
||||
if (list == &report_enum->report_list)
|
||||
return NULL;
|
||||
|
||||
report = list_entry(list, struct hid_report, list);
|
||||
rinfo->report_id = report->id;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return report_enum->report_id_hash[rinfo->report_id];
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform an exhaustive search of the report table for a usage, given its
|
||||
* type and usage id.
|
||||
*/
|
||||
static struct hid_field *
|
||||
hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
|
||||
{
|
||||
int i, j;
|
||||
struct hid_report *report;
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_field *field;
|
||||
|
||||
if (uref->report_type < HID_REPORT_TYPE_MIN ||
|
||||
uref->report_type > HID_REPORT_TYPE_MAX)
|
||||
return NULL;
|
||||
|
||||
report_enum = hid->report_enum +
|
||||
(uref->report_type - HID_REPORT_TYPE_MIN);
|
||||
|
||||
list_for_each_entry(report, &report_enum->report_list, list) {
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
field = report->field[i];
|
||||
for (j = 0; j < field->maxusage; j++) {
|
||||
if (field->usage[j].hid == uref->usage_code) {
|
||||
uref->report_id = report->id;
|
||||
uref->field_index = i;
|
||||
uref->usage_index = j;
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hiddev_send_event(struct hid_device *hid,
|
||||
struct hiddev_usage_ref *uref)
|
||||
{
|
||||
struct hiddev *hiddev = hid->hiddev;
|
||||
struct hiddev_list *list;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hiddev->list_lock, flags);
|
||||
list_for_each_entry(list, &hiddev->list, node) {
|
||||
if (uref->field_index != HID_FIELD_INDEX_NONE ||
|
||||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
|
||||
list->buffer[list->head] = *uref;
|
||||
list->head = (list->head + 1) &
|
||||
(HIDDEV_BUFFER_SIZE - 1);
|
||||
kill_fasync(&list->fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&hiddev->list_lock, flags);
|
||||
|
||||
wake_up_interruptible(&hiddev->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where hid.c calls into hiddev to pass an event that occurred over
|
||||
* the interrupt pipe
|
||||
*/
|
||||
void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
unsigned type = field->report_type;
|
||||
struct hiddev_usage_ref 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 = field->report->id;
|
||||
uref.field_index = field->index;
|
||||
uref.usage_index = (usage - field->usage);
|
||||
uref.usage_code = usage->hid;
|
||||
uref.value = value;
|
||||
|
||||
hiddev_send_event(hid, &uref);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hiddev_hid_event);
|
||||
|
||||
void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
|
||||
{
|
||||
unsigned type = report->type;
|
||||
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_send_event(hid, &uref);
|
||||
}
|
||||
|
||||
/*
|
||||
* fasync file op
|
||||
*/
|
||||
static int hiddev_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
|
||||
return fasync_helper(fd, file, on, &list->fasync);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release file op
|
||||
*/
|
||||
static int hiddev_release(struct inode * inode, struct file * file)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list->hiddev->list_lock, flags);
|
||||
list_del(&list->node);
|
||||
spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
|
||||
|
||||
mutex_lock(&list->hiddev->existancelock);
|
||||
if (!--list->hiddev->open) {
|
||||
if (list->hiddev->exist) {
|
||||
usbhid_close(list->hiddev->hid);
|
||||
usbhid_put_power(list->hiddev->hid);
|
||||
} else {
|
||||
mutex_unlock(&list->hiddev->existancelock);
|
||||
kfree(list->hiddev);
|
||||
vfree(list);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list->hiddev->existancelock);
|
||||
vfree(list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* open file op
|
||||
*/
|
||||
static int hiddev_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hiddev_list *list;
|
||||
struct usb_interface *intf;
|
||||
struct hid_device *hid;
|
||||
struct hiddev *hiddev;
|
||||
int res;
|
||||
|
||||
intf = usbhid_find_interface(iminor(inode));
|
||||
if (!intf)
|
||||
return -ENODEV;
|
||||
hid = usb_get_intfdata(intf);
|
||||
hiddev = hid->hiddev;
|
||||
|
||||
if (!(list = vzalloc(sizeof(struct hiddev_list))))
|
||||
return -ENOMEM;
|
||||
mutex_init(&list->thread_lock);
|
||||
list->hiddev = hiddev;
|
||||
file->private_data = list;
|
||||
|
||||
/*
|
||||
* no need for locking because the USB major number
|
||||
* is shared which usbcore guards against disconnect
|
||||
*/
|
||||
if (list->hiddev->exist) {
|
||||
if (!list->hiddev->open++) {
|
||||
res = usbhid_open(hiddev->hid);
|
||||
if (res < 0) {
|
||||
res = -EIO;
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
spin_lock_irq(&list->hiddev->list_lock);
|
||||
list_add_tail(&list->node, &hiddev->list);
|
||||
spin_unlock_irq(&list->hiddev->list_lock);
|
||||
|
||||
mutex_lock(&hiddev->existancelock);
|
||||
if (!list->hiddev->open++)
|
||||
if (list->hiddev->exist) {
|
||||
struct hid_device *hid = hiddev->hid;
|
||||
res = usbhid_get_power(hid);
|
||||
if (res < 0) {
|
||||
res = -EIO;
|
||||
goto bail_unlock;
|
||||
}
|
||||
usbhid_open(hid);
|
||||
}
|
||||
mutex_unlock(&hiddev->existancelock);
|
||||
return 0;
|
||||
bail_unlock:
|
||||
mutex_unlock(&hiddev->existancelock);
|
||||
bail:
|
||||
file->private_data = NULL;
|
||||
vfree(list);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* "write" file op
|
||||
*/
|
||||
static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "read" file op
|
||||
*/
|
||||
static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
struct hiddev_list *list = file->private_data;
|
||||
int event_size;
|
||||
int retval;
|
||||
|
||||
event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
|
||||
sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
|
||||
|
||||
if (count < event_size)
|
||||
return 0;
|
||||
|
||||
/* lock against other threads */
|
||||
retval = mutex_lock_interruptible(&list->thread_lock);
|
||||
if (retval)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
while (retval == 0) {
|
||||
if (list->head == list->tail) {
|
||||
prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE);
|
||||
|
||||
while (list->head == list->tail) {
|
||||
if (signal_pending(current)) {
|
||||
retval = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
if (!list->hiddev->exist) {
|
||||
retval = -EIO;
|
||||
break;
|
||||
}
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
retval = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* let O_NONBLOCK tasks run */
|
||||
mutex_unlock(&list->thread_lock);
|
||||
schedule();
|
||||
if (mutex_lock_interruptible(&list->thread_lock)) {
|
||||
finish_wait(&list->hiddev->wait, &wait);
|
||||
return -EINTR;
|
||||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
finish_wait(&list->hiddev->wait, &wait);
|
||||
|
||||
}
|
||||
|
||||
if (retval) {
|
||||
mutex_unlock(&list->thread_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
while (list->head != list->tail &&
|
||||
retval + event_size <= count) {
|
||||
if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
|
||||
if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) {
|
||||
struct hiddev_event event;
|
||||
|
||||
event.hid = list->buffer[list->tail].usage_code;
|
||||
event.value = list->buffer[list->tail].value;
|
||||
if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) {
|
||||
mutex_unlock(&list->thread_lock);
|
||||
return -EFAULT;
|
||||
}
|
||||
retval += sizeof(struct hiddev_event);
|
||||
}
|
||||
} else {
|
||||
if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
|
||||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
|
||||
|
||||
if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) {
|
||||
mutex_unlock(&list->thread_lock);
|
||||
return -EFAULT;
|
||||
}
|
||||
retval += sizeof(struct hiddev_usage_ref);
|
||||
}
|
||||
}
|
||||
list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
|
||||
}
|
||||
|
||||
}
|
||||
mutex_unlock(&list->thread_lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* "poll" file op
|
||||
* No kernel lock - fine
|
||||
*/
|
||||
static unsigned int hiddev_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
|
||||
poll_wait(file, &list->hiddev->wait, wait);
|
||||
if (list->head != list->tail)
|
||||
return POLLIN | POLLRDNORM;
|
||||
if (!list->hiddev->exist)
|
||||
return POLLERR | POLLHUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ioctl" file op
|
||||
*/
|
||||
static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
|
||||
{
|
||||
struct hid_device *hid = hiddev->hid;
|
||||
struct hiddev_report_info rinfo;
|
||||
struct hiddev_usage_ref_multi *uref_multi = NULL;
|
||||
struct hiddev_usage_ref *uref;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
int i;
|
||||
|
||||
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
|
||||
if (!uref_multi)
|
||||
return -ENOMEM;
|
||||
uref = &uref_multi->uref;
|
||||
if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
|
||||
if (copy_from_user(uref_multi, user_arg,
|
||||
sizeof(*uref_multi)))
|
||||
goto fault;
|
||||
} else {
|
||||
if (copy_from_user(uref, user_arg, sizeof(*uref)))
|
||||
goto fault;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case HIDIOCGUCODE:
|
||||
rinfo.report_type = uref->report_type;
|
||||
rinfo.report_id = uref->report_id;
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
goto inval;
|
||||
|
||||
if (uref->field_index >= report->maxfield)
|
||||
goto inval;
|
||||
|
||||
field = report->field[uref->field_index];
|
||||
if (uref->usage_index >= field->maxusage)
|
||||
goto inval;
|
||||
|
||||
uref->usage_code = field->usage[uref->usage_index].hid;
|
||||
|
||||
if (copy_to_user(user_arg, uref, sizeof(*uref)))
|
||||
goto fault;
|
||||
|
||||
goto goodreturn;
|
||||
|
||||
default:
|
||||
if (cmd != HIDIOCGUSAGE &&
|
||||
cmd != HIDIOCGUSAGES &&
|
||||
uref->report_type == HID_REPORT_TYPE_INPUT)
|
||||
goto inval;
|
||||
|
||||
if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
|
||||
field = hiddev_lookup_usage(hid, uref);
|
||||
if (field == NULL)
|
||||
goto inval;
|
||||
} else {
|
||||
rinfo.report_type = uref->report_type;
|
||||
rinfo.report_id = uref->report_id;
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
goto inval;
|
||||
|
||||
if (uref->field_index >= report->maxfield)
|
||||
goto inval;
|
||||
|
||||
field = report->field[uref->field_index];
|
||||
|
||||
if (cmd == HIDIOCGCOLLECTIONINDEX) {
|
||||
if (uref->usage_index >= field->maxusage)
|
||||
goto inval;
|
||||
} else if (uref->usage_index >= field->report_count)
|
||||
goto inval;
|
||||
}
|
||||
|
||||
if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
|
||||
(uref_multi->num_values > HID_MAX_MULTI_USAGES ||
|
||||
uref->usage_index + uref_multi->num_values > field->report_count))
|
||||
goto inval;
|
||||
|
||||
switch (cmd) {
|
||||
case HIDIOCGUSAGE:
|
||||
uref->value = field->value[uref->usage_index];
|
||||
if (copy_to_user(user_arg, uref, sizeof(*uref)))
|
||||
goto fault;
|
||||
goto goodreturn;
|
||||
|
||||
case HIDIOCSUSAGE:
|
||||
field->value[uref->usage_index] = uref->value;
|
||||
goto goodreturn;
|
||||
|
||||
case HIDIOCGCOLLECTIONINDEX:
|
||||
i = field->usage[uref->usage_index].collection_index;
|
||||
kfree(uref_multi);
|
||||
return i;
|
||||
case HIDIOCGUSAGES:
|
||||
for (i = 0; i < uref_multi->num_values; i++)
|
||||
uref_multi->values[i] =
|
||||
field->value[uref->usage_index + i];
|
||||
if (copy_to_user(user_arg, uref_multi,
|
||||
sizeof(*uref_multi)))
|
||||
goto fault;
|
||||
goto goodreturn;
|
||||
case HIDIOCSUSAGES:
|
||||
for (i = 0; i < uref_multi->num_values; i++)
|
||||
field->value[uref->usage_index + i] =
|
||||
uref_multi->values[i];
|
||||
goto goodreturn;
|
||||
}
|
||||
|
||||
goodreturn:
|
||||
kfree(uref_multi);
|
||||
return 0;
|
||||
fault:
|
||||
kfree(uref_multi);
|
||||
return -EFAULT;
|
||||
inval:
|
||||
kfree(uref_multi);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
|
||||
{
|
||||
struct hid_device *hid = hiddev->hid;
|
||||
struct usb_device *dev = hid_to_usb_dev(hid);
|
||||
int idx, len;
|
||||
char *buf;
|
||||
|
||||
if (get_user(idx, (int __user *)user_arg))
|
||||
return -EFAULT;
|
||||
|
||||
if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
|
||||
kfree(buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
struct hiddev *hiddev = list->hiddev;
|
||||
struct hid_device *hid;
|
||||
struct hiddev_collection_info cinfo;
|
||||
struct hiddev_report_info rinfo;
|
||||
struct hiddev_field_info finfo;
|
||||
struct hiddev_devinfo dinfo;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
void __user *user_arg = (void __user *)arg;
|
||||
int i, r = -EINVAL;
|
||||
|
||||
/* Called without BKL by compat methods so no BKL taken */
|
||||
|
||||
mutex_lock(&hiddev->existancelock);
|
||||
if (!hiddev->exist) {
|
||||
r = -ENODEV;
|
||||
goto ret_unlock;
|
||||
}
|
||||
|
||||
hid = hiddev->hid;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case HIDIOCGVERSION:
|
||||
r = put_user(HID_VERSION, (int __user *)arg) ?
|
||||
-EFAULT : 0;
|
||||
break;
|
||||
|
||||
case HIDIOCAPPLICATION:
|
||||
if (arg >= hid->maxapplication)
|
||||
break;
|
||||
|
||||
for (i = 0; i < hid->maxcollection; i++)
|
||||
if (hid->collection[i].type ==
|
||||
HID_COLLECTION_APPLICATION && arg-- == 0)
|
||||
break;
|
||||
|
||||
if (i < hid->maxcollection)
|
||||
r = hid->collection[i].usage;
|
||||
break;
|
||||
|
||||
case HIDIOCGDEVINFO:
|
||||
{
|
||||
struct usb_device *dev = hid_to_usb_dev(hid);
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
memset(&dinfo, 0, sizeof(dinfo));
|
||||
|
||||
dinfo.bustype = BUS_USB;
|
||||
dinfo.busnum = dev->bus->busnum;
|
||||
dinfo.devnum = dev->devnum;
|
||||
dinfo.ifnum = usbhid->ifnum;
|
||||
dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||
dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
|
||||
dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
|
||||
dinfo.num_applications = hid->maxapplication;
|
||||
|
||||
r = copy_to_user(user_arg, &dinfo, sizeof(dinfo)) ?
|
||||
-EFAULT : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case HIDIOCGFLAG:
|
||||
r = put_user(list->flags, (int __user *)arg) ?
|
||||
-EFAULT : 0;
|
||||
break;
|
||||
|
||||
case HIDIOCSFLAG:
|
||||
{
|
||||
int newflags;
|
||||
|
||||
if (get_user(newflags, (int __user *)arg)) {
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((newflags & ~HIDDEV_FLAGS) != 0 ||
|
||||
((newflags & HIDDEV_FLAG_REPORT) != 0 &&
|
||||
(newflags & HIDDEV_FLAG_UREF) == 0))
|
||||
break;
|
||||
|
||||
list->flags = newflags;
|
||||
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case HIDIOCGSTRING:
|
||||
r = hiddev_ioctl_string(hiddev, cmd, user_arg);
|
||||
break;
|
||||
|
||||
case HIDIOCINITREPORT:
|
||||
usbhid_init_reports(hid);
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case HIDIOCGREPORT:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
|
||||
break;
|
||||
|
||||
report = hiddev_lookup_report(hid, &rinfo);
|
||||
if (report == NULL)
|
||||
break;
|
||||
|
||||
hid_hw_request(hid, report, HID_REQ_GET_REPORT);
|
||||
hid_hw_wait(hid);
|
||||
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case HIDIOCSREPORT:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
|
||||
break;
|
||||
|
||||
report = hiddev_lookup_report(hid, &rinfo);
|
||||
if (report == NULL)
|
||||
break;
|
||||
|
||||
hid_hw_request(hid, report, HID_REQ_SET_REPORT);
|
||||
hid_hw_wait(hid);
|
||||
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case HIDIOCGREPORTINFO:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
report = hiddev_lookup_report(hid, &rinfo);
|
||||
if (report == NULL)
|
||||
break;
|
||||
|
||||
rinfo.num_fields = report->maxfield;
|
||||
|
||||
r = copy_to_user(user_arg, &rinfo, sizeof(rinfo)) ?
|
||||
-EFAULT : 0;
|
||||
break;
|
||||
|
||||
case HIDIOCGFIELDINFO:
|
||||
if (copy_from_user(&finfo, user_arg, sizeof(finfo))) {
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rinfo.report_type = finfo.report_type;
|
||||
rinfo.report_id = finfo.report_id;
|
||||
|
||||
report = hiddev_lookup_report(hid, &rinfo);
|
||||
if (report == NULL)
|
||||
break;
|
||||
|
||||
if (finfo.field_index >= report->maxfield)
|
||||
break;
|
||||
|
||||
field = report->field[finfo.field_index];
|
||||
memset(&finfo, 0, sizeof(finfo));
|
||||
finfo.report_type = rinfo.report_type;
|
||||
finfo.report_id = rinfo.report_id;
|
||||
finfo.field_index = field->report_count - 1;
|
||||
finfo.maxusage = field->maxusage;
|
||||
finfo.flags = field->flags;
|
||||
finfo.physical = field->physical;
|
||||
finfo.logical = field->logical;
|
||||
finfo.application = field->application;
|
||||
finfo.logical_minimum = field->logical_minimum;
|
||||
finfo.logical_maximum = field->logical_maximum;
|
||||
finfo.physical_minimum = field->physical_minimum;
|
||||
finfo.physical_maximum = field->physical_maximum;
|
||||
finfo.unit_exponent = field->unit_exponent;
|
||||
finfo.unit = field->unit;
|
||||
|
||||
r = copy_to_user(user_arg, &finfo, sizeof(finfo)) ?
|
||||
-EFAULT : 0;
|
||||
break;
|
||||
|
||||
case HIDIOCGUCODE:
|
||||
/* fall through */
|
||||
case HIDIOCGUSAGE:
|
||||
case HIDIOCSUSAGE:
|
||||
case HIDIOCGUSAGES:
|
||||
case HIDIOCSUSAGES:
|
||||
case HIDIOCGCOLLECTIONINDEX:
|
||||
r = hiddev_ioctl_usage(hiddev, cmd, user_arg);
|
||||
break;
|
||||
|
||||
case HIDIOCGCOLLECTIONINFO:
|
||||
if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) {
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cinfo.index >= hid->maxcollection)
|
||||
break;
|
||||
|
||||
cinfo.type = hid->collection[cinfo.index].type;
|
||||
cinfo.usage = hid->collection[cinfo.index].usage;
|
||||
cinfo.level = hid->collection[cinfo.index].level;
|
||||
|
||||
r = copy_to_user(user_arg, &cinfo, sizeof(cinfo)) ?
|
||||
-EFAULT : 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
|
||||
break;
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
|
||||
int len = strlen(hid->name) + 1;
|
||||
if (len > _IOC_SIZE(cmd))
|
||||
len = _IOC_SIZE(cmd);
|
||||
r = copy_to_user(user_arg, hid->name, len) ?
|
||||
-EFAULT : len;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
|
||||
int len = strlen(hid->phys) + 1;
|
||||
if (len > _IOC_SIZE(cmd))
|
||||
len = _IOC_SIZE(cmd);
|
||||
r = copy_to_user(user_arg, hid->phys, len) ?
|
||||
-EFAULT : len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret_unlock:
|
||||
mutex_unlock(&hiddev->existancelock);
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return hiddev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations hiddev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = hiddev_read,
|
||||
.write = hiddev_write,
|
||||
.poll = hiddev_poll,
|
||||
.open = hiddev_open,
|
||||
.release = hiddev_release,
|
||||
.unlocked_ioctl = hiddev_ioctl,
|
||||
.fasync = hiddev_fasync,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = hiddev_compat_ioctl,
|
||||
#endif
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static char *hiddev_devnode(struct device *dev, umode_t *mode)
|
||||
{
|
||||
return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
|
||||
}
|
||||
|
||||
static struct usb_class_driver hiddev_class = {
|
||||
.name = "hiddev%d",
|
||||
.devnode = hiddev_devnode,
|
||||
.fops = &hiddev_fops,
|
||||
.minor_base = HIDDEV_MINOR_BASE,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is where hid.c calls us to connect a hid device to the hiddev driver
|
||||
*/
|
||||
int hiddev_connect(struct hid_device *hid, unsigned int force)
|
||||
{
|
||||
struct hiddev *hiddev;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int retval;
|
||||
|
||||
if (!force) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < hid->maxcollection; i++)
|
||||
if (hid->collection[i].type ==
|
||||
HID_COLLECTION_APPLICATION &&
|
||||
!IS_INPUT_APPLICATION(hid->collection[i].usage))
|
||||
break;
|
||||
|
||||
if (i == hid->maxcollection)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
|
||||
return -1;
|
||||
|
||||
init_waitqueue_head(&hiddev->wait);
|
||||
INIT_LIST_HEAD(&hiddev->list);
|
||||
spin_lock_init(&hiddev->list_lock);
|
||||
mutex_init(&hiddev->existancelock);
|
||||
hid->hiddev = hiddev;
|
||||
hiddev->hid = hid;
|
||||
hiddev->exist = 1;
|
||||
retval = usb_register_dev(usbhid->intf, &hiddev_class);
|
||||
if (retval) {
|
||||
hid_err(hid, "Not able to get a minor for this device\n");
|
||||
hid->hiddev = NULL;
|
||||
kfree(hiddev);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where hid.c calls us to disconnect a hiddev device from the
|
||||
* corresponding hid device (usually because the usb device has disconnected)
|
||||
*/
|
||||
static struct usb_class_driver hiddev_class;
|
||||
void hiddev_disconnect(struct hid_device *hid)
|
||||
{
|
||||
struct hiddev *hiddev = hid->hiddev;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
usb_deregister_dev(usbhid->intf, &hiddev_class);
|
||||
|
||||
mutex_lock(&hiddev->existancelock);
|
||||
hiddev->exist = 0;
|
||||
|
||||
if (hiddev->open) {
|
||||
mutex_unlock(&hiddev->existancelock);
|
||||
usbhid_close(hiddev->hid);
|
||||
wake_up_interruptible(&hiddev->wait);
|
||||
} else {
|
||||
mutex_unlock(&hiddev->existancelock);
|
||||
kfree(hiddev);
|
||||
}
|
||||
}
|
104
drivers/hid/usbhid/usbhid.h
Normal file
104
drivers/hid/usbhid/usbhid.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
#ifndef __USBHID_H
|
||||
#define __USBHID_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2001 Vojtech Pavlik
|
||||
* Copyright (c) 2006 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.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
/* API provided by hid-core.c for USB HID drivers */
|
||||
void usbhid_close(struct hid_device *hid);
|
||||
int usbhid_open(struct hid_device *hid);
|
||||
void usbhid_init_reports(struct hid_device *hid);
|
||||
int usbhid_get_power(struct hid_device *hid);
|
||||
void usbhid_put_power(struct hid_device *hid);
|
||||
struct usb_interface *usbhid_find_interface(int minor);
|
||||
|
||||
/* iofl flags */
|
||||
#define HID_CTRL_RUNNING 1
|
||||
#define HID_OUT_RUNNING 2
|
||||
#define HID_IN_RUNNING 3
|
||||
#define HID_RESET_PENDING 4
|
||||
#define HID_SUSPENDED 5
|
||||
#define HID_CLEAR_HALT 6
|
||||
#define HID_DISCONNECTED 7
|
||||
#define HID_STARTED 8
|
||||
#define HID_KEYS_PRESSED 10
|
||||
#define HID_NO_BANDWIDTH 11
|
||||
|
||||
/*
|
||||
* USB-specific HID struct, to be pointed to
|
||||
* from struct hid_device->driver_data
|
||||
*/
|
||||
|
||||
struct usbhid_device {
|
||||
struct hid_device *hid; /* pointer to corresponding HID dev */
|
||||
|
||||
struct usb_interface *intf; /* USB interface */
|
||||
int ifnum; /* USB interface number */
|
||||
|
||||
unsigned int bufsize; /* URB buffer size */
|
||||
|
||||
struct urb *urbin; /* Input URB */
|
||||
char *inbuf; /* Input buffer */
|
||||
dma_addr_t inbuf_dma; /* Input buffer dma */
|
||||
|
||||
struct urb *urbctrl; /* Control URB */
|
||||
struct usb_ctrlrequest *cr; /* Control request struct */
|
||||
struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
|
||||
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
|
||||
char *ctrlbuf; /* Control buffer */
|
||||
dma_addr_t ctrlbuf_dma; /* Control buffer dma */
|
||||
unsigned long last_ctrl; /* record of last output for timeouts */
|
||||
|
||||
struct urb *urbout; /* Output URB */
|
||||
struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
|
||||
unsigned char outhead, outtail; /* Output pipe fifo head & tail */
|
||||
char *outbuf; /* Output buffer */
|
||||
dma_addr_t outbuf_dma; /* Output buffer dma */
|
||||
unsigned long last_out; /* record of last output for timeouts */
|
||||
|
||||
spinlock_t lock; /* fifo spinlock */
|
||||
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
|
||||
struct timer_list io_retry; /* Retry timer */
|
||||
unsigned long stop_retry; /* Time to give up, in jiffies */
|
||||
unsigned int retry_delay; /* Delay length in ms */
|
||||
struct work_struct reset_work; /* Task context for resets */
|
||||
wait_queue_head_t wait; /* For sleeping */
|
||||
#ifdef CONFIG_USB_DEBUG_DETAILED_LOG
|
||||
unsigned long in_err_isr;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define hid_to_usb_dev(hid_dev) \
|
||||
container_of(hid_dev->dev.parent->parent, struct usb_device, dev)
|
||||
|
||||
#endif
|
||||
|
411
drivers/hid/usbhid/usbkbd.c
Normal file
411
drivers/hid/usbhid/usbkbd.c
Normal file
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*
|
||||
* USB HIDBP Keyboard support
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION ""
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
static const unsigned char usb_kbd_keycode[256] = {
|
||||
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
|
||||
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
|
||||
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
|
||||
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
|
||||
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
|
||||
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
|
||||
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
|
||||
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
|
||||
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
|
||||
150,158,159,128,136,177,178,176,142,152,173,140
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct usb_kbd - state of each attached keyboard
|
||||
* @dev: input device associated with this keyboard
|
||||
* @usbdev: usb device associated with this keyboard
|
||||
* @old: data received in the past from the @irq URB representing which
|
||||
* keys were pressed. By comparing with the current list of keys
|
||||
* that are pressed, we are able to see key releases.
|
||||
* @irq: URB for receiving a list of keys that are pressed when a
|
||||
* new key is pressed or a key that was pressed is released.
|
||||
* @led: URB for sending LEDs (e.g. numlock, ...)
|
||||
* @newleds: data that will be sent with the @led URB representing which LEDs
|
||||
should be on
|
||||
* @name: Name of the keyboard. @dev's name field points to this buffer
|
||||
* @phys: Physical path of the keyboard. @dev's phys field points to this
|
||||
* buffer
|
||||
* @new: Buffer for the @irq URB
|
||||
* @cr: Control request for @led URB
|
||||
* @leds: Buffer for the @led URB
|
||||
* @new_dma: DMA address for @irq URB
|
||||
* @leds_dma: DMA address for @led URB
|
||||
* @leds_lock: spinlock that protects @leds, @newleds, and @led_urb_submitted
|
||||
* @led_urb_submitted: indicates whether @led is in progress, i.e. it has been
|
||||
* submitted and its completion handler has not returned yet
|
||||
* without resubmitting @led
|
||||
*/
|
||||
struct usb_kbd {
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
unsigned char old[8];
|
||||
struct urb *irq, *led;
|
||||
unsigned char newleds;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
|
||||
unsigned char *new;
|
||||
struct usb_ctrlrequest *cr;
|
||||
unsigned char *leds;
|
||||
dma_addr_t new_dma;
|
||||
dma_addr_t leds_dma;
|
||||
|
||||
spinlock_t leds_lock;
|
||||
bool led_urb_submitted;
|
||||
|
||||
};
|
||||
|
||||
static void usb_kbd_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_kbd *kbd = urb->context;
|
||||
int i;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
/* -EPIPE: should clear the halt */
|
||||
default: /* error */
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
|
||||
|
||||
for (i = 2; i < 8; i++) {
|
||||
|
||||
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
|
||||
if (usb_kbd_keycode[kbd->old[i]])
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
|
||||
else
|
||||
hid_info(urb->dev,
|
||||
"Unknown key (scancode %#x) released.\n",
|
||||
kbd->old[i]);
|
||||
}
|
||||
|
||||
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
|
||||
if (usb_kbd_keycode[kbd->new[i]])
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
|
||||
else
|
||||
hid_info(urb->dev,
|
||||
"Unknown key (scancode %#x) pressed.\n",
|
||||
kbd->new[i]);
|
||||
}
|
||||
}
|
||||
|
||||
input_sync(kbd->dev);
|
||||
|
||||
memcpy(kbd->old, kbd->new, 8);
|
||||
|
||||
resubmit:
|
||||
i = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (i)
|
||||
hid_err(urb->dev, "can't resubmit intr, %s-%s/input0, status %d",
|
||||
kbd->usbdev->bus->bus_name,
|
||||
kbd->usbdev->devpath, i);
|
||||
}
|
||||
|
||||
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct usb_kbd *kbd = input_get_drvdata(dev);
|
||||
|
||||
if (type != EV_LED)
|
||||
return -1;
|
||||
|
||||
spin_lock_irqsave(&kbd->leds_lock, flags);
|
||||
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
|
||||
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
|
||||
(!!test_bit(LED_NUML, dev->led));
|
||||
|
||||
if (kbd->led_urb_submitted){
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds){
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*(kbd->leds) = kbd->newleds;
|
||||
|
||||
kbd->led->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
|
||||
pr_err("usb_submit_urb(leds) failed\n");
|
||||
else
|
||||
kbd->led_urb_submitted = true;
|
||||
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_led(struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct usb_kbd *kbd = urb->context;
|
||||
|
||||
if (urb->status)
|
||||
hid_warn(urb->dev, "led urb status %d received\n",
|
||||
urb->status);
|
||||
|
||||
spin_lock_irqsave(&kbd->leds_lock, flags);
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds){
|
||||
kbd->led_urb_submitted = false;
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
*(kbd->leds) = kbd->newleds;
|
||||
|
||||
kbd->led->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC)){
|
||||
hid_err(urb->dev, "usb_submit_urb(leds) failed\n");
|
||||
kbd->led_urb_submitted = false;
|
||||
}
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
|
||||
}
|
||||
|
||||
static int usb_kbd_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_kbd *kbd = input_get_drvdata(dev);
|
||||
|
||||
kbd->irq->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_kbd *kbd = input_get_drvdata(dev);
|
||||
|
||||
usb_kill_urb(kbd->irq);
|
||||
}
|
||||
|
||||
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
|
||||
{
|
||||
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
return -1;
|
||||
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
return -1;
|
||||
if (!(kbd->new = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
|
||||
return -1;
|
||||
if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))
|
||||
return -1;
|
||||
if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
|
||||
{
|
||||
usb_free_urb(kbd->irq);
|
||||
usb_free_urb(kbd->led);
|
||||
usb_free_coherent(dev, 8, kbd->new, kbd->new_dma);
|
||||
kfree(kbd->cr);
|
||||
usb_free_coherent(dev, 1, kbd->leds, kbd->leds_dma);
|
||||
}
|
||||
|
||||
static int usb_kbd_probe(struct usb_interface *iface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(iface);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_kbd *kbd;
|
||||
struct input_dev *input_dev;
|
||||
int i, pipe, maxp;
|
||||
int error = -ENOMEM;
|
||||
|
||||
interface = iface->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!kbd || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
if (usb_kbd_alloc_mem(dev, kbd))
|
||||
goto fail2;
|
||||
|
||||
kbd->usbdev = dev;
|
||||
kbd->dev = input_dev;
|
||||
spin_lock_init(&kbd->leds_lock);
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
|
||||
|
||||
if (dev->product) {
|
||||
if (dev->manufacturer)
|
||||
strlcat(kbd->name, " ", sizeof(kbd->name));
|
||||
strlcat(kbd->name, dev->product, sizeof(kbd->name));
|
||||
}
|
||||
|
||||
if (!strlen(kbd->name))
|
||||
snprintf(kbd->name, sizeof(kbd->name),
|
||||
"USB HIDBP Keyboard %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
|
||||
strlcat(kbd->phys, "/input0", sizeof(kbd->phys));
|
||||
|
||||
input_dev->name = kbd->name;
|
||||
input_dev->phys = kbd->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->dev.parent = &iface->dev;
|
||||
|
||||
input_set_drvdata(input_dev, kbd);
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
|
||||
BIT_MASK(EV_REP);
|
||||
input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
|
||||
BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
|
||||
BIT_MASK(LED_KANA);
|
||||
|
||||
for (i = 0; i < 255; i++)
|
||||
set_bit(usb_kbd_keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
input_dev->event = usb_kbd_event;
|
||||
input_dev->open = usb_kbd_open;
|
||||
input_dev->close = usb_kbd_close;
|
||||
|
||||
usb_fill_int_urb(kbd->irq, dev, pipe,
|
||||
kbd->new, (maxp > 8 ? 8 : maxp),
|
||||
usb_kbd_irq, kbd, endpoint->bInterval);
|
||||
kbd->irq->transfer_dma = kbd->new_dma;
|
||||
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
kbd->cr->bRequest = 0x09;
|
||||
kbd->cr->wValue = cpu_to_le16(0x200);
|
||||
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
|
||||
kbd->cr->wLength = cpu_to_le16(1);
|
||||
|
||||
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
|
||||
(void *) kbd->cr, kbd->leds, 1,
|
||||
usb_kbd_led, kbd);
|
||||
kbd->led->transfer_dma = kbd->leds_dma;
|
||||
kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
error = input_register_device(kbd->dev);
|
||||
if (error)
|
||||
goto fail2;
|
||||
|
||||
usb_set_intfdata(iface, kbd);
|
||||
device_set_wakeup_enable(&dev->dev, 1);
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
usb_kbd_free_mem(dev, kbd);
|
||||
fail1:
|
||||
input_free_device(input_dev);
|
||||
kfree(kbd);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void usb_kbd_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_kbd *kbd = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (kbd) {
|
||||
usb_kill_urb(kbd->irq);
|
||||
input_unregister_device(kbd->dev);
|
||||
usb_kill_urb(kbd->led);
|
||||
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
|
||||
kfree(kbd);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_kbd_id_table [] = {
|
||||
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
|
||||
USB_INTERFACE_PROTOCOL_KEYBOARD) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
|
||||
|
||||
static struct usb_driver usb_kbd_driver = {
|
||||
.name = "usbkbd",
|
||||
.probe = usb_kbd_probe,
|
||||
.disconnect = usb_kbd_disconnect,
|
||||
.id_table = usb_kbd_id_table,
|
||||
};
|
||||
|
||||
module_usb_driver(usb_kbd_driver);
|
245
drivers/hid/usbhid/usbmouse.c
Normal file
245
drivers/hid/usbhid/usbmouse.c
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*
|
||||
* USB HIDBP Mouse support
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
/* for apple IDs */
|
||||
#ifdef CONFIG_USB_HID_MODULE
|
||||
#include "../hid-ids.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.6"
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
struct usb_mouse {
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct usb_device *usbdev;
|
||||
struct input_dev *dev;
|
||||
struct urb *irq;
|
||||
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
};
|
||||
|
||||
static void usb_mouse_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_mouse *mouse = urb->context;
|
||||
signed char *data = mouse->data;
|
||||
struct input_dev *dev = mouse->dev;
|
||||
int status;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
/* -EPIPE: should clear the halt */
|
||||
default: /* error */
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
|
||||
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
|
||||
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
|
||||
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
|
||||
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
|
||||
|
||||
input_report_rel(dev, REL_X, data[1]);
|
||||
input_report_rel(dev, REL_Y, data[2]);
|
||||
input_report_rel(dev, REL_WHEEL, data[3]);
|
||||
|
||||
input_sync(dev);
|
||||
resubmit:
|
||||
status = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (status)
|
||||
dev_err(&mouse->usbdev->dev,
|
||||
"can't resubmit intr, %s-%s/input0, status %d\n",
|
||||
mouse->usbdev->bus->bus_name,
|
||||
mouse->usbdev->devpath, status);
|
||||
}
|
||||
|
||||
static int usb_mouse_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_mouse *mouse = input_get_drvdata(dev);
|
||||
|
||||
mouse->irq->dev = mouse->usbdev;
|
||||
if (usb_submit_urb(mouse->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_mouse_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_mouse *mouse = input_get_drvdata(dev);
|
||||
|
||||
usb_kill_urb(mouse->irq);
|
||||
}
|
||||
|
||||
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_mouse *mouse;
|
||||
struct input_dev *input_dev;
|
||||
int pipe, maxp;
|
||||
int error = -ENOMEM;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!mouse || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);
|
||||
if (!mouse->data)
|
||||
goto fail1;
|
||||
|
||||
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!mouse->irq)
|
||||
goto fail2;
|
||||
|
||||
mouse->usbdev = dev;
|
||||
mouse->dev = input_dev;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
|
||||
|
||||
if (dev->product) {
|
||||
if (dev->manufacturer)
|
||||
strlcat(mouse->name, " ", sizeof(mouse->name));
|
||||
strlcat(mouse->name, dev->product, sizeof(mouse->name));
|
||||
}
|
||||
|
||||
if (!strlen(mouse->name))
|
||||
snprintf(mouse->name, sizeof(mouse->name),
|
||||
"USB HIDBP Mouse %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
|
||||
strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
|
||||
|
||||
input_dev->name = mouse->name;
|
||||
input_dev->phys = mouse->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->dev.parent = &intf->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
|
||||
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
|
||||
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
|
||||
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
|
||||
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
|
||||
BIT_MASK(BTN_EXTRA);
|
||||
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
|
||||
|
||||
input_set_drvdata(input_dev, mouse);
|
||||
|
||||
input_dev->open = usb_mouse_open;
|
||||
input_dev->close = usb_mouse_close;
|
||||
|
||||
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
|
||||
(maxp > 8 ? 8 : maxp),
|
||||
usb_mouse_irq, mouse, endpoint->bInterval);
|
||||
mouse->irq->transfer_dma = mouse->data_dma;
|
||||
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
error = input_register_device(mouse->dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
usb_set_intfdata(intf, mouse);
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
usb_free_urb(mouse->irq);
|
||||
fail2:
|
||||
usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
|
||||
fail1:
|
||||
input_free_device(input_dev);
|
||||
kfree(mouse);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void usb_mouse_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_mouse *mouse = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (mouse) {
|
||||
usb_kill_urb(mouse->irq);
|
||||
input_unregister_device(mouse->dev);
|
||||
usb_free_urb(mouse->irq);
|
||||
usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
|
||||
kfree(mouse);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_mouse_id_table [] = {
|
||||
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
|
||||
USB_INTERFACE_PROTOCOL_MOUSE) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
|
||||
|
||||
static struct usb_driver usb_mouse_driver = {
|
||||
.name = "usbmouse",
|
||||
.probe = usb_mouse_probe,
|
||||
.disconnect = usb_mouse_disconnect,
|
||||
.id_table = usb_mouse_id_table,
|
||||
};
|
||||
|
||||
module_usb_driver(usb_mouse_driver);
|
Loading…
Add table
Add a link
Reference in a new issue