Commit 45090330 authored by Vojtech Pavlik's avatar Vojtech Pavlik Committed by Greg Kroah-Hartman

[PATCH] Big HID update

This cset is update of the HID drivers to the latest version, as a part
of the Input merge. It finally includes ForceFeedback support by Johann
Deneux, enabling ForceFeedback on new Logitech and Microsoft devices.
parent 774921a2
...@@ -23,6 +23,23 @@ CONFIG_USB_HIDINPUT ...@@ -23,6 +23,23 @@ CONFIG_USB_HIDINPUT
If unsure, say Y. 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 CONFIG_USB_HIDDEV
Say Y here if you want to support HID devices (from the USB Say Y here if you want to support HID devices (from the USB
specification standpoint) that aren't strictly user interface specification standpoint) that aren't strictly user interface
...@@ -83,3 +100,17 @@ CONFIG_USB_WACOM ...@@ -83,3 +100,17 @@ CONFIG_USB_WACOM
inserted in and removed from the running kernel whenever you want). 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 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>. module, say M here and read <file:Documentation/modules.txt>.
CONFIG_USB_POWERMATE
Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
These are stainless steel dials 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>.
...@@ -3,10 +3,16 @@ ...@@ -3,10 +3,16 @@
# #
comment 'USB Human Interface Devices (HID)' comment 'USB Human Interface Devices (HID)'
dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB
if [ "$CONFIG_INPUT" = "n" ]; then if [ "$CONFIG_INPUT" = "n" ]; then
comment ' Input core support is needed for USB HID input layer or HIDBP support' comment ' Input core support is needed for USB HID input layer or HIDBP support'
fi fi
dep_mbool ' HID input layer support' CONFIG_USB_HIDINPUT $CONFIG_INPUT $CONFIG_USB_HID dep_mbool ' HID input layer support' CONFIG_USB_HIDINPUT $CONFIG_INPUT $CONFIG_USB_HID
dep_mbool ' Force feedback support' CONFIG_HID_FF $CONFIG_USB_HIDINPUT
dep_mbool ' PID Devices' CONFIG_HID_PID $CONFIG_USB_HID $CONFIG_HID_FF
dep_mbool ' Logitech RumblePad support' CONFIG_LOGITECH_RUMBLE $CONFIG_USB_HID $CONFIG_HID_FF
dep_mbool ' Logitech WingMan Force 3D support' CONFIG_LOGITECH_3D $CONFIG_USB_HID $CONFIG_HID_FF
dep_mbool ' /dev/hiddev raw HID device support' CONFIG_USB_HIDDEV $CONFIG_USB_HID dep_mbool ' /dev/hiddev raw HID device support' CONFIG_USB_HIDDEV $CONFIG_USB_HID
if [ "$CONFIG_USB_HID" != "y" ]; then if [ "$CONFIG_USB_HID" != "y" ]; then
...@@ -16,4 +22,5 @@ fi ...@@ -16,4 +22,5 @@ fi
dep_tristate ' Aiptek 6000U/8000U tablet support' CONFIG_USB_AIPTEK $CONFIG_USB $CONFIG_INPUT dep_tristate ' Aiptek 6000U/8000U tablet support' CONFIG_USB_AIPTEK $CONFIG_USB $CONFIG_INPUT
dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT
dep_tristate ' Griffin PowerMate and Contour Jog support' CONFIG_USB_POWERMATE $CONFIG_USB $CONFIG_INPUT
...@@ -12,12 +12,27 @@ endif ...@@ -12,12 +12,27 @@ endif
ifeq ($(CONFIG_USB_HIDINPUT),y) ifeq ($(CONFIG_USB_HIDINPUT),y)
hid-objs += hid-input.o hid-objs += hid-input.o
endif endif
ifeq ($(CONFIG_HID_PID),y)
hid-objs += pid.o
endif
ifeq ($(CONFIG_LOGITECH_RUMBLE),y)
hid-objs += hid-lgff.o
endif
ifeq ($(CONFIG_LOGITECH_3D),y)
hid-objs += hid-lg3dff.o
endif
ifeq ($(CONFIG_HID_FF),y)
hid-objs += hid-ff.o
endif
obj-$(CONFIG_USB_AIPTEK) += aiptek.o obj-$(CONFIG_USB_AIPTEK) += aiptek.o
obj-$(CONFIG_USB_HID) += hid.o obj-$(CONFIG_USB_HID) += hid.o
obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_KBD) += usbkbd.o
obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_MOUSE) += usbmouse.o
obj-$(CONFIG_USB_WACOM) += wacom.o obj-$(CONFIG_USB_WACOM) += wacom.o
obj-$(CONFIG_USB_POWERMATE) += powermate.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
#ifndef _FIXP_ARITH_H
#define _FIXP_ARITH_H
/*
* $$
*
* Simplistic fixed-point arithmetics.
* Hmm, I'm probably duplicating some code :(
*
* 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 <deneux@ifrance.com>
*/
#include <linux/types.h>
// The type representing fixed-point values
typedef s16 fixp_t;
#define FRAC_N 8
#define FRAC_MASK ((1<<FRAC_N)-1)
// Not to be used directly. Use fixp_{cos,sin}
fixp_t cos_table[45] = {
0x0100, 0x00FF, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FA, 0x00F8,
0x00F6, 0x00F3, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DD,
0x00D9, 0x00D4, 0x00CF, 0x00C9, 0x00C4, 0x00BE, 0x00B8, 0x00B1,
0x00AB, 0x00A4, 0x009D, 0x0096, 0x008F, 0x0087, 0x0080, 0x0078,
0x0070, 0x0068, 0x005F, 0x0057, 0x004F, 0x0046, 0x003D, 0x0035,
0x002C, 0x0023, 0x001A, 0x0011, 0x0008
};
/* a: 123 -> 123.0 */
inline fixp_t fixp_new(s16 a)
{
return a<<FRAC_N;
}
/* a: 0xFFFF -> -1.0
0x8000 -> 1.0
0x0000 -> 0.0
*/
inline fixp_t fixp_new16(s16 a)
{
return ((s32)a)>>(16-FRAC_N);
}
inline fixp_t fixp_cos(unsigned int degrees)
{
int quadrant = (degrees / 90) & 3;
unsigned int i = (degrees % 90) >> 1;
return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i];
}
inline fixp_t fixp_sin(unsigned int degrees)
{
return -fixp_cos(degrees + 90);
}
inline fixp_t fixp_mult(fixp_t a, fixp_t b)
{
return ((s32)(a*b))>>FRAC_N;
}
#endif
/* /*
* $Id: hid-core.c,v 1.42 2002/01/27 00:22:46 vojtech Exp $ * $Id: hid-core.c,v 1.6 2002/06/09 17:34:55 jdeneux Exp $
* *
* Copyright (c) 1999 Andreas Gal * Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2000-2001 Vojtech Pavlik
...@@ -108,11 +108,10 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned ...@@ -108,11 +108,10 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage) memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ values * sizeof(unsigned)); + values * sizeof(unsigned));
report->field[report->maxfield] = field; report->field[report->maxfield++] = field;
field->usage = (struct hid_usage *)(field + 1); field->usage = (struct hid_usage *)(field + 1);
field->value = (unsigned *)(field->usage + usages); field->value = (unsigned *)(field->usage + usages);
field->report = report; field->report = report;
field->index = report->maxfield++;
return field; return field;
} }
...@@ -518,6 +517,8 @@ static void hid_free_device(struct hid_device *device) ...@@ -518,6 +517,8 @@ static void hid_free_device(struct hid_device *device)
{ {
unsigned i,j; unsigned i,j;
hid_ff_exit(device);
for (i = 0; i < HID_REPORT_TYPES; i++) { for (i = 0; i < HID_REPORT_TYPES; i++) {
struct hid_report_enum *report_enum = device->report_enum + i; struct hid_report_enum *report_enum = device->report_enum + i;
...@@ -1171,8 +1172,8 @@ int hid_wait_io(struct hid_device *hid) ...@@ -1171,8 +1172,8 @@ int hid_wait_io(struct hid_device *hid)
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&hid->wait, &wait); add_wait_queue(&hid->wait, &wait);
while (timeout && test_bit(HID_CTRL_RUNNING, &hid->iofl) && while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) ||
test_bit(HID_OUT_RUNNING, &hid->iofl)) test_bit(HID_OUT_RUNNING, &hid->iofl)))
timeout = schedule_timeout(timeout); timeout = schedule_timeout(timeout);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
...@@ -1223,6 +1224,7 @@ void hid_init_reports(struct hid_device *hid) ...@@ -1223,6 +1224,7 @@ void hid_init_reports(struct hid_device *hid)
struct hid_report *report; struct hid_report *report;
struct list_head *list; struct list_head *list;
int len; int len;
int err, ret;
report_enum = hid->report_enum + HID_INPUT_REPORT; report_enum = hid->report_enum + HID_INPUT_REPORT;
list = report_enum->report_list.next; list = report_enum->report_list.next;
...@@ -1240,7 +1242,16 @@ void hid_init_reports(struct hid_device *hid) ...@@ -1240,7 +1242,16 @@ void hid_init_reports(struct hid_device *hid)
list = list->next; list = list->next;
} }
if (hid_wait_io(hid)) { err = 0;
while ((ret = hid_wait_io(hid))) {
err |= ret;
if (test_bit(HID_CTRL_RUNNING, &hid->iofl))
usb_unlink_urb(hid->urbctrl);
if (test_bit(HID_OUT_RUNNING, &hid->iofl))
usb_unlink_urb(hid->urbout);
}
if (err) {
warn("timeout initializing reports\n"); warn("timeout initializing reports\n");
return; return;
} }
...@@ -1299,7 +1310,7 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) ...@@ -1299,7 +1310,7 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
struct hid_descriptor *hdesc; struct hid_descriptor *hdesc;
struct hid_device *hid; struct hid_device *hid;
unsigned quirks = 0, rsize = 0; unsigned quirks = 0, rsize = 0;
char *buf; char *buf, *rdesc;
int n; int n;
for (n = 0; hid_blacklist[n].idVendor; n++) for (n = 0; hid_blacklist[n].idVendor; n++)
...@@ -1325,11 +1336,14 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) ...@@ -1325,11 +1336,14 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
return NULL; return NULL;
} }
{ if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
__u8 rdesc[rsize]; dbg("couldn't allocate rdesc memory");
return NULL;
}
if ((n = hid_get_class_descriptor(dev, interface->bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { if ((n = hid_get_class_descriptor(dev, interface->bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
dbg("reading report descriptor failed"); dbg("reading report descriptor failed");
kfree(rdesc);
return NULL; return NULL;
} }
...@@ -1342,10 +1356,11 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) ...@@ -1342,10 +1356,11 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
if (!(hid = hid_parse_report(rdesc, rsize))) { if (!(hid = hid_parse_report(rdesc, rsize))) {
dbg("parsing report descriptor failed"); dbg("parsing report descriptor failed");
kfree(rdesc);
return NULL; return NULL;
} }
}
kfree(rdesc);
hid->quirks = quirks; hid->quirks = quirks;
for (n = 0; n < interface->bNumEndpoints; n++) { for (n = 0; n < interface->bNumEndpoints; n++) {
...@@ -1439,6 +1454,8 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -1439,6 +1454,8 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
hid_init_reports(hid); hid_init_reports(hid);
hid_dump_device(hid); hid_dump_device(hid);
hid_ff_init(hid);
if (!hidinput_connect(hid)) if (!hidinput_connect(hid))
hid->claimed |= HID_CLAIMED_INPUT; hid->claimed |= HID_CLAIMED_INPUT;
if (!hiddev_connect(hid)) if (!hiddev_connect(hid))
...@@ -1477,20 +1494,20 @@ static void hid_disconnect(struct usb_device *dev, void *ptr) ...@@ -1477,20 +1494,20 @@ static void hid_disconnect(struct usb_device *dev, void *ptr)
{ {
struct hid_device *hid = ptr; struct hid_device *hid = ptr;
dbg("cleanup called");
usb_unlink_urb(hid->urbin); usb_unlink_urb(hid->urbin);
usb_unlink_urb(hid->urbout); usb_unlink_urb(hid->urbout);
usb_unlink_urb(hid->urbctrl); usb_unlink_urb(hid->urbctrl);
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hid);
if (hid->claimed & HID_CLAIMED_HIDDEV)
hiddev_disconnect(hid);
usb_free_urb(hid->urbin); usb_free_urb(hid->urbin);
usb_free_urb(hid->urbctrl); usb_free_urb(hid->urbctrl);
if (hid->urbout) if (hid->urbout)
usb_free_urb(hid->urbout); usb_free_urb(hid->urbout);
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hid);
if (hid->claimed & HID_CLAIMED_HIDDEV)
hiddev_disconnect(hid);
hid_free_device(hid); hid_free_device(hid);
} }
......
/*
* $Id: hid-ff.c,v 1.3 2002/06/09 11:06:38 jdeneux 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 <deneux@ifrance.com>
*/
#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_lg3d_init(struct hid_device* hid);
extern int hid_pid_init(struct hid_device* hid);
/*
* This table contains pointers to initializers. To add support for new
* devices, you need to add the USB vendor and product ids here.
*/
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_LOGITECH_3D
{0x46d, 0xc283, hid_lg3d_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);
if (!init) {
warn("hid_ff_init could not find initializer");
return -ENOSYS;
}
return init->init(hid);
}
/* /*
* $Id: hid-input.c,v 1.18 2001/11/07 09:01:18 vojtech Exp $ * $Id: hid-input.c,v 1.2 2002/04/23 00:59:25 rdamazio Exp $
* *
* Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2000-2001 Vojtech Pavlik
* *
...@@ -274,8 +274,52 @@ static void hidinput_configure_usage(struct hid_device *device, struct hid_field ...@@ -274,8 +274,52 @@ static void hidinput_configure_usage(struct hid_device *device, struct hid_field
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
break; 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]);
dbg("Maximum Effects - %d",input->ff_effects_max);
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;
dbg("Safety Switch Report\n");
break;
case 0x9f: /* Device Paused */
case 0xa0: /* Actuators Enabled */
dbg("Not telling the input API about ");
resolv_usage(usage->hid);
return;
}
break;
default: default:
unknown: unknown:
resolv_usage(usage->hid);
if (field->report_size == 1) { if (field->report_size == 1) {
...@@ -365,6 +409,16 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct ...@@ -365,6 +409,16 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3)); 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;
dbg("Maximum Effects - %d",input->ff_effects_max);
return;
}
if (usage->hid == (HID_UP_PID | 0x7fUL)) {
dbg("PID Pool Report\n");
return;
}
if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UKNOWN */ if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UKNOWN */
return; return;
...@@ -380,6 +434,9 @@ static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsign ...@@ -380,6 +434,9 @@ static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsign
struct hid_field *field = NULL; struct hid_field *field = NULL;
int offset; int offset;
if (type == EV_FF)
return hid_ff_event(hid, dev, type, code, value);
if ((offset = hid_find_field(hid, type, code, &field)) == -1) { if ((offset = hid_find_field(hid, type, code, &field)) == -1) {
warn("event field not found"); warn("event field not found");
return -1; return -1;
......
This diff is collapsed.
This diff is collapsed.
...@@ -359,6 +359,11 @@ struct hid_device { /* device report descriptor */ ...@@ -359,6 +359,11 @@ struct hid_device { /* device report descriptor */
char name[128]; /* Device name */ char name[128]; /* Device name */
char phys[64]; /* Device physical location */ char phys[64]; /* Device physical location */
char uniq[64]; /* Device unique identifier (serial #) */ char uniq[64]; /* Device unique identifier (serial #) */
void *ff_private; /* Private data for the force-feedback driver */
void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */
int (*ff_event)(struct hid_device *hid, struct input_dev *input,
unsigned int type, unsigned int code, int value);
}; };
#define HID_GLOBAL_STACK_SIZE 4 #define HID_GLOBAL_STACK_SIZE 4
...@@ -395,6 +400,7 @@ struct hid_descriptor { ...@@ -395,6 +400,7 @@ struct hid_descriptor {
#define hid_dump_input(a,b) do { } while (0) #define hid_dump_input(a,b) do { } while (0)
#define hid_dump_device(c) do { } while (0) #define hid_dump_device(c) do { } while (0)
#define hid_dump_field(a,b) do { } while (0) #define hid_dump_field(a,b) do { } while (0)
#define resolv_usage(a) do { } while (0)
#endif #endif
#endif #endif
...@@ -419,3 +425,23 @@ int hid_find_field(struct hid_device *, unsigned int, unsigned int, struct hid_f ...@@ -419,3 +425,23 @@ int hid_find_field(struct hid_device *, unsigned int, unsigned int, struct hid_f
int hid_set_field(struct hid_field *, unsigned, __s32); int hid_set_field(struct hid_field *, unsigned, __s32);
void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir); void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir);
void hid_init_reports(struct hid_device *hid); void hid_init_reports(struct hid_device *hid);
int hid_find_report_by_usage(struct hid_device *hid, __u32 wanted_usage, struct hid_report **report, int type);
#ifdef CONFIG_HID_FF
int hid_ff_init(struct hid_device *hid);
#else
static inline int hid_ff_init(struct hid_device *hid) { return -1; }
#endif
static inline void hid_ff_exit(struct hid_device *hid)
{
if (hid->ff_exit)
hid->ff_exit(hid);
}
static inline int hid_ff_event(struct hid_device *hid, struct input_dev *input,
unsigned int type, unsigned int code, int value)
{
if (hid->ff_event)
return hid->ff_event(hid, input, type, code, value);
return -ENOSYS;
}
...@@ -389,9 +389,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -389,9 +389,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
dinfo.product = dev->descriptor.idProduct; dinfo.product = dev->descriptor.idProduct;
dinfo.version = dev->descriptor.bcdDevice; dinfo.version = dev->descriptor.bcdDevice;
dinfo.num_applications = hid->maxapplication; dinfo.num_applications = hid->maxapplication;
if (copy_to_user((void *) arg, &dinfo, sizeof(dinfo))) return copy_to_user((void *) arg, &dinfo, sizeof(dinfo));
return -EFAULT;
return 0;
} }
case HIDIOCGFLAG: case HIDIOCGFLAG:
...@@ -482,9 +480,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -482,9 +480,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
rinfo.num_fields = report->maxfield; rinfo.num_fields = report->maxfield;
if (copy_to_user((void *) arg, &rinfo, sizeof(rinfo))) return copy_to_user((void *) arg, &rinfo, sizeof(rinfo));
return -EFAULT;
return 0;
case HIDIOCGFIELDINFO: case HIDIOCGFIELDINFO:
{ {
...@@ -516,9 +512,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -516,9 +512,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
finfo.unit_exponent = field->unit_exponent; finfo.unit_exponent = field->unit_exponent;
finfo.unit = field->unit; finfo.unit = field->unit;
if (copy_to_user((void *) arg, &finfo, sizeof(finfo))) return copy_to_user((void *) arg, &finfo, sizeof(finfo));
return -EFAULT;
return 0;
} }
case HIDIOCGUCODE: case HIDIOCGUCODE:
...@@ -539,19 +533,12 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -539,19 +533,12 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
uref.usage_code = field->usage[uref.usage_index].hid; uref.usage_code = field->usage[uref.usage_index].hid;
if (copy_to_user((void *) arg, &uref, sizeof(uref))) return copy_to_user((void *) arg, &uref, sizeof(uref));
return -EFAULT;
return 0;
case HIDIOCGUSAGE: case HIDIOCGUSAGE:
case HIDIOCSUSAGE:
if (copy_from_user(&uref, (void *) arg, sizeof(uref))) if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
return -EFAULT; return -EFAULT;
if (cmd == HIDIOCSUSAGE &&
uref.report_type != HID_REPORT_TYPE_OUTPUT)
return -EINVAL;
if (uref.report_id == HID_REPORT_ID_UNKNOWN) { if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
field = hiddev_lookup_usage(hid, &uref); field = hiddev_lookup_usage(hid, &uref);
if (field == NULL) if (field == NULL)
...@@ -570,15 +557,37 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, ...@@ -570,15 +557,37 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
return -EINVAL; return -EINVAL;
} }
if (cmd == HIDIOCGUSAGE) {
uref.value = field->value[uref.usage_index]; uref.value = field->value[uref.usage_index];
if (copy_to_user((void *) arg, &uref, sizeof(uref)))
return copy_to_user((void *) arg, &uref, sizeof(uref));
case HIDIOCSUSAGE:
if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
return -EFAULT; return -EFAULT;
return 0;
if (uref.report_type == HID_REPORT_TYPE_INPUT)
return -EINVAL;
if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
field = hiddev_lookup_usage(hid, &uref);
if (field == NULL)
return -EINVAL;
} else { } else {
field->value[uref.usage_index] = uref.value; rinfo.report_type = uref.report_type;
rinfo.report_id = uref.report_id;
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
if (uref.field_index >= report->maxfield)
return -EINVAL;
field = report->field[uref.field_index];
if (uref.usage_index >= field->maxusage)
return -EINVAL;
} }
field->value[uref.usage_index] = uref.value;
return 0; return 0;
default: default:
...@@ -626,9 +635,9 @@ int hiddev_connect(struct hid_device *hid) ...@@ -626,9 +635,9 @@ int hiddev_connect(struct hid_device *hid)
if (i == hid->maxapplication) if (i == hid->maxapplication)
return -1; return -1;
retval = usb_register_dev (&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor); retval = usb_register_dev(&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor);
if (retval) { if (retval) {
err ("Not able to get a minor for this device."); err("Not able to get a minor for this device.");
return -1; return -1;
} }
......
/*
* PID Force feedback support for hid devices.
*
* Copyright (c) 2002 Rodrigo Damazio.
* Portions by Johann Deneux and Bjorn Augustson
*/
/*
* 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 <rdamazio@lsi.usp.br>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/input.h>
#include <linux/usb.h>
#include "hid.h"
#include "pid.h"
#define DEBUG
MODULE_AUTHOR("Rodrigo Damazio <rdamazio@lsi.usp.br>");
MODULE_DESCRIPTION("USB PID(Physical Interface Device) Driver");
MODULE_LICENSE("GPL");
#define CHECK_OWNERSHIP(i, hid_pid) \
((i) < FF_EFFECTS_MAX && i >= 0 && \
test_bit(FF_PID_FLAGS_USED, &hid_pid->effects[(i)].flags) && \
(current->pid == 0 || \
(hid_pid)->effects[(i)].owner == current->pid))
/* Called when a transfer is completed */
static void hid_pid_ctrl_out(struct urb *u)
{
#ifdef DEBUG
printk("hid_pid_ctrl_out - Transfer Completed\n");
#endif
}
static void hid_pid_exit(struct hid_device* hid)
{
struct hid_ff_pid *private = hid->ff_private;
if (private->urbffout) {
usb_unlink_urb(private->urbffout);
usb_free_urb(private->urbffout);
}
}
static int pid_upload_periodic(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update) {
printk("Requested periodic force upload\n");
return 0;
}
static int pid_upload_constant(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update) {
printk("Requested constant force upload\n");
return 0;
}
static int pid_upload_condition(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update) {
printk("Requested Condition force upload\n");
return 0;
}
static int pid_upload_ramp(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update) {
printk("Request ramp force upload\n");
return 0;
}
static int hid_pid_event(struct hid_device *hid, struct input_dev *input,
unsigned int type, unsigned int code, int value)
{
#ifdef DEBUG
printk ("PID event received: type=%d,code=%d,value=%d.\n",type,code,value);
#endif
if (type != EV_FF)
return -1;
return 0;
}
/* Lock must be held by caller */
static void hid_pid_ctrl_playback(struct hid_device *hid,
struct hid_pid_effect *effect, int play)
{
if (play) {
set_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
} else {
clear_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
}
}
static int hid_pid_erase(struct input_dev *dev, int id)
{
struct hid_device *hid = dev->private;
struct hid_field* field;
struct hid_report* report;
struct hid_ff_pid *pid = hid->ff_private;
unsigned long flags;
unsigned wanted_report = HID_UP_PID | FF_PID_USAGE_BLOCK_FREE; /* PID Block Free Report */
int ret;
if (!CHECK_OWNERSHIP(id, pid)) return -EACCES;
/* Find report */
ret = hid_find_report_by_usage(hid, wanted_report, &report, HID_OUTPUT_REPORT);
if(!ret) {
printk("Couldn't find report\n");
return ret;
}
/* Find field */
field = (struct hid_field *) kmalloc(sizeof(struct hid_field), GFP_KERNEL);
ret = hid_set_field(field, ret, pid->effects[id].device_id);
if(!ret) {
printk("Couldn't set field\n");
return ret;
}
hid_submit_report(hid, report, USB_DIR_OUT);
spin_lock_irqsave(&pid->lock, flags);
hid_pid_ctrl_playback(hid, pid->effects + id, 0);
pid->effects[id].flags = 0;
spin_unlock_irqrestore(&pid->lock, flags);
return ret;
}
/* Erase all effects this process owns */
static int hid_pid_flush(struct input_dev *dev, struct file *file)
{
struct hid_device *hid = dev->private;
struct hid_ff_pid *pid = hid->ff_private;
int 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 */
for (i=0; i<dev->ff_effects_max; ++i)
if ( current->pid == pid->effects[i].owner
&& test_bit(FF_PID_FLAGS_USED, &pid->effects[i].flags))
if (hid_pid_erase(dev, i))
warn("erase effect %d failed", i);
return 0;
}
static int hid_pid_upload_effect(struct input_dev *dev,
struct ff_effect *effect)
{
struct hid_ff_pid* pid_private = (struct hid_ff_pid*)(dev->private);
int ret;
int is_update;
int flags=0;
#ifdef DEBUG
printk("Upload effect called: effect_type=%x\n",effect->type);
#endif
/* Check this effect type is supported by this device */
if (!test_bit(effect->type, dev->ffbit)) {
#ifdef DEBUG
printk("Invalid kind of effect requested.\n");
#endif
return -EINVAL;
}
/*
* If we want to create a new effect, get a free id
*/
if (effect->id == -1) {
int id=0;
// Spinlock so we don`t get a race condition when choosing IDs
spin_lock_irqsave(&pid_private->lock,flags);
while(id < FF_EFFECTS_MAX)
if (!test_and_set_bit(FF_PID_FLAGS_USED, &pid_private->effects[id++].flags))
break;
if ( id == FF_EFFECTS_MAX) {
// TEMP - We need to get ff_effects_max correctly first: || id >= dev->ff_effects_max) {
#ifdef DEBUG
printk("Not enough device memory\n");
#endif
return -ENOMEM;
}
effect->id = id;
#ifdef DEBUG
printk("Effect ID is %d\n.",id);
#endif
pid_private->effects[id].owner = current->pid;
pid_private->effects[id].flags = (1<<FF_PID_FLAGS_USED);
spin_unlock_irqrestore(&pid_private->lock,flags);
is_update = FF_PID_FALSE;
}
else {
/* We want to update an effect */
if (!CHECK_OWNERSHIP(effect->id, pid_private)) return -EACCES;
/* Parameter type cannot be updated */
if (effect->type != pid_private->effects[effect->id].effect.type)
return -EINVAL;
/* Check the effect is not already being updated */
if (test_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags)) {
return -EAGAIN;
}
is_update = FF_PID_TRUE;
}
/*
* Upload the effect
*/
switch (effect->type) {
case FF_PERIODIC:
ret = pid_upload_periodic(pid_private, effect, is_update);
break;
case FF_CONSTANT:
ret = pid_upload_constant(pid_private, effect, is_update);
break;
case FF_SPRING:
case FF_FRICTION:
case FF_DAMPER:
case FF_INERTIA:
ret = pid_upload_condition(pid_private, effect, is_update);
break;
case FF_RAMP:
ret = pid_upload_ramp(pid_private, effect, is_update);
break;
default:
#ifdef DEBUG
printk("Invalid type of effect requested - %x.\n", effect->type);
#endif
return -EINVAL;
}
/* If a packet was sent, forbid new updates until we are notified
* that the packet was updated
*/
if (ret == 0)
set_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags);
pid_private->effects[effect->id].effect = *effect;
return ret;
}
int hid_pid_init(struct hid_device *hid)
{
struct hid_ff_pid *private;
private = hid->ff_private = kmalloc(sizeof(struct hid_ff_pid), GFP_KERNEL);
if (!private) return -1;
memset(private,0,sizeof(struct hid_ff_pid));
hid->ff_private = private; /* 'cause memset can move the block away */
private->hid = hid;
hid->ff_exit = hid_pid_exit;
hid->ff_event = hid_pid_event;
/* Open output URB */
if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) {
kfree(private);
return -1;
}
usb_fill_control_urb(private->urbffout, hid->dev,0,(void *) &private->ffcr,private->ctrl_buffer,8,hid_pid_ctrl_out,hid);
hid->input.upload_effect = hid_pid_upload_effect;
hid->input.flush = hid_pid_flush;
hid->input.ff_effects_max = 8; // A random default
set_bit(EV_FF, hid->input.evbit);
set_bit(EV_FF_STATUS, hid->input.evbit);
spin_lock_init(&private->lock);
printk(KERN_INFO "Force feedback driver for PID devices by Rodrigo Damazio <rdamazio@lsi.usp.br>.\n");
return 0;
}
static int __init hid_pid_modinit(void)
{
return 0;
}
static void __exit hid_pid_modexit(void)
{
}
module_init(hid_pid_modinit);
module_exit(hid_pid_modexit);
/*
* PID Force feedback support for hid devices.
*
* Copyright (c) 2002 Rodrigo Damazio.
*/
/*
* 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 <rdamazio@lsi.usp.br>
*/
#define FF_EFFECTS_MAX 64
#define FF_PID_FLAGS_USED 1 /* If the effect exists */
#define FF_PID_FLAGS_UPDATING 2 /* If the effect is being updated */
#define FF_PID_FLAGS_PLAYING 3 /* If the effect is currently being played */
#define FF_PID_FALSE 0
#define FF_PID_TRUE 1
struct hid_pid_effect {
unsigned long flags;
pid_t owner;
unsigned int device_id; // The device-assigned ID
struct ff_effect effect;
};
struct hid_ff_pid {
struct hid_device *hid;
unsigned long int gain;
struct urb *urbffout;
struct usb_ctrlrequest ffcr;
spinlock_t lock;
char ctrl_buffer[8];
struct hid_pid_effect effects[FF_EFFECTS_MAX];
};
/*
* Constants from the PID usage table (still far from complete)
*/
#define FF_PID_USAGE_BLOCK_LOAD 0x89UL
#define FF_PID_USAGE_BLOCK_FREE 0x90UL
#define FF_PID_USAGE_NEW_EFFECT 0xABUL
#define FF_PID_USAGE_POOL_REPORT 0x7FUL
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment