From: James S. <jsi...@us...> - 2002-04-16 19:37:35
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/usb/input In directory usw-pr-cvs1:/tmp/cvs-serv13887/linux/drivers/usb/input Added Files: Config.help Config.in Makefile hid-core.c hid-debug.h hid-ff.c hid-input.c hid-lgff.c hid.h hiddev.c pid.c pid.h powermate.c usbkbd.c usbmouse.c usbpath.h wacom.c Log Message: Synced to 2.5.8. --- NEW FILE: Config.help --- CONFIG_USB_HID Say Y here if you want full HID support to connect keyboards, mice, joysticks, graphic tablets, or any other HID based devices to your computer via USB. You also need to select HID Input layer support (below) if you want to use keyboards, mice, joysticks and the like. 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. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called hid.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. CONFIG_USB_HIDINPUT Say Y here if you want to use a USB keyboard, mouse or joystick, or any other HID input device. You also need Input layer support, (CONFIG_INPUT) which you select under "Input core support". If unsure, say Y. CONFIG_HID_FF Say Y here is you want force feedback support for a few hid devices. See below for a list of supported devices. See Documentation/input/ff.txt for a description of the force feedback API. If unsure, say N. CONFIG_LOGITECH_RUMBLE Say Y here if you have a Logitech WingMan Cordless rumble pad and if you want to enable force feedback. Note: if you say N here, this device will still be supported, but without force feedback. CONFIG_HID_PID Say Y yes if you have a PID-compliant joystick and wish to enable force feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such device. CONFIG_USB_HIDDEV 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). This driver requires CONFIG_USB_HID. If unsure, say Y. CONFIG_USB_KBD 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 code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called usbkbd.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. If even remotely unsure, say N. CONFIG_USB_MOUSE 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 code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called usbmouse.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. If even remotely unsure, say N. CONFIG_USB_WACOM Say Y here if you want to use the USB version of the Wacom Intuos or Graphire tablet. Make sure to say Y to "Mouse support" (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called wacom.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. CONFIG_USB_POWERMATE Say Y here if you want to use the Griffin Technology, Inc. USB PowerMate device. This device is a stainless steel dial which can measure clockwise and anticlockwise rotation. The dial also acts as a pushbutton. The base contains an LED which can be instructed to pulse or to switch to a particular intensity. You can download userspace tools from http://sowerbutts.com/powermate/ This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called powermate.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. --- NEW FILE: Config.in --- # # USB Input driver configuration # comment 'USB Human Interface Devices (HID)' dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB if [ "$CONFIG_INPUT" = "n" ]; then comment ' Input core support is needed for USB HID input layer or HIDBP support' fi dep_mbool ' HID input layer support' CONFIG_USB_HIDINPUT $CONFIG_INPUT $CONFIG_USB_HID dep_mbool ' /dev/hiddev raw HID device support' CONFIG_USB_HIDDEV $CONFIG_USB_HID if [ "$CONFIG_USB_HID" != "y" ]; then dep_tristate ' USB HIDBP Keyboard (basic) support' CONFIG_USB_KBD $CONFIG_USB $CONFIG_INPUT dep_tristate ' USB HIDBP Mouse (basic) support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT dep_tristate ' Griffin Technology PowerMate support' CONFIG_USB_POWERMATE $CONFIG_USB $CONFIG_INPUT dep_mbool ' Force feedback support' CONFIG_HID_FF $CONFIG_USB_HID dep_mbool ' Logitech RumblePad support' CONFIG_LOGITECH_RUMBLE $CONFIG_USB_HID $CONFIG_HID_FF dep_mbool ' PID Devices' CONFIG_HID_PID $CONFIG_USB_HID $CONFIG_HID_FF fi dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT # Turn on CONFIG_USB_INPUT if any of the drivers are compiled into the kernel # to make our Makefile logic a bit simpler. if [ "$CONFIG_USB_HID" = "y" -o "$CONFIG_USB_KBD" = "y" -o "$CONFIG_USB_MOUSE" = "y" ]; then define_bool CONFIG_USB_INPUT y fi if [ "$CONFIG_USB_WACOM" = "y" ]; then define_bool CONFIG_USB_INPUT y fi --- NEW FILE: Makefile --- # # Makefile for the USB input drivers # O_TARGET := usb-input.o # Multipart objects. hid-objs := hid-core.o # Optional parts of multipart objects. ifeq ($(CONFIG_USB_HIDDEV),y) hid-objs += hiddev.o endif ifeq ($(CONFIG_USB_HIDINPUT),y) hid-objs += hid-input.o endif ifeq ($(CONFIG_HID_PID),y) hid-objs += pid.o endif ifeq ($(CONFIG_LOGITECH_RUMBLE),y) hid-objs += hid-lgff.o endif ifeq ($(CONFIG_HID_FF),y) hid-objs += hid-ff.o endif obj-$(CONFIG_USB_HID) += hid.o obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_WACOM) += wacom.o obj-$(CONFIG_USB_POWERMATE) += powermate.o include $(TOPDIR)/Rules.make --- NEW FILE: hid-core.c --- /* * $Id: hid-core.c,v 1.1 2002/04/16 17:41:51 jsimmons Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2001 Vojtech Pavlik * * USB HID support for Linux */ /* * 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. [...1483 lines suppressed...] usb_register(&hid_driver); info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } static void __exit hid_exit(void) { #ifdef CONFIG_USB_HIDDEV hiddev_exit(); #endif usb_deregister(&hid_driver); } module_init(hid_init); module_exit(hid_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE(DRIVER_LICENSE); --- NEW FILE: hid-debug.h --- /* * $Id: hid-debug.h,v 1.1 2002/04/16 17:41:51 jsimmons Exp $ * * (c) 1999 Andreas Gal <ga...@cs...> * (c) 2000-2001 Vojtech Pavlik <vo...@uc...> * * Some debug stuff for the HID parser. */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ struct hid_usage_entry { unsigned page; unsigned usage; char *description; }; static struct hid_usage_entry hid_usage_table[] = { { 0, 0, "Undefined" }, { 1, 0, "GenericDesktop" }, {0, 0x01, "Pointer"}, {0, 0x02, "Mouse"}, {0, 0x04, "Joystick"}, {0, 0x05, "GamePad"}, {0, 0x06, "Keyboard"}, {0, 0x07, "Keypad"}, {0, 0x08, "MultiAxis"}, {0, 0x30, "X"}, {0, 0x31, "Y"}, {0, 0x32, "Z"}, {0, 0x33, "Rx"}, {0, 0x34, "Ry"}, {0, 0x35, "Rz"}, {0, 0x36, "Slider"}, {0, 0x37, "Dial"}, {0, 0x38, "Wheel"}, {0, 0x39, "HatSwitch"}, {0, 0x3a, "CountedBuffer"}, {0, 0x3b, "ByteCount"}, {0, 0x3c, "MotionWakeup"}, {0, 0x3d, "Start"}, {0, 0x3e, "Select"}, {0, 0x40, "Vx"}, {0, 0x41, "Vy"}, {0, 0x42, "Vz"}, {0, 0x43, "Vbrx"}, {0, 0x44, "Vbry"}, {0, 0x45, "Vbrz"}, {0, 0x46, "Vno"}, {0, 0x80, "SystemControl"}, {0, 0x81, "SystemPowerDown"}, {0, 0x82, "SystemSleep"}, {0, 0x83, "SystemWakeUp"}, {0, 0x84, "SystemContextMenu"}, {0, 0x85, "SystemMainMenu"}, {0, 0x86, "SystemAppMenu"}, {0, 0x87, "SystemMenuHelp"}, {0, 0x88, "SystemMenuExit"}, {0, 0x89, "SystemMenuSelect"}, {0, 0x8a, "SystemMenuRight"}, {0, 0x8b, "SystemMenuLeft"}, {0, 0x8c, "SystemMenuUp"}, {0, 0x8d, "SystemMenuDown"}, {0, 0x90, "D-padUp"}, {0, 0x91, "D-padDown"}, {0, 0x92, "D-padRight"}, {0, 0x93, "D-padLeft"}, { 7, 0, "Keyboard" }, { 8, 0, "LED" }, { 9, 0, "Button" }, { 10, 0, "Ordinal" }, { 12, 0, "Hotkey" }, { 13, 0, "Digitizers" }, {0, 0x01, "Digitizer"}, {0, 0x02, "Pen"}, {0, 0x03, "LightPen"}, {0, 0x04, "TouchScreen"}, {0, 0x05, "TouchPad"}, {0, 0x20, "Stylus"}, {0, 0x21, "Puck"}, {0, 0x22, "Finger"}, {0, 0x30, "TipPressure"}, {0, 0x31, "BarrelPressure"}, {0, 0x32, "InRange"}, {0, 0x33, "Touch"}, {0, 0x34, "UnTouch"}, {0, 0x35, "Tap"}, {0, 0x39, "TabletFunctionKey"}, {0, 0x3a, "ProgramChangeKey"}, {0, 0x3c, "Invert"}, {0, 0x42, "TipSwitch"}, {0, 0x43, "SecondaryTipSwitch"}, {0, 0x44, "BarrelSwitch"}, {0, 0x45, "Eraser"}, {0, 0x46, "TabletPick"}, { 15, 0, "PhysicalInterfaceDevice" }, {0, 0x00, "Undefined"}, {0, 0x01, "Physical_Interface_Device"}, {0, 0x20, "Normal"}, {0, 0x21, "Set_Effect_Report"}, {0, 0x22, "Effect_Block_Index"}, {0, 0x23, "Parameter_Block_Offset"}, {0, 0x24, "ROM_Flag"}, {0, 0x25, "Effect_Type"}, {0, 0x26, "ET_Constant_Force"}, {0, 0x27, "ET_Ramp"}, {0, 0x28, "ET_Custom_Force_Data"}, {0, 0x30, "ET_Square"}, {0, 0x31, "ET_Sine"}, {0, 0x32, "ET_Triangle"}, {0, 0x33, "ET_Sawtooth_Up"}, {0, 0x34, "ET_Sawtooth_Down"}, {0, 0x40, "ET_Spring"}, {0, 0x41, "ET_Damper"}, {0, 0x42, "ET_Inertia"}, {0, 0x43, "ET_Friction"}, {0, 0x50, "Duration"}, {0, 0x51, "Sample_Period"}, {0, 0x52, "Gain"}, {0, 0x53, "Trigger_Button"}, {0, 0x54, "Trigger_Repeat_Interval"}, {0, 0x55, "Axes_Enable"}, {0, 0x56, "Direction_Enable"}, {0, 0x57, "Direction"}, {0, 0x58, "Type_Specific_Block_Offset"}, {0, 0x59, "Block_Type"}, {0, 0x5A, "Set_Envelope_Report"}, {0, 0x5B, "Attack_Level"}, {0, 0x5C, "Attack_Time"}, {0, 0x5D, "Fade_Level"}, {0, 0x5E, "Fade_Time"}, {0, 0x5F, "Set_Condition_Report"}, {0, 0x60, "CP_Offset"}, {0, 0x61, "Positive_Coefficient"}, {0, 0x62, "Negative_Coefficient"}, {0, 0x63, "Positive_Saturation"}, {0, 0x64, "Negative_Saturation"}, {0, 0x65, "Dead_Band"}, {0, 0x66, "Download_Force_Sample"}, {0, 0x67, "Isoch_Custom_Force_Enable"}, {0, 0x68, "Custom_Force_Data_Report"}, {0, 0x69, "Custom_Force_Data"}, {0, 0x6A, "Custom_Force_Vendor_Defined_Data"}, {0, 0x6B, "Set_Custom_Force_Report"}, {0, 0x6C, "Custom_Force_Data_Offset"}, {0, 0x6D, "Sample_Count"}, {0, 0x6E, "Set_Periodic_Report"}, {0, 0x6F, "Offset"}, {0, 0x70, "Magnitude"}, {0, 0x71, "Phase"}, {0, 0x72, "Period"}, {0, 0x73, "Set_Constant_Force_Report"}, {0, 0x74, "Set_Ramp_Force_Report"}, {0, 0x75, "Ramp_Start"}, {0, 0x76, "Ramp_End"}, {0, 0x77, "Effect_Operation_Report"}, {0, 0x78, "Effect_Operation"}, {0, 0x79, "Op_Effect_Start"}, {0, 0x7A, "Op_Effect_Start_Solo"}, {0, 0x7B, "Op_Effect_Stop"}, {0, 0x7C, "Loop_Count"}, {0, 0x7D, "Device_Gain_Report"}, {0, 0x7E, "Device_Gain"}, {0, 0x7F, "PID_Pool_Report"}, {0, 0x80, "RAM_Pool_Size"}, {0, 0x81, "ROM_Pool_Size"}, {0, 0x82, "ROM_Effect_Block_Count"}, {0, 0x83, "Simultaneous_Effects_Max"}, {0, 0x84, "Pool_Alignment"}, {0, 0x85, "PID_Pool_Move_Report"}, {0, 0x86, "Move_Source"}, {0, 0x87, "Move_Destination"}, {0, 0x88, "Move_Length"}, {0, 0x89, "PID_Block_Load_Report"}, {0, 0x8B, "Block_Load_Status"}, {0, 0x8C, "Block_Load_Success"}, {0, 0x8D, "Block_Load_Full"}, {0, 0x8E, "Block_Load_Error"}, {0, 0x8F, "Block_Handle"}, {0, 0x90, "PID_Block_Free_Report"}, {0, 0x91, "Type_Specific_Block_Handle"}, {0, 0x92, "PID_State_Report"}, {0, 0x94, "Effect_Playing"}, {0, 0x95, "PID_Device_Control_Report"}, {0, 0x96, "PID_Device_Control"}, {0, 0x97, "DC_Enable_Actuators"}, {0, 0x98, "DC_Disable_Actuators"}, {0, 0x99, "DC_Stop_All_Effects"}, {0, 0x9A, "DC_Device_Reset"}, {0, 0x9B, "DC_Device_Pause"}, {0, 0x9C, "DC_Device_Continue"}, {0, 0x9F, "Device_Paused"}, {0, 0xA0, "Actuators_Enabled"}, {0, 0xA4, "Safety_Switch"}, {0, 0xA5, "Actuator_Override_Switch"}, {0, 0xA6, "Actuator_Power"}, {0, 0xA7, "Start_Delay"}, {0, 0xA8, "Parameter_Block_Size"}, {0, 0xA9, "Device_Managed_Pool"}, {0, 0xAA, "Shared_Parameter_Blocks"}, {0, 0xAB, "Create_New_Effect_Report"}, {0, 0xAC, "RAM_Pool_Available"}, { 0, 0, NULL } }; static void resolv_usage_page(unsigned page) { struct hid_usage_entry *p; for (p = hid_usage_table; p->description; p++) if (p->page == page) { printk("%s", p->description); return; } printk("%04x", page); } static void resolv_usage(unsigned usage) { struct hid_usage_entry *p; resolv_usage_page(usage >> 16); printk("."); for (p = hid_usage_table; p->description; p++) if (p->page == (usage >> 16)) { for(++p; p->description && p->page == 0; p++) if (p->usage == (usage & 0xffff)) { printk("%s", p->description); return; } break; } printk("%04x", usage & 0xffff); } __inline__ static void tab(int n) { while (n--) printk(" "); } static void hid_dump_field(struct hid_field *field, int n) { int j; if (field->physical) { tab(n); printk("Physical("); resolv_usage(field->physical); printk(")\n"); } if (field->logical) { tab(n); printk("Logical("); resolv_usage(field->logical); printk(")\n"); } tab(n); printk("Usage(%d)\n", field->maxusage); for (j = 0; j < field->maxusage; j++) { tab(n+2);resolv_usage(field->usage[j].hid); printk("\n"); } if (field->logical_minimum != field->logical_maximum) { tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); } if (field->physical_minimum != field->physical_maximum) { tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); } if (field->unit_exponent) { tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); } if (field->unit) { char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; char *units[5][8] = { { "None", "None", "None", "None", "None", "None", "None", "None" }, { "None", "Centimeter", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" }, { "None", "Radians", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" }, { "None", "Inch", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" }, { "None", "Degrees", "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" } }; int i; int sys; __u32 data = field->unit; /* First nibble tells us which system we're in. */ sys = data & 0xf; data >>= 4; if(sys > 4) { tab(n); printk("Unit(Invalid)\n"); } else { int earlier_unit = 0; tab(n); printk("Unit(%s : ", systems[sys]); for (i=1 ; i<sizeof(__u32)*2 ; i++) { char nibble = data & 0xf; data >>= 4; if (nibble != 0) { if(earlier_unit++ > 0) printk("*"); printk("%s", units[sys][i]); if(nibble != 1) { /* This is a _signed_ nibble(!) */ int val = nibble & 0x7; if(nibble & 0x08) val = -((0x7 & ~val) +1); printk("^%d", val); } } } printk(")\n"); } } tab(n); printk("Report Size(%u)\n", field->report_size); tab(n); printk("Report Count(%u)\n", field->report_count); tab(n); printk("Report Offset(%u)\n", field->report_offset); tab(n); printk("Flags( "); j = field->flags; printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : ""); printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); printk(")\n"); } static void hid_dump_device(struct hid_device *device) { struct hid_report_enum *report_enum; struct hid_report *report; struct list_head *list; unsigned i,k; static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; for (i = 0; i < device->maxapplication; i++) { printk("Application("); resolv_usage(device->application[i]); printk(")\n"); } for (i = 0; i < HID_REPORT_TYPES; i++) { report_enum = device->report_enum + i; list = report_enum->report_list.next; while (list != &report_enum->report_list) { report = (struct hid_report *) list; tab(2); printk("%s", table[i]); if (report->id) printk("(%d)", report->id); printk("[%s]", table[report->type]); printk("\n"); for (k = 0; k < report->maxfield; k++) { tab(4); printk("Field(%d)\n", k); hid_dump_field(report->field[k], 6); } list = list->next; } } } static void hid_dump_input(struct hid_usage *usage, __s32 value) { printk("hid-debug: input "); resolv_usage(usage->hid); printk(" = %d\n", value); } --- NEW FILE: hid-ff.c --- /* * $Id: hid-ff.c,v 1.1 2002/04/16 17:41:51 jsimmons Exp $ * * Force feedback support for hid devices. * Not all hid devices use the same protocol. For example, some use PID, * other use their own proprietary procotol. * * Copyright (c) 2002 Johann Deneux */ /* * 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 by * e-mail - mail your message to <de...@if...> */ #include <linux/input.h> #define DEBUG #include <linux/usb.h> #include "hid.h" /* Drivers' initializing functions */ extern int hid_lgff_init(struct hid_device* hid); extern int hid_pid_init(struct hid_device* hid); struct hid_ff_initializer { __u16 idVendor; __u16 idProduct; int (*init)(struct hid_device*); }; static struct hid_ff_initializer inits[] = { #ifdef CONFIG_LOGITECH_RUMBLE {0x46d, 0xc211, hid_lgff_init}, #endif #ifdef CONFIG_HID_PID {0x45e, 0x001b, hid_pid_init}, #endif {0, 0, NULL} /* Terminating entry */ }; static struct hid_ff_initializer *hid_get_ff_init(__u16 idVendor, __u16 idProduct) { struct hid_ff_initializer *init; for (init = inits; init->idVendor && !(init->idVendor == idVendor && init->idProduct == idProduct); init++); return init->idVendor? init : NULL; } int hid_ff_init(struct hid_device* hid) { struct hid_ff_initializer *init; init = hid_get_ff_init(hid->dev->descriptor.idVendor, hid->dev->descriptor.idProduct); return init? init->init(hid) : -ENOSYS; } --- NEW FILE: hid-input.c --- /* * $Id: hid-input.c,v 1.1 2002/04/16 17:41:51 jsimmons Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik * * USB HID to Linux Input mapping */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/module.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/input.h> #include <linux/usb.h> #include "hid.h" #define unk KEY_UNKNOWN static unsigned char hid_keyboard[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, 84, 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, 85, 89, 90, 91, 92, 93, 94, 95, 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, 115,114,unk,unk,unk,124,unk,181,182,183,184,185,186,187,188,189, 190,191,192,193,194,195,196,197,198,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, 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,unk,unk,unk,unk }; static struct { __s32 x; __s32 y; } hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; static void hidinput_configure_usage(struct hid_device *device, struct hid_field *field, struct hid_usage *usage) { struct input_dev *input = &device->input; int max; int is_abs = 0; unsigned long *bit; switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_KEYBOARD: set_bit(EV_REP, input->evbit); usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; if ((usage->hid & HID_USAGE) < 256) { if (!(usage->code = hid_keyboard[usage->hid & HID_USAGE])) return; clear_bit(usage->code, bit); } else usage->code = KEY_UNKNOWN; break; case HID_UP_BUTTON: usage->code = ((usage->hid - 1) & 0xf) + 0x100; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; switch (field->application) { case HID_GD_GAMEPAD: usage->code += 0x10; case HID_GD_JOYSTICK: usage->code += 0x10; case HID_GD_MOUSE: usage->code += 0x10; break; default: if (field->physical == HID_GD_POINTER) usage->code += 0x10; break; } break; case HID_UP_GENDESK: if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */ switch (usage->hid & 0xf) { case 0x1: usage->code = KEY_POWER; break; case 0x2: usage->code = KEY_SLEEP; break; case 0x3: usage->code = KEY_WAKEUP; break; default: usage->code = KEY_UNKNOWN; break; } usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; break; } usage->code = usage->hid & 0xf; if (field->report_size == 1) { usage->code = BTN_MISC; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; break; } if (field->flags & HID_MAIN_ITEM_RELATIVE) { usage->type = EV_REL; bit = input->relbit; max = REL_MAX; break; } usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; if (usage->hid == HID_GD_HATSWITCH) { usage->code = ABS_HAT0X; usage->hat_min = field->logical_minimum; usage->hat_max = field->logical_maximum; } break; case HID_UP_LED: usage->code = (usage->hid - 1) & 0xf; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; break; case HID_UP_DIGITIZER: switch (usage->hid & 0xff) { case 0x30: /* TipPressure */ if (!test_bit(BTN_TOUCH, input->keybit)) { device->quirks |= HID_QUIRK_NOTOUCH; set_bit(EV_KEY, input->evbit); set_bit(BTN_TOUCH, input->keybit); } usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; usage->code = ABS_PRESSURE; clear_bit(usage->code, bit); break; case 0x32: /* InRange */ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; switch (field->physical & 0xff) { case 0x21: usage->code = BTN_TOOL_MOUSE; break; case 0x22: usage->code = BTN_TOOL_FINGER; break; default: usage->code = BTN_TOOL_PEN; break; } break; case 0x3c: /* Invert */ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; usage->code = BTN_TOOL_RUBBER; clear_bit(usage->code, bit); break; case 0x33: /* Touch */ case 0x42: /* TipSwitch */ case 0x43: /* TipSwitch2 */ device->quirks &= ~HID_QUIRK_NOTOUCH; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; usage->code = BTN_TOUCH; clear_bit(usage->code, bit); break; case 0x44: /* BarrelSwitch */ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; usage->code = BTN_STYLUS; clear_bit(usage->code, bit); break; default: goto unknown; } break; case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */ set_bit(EV_REP, input->evbit); switch (usage->hid & HID_USAGE) { case 0x000: usage->code = 0; break; case 0x034: usage->code = KEY_SLEEP; break; case 0x036: usage->code = BTN_MISC; break; case 0x08a: usage->code = KEY_WWW; break; case 0x095: usage->code = KEY_HELP; break; case 0x0b0: usage->code = KEY_PLAY; break; case 0x0b1: usage->code = KEY_PAUSE; break; case 0x0b2: usage->code = KEY_RECORD; break; case 0x0b3: usage->code = KEY_FASTFORWARD; break; case 0x0b4: usage->code = KEY_REWIND; break; case 0x0b5: usage->code = KEY_NEXTSONG; break; case 0x0b6: usage->code = KEY_PREVIOUSSONG; break; case 0x0b7: usage->code = KEY_STOPCD; break; case 0x0b8: usage->code = KEY_EJECTCD; break; case 0x0cd: usage->code = KEY_PLAYPAUSE; break; case 0x0e0: is_abs = 1; usage->code = ABS_VOLUME; break; case 0x0e2: usage->code = KEY_MUTE; break; case 0x0e5: usage->code = KEY_BASSBOOST; break; case 0x0e9: usage->code = KEY_VOLUMEUP; break; case 0x0ea: usage->code = KEY_VOLUMEDOWN; break; case 0x183: usage->code = KEY_CONFIG; break; case 0x18a: usage->code = KEY_MAIL; break; case 0x192: usage->code = KEY_CALC; break; case 0x194: usage->code = KEY_FILE; break; case 0x21a: usage->code = KEY_UNDO; break; case 0x21b: usage->code = KEY_COPY; break; case 0x21c: usage->code = KEY_CUT; break; case 0x21d: usage->code = KEY_PASTE; break; case 0x221: usage->code = KEY_FIND; break; case 0x223: usage->code = KEY_HOMEPAGE; break; case 0x224: usage->code = KEY_BACK; break; case 0x225: usage->code = KEY_FORWARD; break; case 0x226: usage->code = KEY_STOP; break; case 0x227: usage->code = KEY_REFRESH; break; case 0x22a: usage->code = KEY_BOOKMARKS; break; default: usage->code = KEY_UNKNOWN; break; } if (is_abs) { usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } else { usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } break; case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ set_bit(EV_REP, input->evbit); switch (usage->hid & HID_USAGE) { case 0x021: usage->code = KEY_PRINT; break; case 0x070: usage->code = KEY_HP; break; case 0x071: usage->code = KEY_CAMERA; break; case 0x072: usage->code = KEY_SOUND; break; case 0x073: usage->code = KEY_QUESTION; break; case 0x080: usage->code = KEY_EMAIL; break; case 0x081: usage->code = KEY_CHAT; break; case 0x082: usage->code = KEY_SEARCH; break; case 0x083: usage->code = KEY_CONNECT; break; case 0x084: usage->code = KEY_FINANCE; break; case 0x085: usage->code = KEY_SPORT; break; case 0x086: usage->code = KEY_SHOP; break; default: usage->code = KEY_UNKNOWN; break; } usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; break; case HID_UP_PID: usage->type = EV_FF; bit = input->ffbit; max = FF_MAX; switch(usage->hid & HID_USAGE) { case 0x26: set_bit(FF_CONSTANT, input->ffbit); break; case 0x27: set_bit(FF_RAMP, input->ffbit); break; case 0x28: set_bit(FF_CUSTOM, input->ffbit); break; case 0x30: set_bit(FF_SQUARE, input->ffbit); set_bit(FF_PERIODIC, input->ffbit); break; case 0x31: set_bit(FF_SINE, input->ffbit); set_bit(FF_PERIODIC, input->ffbit); break; case 0x32: set_bit(FF_TRIANGLE, input->ffbit); set_bit(FF_PERIODIC, input->ffbit); break; case 0x33: set_bit(FF_SAW_UP, input->ffbit); set_bit(FF_PERIODIC, input->ffbit); break; case 0x34: set_bit(FF_SAW_DOWN, input->ffbit); set_bit(FF_PERIODIC, input->ffbit); break; case 0x40: set_bit(FF_SPRING, input->ffbit); break; case 0x41: set_bit(FF_DAMPER, input->ffbit); break; case 0x42: set_bit(FF_INERTIA , input->ffbit); break; case 0x43: set_bit(FF_FRICTION, input->ffbit); break; case 0x7e: usage->code = FF_GAIN; break; case 0x83: /* Simultaneous Effects Max */ input->ff_effects_max = (field->value[0]); break; case 0x98: /* Device Control */ usage->code = FF_AUTOCENTER; break; case 0xa4: /* Safety Switch */ usage->code = BTN_DEAD; bit = input->keybit; usage->type = EV_KEY; max = KEY_MAX; break; // case 0x94: /* Effect Playing */ // usage->code = FF_STATUS_PLAYING; // bit = input->ffbit; // max = FF_STATUS_MAX; // break; case 0x9f: /* Device Paused */ case 0xa0: /* Actuators Enabled */ #ifdef DEBUG printk("Not telling the input API about "); resolv_usage(usage->hid); printk("\n"); #endif return; } //AUG goto unknown; //AUG break; default: unknown: if (field->report_size == 1) { if (field->report->type == HID_OUTPUT_REPORT) { usage->code = LED_MISC; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; break; } usage->code = BTN_MISC; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; break; } if (field->flags & HID_MAIN_ITEM_RELATIVE) { usage->code = REL_MISC; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; break; } usage->code = ABS_MISC; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; break; } set_bit(usage->type, input->evbit); while (usage->code <= max && test_and_set_bit(usage->code, bit)) { usage->code = find_next_zero_bit(bit, max + 1, usage->code); } if (usage->code > max) return; if (usage->type == EV_ABS) { int a = field->logical_minimum; int b = field->logical_maximum; input->absmin[usage->code] = a; input->absmax[usage->code] = b; input->absfuzz[usage->code] = (b - a) >> 8; input->absflat[usage->code] = (b - a) >> 4; } if (usage->hat_min != usage->hat_max) { int i; for (i = usage->code; i < usage->code + 2 && i <= max; i++) { input->absmax[i] = 1; input->absmin[i] = -1; input->absfuzz[i] = 0; input->absflat[i] = 0; } set_bit(usage->code + 1, input->absbit); } } void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct input_dev *input = &hid->input; int *quirks = &hid->quirks; if (usage->hat_min != usage->hat_max) { value = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1; if (value < 0 || value > 8) value = 0; input_event(input, usage->type, usage->code , hid_hat_to_axis[value].x); input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[value].y); return; } if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */ *quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT); return; } if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */ if (value) { input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1); return; } input_event(input, usage->type, usage->code, 0); input_event(input, usage->type, BTN_TOOL_RUBBER, 0); return; } if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */ int a = field->logical_minimum; int b = field->logical_maximum; input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3)); } if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */ input->ff_effects_max = value; return; } if (usage->hid == (HID_UP_PID | 0x7fUL)) { printk("PID Pool Report\n"); return; } if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UKNOWN */ return; input_event(input, usage->type, usage->code, value); if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY)) input_event(input, usage->type, usage->code, 0); } static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct hid_device *hid = dev->private; warn("hid input event"); #ifdef CONFIG_HID_FF if (type == EV_FF) { return hid_ff_event(hid, dev, type, code, value); } #else if (0) {} #endif else { struct hid_field *field = NULL; int offset; if ((offset = hid_find_field(hid, type, code, &field)) == -1) { warn("event field not found"); return -1; } hid_set_field(field, offset, value); hid_submit_report(hid, field->report, USB_DIR_OUT); } return 0; } static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = dev->private; return hid_open(hid); } static void hidinput_close(struct input_dev *dev) { struct hid_device *hid = dev->private; hid_close(hid); } /* * Register the input device; print a message. * Configure the input layer interface * Read all reports and initalize the absoulte field values. */ int hidinput_connect(struct hid_device *hid) { struct usb_device *dev = hid->dev; struct hid_report_enum *report_enum; struct hid_report *report; struct list_head *list; int i, j, k; for (i = 0; i < hid->maxapplication; i++) if (IS_INPUT_APPLICATION(hid->application[i])) break; if (i == hid->maxapplication) return -1; hid->input.private = hid; hid->input.event = hidinput_input_event; hid->input.open = hidinput_open; hid->input.close = hidinput_close; hid->input.name = hid->name; hid->input.phys = hid->phys; hid->input.uniq = hid->uniq; hid->input.idbus = BUS_USB; hid->input.idvendor = dev->descriptor.idVendor; hid->input.idproduct = dev->descriptor.idProduct; hid->input.idversion = dev->descriptor.bcdDevice; for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { report_enum = hid->report_enum + k; list = report_enum->report_list.next; while (list != &report_enum->report_list) { report = (struct hid_report *) list; for (i = 0; i < report->maxfield; i++) for (j = 0; j < report->field[i]->maxusage; j++) hidinput_configure_usage(hid, report->field[i], report->field[i]->usage + j); list = list->next; } } input_register_device(&hid->input); return 0; } void hidinput_disconnect(struct hid_device *hid) { input_unregister_device(&hid->input); } --- NEW FILE: hid-lgff.c --- /* * $$ * * Force feedback support for hid-compliant devices from Logitech. * * Copyright (c) 2002 Johann Deneux */ /* * 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 by * e-mail - mail your message to <de...@if...> */ #include <linux/input.h> #include <linux/sched.h> #define DEBUG #include <linux/usb.h> #include <linux/circ_buf.h> #include "hid.h" #define RUN_AT(t) (jiffies + (t)) /* Transmition state */ #define XMIT_RUNNING 0 /* Effect status */ #define EFFECT_STARTED 0 /* Effect is going to play after some time (ff_replay.delay) */ #define EFFECT_PLAYING 1 /* Effect is being played */ #define EFFECT_USED 2 /* Check that the current process can access an effect */ #define CHECK_OWNERSHIP(effect) (current->pid == 0 \ || effect.owner == current->pid) /* **************************************************************************/ /* Implements the protocol used by the Logitech WingMan Cordless rumble pad */ /* **************************************************************************/ #ifdef CONFIG_LOGITECH_RUMBLE #define LGFF_CHECK_OWNERSHIP(i, l) \ (i>=0 && i<LGFF_EFFECTS \ && test_bit(EFFECT_USED, l->effects[i].flags) \ && CHECK_OWNERSHIP(l->effects[i])) #define LGFF_BUFFER_SIZE 64 #define LGFF_EFFECTS 8 struct lgff_magnitudes { unsigned char left; unsigned char right; }; struct lgff_effect { int id; struct hid_ff_logitech* lgff; pid_t owner; unsigned char left; /* Magnitude of vibration for left motor */ unsigned char right; /* Magnitude of vibration for right motor */ struct ff_replay replay; unsigned int count; /* Number of times to play */ struct timer_list timer; unsigned long flags[1]; }; struct hid_ff_logitech { struct hid_device* hid; struct urb* urbffout; /* Output URB used to send ff commands */ struct usb_ctrlrequest ffcr; /* ff commands use control URBs */ char buf[8]; spinlock_t xmit_lock; unsigned int xmit_head, xmit_tail; struct lgff_magnitudes xmit_data[LGFF_BUFFER_SIZE]; long xmit_flags[1]; struct lgff_effect effects[LGFF_EFFECTS]; spinlock_t lock; /* device-level lock. Having locks on a per-effect basis could be nice, but isn't really necessary */ }; static void hid_lgff_ctrl_out(struct urb *urb); static void hid_lgff_exit(struct hid_device* hid); static int hid_lgff_event(struct hid_device *hid, struct input_dev *input, unsigned int type, unsigned int code, int value); static void hid_lgff_make_rumble(struct hid_device* hid); static int hid_lgff_flush(struct input_dev *input, struct file *file); static int hid_lgff_upload_effect(struct input_dev *input, struct ff_effect *effect); static int hid_lgff_erase(struct input_dev *input, int id); static void hid_lgff_ctrl_playback(struct hid_device* hid, struct lgff_effect*, int play); static void hid_lgff_timer(unsigned long timer_data); int hid_lgff_init(struct hid_device* hid) { struct hid_ff_logitech *private; int i; /* Private data */ hid->ff_private = kmalloc(sizeof(struct hid_ff_logitech), GFP_KERNEL); private = hid->ff_private; if (!private) return -1; memset(private, 0, sizeof(struct hid_ff_logitech)); hid->ff_private = private; private->hid = hid; spin_lock_init(&private->lock); spin_lock_init(&private->xmit_lock); private->buf[0] = 0x03; private->buf[1] = 0x42; for (i=0; i<LGFF_EFFECTS; ++i) { struct lgff_effect* effect = &private->effects[i]; struct timer_list* timer = &effect->timer; init_timer(timer); effect->id = i; effect->lgff = private; timer->data = (unsigned long)effect; timer->function = hid_lgff_timer; } /* Event and exit callbacks */ hid->ff_exit = hid_lgff_exit; hid->ff_event = hid_lgff_event; /* USB init */ if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) { kfree(hid->ff_private); return -1; } usb_fill_control_urb(private->urbffout, hid->dev, 0, (void*) &private->ffcr, private->buf, 8, hid_lgff_ctrl_out, hid); dbg("Created ff output control urb"); /* Input init */ hid->input.upload_effect = hid_lgff_upload_effect; hid->input.flush = hid_lgff_flush; set_bit(FF_RUMBLE, hid->input.ffbit); set_bit(EV_FF, hid->input.evbit); hid->input.ff_effects_max = LGFF_EFFECTS; printk(KERN_INFO "Force feedback for Logitech rumble devices by Johann Deneux <de...@if...>\n"); return 0; } static void hid_lgff_exit(struct hid_device* hid) { struct hid_ff_logitech *lgff = hid->ff_private; if (lgff->urbffout) { usb_unlink_urb(lgff->urbffout); usb_free_urb(lgff->urbffout); } } static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, unsigned int type, unsigned int code, int value) { struct hid_ff_logitech *lgff = hid->ff_private; struct lgff_effect *effect = lgff->effects + code; unsigned long flags; if (type != EV_FF) return -EINVAL; if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES; if (value < 0) return -EINVAL; spin_lock_irqsave(&lgff->lock, flags); if (value > 0) { if (test_bit(EFFECT_STARTED, effect->flags)) { spin_unlock_irqrestore(&lgff->lock, flags); return -EBUSY; } if (test_bit(EFFECT_PLAYING, effect->flags)) { spin_unlock_irqrestore(&lgff->lock, flags); return -EBUSY; } effect->count = value; if (effect->replay.delay) { set_bit(EFFECT_STARTED, effect->flags); effect->timer.expires = RUN_AT(effect->replay.delay * HZ / 1000); } else { hid_lgff_ctrl_playback(hid, effect, value); effect->timer.expires = RUN_AT(effect->replay.length * HZ / 1000); } add_timer(&effect->timer); } else { /* value == 0 */ if (test_and_clear_bit(EFFECT_STARTED, effect->flags)) { del_timer(&effect->timer); } else if (test_and_clear_bit(EFFECT_PLAYING, effect->flags)) { del_timer(&effect->timer); hid_lgff_ctrl_playback(hid, effect, value); } if (test_bit(EFFECT_PLAYING, effect->flags)) warn("Effect %d still playing", code); } spin_unlock_irqrestore(&lgff->lock, flags); return 0; } /* Erase all effects this process owns */ static int hid_lgff_flush(struct input_dev *dev, struct file *file) { struct hid_device *hid = dev->private; struct hid_ff_logitech *lgff = hid->ff_private; int i; for (i=0; i<dev->ff_effects_max; ++i) { /*NOTE: no need to lock here. The only times EFFECT_USED is modified is when effects are uploaded or when an effect is erased. But a process cannot close its dev/input/eventX fd and perform ioctls on the same fd all at the same time */ if ( current->pid == lgff->effects[i].owner && test_bit(EFFECT_USED, lgff->effects[i].flags)) { if (hid_lgff_erase(dev, i)) warn("erase effect %d failed", i); } } return 0; } static int hid_lgff_erase(struct input_dev *dev, int id) { struct hid_device *hid = dev->private; struct hid_ff_logitech *lgff = hid->ff_private; unsigned long flags; if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES; spin_lock_irqsave(&lgff->lock, flags); hid_lgff_ctrl_playback(hid, lgff->effects + id, 0); lgff->effects[id].flags[0] = 0; spin_unlock_irqrestore(&lgff->lock, flags); return 0; } static int hid_lgff_upload_effect(struct input_dev* input, struct ff_effect* effect) { struct hid_device *hid = input->private; struct hid_ff_logitech *lgff = hid->ff_private; struct lgff_effect new; int id; unsigned long flags; dbg("ioctl rumble"); if (!test_bit(effect->type, input->ffbit)) return -EINVAL; if (effect->type != FF_RUMBLE) return -EINVAL; spin_lock_irqsave(&lgff->lock, flags); if (effect->id == -1) { int i; for (i=0; i<LGFF_EFFECTS && test_bit(EFFECT_USED, lgff->effects[i].flags); ++i); if (i >= LGFF_EFFECTS) { spin_unlock_irqrestore(&lgff->lock, flags); return -ENOSPC; } effect->id = i; lgff->effects[i].owner = current->pid; lgff->effects[i].flags[0] = 0; set_bit(EFFECT_USED, lgff->effects[i].flags); } else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) { spin_unlock_irqrestore(&lgff->lock, flags); return -EACCES; } id = effect->id; new = lgff->effects[id]; new.right = effect->u.rumble.strong_magnitude >> 9; new.left = effect->u.rumble.weak_magnitude >> 9; new.replay = effect->replay; /* If we updated an effect that was being played, we need to remake the rumble effect */ if (test_bit(EFFECT_STARTED, lgff->effects[id].flags) || test_bit(EFFECT_STARTED, lgff->effects[id].flags)) { /* Changing replay parameters is not allowed (for the time being) */ if (new.replay.delay != lgff->effects[id].replay.delay || new.replay.length != lgff->effects[id].replay.length) { spin_unlock_irqrestore(&lgff->lock, flags); return -ENOSYS; } lgff->effects[id] = new; hid_lgff_make_rumble(hid); } else { lgff->effects[id] = new; } spin_unlock_irqrestore(&lgff->lock, flags); return 0; } static void hid_lgff_xmit(struct hid_device* hid) { struct hid_ff_logitech *lgff = hid->ff_private; int err; int tail; unsigned long flags; spin_lock_irqsave(&lgff->xmit_lock, flags); tail = lgff->xmit_tail; if (lgff->xmit_head == tail) { clear_bit(XMIT_RUNNING, lgff->xmit_flags); spin_unlock_irqrestore(&lgff->xmit_lock, flags); return; } lgff->buf[3] = lgff->xmit_data[tail].left; lgff->buf[4] = lgff->xmit_data[tail].right; tail++; tail &= LGFF_BUFFER_SIZE -1; lgff->xmit_tail = tail; spin_unlock_irqrestore(&lgff->xmit_lock, flags); lgff->urbffout->pipe = usb_sndctrlpipe(hid->dev, 0); lgff->ffcr.bRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE; lgff->urbffout->transfer_buffer_length = lgff->ffcr.wLength = 8; lgff->ffcr.bRequest = 9; lgff->ffcr.wValue = 0x0203; /*NOTE: Potential problem with little/big endian */ lgff->ffcr.wIndex = 0; lgff->urbffout->dev = hid->dev; if ((err=usb_submit_urb(lgff->urbffout, GFP_ATOMIC))) warn("usb_submit_urb returned %d", err); } static void hid_lgff_make_rumble(struct hid_device* hid) { struct hid_ff_logitech *lgff = hid->ff_private; int left = 0, right = 0; int i; int head, tail; unsigned long flags; for (i=0; i<LGFF_EFFECTS; ++i) { if (test_bit(EFFECT_USED, lgff->effects[i].flags) && test_bit(EFFECT_PLAYING, lgff->effects[i].flags)) { left += lgff->effects[i].left; right += lgff->effects[i].right; } } spin_lock_irqsave(&lgff->xmit_lock, flags); head = lgff->xmit_head; tail = lgff->xmit_tail; if (CIRC_SPACE(head, tail, LGFF_BUFFER_SIZE) < 1) { warn("not enough space in xmit buffer to send new packet"); spin_unlock_irqrestore(&lgff->xmit_lock, flags); return; } lgff->xmit_data[head].left = left > 0x7f ? 0x7f : left; lgff->xmit_data[head].right = right > 0x7f ? 0x7f : right; head++; head &= LGFF_BUFFER_SIZE -1; lgff->xmit_head = head; if (test_and_set_bit(XMIT_RUNNING, lgff->xmit_flags)) spin_unlock_irqrestore(&lgff->xmit_lock, flags); else { spin_unlock_irqrestore(&lgff->xmit_lock, flags); hid_lgff_xmit(hid); } } static void hid_lgff_ctrl_out(struct urb *urb) { struct hid_device *hid = urb->context; if (urb->status) warn("hid_irq_ffout status %d received", urb->status); hid_lgff_xmit(hid); } /* Lock must be held by caller */ static void hid_lgff_ctrl_playback(struct hid_device *hid, struct lgff_effect *effect, int play) { if (play) { set_bit(EFFECT_PLAYING, effect->flags); hid_lgff_make_rumble(hid); } else { clear_bit(EFFECT_PLAYING, effect->flags); hid_lgff_make_rumble(hid); } } static void hid_lgff_timer(unsigned long timer_data) { struct lgff_effect *effect = (struct lgff_effect*) timer_data; struct hid_ff_logitech* lgff = effect->lgff; int id = effect->id; unsigned long flags; dbg("in hid_lgff_timer"); if (id < 0 || id >= LGFF_EFFECTS) { warn("Bad effect id %d", id); return; } effect = lgff->effects + id; spin_lock_irqsave(&lgff->lock, flags); if (!test_bit(EFFECT_USED, effect->flags)) { warn("Unused effect id %d", id); } else if (test_bit(EFFECT_STARTED, effect->flags)) { clear_bit(EFFECT_STARTED, effect->flags); set_bit(EFFECT_PLAYING, effect->flags); hid_lgff_ctrl_playback(lgff->hid, effect, 1); effect->timer.expires = RUN_AT(effect->replay.length * HZ / 1000); add_timer(&effect->timer); dbg("Effect %d starts playing", id); } else if (test_bit(EFFECT_PLAYING, effect->flags)) { clear_bit(EFFECT_PLAYING, effect->flags); hid_lgff_ctrl_playback(lgff->hid, effect, 0); if (--effect->count > 0) { /*TODO: check that replay.delay is non-null */ set_bit(EFFECT_STARTED, effect->flags); effect->timer.expires = RUN_AT(effect->replay.delay * HZ / 1000); add_timer(&effect->timer); dbg("Effect %d restarted", id); } else { dbg("Effect %d stopped", id); } } else { warn("Effect %d is not started nor playing", id); } spin_unlock_irqrestore(&lgff->lock, flags); } #endif /* CONFIG_LOGITECH_RUMBLE */ --- NEW FILE: hid.h --- #ifndef __HID_H #define __HID_H /* * $Id: hid.h,v 1.1 2002/04/16 17:41:51 jsimmons Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2001 Vojtech Pavlik */ /* * 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 <vo...@uc...>, or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ #include <linux/types.h> #include <linux/slab.h> #include <linux/list.h> /* * USB HID (Human Interface Device) interface class code */ #define USB_INTERFACE_CLASS_HID 3 /* * HID class requests */ #define HID_REQ_GET_REPORT 0x01 #define HID_REQ_GET_IDLE 0x02 #define HID_REQ_GET_PROTOCOL 0x03 #define HID_REQ_SET_REPORT 0x09 #define HID_REQ_SET_IDLE 0x0A #define HID_REQ_SET_PROTOCOL 0x0B /* * HID class descriptor types */ #define HID_DT_HID (USB_TYPE_CLASS | 0x01) #define HID_DT_REPORT (USB_TYPE_CLASS | 0x02) #define HID_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) /* * We parse each description item into this structure. Short items data * values are expanded to 32-bit signed int, long items contain a pointer * into the data area. */ struct hid_item { unsigned format; __u8 size; __u8 type; __u8 tag; union { __u8 u8; __s8 s8; __u16 u16; __s16 s16; __u32 u32; __s32 s32; __u8 *longdata; } data; }; /* * HID report item format */ #define HID_ITEM_FORMAT_SHORT 0 #define HID_ITEM_FORMAT_LONG 1 /* * Special tag indicating long items */ #define HID_ITEM_TAG_LONG 15 /* * HID report descriptor item type (prefix bit 2,3) */ #define HID_ITEM_TYPE_MAIN 0 #define HID_ITEM_TYPE_GLOBAL 1 #define HID_ITEM_TYPE_LOCAL 2 #define HID_ITEM_TYPE_RESERVED 3 /* * HID report descriptor main item tags */ #define HID_MAIN_ITEM_TAG_INPUT 8 #define HID_MAIN_ITEM_TAG_OUTPUT 9 #define HID_MAIN_ITEM_TAG_FEATURE 11 #define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10 #define HID_MAIN_ITEM_TAG_END_COLLECTION 12 /* * HID report descriptor main item contents */ #define HID_MAIN_ITEM_CONSTANT 0x001 #define HID_MAIN_ITEM_VARIABLE 0x002 #define HID_MAIN_ITEM_RELATIVE 0x004 #define HID_MAIN_ITEM_WRAP 0x008 #define HID_MAIN_ITEM_NONLINEAR 0x010 #define HID_MAIN_ITEM_NO_PREFERRED 0x020 #define HID_MAIN_ITEM_NULL_STATE 0x040 #define HID_MAIN_ITEM_VOLATILE 0x080 #define HID_MAIN_ITEM_BUFFERED_BYTE 0x100 /* * HID report descriptor collection item types */ #define HID_COLLECTION_PHYSICAL 0 #define HID_COLLECTION_APPLICATION 1 #define HID_COLLECTION_LOGICAL 2 /* * HID report descriptor global item tags */ #define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0 #define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1 #define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2 #define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3 #define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4 #define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5 #define HID_GLOBAL_ITEM_TAG_UNIT 6 #define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7 #define HID_GLOBAL_ITEM_TAG_REPORT_ID 8 #define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9 #define HID_GLOBAL_ITEM_TAG_PUSH 10 #define HID_GLOBAL_ITEM_TAG_POP 11 /* * HID report descriptor local item tags */ #define HID_LOCAL_ITEM_TAG_USAGE 0 #define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1 #define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2 #define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3 #define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4 #define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5 #define HID_LOCAL_ITEM_TAG_STRING_INDEX 7 #define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8 #define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9 #define HID_LOCAL_ITEM_TAG_DELIMITER 10 /* * HID usage tables */ #define HID_USAGE_PAGE 0xffff0000 #define HID_UP_GENDESK 0x00010000 #define HID_UP_KEYBOARD 0x00070000 #define HID_UP_LED 0x00080000 #define HID_UP_BUTTON 0x00090000 #define HID_UP_ORDINAL 0x000a0000 #define HID_UP_CONSUMER 0x000c0000 #define HID_UP_DIGITIZER 0x000d0000 #define HID_UP_PID 0x000f0000 #define HID_UP_HPVENDOR 0xff7f0000 #define HID_USAGE 0x0000ffff #define HID_GD_POINTER 0x00010001 #define HID_GD_MOUSE 0x00010002 #define HID_GD_JOYSTICK 0x00010004 #define HID_GD_GAMEPAD 0x00010005 #define HID_GD_HATSWITCH 0x00010039 /* * HID report types --- Ouch! HID spec says 1 2 3! */ #define HID_INPUT_REPORT 0 #define HID_OUTPUT_REPORT 1 #define HID_FEATURE_REPORT 2 /* * HID device quirks. */ #define HID_QUIRK_INVERT 0x01 #define HID_QUIRK_NOTOUCH 0x02 #define HID_QUIRK_IGNORE 0x04 #define HID_QUIRK_NOGET 0x08 /* * This is the global enviroment of the parser. This information is * persistent for main-items. The global enviroment can be saved and * restored with PUSH/POP statements. */ struct hid_global { unsigned usage_page; __s32 logical_minimum; __s32 logical_maximum; __s32 physical_minimum; __s32 physical_maximum; __s32 unit_exponent; unsigned unit; unsigned report_id; unsigned report_size; unsigned report_count; }; /* * This is the local enviroment. It is resistent up the next main-item. */ #define HID_MAX_DESCRIPTOR_SIZE 4096 #define HID_MAX_USAGES 1024 #define HID_MAX_APPLICATIONS 16 struct hid_local { unsigned usage[HID_MAX_USAGES]; /* usage array */ unsigned usage_index; unsigned usage_minimum; unsigned delimiter_depth; unsigned delimiter_branch; }; /* * This is the collection stack. We climb up the stack to determine * application and function of each field. */ struct hid_collection { unsigned type; unsigned usage; }; struct hid_usage { unsigned hid; /* hid usage code */ __u16 code; /* input driver code */ __u8 type; /* input driver type */ __s8 hat_min; /* hat switch fun */ __s8 hat_max; /* ditto */ }; struct hid_field { unsigned physical; /* physical usage for this field */ unsigned logical; /* logical usage for this field */ unsigned application; /* application usage for this field */ struct hid_usage *usage; /* usage table for this function */ unsigned maxusage; /* maximum usage index */ unsigned flags; /* main-item flags (i.e. volatile,array,constant) */ unsigned report_offset; /* bit offset in the report */ unsigned report_size; /* size of this field in the report */ unsigned report_count; /* number of this field in the report */ unsigned report_type; /* (input,output,feature) */ __s32 *value; /* last known value(s) */ __s32 logical_minimum; __s32 logical_maximum; __s32 physical_minimum; __s32 physical_maximum; __s32 unit_exponent; unsigned unit; struct hid_report *report; /* associated report */ unsigned index; /* index into report->field[] */ }; #define HID_MAX_FIELDS 64 struct hid_report { struct list_head list; unsigned id; /* id of this report */ unsigned type; /* report type */ struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */ unsigned maxfield; /* maximum valid field index */ unsigned size; /* size of the report (bits) */ struct hid_device *device; /* associated device */ }; struct hid_report_enum { unsigned numbered; struct list_head report_list; struct hid_report *report_id_hash[256]; }; #define HID_REPORT_TYPES 3 #define HID_BUFFER_SIZE 32 #define HID_CONTROL_FIFO_SIZE 64 #define HID_OUTPUT_FIFO_SIZE 64 struct hid_control_fifo { unsigned char dir; struct hid_report *report; }; #define HID_CLAIMED_INPUT 1 #define HID_CLAIMED_HIDDEV 2 #define HID_CTRL_RUNNING 1 #define H... [truncated message content] |