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
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
......@@ -83,3 +100,17 @@ CONFIG_USB_WACOM
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 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 @@
#
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 ' 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
if [ "$CONFIG_USB_HID" != "y" ]; then
......@@ -16,4 +22,5 @@ fi
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 ' Griffin PowerMate and Contour Jog support' CONFIG_USB_POWERMATE $CONFIG_USB $CONFIG_INPUT
......@@ -12,12 +12,27 @@ 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_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_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
#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) 2000-2001 Vojtech Pavlik
......@@ -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)
+ values * sizeof(unsigned));
report->field[report->maxfield] = field;
report->field[report->maxfield++] = field;
field->usage = (struct hid_usage *)(field + 1);
field->value = (unsigned *)(field->usage + usages);
field->report = report;
field->index = report->maxfield++;
return field;
}
......@@ -518,6 +517,8 @@ static void hid_free_device(struct hid_device *device)
{
unsigned i,j;
hid_ff_exit(device);
for (i = 0; i < HID_REPORT_TYPES; i++) {
struct hid_report_enum *report_enum = device->report_enum + i;
......@@ -1171,8 +1172,8 @@ int hid_wait_io(struct hid_device *hid)
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&hid->wait, &wait);
while (timeout && test_bit(HID_CTRL_RUNNING, &hid->iofl) &&
test_bit(HID_OUT_RUNNING, &hid->iofl))
while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) ||
test_bit(HID_OUT_RUNNING, &hid->iofl)))
timeout = schedule_timeout(timeout);
set_current_state(TASK_RUNNING);
......@@ -1223,6 +1224,7 @@ void hid_init_reports(struct hid_device *hid)
struct hid_report *report;
struct list_head *list;
int len;
int err, ret;
report_enum = hid->report_enum + HID_INPUT_REPORT;
list = report_enum->report_list.next;
......@@ -1240,7 +1242,16 @@ void hid_init_reports(struct hid_device *hid)
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");
return;
}
......@@ -1299,7 +1310,7 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
struct hid_descriptor *hdesc;
struct hid_device *hid;
unsigned quirks = 0, rsize = 0;
char *buf;
char *buf, *rdesc;
int 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)
return NULL;
}
{
__u8 rdesc[rsize];
if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
dbg("couldn't allocate rdesc memory");
return NULL;
}
if ((n = hid_get_class_descriptor(dev, interface->bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
dbg("reading report descriptor failed");
kfree(rdesc);
return NULL;
}
......@@ -1342,10 +1356,11 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum)
if (!(hid = hid_parse_report(rdesc, rsize))) {
dbg("parsing report descriptor failed");
kfree(rdesc);
return NULL;
}
}
kfree(rdesc);
hid->quirks = quirks;
for (n = 0; n < interface->bNumEndpoints; n++) {
......@@ -1439,6 +1454,8 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
hid_init_reports(hid);
hid_dump_device(hid);
hid_ff_init(hid);
if (!hidinput_connect(hid))
hid->claimed |= HID_CLAIMED_INPUT;
if (!hiddev_connect(hid))
......@@ -1477,20 +1494,20 @@ static void hid_disconnect(struct usb_device *dev, void *ptr)
{
struct hid_device *hid = ptr;
dbg("cleanup called");
usb_unlink_urb(hid->urbin);
usb_unlink_urb(hid->urbout);
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->urbctrl);
if (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);
}
......
/*
* $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
*
......@@ -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;
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:
unknown:
resolv_usage(usage->hid);
if (field->report_size == 1) {
......@@ -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));
}
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 */
return;
......@@ -380,6 +434,9 @@ static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsign
struct hid_field *field = NULL;
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) {
warn("event field not found");
return -1;
......
This diff is collapsed.
This diff is collapsed.
......@@ -359,6 +359,11 @@ struct hid_device { /* device report descriptor */
char name[128]; /* Device name */
char phys[64]; /* Device physical location */
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
......@@ -395,6 +400,7 @@ struct hid_descriptor {
#define hid_dump_input(a,b) do { } while (0)
#define hid_dump_device(c) do { } while (0)
#define hid_dump_field(a,b) do { } while (0)
#define resolv_usage(a) do { } while (0)
#endif
#endif
......@@ -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);
void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir);
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,
dinfo.product = dev->descriptor.idProduct;
dinfo.version = dev->descriptor.bcdDevice;
dinfo.num_applications = hid->maxapplication;
if (copy_to_user((void *) arg, &dinfo, sizeof(dinfo)))
return -EFAULT;
return 0;
return copy_to_user((void *) arg, &dinfo, sizeof(dinfo));
}
case HIDIOCGFLAG:
......@@ -482,9 +480,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
rinfo.num_fields = report->maxfield;
if (copy_to_user((void *) arg, &rinfo, sizeof(rinfo)))
return -EFAULT;
return 0;
return copy_to_user((void *) arg, &rinfo, sizeof(rinfo));
case HIDIOCGFIELDINFO:
{
......@@ -516,9 +512,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
finfo.unit_exponent = field->unit_exponent;
finfo.unit = field->unit;
if (copy_to_user((void *) arg, &finfo, sizeof(finfo)))
return -EFAULT;
return 0;
return copy_to_user((void *) arg, &finfo, sizeof(finfo));
}
case HIDIOCGUCODE:
......@@ -539,19 +533,12 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
uref.usage_code = field->usage[uref.usage_index].hid;
if (copy_to_user((void *) arg, &uref, sizeof(uref)))
return -EFAULT;
return 0;
return copy_to_user((void *) arg, &uref, sizeof(uref));
case HIDIOCGUSAGE:
case HIDIOCSUSAGE:
if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
return -EFAULT;
if (cmd == HIDIOCSUSAGE &&
uref.report_type != HID_REPORT_TYPE_OUTPUT)
return -EINVAL;
if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
field = hiddev_lookup_usage(hid, &uref);
if (field == NULL)
......@@ -570,15 +557,37 @@ static int hiddev_ioctl(struct inode *inode, struct file *file,
return -EINVAL;
}
if (cmd == HIDIOCGUSAGE) {
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 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 {
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;
default:
......@@ -626,9 +635,9 @@ int hiddev_connect(struct hid_device *hid)
if (i == hid->maxapplication)
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) {
err ("Not able to get a minor for this device.");
err("Not able to get a minor for this device.");
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