summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Kconfig2
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/evbug.c32
-rw-r--r--drivers/input/evdev.c242
-rw-r--r--drivers/input/ff-core.c3
-rw-r--r--drivers/input/input.c267
-rw-r--r--drivers/input/joydev.c188
-rw-r--r--drivers/input/joystick/Kconfig18
-rw-r--r--drivers/input/joystick/Makefile1
-rw-r--r--drivers/input/joystick/a3d.c9
-rw-r--r--drivers/input/joystick/adi.c9
-rw-r--r--drivers/input/joystick/analog.c11
-rw-r--r--drivers/input/joystick/cobra.c9
-rw-r--r--drivers/input/joystick/db9.c25
-rw-r--r--drivers/input/joystick/gamecon.c24
-rw-r--r--drivers/input/joystick/gf2k.c10
-rw-r--r--drivers/input/joystick/grip.c9
-rw-r--r--drivers/input/joystick/grip_mp.c11
-rw-r--r--drivers/input/joystick/guillemot.c9
-rw-r--r--drivers/input/joystick/iforce/iforce-ff.c10
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c26
-rw-r--r--drivers/input/joystick/iforce/iforce-packets.c22
-rw-r--r--drivers/input/joystick/iforce/iforce-serio.c2
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c13
-rw-r--r--drivers/input/joystick/iforce/iforce.h4
-rw-r--r--drivers/input/joystick/interact.c8
-rw-r--r--drivers/input/joystick/magellan.c3
-rw-r--r--drivers/input/joystick/sidewinder.c9
-rw-r--r--drivers/input/joystick/spaceball.c3
-rw-r--r--drivers/input/joystick/spaceorb.c3
-rw-r--r--drivers/input/joystick/stinger.c3
-rw-r--r--drivers/input/joystick/tmdc.c9
-rw-r--r--drivers/input/joystick/turbografx.c25
-rw-r--r--drivers/input/joystick/twidjoy.c4
-rw-r--r--drivers/input/joystick/warrior.c3
-rw-r--r--drivers/input/joystick/xpad.c433
-rw-r--r--drivers/input/keyboard/Kconfig21
-rw-r--r--drivers/input/keyboard/Makefile2
-rw-r--r--drivers/input/keyboard/aaed2000_kbd.c63
-rw-r--r--drivers/input/keyboard/atakbd.c134
-rw-r--r--drivers/input/keyboard/atkbd.c7
-rw-r--r--drivers/input/keyboard/corgikbd.c3
-rw-r--r--drivers/input/keyboard/gpio_keys.c22
-rw-r--r--drivers/input/keyboard/hil_kbd.c89
-rw-r--r--drivers/input/keyboard/hilkbd.c8
-rw-r--r--drivers/input/keyboard/lkkbd.c7
-rw-r--r--drivers/input/keyboard/locomokbd.c2
-rw-r--r--drivers/input/keyboard/newtonkbd.c3
-rw-r--r--drivers/input/keyboard/omap-keypad.c3
-rw-r--r--drivers/input/keyboard/pxa27x_keyboard.c258
-rw-r--r--drivers/input/keyboard/spitzkbd.c3
-rw-r--r--drivers/input/keyboard/stowaway.c3
-rw-r--r--drivers/input/keyboard/sunkbd.c8
-rw-r--r--drivers/input/keyboard/xtkbd.c3
-rw-r--r--drivers/input/misc/Kconfig111
-rw-r--r--drivers/input/misc/Makefile11
-rw-r--r--drivers/input/misc/ati_remote.c862
-rw-r--r--drivers/input/misc/ati_remote2.c543
-rw-r--r--drivers/input/misc/cobalt_btns.c172
-rw-r--r--drivers/input/misc/input-polldev.c171
-rw-r--r--drivers/input/misc/ixp4xx-beeper.c11
-rw-r--r--drivers/input/misc/keyspan_remote.c592
-rw-r--r--drivers/input/misc/m68kspkr.c2
-rw-r--r--drivers/input/misc/map_to_7segment.h189
-rw-r--r--drivers/input/misc/pcspkr.c2
-rw-r--r--drivers/input/misc/powermate.c463
-rw-r--r--drivers/input/misc/sparcspkr.c6
-rw-r--r--drivers/input/misc/uinput.c11
-rw-r--r--drivers/input/misc/wistron_btns.c685
-rw-r--r--drivers/input/misc/yealink.c1004
-rw-r--r--drivers/input/misc/yealink.h220
-rw-r--r--drivers/input/mouse/Kconfig98
-rw-r--r--drivers/input/mouse/Makefile10
-rw-r--r--drivers/input/mouse/alps.c13
-rw-r--r--drivers/input/mouse/alps.h18
-rw-r--r--drivers/input/mouse/appletouch.c706
-rw-r--r--drivers/input/mouse/atarimouse.c160
-rw-r--r--drivers/input/mouse/hil_ptr.c97
-rw-r--r--drivers/input/mouse/lifebook.c195
-rw-r--r--drivers/input/mouse/lifebook.h11
-rw-r--r--drivers/input/mouse/logips2pp.c7
-rw-r--r--drivers/input/mouse/logips2pp.h7
-rw-r--r--drivers/input/mouse/psmouse-base.c50
-rw-r--r--drivers/input/mouse/psmouse.h1
-rw-r--r--drivers/input/mouse/sermouse.c19
-rw-r--r--drivers/input/mouse/synaptics.c111
-rw-r--r--drivers/input/mouse/synaptics.h18
-rw-r--r--drivers/input/mouse/touchkit_ps2.c100
-rw-r--r--drivers/input/mouse/touchkit_ps2.h24
-rw-r--r--drivers/input/mouse/trackpoint.h9
-rw-r--r--drivers/input/mouse/vsxxxaa.c3
-rw-r--r--drivers/input/mousedev.c442
-rw-r--r--drivers/input/power.c166
-rw-r--r--drivers/input/serio/hil_mlc.c505
-rw-r--r--drivers/input/serio/hp_sdc.c427
-rw-r--r--drivers/input/serio/hp_sdc_mlc.c227
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h24
-rw-r--r--drivers/input/serio/i8042.c42
-rw-r--r--drivers/input/tablet/Kconfig74
-rw-r--r--drivers/input/tablet/Makefile12
-rw-r--r--drivers/input/tablet/acecad.c289
-rw-r--r--drivers/input/tablet/aiptek.c2236
-rw-r--r--drivers/input/tablet/gtco.c1055
-rw-r--r--drivers/input/tablet/kbtab.c226
-rw-r--r--drivers/input/tablet/wacom.h131
-rw-r--r--drivers/input/tablet/wacom_sys.c318
-rw-r--r--drivers/input/tablet/wacom_wac.c675
-rw-r--r--drivers/input/tablet/wacom_wac.h49
-rw-r--r--drivers/input/touchscreen/Kconfig60
-rw-r--r--drivers/input/touchscreen/Makefile17
-rw-r--r--drivers/input/touchscreen/ads7846.c30
-rw-r--r--drivers/input/touchscreen/corgi_ts.c3
-rw-r--r--drivers/input/touchscreen/elo.c3
-rw-r--r--drivers/input/touchscreen/gunze.c2
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c9
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c2
-rw-r--r--drivers/input/touchscreen/mtouch.c2
-rw-r--r--drivers/input/touchscreen/penmount.c3
-rw-r--r--drivers/input/touchscreen/touchright.c2
-rw-r--r--drivers/input/touchscreen/touchwin.c2
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c26
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c839
-rw-r--r--drivers/input/tsdev.c178
123 files changed, 15122 insertions, 1766 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 96232313b1b..0e9b69535ad 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -153,6 +153,8 @@ source "drivers/input/mouse/Kconfig"
source "drivers/input/joystick/Kconfig"
+source "drivers/input/tablet/Kconfig"
+
source "drivers/input/touchscreen/Kconfig"
source "drivers/input/misc/Kconfig"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index da575deb3c7..8a2dd987546 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -13,12 +13,12 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
obj-$(CONFIG_INPUT_TSDEV) += tsdev.o
-obj-$(CONFIG_INPUT_POWER) += power.o
obj-$(CONFIG_INPUT_EVBUG) += evbug.o
obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
obj-$(CONFIG_INPUT_MOUSE) += mouse/
obj-$(CONFIG_INPUT_JOYSTICK) += joystick/
+obj-$(CONFIG_INPUT_TABLET) += tablet/
obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c
index 5a9653c3128..c21f2f12723 100644
--- a/drivers/input/evbug.c
+++ b/drivers/input/evbug.c
@@ -38,31 +38,43 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Input driver event debug module");
MODULE_LICENSE("GPL");
-static char evbug_name[] = "evbug";
-
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
handle->dev->phys, type, code, value);
}
-static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
+static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct input_handle *handle;
+ int error;
- if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL)))
- return NULL;
+ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
handle->dev = dev;
handle->handler = handler;
- handle->name = evbug_name;
+ handle->name = "evbug";
+
+ error = input_register_handle(handle);
+ if (error)
+ goto err_free_handle;
- input_open_device(handle);
+ error = input_open_device(handle);
+ if (error)
+ goto err_unregister_handle;
printk(KERN_DEBUG "evbug.c: Connected device: \"%s\", %s\n", dev->name, dev->phys);
- return handle;
+ return 0;
+
+ err_unregister_handle:
+ input_unregister_handle(handle);
+ err_free_handle:
+ kfree(handle);
+ return error;
}
static void evbug_disconnect(struct input_handle *handle)
@@ -70,7 +82,7 @@ static void evbug_disconnect(struct input_handle *handle)
printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n", handle->dev->phys);
input_close_device(handle);
-
+ input_unregister_handle(handle);
kfree(handle);
}
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 6439f378f6c..55a72592704 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -18,7 +18,6 @@
#include <linux/init.h>
#include <linux/input.h>
#include <linux/major.h>
-#include <linux/smp_lock.h>
#include <linux/device.h>
#include <linux/compat.h>
@@ -29,11 +28,11 @@ struct evdev {
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
- struct evdev_list *grab;
- struct list_head list;
+ struct evdev_client *grab;
+ struct list_head client_list;
};
-struct evdev_list {
+struct evdev_client {
struct input_event buffer[EVDEV_BUFFER_SIZE];
int head;
int tail;
@@ -47,28 +46,28 @@ static struct evdev *evdev_table[EVDEV_MINORS];
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
- struct evdev_list *list;
+ struct evdev_client *client;
if (evdev->grab) {
- list = evdev->grab;
+ client = evdev->grab;
- do_gettimeofday(&list->buffer[list->head].time);
- list->buffer[list->head].type = type;
- list->buffer[list->head].code = code;
- list->buffer[list->head].value = value;
- list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+ do_gettimeofday(&client->buffer[client->head].time);
+ client->buffer[client->head].type = type;
+ client->buffer[client->head].code = code;
+ client->buffer[client->head].value = value;
+ client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
} else
- list_for_each_entry(list, &evdev->list, node) {
+ list_for_each_entry(client, &evdev->client_list, node) {
- do_gettimeofday(&list->buffer[list->head].time);
- list->buffer[list->head].type = type;
- list->buffer[list->head].code = code;
- list->buffer[list->head].value = value;
- list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+ do_gettimeofday(&client->buffer[client->head].time);
+ client->buffer[client->head].type = type;
+ client->buffer[client->head].code = code;
+ client->buffer[client->head].value = value;
+ client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&evdev->wait);
@@ -76,22 +75,23 @@ static void evdev_event(struct input_handle *handle, unsigned int type, unsigned
static int evdev_fasync(int fd, struct file *file, int on)
{
+ struct evdev_client *client = file->private_data;
int retval;
- struct evdev_list *list = file->private_data;
- retval = fasync_helper(fd, file, on, &list->fasync);
+ retval = fasync_helper(fd, file, on, &client->fasync);
return retval < 0 ? retval : 0;
}
static int evdev_flush(struct file *file, fl_owner_t id)
{
- struct evdev_list *list = file->private_data;
+ struct evdev_client *client = file->private_data;
+ struct evdev *evdev = client->evdev;
- if (!list->evdev->exist)
+ if (!evdev->exist)
return -ENODEV;
- return input_flush_device(&list->evdev->handle, file);
+ return input_flush_device(&evdev->handle, file);
}
static void evdev_free(struct evdev *evdev)
@@ -100,48 +100,62 @@ static void evdev_free(struct evdev *evdev)
kfree(evdev);
}
-static int evdev_release(struct inode * inode, struct file * file)
+static int evdev_release(struct inode *inode, struct file *file)
{
- struct evdev_list *list = file->private_data;
+ struct evdev_client *client = file->private_data;
+ struct evdev *evdev = client->evdev;
- if (list->evdev->grab == list) {
- input_release_device(&list->evdev->handle);
- list->evdev->grab = NULL;
+ if (evdev->grab == client) {
+ input_release_device(&evdev->handle);
+ evdev->grab = NULL;
}
evdev_fasync(-1, file, 0);
- list_del(&list->node);
+ list_del(&client->node);
+ kfree(client);
- if (!--list->evdev->open) {
- if (list->evdev->exist)
- input_close_device(&list->evdev->handle);
+ if (!--evdev->open) {
+ if (evdev->exist)
+ input_close_device(&evdev->handle);
else
- evdev_free(list->evdev);
+ evdev_free(evdev);
}
- kfree(list);
return 0;
}
-static int evdev_open(struct inode * inode, struct file * file)
+static int evdev_open(struct inode *inode, struct file *file)
{
- struct evdev_list *list;
+ struct evdev_client *client;
+ struct evdev *evdev;
int i = iminor(inode) - EVDEV_MINOR_BASE;
+ int error;
- if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist)
+ if (i >= EVDEV_MINORS)
return -ENODEV;
- if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL)))
+ evdev = evdev_table[i];
+
+ if (!evdev || !evdev->exist)
+ return -ENODEV;
+
+ client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
+ if (!client)
return -ENOMEM;
- list->evdev = evdev_table[i];
- list_add_tail(&list->node, &evdev_table[i]->list);
- file->private_data = list;
+ client->evdev = evdev;
+ list_add_tail(&client->node, &evdev->client_list);
- if (!list->evdev->open++)
- if (list->evdev->exist)
- input_open_device(&list->evdev->handle);
+ if (!evdev->open++ && evdev->exist) {
+ error = input_open_device(&evdev->handle);
+ if (error) {
+ list_del(&client->node);
+ kfree(client);
+ return error;
+ }
+ }
+ file->private_data = client;
return 0;
}
@@ -243,54 +257,55 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
#endif /* CONFIG_COMPAT */
-static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
- struct evdev_list *list = file->private_data;
+ struct evdev_client *client = file->private_data;
+ struct evdev *evdev = client->evdev;
struct input_event event;
int retval = 0;
- if (!list->evdev->exist)
+ if (!evdev->exist)
return -ENODEV;
while (retval < count) {
if (evdev_event_from_user(buffer + retval, &event))
return -EFAULT;
- input_inject_event(&list->evdev->handle, event.type, event.code, event.value);
+ input_inject_event(&evdev->handle, event.type, event.code, event.value);
retval += evdev_event_size();
}
return retval;
}
-static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
+static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
- struct evdev_list *list = file->private_data;
+ struct evdev_client *client = file->private_data;
+ struct evdev *evdev = client->evdev;
int retval;
if (count < evdev_event_size())
return -EINVAL;
- if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
+ if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
- retval = wait_event_interruptible(list->evdev->wait,
- list->head != list->tail || (!list->evdev->exist));
-
+ retval = wait_event_interruptible(evdev->wait,
+ client->head != client->tail || !evdev->exist);
if (retval)
return retval;
- if (!list->evdev->exist)
+ if (!evdev->exist)
return -ENODEV;
- while (list->head != list->tail && retval + evdev_event_size() <= count) {
+ while (client->head != client->tail && retval + evdev_event_size() <= count) {
- struct input_event *event = (struct input_event *) list->buffer + list->tail;
+ struct input_event *event = (struct input_event *) client->buffer + client->tail;
if (evdev_event_to_user(buffer + retval, event))
return -EFAULT;
- list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
+ client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
retval += evdev_event_size();
}
@@ -300,11 +315,12 @@ static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count
/* No kernel lock - fine */
static unsigned int evdev_poll(struct file *file, poll_table *wait)
{
- struct evdev_list *list = file->private_data;
+ struct evdev_client *client = file->private_data;
+ struct evdev *evdev = client->evdev;
- poll_wait(file, &list->evdev->wait, wait);
- return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
- (list->evdev->exist ? 0 : (POLLHUP | POLLERR));
+ poll_wait(file, &evdev->wait, wait);
+ return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
+ (evdev->exist ? 0 : (POLLHUP | POLLERR));
}
#ifdef CONFIG_COMPAT
@@ -387,8 +403,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
- struct evdev_list *list = file->private_data;
- struct evdev *evdev = list->evdev;
+ struct evdev_client *client = file->private_data;
+ struct evdev *evdev = client->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
struct ff_effect effect;
@@ -434,32 +450,21 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
case EVIOCGKEYCODE:
if (get_user(t, ip))
return -EFAULT;
- if (t < 0 || t >= dev->keycodemax || !dev->keycodesize)
- return -EINVAL;
- if (put_user(INPUT_KEYCODE(dev, t), ip + 1))
+
+ error = dev->getkeycode(dev, t, &v);
+ if (error)
+ return error;
+
+ if (put_user(v, ip + 1))
return -EFAULT;
+
return 0;
case EVIOCSKEYCODE:
- if (get_user(t, ip))
+ if (get_user(t, ip) || get_user(v, ip + 1))
return -EFAULT;
- if (t < 0 || t >= dev->keycodemax || !dev->keycodesize)
- return -EINVAL;
- if (get_user(v, ip + 1))
- return -EFAULT;
- if (v < 0 || v > KEY_MAX)
- return -EINVAL;
- if (dev->keycodesize < sizeof(v) && (v >> (dev->keycodesize * 8)))
- return -EINVAL;
-
- u = SET_INPUT_KEYCODE(dev, t, v);
- clear_bit(u, dev->keybit);
- set_bit(v, dev->keybit);
- for (i = 0; i < dev->keycodemax; i++)
- if (INPUT_KEYCODE(dev, i) == u)
- set_bit(u, dev->keybit);
- return 0;
+ return dev->setkeycode(dev, t, v);
case EVIOCSFF:
if (copy_from_user(&effect, p, sizeof(effect)))
@@ -487,10 +492,10 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
return -EBUSY;
if (input_grab_device(&evdev->handle))
return -EBUSY;
- evdev->grab = list;
+ evdev->grab = client;
return 0;
} else {
- if (evdev->grab != list)
+ if (evdev->grab != client)
return -EINVAL;
input_release_device(&evdev->handle);
evdev->grab = NULL;
@@ -506,7 +511,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
- long *bits;
+ unsigned long *bits;
int len;
switch (_IOC_NR(cmd) & EV_MAX) {
@@ -551,7 +556,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
- int t = _IOC_NR(cmd) & ABS_MAX;
+ t = _IOC_NR(cmd) & ABS_MAX;
abs.value = dev->abs[t];
abs.minimum = dev->absmin[t];
@@ -571,7 +576,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
- int t = _IOC_NR(cmd) & ABS_MAX;
+ t = _IOC_NR(cmd) & ABS_MAX;
if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
return -EFAULT;
@@ -616,23 +621,26 @@ static const struct file_operations evdev_fops = {
.flush = evdev_flush
};
-static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
+static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
+ dev_t devt;
int minor;
+ int error;
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
- return NULL;
+ return -ENFILE;
}
- if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
- return NULL;
+ evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
+ if (!evdev)
+ return -ENOMEM;
- INIT_LIST_HEAD(&evdev->list);
+ INIT_LIST_HEAD(&evdev->client_list);
init_waitqueue_head(&evdev->wait);
evdev->exist = 1;
@@ -645,23 +653,45 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct
evdev_table[minor] = evdev;
- cdev = class_device_create(&input_class, &dev->cdev,
- MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
- dev->cdev.dev, evdev->name);
+ devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
+
+ cdev = class_device_create(&input_class, &dev->cdev, devt,
+ dev->cdev.dev, evdev->name);
+ if (IS_ERR(cdev)) {
+ error = PTR_ERR(cdev);
+ goto err_free_evdev;
+ }
/* temporary symlink to keep userspace happy */
- sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
- evdev->name);
+ error = sysfs_create_link(&input_class.subsys.kobj,
+ &cdev->kobj, evdev->name);
+ if (error)
+ goto err_cdev_destroy;
+
+ error = input_register_handle(&evdev->handle);
+ if (error)
+ goto err_remove_link;
- return &evdev->handle;
+ return 0;
+
+ err_remove_link:
+ sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
+ err_cdev_destroy:
+ class_device_destroy(&input_class, devt);
+ err_free_evdev:
+ kfree(evdev);
+ evdev_table[minor] = NULL;
+ return error;
}
static void evdev_disconnect(struct input_handle *handle)
{
struct evdev *evdev = handle->private;
- struct evdev_list *list;
+ struct evdev_client *client;
+
+ input_unregister_handle(handle);
- sysfs_remove_link(&input_class.subsys.kset.kobj, evdev->name);
+ sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
class_device_destroy(&input_class,
MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor));
evdev->exist = 0;
@@ -670,8 +700,8 @@ static void evdev_disconnect(struct input_handle *handle)
input_flush_device(handle, NULL);
input_close_device(handle);
wake_up_interruptible(&evdev->wait);
- list_for_each_entry(list, &evdev->list, node)
- kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+ list_for_each_entry(client, &evdev->client_list, node)
+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
} else
evdev_free(evdev);
}
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index 783b3412cea..eebc72465fc 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -281,7 +281,8 @@ int input_ff_event(struct input_dev *dev, unsigned int type,
break;
default:
- ff->playback(dev, code, value);
+ if (check_effect_access(ff, code, NULL) == 0)
+ ff->playback(dev, code, value);
break;
}
diff --git a/drivers/input/input.c b/drivers/input/input.c
index a9a706f8fff..ccd8abafcb7 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -11,7 +11,6 @@
*/
#include <linux/init.h>
-#include <linux/smp_lock.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/random.h>
@@ -299,12 +298,87 @@ void input_close_device(struct input_handle *handle)
}
EXPORT_SYMBOL(input_close_device);
-static void input_link_handle(struct input_handle *handle)
+static int input_fetch_keycode(struct input_dev *dev, int scancode)
{
- list_add_tail(&handle->d_node, &handle->dev->h_list);
- list_add_tail(&handle->h_node, &handle->handler->h_list);
+ switch (dev->keycodesize) {
+ case 1:
+ return ((u8 *)dev->keycode)[scancode];
+
+ case 2:
+ return ((u16 *)dev->keycode)[scancode];
+
+ default:
+ return ((u32 *)dev->keycode)[scancode];
+ }
+}
+
+static int input_default_getkeycode(struct input_dev *dev,
+ int scancode, int *keycode)
+{
+ if (!dev->keycodesize)
+ return -EINVAL;
+
+ if (scancode < 0 || scancode >= dev->keycodemax)
+ return -EINVAL;
+
+ *keycode = input_fetch_keycode(dev, scancode);
+
+ return 0;
+}
+
+static int input_default_setkeycode(struct input_dev *dev,
+ int scancode, int keycode)
+{
+ int old_keycode;
+ int i;
+
+ if (scancode < 0 || scancode >= dev->keycodemax)
+ return -EINVAL;
+
+ if (keycode < 0 || keycode > KEY_MAX)
+ return -EINVAL;
+
+ if (!dev->keycodesize)
+ return -EINVAL;
+
+ if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8)))
+ return -EINVAL;
+
+ switch (dev->keycodesize) {
+ case 1: {
+ u8 *k = (u8 *)dev->keycode;
+ old_keycode = k[scancode];
+ k[scancode] = keycode;
+ break;
+ }
+ case 2: {
+ u16 *k = (u16 *)dev->keycode;
+ old_keycode = k[scancode];
+ k[scancode] = keycode;
+ break;
+ }
+ default: {
+ u32 *k = (u32 *)dev->keycode;
+ old_keycode = k[scancode];
+ k[scancode] = keycode;
+ break;
+ }
+ }
+
+ clear_bit(old_keycode, dev->keybit);
+ set_bit(keycode, dev->keybit);
+
+ for (i = 0; i < dev->keycodemax; i++) {
+ if (input_fetch_keycode(dev, i) == old_keycode) {
+ set_bit(old_keycode, dev->keybit);
+ break; /* Setting the bit twice is useless, so break */
+ }
+ }
+
+ return 0;
}
+
#define MATCH_BIT(bit, max) \
for (i = 0; i < NBITS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
@@ -351,6 +425,29 @@ static const struct input_device_id *input_match_device(const struct input_devic
return NULL;
}
+static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
+{
+ const struct input_device_id *id;
+ int error;
+
+ if (handler->blacklist && input_match_device(handler->blacklist, dev))
+ return -ENODEV;
+
+ id = input_match_device(handler->id_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ error = handler->connect(handler, dev, id);
+ if (error && error != -ENODEV)
+ printk(KERN_ERR
+ "input: failed to attach handler %s to device %s, "
+ "error: %d\n",
+ handler->name, kobject_name(&dev->cdev.kobj), error);
+
+ return error;
+}
+
+
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_bus_input_dir;
@@ -439,6 +536,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : "");
seq_printf(seq, "S: Sysfs=%s\n", path ? path : "");
+ seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : "");
seq_printf(seq, "H: Handlers=");
list_for_each_entry(handle, &dev->h_list, d_node)
@@ -753,6 +851,13 @@ static struct attribute_group input_dev_caps_attr_group = {
.attrs = input_dev_caps_attrs,
};
+static struct attribute_group *input_dev_attr_groups[] = {
+ &input_dev_attr_group,
+ &input_dev_id_attr_group,
+ &input_dev_caps_attr_group,
+ NULL
+};
+
static void input_dev_release(struct class_device *class_dev)
{
struct input_dev *dev = to_input_dev(class_dev);
@@ -906,6 +1011,7 @@ struct input_dev *input_allocate_device(void)
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
dev->cdev.class = &input_class;
+ dev->cdev.groups = input_dev_attr_groups;
class_device_initialize(&dev->cdev);
mutex_init(&dev->mutex);
INIT_LIST_HEAD(&dev->h_list);
@@ -934,23 +1040,71 @@ EXPORT_SYMBOL(input_allocate_device);
*/
void input_free_device(struct input_dev *dev)
{
- if (dev) {
-
- mutex_lock(&dev->mutex);
- dev->name = dev->phys = dev->uniq = NULL;
- mutex_unlock(&dev->mutex);
-
+ if (dev)
input_put_device(dev);
- }
}
EXPORT_SYMBOL(input_free_device);
+/**
+ * input_set_capability - mark device as capable of a certain event
+ * @dev: device that is capable of emitting or accepting event
+ * @type: type of the event (EV_KEY, EV_REL, etc...)
+ * @code: event code
+ *
+ * In addition to setting up corresponding bit in appropriate capability
+ * bitmap the function also adjusts dev->evbit.
+ */
+void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
+{
+ switch (type) {
+ case EV_KEY:
+ __set_bit(code, dev->keybit);
+ break;
+
+ case EV_REL:
+ __set_bit(code, dev->relbit);
+ break;
+
+ case EV_ABS:
+ __set_bit(code, dev->absbit);
+ break;
+
+ case EV_MSC:
+ __set_bit(code, dev->mscbit);
+ break;
+
+ case EV_SW:
+ __set_bit(code, dev->swbit);
+ break;
+
+ case EV_LED:
+ __set_bit(code, dev->ledbit);
+ break;
+
+ case EV_SND:
+ __set_bit(code, dev->sndbit);
+ break;
+
+ case EV_FF:
+ __set_bit(code, dev->ffbit);
+ break;
+
+ default:
+ printk(KERN_ERR
+ "input_set_capability: unknown type %u (code %u)\n",
+ type, code);
+ dump_stack();
+ return;
+ }
+
+ __set_bit(type, dev->evbit);
+}
+EXPORT_SYMBOL(input_set_capability);
+
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
- struct input_handle *handle;
struct input_handler *handler;
- const struct input_device_id *id;
const char *path;
int error;
@@ -969,55 +1123,41 @@ int input_register_device(struct input_dev *dev)
dev->rep[REP_PERIOD] = 33;
}
+ if (!dev->getkeycode)
+ dev->getkeycode = input_default_getkeycode;
+
+ if (!dev->setkeycode)
+ dev->setkeycode = input_default_setkeycode;
+
list_add_tail(&dev->node, &input_dev_list);
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
+ if (!dev->cdev.dev)
+ dev->cdev.dev = dev->dev.parent;
+
error = class_device_add(&dev->cdev);
if (error)
return error;
- error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group);
- if (error)
- goto fail1;
-
- error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
- if (error)
- goto fail2;
-
- error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
- if (error)
- goto fail3;
-
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
list_for_each_entry(handler, &input_handler_list, node)
- if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
- if ((id = input_match_device(handler->id_table, dev)))
- if ((handle = handler->connect(handler, dev, id))) {
- input_link_handle(handle);
- if (handler->start)
- handler->start(handle);
- }
+ input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
-
- fail3: sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
- fail2: sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
- fail1: class_device_del(&dev->cdev);
- return error;
}
EXPORT_SYMBOL(input_register_device);
void input_unregister_device(struct input_dev *dev)
{
- struct list_head *node, *next;
+ struct input_handle *handle, *next;
int code;
for (code = 0; code <= KEY_MAX; code++)
@@ -1027,19 +1167,12 @@ void input_unregister_device(struct input_dev *dev)
del_timer_sync(&dev->timer);
- list_for_each_safe(node, next, &dev->h_list) {
- struct input_handle * handle = to_handle(node);
- list_del_init(&handle->d_node);
- list_del_init(&handle->h_node);
+ list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
handle->handler->disconnect(handle);
- }
+ WARN_ON(!list_empty(&dev->h_list));
list_del_init(&dev->node);
- sysfs_remove_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
- sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
- sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
-
class_device_unregister(&dev->cdev);
input_wakeup_procfs_readers();
@@ -1049,8 +1182,6 @@ EXPORT_SYMBOL(input_unregister_device);
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
- struct input_handle *handle;
- const struct input_device_id *id;
INIT_LIST_HEAD(&handler->h_list);
@@ -1064,13 +1195,7 @@ int input_register_handler(struct input_handler *handler)
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
- if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
- if ((id = input_match_device(handler->id_table, dev)))
- if ((handle = handler->connect(handler, dev, id))) {
- input_link_handle(handle);
- if (handler->start)
- handler->start(handle);
- }
+ input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
@@ -1079,14 +1204,11 @@ EXPORT_SYMBOL(input_register_handler);
void input_unregister_handler(struct input_handler *handler)
{
- struct list_head *node, *next;
+ struct input_handle *handle, *next;
- list_for_each_safe(node, next, &handler->h_list) {
- struct input_handle * handle = to_handle_h(node);
- list_del_init(&handle->h_node);
- list_del_init(&handle->d_node);
+ list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
handler->disconnect(handle);
- }
+ WARN_ON(!list_empty(&handler->h_list));
list_del_init(&handler->node);
@@ -1097,6 +1219,27 @@ void input_unregister_handler(struct input_handler *handler)
}
EXPORT_SYMBOL(input_unregister_handler);
+int input_register_handle(struct input_handle *handle)
+{
+ struct input_handler *handler = handle->handler;
+
+ list_add_tail(&handle->d_node, &handle->dev->h_list);
+ list_add_tail(&handle->h_node, &handler->h_list);
+
+ if (handler->start)
+ handler->start(handle);
+
+ return 0;
+}
+EXPORT_SYMBOL(input_register_handle);
+
+void input_unregister_handle(struct input_handle *handle)
+{
+ list_del_init(&handle->h_node);
+ list_del_init(&handle->d_node);
+}
+EXPORT_SYMBOL(input_unregister_handle);
+
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 9f3529ad3fd..06f0541b24d 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -24,7 +24,6 @@
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#include <linux/device.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
@@ -43,7 +42,7 @@ struct joydev {
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
- struct list_head list;
+ struct list_head client_list;
struct js_corr corr[ABS_MAX + 1];
struct JS_DATA_SAVE_TYPE glue;
int nabs;
@@ -55,7 +54,7 @@ struct joydev {
__s16 abs[ABS_MAX + 1];
};
-struct joydev_list {
+struct joydev_client {
struct js_event buffer[JOYDEV_BUFFER_SIZE];
int head;
int tail;
@@ -87,7 +86,7 @@ static int joydev_correct(int value, struct js_corr *corr)
static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct joydev *joydev = handle->private;
- struct joydev_list *list;
+ struct joydev_client *client;
struct js_event event;
switch (type) {
@@ -115,15 +114,15 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
event.time = jiffies_to_msecs(jiffies);
- list_for_each_entry(list, &joydev->list, node) {
+ list_for_each_entry(client, &joydev->client_list, node) {
- memcpy(list->buffer + list->head, &event, sizeof(struct js_event));
+ memcpy(client->buffer + client->head, &event, sizeof(struct js_event));
- if (list->startup == joydev->nabs + joydev->nkey)
- if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
- list->startup = 0;
+ if (client->startup == joydev->nabs + joydev->nkey)
+ if (client->tail == (client->head = (client->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
+ client->startup = 0;
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&joydev->wait);
@@ -132,9 +131,9 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
static int joydev_fasync(int fd, struct file *file, int on)
{
int retval;
- struct joydev_list *list = file->private_data;
+ struct joydev_client *client = file->private_data;
- retval = fasync_helper(fd, file, on, &list->fasync);
+ retval = fasync_helper(fd, file, on, &client->fasync);
return retval < 0 ? retval : 0;
}
@@ -145,60 +144,73 @@ static void joydev_free(struct joydev *joydev)
kfree(joydev);
}
-static int joydev_release(struct inode * inode, struct file * file)
+static int joydev_release(struct inode *inode, struct file *file)
{
- struct joydev_list *list = file->private_data;
+ struct joydev_client *client = file->private_data;
+ struct joydev *joydev = client->joydev;
joydev_fasync(-1, file, 0);
- list_del(&list->node);
+ list_del(&client->node);
+ kfree(client);
- if (!--list->joydev->open) {
- if (list->joydev->exist)
- input_close_device(&list->joydev->handle);
+ if (!--joydev->open) {
+ if (joydev->exist)
+ input_close_device(&joydev->handle);
else
- joydev_free(list->joydev);
+ joydev_free(joydev);
}
- kfree(list);
return 0;
}
static int joydev_open(struct inode *inode, struct file *file)
{
- struct joydev_list *list;
+ struct joydev_client *client;
+ struct joydev *joydev;
int i = iminor(inode) - JOYDEV_MINOR_BASE;
+ int error;
+
+ if (i >= JOYDEV_MINORS)
+ return -ENODEV;
- if (i >= JOYDEV_MINORS || !joydev_table[i])
+ joydev = joydev_table[i];
+ if (!joydev || !joydev->exist)
return -ENODEV;
- if (!(list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL)))
+ client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);
+ if (!client)
return -ENOMEM;
- list->joydev = joydev_table[i];
- list_add_tail(&list->node, &joydev_table[i]->list);
- file->private_data = list;
+ client->joydev = joydev;
+ list_add_tail(&client->node, &joydev->client_list);
- if (!list->joydev->open++)
- if (list->joydev->exist)
- input_open_device(&list->joydev->handle);
+ if (!joydev->open++ && joydev->exist) {
+ error = input_open_device(&joydev->handle);
+ if (error) {
+ list_del(&client->node);
+ kfree(client);
+ return error;
+ }
+ }
+ file->private_data = client;
return 0;
}
-static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
- struct joydev_list *list = file->private_data;
- struct joydev *joydev = list->joydev;
+ struct joydev_client *client = file->private_data;
+ struct joydev *joydev = client->joydev;
struct input_dev *input = joydev->handle.dev;
int retval = 0;
- if (!list->joydev->exist)
+ if (!joydev->exist)
return -ENODEV;
if (count < sizeof(struct js_event))
@@ -217,56 +229,55 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
return -EFAULT;
- list->startup = 0;
- list->tail = list->head;
+ client->startup = 0;
+ client->tail = client->head;
return sizeof(struct JS_DATA_TYPE);
}
- if (list->startup == joydev->nabs + joydev->nkey &&
- list->head == list->tail && (file->f_flags & O_NONBLOCK))
+ if (client->startup == joydev->nabs + joydev->nkey &&
+ client->head == client->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
- retval = wait_event_interruptible(list->joydev->wait,
- !list->joydev->exist ||
- list->startup < joydev->nabs + joydev->nkey ||
- list->head != list->tail);
-
+ retval = wait_event_interruptible(joydev->wait,
+ !joydev->exist ||
+ client->startup < joydev->nabs + joydev->nkey ||
+ client->head != client->tail);
if (retval)
return retval;
- if (!list->joydev->exist)
+ if (!joydev->exist)
return -ENODEV;
- while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
+ while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
struct js_event event;
event.time = jiffies_to_msecs(jiffies);
- if (list->startup < joydev->nkey) {
+ if (client->startup < joydev->nkey) {
event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
- event.number = list->startup;
+ event.number = client->startup;
event.value = !!test_bit(joydev->keypam[event.number], input->key);
} else {
event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
- event.number = list->startup - joydev->nkey;
+ event.number = client->startup - joydev->nkey;
event.value = joydev->abs[event.number];
}
if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
return -EFAULT;
- list->startup++;
+ client->startup++;
retval += sizeof(struct js_event);
}
- while (list->head != list->tail && retval + sizeof(struct js_event) <= count) {
+ while (client->head != client->tail && retval + sizeof(struct js_event) <= count) {
- if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event)))
+ if (copy_to_user(buf + retval, client->buffer + client->tail, sizeof(struct js_event)))
return -EFAULT;
- list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
+ client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
retval += sizeof(struct js_event);
}
@@ -276,11 +287,12 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
/* No kernel lock - fine */
static unsigned int joydev_poll(struct file *file, poll_table *wait)
{
- struct joydev_list *list = file->private_data;
+ struct joydev_client *client = file->private_data;
+ struct joydev *joydev = client->joydev;
- poll_wait(file, &list->joydev->wait, wait);
- return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ?
- (POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR));
+ poll_wait(file, &joydev->wait, wait);
+ return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ?
+ (POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR));
}
static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp)
@@ -374,8 +386,8 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u
#ifdef CONFIG_COMPAT
static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct joydev_list *list = file->private_data;
- struct joydev *joydev = list->joydev;
+ struct joydev_client *client = file->private_data;
+ struct joydev *joydev = client->joydev;
void __user *argp = (void __user *)arg;
s32 tmp32;
struct JS_DATA_SAVE_TYPE_32 ds32;
@@ -428,8 +440,8 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct joydev_list *list = file->private_data;
- struct joydev *joydev = list->joydev;
+ struct joydev_client *client = file->private_data;
+ struct joydev *joydev = client->joydev;
void __user *argp = (void __user *)arg;
if (!joydev->exist)
@@ -465,23 +477,26 @@ static const struct file_operations joydev_fops = {
.fasync = joydev_fasync,
};
-static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
+static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct joydev *joydev;
struct class_device *cdev;
+ dev_t devt;
int i, j, t, minor;
+ int error;
for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
if (minor == JOYDEV_MINORS) {
printk(KERN_ERR "joydev: no more free joydev devices\n");
- return NULL;
+ return -ENFILE;
}
- if (!(joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL)))
- return NULL;
+ joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL);
+ if (!joydev)
+ return -ENOMEM;
- INIT_LIST_HEAD(&joydev->list);
+ INIT_LIST_HEAD(&joydev->client_list);
init_waitqueue_head(&joydev->wait);
joydev->minor = minor;
@@ -534,31 +549,54 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct
joydev_table[minor] = joydev;
- cdev = class_device_create(&input_class, &dev->cdev,
- MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor),
- dev->cdev.dev, joydev->name);
+ devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor),
+
+ cdev = class_device_create(&input_class, &dev->cdev, devt,
+ dev->cdev.dev, joydev->name);
+ if (IS_ERR(cdev)) {
+ error = PTR_ERR(cdev);
+ goto err_free_joydev;
+ }
/* temporary symlink to keep userspace happy */
- sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
- joydev->name);
+ error = sysfs_create_link(&input_class.subsys.kobj,
+ &cdev->kobj, joydev->name);
+ if (error)
+ goto err_cdev_destroy;
+
+ error = input_register_handle(&joydev->handle);
+ if (error)
+ goto err_remove_link;
+
+ return 0;
- return &joydev->handle;
+ err_remove_link:
+ sysfs_remove_link(&input_class.subsys.kobj, joydev->name);
+ err_cdev_destroy:
+ class_device_destroy(&input_class, devt);
+ err_free_joydev:
+ joydev_table[minor] = NULL;
+ kfree(joydev);
+ return error;
}
+
static void joydev_disconnect(struct input_handle *handle)
{
struct joydev *joydev = handle->private;
- struct joydev_list *list;
+ struct joydev_client *client;
+
+ input_unregister_handle(handle);
- sysfs_remove_link(&input_class.subsys.kset.kobj, joydev->name);
+ sysfs_remove_link(&input_class.subsys.kobj, joydev->name);
class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor));
joydev->exist = 0;
if (joydev->open) {
input_close_device(handle);
wake_up_interruptible(&joydev->wait);
- list_for_each_entry(list, &joydev->list, node)
- kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+ list_for_each_entry(client, &joydev->client_list, node)
+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
} else
joydev_free(joydev);
}
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 271263443c3..82f563e24fd 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -2,7 +2,7 @@
# Joystick driver configuration
#
menuconfig INPUT_JOYSTICK
- bool "Joysticks"
+ bool "Joysticks/Gamepads"
help
If you have a joystick, 6dof controller, gamepad, steering wheel,
weapon control system or something like that you can say Y here
@@ -196,7 +196,7 @@ config JOYSTICK_TWIDJOY
config JOYSTICK_DB9
tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads"
depends on PARPORT
- ---help---
+ help
Say Y here if you have a Sega Master System gamepad, Sega Genesis
gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
Commodore, Amstrad CPC joystick connected to your parallel port.
@@ -253,4 +253,18 @@ config JOYSTICK_JOYDUMP
To compile this driver as a module, choose M here: the
module will be called joydump.
+config JOYSTICK_XPAD
+ tristate "X-Box gamepad support"
+ select USB
+ help
+ Say Y here if you want to use the X-Box pad with your computer.
+ Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV)
+ and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well.
+
+ For information about how to connect the X-Box pad to USB, see
+ <file:Documentation/input/xpad.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xpad.
+
endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 5231f6ff75b..e855abb0cc5 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -26,5 +26,6 @@ obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o
obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o
obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o
obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
+obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/
diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c
index b11a4bbc84c..ff701ab10d7 100644
--- a/drivers/input/joystick/a3d.c
+++ b/drivers/input/joystick/a3d.c
@@ -241,7 +241,7 @@ static void a3d_adc_close(struct gameport *gameport)
static int a3d_open(struct input_dev *dev)
{
- struct a3d *a3d = dev->private;
+ struct a3d *a3d = input_get_drvdata(dev);
gameport_start_polling(a3d->gameport);
return 0;
@@ -253,7 +253,7 @@ static int a3d_open(struct input_dev *dev)
static void a3d_close(struct input_dev *dev)
{
- struct a3d *a3d = dev->private;
+ struct a3d *a3d = input_get_drvdata(dev);
gameport_stop_polling(a3d->gameport);
}
@@ -314,11 +314,12 @@ static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv)
input_dev->id.vendor = GAMEPORT_ID_VENDOR_MADCATZ;
input_dev->id.product = a3d->mode;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &gameport->dev;
- input_dev->private = a3d;
+ input_dev->dev.parent = &gameport->dev;
input_dev->open = a3d_open;
input_dev->close = a3d_close;
+ input_set_drvdata(input_dev, a3d);
+
if (a3d->mode == A3D_MODE_PXL) {
int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER };
diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c
index 6279ced8a35..28140c4a110 100644
--- a/drivers/input/joystick/adi.c
+++ b/drivers/input/joystick/adi.c
@@ -290,7 +290,7 @@ static void adi_poll(struct gameport *gameport)
static int adi_open(struct input_dev *dev)
{
- struct adi_port *port = dev->private;
+ struct adi_port *port = input_get_drvdata(dev);
gameport_start_polling(port->gameport);
return 0;
@@ -302,7 +302,7 @@ static int adi_open(struct input_dev *dev)
static void adi_close(struct input_dev *dev)
{
- struct adi_port *port = dev->private;
+ struct adi_port *port = input_get_drvdata(dev);
gameport_stop_polling(port->gameport);
}
@@ -424,8 +424,9 @@ static int adi_init_input(struct adi *adi, struct adi_port *port, int half)
input_dev->id.vendor = GAMEPORT_ID_VENDOR_LOGITECH;
input_dev->id.product = adi->id;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &port->gameport->dev;
- input_dev->private = port;
+ input_dev->dev.parent = &port->gameport->dev;
+
+ input_set_drvdata(input_dev, port);
input_dev->open = adi_open;
input_dev->close = adi_close;
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index 51f1e4bfff3..bdd157c1ebf 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -53,7 +53,7 @@ MODULE_LICENSE("GPL");
#define ANALOG_PORTS 16
static char *js[ANALOG_PORTS];
-static int js_nargs;
+static unsigned int js_nargs;
static int analog_options[ANALOG_PORTS];
module_param_array_named(map, js, charp, &js_nargs, 0);
MODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities");
@@ -343,7 +343,7 @@ static void analog_poll(struct gameport *gameport)
static int analog_open(struct input_dev *dev)
{
- struct analog_port *port = dev->private;
+ struct analog_port *port = input_get_drvdata(dev);
gameport_start_polling(port->gameport);
return 0;
@@ -355,7 +355,7 @@ static int analog_open(struct input_dev *dev)
static void analog_close(struct input_dev *dev)
{
- struct analog_port *port = dev->private;
+ struct analog_port *port = input_get_drvdata(dev);
gameport_stop_polling(port->gameport);
}
@@ -449,10 +449,13 @@ static int analog_init_device(struct analog_port *port, struct analog *analog, i
input_dev->id.vendor = GAMEPORT_ID_VENDOR_ANALOG;
input_dev->id.product = analog->mask >> 4;
input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &port->gameport->dev;
+
+ input_set_drvdata(input_dev, port);
input_dev->open = analog_open;
input_dev->close = analog_close;
- input_dev->private = port;
+
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = j = 0; i < 4; i++)
diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c
index 034ec39c251..d3352a849b8 100644
--- a/drivers/input/joystick/cobra.c
+++ b/drivers/input/joystick/cobra.c
@@ -142,7 +142,7 @@ static void cobra_poll(struct gameport *gameport)
static int cobra_open(struct input_dev *dev)
{
- struct cobra *cobra = dev->private;
+ struct cobra *cobra = input_get_drvdata(dev);
gameport_start_polling(cobra->gameport);
return 0;
@@ -150,7 +150,7 @@ static int cobra_open(struct input_dev *dev)
static void cobra_close(struct input_dev *dev)
{
- struct cobra *cobra = dev->private;
+ struct cobra *cobra = input_get_drvdata(dev);
gameport_stop_polling(cobra->gameport);
}
@@ -211,8 +211,9 @@ static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv)
input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE;
input_dev->id.product = 0x0008;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &gameport->dev;
- input_dev->private = cobra;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, cobra);
input_dev->open = cobra_open;
input_dev->close = cobra_close;
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
index b41bd2eb37d..86ad1027e12 100644
--- a/drivers/input/joystick/db9.c
+++ b/drivers/input/joystick/db9.c
@@ -46,17 +46,17 @@ MODULE_LICENSE("GPL");
struct db9_config {
int args[2];
- int nargs;
+ unsigned int nargs;
};
#define DB9_MAX_PORTS 3
-static struct db9_config db9[DB9_MAX_PORTS] __initdata;
+static struct db9_config db9_cfg[DB9_MAX_PORTS] __initdata;
-module_param_array_named(dev, db9[0].args, int, &db9[0].nargs, 0);
+module_param_array_named(dev, db9_cfg[0].args, int, &db9_cfg[0].nargs, 0);
MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)");
-module_param_array_named(dev2, db9[1].args, int, &db9[0].nargs, 0);
+module_param_array_named(dev2, db9_cfg[1].args, int, &db9_cfg[0].nargs, 0);
MODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)");
-module_param_array_named(dev3, db9[2].args, int, &db9[2].nargs, 0);
+module_param_array_named(dev3, db9_cfg[2].args, int, &db9_cfg[2].nargs, 0);
MODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)");
#define DB9_ARG_PARPORT 0
@@ -518,7 +518,7 @@ static void db9_timer(unsigned long private)
static int db9_open(struct input_dev *dev)
{
- struct db9 *db9 = dev->private;
+ struct db9 *db9 = input_get_drvdata(dev);
struct parport *port = db9->pd->port;
int err;
@@ -542,7 +542,7 @@ static int db9_open(struct input_dev *dev)
static void db9_close(struct input_dev *dev)
{
- struct db9 *db9 = dev->private;
+ struct db9 *db9 = input_get_drvdata(dev);
struct parport *port = db9->pd->port;
mutex_lock(&db9->mutex);
@@ -625,7 +625,8 @@ static struct db9 __init *db9_probe(int parport, int mode)
input_dev->id.vendor = 0x0002;
input_dev->id.product = mode;
input_dev->id.version = 0x0100;
- input_dev->private = db9;
+
+ input_set_drvdata(input_dev, db9);
input_dev->open = db9_open;
input_dev->close = db9_close;
@@ -679,17 +680,17 @@ static int __init db9_init(void)
int err = 0;
for (i = 0; i < DB9_MAX_PORTS; i++) {
- if (db9[i].nargs == 0 || db9[i].args[DB9_ARG_PARPORT] < 0)
+ if (db9_cfg[i].nargs == 0 || db9_cfg[i].args[DB9_ARG_PARPORT] < 0)
continue;
- if (db9[i].nargs < 2) {
+ if (db9_cfg[i].nargs < 2) {
printk(KERN_ERR "db9.c: Device type must be specified.\n");
err = -EINVAL;
break;
}
- db9_base[i] = db9_probe(db9[i].args[DB9_ARG_PARPORT],
- db9[i].args[DB9_ARG_MODE]);
+ db9_base[i] = db9_probe(db9_cfg[i].args[DB9_ARG_PARPORT],
+ db9_cfg[i].args[DB9_ARG_MODE]);
if (IS_ERR(db9_base[i])) {
err = PTR_ERR(db9_base[i]);
break;
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index 711e4b3e9e6..1a452e0e5f2 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -48,16 +48,16 @@ MODULE_LICENSE("GPL");
struct gc_config {
int args[GC_MAX_DEVICES + 1];
- int nargs;
+ unsigned int nargs;
};
-static struct gc_config gc[GC_MAX_PORTS] __initdata;
+static struct gc_config gc_cfg[GC_MAX_PORTS] __initdata;
-module_param_array_named(map, gc[0].args, int, &gc[0].nargs, 0);
+module_param_array_named(map, gc_cfg[0].args, int, &gc_cfg[0].nargs, 0);
MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)");
-module_param_array_named(map2, gc[1].args, int, &gc[1].nargs, 0);
+module_param_array_named(map2, gc_cfg[1].args, int, &gc_cfg[1].nargs, 0);
MODULE_PARM_DESC(map2, "Describes second set of devices");
-module_param_array_named(map3, gc[2].args, int, &gc[2].nargs, 0);
+module_param_array_named(map3, gc_cfg[2].args, int, &gc_cfg[2].nargs, 0);
MODULE_PARM_DESC(map3, "Describes third set of devices");
/* see also gs_psx_delay parameter in PSX support section */
@@ -591,7 +591,7 @@ static void gc_timer(unsigned long private)
static int gc_open(struct input_dev *dev)
{
- struct gc *gc = dev->private;
+ struct gc *gc = input_get_drvdata(dev);
int err;
err = mutex_lock_interruptible(&gc->mutex);
@@ -610,7 +610,7 @@ static int gc_open(struct input_dev *dev)
static void gc_close(struct input_dev *dev)
{
- struct gc *gc = dev->private;
+ struct gc *gc = input_get_drvdata(dev);
mutex_lock(&gc->mutex);
if (!--gc->used) {
@@ -646,7 +646,8 @@ static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type)
input_dev->id.vendor = 0x0001;
input_dev->id.product = pad_type;
input_dev->id.version = 0x0100;
- input_dev->private = gc;
+
+ input_set_drvdata(input_dev, gc);
input_dev->open = gc_open;
input_dev->close = gc_close;
@@ -809,16 +810,17 @@ static int __init gc_init(void)
int err = 0;
for (i = 0; i < GC_MAX_PORTS; i++) {
- if (gc[i].nargs == 0 || gc[i].args[0] < 0)
+ if (gc_cfg[i].nargs == 0 || gc_cfg[i].args[0] < 0)
continue;
- if (gc[i].nargs < 2) {
+ if (gc_cfg[i].nargs < 2) {
printk(KERN_ERR "gamecon.c: at least one device must be specified\n");
err = -EINVAL;
break;
}
- gc_base[i] = gc_probe(gc[i].args[0], gc[i].args + 1, gc[i].nargs - 1);
+ gc_base[i] = gc_probe(gc_cfg[i].args[0],
+ gc_cfg[i].args + 1, gc_cfg[i].nargs - 1);
if (IS_ERR(gc_base[i])) {
err = PTR_ERR(gc_base[i]);
break;
diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c
index bacbab5d1b6..d514aebf755 100644
--- a/drivers/input/joystick/gf2k.c
+++ b/drivers/input/joystick/gf2k.c
@@ -220,7 +220,7 @@ static void gf2k_poll(struct gameport *gameport)
static int gf2k_open(struct input_dev *dev)
{
- struct gf2k *gf2k = dev->private;
+ struct gf2k *gf2k = input_get_drvdata(dev);
gameport_start_polling(gf2k->gameport);
return 0;
@@ -228,7 +228,7 @@ static int gf2k_open(struct input_dev *dev)
static void gf2k_close(struct input_dev *dev)
{
- struct gf2k *gf2k = dev->private;
+ struct gf2k *gf2k = input_get_drvdata(dev);
gameport_stop_polling(gf2k->gameport);
}
@@ -308,11 +308,13 @@ static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv)
input_dev->id.vendor = GAMEPORT_ID_VENDOR_GENIUS;
input_dev->id.product = gf2k->id;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &gameport->dev;
- input_dev->private = gf2k;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, gf2k);
input_dev->open = gf2k_open;
input_dev->close = gf2k_close;
+
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
for (i = 0; i < gf2k_axes[gf2k->id]; i++)
diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c
index 17a90c436de..73eb5ab6f14 100644
--- a/drivers/input/joystick/grip.c
+++ b/drivers/input/joystick/grip.c
@@ -285,7 +285,7 @@ static void grip_poll(struct gameport *gameport)
static int grip_open(struct input_dev *dev)
{
- struct grip *grip = dev->private;
+ struct grip *grip = input_get_drvdata(dev);
gameport_start_polling(grip->gameport);
return 0;
@@ -293,7 +293,7 @@ static int grip_open(struct input_dev *dev)
static void grip_close(struct input_dev *dev)
{
- struct grip *grip = dev->private;
+ struct grip *grip = input_get_drvdata(dev);
gameport_stop_polling(grip->gameport);
}
@@ -363,8 +363,9 @@ static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
input_dev->id.product = grip->mode[i];
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &gameport->dev;
- input_dev->private = grip;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, grip);
input_dev->open = grip_open;
input_dev->close = grip_close;
diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c
index 8120a9c4077..555319e6378 100644
--- a/drivers/input/joystick/grip_mp.c
+++ b/drivers/input/joystick/grip_mp.c
@@ -562,7 +562,7 @@ static void grip_poll(struct gameport *gameport)
static int grip_open(struct input_dev *dev)
{
- struct grip_mp *grip = dev->private;
+ struct grip_mp *grip = input_get_drvdata(dev);
gameport_start_polling(grip->gameport);
return 0;
@@ -574,9 +574,9 @@ static int grip_open(struct input_dev *dev)
static void grip_close(struct input_dev *dev)
{
- struct grip_mp *grip = dev->private;
+ struct grip_mp *grip = input_get_drvdata(dev);
- gameport_start_polling(grip->gameport);
+ gameport_stop_polling(grip->gameport);
}
/*
@@ -599,8 +599,9 @@ static int register_slot(int slot, struct grip_mp *grip)
input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
input_dev->id.product = 0x0100 + port->mode;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &grip->gameport->dev;
- input_dev->private = grip;
+ input_dev->dev.parent = &grip->gameport->dev;
+
+ input_set_drvdata(input_dev, grip);
input_dev->open = grip_open;
input_dev->close = grip_close;
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
index dbc5d92858b..d4e8073caf2 100644
--- a/drivers/input/joystick/guillemot.c
+++ b/drivers/input/joystick/guillemot.c
@@ -156,7 +156,7 @@ static void guillemot_poll(struct gameport *gameport)
static int guillemot_open(struct input_dev *dev)
{
- struct guillemot *guillemot = dev->private;
+ struct guillemot *guillemot = input_get_drvdata(dev);
gameport_start_polling(guillemot->gameport);
return 0;
@@ -168,7 +168,7 @@ static int guillemot_open(struct input_dev *dev)
static void guillemot_close(struct input_dev *dev)
{
- struct guillemot *guillemot = dev->private;
+ struct guillemot *guillemot = input_get_drvdata(dev);
gameport_stop_polling(guillemot->gameport);
}
@@ -231,8 +231,9 @@ static int guillemot_connect(struct gameport *gameport, struct gameport_driver *
input_dev->id.vendor = GAMEPORT_ID_VENDOR_GUILLEMOT;
input_dev->id.product = guillemot_type[i].id;
input_dev->id.version = (int)data[14] << 8 | data[15];
- input_dev->cdev.dev = &gameport->dev;
- input_dev->private = guillemot;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, guillemot);
input_dev->open = guillemot_open;
input_dev->close = guillemot_close;
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
index 8fb0c19cc60..f2a4381d0ab 100644
--- a/drivers/input/joystick/iforce/iforce-ff.c
+++ b/drivers/input/joystick/iforce/iforce-ff.c
@@ -2,7 +2,7 @@
* $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
- * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
@@ -205,7 +205,7 @@ static int need_condition_modifier(struct ff_effect *old, struct ff_effect *new)
int i;
if (new->type != FF_SPRING && new->type != FF_FRICTION) {
- printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n");
+ warn("bad effect type in need_condition_modifier");
return 0;
}
@@ -227,7 +227,7 @@ static int need_condition_modifier(struct ff_effect *old, struct ff_effect *new)
static int need_magnitude_modifier(struct ff_effect *old, struct ff_effect *effect)
{
if (effect->type != FF_CONSTANT) {
- printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
+ warn("bad effect type in need_envelope_modifier");
return 0;
}
@@ -258,7 +258,7 @@ static int need_envelope_modifier(struct ff_effect *old, struct ff_effect *effec
break;
default:
- printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
+ warn("bad effect type in need_envelope_modifier");
}
return 0;
@@ -271,7 +271,7 @@ static int need_envelope_modifier(struct ff_effect *old, struct ff_effect *effec
static int need_period_modifier(struct ff_effect *old, struct ff_effect *new)
{
if (new->type != FF_PERIODIC) {
- printk(KERN_WARNING "iforce.c: bad effect type in need_period_modifier\n");
+ warn("bad effect type in need_period_modifier");
return 0;
}
return (old->u.periodic.period != new->u.periodic.period
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
index 3393a37fec3..fb129c479a6 100644
--- a/drivers/input/joystick/iforce/iforce-main.c
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -2,7 +2,7 @@
* $Id: iforce-main.c,v 1.19 2002/07/07 10:22:50 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
- * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
@@ -29,7 +29,7 @@
#include "iforce.h"
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <deneux@ifrance.com>");
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
MODULE_LICENSE("GPL");
@@ -220,7 +220,7 @@ static void iforce_release(struct input_dev *dev)
/* Check: no effects should be present in memory */
for (i = 0; i < dev->ff->max_effects; i++) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) {
- printk(KERN_WARNING "iforce_release: Device still owns effects\n");
+ warn("iforce_release: Device still owns effects");
break;
}
}
@@ -232,7 +232,7 @@ static void iforce_release(struct input_dev *dev)
switch (iforce->bus) {
#ifdef CONFIG_JOYSTICK_IFORCE_USB
case IFORCE_USB:
- usb_unlink_urb(iforce->irq);
+ usb_kill_urb(iforce->irq);
/* The device was unplugged before the file
* was released */
@@ -287,13 +287,13 @@ int iforce_init_device(struct iforce *iforce)
#ifdef CONFIG_JOYSTICK_IFORCE_USB
case IFORCE_USB:
input_dev->id.bustype = BUS_USB;
- input_dev->cdev.dev = &iforce->usbdev->dev;
+ input_dev->dev.parent = &iforce->usbdev->dev;
break;
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_232
case IFORCE_232:
input_dev->id.bustype = BUS_RS232;
- input_dev->cdev.dev = &iforce->serio->dev;
+ input_dev->dev.parent = &iforce->serio->dev;
break;
#endif
}
@@ -324,7 +324,7 @@ int iforce_init_device(struct iforce *iforce)
break;
if (i == 20) { /* 5 seconds */
- printk(KERN_ERR "iforce-main.c: Timeout waiting for response from device.\n");
+ err("Timeout waiting for response from device.");
error = -ENODEV;
goto fail;
}
@@ -336,26 +336,26 @@ int iforce_init_device(struct iforce *iforce)
if (!iforce_get_id_packet(iforce, "M"))
input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1];
else
- printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet M\n");
+ warn("Device does not respond to id packet M");
if (!iforce_get_id_packet(iforce, "P"))
input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1];
else
- printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet P\n");
+ warn("Device does not respond to id packet P");
if (!iforce_get_id_packet(iforce, "B"))
iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
else
- printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n");
+ warn("Device does not respond to id packet B");
if (!iforce_get_id_packet(iforce, "N"))
ff_effects = iforce->edata[1];
else
- printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n");
+ warn("Device does not respond to id packet N");
/* Check if the device can store more effects than the driver can really handle */
if (ff_effects > IFORCE_EFFECTS_MAX) {
- printk(KERN_WARNING "iforce: Limiting number of effects to %d (device reports %d)\n",
+ warn("Limiting number of effects to %d (device reports %d)",
IFORCE_EFFECTS_MAX, ff_effects);
ff_effects = IFORCE_EFFECTS_MAX;
}
@@ -457,8 +457,6 @@ int iforce_init_device(struct iforce *iforce)
if (error)
goto fail;
- printk(KERN_DEBUG "iforce->dev->open = %p\n", iforce->dev->open);
-
return 0;
fail: input_free_device(input_dev);
diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c
index 808f05932a6..21c4e13d3a5 100644
--- a/drivers/input/joystick/iforce/iforce-packets.c
+++ b/drivers/input/joystick/iforce/iforce-packets.c
@@ -2,7 +2,7 @@
* $Id: iforce-packets.c,v 1.16 2002/07/07 10:22:50 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
- * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
@@ -39,10 +39,10 @@ void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data)
{
int i;
- printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
+ printk(KERN_DEBUG __FILE__ ": %s cmd = %04x, data = ", msg, cmd);
for (i = 0; i < LO(cmd); i++)
printk("%02x ", data[i]);
- printk(")\n");
+ printk("\n");
}
/*
@@ -65,8 +65,9 @@ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
head = iforce->xmit.head;
tail = iforce->xmit.tail;
+
if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) {
- printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n");
+ warn("not enough space in xmit buffer to send new packet");
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
return -1;
}
@@ -126,8 +127,6 @@ int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
{
unsigned char data[3];
-printk(KERN_DEBUG "iforce-packets.c: control_playback %d %d\n", id, value);
-
data[0] = LO(id);
data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
data[2] = LO(value);
@@ -151,7 +150,7 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
return 0;
}
}
- printk(KERN_WARNING "iforce-packets.c: unused effect %04x updated !!!\n", addr);
+ warn("unused effect %04x updated !!!", addr);
return -1;
}
@@ -162,7 +161,7 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
static int being_used = 0;
if (being_used)
- printk(KERN_WARNING "iforce-packets.c: re-entrant call to iforce_process %d\n", being_used);
+ warn("re-entrant call to iforce_process %d", being_used);
being_used++;
#ifdef CONFIG_JOYSTICK_IFORCE_232
@@ -266,7 +265,7 @@ int iforce_get_id_packet(struct iforce *iforce, char *packet)
return -1;
}
#else
- printk(KERN_ERR "iforce_get_id_packet: iforce->bus = USB!\n");
+ err("iforce_get_id_packet: iforce->bus = USB!");
#endif
break;
@@ -284,13 +283,12 @@ int iforce_get_id_packet(struct iforce *iforce, char *packet)
return -1;
}
#else
- printk(KERN_ERR "iforce_get_id_packet: iforce->bus = SERIO!\n");
+ err("iforce_get_id_packet: iforce->bus = SERIO!");
#endif
break;
default:
- printk(KERN_ERR "iforce_get_id_packet: iforce->bus = %d\n",
- iforce->bus);
+ err("iforce_get_id_packet: iforce->bus = %d", iforce->bus);
break;
}
diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c
index ec4be535f48..7b4bc19cef2 100644
--- a/drivers/input/joystick/iforce/iforce-serio.c
+++ b/drivers/input/joystick/iforce/iforce-serio.c
@@ -2,7 +2,7 @@
* $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
- * Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
+ * Copyright (c) 2001, 2007 Johann Deneux <johann.deneux@gmail.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c
index 80cdebcbcb9..750099d8e3c 100644
--- a/drivers/input/joystick/iforce/iforce-usb.c
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -2,7 +2,7 @@
* $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
- * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
@@ -65,7 +65,7 @@ void iforce_usb_xmit(struct iforce *iforce)
XMIT_INC(iforce->xmit.tail, n);
if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
- printk(KERN_WARNING "iforce-usb.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n);
+ warn("usb_submit_urb failed %d\n", n);
}
/* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
@@ -110,7 +110,7 @@ static void iforce_usb_out(struct urb *urb)
struct iforce *iforce = urb->context;
if (urb->status) {
- printk(KERN_DEBUG "iforce_usb_out: urb->status %d, exiting", urb->status);
+ dbg("urb->status %d, exiting", urb->status);
return;
}
@@ -190,10 +190,9 @@ fail:
/* Called by iforce_delete() */
void iforce_usb_delete(struct iforce* iforce)
{
- usb_unlink_urb(iforce->irq);
-/* Is it ok to unlink those ? */
- usb_unlink_urb(iforce->out);
- usb_unlink_urb(iforce->ctrl);
+ usb_kill_urb(iforce->irq);
+ usb_kill_urb(iforce->out);
+ usb_kill_urb(iforce->ctrl);
usb_free_urb(iforce->irq);
usb_free_urb(iforce->out);
diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h
index ffaeaefa1a4..40a853ac21c 100644
--- a/drivers/input/joystick/iforce/iforce.h
+++ b/drivers/input/joystick/iforce/iforce.h
@@ -2,7 +2,7 @@
* $Id: iforce.h,v 1.13 2002/07/07 10:22:50 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
- * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
*
* USB/RS232 I-Force joysticks and wheels.
*/
@@ -124,7 +124,7 @@ struct iforce {
/* Buffer used for asynchronous sending of bytes to the device */
struct circ_buf xmit;
unsigned char xmit_data[XMIT_SIZE];
- long xmit_flags[1];
+ unsigned long xmit_flags[1];
/* Force Feedback */
wait_queue_head_t wait;
diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c
index fec8b3d0967..1aec1e9d7c5 100644
--- a/drivers/input/joystick/interact.c
+++ b/drivers/input/joystick/interact.c
@@ -185,7 +185,7 @@ static void interact_poll(struct gameport *gameport)
static int interact_open(struct input_dev *dev)
{
- struct interact *interact = dev->private;
+ struct interact *interact = input_get_drvdata(dev);
gameport_start_polling(interact->gameport);
return 0;
@@ -197,7 +197,7 @@ static int interact_open(struct input_dev *dev)
static void interact_close(struct input_dev *dev)
{
- struct interact *interact = dev->private;
+ struct interact *interact = input_get_drvdata(dev);
gameport_stop_polling(interact->gameport);
}
@@ -262,7 +262,9 @@ static int interact_connect(struct gameport *gameport, struct gameport_driver *d
input_dev->id.vendor = GAMEPORT_ID_VENDOR_INTERACT;
input_dev->id.product = interact_type[i].id;
input_dev->id.version = 0x0100;
- input_dev->private = interact;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, interact);
input_dev->open = interact_open;
input_dev->close = interact_close;
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
index 4112789f119..b35604ee43a 100644
--- a/drivers/input/joystick/magellan.c
+++ b/drivers/input/joystick/magellan.c
@@ -168,8 +168,7 @@ static int magellan_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_MAGELLAN;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = magellan;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c
index e58b22c018e..2adf73f63c9 100644
--- a/drivers/input/joystick/sidewinder.c
+++ b/drivers/input/joystick/sidewinder.c
@@ -509,7 +509,7 @@ static void sw_poll(struct gameport *gameport)
static int sw_open(struct input_dev *dev)
{
- struct sw *sw = dev->private;
+ struct sw *sw = input_get_drvdata(dev);
gameport_start_polling(sw->gameport);
return 0;
@@ -517,7 +517,7 @@ static int sw_open(struct input_dev *dev)
static void sw_close(struct input_dev *dev)
{
- struct sw *sw = dev->private;
+ struct sw *sw = input_get_drvdata(dev);
gameport_stop_polling(sw->gameport);
}
@@ -751,8 +751,9 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
input_dev->id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT;
input_dev->id.product = sw->type;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &gameport->dev;
- input_dev->private = sw;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, sw);
input_dev->open = sw_open;
input_dev->close = sw_close;
diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c
index 08bf113e62e..abb7c4cf54a 100644
--- a/drivers/input/joystick/spaceball.c
+++ b/drivers/input/joystick/spaceball.c
@@ -226,8 +226,7 @@ static int spaceball_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_SPACEBALL;
input_dev->id.product = id;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = spaceball;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c
index c9c79211af7..c4937f1e837 100644
--- a/drivers/input/joystick/spaceorb.c
+++ b/drivers/input/joystick/spaceorb.c
@@ -183,8 +183,7 @@ static int spaceorb_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_SPACEORB;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = spaceorb;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
index ecb0916215f..8581ee991d4 100644
--- a/drivers/input/joystick/stinger.c
+++ b/drivers/input/joystick/stinger.c
@@ -154,8 +154,7 @@ static int stinger_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_STINGER;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = stinger;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_A)] = BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_X) |
diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c
index bb23ed2a04a..3b36ee04f72 100644
--- a/drivers/input/joystick/tmdc.c
+++ b/drivers/input/joystick/tmdc.c
@@ -265,7 +265,7 @@ static void tmdc_poll(struct gameport *gameport)
static int tmdc_open(struct input_dev *dev)
{
- struct tmdc *tmdc = dev->private;
+ struct tmdc *tmdc = input_get_drvdata(dev);
gameport_start_polling(tmdc->gameport);
return 0;
@@ -273,7 +273,7 @@ static int tmdc_open(struct input_dev *dev)
static void tmdc_close(struct input_dev *dev)
{
- struct tmdc *tmdc = dev->private;
+ struct tmdc *tmdc = input_get_drvdata(dev);
gameport_stop_polling(tmdc->gameport);
}
@@ -326,8 +326,9 @@ static int tmdc_setup_port(struct tmdc *tmdc, int idx, unsigned char *data)
input_dev->id.vendor = GAMEPORT_ID_VENDOR_THRUSTMASTER;
input_dev->id.product = model->id;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &tmdc->gameport->dev;
- input_dev->private = tmdc;
+ input_dev->dev.parent = &tmdc->gameport->dev;
+
+ input_set_drvdata(input_dev, tmdc);
input_dev->open = tmdc_open;
input_dev->close = tmdc_close;
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
index 037d3487fcc..8381c6f1437 100644
--- a/drivers/input/joystick/turbografx.c
+++ b/drivers/input/joystick/turbografx.c
@@ -48,16 +48,16 @@ MODULE_LICENSE("GPL");
struct tgfx_config {
int args[TGFX_MAX_DEVICES + 1];
- int nargs;
+ unsigned int nargs;
};
-static struct tgfx_config tgfx[TGFX_MAX_PORTS] __initdata;
+static struct tgfx_config tgfx_cfg[TGFX_MAX_PORTS] __initdata;
-module_param_array_named(map, tgfx[0].args, int, &tgfx[0].nargs, 0);
+module_param_array_named(map, tgfx_cfg[0].args, int, &tgfx_cfg[0].nargs, 0);
MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>");
-module_param_array_named(map2, tgfx[1].args, int, &tgfx[1].nargs, 0);
+module_param_array_named(map2, tgfx_cfg[1].args, int, &tgfx_cfg[1].nargs, 0);
MODULE_PARM_DESC(map2, "Describes second set of devices");
-module_param_array_named(map3, tgfx[2].args, int, &tgfx[2].nargs, 0);
+module_param_array_named(map3, tgfx_cfg[2].args, int, &tgfx_cfg[2].nargs, 0);
MODULE_PARM_DESC(map3, "Describes third set of devices");
#define TGFX_REFRESH_TIME HZ/100 /* 10 ms */
@@ -122,7 +122,7 @@ static void tgfx_timer(unsigned long private)
static int tgfx_open(struct input_dev *dev)
{
- struct tgfx *tgfx = dev->private;
+ struct tgfx *tgfx = input_get_drvdata(dev);
int err;
err = mutex_lock_interruptible(&tgfx->sem);
@@ -141,7 +141,7 @@ static int tgfx_open(struct input_dev *dev)
static void tgfx_close(struct input_dev *dev)
{
- struct tgfx *tgfx = dev->private;
+ struct tgfx *tgfx = input_get_drvdata(dev);
mutex_lock(&tgfx->sem);
if (!--tgfx->used) {
@@ -224,7 +224,8 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs)
input_dev->id.product = n_buttons[i];
input_dev->id.version = 0x0100;
- input_dev->private = tgfx;
+ input_set_drvdata(input_dev, tgfx);
+
input_dev->open = tgfx_open;
input_dev->close = tgfx_close;
@@ -282,16 +283,18 @@ static int __init tgfx_init(void)
int err = 0;
for (i = 0; i < TGFX_MAX_PORTS; i++) {
- if (tgfx[i].nargs == 0 || tgfx[i].args[0] < 0)
+ if (tgfx_cfg[i].nargs == 0 || tgfx_cfg[i].args[0] < 0)
continue;
- if (tgfx[i].nargs < 2) {
+ if (tgfx_cfg[i].nargs < 2) {
printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n");
err = -EINVAL;
break;
}
- tgfx_base[i] = tgfx_probe(tgfx[i].args[0], tgfx[i].args + 1, tgfx[i].nargs - 1);
+ tgfx_base[i] = tgfx_probe(tgfx_cfg[i].args[0],
+ tgfx_cfg[i].args + 1,
+ tgfx_cfg[i].nargs - 1);
if (IS_ERR(tgfx_base[i])) {
err = PTR_ERR(tgfx_base[i]);
break;
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
index 9cf17d6ced8..c91504ec38e 100644
--- a/drivers/input/joystick/twidjoy.c
+++ b/drivers/input/joystick/twidjoy.c
@@ -205,11 +205,9 @@ static int twidjoy_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_TWIDJOY;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = twidjoy;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
- input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
input_set_abs_params(input_dev, ABS_X, -50, 50, 4, 4);
input_set_abs_params(input_dev, ABS_Y, -50, 50, 4, 4);
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
index 29d339acf43..4e85f72eefd 100644
--- a/drivers/input/joystick/warrior.c
+++ b/drivers/input/joystick/warrior.c
@@ -160,8 +160,7 @@ static int warrior_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_WARRIOR;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = warrior;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TRIGGER)] = BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_TOP2);
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
new file mode 100644
index 00000000000..8c8cd95a698
--- /dev/null
+++ b/drivers/input/joystick/xpad.c
@@ -0,0 +1,433 @@
+/*
+ * X-Box gamepad - v0.0.6
+ *
+ * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
+ * 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>,
+ * Steven Toth <steve@toth.demon.co.uk>,
+ * Franz Lehner <franz@caos.at>,
+ * Ivan Hawkes <blackhawk@ivanhawkes.com>
+ * 2005 Dominic Cerquetti <binary1230@yahoo.com>
+ * 2006 Adam Buchbinder <adam.buchbinder@gmail.com>
+ *
+ * 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
+ *
+ *
+ * This driver is based on:
+ * - information from http://euc.jp/periphs/xbox-controller.ja.html
+ * - the iForce driver drivers/char/joystick/iforce.c
+ * - the skeleton-driver drivers/usb/usb-skeleton.c
+ *
+ * Thanks to:
+ * - ITO Takayuki for providing essential xpad information on his website
+ * - Vojtech Pavlik - iforce driver / input subsystem
+ * - Greg Kroah-Hartman - usb-skeleton driver
+ * - XBOX Linux project - extra USB id's
+ *
+ * TODO:
+ * - fine tune axes (especially trigger axes)
+ * - fix "analog" buttons (reported as digital now)
+ * - get rumble working
+ * - need USB IDs for other dance pads
+ *
+ * History:
+ *
+ * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller"
+ *
+ * 2002-07-02 - 0.0.2 : basic working version
+ * - all axes and 9 of the 10 buttons work (german InterAct device)
+ * - the black button does not work
+ *
+ * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik
+ * - indentation fixes
+ * - usb + input init sequence fixes
+ *
+ * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3
+ * - verified the lack of HID and report descriptors
+ * - verified that ALL buttons WORK
+ * - fixed d-pad to axes mapping
+ *
+ * 2002-07-17 - 0.0.5 : simplified d-pad handling
+ *
+ * 2004-10-02 - 0.0.6 : DDR pad support
+ * - borrowed from the XBOX linux kernel
+ * - USB id's for commonly used dance pads are present
+ * - dance pads will map D-PAD to buttons, not axes
+ * - pass the module paramater 'dpad_to_buttons' to force
+ * the D-PAD to map to buttons if your pad is not detected
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_VERSION "v0.0.6"
+#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
+#define DRIVER_DESC "X-Box pad driver"
+
+#define XPAD_PKT_LEN 32
+
+/* xbox d-pads should map to buttons, as is required for DDR pads
+ but we map them to axes when possible to simplify things */
+#define MAP_DPAD_TO_BUTTONS 0
+#define MAP_DPAD_TO_AXES 1
+#define MAP_DPAD_UNKNOWN -1
+
+static int dpad_to_buttons;
+module_param(dpad_to_buttons, bool, S_IRUGO);
+MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
+
+static const struct xpad_device {
+ u16 idVendor;
+ u16 idProduct;
+ char *name;
+ u8 dpad_mapping;
+} xpad_device[] = {
+ { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES },
+ { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES },
+ { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES },
+ { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES },
+ { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS },
+ { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES },
+ { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES },
+ { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES },
+ { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES },
+ { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES },
+ { 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES },
+ { 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES },
+ { 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES },
+ { 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES },
+ { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS },
+ { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES },
+ { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS },
+ { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES },
+ { 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES },
+ { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES },
+ { 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES },
+ { 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES},
+ { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", MAP_DPAD_TO_AXES },
+ { 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES },
+ { 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES },
+ { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES },
+ { 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES },
+ { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES },
+ { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES },
+ { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS },
+ { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS },
+ { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES },
+ { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN }
+};
+
+static const signed short xpad_btn[] = {
+ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* "analog" buttons */
+ BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
+ -1 /* terminating entry */
+};
+
+/* only used if MAP_DPAD_TO_BUTTONS */
+static const signed short xpad_btn_pad[] = {
+ BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
+ BTN_0, BTN_1, /* d-pad up, down (XXX names??) */
+ -1 /* terminating entry */
+};
+
+static const signed short xpad_abs[] = {
+ ABS_X, ABS_Y, /* left stick */
+ ABS_RX, ABS_RY, /* right stick */
+ ABS_Z, ABS_RZ, /* triggers left/right */
+ -1 /* terminating entry */
+};
+
+/* only used if MAP_DPAD_TO_AXES */
+static const signed short xpad_abs_pad[] = {
+ ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
+ -1 /* terminating entry */
+};
+
+static struct usb_device_id xpad_table [] = {
+ { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
+ { }
+};
+
+MODULE_DEVICE_TABLE (usb, xpad_table);
+
+struct usb_xpad {
+ struct input_dev *dev; /* input device interface */
+ struct usb_device *udev; /* usb device */
+
+ struct urb *irq_in; /* urb for interrupt in report */
+ unsigned char *idata; /* input data */
+ dma_addr_t idata_dma;
+
+ char phys[65]; /* physical device path */
+
+ int dpad_mapping; /* map d-pad to buttons or to axes */
+};
+
+/*
+ * xpad_process_packet
+ *
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ *
+ * The used report descriptor was taken from ITO Takayukis website:
+ * http://euc.jp/periphs/xbox-controller.ja.html
+ */
+
+static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+{
+ struct input_dev *dev = xpad->dev;
+
+ /* left stick */
+ input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12]));
+ input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14]));
+
+ /* right stick */
+ input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16]));
+ input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18]));
+
+ /* triggers left/right */
+ input_report_abs(dev, ABS_Z, data[10]);
+ input_report_abs(dev, ABS_RZ, data[11]);
+
+ /* digital pad */
+ if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
+ input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
+ input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
+ } else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
+ input_report_key(dev, BTN_LEFT, data[2] & 0x04);
+ input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
+ input_report_key(dev, BTN_0, data[2] & 0x01); // up
+ input_report_key(dev, BTN_1, data[2] & 0x02); // down
+ }
+
+ /* start/back buttons and stick press left/right */
+ input_report_key(dev, BTN_START, data[2] & 0x10);
+ input_report_key(dev, BTN_BACK, data[2] & 0x20);
+ input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
+ input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
+
+ /* "analog" buttons A, B, X, Y */
+ input_report_key(dev, BTN_A, data[4]);
+ input_report_key(dev, BTN_B, data[5]);
+ input_report_key(dev, BTN_X, data[6]);
+ input_report_key(dev, BTN_Y, data[7]);
+
+ /* "analog" buttons black, white */
+ input_report_key(dev, BTN_C, data[8]);
+ input_report_key(dev, BTN_Z, data[9]);
+
+ input_sync(dev);
+}
+
+static void xpad_irq_in(struct urb *urb)
+{
+ struct usb_xpad *xpad = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ xpad_process_packet(xpad, 0, xpad->idata);
+
+exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+}
+
+static int xpad_open (struct input_dev *dev)
+{
+ struct usb_xpad *xpad = input_get_drvdata(dev);
+
+ xpad->irq_in->dev = xpad->udev;
+ if (usb_submit_urb(xpad->irq_in, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void xpad_close (struct input_dev *dev)
+{
+ struct usb_xpad *xpad = input_get_drvdata(dev);
+
+ usb_kill_urb(xpad->irq_in);
+}
+
+static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
+{
+ set_bit(abs, input_dev->absbit);
+
+ switch (abs) {
+ case ABS_X:
+ case ABS_Y:
+ case ABS_RX:
+ case ABS_RY: /* the two sticks */
+ input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128);
+ break;
+ case ABS_Z:
+ case ABS_RZ: /* the triggers */
+ input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
+ break;
+ case ABS_HAT0X:
+ case ABS_HAT0Y: /* the d-pad (only if MAP_DPAD_TO_AXES) */
+ input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
+ break;
+ }
+}
+
+static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct usb_xpad *xpad;
+ struct input_dev *input_dev;
+ struct usb_endpoint_descriptor *ep_irq_in;
+ int i;
+ int error = -ENOMEM;
+
+ for (i = 0; xpad_device[i].idVendor; i++) {
+ if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
+ (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
+ break;
+ }
+
+ xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!xpad || !input_dev)
+ goto fail1;
+
+ xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN,
+ GFP_ATOMIC, &xpad->idata_dma);
+ if (!xpad->idata)
+ goto fail1;
+
+ xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (!xpad->irq_in)
+ goto fail2;
+
+ xpad->udev = udev;
+ xpad->dpad_mapping = xpad_device[i].dpad_mapping;
+ if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN)
+ xpad->dpad_mapping = dpad_to_buttons;
+ xpad->dev = input_dev;
+ usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
+ strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
+
+ input_dev->name = xpad_device[i].name;
+ input_dev->phys = xpad->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, xpad);
+
+ input_dev->open = xpad_open;
+ input_dev->close = xpad_close;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ /* set up buttons */
+ for (i = 0; xpad_btn[i] >= 0; i++)
+ set_bit(xpad_btn[i], input_dev->keybit);
+ if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS)
+ for (i = 0; xpad_btn_pad[i] >= 0; i++)
+ set_bit(xpad_btn_pad[i], input_dev->keybit);
+
+ /* set up axes */
+ for (i = 0; xpad_abs[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs[i]);
+ if (xpad->dpad_mapping == MAP_DPAD_TO_AXES)
+ for (i = 0; xpad_abs_pad[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
+
+ ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(xpad->irq_in, udev,
+ usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
+ xpad->idata, XPAD_PKT_LEN, xpad_irq_in,
+ xpad, ep_irq_in->bInterval);
+ xpad->irq_in->transfer_dma = xpad->idata_dma;
+ xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ error = input_register_device(xpad->dev);
+ if (error)
+ goto fail3;
+
+ usb_set_intfdata(intf, xpad);
+ return 0;
+
+ fail3: usb_free_urb(xpad->irq_in);
+ fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
+ fail1: input_free_device(input_dev);
+ kfree(xpad);
+ return error;
+
+}
+
+static void xpad_disconnect(struct usb_interface *intf)
+{
+ struct usb_xpad *xpad = usb_get_intfdata (intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (xpad) {
+ usb_kill_urb(xpad->irq_in);
+ input_unregister_device(xpad->dev);
+ usb_free_urb(xpad->irq_in);
+ usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN,
+ xpad->idata, xpad->idata_dma);
+ kfree(xpad);
+ }
+}
+
+static struct usb_driver xpad_driver = {
+ .name = "xpad",
+ .probe = xpad_probe,
+ .disconnect = xpad_disconnect,
+ .id_table = xpad_table,
+};
+
+static int __init usb_xpad_init(void)
+{
+ int result = usb_register(&xpad_driver);
+ if (result == 0)
+ info(DRIVER_DESC ":" DRIVER_VERSION);
+ return result;
+}
+
+static void __exit usb_xpad_exit(void)
+{
+ usb_deregister(&xpad_driver);
+}
+
+module_init(usb_xpad_init);
+module_exit(usb_xpad_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index f17e9c7d4b3..bd707b86c11 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -164,6 +164,17 @@ config KEYBOARD_AMIGA
To compile this driver as a module, choose M here: the
module will be called amikbd.
+config KEYBOARD_ATARI
+ tristate "Atari keyboard"
+ depends on ATARI
+ select ATARI_KBD_CORE
+ help
+ Say Y here if you are running Linux on any Atari and have a keyboard
+ attached.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atakbd.
+
config KEYBOARD_HIL_OLD
tristate "HP HIL keyboard support (simple driver)"
depends on GSC || HP300
@@ -203,9 +214,19 @@ config KEYBOARD_OMAP
To compile this driver as a module, choose M here: the
module will be called omap-keypad.
+config KEYBOARD_PXA27x
+ tristate "PXA27x keyboard support"
+ depends on PXA27x
+ help
+ Enable support for PXA27x matrix keyboard controller
+
+ To compile this driver as a module, choose M here: the
+ module will be called pxa27x_keyboard.
+
config KEYBOARD_AAED2000
tristate "AAED-2000 keyboard"
depends on MACH_AAED2000
+ select INPUT_POLLDEV
default y
help
Say Y here to enable the keyboard on the Agilent AAED-2000
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 586a0fe53be..28d211b87b1 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
+obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
@@ -17,6 +18,7 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c
index 65fcb6af63a..63d6ead6b87 100644
--- a/drivers/input/keyboard/aaed2000_kbd.c
+++ b/drivers/input/keyboard/aaed2000_kbd.c
@@ -14,12 +14,11 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/init.h>
-#include <linux/input.h>
+#include <linux/input-polldev.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
#include <asm/arch/hardware.h>
#include <asm/arch/aaed2000.h>
@@ -46,8 +45,7 @@ static unsigned char aaedkbd_keycode[NR_SCANCODES] = {
struct aaedkbd {
unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)];
- struct input_dev *input;
- struct work_struct workq;
+ struct input_polled_dev *poll_dev;
int kbdscan_state[KB_COLS];
int kbdscan_count[KB_COLS];
};
@@ -64,14 +62,15 @@ static void aaedkbd_report_col(struct aaedkbd *aaedkbd,
scancode = SCANCODE(row, col);
pressed = rowd & KB_ROWMASK(row);
- input_report_key(aaedkbd->input, aaedkbd->keycode[scancode], pressed);
+ input_report_key(aaedkbd->poll_dev->input,
+ aaedkbd->keycode[scancode], pressed);
}
}
/* Scan the hardware keyboard and push any changes up through the input layer */
-static void aaedkbd_work(void *data)
+static void aaedkbd_poll(struct input_polled_dev *dev)
{
- struct aaedkbd *aaedkbd = data;
+ struct aaedkbd *aaedkbd = dev->private;
unsigned int col, rowd;
col = 0;
@@ -90,59 +89,41 @@ static void aaedkbd_work(void *data)
} while (col < KB_COLS);
AAEC_GPIO_KSCAN = 0x07;
- input_sync(aaedkbd->input);
-
- schedule_delayed_work(&aaedkbd->workq, msecs_to_jiffies(SCAN_INTERVAL));
-}
-
-static int aaedkbd_open(struct input_dev *indev)
-{
- struct aaedkbd *aaedkbd = indev->private;
-
- schedule_delayed_work(&aaedkbd->workq, msecs_to_jiffies(SCAN_INTERVAL));
-
- return 0;
-}
-
-static void aaedkbd_close(struct input_dev *indev)
-{
- struct aaedkbd *aaedkbd = indev->private;
-
- cancel_delayed_work(&aaedkbd->workq);
- flush_scheduled_work();
+ input_sync(dev->input);
}
static int __devinit aaedkbd_probe(struct platform_device *pdev)
{
struct aaedkbd *aaedkbd;
+ struct input_polled_dev *poll_dev;
struct input_dev *input_dev;
int i;
int error;
aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!aaedkbd || !input_dev) {
+ poll_dev = input_allocate_polled_device();
+ if (!aaedkbd || !poll_dev) {
error = -ENOMEM;
goto fail;
}
platform_set_drvdata(pdev, aaedkbd);
- aaedkbd->input = input_dev;
-
- /* Init keyboard rescan workqueue */
- INIT_WORK(&aaedkbd->workq, aaedkbd_work, aaedkbd);
-
+ aaedkbd->poll_dev = poll_dev;
memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode));
+ poll_dev->private = aaedkbd;
+ poll_dev->poll = aaedkbd_poll;
+ poll_dev->poll_interval = SCAN_INTERVAL;
+
+ input_dev = poll_dev->input;
input_dev->name = "AAED-2000 Keyboard";
input_dev->phys = "aaedkbd/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &pdev->dev;
- input_dev->private = aaedkbd;
+ input_dev->dev.parent = &pdev->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->keycode = aaedkbd->keycode;
@@ -153,17 +134,14 @@ static int __devinit aaedkbd_probe(struct platform_device *pdev)
set_bit(aaedkbd->keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
- input_dev->open = aaedkbd_open;
- input_dev->close = aaedkbd_close;
-
- error = input_register_device(aaedkbd->input);
+ error = input_register_polled_device(aaedkbd->poll_dev);
if (error)
goto fail;
return 0;
fail: kfree(aaedkbd);
- input_free_device(input_dev);
+ input_free_polled_device(poll_dev);
return error;
}
@@ -171,7 +149,8 @@ static int __devexit aaedkbd_remove(struct platform_device *pdev)
{
struct aaedkbd *aaedkbd = platform_get_drvdata(pdev);
- input_unregister_device(aaedkbd->input);
+ input_unregister_polled_device(aaedkbd->poll_dev);
+ input_free_polled_device(aaedkbd->poll_dev);
kfree(aaedkbd);
return 0;
diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c
new file mode 100644
index 00000000000..ded1d6ac6ff
--- /dev/null
+++ b/drivers/input/keyboard/atakbd.c
@@ -0,0 +1,134 @@
+/*
+ * atakbd.c
+ *
+ * Copyright (c) 2005 Michael Schmitz
+ *
+ * Based on amikbd.c, which is
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Hamish Macdonald
+ */
+
+/*
+ * Atari keyboard driver for Linux/m68k
+ *
+ * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
+ * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
+ * interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
+ * This driver only deals with handing key events off to the input layer.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <asm/atariints.h>
+#include <asm/atarihw.h>
+#include <asm/atarikb.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>");
+MODULE_DESCRIPTION("Atari keyboard driver");
+MODULE_LICENSE("GPL");
+
+static unsigned char atakbd_keycode[0x72];
+
+static struct input_dev *atakbd_dev;
+
+static void atakbd_interrupt(unsigned char scancode, char down)
+{
+
+ if (scancode < 0x72) { /* scancodes < 0xf2 are keys */
+
+ // report raw events here?
+
+ scancode = atakbd_keycode[scancode];
+
+ if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */
+ input_report_key(atakbd_dev, scancode, 1);
+ input_report_key(atakbd_dev, scancode, 0);
+ input_sync(atakbd_dev);
+ } else {
+ input_report_key(atakbd_dev, scancode, down);
+ input_sync(atakbd_dev);
+ }
+ } else /* scancodes >= 0xf2 are mouse data, most likely */
+ printk(KERN_INFO "atakbd: unhandled scancode %x\n", scancode);
+
+ return;
+}
+
+static int __init atakbd_init(void)
+{
+ int i;
+
+ if (!ATARIHW_PRESENT(ST_MFP))
+ return -EIO;
+
+ // TODO: request_mem_region if not done in arch code
+
+ if (!(atakbd_dev = input_allocate_device()))
+ return -ENOMEM;
+
+ // need to init core driver if not already done so
+ if (atari_keyb_init())
+ return -ENODEV;
+
+ atakbd_dev->name = "Atari Keyboard";
+ atakbd_dev->phys = "atakbd/input0";
+ atakbd_dev->id.bustype = BUS_ATARI;
+ atakbd_dev->id.vendor = 0x0001;
+ atakbd_dev->id.product = 0x0001;
+ atakbd_dev->id.version = 0x0100;
+
+ atakbd_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ atakbd_dev->keycode = atakbd_keycode;
+ atakbd_dev->keycodesize = sizeof(unsigned char);
+ atakbd_dev->keycodemax = ARRAY_SIZE(atakbd_keycode);
+
+ for (i = 1; i < 0x72; i++) {
+ atakbd_keycode[i] = i;
+ set_bit(atakbd_keycode[i], atakbd_dev->keybit);
+ }
+
+ input_register_device(atakbd_dev);
+
+ atari_input_keyboard_interrupt_hook = atakbd_interrupt;
+
+ printk(KERN_INFO "input: %s at IKBD ACIA\n", atakbd_dev->name);
+
+ return 0;
+}
+
+static void __exit atakbd_exit(void)
+{
+ atari_input_keyboard_interrupt_hook = NULL;
+ input_unregister_device(atakbd_dev);
+}
+
+module_init(atakbd_init);
+module_exit(atakbd_exit);
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 663877076bc..be1fe46cd30 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -586,7 +586,7 @@ static void atkbd_event_work(struct work_struct *work)
static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
- struct atkbd *atkbd = dev->private;
+ struct atkbd *atkbd = input_get_drvdata(dev);
if (!atkbd->write)
return -1;
@@ -883,8 +883,9 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd)
input_dev->id.product = atkbd->translated ? 1 : atkbd->set;
input_dev->id.version = atkbd->id;
input_dev->event = atkbd_event;
- input_dev->private = atkbd;
- input_dev->cdev.dev = &atkbd->ps2dev.serio->dev;
+ input_dev->dev.parent = &atkbd->ps2dev.serio->dev;
+
+ input_set_drvdata(input_dev, atkbd);
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_MSC);
diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c
index 1016c94e65d..6578bfff644 100644
--- a/drivers/input/keyboard/corgikbd.c
+++ b/drivers/input/keyboard/corgikbd.c
@@ -323,8 +323,7 @@ static int __init corgikbd_probe(struct platform_device *pdev)
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &pdev->dev;
- input_dev->private = corgikbd;
+ input_dev->dev.parent = &pdev->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW);
input_dev->keycode = corgikbd->keycode;
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index ccf6df387b6..739212252b0 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -35,11 +35,14 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
struct input_dev *input = platform_get_drvdata(pdev);
for (i = 0; i < pdata->nbuttons; i++) {
- int gpio = pdata->buttons[i].gpio;
+ struct gpio_keys_button *button = &pdata->buttons[i];
+ int gpio = button->gpio;
+
if (irq == gpio_to_irq(gpio)) {
- int state = (gpio_get_value(gpio) ? 1 : 0) ^ (pdata->buttons[i].active_low);
+ unsigned int type = button->type ?: EV_KEY;
+ int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low;
- input_report_key(input, pdata->buttons[i].keycode, state);
+ input_event(input, type, button->code, !!state);
input_sync(input);
}
}
@@ -63,8 +66,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
input->name = pdev->name;
input->phys = "gpio-keys/input0";
- input->cdev.dev = &pdev->dev;
- input->private = pdata;
+ input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
@@ -72,19 +74,21 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
input->id.version = 0x0100;
for (i = 0; i < pdata->nbuttons; i++) {
- int code = pdata->buttons[i].keycode;
- int irq = gpio_to_irq(pdata->buttons[i].gpio);
+ struct gpio_keys_button *button = &pdata->buttons[i];
+ int irq = gpio_to_irq(button->gpio);
+ unsigned int type = button->type ?: EV_KEY;
set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
- pdata->buttons[i].desc ? pdata->buttons[i].desc : "gpio_keys",
+ button->desc ? button->desc : "gpio_keys",
pdev);
if (error) {
printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n",
irq, error);
goto fail;
}
- set_bit(code, input->keybit);
+
+ input_set_capability(input, type, button->code);
}
error = input_register_device(input);
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index 7cc9728b04d..cdd254f2e6c 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -51,7 +51,7 @@ MODULE_LICENSE("Dual BSD/GPL");
#define HIL_KBD_SET1_UPBIT 0x01
#define HIL_KBD_SET1_SHIFT 1
-static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] =
+static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
{ HIL_KEYCODES_SET1 };
#define HIL_KBD_SET2_UPBIT 0x01
@@ -60,10 +60,10 @@ static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] =
#define HIL_KBD_SET3_UPBIT 0x80
#define HIL_KBD_SET3_SHIFT 0
-static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] =
+static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] __read_mostly =
{ HIL_KEYCODES_SET3 };
-static char hil_language[][16] = { HIL_LOCALE_MAP };
+static const char hil_language[][16] = { HIL_LOCALE_MAP };
struct hil_kbd {
struct input_dev *dev;
@@ -94,10 +94,12 @@ static void hil_kbd_process_record(struct hil_kbd *kbd)
idx = kbd->idx4/4;
p = data[idx - 1];
- if ((p & ~HIL_CMDCT_POL) ==
- (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report;
- if ((p & ~HIL_CMDCT_RPL) ==
- (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report;
+ if ((p & ~HIL_CMDCT_POL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
+ goto report;
+ if ((p & ~HIL_CMDCT_RPL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
+ goto report;
/* Not a poll response. See if we are loading config records. */
switch (p & HIL_PKT_DATA_MASK) {
@@ -107,27 +109,32 @@ static void hil_kbd_process_record(struct hil_kbd *kbd)
for (; i < HIL_KBD_MAX_LENGTH; i++)
kbd->idd[i] = 0;
break;
+
case HIL_CMD_RSC:
for (i = 0; i < idx; i++)
kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
for (; i < HIL_KBD_MAX_LENGTH; i++)
kbd->rsc[i] = 0;
break;
+
case HIL_CMD_EXD:
for (i = 0; i < idx; i++)
kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
for (; i < HIL_KBD_MAX_LENGTH; i++)
kbd->exd[i] = 0;
break;
+
case HIL_CMD_RNM:
for (i = 0; i < idx; i++)
kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
for (; i < HIL_KBD_MAX_LENGTH + 1; i++)
kbd->rnm[i] = '\0';
break;
+
default:
/* These occur when device isn't present */
- if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break;
+ if (p == (HIL_ERR_INT | HIL_PKT_CMD))
+ break;
/* Anything else we'd like to know about. */
printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
break;
@@ -139,16 +146,19 @@ static void hil_kbd_process_record(struct hil_kbd *kbd)
switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
case HIL_POL_CHARTYPE_NONE:
break;
+
case HIL_POL_CHARTYPE_ASCII:
while (cnt < idx - 1)
input_report_key(dev, kbd->data[cnt++] & 0x7f, 1);
break;
+
case HIL_POL_CHARTYPE_RSVD1:
case HIL_POL_CHARTYPE_RSVD2:
case HIL_POL_CHARTYPE_BINARY:
while (cnt < idx - 1)
input_report_key(dev, kbd->data[cnt++], 1);
break;
+
case HIL_POL_CHARTYPE_SET1:
while (cnt < idx - 1) {
unsigned int key;
@@ -161,6 +171,7 @@ static void hil_kbd_process_record(struct hil_kbd *kbd)
input_report_key(dev, key, !up);
}
break;
+
case HIL_POL_CHARTYPE_SET2:
while (cnt < idx - 1) {
unsigned int key;
@@ -173,6 +184,7 @@ static void hil_kbd_process_record(struct hil_kbd *kbd)
input_report_key(dev, key, !up);
}
break;
+
case HIL_POL_CHARTYPE_SET3:
while (cnt < idx - 1) {
unsigned int key;
@@ -191,42 +203,43 @@ static void hil_kbd_process_record(struct hil_kbd *kbd)
up(&kbd->sem);
}
-static void hil_kbd_process_err(struct hil_kbd *kbd) {
+static void hil_kbd_process_err(struct hil_kbd *kbd)
+{
printk(KERN_WARNING PREFIX "errored HIL packet\n");
kbd->idx4 = 0;
up(&kbd->sem);
}
-static irqreturn_t hil_kbd_interrupt(struct serio *serio,
- unsigned char data, unsigned int flags)
+static irqreturn_t hil_kbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
{
struct hil_kbd *kbd;
hil_packet packet;
int idx;
kbd = serio_get_drvdata(serio);
- if (kbd == NULL) {
- BUG();
- return IRQ_HANDLED;
- }
+ BUG_ON(kbd == NULL);
if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) {
hil_kbd_process_err(kbd);
return IRQ_HANDLED;
}
idx = kbd->idx4/4;
- if (!(kbd->idx4 % 4)) kbd->data[idx] = 0;
+ if (!(kbd->idx4 % 4))
+ kbd->data[idx] = 0;
packet = kbd->data[idx];
packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8);
kbd->data[idx] = packet;
/* Records of N 4-byte hil_packets must terminate with a command. */
- if ((++(kbd->idx4)) % 4) return IRQ_HANDLED;
+ if ((++(kbd->idx4)) % 4)
+ return IRQ_HANDLED;
if ((packet & 0xffff0000) != HIL_ERR_INT) {
hil_kbd_process_err(kbd);
return IRQ_HANDLED;
}
- if (packet & HIL_PKT_CMD) hil_kbd_process_record(kbd);
+ if (packet & HIL_PKT_CMD)
+ hil_kbd_process_record(kbd);
return IRQ_HANDLED;
}
@@ -235,10 +248,7 @@ static void hil_kbd_disconnect(struct serio *serio)
struct hil_kbd *kbd;
kbd = serio_get_drvdata(serio);
- if (kbd == NULL) {
- BUG();
- return;
- }
+ BUG_ON(kbd == NULL);
serio_close(serio);
input_unregister_device(kbd->dev);
@@ -259,42 +269,40 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
if (!kbd->dev)
goto bail0;
- kbd->dev->private = kbd;
-
if (serio_open(serio, drv))
goto bail1;
serio_set_drvdata(serio, kbd);
kbd->serio = serio;
- init_MUTEX_LOCKED(&(kbd->sem));
+ init_MUTEX_LOCKED(&kbd->sem);
/* Get device info. MLC driver supplies devid/status/etc. */
serio->write(serio, 0);
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_IDD);
- down(&(kbd->sem));
+ down(&kbd->sem);
serio->write(serio, 0);
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_RSC);
- down(&(kbd->sem));
+ down(&kbd->sem);
serio->write(serio, 0);
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_RNM);
- down(&(kbd->sem));
+ down(&kbd->sem);
serio->write(serio, 0);
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_EXD);
- down(&(kbd->sem));
+ down(&kbd->sem);
- up(&(kbd->sem));
+ up(&kbd->sem);
did = kbd->idd[0];
idd = kbd->idd + 1;
@@ -310,12 +318,11 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
goto bail2;
}
- if(HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
+ if (HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n");
goto bail2;
}
-
kbd->dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
kbd->dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
kbd->dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
@@ -328,7 +335,7 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
kbd->dev->id.vendor = PCI_VENDOR_ID_HP;
kbd->dev->id.product = 0x0001; /* TODO: get from kbd->rsc */
kbd->dev->id.version = 0x0100; /* TODO: get from kbd->rsc */
- kbd->dev->cdev.dev = &serio->dev;
+ kbd->dev->dev.parent = &serio->dev;
for (i = 0; i < 128; i++) {
set_bit(hil_kbd_set1[i], kbd->dev->keybit);
@@ -344,8 +351,8 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */
- down(&(kbd->sem));
- up(&(kbd->sem));
+ down(&kbd->sem);
+ up(&kbd->sem);
return 0;
bail2:
@@ -368,26 +375,26 @@ static struct serio_device_id hil_kbd_ids[] = {
{ 0 }
};
-struct serio_driver hil_kbd_serio_drv = {
+static struct serio_driver hil_kbd_serio_drv = {
.driver = {
.name = "hil_kbd",
},
.description = "HP HIL keyboard driver",
.id_table = hil_kbd_ids,
- .connect = hil_kbd_connect,
- .disconnect = hil_kbd_disconnect,
- .interrupt = hil_kbd_interrupt
+ .connect = hil_kbd_connect,
+ .disconnect = hil_kbd_disconnect,
+ .interrupt = hil_kbd_interrupt
};
static int __init hil_kbd_init(void)
{
return serio_register_driver(&hil_kbd_serio_drv);
}
-
+
static void __exit hil_kbd_exit(void)
{
serio_unregister_driver(&hil_kbd_serio_drv);
}
-
+
module_init(hil_kbd_init);
module_exit(hil_kbd_exit);
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
index 4de4dc297d5..499b6974457 100644
--- a/drivers/input/keyboard/hilkbd.c
+++ b/drivers/input/keyboard/hilkbd.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1998 Philip Blundell <philb@gnu.org>
* Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
- * Copyright (C) 1999-2006 Helge Deller <deller@gmx.de>
+ * Copyright (C) 1999-2007 Helge Deller <deller@gmx.de>
*
* Very basic HP Human Interface Loop (HIL) driver.
* This driver handles the keyboard on HP300 (m68k) and on some
@@ -52,7 +52,7 @@ MODULE_LICENSE("GPL v2");
#elif defined(CONFIG_HP300)
- #define HILBASE 0xf0428000 /* HP300 (m86k) port address */
+ #define HILBASE 0xf0428000UL /* HP300 (m68k) port address */
#define HIL_DATA 0x1
#define HIL_CMD 0x3
#define HIL_IRQ 2
@@ -89,7 +89,7 @@ MODULE_LICENSE("GPL v2");
#define HIL_READKBDSADR 0xF9
#define HIL_WRITEKBDSADR 0xE9
-static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] =
+static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
{ HIL_KEYCODES_SET1 };
/* HIL structure */
@@ -211,10 +211,10 @@ hil_keyb_init(void)
return -ENODEV; /* already initialized */
}
+ spin_lock_init(&hil_dev.lock);
hil_dev.dev = input_allocate_device();
if (!hil_dev.dev)
return -ENOMEM;
- hil_dev.dev->private = &hil_dev;
#if defined(CONFIG_HP300)
if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
index 3d4d0a0ede2..1b08f4e79dd 100644
--- a/drivers/input/keyboard/lkkbd.c
+++ b/drivers/input/keyboard/lkkbd.c
@@ -515,7 +515,7 @@ static int
lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
int value)
{
- struct lkkbd *lk = dev->private;
+ struct lkkbd *lk = input_get_drvdata (dev);
unsigned char leds_on = 0;
unsigned char leds_off = 0;
@@ -666,9 +666,10 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_LKKBD;
input_dev->id.product = 0;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
+ input_dev->dev.parent = &serio->dev;
input_dev->event = lkkbd_event;
- input_dev->private = lk;
+
+ input_set_drvdata (input_dev, lk);
set_bit (EV_KEY, input_dev->evbit);
set_bit (EV_LED, input_dev->evbit);
diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
index 2ade5186cc4..7a41b271f22 100644
--- a/drivers/input/keyboard/locomokbd.c
+++ b/drivers/input/keyboard/locomokbd.c
@@ -231,7 +231,7 @@ static int locomokbd_probe(struct locomo_dev *dev)
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->private = locomokbd;
+ input_dev->dev.parent = &dev->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->keycode = locomokbd->keycode;
diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c
index aa29b50765c..b97a41e3ee5 100644
--- a/drivers/input/keyboard/newtonkbd.c
+++ b/drivers/input/keyboard/newtonkbd.c
@@ -104,8 +104,7 @@ static int nkbd_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_NEWTON;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = nkbd;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->keycode = nkbd->keycode;
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index 5680a6d95b2..3a228634f10 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -370,8 +370,7 @@ static int __init omap_kp_probe(struct platform_device *pdev)
set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
input_dev->name = "omap-keypad";
input_dev->phys = "omap-keypad/input0";
- input_dev->cdev.dev = &pdev->dev;
- input_dev->private = omap_kp;
+ input_dev->dev.parent = &pdev->dev;
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
diff --git a/drivers/input/keyboard/pxa27x_keyboard.c b/drivers/input/keyboard/pxa27x_keyboard.c
new file mode 100644
index 00000000000..06eaf766d9d
--- /dev/null
+++ b/drivers/input/keyboard/pxa27x_keyboard.c
@@ -0,0 +1,258 @@
+/*
+ * linux/drivers/input/keyboard/pxa27x_keyboard.c
+ *
+ * Driver for the pxa27x matrix keyboard controller.
+ *
+ * Created: Feb 22, 2007
+ * Author: Rodolfo Giometti <giometti@linux.it>
+ *
+ * Based on a previous implementations by Kevin O'Connor
+ * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and
+ * on some suggestions by Nicolas Pitre <nico@cam.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/pxa27x_keyboard.h>
+
+#define DRIVER_NAME "pxa27x-keyboard"
+
+#define KPASMKP(col) (col/2 == 0 ? KPASMKP0 : \
+ col/2 == 1 ? KPASMKP1 : \
+ col/2 == 2 ? KPASMKP2 : KPASMKP3)
+#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2)))
+
+static irqreturn_t pxakbd_irq_handler(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data;
+ struct input_dev *input_dev = platform_get_drvdata(pdev);
+ unsigned long kpc = KPC;
+ int p, row, col, rel;
+
+ if (kpc & KPC_DI) {
+ unsigned long kpdk = KPDK;
+
+ if (!(kpdk & KPDK_DKP)) {
+ /* better luck next time */
+ } else if (kpc & KPC_REE0) {
+ unsigned long kprec = KPREC;
+ KPREC = 0x7f;
+
+ if (kprec & KPREC_OF0)
+ rel = (kprec & 0xff) + 0x7f;
+ else if (kprec & KPREC_UF0)
+ rel = (kprec & 0xff) - 0x7f - 0xff;
+ else
+ rel = (kprec & 0xff) - 0x7f;
+
+ if (rel) {
+ input_report_rel(input_dev, REL_WHEEL, rel);
+ input_sync(input_dev);
+ }
+ }
+ }
+
+ if (kpc & KPC_MI) {
+ /* report the status of every button */
+ for (row = 0; row < pdata->nr_rows; row++) {
+ for (col = 0; col < pdata->nr_cols; col++) {
+ p = KPASMKP(col) & KPASMKPx_MKC(row, col) ?
+ 1 : 0;
+ pr_debug("keycode %x - pressed %x\n",
+ pdata->keycodes[row][col], p);
+ input_report_key(input_dev,
+ pdata->keycodes[row][col], p);
+ }
+ }
+ input_sync(input_dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int pxakbd_open(struct input_dev *dev)
+{
+ /* Set keypad control register */
+ KPC |= (KPC_ASACT |
+ KPC_MS_ALL |
+ (2 << 6) | KPC_REE0 | KPC_DK_DEB_SEL |
+ KPC_ME | KPC_MIE | KPC_DE | KPC_DIE);
+
+ KPC &= ~KPC_AS; /* disable automatic scan */
+ KPC &= ~KPC_IMKP; /* do not ignore multiple keypresses */
+
+ /* Set rotary count to mid-point value */
+ KPREC = 0x7F;
+
+ /* Enable unit clock */
+ pxa_set_cken(CKEN19_KEYPAD, 1);
+
+ return 0;
+}
+
+static void pxakbd_close(struct input_dev *dev)
+{
+ /* Disable clock unit */
+ pxa_set_cken(CKEN19_KEYPAD, 0);
+}
+
+#ifdef CONFIG_PM
+static int pxakbd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data;
+
+ /* Save controller status */
+ pdata->reg_kpc = KPC;
+ pdata->reg_kprec = KPREC;
+
+ return 0;
+}
+
+static int pxakbd_resume(struct platform_device *pdev)
+{
+ struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data;
+ struct input_dev *input_dev = platform_get_drvdata(pdev);
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users) {
+ /* Restore controller status */
+ KPC = pdata->reg_kpc;
+ KPREC = pdata->reg_kprec;
+
+ /* Enable unit clock */
+ pxa_set_cken(CKEN19_KEYPAD, 1);
+ }
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+#else
+#define pxakbd_suspend NULL
+#define pxakbd_resume NULL
+#endif
+
+static int __devinit pxakbd_probe(struct platform_device *pdev)
+{
+ struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data;
+ struct input_dev *input_dev;
+ int i, row, col, error;
+
+ /* Create and register the input driver. */
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ printk(KERN_ERR "Cannot request keypad device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = DRIVER_NAME;
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->open = pxakbd_open;
+ input_dev->close = pxakbd_close;
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL);
+ input_dev->relbit[LONG(REL_WHEEL)] = BIT(REL_WHEEL);
+ for (row = 0; row < pdata->nr_rows; row++) {
+ for (col = 0; col < pdata->nr_cols; col++) {
+ int code = pdata->keycodes[row][col];
+ if (code > 0)
+ set_bit(code, input_dev->keybit);
+ }
+ }
+
+ error = request_irq(IRQ_KEYPAD, pxakbd_irq_handler, IRQF_DISABLED,
+ DRIVER_NAME, pdev);
+ if (error) {
+ printk(KERN_ERR "Cannot request keypad IRQ\n");
+ pxa_set_cken(CKEN19_KEYPAD, 0);
+ goto err_free_dev;
+ }
+
+ platform_set_drvdata(pdev, input_dev);
+
+ /* Register the input device */
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_free_irq;
+
+ /* Setup GPIOs. */
+ for (i = 0; i < pdata->nr_rows + pdata->nr_cols; i++)
+ pxa_gpio_mode(pdata->gpio_modes[i]);
+
+ /*
+ * Store rows/cols info into keyboard registers.
+ */
+
+ KPC |= (pdata->nr_rows - 1) << 26;
+ KPC |= (pdata->nr_cols - 1) << 23;
+
+ for (col = 0; col < pdata->nr_cols; col++)
+ KPC |= KPC_MS0 << col;
+
+ return 0;
+
+ err_free_irq:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(IRQ_KEYPAD, pdev);
+ err_free_dev:
+ input_free_device(input_dev);
+ return error;
+}
+
+static int __devexit pxakbd_remove(struct platform_device *pdev)
+{
+ struct input_dev *input_dev = platform_get_drvdata(pdev);
+
+ input_unregister_device(input_dev);
+ free_irq(IRQ_KEYPAD, pdev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver pxakbd_driver = {
+ .probe = pxakbd_probe,
+ .remove = __devexit_p(pxakbd_remove),
+ .suspend = pxakbd_suspend,
+ .resume = pxakbd_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init pxakbd_init(void)
+{
+ return platform_driver_register(&pxakbd_driver);
+}
+
+static void __exit pxakbd_exit(void)
+{
+ platform_driver_unregister(&pxakbd_driver);
+}
+
+module_init(pxakbd_init);
+module_exit(pxakbd_exit);
+
+MODULE_DESCRIPTION("PXA27x Matrix Keyboard Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c
index 8a2166c77ff..41b80385476 100644
--- a/drivers/input/keyboard/spitzkbd.c
+++ b/drivers/input/keyboard/spitzkbd.c
@@ -372,10 +372,9 @@ static int __init spitzkbd_probe(struct platform_device *dev)
spitzkbd->input = input_dev;
- input_dev->private = spitzkbd;
input_dev->name = "Spitz Keyboard";
input_dev->phys = spitzkbd->phys;
- input_dev->cdev.dev = &dev->dev;
+ input_dev->dev.parent = &dev->dev;
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c
index f7b5c5b8145..b44b0684d54 100644
--- a/drivers/input/keyboard/stowaway.c
+++ b/drivers/input/keyboard/stowaway.c
@@ -108,8 +108,7 @@ static int skbd_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_STOWAWAY;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = skbd;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->keycode = skbd->keycode;
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
index cc023836641..1d4e39624cf 100644
--- a/drivers/input/keyboard/sunkbd.c
+++ b/drivers/input/keyboard/sunkbd.c
@@ -146,7 +146,7 @@ out:
static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
- struct sunkbd *sunkbd = dev->private;
+ struct sunkbd *sunkbd = input_get_drvdata(dev);
switch (type) {
@@ -271,8 +271,10 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_SUNKBD;
input_dev->id.product = sunkbd->type;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = sunkbd;
+ input_dev->dev.parent = &serio->dev;
+
+ input_set_drvdata(input_dev, sunkbd);
+
input_dev->event = sunkbd_event;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c
index a8209343213..f3a56eb58ed 100644
--- a/drivers/input/keyboard/xtkbd.c
+++ b/drivers/input/keyboard/xtkbd.c
@@ -108,8 +108,7 @@ static int xtkbd_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = xtkbd;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->keycode = xtkbd->keycode;
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 41b42587f5e..6013ace94d9 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -40,6 +40,28 @@ config INPUT_M68K_BEEP
tristate "M68k Beeper support"
depends on M68K
+config INPUT_IXP4XX_BEEPER
+ tristate "IXP4XX Beeper support"
+ depends on ARCH_IXP4XX
+ help
+ If you say yes here, you can connect a beeper to the
+ ixp4xx gpio pins. This is used by the LinkSys NSLU2.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ixp4xx-beeper.
+
+config INPUT_COBALT_BTNS
+ tristate "Cobalt button interface"
+ depends on MIPS_COBALT
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want to support MIPS Cobalt button interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cobalt_btns.
+
config INPUT_WISTRON_BTNS
tristate "x86 Wistron laptop button interface"
depends on X86 && !X86_64
@@ -60,17 +82,79 @@ config INPUT_ATLAS_BTNS
To compile this driver as a module, choose M here: the module will
be called atlas_btns.
-config INPUT_IXP4XX_BEEPER
- tristate "IXP4XX Beeper support"
- depends on ARCH_IXP4XX
+config INPUT_ATI_REMOTE
+ tristate "ATI / X10 USB RF remote control"
+ select USB
help
- If you say yes here, you can connect a beeper to the
- ixp4xx gpio pins. This is used by the LinkSys NSLU2.
+ Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
+ These are RF remotes with USB receivers.
+ The ATI remote comes with many of ATI's All-In-Wonder video cards.
+ The X10 "Lola" remote is available at:
+ <http://www.x10.com/products/lola_sg1.htm>
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote.
+
+config INPUT_ATI_REMOTE2
+ tristate "ATI / Philips USB RF remote control"
+ select USB
+ help
+ Say Y here if you want to use an ATI or Philips USB RF remote control.
+ These are RF remotes with USB receivers.
+ ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards
+ and is also available as a separate product.
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote2.
+
+config INPUT_KEYSPAN_REMOTE
+ tristate "Keyspan DMR USB remote control (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ select USB
+ help
+ Say Y here if you want to use a Keyspan DMR USB remote control.
+ Currently only the UIA-11 type of receiver has been tested. The tag
+ on the receiver that connects to the USB port should have a P/N that
+ will tell you what type of DMR you have. The UIA-10 type is not
+ supported at this time. This driver maps all buttons to keypress
+ events.
- If unsure, say Y.
+ To compile this driver as a module, choose M here: the module will
+ be called keyspan_remote.
+
+config INPUT_POWERMATE
+ tristate "Griffin PowerMate and Contour Jog support"
+ select USB
+ help
+ Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
+ These are aluminum 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/>.
To compile this driver as a module, choose M here: the
- module will be called ixp4xx-beeper.
+ module will be called powermate.
+
+config INPUT_YEALINK
+ tristate "Yealink usb-p1k voip phone"
+ depends EXPERIMENTAL
+ select USB
+ help
+ Say Y here if you want to enable keyboard and LCD functions of the
+ Yealink usb-p1k usb phones. The audio part is enabled by the generic
+ usb sound driver, so you might want to enable that as well.
+
+ For information about how to use these additional functions, see
+ <file:Documentation/input/yealink.txt>.
+
+ To compile this driver as a module, choose M here: the module will be
+ called yealink.
config INPUT_UINPUT
tristate "User level driver support"
@@ -81,8 +165,19 @@ config INPUT_UINPUT
To compile this driver as a module, choose M here: the
module will be called uinput.
+config INPUT_POLLDEV
+ tristate "Polled input device skeleton"
+ help
+ Say Y here if you are using a driver for an input
+ device that periodically polls hardware state. This
+ option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
+
+ To compile this driver as a module, choose M here: the
+ module will be called input-polldev.
+
config HP_SDC_RTC
- tristate "HP SDC Real Time Clock"
+ tristate "HP SDC Real Time Clock"
depends on GSC || HP300
select HP_SDC
help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index e0a8d58c9e9..8b2f7799e25 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -4,11 +4,18 @@
# Each configuration option enables a list of files.
+obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
-obj-$(CONFIG_INPUT_UINPUT) += uinput.o
+obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
+obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
+obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
+obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
+obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
+obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
-obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_UINPUT) += uinput.o
diff --git a/drivers/input/misc/ati_remote.c b/drivers/input/misc/ati_remote.c
new file mode 100644
index 00000000000..471aab20644
--- /dev/null
+++ b/drivers/input/misc/ati_remote.c
@@ -0,0 +1,862 @@
+/*
+ * USB ATI Remote support
+ *
+ * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
+ *
+ * This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
+ * porting to the 2.6 kernel interfaces, along with other modification
+ * to better match the style of the existing usb/input drivers. However, the
+ * protocol and hardware handling is essentially unchanged from 2.1.1.
+ *
+ * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by
+ * Vojtech Pavlik.
+ *
+ * Changes:
+ *
+ * Feb 2004: Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.2.0
+ * Jun 2004: Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.2.1
+ * Added key repeat support contributed by:
+ * Vincent Vanackere <vanackere@lif.univ-mrs.fr>
+ * Added support for the "Lola" remote contributed by:
+ * Seth Cohn <sethcohn@yahoo.com>
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * 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
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Hardware & software notes
+ *
+ * These remote controls are distributed by ATI as part of their
+ * "All-In-Wonder" video card packages. The receiver self-identifies as a
+ * "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
+ *
+ * The "Lola" remote is available from X10. See:
+ * http://www.x10.com/products/lola_sg1.htm
+ * The Lola is similar to the ATI remote but has no mouse support, and slightly
+ * different keys.
+ *
+ * It is possible to use multiple receivers and remotes on multiple computers
+ * simultaneously by configuring them to use specific channels.
+ *
+ * The RF protocol used by the remote supports 16 distinct channels, 1 to 16.
+ * Actually, it may even support more, at least in some revisions of the
+ * hardware.
+ *
+ * Each remote can be configured to transmit on one channel as follows:
+ * - Press and hold the "hand icon" button.
+ * - When the red LED starts to blink, let go of the "hand icon" button.
+ * - When it stops blinking, input the channel code as two digits, from 01
+ * to 16, and press the hand icon again.
+ *
+ * The timing can be a little tricky. Try loading the module with debug=1
+ * to have the kernel print out messages about the remote control number
+ * and mask. Note: debugging prints remote numbers as zero-based hexadecimal.
+ *
+ * The driver has a "channel_mask" parameter. This bitmask specifies which
+ * channels will be ignored by the module. To mask out channels, just add
+ * all the 2^channel_number values together.
+ *
+ * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
+ * ignore signals coming from remote controls transmitting on channel 4, but
+ * accept all other channels.
+ *
+ * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be
+ * ignored.
+ *
+ * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this
+ * parameter are unused.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb/input.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+
+/*
+ * Module and Version Information, Module Parameters
+ */
+
+#define ATI_REMOTE_VENDOR_ID 0x0bc7
+#define ATI_REMOTE_PRODUCT_ID 0x004
+#define LOLA_REMOTE_PRODUCT_ID 0x002
+#define MEDION_REMOTE_PRODUCT_ID 0x006
+
+#define DRIVER_VERSION "2.2.1"
+#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>"
+#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
+
+#define NAME_BUFSIZE 80 /* size of product name, path buffers */
+#define DATA_BUFSIZE 63 /* size of URB data buffers */
+
+/*
+ * Duplicate event filtering time.
+ * Sequential, identical KIND_FILTERED inputs with less than
+ * FILTER_TIME milliseconds between them are considered as repeat
+ * events. The hardware generates 5 events for the first keypress
+ * and we have to take this into account for an accurate repeat
+ * behaviour.
+ */
+#define FILTER_TIME 60 /* msec */
+#define REPEAT_DELAY 500 /* msec */
+
+static unsigned long channel_mask;
+module_param(channel_mask, ulong, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+static int repeat_filter = FILTER_TIME;
+module_param(repeat_filter, int, 0644);
+MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec");
+
+static int repeat_delay = REPEAT_DELAY;
+module_param(repeat_delay, int, 0644);
+MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec");
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#undef err
+#define err(format, arg...) printk(KERN_ERR format , ## arg)
+
+static struct usb_device_id ati_remote_table[] = {
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ati_remote_table);
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a) ((unsigned char)((a) >> 8))
+#define LO(a) ((unsigned char)((a) & 0xff))
+
+#define SEND_FLAG_IN_PROGRESS 1
+#define SEND_FLAG_COMPLETE 2
+
+/* Device initialization strings */
+static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
+static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
+
+struct ati_remote {
+ struct input_dev *idev;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+
+ struct urb *irq_urb;
+ struct urb *out_urb;
+ struct usb_endpoint_descriptor *endpoint_in;
+ struct usb_endpoint_descriptor *endpoint_out;
+ unsigned char *inbuf;
+ unsigned char *outbuf;
+ dma_addr_t inbuf_dma;
+ dma_addr_t outbuf_dma;
+
+ unsigned char old_data[2]; /* Detect duplicate events */
+ unsigned long old_jiffies;
+ unsigned long acc_jiffies; /* handle acceleration */
+ unsigned long first_jiffies;
+
+ unsigned int repeat_count;
+
+ char name[NAME_BUFSIZE];
+ char phys[NAME_BUFSIZE];
+
+ wait_queue_head_t wait;
+ int send_flags;
+};
+
+/* "Kinds" of messages sent from the hardware to the driver. */
+#define KIND_END 0
+#define KIND_LITERAL 1 /* Simply pass to input system */
+#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */
+#define KIND_LU 3 /* Directional keypad diagonals - left up, */
+#define KIND_RU 4 /* right up, */
+#define KIND_LD 5 /* left down, */
+#define KIND_RD 6 /* right down */
+#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/
+
+/* Translation table from hardware messages to input events. */
+static const struct {
+ short kind;
+ unsigned char data1, data2;
+ int type;
+ unsigned int code;
+ int value;
+} ati_remote_tbl[] = {
+ /* Directional control pad axes */
+ {KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */
+ {KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */
+ {KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */
+ {KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */
+ /* Directional control pad diagonals */
+ {KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */
+ {KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */
+ {KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */
+ {KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */
+
+ /* "Mouse button" buttons */
+ {KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
+ {KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
+ {KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
+ {KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
+
+ /* Artificial "doubleclick" events are generated by the hardware.
+ * They are mapped to the "side" and "extra" mouse buttons here. */
+ {KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
+ {KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
+
+ /* keyboard. */
+ {KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
+ {KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
+ {KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
+ {KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
+ {KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
+ {KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
+ {KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
+ {KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
+ {KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
+ {KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
+ {KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
+ {KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
+ {KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
+ {KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
+ {KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
+ {KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
+
+ /* "special" keys */
+ {KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */
+ {KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */
+ {KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */
+ {KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */
+ {KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */
+ {KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */
+ {KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */
+ {KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */
+ {KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */
+ {KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */
+ {KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */
+ {KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */
+ {KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */
+ {KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */
+ {KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */
+ {KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
+ {KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */
+ {KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */
+ {KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */
+ {KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
+ {KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */
+ {KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */
+ {KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */
+ {KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */
+ {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */
+ {KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */
+ {KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */
+ {KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */
+ {KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */
+ {KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */
+ {KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */
+ {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */
+
+ {KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
+};
+
+/* Local function prototypes */
+static void ati_remote_dump (unsigned char *data, unsigned int actual_length);
+static int ati_remote_open (struct input_dev *inputdev);
+static void ati_remote_close (struct input_dev *inputdev);
+static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
+static void ati_remote_irq_out (struct urb *urb);
+static void ati_remote_irq_in (struct urb *urb);
+static void ati_remote_input_report (struct urb *urb);
+static int ati_remote_initialize (struct ati_remote *ati_remote);
+static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote_disconnect (struct usb_interface *interface);
+
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+ .name = "ati_remote",
+ .probe = ati_remote_probe,
+ .disconnect = ati_remote_disconnect,
+ .id_table = ati_remote_table,
+};
+
+/*
+ * ati_remote_dump_input
+ */
+static void ati_remote_dump(unsigned char *data, unsigned int len)
+{
+ if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
+ warn("Weird byte 0x%02x", data[0]);
+ else if (len == 4)
+ warn("Weird key %02x %02x %02x %02x",
+ data[0], data[1], data[2], data[3]);
+ else
+ warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...",
+ len, data[0], data[1], data[2], data[3], data[4], data[5]);
+}
+
+/*
+ * ati_remote_open
+ */
+static int ati_remote_open(struct input_dev *inputdev)
+{
+ struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+
+ /* On first open, submit the read urb which was set up previously. */
+ ati_remote->irq_urb->dev = ati_remote->udev;
+ if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
+ dev_err(&ati_remote->interface->dev,
+ "%s: usb_submit_urb failed!\n", __FUNCTION__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * ati_remote_close
+ */
+static void ati_remote_close(struct input_dev *inputdev)
+{
+ struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+
+ usb_kill_urb(ati_remote->irq_urb);
+}
+
+/*
+ * ati_remote_irq_out
+ */
+static void ati_remote_irq_out(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+
+ if (urb->status) {
+ dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
+ __FUNCTION__, urb->status);
+ return;
+ }
+
+ ati_remote->send_flags |= SEND_FLAG_COMPLETE;
+ wmb();
+ wake_up(&ati_remote->wait);
+}
+
+/*
+ * ati_remote_sendpacket
+ *
+ * Used to send device initialization strings
+ */
+static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
+{
+ int retval = 0;
+
+ /* Set up out_urb */
+ memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
+ ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);
+
+ ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
+ ati_remote->out_urb->dev = ati_remote->udev;
+ ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
+
+ retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
+ if (retval) {
+ dev_dbg(&ati_remote->interface->dev,
+ "sendpacket: usb_submit_urb failed: %d\n", retval);
+ return retval;
+ }
+
+ wait_event_timeout(ati_remote->wait,
+ ((ati_remote->out_urb->status != -EINPROGRESS) ||
+ (ati_remote->send_flags & SEND_FLAG_COMPLETE)),
+ HZ);
+ usb_kill_urb(ati_remote->out_urb);
+
+ return retval;
+}
+
+/*
+ * ati_remote_event_lookup
+ */
+static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
+{
+ int i;
+
+ for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
+ /*
+ * Decide if the table entry matches the remote input.
+ */
+ if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
+ ((((ati_remote_tbl[i].data1 >> 4) -
+ (d1 >> 4) + rem) & 0x0f) == 0x0f) &&
+ (ati_remote_tbl[i].data2 == d2))
+ return i;
+
+ }
+ return -1;
+}
+
+/*
+ * ati_remote_compute_accel
+ *
+ * Implements acceleration curve for directional control pad
+ * If elapsed time since last event is > 1/4 second, user "stopped",
+ * so reset acceleration. Otherwise, user is probably holding the control
+ * pad down, so we increase acceleration, ramping up over two seconds to
+ * a maximum speed.
+ */
+static int ati_remote_compute_accel(struct ati_remote *ati_remote)
+{
+ static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
+ unsigned long now = jiffies;
+ int acc;
+
+ if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
+ acc = 1;
+ ati_remote->acc_jiffies = now;
+ }
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
+ acc = accel[0];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
+ acc = accel[1];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
+ acc = accel[2];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
+ acc = accel[3];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
+ acc = accel[4];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
+ acc = accel[5];
+ else
+ acc = accel[6];
+
+ return acc;
+}
+
+/*
+ * ati_remote_report_input
+ */
+static void ati_remote_input_report(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+ unsigned char *data= ati_remote->inbuf;
+ struct input_dev *dev = ati_remote->idev;
+ int index, acc;
+ int remote_num;
+
+ /* Deal with strange looking inputs */
+ if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
+ ((data[3] & 0x0f) != 0x00) ) {
+ ati_remote_dump(data, urb->actual_length);
+ return;
+ }
+
+ /* Mask unwanted remote channels. */
+ /* note: remote_num is 0-based, channel 1 on remote == 0 here */
+ remote_num = (data[3] >> 4) & 0x0f;
+ if (channel_mask & (1 << (remote_num + 1))) {
+ dbginfo(&ati_remote->interface->dev,
+ "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
+ remote_num, data[1], data[2], channel_mask);
+ return;
+ }
+
+ /* Look up event code index in translation table */
+ index = ati_remote_event_lookup(remote_num, data[1], data[2]);
+ if (index < 0) {
+ dev_warn(&ati_remote->interface->dev,
+ "Unknown input from channel 0x%02x: data %02x,%02x\n",
+ remote_num, data[1], data[2]);
+ return;
+ }
+ dbginfo(&ati_remote->interface->dev,
+ "channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
+ remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
+
+ if (ati_remote_tbl[index].kind == KIND_LITERAL) {
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code,
+ ati_remote_tbl[index].value);
+ input_sync(dev);
+
+ ati_remote->old_jiffies = jiffies;
+ return;
+ }
+
+ if (ati_remote_tbl[index].kind == KIND_FILTERED) {
+ unsigned long now = jiffies;
+
+ /* Filter duplicate events which happen "too close" together. */
+ if (ati_remote->old_data[0] == data[1] &&
+ ati_remote->old_data[1] == data[2] &&
+ time_before(now, ati_remote->old_jiffies +
+ msecs_to_jiffies(repeat_filter))) {
+ ati_remote->repeat_count++;
+ } else {
+ ati_remote->repeat_count = 0;
+ ati_remote->first_jiffies = now;
+ }
+
+ ati_remote->old_data[0] = data[1];
+ ati_remote->old_data[1] = data[2];
+ ati_remote->old_jiffies = now;
+
+ /* Ensure we skip at least the 4 first duplicate events (generated
+ * by a single keypress), and continue skipping until repeat_delay
+ * msecs have passed
+ */
+ if (ati_remote->repeat_count > 0 &&
+ (ati_remote->repeat_count < 5 ||
+ time_before(now, ati_remote->first_jiffies +
+ msecs_to_jiffies(repeat_delay))))
+ return;
+
+
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code, 1);
+ input_sync(dev);
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code, 0);
+ input_sync(dev);
+
+ } else {
+
+ /*
+ * Other event kinds are from the directional control pad, and have an
+ * acceleration factor applied to them. Without this acceleration, the
+ * control pad is mostly unusable.
+ */
+ acc = ati_remote_compute_accel(ati_remote);
+
+ switch (ati_remote_tbl[index].kind) {
+ case KIND_ACCEL:
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code,
+ ati_remote_tbl[index].value * acc);
+ break;
+ case KIND_LU:
+ input_report_rel(dev, REL_X, -acc);
+ input_report_rel(dev, REL_Y, -acc);
+ break;
+ case KIND_RU:
+ input_report_rel(dev, REL_X, acc);
+ input_report_rel(dev, REL_Y, -acc);
+ break;
+ case KIND_LD:
+ input_report_rel(dev, REL_X, -acc);
+ input_report_rel(dev, REL_Y, acc);
+ break;
+ case KIND_RD:
+ input_report_rel(dev, REL_X, acc);
+ input_report_rel(dev, REL_Y, acc);
+ break;
+ default:
+ dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
+ ati_remote_tbl[index].kind);
+ }
+ input_sync(dev);
+
+ ati_remote->old_jiffies = jiffies;
+ ati_remote->old_data[0] = data[1];
+ ati_remote->old_data[1] = data[2];
+ }
+}
+
+/*
+ * ati_remote_irq_in
+ */
+static void ati_remote_irq_in(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0: /* success */
+ ati_remote_input_report(urb);
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
+ __FUNCTION__);
+ return;
+ default: /* error */
+ dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
+ __FUNCTION__, urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
+ __FUNCTION__, retval);
+}
+
+/*
+ * ati_remote_alloc_buffers
+ */
+static int ati_remote_alloc_buffers(struct usb_device *udev,
+ struct ati_remote *ati_remote)
+{
+ ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
+ &ati_remote->inbuf_dma);
+ if (!ati_remote->inbuf)
+ return -1;
+
+ ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
+ &ati_remote->outbuf_dma);
+ if (!ati_remote->outbuf)
+ return -1;
+
+ ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ati_remote->irq_urb)
+ return -1;
+
+ ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ati_remote->out_urb)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * ati_remote_free_buffers
+ */
+static void ati_remote_free_buffers(struct ati_remote *ati_remote)
+{
+ usb_free_urb(ati_remote->irq_urb);
+ usb_free_urb(ati_remote->out_urb);
+
+ usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
+ ati_remote->inbuf, ati_remote->inbuf_dma);
+
+ usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
+ ati_remote->outbuf, ati_remote->outbuf_dma);
+}
+
+static void ati_remote_input_init(struct ati_remote *ati_remote)
+{
+ struct input_dev *idev = ati_remote->idev;
+ int i;
+
+ idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) |
+ BIT(BTN_SIDE) | BIT(BTN_EXTRA) );
+ idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
+ if (ati_remote_tbl[i].type == EV_KEY)
+ set_bit(ati_remote_tbl[i].code, idev->keybit);
+
+ input_set_drvdata(idev, ati_remote);
+
+ idev->open = ati_remote_open;
+ idev->close = ati_remote_close;
+
+ idev->name = ati_remote->name;
+ idev->phys = ati_remote->phys;
+
+ usb_to_input_id(ati_remote->udev, &idev->id);
+ idev->dev.parent = &ati_remote->udev->dev;
+}
+
+static int ati_remote_initialize(struct ati_remote *ati_remote)
+{
+ struct usb_device *udev = ati_remote->udev;
+ int pipe, maxp;
+
+ init_waitqueue_head(&ati_remote->wait);
+
+ /* Set up irq_urb */
+ pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+ usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
+ maxp, ati_remote_irq_in, ati_remote,
+ ati_remote->endpoint_in->bInterval);
+ ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
+ ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* Set up out_urb */
+ pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+ usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
+ maxp, ati_remote_irq_out, ati_remote,
+ ati_remote->endpoint_out->bInterval);
+ ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
+ ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* send initialization strings */
+ if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
+ (ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
+ dev_err(&ati_remote->interface->dev,
+ "Initializing ati_remote hardware failed.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * ati_remote_probe
+ */
+static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_host = interface->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
+ struct ati_remote *ati_remote;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+
+ if (iface_host->desc.bNumEndpoints != 2) {
+ err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ endpoint_in = &iface_host->endpoint[0].desc;
+ endpoint_out = &iface_host->endpoint[1].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint_in)) {
+ err("%s: Unexpected endpoint_in\n", __FUNCTION__);
+ return -ENODEV;
+ }
+ if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
+ err("%s: endpoint_in message size==0? \n", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ati_remote || !input_dev)
+ goto fail1;
+
+ /* Allocate URB buffers, URBs */
+ if (ati_remote_alloc_buffers(udev, ati_remote))
+ goto fail2;
+
+ ati_remote->endpoint_in = endpoint_in;
+ ati_remote->endpoint_out = endpoint_out;
+ ati_remote->udev = udev;
+ ati_remote->idev = input_dev;
+ ati_remote->interface = interface;
+
+ usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
+ strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
+
+ if (udev->manufacturer)
+ strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
+
+ if (udev->product)
+ snprintf(ati_remote->name, sizeof(ati_remote->name),
+ "%s %s", ati_remote->name, udev->product);
+
+ if (!strlen(ati_remote->name))
+ snprintf(ati_remote->name, sizeof(ati_remote->name),
+ DRIVER_DESC "(%04x,%04x)",
+ le16_to_cpu(ati_remote->udev->descriptor.idVendor),
+ le16_to_cpu(ati_remote->udev->descriptor.idProduct));
+
+ ati_remote_input_init(ati_remote);
+
+ /* Device Hardware Initialization - fills in ati_remote->idev from udev. */
+ err = ati_remote_initialize(ati_remote);
+ if (err)
+ goto fail3;
+
+ /* Set up and register input device */
+ err = input_register_device(ati_remote->idev);
+ if (err)
+ goto fail3;
+
+ usb_set_intfdata(interface, ati_remote);
+ return 0;
+
+ fail3: usb_kill_urb(ati_remote->irq_urb);
+ usb_kill_urb(ati_remote->out_urb);
+ fail2: ati_remote_free_buffers(ati_remote);
+ fail1: input_free_device(input_dev);
+ kfree(ati_remote);
+ return err;
+}
+
+/*
+ * ati_remote_disconnect
+ */
+static void ati_remote_disconnect(struct usb_interface *interface)
+{
+ struct ati_remote *ati_remote;
+
+ ati_remote = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ if (!ati_remote) {
+ warn("%s - null device?\n", __FUNCTION__);
+ return;
+ }
+
+ usb_kill_urb(ati_remote->irq_urb);
+ usb_kill_urb(ati_remote->out_urb);
+ input_unregister_device(ati_remote->idev);
+ ati_remote_free_buffers(ati_remote);
+ kfree(ati_remote);
+}
+
+/*
+ * ati_remote_init
+ */
+static int __init ati_remote_init(void)
+{
+ int result;
+
+ result = usb_register(&ati_remote_driver);
+ if (result)
+ err("usb_register error #%d\n", result);
+ else
+ info("Registered USB driver " DRIVER_DESC " v. " DRIVER_VERSION);
+
+ return result;
+}
+
+/*
+ * ati_remote_exit
+ */
+static void __exit ati_remote_exit(void)
+{
+ usb_deregister(&ati_remote_driver);
+}
+
+/*
+ * module specification
+ */
+
+module_init(ati_remote_init);
+module_exit(ati_remote_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
new file mode 100644
index 00000000000..1031543e5c3
--- /dev/null
+++ b/drivers/input/misc/ati_remote2.c
@@ -0,0 +1,543 @@
+/*
+ * ati_remote2 - ATI/Philips USB RF remote driver
+ *
+ * Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi>
+ * Copyright (C) 2007 Peter Stokes <linux@dadeos.freeserve.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/usb/input.h>
+
+#define DRIVER_DESC "ATI/Philips USB RF remote driver"
+#define DRIVER_VERSION "0.2"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
+MODULE_LICENSE("GPL");
+
+/*
+ * ATI Remote Wonder II Channel Configuration
+ *
+ * The remote control can by assigned one of sixteen "channels" in order to facilitate
+ * the use of multiple remote controls within range of each other.
+ * A remote's "channel" may be altered by pressing and holding the "PC" button for
+ * approximately 3 seconds, after which the button will slowly flash the count of the
+ * currently configured "channel", using the numeric keypad enter a number between 1 and
+ * 16 and then the "PC" button again, the button will slowly flash the count of the
+ * newly configured "channel".
+ */
+
+static unsigned int channel_mask = 0xFFFF;
+module_param(channel_mask, uint, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
+
+static unsigned int mode_mask = 0x1F;
+module_param(mode_mask, uint, 0644);
+MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
+
+static struct usb_device_id ati_remote2_id_table[] = {
+ { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
+
+static struct {
+ int hw_code;
+ int key_code;
+} ati_remote2_key_table[] = {
+ { 0x00, KEY_0 },
+ { 0x01, KEY_1 },
+ { 0x02, KEY_2 },
+ { 0x03, KEY_3 },
+ { 0x04, KEY_4 },
+ { 0x05, KEY_5 },
+ { 0x06, KEY_6 },
+ { 0x07, KEY_7 },
+ { 0x08, KEY_8 },
+ { 0x09, KEY_9 },
+ { 0x0c, KEY_POWER },
+ { 0x0d, KEY_MUTE },
+ { 0x10, KEY_VOLUMEUP },
+ { 0x11, KEY_VOLUMEDOWN },
+ { 0x20, KEY_CHANNELUP },
+ { 0x21, KEY_CHANNELDOWN },
+ { 0x28, KEY_FORWARD },
+ { 0x29, KEY_REWIND },
+ { 0x2c, KEY_PLAY },
+ { 0x30, KEY_PAUSE },
+ { 0x31, KEY_STOP },
+ { 0x37, KEY_RECORD },
+ { 0x38, KEY_DVD },
+ { 0x39, KEY_TV },
+ { 0x54, KEY_MENU },
+ { 0x58, KEY_UP },
+ { 0x59, KEY_DOWN },
+ { 0x5a, KEY_LEFT },
+ { 0x5b, KEY_RIGHT },
+ { 0x5c, KEY_OK },
+ { 0x78, KEY_A },
+ { 0x79, KEY_B },
+ { 0x7a, KEY_C },
+ { 0x7b, KEY_D },
+ { 0x7c, KEY_E },
+ { 0x7d, KEY_F },
+ { 0x82, KEY_ENTER },
+ { 0x8e, KEY_VENDOR },
+ { 0x96, KEY_COFFEE },
+ { 0xa9, BTN_LEFT },
+ { 0xaa, BTN_RIGHT },
+ { 0xbe, KEY_QUESTION },
+ { 0xd5, KEY_FRONT },
+ { 0xd0, KEY_EDIT },
+ { 0xf9, KEY_INFO },
+ { (0x00 << 8) | 0x3f, KEY_PROG1 },
+ { (0x01 << 8) | 0x3f, KEY_PROG2 },
+ { (0x02 << 8) | 0x3f, KEY_PROG3 },
+ { (0x03 << 8) | 0x3f, KEY_PROG4 },
+ { (0x04 << 8) | 0x3f, KEY_PC },
+ { 0, KEY_RESERVED }
+};
+
+struct ati_remote2 {
+ struct input_dev *idev;
+ struct usb_device *udev;
+
+ struct usb_interface *intf[2];
+ struct usb_endpoint_descriptor *ep[2];
+ struct urb *urb[2];
+ void *buf[2];
+ dma_addr_t buf_dma[2];
+
+ unsigned long jiffies;
+ int mode;
+
+ char name[64];
+ char phys[64];
+};
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote2_disconnect(struct usb_interface *interface);
+
+static struct usb_driver ati_remote2_driver = {
+ .name = "ati_remote2",
+ .probe = ati_remote2_probe,
+ .disconnect = ati_remote2_disconnect,
+ .id_table = ati_remote2_id_table,
+};
+
+static int ati_remote2_open(struct input_dev *idev)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+ int r;
+
+ r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
+ if (r) {
+ dev_err(&ar2->intf[0]->dev,
+ "%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
+ return r;
+ }
+ r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
+ if (r) {
+ usb_kill_urb(ar2->urb[0]);
+ dev_err(&ar2->intf[1]->dev,
+ "%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
+ return r;
+ }
+
+ return 0;
+}
+
+static void ati_remote2_close(struct input_dev *idev)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+
+ usb_kill_urb(ar2->urb[0]);
+ usb_kill_urb(ar2->urb[1]);
+}
+
+static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev = ar2->idev;
+ u8 *data = ar2->buf[0];
+ int channel, mode;
+
+ channel = data[0] >> 4;
+
+ if (!((1 << channel) & channel_mask))
+ return;
+
+ mode = data[0] & 0x0F;
+
+ if (mode > 4) {
+ dev_err(&ar2->intf[0]->dev,
+ "Unknown mode byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ if (!((1 << mode) & mode_mask))
+ return;
+
+ input_event(idev, EV_REL, REL_X, (s8) data[1]);
+ input_event(idev, EV_REL, REL_Y, (s8) data[2]);
+ input_sync(idev);
+}
+
+static int ati_remote2_lookup(unsigned int hw_code)
+{
+ int i;
+
+ for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
+ if (ati_remote2_key_table[i].hw_code == hw_code)
+ return i;
+
+ return -1;
+}
+
+static void ati_remote2_input_key(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev = ar2->idev;
+ u8 *data = ar2->buf[1];
+ int channel, mode, hw_code, index;
+
+ channel = data[0] >> 4;
+
+ if (!((1 << channel) & channel_mask))
+ return;
+
+ mode = data[0] & 0x0F;
+
+ if (mode > 4) {
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown mode byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ hw_code = data[2];
+ /*
+ * Mode keys (AUX1-AUX4, PC) all generate the same code byte.
+ * Use the mode byte to figure out which one was pressed.
+ */
+ if (hw_code == 0x3f) {
+ /*
+ * For some incomprehensible reason the mouse pad generates
+ * events which look identical to the events from the last
+ * pressed mode key. Naturally we don't want to generate key
+ * events for the mouse pad so we filter out any subsequent
+ * events from the same mode key.
+ */
+ if (ar2->mode == mode)
+ return;
+
+ if (data[1] == 0)
+ ar2->mode = mode;
+
+ hw_code |= mode << 8;
+ }
+
+ if (!((1 << mode) & mode_mask))
+ return;
+
+ index = ati_remote2_lookup(hw_code);
+ if (index < 0) {
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown code byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ switch (data[1]) {
+ case 0: /* release */
+ break;
+ case 1: /* press */
+ ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
+ break;
+ case 2: /* repeat */
+
+ /* No repeat for mouse buttons. */
+ if (ati_remote2_key_table[index].key_code == BTN_LEFT ||
+ ati_remote2_key_table[index].key_code == BTN_RIGHT)
+ return;
+
+ if (!time_after_eq(jiffies, ar2->jiffies))
+ return;
+
+ ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
+ break;
+ default:
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown state byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]);
+ input_sync(idev);
+}
+
+static void ati_remote2_complete_mouse(struct urb *urb)
+{
+ struct ati_remote2 *ar2 = urb->context;
+ int r;
+
+ switch (urb->status) {
+ case 0:
+ ati_remote2_input_mouse(ar2);
+ break;
+ case -ENOENT:
+ case -EILSEQ:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&ar2->intf[0]->dev,
+ "%s(): urb status = %d\n", __FUNCTION__, urb->status);
+ return;
+ default:
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): urb status = %d\n", __FUNCTION__, urb->status);
+ }
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
+}
+
+static void ati_remote2_complete_key(struct urb *urb)
+{
+ struct ati_remote2 *ar2 = urb->context;
+ int r;
+
+ switch (urb->status) {
+ case 0:
+ ati_remote2_input_key(ar2);
+ break;
+ case -ENOENT:
+ case -EILSEQ:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&ar2->intf[1]->dev,
+ "%s(): urb status = %d\n", __FUNCTION__, urb->status);
+ return;
+ default:
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): urb status = %d\n", __FUNCTION__, urb->status);
+ }
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
+}
+
+static int ati_remote2_input_init(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev;
+ int i, retval;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ ar2->idev = idev;
+ input_set_drvdata(idev, ar2);
+
+ idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL);
+ idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
+ idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
+ set_bit(ati_remote2_key_table[i].key_code, idev->keybit);
+
+ idev->rep[REP_DELAY] = 250;
+ idev->rep[REP_PERIOD] = 33;
+
+ idev->open = ati_remote2_open;
+ idev->close = ati_remote2_close;
+
+ idev->name = ar2->name;
+ idev->phys = ar2->phys;
+
+ usb_to_input_id(ar2->udev, &idev->id);
+ idev->dev.parent = &ar2->udev->dev;
+
+ retval = input_register_device(idev);
+ if (retval)
+ input_free_device(idev);
+
+ return retval;
+}
+
+static int ati_remote2_urb_init(struct ati_remote2 *ar2)
+{
+ struct usb_device *udev = ar2->udev;
+ int i, pipe, maxp;
+
+ for (i = 0; i < 2; i++) {
+ ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
+ if (!ar2->buf[i])
+ return -ENOMEM;
+
+ ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ar2->urb[i])
+ return -ENOMEM;
+
+ pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = maxp > 4 ? 4 : maxp;
+
+ usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
+ i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
+ ar2, ar2->ep[i]->bInterval);
+ ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
+ ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ }
+
+ return 0;
+}
+
+static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ usb_free_urb(ar2->urb[i]);
+ usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
+ }
+}
+
+static int ati_remote2_setup(struct ati_remote2 *ar2)
+{
+ int r, i, channel;
+
+ /*
+ * Configure receiver to only accept input from remote "channel"
+ * channel == 0 -> Accept input from any remote channel
+ * channel == 1 -> Only accept input from remote channel 1
+ * channel == 2 -> Only accept input from remote channel 2
+ * ...
+ * channel == 16 -> Only accept input from remote channel 16
+ */
+
+ channel = 0;
+ for (i = 0; i < 16; i++) {
+ if ((1 << i) & channel_mask) {
+ if (!(~(1 << i) & 0xFFFF & channel_mask))
+ channel = i + 1;
+ break;
+ }
+ }
+
+ r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
+ 0x20,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (r) {
+ dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
+ __FUNCTION__, r);
+ return r;
+ }
+
+ return 0;
+}
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *alt = interface->cur_altsetting;
+ struct ati_remote2 *ar2;
+ int r;
+
+ if (alt->desc.bInterfaceNumber)
+ return -ENODEV;
+
+ ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
+ if (!ar2)
+ return -ENOMEM;
+
+ ar2->udev = udev;
+
+ ar2->intf[0] = interface;
+ ar2->ep[0] = &alt->endpoint[0].desc;
+
+ ar2->intf[1] = usb_ifnum_to_if(udev, 1);
+ r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
+ if (r)
+ goto fail1;
+ alt = ar2->intf[1]->cur_altsetting;
+ ar2->ep[1] = &alt->endpoint[0].desc;
+
+ r = ati_remote2_urb_init(ar2);
+ if (r)
+ goto fail2;
+
+ r = ati_remote2_setup(ar2);
+ if (r)
+ goto fail2;
+
+ usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
+ strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
+
+ strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
+
+ r = ati_remote2_input_init(ar2);
+ if (r)
+ goto fail2;
+
+ usb_set_intfdata(interface, ar2);
+
+ return 0;
+
+ fail2:
+ ati_remote2_urb_cleanup(ar2);
+
+ usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+ fail1:
+ kfree(ar2);
+
+ return r;
+}
+
+static void ati_remote2_disconnect(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+
+ if (alt->desc.bInterfaceNumber)
+ return;
+
+ ar2 = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ input_unregister_device(ar2->idev);
+
+ ati_remote2_urb_cleanup(ar2);
+
+ usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+
+ kfree(ar2);
+}
+
+static int __init ati_remote2_init(void)
+{
+ int r;
+
+ r = usb_register(&ati_remote2_driver);
+ if (r)
+ printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r);
+ else
+ printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n");
+
+ return r;
+}
+
+static void __exit ati_remote2_exit(void)
+{
+ usb_deregister(&ati_remote2_driver);
+}
+
+module_init(ati_remote2_init);
+module_exit(ati_remote2_exit);
diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c
new file mode 100644
index 00000000000..064b0793601
--- /dev/null
+++ b/drivers/input/misc/cobalt_btns.c
@@ -0,0 +1,172 @@
+/*
+ * Cobalt button interface driver.
+ *
+ * Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define BUTTONS_POLL_INTERVAL 30 /* msec */
+#define BUTTONS_COUNT_THRESHOLD 3
+#define BUTTONS_STATUS_MASK 0xfe000000
+
+struct buttons_dev {
+ struct input_polled_dev *poll_dev;
+ void __iomem *reg;
+};
+
+struct buttons_map {
+ uint32_t mask;
+ int keycode;
+ int count;
+};
+
+static struct buttons_map buttons_map[] = {
+ { 0x02000000, KEY_RESTART, },
+ { 0x04000000, KEY_LEFT, },
+ { 0x08000000, KEY_UP, },
+ { 0x10000000, KEY_DOWN, },
+ { 0x20000000, KEY_RIGHT, },
+ { 0x40000000, KEY_ENTER, },
+ { 0x80000000, KEY_SELECT, },
+};
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+ struct buttons_map *button = buttons_map;
+ struct buttons_dev *bdev = dev->private;
+ struct input_dev *input = dev->input;
+ uint32_t status;
+ int i;
+
+ status = readl(bdev->reg);
+ status = ~status & BUTTONS_STATUS_MASK;
+
+ for (i = 0; i < ARRAY_SIZE(buttons_map); i++) {
+ if (status & button->mask) {
+ button->count++;
+ } else {
+ if (button->count >= BUTTONS_COUNT_THRESHOLD) {
+ input_report_key(input, button->keycode, 0);
+ input_sync(input);
+ }
+ button->count = 0;
+ }
+
+ if (button->count == BUTTONS_COUNT_THRESHOLD) {
+ input_report_key(input, button->keycode, 1);
+ input_sync(input);
+ }
+
+ button++;
+ }
+}
+
+static int __devinit cobalt_buttons_probe(struct platform_device *pdev)
+{
+ struct buttons_dev *bdev;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input;
+ struct resource *res;
+ int error, i;
+
+ bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
+ poll_dev = input_allocate_polled_device();
+ if (!bdev || !poll_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ poll_dev->private = bdev;
+ poll_dev->poll = handle_buttons;
+ poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+
+ input = poll_dev->input;
+ input->name = "Cobalt buttons";
+ input->phys = "cobalt/input0";
+ input->id.bustype = BUS_HOST;
+ input->cdev.dev = &pdev->dev;
+
+ input->evbit[0] = BIT(EV_KEY);
+ for (i = 0; i < ARRAY_SIZE(buttons_map); i++) {
+ set_bit(buttons_map[i].keycode, input->keybit);
+ buttons_map[i].count = 0;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ bdev->poll_dev = poll_dev;
+ bdev->reg = ioremap(res->start, res->end - res->start + 1);
+ dev_set_drvdata(&pdev->dev, bdev);
+
+ error = input_register_polled_device(poll_dev);
+ if (error)
+ goto err_iounmap;
+
+ return 0;
+
+ err_iounmap:
+ iounmap(bdev->reg);
+ err_free_mem:
+ input_free_polled_device(poll_dev);
+ kfree(bdev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return error;
+}
+
+static int __devexit cobalt_buttons_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct buttons_dev *bdev = dev_get_drvdata(dev);
+
+ input_unregister_polled_device(bdev->poll_dev);
+ input_free_polled_device(bdev->poll_dev);
+ iounmap(bdev->reg);
+ kfree(bdev);
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver cobalt_buttons_driver = {
+ .probe = cobalt_buttons_probe,
+ .remove = __devexit_p(cobalt_buttons_remove),
+ .driver = {
+ .name = "Cobalt buttons",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cobalt_buttons_init(void)
+{
+ return platform_driver_register(&cobalt_buttons_driver);
+}
+
+static void __exit cobalt_buttons_exit(void)
+{
+ platform_driver_unregister(&cobalt_buttons_driver);
+}
+
+module_init(cobalt_buttons_init);
+module_exit(cobalt_buttons_exit);
diff --git a/drivers/input/misc/input-polldev.c b/drivers/input/misc/input-polldev.c
new file mode 100644
index 00000000000..1b2b9c9c5d8
--- /dev/null
+++ b/drivers/input/misc/input-polldev.c
@@ -0,0 +1,171 @@
+/*
+ * Generic implementation of a polled input device
+
+ * Copyright (c) 2007 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/input-polldev.h>
+
+static DEFINE_MUTEX(polldev_mutex);
+static int polldev_users;
+static struct workqueue_struct *polldev_wq;
+
+static int input_polldev_start_workqueue(void)
+{
+ int retval;
+
+ retval = mutex_lock_interruptible(&polldev_mutex);
+ if (retval)
+ return retval;
+
+ if (!polldev_users) {
+ polldev_wq = create_singlethread_workqueue("ipolldevd");
+ if (!polldev_wq) {
+ printk(KERN_ERR "input-polldev: failed to create "
+ "ipolldevd workqueue\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+ }
+
+ polldev_users++;
+
+ out:
+ mutex_unlock(&polldev_mutex);
+ return retval;
+}
+
+static void input_polldev_stop_workqueue(void)
+{
+ mutex_lock(&polldev_mutex);
+
+ if (!--polldev_users)
+ destroy_workqueue(polldev_wq);
+
+ mutex_unlock(&polldev_mutex);
+}
+
+static void input_polled_device_work(struct work_struct *work)
+{
+ struct input_polled_dev *dev =
+ container_of(work, struct input_polled_dev, work.work);
+
+ dev->poll(dev);
+ queue_delayed_work(polldev_wq, &dev->work,
+ msecs_to_jiffies(dev->poll_interval));
+}
+
+static int input_open_polled_device(struct input_dev *input)
+{
+ struct input_polled_dev *dev = input->private;
+ int error;
+
+ error = input_polldev_start_workqueue();
+ if (error)
+ return error;
+
+ if (dev->flush)
+ dev->flush(dev);
+
+ queue_delayed_work(polldev_wq, &dev->work,
+ msecs_to_jiffies(dev->poll_interval));
+
+ return 0;
+}
+
+static void input_close_polled_device(struct input_dev *input)
+{
+ struct input_polled_dev *dev = input->private;
+
+ cancel_rearming_delayed_workqueue(polldev_wq, &dev->work);
+ input_polldev_stop_workqueue();
+}
+
+/**
+ * input_allocate_polled_device - allocated memory polled device
+ *
+ * The function allocates memory for a polled device and also
+ * for an input device associated with this polled device.
+ */
+struct input_polled_dev *input_allocate_polled_device(void)
+{
+ struct input_polled_dev *dev;
+
+ dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ dev->input = input_allocate_device();
+ if (!dev->input) {
+ kfree(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL(input_allocate_polled_device);
+
+/**
+ * input_free_polled_device - free memory allocated for polled device
+ * @dev: device to free
+ *
+ * The function frees memory allocated for polling device and drops
+ * reference to the associated input device (if present).
+ */
+void input_free_polled_device(struct input_polled_dev *dev)
+{
+ if (dev) {
+ input_free_device(dev->input);
+ kfree(dev);
+ }
+}
+EXPORT_SYMBOL(input_free_polled_device);
+
+/**
+ * input_register_polled_device - register polled device
+ * @dev: device to register
+ *
+ * The function registers previously initialized polled input device
+ * with input layer. The device should be allocated with call to
+ * input_allocate_polled_device(). Callers should also set up poll()
+ * method and set up capabilities (id, name, phys, bits) of the
+ * corresponing input_dev structure.
+ */
+int input_register_polled_device(struct input_polled_dev *dev)
+{
+ struct input_dev *input = dev->input;
+
+ INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
+ if (!dev->poll_interval)
+ dev->poll_interval = 500;
+ input->private = dev;
+ input->open = input_open_polled_device;
+ input->close = input_close_polled_device;
+
+ return input_register_device(input);
+}
+EXPORT_SYMBOL(input_register_polled_device);
+
+/**
+ * input_unregister_polled_device - unregister polled device
+ * @dev: device to unregister
+ *
+ * The function unregisters previously registered polled input
+ * device from input layer. Polling is stopped and device is
+ * ready to be freed with call to input_free_polled_device().
+ * Callers should not attempt to access dev->input pointer
+ * after calling this function.
+ */
+void input_unregister_polled_device(struct input_polled_dev *dev)
+{
+ input_unregister_device(dev->input);
+ dev->input = NULL;
+}
+EXPORT_SYMBOL(input_unregister_polled_device);
+
diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c
index 105c6fc2782..3d4b619dada 100644
--- a/drivers/input/misc/ixp4xx-beeper.c
+++ b/drivers/input/misc/ixp4xx-beeper.c
@@ -51,7 +51,7 @@ static void ixp4xx_spkr_control(unsigned int pin, unsigned int count)
static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
- unsigned int pin = (unsigned int) dev->private;
+ unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
unsigned int count = 0;
if (type != EV_SND)
@@ -99,14 +99,15 @@ static int __devinit ixp4xx_spkr_probe(struct platform_device *dev)
if (!input_dev)
return -ENOMEM;
- input_dev->private = (void *) dev->id;
+ input_set_drvdata(input_dev, (void *) dev->id);
+
input_dev->name = "ixp4xx beeper",
input_dev->phys = "ixp4xx/gpio";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x001f;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &dev->dev;
+ input_dev->dev.parent = &dev->dev;
input_dev->evbit[0] = BIT(EV_SND);
input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
@@ -136,7 +137,7 @@ static int __devinit ixp4xx_spkr_probe(struct platform_device *dev)
static int __devexit ixp4xx_spkr_remove(struct platform_device *dev)
{
struct input_dev *input_dev = platform_get_drvdata(dev);
- unsigned int pin = (unsigned int) input_dev->private;
+ unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
input_unregister_device(input_dev);
platform_set_drvdata(dev, NULL);
@@ -153,7 +154,7 @@ static int __devexit ixp4xx_spkr_remove(struct platform_device *dev)
static void ixp4xx_spkr_shutdown(struct platform_device *dev)
{
struct input_dev *input_dev = platform_get_drvdata(dev);
- unsigned int pin = (unsigned int) input_dev->private;
+ unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
/* turn off the speaker */
disable_irq(IRQ_IXP4XX_TIMER2);
diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c
new file mode 100644
index 00000000000..1bffc9fa98c
--- /dev/null
+++ b/drivers/input/misc/keyspan_remote.c
@@ -0,0 +1,592 @@
+/*
+ * keyspan_remote: USB driver for the Keyspan DMR
+ *
+ * Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com)
+ *
+ * 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, version 2.
+ *
+ * This driver has been put together with the support of Innosys, Inc.
+ * and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Michael Downey <downey@zymeta.com>"
+#define DRIVER_DESC "Driver for the USB Keyspan remote control."
+#define DRIVER_LICENSE "GPL"
+
+/* Parameters that can be passed to the driver. */
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+/* Vendor and product ids */
+#define USB_KEYSPAN_VENDOR_ID 0x06CD
+#define USB_KEYSPAN_PRODUCT_UIA11 0x0202
+
+/* Defines for converting the data from the remote. */
+#define ZERO 0x18
+#define ZERO_MASK 0x1F /* 5 bits for a 0 */
+#define ONE 0x3C
+#define ONE_MASK 0x3F /* 6 bits for a 1 */
+#define SYNC 0x3F80
+#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */
+#define STOP 0x00
+#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */
+#define GAP 0xFF
+
+#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */
+
+/* table of devices that work with this driver */
+static struct usb_device_id keyspan_table[] = {
+ { USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) },
+ { } /* Terminating entry */
+};
+
+/* Structure to store all the real stuff that a remote sends to us. */
+struct keyspan_message {
+ u16 system;
+ u8 button;
+ u8 toggle;
+};
+
+/* Structure used for all the bit testing magic needed to be done. */
+struct bit_tester {
+ u32 tester;
+ int len;
+ int pos;
+ int bits_left;
+ u8 buffer[32];
+};
+
+/* Structure to hold all of our driver specific stuff */
+struct usb_keyspan {
+ char name[128];
+ char phys[64];
+ struct usb_device* udev;
+ struct input_dev *input;
+ struct usb_interface* interface;
+ struct usb_endpoint_descriptor* in_endpoint;
+ struct urb* irq_urb;
+ int open;
+ dma_addr_t in_dma;
+ unsigned char* in_buffer;
+
+ /* variables used to parse messages from remote. */
+ struct bit_tester data;
+ int stage;
+ int toggle;
+};
+
+/*
+ * Table that maps the 31 possible keycodes to input keys.
+ * Currently there are 15 and 17 button models so RESERVED codes
+ * are blank areas in the mapping.
+ */
+static const int keyspan_key_table[] = {
+ KEY_RESERVED, /* 0 is just a place holder. */
+ KEY_RESERVED,
+ KEY_STOP,
+ KEY_PLAYCD,
+ KEY_RESERVED,
+ KEY_PREVIOUSSONG,
+ KEY_REWIND,
+ KEY_FORWARD,
+ KEY_NEXTSONG,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PAUSE,
+ KEY_VOLUMEUP,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+ KEY_UP,
+ KEY_RESERVED,
+ KEY_MUTE,
+ KEY_LEFT,
+ KEY_ENTER,
+ KEY_RIGHT,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_DOWN,
+ KEY_RESERVED,
+ KEY_KPASTERISK,
+ KEY_RESERVED,
+ KEY_MENU
+};
+
+static struct usb_driver keyspan_driver;
+
+/*
+ * Debug routine that prints out what we've received from the remote.
+ */
+static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/
+{
+ char codes[4 * RECV_SIZE];
+ int i;
+
+ for (i = 0; i < RECV_SIZE; i++)
+ snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]);
+
+ dev_info(&dev->udev->dev, "%s\n", codes);
+}
+
+/*
+ * Routine that manages the bit_tester structure. It makes sure that there are
+ * at least bits_needed bits loaded into the tester.
+ */
+static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed)
+{
+ if (dev->data.bits_left >= bits_needed)
+ return 0;
+
+ /*
+ * Somehow we've missed the last message. The message will be repeated
+ * though so it's not too big a deal
+ */
+ if (dev->data.pos >= dev->data.len) {
+ dev_dbg(&dev->udev->dev,
+ "%s - Error ran out of data. pos: %d, len: %d\n",
+ __FUNCTION__, dev->data.pos, dev->data.len);
+ return -1;
+ }
+
+ /* Load as much as we can into the tester. */
+ while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) &&
+ (dev->data.pos < dev->data.len)) {
+ dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left);
+ dev->data.bits_left += 8;
+ }
+
+ return 0;
+}
+
+/*
+ * Routine that handles all the logic needed to parse out the message from the remote.
+ */
+static void keyspan_check_data(struct usb_keyspan *remote)
+{
+ int i;
+ int found = 0;
+ struct keyspan_message message;
+
+ switch(remote->stage) {
+ case 0:
+ /*
+ * In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler.
+ * So the first byte that isn't a FF should be the start of a new message.
+ */
+ for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i);
+
+ if (i < RECV_SIZE) {
+ memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE);
+ remote->data.len = RECV_SIZE;
+ remote->data.pos = 0;
+ remote->data.tester = 0;
+ remote->data.bits_left = 0;
+ remote->stage = 1;
+ }
+ break;
+
+ case 1:
+ /*
+ * Stage 1 we should have 16 bytes and should be able to detect a
+ * SYNC. The SYNC is 14 bits, 7 0's and then 7 1's.
+ */
+ memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
+ remote->data.len += RECV_SIZE;
+
+ found = 0;
+ while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) {
+ for (i = 0; i < 8; ++i) {
+ if (keyspan_load_tester(remote, 14) != 0) {
+ remote->stage = 0;
+ return;
+ }
+
+ if ((remote->data.tester & SYNC_MASK) == SYNC) {
+ remote->data.tester = remote->data.tester >> 14;
+ remote->data.bits_left -= 14;
+ found = 1;
+ break;
+ } else {
+ remote->data.tester = remote->data.tester >> 1;
+ --remote->data.bits_left;
+ }
+ }
+ }
+
+ if (!found) {
+ remote->stage = 0;
+ remote->data.len = 0;
+ } else {
+ remote->stage = 2;
+ }
+ break;
+
+ case 2:
+ /*
+ * Stage 2 we should have 24 bytes which will be enough for a full
+ * message. We need to parse out the system code, button code,
+ * toggle code, and stop.
+ */
+ memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
+ remote->data.len += RECV_SIZE;
+
+ message.system = 0;
+ for (i = 0; i < 9; i++) {
+ keyspan_load_tester(remote, 6);
+
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.system = message.system << 1;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.system = (message.system << 1) + 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ err("%s - Unknown sequence found in system data.\n", __FUNCTION__);
+ remote->stage = 0;
+ return;
+ }
+ }
+
+ message.button = 0;
+ for (i = 0; i < 5; i++) {
+ keyspan_load_tester(remote, 6);
+
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.button = message.button << 1;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.button = (message.button << 1) + 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ err("%s - Unknown sequence found in button data.\n", __FUNCTION__);
+ remote->stage = 0;
+ return;
+ }
+ }
+
+ keyspan_load_tester(remote, 6);
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.toggle = 0;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.toggle = 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ err("%s - Error in message, invalid toggle.\n", __FUNCTION__);
+ remote->stage = 0;
+ return;
+ }
+
+ keyspan_load_tester(remote, 5);
+ if ((remote->data.tester & STOP_MASK) == STOP) {
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else {
+ err("Bad message recieved, no stop bit found.\n");
+ }
+
+ dev_dbg(&remote->udev->dev,
+ "%s found valid message: system: %d, button: %d, toggle: %d\n",
+ __FUNCTION__, message.system, message.button, message.toggle);
+
+ if (message.toggle != remote->toggle) {
+ input_report_key(remote->input, keyspan_key_table[message.button], 1);
+ input_report_key(remote->input, keyspan_key_table[message.button], 0);
+ input_sync(remote->input);
+ remote->toggle = message.toggle;
+ }
+
+ remote->stage = 0;
+ break;
+ }
+}
+
+/*
+ * Routine for sending all the initialization messages to the remote.
+ */
+static int keyspan_setup(struct usb_device* dev)
+{
+ int retval = 0;
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
+ __FUNCTION__, retval);
+ return(retval);
+ }
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
+ __FUNCTION__, retval);
+ return(retval);
+ }
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
+ __FUNCTION__, retval);
+ return(retval);
+ }
+
+ dev_dbg(&dev->dev, "%s - Setup complete.\n", __FUNCTION__);
+ return(retval);
+}
+
+/*
+ * Routine used to handle a new message that has come in.
+ */
+static void keyspan_irq_recv(struct urb *urb)
+{
+ struct usb_keyspan *dev = urb->context;
+ int retval;
+
+ /* Check our status in case we need to bail out early. */
+ switch (urb->status) {
+ case 0:
+ break;
+
+ /* Device went away so don't keep trying to read from it. */
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+
+ default:
+ goto resubmit;
+ break;
+ }
+
+ if (debug)
+ keyspan_print(dev);
+
+ keyspan_check_data(dev);
+
+resubmit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result: %d", __FUNCTION__, retval);
+}
+
+static int keyspan_open(struct input_dev *dev)
+{
+ struct usb_keyspan *remote = input_get_drvdata(dev);
+
+ remote->irq_urb->dev = remote->udev;
+ if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void keyspan_close(struct input_dev *dev)
+{
+ struct usb_keyspan *remote = input_get_drvdata(dev);
+
+ usb_kill_urb(remote->irq_urb);
+}
+
+static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface)
+{
+
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+ endpoint = &iface->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint)) {
+ /* we found our interrupt in endpoint */
+ return endpoint;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Routine that sets up the driver to handle a specific USB device detected on the bus.
+ */
+static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_keyspan *remote;
+ struct input_dev *input_dev;
+ int i, error;
+
+ endpoint = keyspan_get_in_endpoint(interface->cur_altsetting);
+ if (!endpoint)
+ return -ENODEV;
+
+ remote = kzalloc(sizeof(*remote), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!remote || !input_dev) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ remote->udev = udev;
+ remote->input = input_dev;
+ remote->interface = interface;
+ remote->in_endpoint = endpoint;
+ remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */
+
+ remote->in_buffer = usb_buffer_alloc(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma);
+ if (!remote->in_buffer) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!remote->irq_urb) {
+ error = -ENOMEM;
+ goto fail2;
+ }
+
+ error = keyspan_setup(udev);
+ if (error) {
+ error = -ENODEV;
+ goto fail3;
+ }
+
+ if (udev->manufacturer)
+ strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
+
+ if (udev->product) {
+ if (udev->manufacturer)
+ strlcat(remote->name, " ", sizeof(remote->name));
+ strlcat(remote->name, udev->product, sizeof(remote->name));
+ }
+
+ if (!strlen(remote->name))
+ snprintf(remote->name, sizeof(remote->name),
+ "USB Keyspan Remote %04x:%04x",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ usb_make_path(udev, remote->phys, sizeof(remote->phys));
+ strlcat(remote->phys, "/input0", sizeof(remote->phys));
+
+ input_dev->name = remote->name;
+ input_dev->phys = remote->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &interface->dev;
+
+ input_dev->evbit[0] = BIT(EV_KEY); /* We will only report KEY events. */
+ for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++)
+ if (keyspan_key_table[i] != KEY_RESERVED)
+ set_bit(keyspan_key_table[i], input_dev->keybit);
+
+ input_set_drvdata(input_dev, remote);
+
+ input_dev->open = keyspan_open;
+ input_dev->close = keyspan_close;
+
+ /*
+ * Initialize the URB to access the device. The urb gets sent to the device in keyspan_open()
+ */
+ usb_fill_int_urb(remote->irq_urb,
+ remote->udev, usb_rcvintpipe(remote->udev, remote->in_endpoint->bEndpointAddress),
+ remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote,
+ remote->in_endpoint->bInterval);
+ remote->irq_urb->transfer_dma = remote->in_dma;
+ remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* we can register the device now, as it is ready */
+ error = input_register_device(remote->input);
+ if (error)
+ goto fail3;
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, remote);
+
+ return 0;
+
+ fail3: usb_free_urb(remote->irq_urb);
+ fail2: usb_buffer_free(udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
+ fail1: kfree(remote);
+ input_free_device(input_dev);
+
+ return error;
+}
+
+/*
+ * Routine called when a device is disconnected from the USB.
+ */
+static void keyspan_disconnect(struct usb_interface *interface)
+{
+ struct usb_keyspan *remote;
+
+ remote = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (remote) { /* We have a valid driver structure so clean up everything we allocated. */
+ input_unregister_device(remote->input);
+ usb_kill_urb(remote->irq_urb);
+ usb_free_urb(remote->irq_urb);
+ usb_buffer_free(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
+ kfree(remote);
+ }
+}
+
+/*
+ * Standard driver set up sections
+ */
+static struct usb_driver keyspan_driver =
+{
+ .name = "keyspan_remote",
+ .probe = keyspan_probe,
+ .disconnect = keyspan_disconnect,
+ .id_table = keyspan_table
+};
+
+static int __init usb_keyspan_init(void)
+{
+ int result;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&keyspan_driver);
+ if (result)
+ err("usb_register failed. Error number %d\n", result);
+
+ return result;
+}
+
+static void __exit usb_keyspan_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&keyspan_driver);
+}
+
+module_init(usb_keyspan_init);
+module_exit(usb_keyspan_exit);
+
+MODULE_DEVICE_TABLE(usb, keyspan_table);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c
index 8d6c3837bad..e9f26e766b4 100644
--- a/drivers/input/misc/m68kspkr.c
+++ b/drivers/input/misc/m68kspkr.c
@@ -63,7 +63,7 @@ static int __devinit m68kspkr_probe(struct platform_device *dev)
input_dev->id.vendor = 0x001f;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &dev->dev;
+ input_dev->dev.parent = &dev->dev;
input_dev->evbit[0] = BIT(EV_SND);
input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
diff --git a/drivers/input/misc/map_to_7segment.h b/drivers/input/misc/map_to_7segment.h
new file mode 100644
index 00000000000..a424094d9fe
--- /dev/null
+++ b/drivers/input/misc/map_to_7segment.h
@@ -0,0 +1,189 @@
+/*
+ * drivers/usb/input/map_to_7segment.h
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ * 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
+ */
+
+#ifndef MAP_TO_7SEGMENT_H
+#define MAP_TO_7SEGMENT_H
+
+/* This file provides translation primitives and tables for the conversion
+ * of (ASCII) characters to a 7-segments notation.
+ *
+ * The 7 segment's wikipedia notation below is used as standard.
+ * See: http://en.wikipedia.org/wiki/Seven_segment_display
+ *
+ * Notation: +-a-+
+ * f b
+ * +-g-+
+ * e c
+ * +-d-+
+ *
+ * Usage:
+ *
+ * Register a map variable, and fill it with a character set:
+ * static SEG7_DEFAULT_MAP(map_seg7);
+ *
+ *
+ * Then use for conversion:
+ * seg7 = map_to_seg7(&map_seg7, some_char);
+ * ...
+ *
+ * In device drivers it is recommended, if required, to make the char map
+ * accessible via the sysfs interface using the following scheme:
+ *
+ * static ssize_t show_map(struct device *dev, char *buf) {
+ * memcpy(buf, &map_seg7, sizeof(map_seg7));
+ * return sizeof(map_seg7);
+ * }
+ * static ssize_t store_map(struct device *dev, const char *buf, size_t cnt) {
+ * if(cnt != sizeof(map_seg7))
+ * return -EINVAL;
+ * memcpy(&map_seg7, buf, cnt);
+ * return cnt;
+ * }
+ * static DEVICE_ATTR(map_seg7, PERMS_RW, show_map, store_map);
+ *
+ * History:
+ * 2005-05-31 RFC linux-kernel@vger.kernel.org
+ */
+#include <linux/errno.h>
+
+
+#define BIT_SEG7_A 0
+#define BIT_SEG7_B 1
+#define BIT_SEG7_C 2
+#define BIT_SEG7_D 3
+#define BIT_SEG7_E 4
+#define BIT_SEG7_F 5
+#define BIT_SEG7_G 6
+#define BIT_SEG7_RESERVED 7
+
+struct seg7_conversion_map {
+ unsigned char table[128];
+};
+
+static inline int map_to_seg7(struct seg7_conversion_map *map, int c)
+{
+ return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL;
+}
+
+#define SEG7_CONVERSION_MAP(_name, _map) \
+ struct seg7_conversion_map _name = { .table = { _map } }
+
+/*
+ * It is recommended to use a facility that allows user space to redefine
+ * custom character sets for LCD devices. Please use a sysfs interface
+ * as described above.
+ */
+#define MAP_TO_SEG7_SYSFS_FILE "map_seg7"
+
+/*******************************************************************************
+ * ASCII conversion table
+ ******************************************************************************/
+
+#define _SEG7(l,a,b,c,d,e,f,g) \
+ ( a<<BIT_SEG7_A | b<<BIT_SEG7_B | c<<BIT_SEG7_C | d<<BIT_SEG7_D | \
+ e<<BIT_SEG7_E | f<<BIT_SEG7_F | g<<BIT_SEG7_G )
+
+#define _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+
+#define _MAP_33_47_ASCII_SEG7_SYMBOL \
+ _SEG7('!',0,0,0,0,1,1,0), _SEG7('"',0,1,0,0,0,1,0), _SEG7('#',0,1,1,0,1,1,0),\
+ _SEG7('$',1,0,1,1,0,1,1), _SEG7('%',0,0,1,0,0,1,0), _SEG7('&',1,0,1,1,1,1,1),\
+ _SEG7('\'',0,0,0,0,0,1,0),_SEG7('(',1,0,0,1,1,1,0), _SEG7(')',1,1,1,1,0,0,0),\
+ _SEG7('*',0,1,1,0,1,1,1), _SEG7('+',0,1,1,0,0,0,1), _SEG7(',',0,0,0,0,1,0,0),\
+ _SEG7('-',0,0,0,0,0,0,1), _SEG7('.',0,0,0,0,1,0,0), _SEG7('/',0,1,0,0,1,0,1),
+
+#define _MAP_48_57_ASCII_SEG7_NUMERIC \
+ _SEG7('0',1,1,1,1,1,1,0), _SEG7('1',0,1,1,0,0,0,0), _SEG7('2',1,1,0,1,1,0,1),\
+ _SEG7('3',1,1,1,1,0,0,1), _SEG7('4',0,1,1,0,0,1,1), _SEG7('5',1,0,1,1,0,1,1),\
+ _SEG7('6',1,0,1,1,1,1,1), _SEG7('7',1,1,1,0,0,0,0), _SEG7('8',1,1,1,1,1,1,1),\
+ _SEG7('9',1,1,1,1,0,1,1),
+
+#define _MAP_58_64_ASCII_SEG7_SYMBOL \
+ _SEG7(':',0,0,0,1,0,0,1), _SEG7(';',0,0,0,1,0,0,1), _SEG7('<',1,0,0,0,0,1,1),\
+ _SEG7('=',0,0,0,1,0,0,1), _SEG7('>',1,1,0,0,0,0,1), _SEG7('?',1,1,1,0,0,1,0),\
+ _SEG7('@',1,1,0,1,1,1,1),
+
+#define _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
+ _SEG7('A',1,1,1,0,1,1,1), _SEG7('B',1,1,1,1,1,1,1), _SEG7('C',1,0,0,1,1,1,0),\
+ _SEG7('D',1,1,1,1,1,1,0), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
+ _SEG7('G',1,1,1,1,0,1,1), _SEG7('H',0,1,1,0,1,1,1), _SEG7('I',0,1,1,0,0,0,0),\
+ _SEG7('J',0,1,1,1,0,0,0), _SEG7('K',0,1,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
+ _SEG7('M',1,1,1,0,1,1,0), _SEG7('N',1,1,1,0,1,1,0), _SEG7('O',1,1,1,1,1,1,0),\
+ _SEG7('P',1,1,0,0,1,1,1), _SEG7('Q',1,1,1,1,1,1,0), _SEG7('R',1,1,1,0,1,1,1),\
+ _SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('U',0,1,1,1,1,1,0),\
+ _SEG7('V',0,1,1,1,1,1,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
+ _SEG7('Y',0,1,1,0,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
+
+#define _MAP_91_96_ASCII_SEG7_SYMBOL \
+ _SEG7('[',1,0,0,1,1,1,0), _SEG7('\\',0,0,1,0,0,1,1),_SEG7(']',1,1,1,1,0,0,0),\
+ _SEG7('^',1,1,0,0,0,1,0), _SEG7('_',0,0,0,1,0,0,0), _SEG7('`',0,1,0,0,0,0,0),
+
+#define _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
+ _SEG7('A',1,1,1,0,1,1,1), _SEG7('b',0,0,1,1,1,1,1), _SEG7('c',0,0,0,1,1,0,1),\
+ _SEG7('d',0,1,1,1,1,0,1), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
+ _SEG7('G',1,1,1,1,0,1,1), _SEG7('h',0,0,1,0,1,1,1), _SEG7('i',0,0,1,0,0,0,0),\
+ _SEG7('j',0,0,1,1,0,0,0), _SEG7('k',0,0,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
+ _SEG7('M',1,1,1,0,1,1,0), _SEG7('n',0,0,1,0,1,0,1), _SEG7('o',0,0,1,1,1,0,1),\
+ _SEG7('P',1,1,0,0,1,1,1), _SEG7('q',1,1,1,0,0,1,1), _SEG7('r',0,0,0,0,1,0,1),\
+ _SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('u',0,0,1,1,1,0,0),\
+ _SEG7('v',0,0,1,1,1,0,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
+ _SEG7('y',0,1,1,1,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
+
+#define _MAP_123_126_ASCII_SEG7_SYMBOL \
+ _SEG7('{',1,0,0,1,1,1,0), _SEG7('|',0,0,0,0,1,1,0), _SEG7('}',1,1,1,1,0,0,0),\
+ _SEG7('~',1,0,0,0,0,0,0),
+
+/* Maps */
+
+/* This set tries to map as close as possible to the visible characteristics
+ * of the ASCII symbol, lowercase and uppercase letters may differ in
+ * presentation on the display.
+ */
+#define MAP_ASCII7SEG_ALPHANUM \
+ _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
+ _MAP_33_47_ASCII_SEG7_SYMBOL \
+ _MAP_48_57_ASCII_SEG7_NUMERIC \
+ _MAP_58_64_ASCII_SEG7_SYMBOL \
+ _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
+ _MAP_91_96_ASCII_SEG7_SYMBOL \
+ _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
+ _MAP_123_126_ASCII_SEG7_SYMBOL
+
+/* This set tries to map as close as possible to the symbolic characteristics
+ * of the ASCII character for maximum discrimination.
+ * For now this means all alpha chars are in lower case representations.
+ * (This for example facilitates the use of hex numbers with uppercase input.)
+ */
+#define MAP_ASCII7SEG_ALPHANUM_LC \
+ _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
+ _MAP_33_47_ASCII_SEG7_SYMBOL \
+ _MAP_48_57_ASCII_SEG7_NUMERIC \
+ _MAP_58_64_ASCII_SEG7_SYMBOL \
+ _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
+ _MAP_91_96_ASCII_SEG7_SYMBOL \
+ _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
+ _MAP_123_126_ASCII_SEG7_SYMBOL
+
+#define SEG7_DEFAULT_MAP(_name) \
+ SEG7_CONVERSION_MAP(_name,MAP_ASCII7SEG_ALPHANUM)
+
+#endif /* MAP_TO_7SEGMENT_H */
+
diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c
index afd322185bb..31989dcd922 100644
--- a/drivers/input/misc/pcspkr.c
+++ b/drivers/input/misc/pcspkr.c
@@ -78,7 +78,7 @@ static int __devinit pcspkr_probe(struct platform_device *dev)
pcspkr_dev->id.vendor = 0x001f;
pcspkr_dev->id.product = 0x0001;
pcspkr_dev->id.version = 0x0100;
- pcspkr_dev->cdev.dev = &dev->dev;
+ pcspkr_dev->dev.parent = &dev->dev;
pcspkr_dev->evbit[0] = BIT(EV_SND);
pcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c
new file mode 100644
index 00000000000..448a470d28f
--- /dev/null
+++ b/drivers/input/misc/powermate.c
@@ -0,0 +1,463 @@
+/*
+ * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
+ *
+ * v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
+ *
+ * This device is a anodised aluminium knob which connects over USB. It can measure
+ * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
+ * a spring for automatic release. The base contains a pair of LEDs which illuminate
+ * the translucent base. It rotates without limit and reports its relative rotation
+ * back to the host when polled by the USB controller.
+ *
+ * Testing with the knob I have has shown that it measures approximately 94 "clicks"
+ * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was
+ * a variable speed cordless electric drill) has shown that the device can measure
+ * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
+ * the host. If it counts more than 7 clicks before it is polled, it will wrap back
+ * to zero and start counting again. This was at quite high speed, however, almost
+ * certainly faster than the human hand could turn it. Griffin say that it loses a
+ * pulse or two on a direction change; the granularity is so fine that I never
+ * noticed this in practice.
+ *
+ * The device's microcontroller can be programmed to set the LED to either a constant
+ * intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
+ *
+ * Griffin were very happy to provide documentation and free hardware for development.
+ *
+ * Some userspace tools are available on the web: http://sowerbutts.com/powermate/
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/usb/input.h>
+
+#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */
+#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */
+#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */
+
+#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */
+#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */
+
+/* these are the command codes we send to the device */
+#define SET_STATIC_BRIGHTNESS 0x01
+#define SET_PULSE_ASLEEP 0x02
+#define SET_PULSE_AWAKE 0x03
+#define SET_PULSE_MODE 0x04
+
+/* these refer to bits in the powermate_device's requires_update field. */
+#define UPDATE_STATIC_BRIGHTNESS (1<<0)
+#define UPDATE_PULSE_ASLEEP (1<<1)
+#define UPDATE_PULSE_AWAKE (1<<2)
+#define UPDATE_PULSE_MODE (1<<3)
+
+/* at least two versions of the hardware exist, with differing payload
+ sizes. the first three bytes always contain the "interesting" data in
+ the relevant format. */
+#define POWERMATE_PAYLOAD_SIZE_MAX 6
+#define POWERMATE_PAYLOAD_SIZE_MIN 3
+struct powermate_device {
+ signed char *data;
+ dma_addr_t data_dma;
+ struct urb *irq, *config;
+ struct usb_ctrlrequest *configcr;
+ dma_addr_t configcr_dma;
+ struct usb_device *udev;
+ struct input_dev *input;
+ spinlock_t lock;
+ int static_brightness;
+ int pulse_speed;
+ int pulse_table;
+ int pulse_asleep;
+ int pulse_awake;
+ int requires_update; // physical settings which are out of sync
+ char phys[64];
+};
+
+static char pm_name_powermate[] = "Griffin PowerMate";
+static char pm_name_soundknob[] = "Griffin SoundKnob";
+
+static void powermate_config_complete(struct urb *urb);
+
+/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
+static void powermate_irq(struct urb *urb)
+{
+ struct powermate_device *pm = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ /* handle updates to device state */
+ input_report_key(pm->input, BTN_0, pm->data[0] & 0x01);
+ input_report_rel(pm->input, REL_DIAL, pm->data[1]);
+ input_sync(pm->input);
+
+exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+}
+
+/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
+static void powermate_sync_state(struct powermate_device *pm)
+{
+ if (pm->requires_update == 0)
+ return; /* no updates are required */
+ if (pm->config->status == -EINPROGRESS)
+ return; /* an update is already in progress; it'll issue this update when it completes */
+
+ if (pm->requires_update & UPDATE_PULSE_ASLEEP){
+ pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
+ pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
+ pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
+ }else if (pm->requires_update & UPDATE_PULSE_AWAKE){
+ pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
+ pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
+ pm->requires_update &= ~UPDATE_PULSE_AWAKE;
+ }else if (pm->requires_update & UPDATE_PULSE_MODE){
+ int op, arg;
+ /* the powermate takes an operation and an argument for its pulse algorithm.
+ the operation can be:
+ 0: divide the speed
+ 1: pulse at normal speed
+ 2: multiply the speed
+ the argument only has an effect for operations 0 and 2, and ranges between
+ 1 (least effect) to 255 (maximum effect).
+
+ thus, several states are equivalent and are coalesced into one state.
+
+ we map this onto a range from 0 to 510, with:
+ 0 -- 254 -- use divide (0 = slowest)
+ 255 -- use normal speed
+ 256 -- 510 -- use multiple (510 = fastest).
+
+ Only values of 'arg' quite close to 255 are particularly useful/spectacular.
+ */
+ if (pm->pulse_speed < 255) {
+ op = 0; // divide
+ arg = 255 - pm->pulse_speed;
+ } else if (pm->pulse_speed > 255) {
+ op = 2; // multiply
+ arg = pm->pulse_speed - 255;
+ } else {
+ op = 1; // normal speed
+ arg = 0; // can be any value
+ }
+ pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
+ pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op );
+ pm->requires_update &= ~UPDATE_PULSE_MODE;
+ } else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) {
+ pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
+ pm->configcr->wIndex = cpu_to_le16( pm->static_brightness );
+ pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
+ } else {
+ printk(KERN_ERR "powermate: unknown update required");
+ pm->requires_update = 0; /* fudge the bug */
+ return;
+ }
+
+/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */
+
+ pm->configcr->bRequestType = 0x41; /* vendor request */
+ pm->configcr->bRequest = 0x01;
+ pm->configcr->wLength = 0;
+
+ usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
+ (void *) pm->configcr, NULL, 0,
+ powermate_config_complete, pm);
+ pm->config->setup_dma = pm->configcr_dma;
+ pm->config->transfer_flags |= URB_NO_SETUP_DMA_MAP;
+
+ if (usb_submit_urb(pm->config, GFP_ATOMIC))
+ printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
+}
+
+/* Called when our asynchronous control message completes. We may need to issue another immediately */
+static void powermate_config_complete(struct urb *urb)
+{
+ struct powermate_device *pm = urb->context;
+ unsigned long flags;
+
+ if (urb->status)
+ printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
+
+ spin_lock_irqsave(&pm->lock, flags);
+ powermate_sync_state(pm);
+ spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* Set the LED up as described and begin the sync with the hardware if required */
+static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
+ int pulse_table, int pulse_asleep, int pulse_awake)
+{
+ unsigned long flags;
+
+ if (pulse_speed < 0)
+ pulse_speed = 0;
+ if (pulse_table < 0)
+ pulse_table = 0;
+ if (pulse_speed > 510)
+ pulse_speed = 510;
+ if (pulse_table > 2)
+ pulse_table = 2;
+
+ pulse_asleep = !!pulse_asleep;
+ pulse_awake = !!pulse_awake;
+
+
+ spin_lock_irqsave(&pm->lock, flags);
+
+ /* mark state updates which are required */
+ if (static_brightness != pm->static_brightness) {
+ pm->static_brightness = static_brightness;
+ pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;
+ }
+ if (pulse_asleep != pm->pulse_asleep) {
+ pm->pulse_asleep = pulse_asleep;
+ pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
+ }
+ if (pulse_awake != pm->pulse_awake) {
+ pm->pulse_awake = pulse_awake;
+ pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
+ }
+ if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) {
+ pm->pulse_speed = pulse_speed;
+ pm->pulse_table = pulse_table;
+ pm->requires_update |= UPDATE_PULSE_MODE;
+ }
+
+ powermate_sync_state(pm);
+
+ spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* Callback from the Input layer when an event arrives from userspace to configure the LED */
+static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
+{
+ unsigned int command = (unsigned int)_value;
+ struct powermate_device *pm = input_get_drvdata(dev);
+
+ if (type == EV_MSC && code == MSC_PULSELED){
+ /*
+ bits 0- 7: 8 bits: LED brightness
+ bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
+ bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
+ bit 19: 1 bit : pulse whilst asleep?
+ bit 20: 1 bit : pulse constantly?
+ */
+ int static_brightness = command & 0xFF; // bits 0-7
+ int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
+ int pulse_table = (command >> 17) & 0x3; // bits 17-18
+ int pulse_asleep = (command >> 19) & 0x1; // bit 19
+ int pulse_awake = (command >> 20) & 0x1; // bit 20
+
+ powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
+ }
+
+ return 0;
+}
+
+static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+ pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+ GFP_ATOMIC, &pm->data_dma);
+ if (!pm->data)
+ return -1;
+
+ pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)),
+ GFP_ATOMIC, &pm->configcr_dma);
+ if (!pm->configcr)
+ return -1;
+
+ return 0;
+}
+
+static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+ usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+ pm->data, pm->data_dma);
+ usb_buffer_free(udev, sizeof(*(pm->configcr)),
+ pm->configcr, pm->configcr_dma);
+}
+
+/* Called whenever a USB device matching one in our supported devices table is connected */
+static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct powermate_device *pm;
+ struct input_dev *input_dev;
+ int pipe, maxp;
+ int error = -ENOMEM;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -EIO;
+
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, interface->desc.bInterfaceNumber, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+
+ pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pm || !input_dev)
+ goto fail1;
+
+ if (powermate_alloc_buffers(udev, pm))
+ goto fail2;
+
+ pm->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pm->irq)
+ goto fail2;
+
+ pm->config = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pm->config)
+ goto fail3;
+
+ pm->udev = udev;
+ pm->input = input_dev;
+
+ usb_make_path(udev, pm->phys, sizeof(pm->phys));
+ strlcpy(pm->phys, "/input0", sizeof(pm->phys));
+
+ spin_lock_init(&pm->lock);
+
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
+ case POWERMATE_PRODUCT_NEW:
+ input_dev->name = pm_name_powermate;
+ break;
+ case POWERMATE_PRODUCT_OLD:
+ input_dev->name = pm_name_soundknob;
+ break;
+ default:
+ input_dev->name = pm_name_soundknob;
+ printk(KERN_WARNING "powermate: unknown product id %04x\n",
+ le16_to_cpu(udev->descriptor.idProduct));
+ }
+
+ input_dev->phys = pm->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, pm);
+
+ input_dev->event = powermate_input_event;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_MSC);
+ input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
+ input_dev->relbit[LONG(REL_DIAL)] = BIT(REL_DIAL);
+ input_dev->mscbit[LONG(MSC_PULSELED)] = BIT(MSC_PULSELED);
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+
+ if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
+ printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
+ POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
+ maxp = POWERMATE_PAYLOAD_SIZE_MAX;
+ }
+
+ usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
+ maxp, powermate_irq,
+ pm, endpoint->bInterval);
+ pm->irq->transfer_dma = pm->data_dma;
+ pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* register our interrupt URB with the USB system */
+ if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
+ error = -EIO;
+ goto fail4;
+ }
+
+ error = input_register_device(pm->input);
+ if (error)
+ goto fail5;
+
+
+ /* force an update of everything */
+ pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
+ powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
+
+ usb_set_intfdata(intf, pm);
+ return 0;
+
+ fail5: usb_kill_urb(pm->irq);
+ fail4: usb_free_urb(pm->config);
+ fail3: usb_free_urb(pm->irq);
+ fail2: powermate_free_buffers(udev, pm);
+ fail1: input_free_device(input_dev);
+ kfree(pm);
+ return error;
+}
+
+/* Called when a USB device we've accepted ownership of is removed */
+static void powermate_disconnect(struct usb_interface *intf)
+{
+ struct powermate_device *pm = usb_get_intfdata (intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (pm) {
+ pm->requires_update = 0;
+ usb_kill_urb(pm->irq);
+ input_unregister_device(pm->input);
+ usb_free_urb(pm->irq);
+ usb_free_urb(pm->config);
+ powermate_free_buffers(interface_to_usbdev(intf), pm);
+
+ kfree(pm);
+ }
+}
+
+static struct usb_device_id powermate_devices [] = {
+ { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
+ { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
+ { USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, powermate_devices);
+
+static struct usb_driver powermate_driver = {
+ .name = "powermate",
+ .probe = powermate_probe,
+ .disconnect = powermate_disconnect,
+ .id_table = powermate_devices,
+};
+
+static int __init powermate_init(void)
+{
+ return usb_register(&powermate_driver);
+}
+
+static void __exit powermate_cleanup(void)
+{
+ usb_deregister(&powermate_driver);
+}
+
+module_init(powermate_init);
+module_exit(powermate_cleanup);
+
+MODULE_AUTHOR( "William R Sowerbutts" );
+MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
index 106c94f33b9..e36ec1d92be 100644
--- a/drivers/input/misc/sparcspkr.c
+++ b/drivers/input/misc/sparcspkr.c
@@ -28,7 +28,7 @@ struct sparcspkr_state {
static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
- struct sparcspkr_state *state = dev_get_drvdata(dev->cdev.dev);
+ struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
unsigned int count = 0;
unsigned long flags;
@@ -61,7 +61,7 @@ static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned in
static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
- struct sparcspkr_state *state = dev_get_drvdata(dev->cdev.dev);
+ struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
unsigned int count = 0;
unsigned long flags;
@@ -113,7 +113,7 @@ static int __devinit sparcspkr_probe(struct device *dev)
input_dev->id.vendor = 0x001f;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = dev;
+ input_dev->dev.parent = dev;
input_dev->evbit[0] = BIT(EV_SND);
input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 42556232c52..a56ad4ba8fe 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -33,7 +33,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/input.h>
#include <linux/smp_lock.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
@@ -41,9 +40,7 @@
static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
- struct uinput_device *udev;
-
- udev = dev->private;
+ struct uinput_device *udev = input_get_drvdata(dev);
udev->buff[udev->head].type = type;
udev->buff[udev->head].code = code;
@@ -136,7 +133,7 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff
request.u.upload.effect = effect;
request.u.upload.old = old;
- retval = uinput_request_reserve_slot(dev->private, &request);
+ retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request);
if (!retval)
retval = uinput_request_submit(dev, &request);
@@ -156,7 +153,7 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
request.code = UI_FF_ERASE;
request.u.effect_id = effect_id;
- retval = uinput_request_reserve_slot(dev->private, &request);
+ retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request);
if (!retval)
retval = uinput_request_submit(dev, &request);
@@ -274,7 +271,7 @@ static int uinput_allocate_device(struct uinput_device *udev)
return -ENOMEM;
udev->dev->event = uinput_dev_event;
- udev->dev->private = udev;
+ input_set_drvdata(udev->dev, udev);
return 0;
}
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index e1183aeb8ed..961aad7a047 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -50,7 +50,7 @@
MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>");
MODULE_DESCRIPTION("Wistron laptop button driver");
MODULE_LICENSE("GPL v2");
-MODULE_VERSION("0.1");
+MODULE_VERSION("0.2");
static int force; /* = 0; */
module_param(force, bool, 0);
@@ -58,7 +58,7 @@ MODULE_PARM_DESC(force, "Load even if computer is not in database");
static char *keymap_name; /* = NULL; */
module_param_named(keymap, keymap_name, charp, 0);
-MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected");
+MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected [generic, 1557/MS2141]");
static struct platform_device *wistron_device;
@@ -233,10 +233,20 @@ static void bios_set_state(u8 subsys, int enable)
struct key_entry {
char type; /* See KE_* below */
u8 code;
- unsigned keycode; /* For KE_KEY */
+ union {
+ u16 keycode; /* For KE_KEY */
+ struct { /* For KE_SW */
+ u8 code;
+ u8 value;
+ } sw;
+ };
};
-enum { KE_END, KE_KEY, KE_WIFI, KE_BLUETOOTH };
+enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH };
+
+#define FE_MAIL_LED 0x01
+#define FE_WIFI_LED 0x02
+#define FE_UNTESTED 0x80
static const struct key_entry *keymap; /* = NULL; Current key map */
static int have_wifi;
@@ -256,93 +266,341 @@ static int __init dmi_matched(struct dmi_system_id *dmi)
return 1;
}
-static struct key_entry keymap_empty[] = {
+static struct key_entry keymap_empty[] __initdata = {
{ KE_END, 0 }
};
-static struct key_entry keymap_fs_amilo_pro_v2000[] = {
- { KE_KEY, 0x01, KEY_HELP },
- { KE_KEY, 0x11, KEY_PROG1 },
- { KE_KEY, 0x12, KEY_PROG2 },
- { KE_WIFI, 0x30, 0 },
- { KE_KEY, 0x31, KEY_MAIL },
- { KE_KEY, 0x36, KEY_WWW },
+static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
{ KE_END, 0 }
};
-static struct key_entry keymap_fujitsu_n3510[] = {
- { KE_KEY, 0x11, KEY_PROG1 },
- { KE_KEY, 0x12, KEY_PROG2 },
- { KE_KEY, 0x36, KEY_WWW },
- { KE_KEY, 0x31, KEY_MAIL },
- { KE_KEY, 0x71, KEY_STOPCD },
- { KE_KEY, 0x72, KEY_PLAYPAUSE },
- { KE_KEY, 0x74, KEY_REWIND },
- { KE_KEY, 0x78, KEY_FORWARD },
+static struct key_entry keymap_fujitsu_n3510[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x71, {KEY_STOPCD} },
+ { KE_KEY, 0x72, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x74, {KEY_REWIND} },
+ { KE_KEY, 0x78, {KEY_FORWARD} },
{ KE_END, 0 }
};
-static struct key_entry keymap_wistron_ms2111[] = {
- { KE_KEY, 0x11, KEY_PROG1 },
- { KE_KEY, 0x12, KEY_PROG2 },
- { KE_KEY, 0x13, KEY_PROG3 },
- { KE_KEY, 0x31, KEY_MAIL },
- { KE_KEY, 0x36, KEY_WWW },
- { KE_END, 0 }
+static struct key_entry keymap_wistron_ms2111[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, FE_MAIL_LED }
+};
+
+static struct key_entry keymap_wistron_md40100[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
};
-static struct key_entry keymap_wistron_ms2141[] = {
- { KE_KEY, 0x11, KEY_PROG1 },
- { KE_KEY, 0x12, KEY_PROG2 },
- { KE_WIFI, 0x30, 0 },
- { KE_KEY, 0x22, KEY_REWIND },
- { KE_KEY, 0x23, KEY_FORWARD },
- { KE_KEY, 0x24, KEY_PLAYPAUSE },
- { KE_KEY, 0x25, KEY_STOPCD },
- { KE_KEY, 0x31, KEY_MAIL },
- { KE_KEY, 0x36, KEY_WWW },
+static struct key_entry keymap_wistron_ms2141[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
{ KE_END, 0 }
};
-static struct key_entry keymap_acer_aspire_1500[] = {
- { KE_KEY, 0x11, KEY_PROG1 },
- { KE_KEY, 0x12, KEY_PROG2 },
- { KE_WIFI, 0x30, 0 },
- { KE_KEY, 0x31, KEY_MAIL },
- { KE_KEY, 0x36, KEY_WWW },
- { KE_BLUETOOTH, 0x44, 0 },
- { KE_END, 0 }
+static struct key_entry keymap_acer_aspire_1500[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_UNTESTED }
};
-static struct key_entry keymap_acer_travelmate_240[] = {
- { KE_KEY, 0x31, KEY_MAIL },
- { KE_KEY, 0x36, KEY_WWW },
- { KE_KEY, 0x11, KEY_PROG1 },
- { KE_KEY, 0x12, KEY_PROG2 },
- { KE_BLUETOOTH, 0x44, 0 },
- { KE_WIFI, 0x30, 0 },
- { KE_END, 0 }
+static struct key_entry keymap_acer_aspire_1600[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+/* 3020 has been tested */
+static struct key_entry keymap_acer_aspire_5020[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_2410[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x6d, {KEY_POWER} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_110[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */
+ { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_300[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
};
-static struct key_entry keymap_aopen_1559as[] = {
- { KE_KEY, 0x01, KEY_HELP },
- { KE_KEY, 0x06, KEY_PROG3 },
- { KE_KEY, 0x11, KEY_PROG1 },
- { KE_KEY, 0x12, KEY_PROG2 },
- { KE_WIFI, 0x30, 0 },
- { KE_KEY, 0x31, KEY_MAIL },
- { KE_KEY, 0x36, KEY_WWW },
+static struct key_entry keymap_acer_travelmate_380[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} }, /* not 370 */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+/* unusual map */
+static struct key_entry keymap_acer_travelmate_220[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_MAIL} },
+ { KE_KEY, 0x12, {KEY_WWW} },
+ { KE_KEY, 0x13, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_PROG1} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_230[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_240[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_350[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_MAIL} },
+ { KE_KEY, 0x14, {KEY_PROG3} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_360[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_MAIL} },
+ { KE_KEY, 0x14, {KEY_PROG3} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED } /* no mail led */
+};
+
+/* Wifi subsystem only activates the led. Therefore we need to pass
+ * wifi event as a normal key, then userspace can really change the wifi state.
+ * TODO we need to export led state to userspace (wifi and mail) */
+static struct key_entry keymap_acer_travelmate_610[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x14, {KEY_MAIL} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED }
+};
+
+static struct key_entry keymap_acer_travelmate_630[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} }, /* not 620 */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_aopen_1559as[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x06, {KEY_PROG3} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
{ KE_END, 0 },
};
-static struct key_entry keymap_fs_amilo_d88x0[] = {
- { KE_KEY, 0x01, KEY_HELP },
- { KE_KEY, 0x08, KEY_MUTE },
- { KE_KEY, 0x31, KEY_MAIL },
- { KE_KEY, 0x36, KEY_WWW },
- { KE_KEY, 0x11, KEY_PROG1 },
- { KE_KEY, 0x12, KEY_PROG2 },
- { KE_KEY, 0x13, KEY_PROG3 },
+static struct key_entry keymap_fs_amilo_d88x0[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_md2900[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_md96500[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_generic[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x14, {KEY_MAIL} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */
+ { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_KEY, 0x6d, {KEY_POWER} },
+ { KE_KEY, 0x71, {KEY_STOPCD} },
+ { KE_KEY, 0x72, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x74, {KEY_REWIND} },
+ { KE_KEY, 0x78, {KEY_FORWARD} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
{ KE_END, 0 }
};
@@ -390,6 +648,133 @@ static struct dmi_system_id dmi_ids[] __initdata = {
},
{
.callback = dmi_matched,
+ .ident = "Acer Aspire 1600",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"),
+ },
+ .driver_data = keymap_acer_aspire_1600
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer Aspire 3020",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer Aspire 5020",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 2100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 2410",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"),
+ },
+ .driver_data = keymap_acer_travelmate_2410
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate C300",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"),
+ },
+ .driver_data = keymap_acer_travelmate_300
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate C100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"),
+ },
+ .driver_data = keymap_acer_travelmate_300
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate C110",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"),
+ },
+ .driver_data = keymap_acer_travelmate_110
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 380",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"),
+ },
+ .driver_data = keymap_acer_travelmate_380
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 370",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"),
+ },
+ .driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 220",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"),
+ },
+ .driver_data = keymap_acer_travelmate_220
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 260",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"),
+ },
+ .driver_data = keymap_acer_travelmate_220
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 230",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"),
+ /* acerhk looks for "TravelMate F4..." ?! */
+ },
+ .driver_data = keymap_acer_travelmate_230
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 280",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"),
+ },
+ .driver_data = keymap_acer_travelmate_230
+ },
+ {
+ .callback = dmi_matched,
.ident = "Acer TravelMate 240",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
@@ -399,6 +784,15 @@ static struct dmi_system_id dmi_ids[] __initdata = {
},
{
.callback = dmi_matched,
+ .ident = "Acer TravelMate 250",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"),
+ },
+ .driver_data = keymap_acer_travelmate_240
+ },
+ {
+ .callback = dmi_matched,
.ident = "Acer TravelMate 2424NWXCi",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
@@ -408,6 +802,51 @@ static struct dmi_system_id dmi_ids[] __initdata = {
},
{
.callback = dmi_matched,
+ .ident = "Acer TravelMate 350",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"),
+ },
+ .driver_data = keymap_acer_travelmate_350
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 360",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
+ },
+ .driver_data = keymap_acer_travelmate_360
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 610",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ACER"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"),
+ },
+ .driver_data = keymap_acer_travelmate_610
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 620",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"),
+ },
+ .driver_data = keymap_acer_travelmate_630
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 630",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"),
+ },
+ .driver_data = keymap_acer_travelmate_630
+ },
+ {
+ .callback = dmi_matched,
.ident = "AOpen 1559AS",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "E2U"),
@@ -426,6 +865,51 @@ static struct dmi_system_id dmi_ids[] __initdata = {
},
{
.callback = dmi_matched,
+ .ident = "Medion MD 40100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"),
+ },
+ .driver_data = keymap_wistron_md40100
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 2900",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"),
+ },
+ .driver_data = keymap_wistron_md2900
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 96500",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"),
+ },
+ .driver_data = keymap_wistron_md96500
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 95400",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"),
+ },
+ .driver_data = keymap_wistron_md96500
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Fujitsu Siemens Amilo D7820",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */
+ DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"),
+ },
+ .driver_data = keymap_fs_amilo_d88x0
+ },
+ {
+ .callback = dmi_matched,
.ident = "Fujitsu Siemens Amilo D88x0",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
@@ -436,17 +920,39 @@ static struct dmi_system_id dmi_ids[] __initdata = {
{ NULL, }
};
+/* Copy the good keymap, as the original ones are free'd */
+static int __init copy_keymap(void)
+{
+ const struct key_entry *key;
+ struct key_entry *new_keymap;
+ unsigned int length = 1;
+
+ for (key = keymap; key->type != KE_END; key++)
+ length++;
+
+ new_keymap = kmalloc(length * sizeof(struct key_entry), GFP_KERNEL);
+ if (!new_keymap)
+ return -ENOMEM;
+
+ memcpy(new_keymap, keymap, length * sizeof(struct key_entry));
+ keymap = new_keymap;
+
+ return 0;
+}
+
static int __init select_keymap(void)
{
+ dmi_check_system(dmi_ids);
if (keymap_name != NULL) {
if (strcmp (keymap_name, "1557/MS2141") == 0)
keymap = keymap_wistron_ms2141;
+ else if (strcmp (keymap_name, "generic") == 0)
+ keymap = keymap_wistron_generic;
else {
printk(KERN_ERR "wistron_btns: Keymap unknown\n");
return -EINVAL;
}
}
- dmi_check_system(dmi_ids);
if (keymap == NULL) {
if (!force) {
printk(KERN_ERR "wistron_btns: System unknown\n");
@@ -454,7 +960,8 @@ static int __init select_keymap(void)
}
keymap = keymap_empty;
}
- return 0;
+
+ return copy_keymap();
}
/* Input layer interface */
@@ -476,12 +983,28 @@ static int __devinit setup_input_dev(void)
input_dev->cdev.dev = &wistron_device->dev;
for (key = keymap; key->type != KE_END; key++) {
- if (key->type == KE_KEY) {
- input_dev->evbit[LONG(EV_KEY)] = BIT(EV_KEY);
- set_bit(key->keycode, input_dev->keybit);
+ switch (key->type) {
+ case KE_KEY:
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(key->keycode, input_dev->keybit);
+ break;
+
+ case KE_SW:
+ set_bit(EV_SW, input_dev->evbit);
+ set_bit(key->sw.code, input_dev->swbit);
+ break;
+
+ default:
+ ;
}
}
+ /* reads information flags on KE_END */
+ if (key->code & FE_UNTESTED)
+ printk(KERN_WARNING "Untested laptop multimedia keys, "
+ "please report success or failure to eric.piel"
+ "@tremplin-utc.net\n");
+
error = input_register_device(input_dev);
if (error) {
input_free_device(input_dev);
@@ -499,6 +1022,12 @@ static void report_key(unsigned keycode)
input_sync(input_dev);
}
+static void report_switch(unsigned code, int value)
+{
+ input_report_switch(input_dev, code, value);
+ input_sync(input_dev);
+}
+
/* Driver core */
static int wifi_enabled;
@@ -519,6 +1048,10 @@ static void handle_key(u8 code)
report_key(key->keycode);
break;
+ case KE_SW:
+ report_switch(key->sw.code, key->sw.value);
+ break;
+
case KE_WIFI:
if (have_wifi) {
wifi_enabled = !wifi_enabled;
@@ -534,6 +1067,7 @@ static void handle_key(u8 code)
break;
case KE_END:
+ break;
default:
BUG();
}
@@ -690,6 +1224,7 @@ static void __exit wb_module_exit(void)
platform_device_unregister(wistron_device);
platform_driver_unregister(&wistron_driver);
unmap_bios();
+ kfree(keymap);
}
module_init(wb_module_init);
diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c
new file mode 100644
index 00000000000..ab15880fd56
--- /dev/null
+++ b/drivers/input/misc/yealink.c
@@ -0,0 +1,1004 @@
+/*
+ * drivers/usb/input/yealink.c
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ * 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
+ */
+/*
+ * Description:
+ * Driver for the USB-P1K voip usb phone.
+ * This device is produced by Yealink Network Technology Co Ltd
+ * but may be branded under several names:
+ * - Yealink usb-p1k
+ * - Tiptel 115
+ * - ...
+ *
+ * This driver is based on:
+ * - the usbb2k-api http://savannah.nongnu.org/projects/usbb2k-api/
+ * - information from http://memeteau.free.fr/usbb2k
+ * - the xpad-driver drivers/input/joystick/xpad.c
+ *
+ * Thanks to:
+ * - Olivier Vandorpe, for providing the usbb2k-api.
+ * - Martin Diehl, for spotting my memory allocation bug.
+ *
+ * History:
+ * 20050527 henk First version, functional keyboard. Keyboard events
+ * will pop-up on the ../input/eventX bus.
+ * 20050531 henk Added led, LCD, dialtone and sysfs interface.
+ * 20050610 henk Cleanups, make it ready for public consumption.
+ * 20050630 henk Cleanups, fixes in response to comments.
+ * 20050701 henk sysfs write serialisation, fix potential unload races
+ * 20050801 henk Added ringtone, restructure USB
+ * 20050816 henk Merge 2.6.13-rc6
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#include "map_to_7segment.h"
+#include "yealink.h"
+
+#define DRIVER_VERSION "yld-20051230"
+#define DRIVER_AUTHOR "Henk Vergonet"
+#define DRIVER_DESC "Yealink phone driver"
+
+#define YEALINK_POLLING_FREQUENCY 10 /* in [Hz] */
+
+struct yld_status {
+ u8 lcd[24];
+ u8 led;
+ u8 dialtone;
+ u8 ringtone;
+ u8 keynum;
+} __attribute__ ((packed));
+
+/*
+ * Register the LCD segment and icon map
+ */
+#define _LOC(k,l) { .a = (k), .m = (l) }
+#define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm) \
+ { .type = (t), \
+ .u = { .s = { _LOC(a, am), _LOC(b, bm), _LOC(c, cm), \
+ _LOC(d, dm), _LOC(e, em), _LOC(g, gm), \
+ _LOC(f, fm) } } }
+#define _PIC(t, h, hm, n) \
+ { .type = (t), \
+ .u = { .p = { .name = (n), .a = (h), .m = (hm) } } }
+
+static const struct lcd_segment_map {
+ char type;
+ union {
+ struct pictogram_map {
+ u8 a,m;
+ char name[10];
+ } p;
+ struct segment_map {
+ u8 a,m;
+ } s[7];
+ } u;
+} lcdMap[] = {
+#include "yealink.h"
+};
+
+struct yealink_dev {
+ struct input_dev *idev; /* input device */
+ struct usb_device *udev; /* usb device */
+
+ /* irq input channel */
+ struct yld_ctl_packet *irq_data;
+ dma_addr_t irq_dma;
+ struct urb *urb_irq;
+
+ /* control output channel */
+ struct yld_ctl_packet *ctl_data;
+ dma_addr_t ctl_dma;
+ struct usb_ctrlrequest *ctl_req;
+ dma_addr_t ctl_req_dma;
+ struct urb *urb_ctl;
+
+ char phys[64]; /* physical device path */
+
+ u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */
+ int key_code; /* last reported key */
+
+ int stat_ix;
+ union {
+ struct yld_status s;
+ u8 b[sizeof(struct yld_status)];
+ } master, copy;
+};
+
+
+/*******************************************************************************
+ * Yealink lcd interface
+ ******************************************************************************/
+
+/*
+ * Register a default 7 segment character set
+ */
+static SEG7_DEFAULT_MAP(map_seg7);
+
+ /* Display a char,
+ * char '\9' and '\n' are placeholders and do not overwrite the original text.
+ * A space will always hide an icon.
+ */
+static int setChar(struct yealink_dev *yld, int el, int chr)
+{
+ int i, a, m, val;
+
+ if (el >= ARRAY_SIZE(lcdMap))
+ return -EINVAL;
+
+ if (chr == '\t' || chr == '\n')
+ return 0;
+
+ yld->lcdMap[el] = chr;
+
+ if (lcdMap[el].type == '.') {
+ a = lcdMap[el].u.p.a;
+ m = lcdMap[el].u.p.m;
+ if (chr != ' ')
+ yld->master.b[a] |= m;
+ else
+ yld->master.b[a] &= ~m;
+ return 0;
+ }
+
+ val = map_to_seg7(&map_seg7, chr);
+ for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) {
+ m = lcdMap[el].u.s[i].m;
+
+ if (m == 0)
+ continue;
+
+ a = lcdMap[el].u.s[i].a;
+ if (val & 1)
+ yld->master.b[a] |= m;
+ else
+ yld->master.b[a] &= ~m;
+ val = val >> 1;
+ }
+ return 0;
+};
+
+/*******************************************************************************
+ * Yealink key interface
+ ******************************************************************************/
+
+/* Map device buttons to internal key events.
+ *
+ * USB-P1K button layout:
+ *
+ * up
+ * IN OUT
+ * down
+ *
+ * pickup C hangup
+ * 1 2 3
+ * 4 5 6
+ * 7 8 9
+ * * 0 #
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+ */
+static int map_p1k_to_key(int scancode)
+{
+ switch(scancode) { /* phone key: */
+ case 0x23: return KEY_LEFT; /* IN */
+ case 0x33: return KEY_UP; /* up */
+ case 0x04: return KEY_RIGHT; /* OUT */
+ case 0x24: return KEY_DOWN; /* down */
+ case 0x03: return KEY_ENTER; /* pickup */
+ case 0x14: return KEY_BACKSPACE; /* C */
+ case 0x13: return KEY_ESC; /* hangup */
+ case 0x00: return KEY_1; /* 1 */
+ case 0x01: return KEY_2; /* 2 */
+ case 0x02: return KEY_3; /* 3 */
+ case 0x10: return KEY_4; /* 4 */
+ case 0x11: return KEY_5; /* 5 */
+ case 0x12: return KEY_6; /* 6 */
+ case 0x20: return KEY_7; /* 7 */
+ case 0x21: return KEY_8; /* 8 */
+ case 0x22: return KEY_9; /* 9 */
+ case 0x30: return KEY_KPASTERISK; /* * */
+ case 0x31: return KEY_0; /* 0 */
+ case 0x32: return KEY_LEFTSHIFT |
+ KEY_3 << 8; /* # */
+ }
+ return -EINVAL;
+}
+
+/* Completes a request by converting the data into events for the
+ * input subsystem.
+ *
+ * The key parameter can be cascaded: key2 << 8 | key1
+ */
+static void report_key(struct yealink_dev *yld, int key)
+{
+ struct input_dev *idev = yld->idev;
+
+ if (yld->key_code >= 0) {
+ /* old key up */
+ input_report_key(idev, yld->key_code & 0xff, 0);
+ if (yld->key_code >> 8)
+ input_report_key(idev, yld->key_code >> 8, 0);
+ }
+
+ yld->key_code = key;
+ if (key >= 0) {
+ /* new valid key */
+ input_report_key(idev, key & 0xff, 1);
+ if (key >> 8)
+ input_report_key(idev, key >> 8, 1);
+ }
+ input_sync(idev);
+}
+
+/*******************************************************************************
+ * Yealink usb communication interface
+ ******************************************************************************/
+
+static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p)
+{
+ u8 *buf = (u8 *)p;
+ int i;
+ u8 sum = 0;
+
+ for(i=0; i<USB_PKT_LEN-1; i++)
+ sum -= buf[i];
+ p->sum = sum;
+ return usb_control_msg(yld->udev,
+ usb_sndctrlpipe(yld->udev, 0),
+ USB_REQ_SET_CONFIGURATION,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0x200, 3,
+ p, sizeof(*p),
+ USB_CTRL_SET_TIMEOUT);
+}
+
+static u8 default_ringtone[] = {
+ 0xEF, /* volume [0-255] */
+ 0xFB, 0x1E, 0x00, 0x0C, /* 1250 [hz], 12/100 [s] */
+ 0xFC, 0x18, 0x00, 0x0C, /* 1000 [hz], 12/100 [s] */
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFF, 0xFF, 0x01, 0x90, /* silent, 400/100 [s] */
+ 0x00, 0x00 /* end of sequence */
+};
+
+static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size)
+{
+ struct yld_ctl_packet *p = yld->ctl_data;
+ int ix, len;
+
+ if (size <= 0)
+ return -EINVAL;
+
+ /* Set the ringtone volume */
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_RING_VOLUME;
+ yld->ctl_data->size = 1;
+ yld->ctl_data->data[0] = buf[0];
+ yealink_cmd(yld, p);
+
+ buf++;
+ size--;
+
+ p->cmd = CMD_RING_NOTE;
+ ix = 0;
+ while (size != ix) {
+ len = size - ix;
+ if (len > sizeof(p->data))
+ len = sizeof(p->data);
+ p->size = len;
+ p->offset = cpu_to_be16(ix);
+ memcpy(p->data, &buf[ix], len);
+ yealink_cmd(yld, p);
+ ix += len;
+ }
+ return 0;
+}
+
+/* keep stat_master & stat_copy in sync.
+ */
+static int yealink_do_idle_tasks(struct yealink_dev *yld)
+{
+ u8 val;
+ int i, ix, len;
+
+ ix = yld->stat_ix;
+
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_KEYPRESS;
+ yld->ctl_data->size = 1;
+ yld->ctl_data->sum = 0xff - CMD_KEYPRESS;
+
+ /* If state update pointer wraps do a KEYPRESS first. */
+ if (ix >= sizeof(yld->master)) {
+ yld->stat_ix = 0;
+ return 0;
+ }
+
+ /* find update candidates: copy != master */
+ do {
+ val = yld->master.b[ix];
+ if (val != yld->copy.b[ix])
+ goto send_update;
+ } while (++ix < sizeof(yld->master));
+
+ /* nothing todo, wait a bit and poll for a KEYPRESS */
+ yld->stat_ix = 0;
+ /* TODO how can we wait abit. ??
+ * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY);
+ */
+ return 0;
+
+send_update:
+
+ /* Setup an appropriate update request */
+ yld->copy.b[ix] = val;
+ yld->ctl_data->data[0] = val;
+
+ switch(ix) {
+ case offsetof(struct yld_status, led):
+ yld->ctl_data->cmd = CMD_LED;
+ yld->ctl_data->sum = -1 - CMD_LED - val;
+ break;
+ case offsetof(struct yld_status, dialtone):
+ yld->ctl_data->cmd = CMD_DIALTONE;
+ yld->ctl_data->sum = -1 - CMD_DIALTONE - val;
+ break;
+ case offsetof(struct yld_status, ringtone):
+ yld->ctl_data->cmd = CMD_RINGTONE;
+ yld->ctl_data->sum = -1 - CMD_RINGTONE - val;
+ break;
+ case offsetof(struct yld_status, keynum):
+ val--;
+ val &= 0x1f;
+ yld->ctl_data->cmd = CMD_SCANCODE;
+ yld->ctl_data->offset = cpu_to_be16(val);
+ yld->ctl_data->data[0] = 0;
+ yld->ctl_data->sum = -1 - CMD_SCANCODE - val;
+ break;
+ default:
+ len = sizeof(yld->master.s.lcd) - ix;
+ if (len > sizeof(yld->ctl_data->data))
+ len = sizeof(yld->ctl_data->data);
+
+ /* Combine up to <len> consecutive LCD bytes in a singe request
+ */
+ yld->ctl_data->cmd = CMD_LCD;
+ yld->ctl_data->offset = cpu_to_be16(ix);
+ yld->ctl_data->size = len;
+ yld->ctl_data->sum = -CMD_LCD - ix - val - len;
+ for(i=1; i<len; i++) {
+ ix++;
+ val = yld->master.b[ix];
+ yld->copy.b[ix] = val;
+ yld->ctl_data->data[i] = val;
+ yld->ctl_data->sum -= val;
+ }
+ }
+ yld->stat_ix = ix + 1;
+ return 1;
+}
+
+/* Decide on how to handle responses
+ *
+ * The state transition diagram is somethhing like:
+ *
+ * syncState<--+
+ * | |
+ * | idle
+ * \|/ |
+ * init --ok--> waitForKey --ok--> getKey
+ * ^ ^ |
+ * | +-------ok-------+
+ * error,start
+ *
+ */
+static void urb_irq_callback(struct urb *urb)
+{
+ struct yealink_dev *yld = urb->context;
+ int ret;
+
+ if (urb->status)
+ err("%s - urb status %d", __FUNCTION__, urb->status);
+
+ switch (yld->irq_data->cmd) {
+ case CMD_KEYPRESS:
+
+ yld->master.s.keynum = yld->irq_data->data[0];
+ break;
+
+ case CMD_SCANCODE:
+ dbg("get scancode %x", yld->irq_data->data[0]);
+
+ report_key(yld, map_p1k_to_key(yld->irq_data->data[0]));
+ break;
+
+ default:
+ err("unexpected response %x", yld->irq_data->cmd);
+ }
+
+ yealink_do_idle_tasks(yld);
+
+ ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
+ if (ret)
+ err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);
+}
+
+static void urb_ctl_callback(struct urb *urb)
+{
+ struct yealink_dev *yld = urb->context;
+ int ret;
+
+ if (urb->status)
+ err("%s - urb status %d", __FUNCTION__, urb->status);
+
+ switch (yld->ctl_data->cmd) {
+ case CMD_KEYPRESS:
+ case CMD_SCANCODE:
+ /* ask for a response */
+ ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC);
+ break;
+ default:
+ /* send new command */
+ yealink_do_idle_tasks(yld);
+ ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
+ }
+
+ if (ret)
+ err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);
+}
+
+/*******************************************************************************
+ * input event interface
+ ******************************************************************************/
+
+/* TODO should we issue a ringtone on a SND_BELL event?
+static int input_ev(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+
+ if (type != EV_SND)
+ return -EINVAL;
+
+ switch (code) {
+ case SND_BELL:
+ case SND_TONE:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+*/
+
+static int input_open(struct input_dev *dev)
+{
+ struct yealink_dev *yld = input_get_drvdata(dev);
+ int i, ret;
+
+ dbg("%s", __FUNCTION__);
+
+ /* force updates to device */
+ for (i = 0; i<sizeof(yld->master); i++)
+ yld->copy.b[i] = ~yld->master.b[i];
+ yld->key_code = -1; /* no keys pressed */
+
+ yealink_set_ringtone(yld, default_ringtone, sizeof(default_ringtone));
+
+ /* issue INIT */
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_INIT;
+ yld->ctl_data->size = 10;
+ yld->ctl_data->sum = 0x100-CMD_INIT-10;
+ if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) {
+ dbg("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void input_close(struct input_dev *dev)
+{
+ struct yealink_dev *yld = input_get_drvdata(dev);
+
+ usb_kill_urb(yld->urb_ctl);
+ usb_kill_urb(yld->urb_irq);
+}
+
+/*******************************************************************************
+ * sysfs interface
+ ******************************************************************************/
+
+static DECLARE_RWSEM(sysfs_rwsema);
+
+/* Interface to the 7-segments translation table aka. char set.
+ */
+static ssize_t show_map(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ memcpy(buf, &map_seg7, sizeof(map_seg7));
+ return sizeof(map_seg7);
+}
+
+static ssize_t store_map(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t cnt)
+{
+ if (cnt != sizeof(map_seg7))
+ return -EINVAL;
+ memcpy(&map_seg7, buf, sizeof(map_seg7));
+ return sizeof(map_seg7);
+}
+
+/* Interface to the LCD.
+ */
+
+/* Reading /sys/../lineX will return the format string with its settings:
+ *
+ * Example:
+ * cat ./line3
+ * 888888888888
+ * Linux Rocks!
+ */
+static ssize_t show_line(struct device *dev, char *buf, int a, int b)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_read(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_read(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = a; i < b; i++)
+ *buf++ = lcdMap[i].type;
+ *buf++ = '\n';
+ for (i = a; i < b; i++)
+ *buf++ = yld->lcdMap[i];
+ *buf++ = '\n';
+ *buf = 0;
+
+ up_read(&sysfs_rwsema);
+ return 3 + ((b - a) << 1);
+}
+
+static ssize_t show_line1(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET);
+}
+
+static ssize_t show_line2(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET);
+}
+
+static ssize_t show_line3(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET);
+}
+
+/* Writing to /sys/../lineX will set the coresponding LCD line.
+ * - Excess characters are ignored.
+ * - If less characters are written than allowed, the remaining digits are
+ * unchanged.
+ * - The '\n' or '\t' char is a placeholder, it does not overwrite the
+ * original content.
+ */
+static ssize_t store_line(struct device *dev, const char *buf, size_t count,
+ int el, size_t len)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ if (len > count)
+ len = count;
+ for (i = 0; i < len; i++)
+ setChar(yld, el++, buf[i]);
+
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+static ssize_t store_line1(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE);
+}
+
+static ssize_t store_line2(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE);
+}
+
+static ssize_t store_line3(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE);
+}
+
+/* Interface to visible and audible "icons", these include:
+ * pictures on the LCD, the LED, and the dialtone signal.
+ */
+
+/* Get a list of "switchable elements" with their current state. */
+static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct yealink_dev *yld;
+ int i, ret = 1;
+
+ down_read(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_read(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+ if (lcdMap[i].type != '.')
+ continue;
+ ret += sprintf(&buf[ret], "%s %s\n",
+ yld->lcdMap[i] == ' ' ? " " : "on",
+ lcdMap[i].u.p.name);
+ }
+ up_read(&sysfs_rwsema);
+ return ret;
+}
+
+/* Change the visibility of a particular element. */
+static ssize_t set_icon(struct device *dev, const char *buf, size_t count,
+ int chr)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+ if (lcdMap[i].type != '.')
+ continue;
+ if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) {
+ setChar(yld, i, chr);
+ break;
+ }
+ }
+
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+static ssize_t show_icon(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return set_icon(dev, buf, count, buf[0]);
+}
+
+static ssize_t hide_icon(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return set_icon(dev, buf, count, ' ');
+}
+
+/* Upload a ringtone to the device.
+ */
+
+/* Stores raw ringtone data in the phone */
+static ssize_t store_ringtone(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct yealink_dev *yld;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ /* TODO locking with async usb control interface??? */
+ yealink_set_ringtone(yld, (char *)buf, count);
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+#define _M444 S_IRUGO
+#define _M664 S_IRUGO|S_IWUSR|S_IWGRP
+#define _M220 S_IWUSR|S_IWGRP
+
+static DEVICE_ATTR(map_seg7 , _M664, show_map , store_map );
+static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 );
+static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 );
+static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 );
+static DEVICE_ATTR(get_icons , _M444, get_icons , NULL );
+static DEVICE_ATTR(show_icon , _M220, NULL , show_icon );
+static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon );
+static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone);
+
+static struct attribute *yld_attributes[] = {
+ &dev_attr_line1.attr,
+ &dev_attr_line2.attr,
+ &dev_attr_line3.attr,
+ &dev_attr_get_icons.attr,
+ &dev_attr_show_icon.attr,
+ &dev_attr_hide_icon.attr,
+ &dev_attr_map_seg7.attr,
+ &dev_attr_ringtone.attr,
+ NULL
+};
+
+static struct attribute_group yld_attr_group = {
+ .attrs = yld_attributes
+};
+
+/*******************************************************************************
+ * Linux interface and usb initialisation
+ ******************************************************************************/
+
+struct driver_info {
+ char *name;
+};
+
+static const struct driver_info info_P1K = {
+ .name = "Yealink usb-p1k",
+};
+
+static const struct usb_device_id usb_table [] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x6993,
+ .idProduct = 0xb001,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t)&info_P1K
+ },
+ { }
+};
+
+static int usb_cleanup(struct yealink_dev *yld, int err)
+{
+ if (yld == NULL)
+ return err;
+
+ usb_kill_urb(yld->urb_irq); /* parameter validation in core/urb */
+ usb_kill_urb(yld->urb_ctl); /* parameter validation in core/urb */
+
+ if (yld->idev) {
+ if (err)
+ input_free_device(yld->idev);
+ else
+ input_unregister_device(yld->idev);
+ }
+
+ usb_free_urb(yld->urb_irq);
+ usb_free_urb(yld->urb_ctl);
+
+ usb_buffer_free(yld->udev, sizeof(*(yld->ctl_req)),
+ yld->ctl_req, yld->ctl_req_dma);
+ usb_buffer_free(yld->udev, USB_PKT_LEN,
+ yld->ctl_data, yld->ctl_dma);
+ usb_buffer_free(yld->udev, USB_PKT_LEN,
+ yld->irq_data, yld->irq_dma);
+
+ kfree(yld);
+ return err;
+}
+
+static void usb_disconnect(struct usb_interface *intf)
+{
+ struct yealink_dev *yld;
+
+ down_write(&sysfs_rwsema);
+ yld = usb_get_intfdata(intf);
+ sysfs_remove_group(&intf->dev.kobj, &yld_attr_group);
+ usb_set_intfdata(intf, NULL);
+ up_write(&sysfs_rwsema);
+
+ usb_cleanup(yld, 0);
+}
+
+static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct driver_info *nfo = (struct driver_info *)id->driver_info;
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct yealink_dev *yld;
+ struct input_dev *input_dev;
+ int ret, pipe, i;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -ENODEV;
+
+ yld = kzalloc(sizeof(struct yealink_dev), GFP_KERNEL);
+ if (!yld)
+ return -ENOMEM;
+
+ yld->udev = udev;
+
+ yld->idev = input_dev = input_allocate_device();
+ if (!input_dev)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* allocate usb buffers */
+ yld->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+ GFP_ATOMIC, &yld->irq_dma);
+ if (yld->irq_data == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+ GFP_ATOMIC, &yld->ctl_dma);
+ if (!yld->ctl_data)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->ctl_req = usb_buffer_alloc(udev, sizeof(*(yld->ctl_req)),
+ GFP_ATOMIC, &yld->ctl_req_dma);
+ if (yld->ctl_req == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* allocate urb structures */
+ yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (yld->urb_irq == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+ if (yld->urb_ctl == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ if (ret != USB_PKT_LEN)
+ err("invalid payload size %d, expected %zd", ret, USB_PKT_LEN);
+
+ /* initialise irq urb */
+ usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data,
+ USB_PKT_LEN,
+ urb_irq_callback,
+ yld, endpoint->bInterval);
+ yld->urb_irq->transfer_dma = yld->irq_dma;
+ yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ yld->urb_irq->dev = udev;
+
+ /* initialise ctl urb */
+ yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+ USB_DIR_OUT;
+ yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+ yld->ctl_req->wValue = cpu_to_le16(0x200);
+ yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+ yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+ usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+ (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN,
+ urb_ctl_callback, yld);
+ yld->urb_ctl->setup_dma = yld->ctl_req_dma;
+ yld->urb_ctl->transfer_dma = yld->ctl_dma;
+ yld->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
+ URB_NO_TRANSFER_DMA_MAP;
+ yld->urb_ctl->dev = udev;
+
+ /* find out the physical bus location */
+ usb_make_path(udev, yld->phys, sizeof(yld->phys));
+ strlcat(yld->phys, "/input0", sizeof(yld->phys));
+
+ /* register settings for the input device */
+ input_dev->name = nfo->name;
+ input_dev->phys = yld->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, yld);
+
+ input_dev->open = input_open;
+ input_dev->close = input_close;
+ /* input_dev->event = input_ev; TODO */
+
+ /* register available key events */
+ input_dev->evbit[0] = BIT(EV_KEY);
+ for (i = 0; i < 256; i++) {
+ int k = map_p1k_to_key(i);
+ if (k >= 0) {
+ set_bit(k & 0xff, input_dev->keybit);
+ if (k >> 8)
+ set_bit(k >> 8, input_dev->keybit);
+ }
+ }
+
+ ret = input_register_device(yld->idev);
+ if (ret)
+ return usb_cleanup(yld, ret);
+
+ usb_set_intfdata(intf, yld);
+
+ /* clear visible elements */
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++)
+ setChar(yld, i, ' ');
+
+ /* display driver version on LCD line 3 */
+ store_line3(&intf->dev, NULL,
+ DRIVER_VERSION, sizeof(DRIVER_VERSION));
+
+ /* Register sysfs hooks (don't care about failure) */
+ ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
+ return 0;
+}
+
+static struct usb_driver yealink_driver = {
+ .name = "yealink",
+ .probe = usb_probe,
+ .disconnect = usb_disconnect,
+ .id_table = usb_table,
+};
+
+static int __init yealink_dev_init(void)
+{
+ int ret = usb_register(&yealink_driver);
+ if (ret == 0)
+ info(DRIVER_DESC ":" DRIVER_VERSION);
+ return ret;
+}
+
+static void __exit yealink_dev_exit(void)
+{
+ usb_deregister(&yealink_driver);
+}
+
+module_init(yealink_dev_init);
+module_exit(yealink_dev_exit);
+
+MODULE_DEVICE_TABLE (usb, usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/yealink.h b/drivers/input/misc/yealink.h
new file mode 100644
index 00000000000..48af0be9cbd
--- /dev/null
+++ b/drivers/input/misc/yealink.h
@@ -0,0 +1,220 @@
+/*
+ * drivers/usb/input/yealink.h
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ *
+ * 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
+ */
+#ifndef INPUT_YEALINK_H
+#define INPUT_YEALINK_H
+
+/* Using the control channel on interface 3 various aspects of the phone
+ * can be controlled like LCD, LED, dialtone and the ringtone.
+ */
+
+struct yld_ctl_packet {
+ u8 cmd; /* command code, see below */
+ u8 size; /* 1-11, size of used data bytes. */
+ u16 offset; /* internal packet offset */
+ u8 data[11];
+ s8 sum; /* negative sum of 15 preceding bytes */
+} __attribute__ ((packed));
+
+#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
+
+/* The following yld_ctl_packet's are available: */
+
+/* Init registers
+ *
+ * cmd 0x8e
+ * size 10
+ * offset 0
+ * data 0,0,0,0....
+ */
+#define CMD_INIT 0x8e
+
+/* Request key scan
+ *
+ * cmd 0x80
+ * size 1
+ * offset 0
+ * data[0] on return returns the key number, if it changes there's a new
+ * key pressed.
+ */
+#define CMD_KEYPRESS 0x80
+
+/* Request scancode
+ *
+ * cmd 0x81
+ * size 1
+ * offset key number [0-1f]
+ * data[0] on return returns the scancode
+ */
+#define CMD_SCANCODE 0x81
+
+/* Set LCD
+ *
+ * cmd 0x04
+ * size 1-11
+ * offset 0-23
+ * data segment bits
+ */
+#define CMD_LCD 0x04
+
+/* Set led
+ *
+ * cmd 0x05
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 1 ON
+ */
+#define CMD_LED 0x05
+
+/* Set ringtone volume
+ *
+ * cmd 0x11
+ * size 1
+ * offset 0
+ * data[0] 0-0xff volume
+ */
+#define CMD_RING_VOLUME 0x11
+
+/* Set ringtone notes
+ *
+ * cmd 0x02
+ * size 1-11
+ * offset 0->
+ * data binary representation LE16(-freq), LE16(duration) ....
+ */
+#define CMD_RING_NOTE 0x02
+
+/* Sound ringtone via the speaker on the back
+ *
+ * cmd 0x03
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 0x24 ON
+ */
+#define CMD_RINGTONE 0x03
+
+/* Sound dial tone via the ear speaker
+ *
+ * cmd 0x09
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 1 ON
+ */
+#define CMD_DIALTONE 0x09
+
+#endif /* INPUT_YEALINK_H */
+
+
+#if defined(_SEG) && defined(_PIC)
+/* This table maps the LCD segments onto individual bit positions in the
+ * yld_status struct.
+ */
+
+/* LCD, each segment must be driven seperately.
+ *
+ * Layout:
+ *
+ * |[] [][] [][] [][] in |[][]
+ * |[] M [][] D [][] : [][] out |[][]
+ * store
+ *
+ * NEW REP SU MO TU WE TH FR SA
+ *
+ * [] [] [] [] [] [] [] [] [] [] [] []
+ * [] [] [] [] [] [] [] [] [] [] [] []
+ */
+
+/* Line 1
+ * Format : 18.e8.M8.88...188
+ * Icon names : M D : IN OUT STORE
+ */
+#define LCD_LINE1_OFFSET 0
+#define LCD_LINE1_SIZE 17
+
+/* Note: first g then f => ! ! */
+/* _SEG( type a b c d e g f ) */
+ _SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
+ _SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
+ _PIC('.', 22,1 , "M" ),
+ _SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
+ _SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
+ _PIC('.', 15,8 , "D" ),
+ _SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
+ _SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
+ _PIC('.', 11,8 , ":" ),
+ _SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
+ _SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
+ _PIC('.', 7,1 , "IN" ),
+ _PIC('.', 7,2 , "OUT" ),
+ _PIC('.', 7,4 , "STORE" ),
+ _SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
+ _SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
+ _SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
+
+/* Line 2
+ * Format : .........
+ * Pict. name : NEW REP SU MO TU WE TH FR SA
+ */
+#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
+#define LCD_LINE2_SIZE 9
+
+ _PIC('.', 23,2 , "NEW" ),
+ _PIC('.', 23,4 , "REP" ),
+ _PIC('.', 1,8 , "SU" ),
+ _PIC('.', 1,4 , "MO" ),
+ _PIC('.', 1,2 , "TU" ),
+ _PIC('.', 1,1 , "WE" ),
+ _PIC('.', 0,1 , "TH" ),
+ _PIC('.', 0,2 , "FR" ),
+ _PIC('.', 0,4 , "SA" ),
+
+/* Line 3
+ * Format : 888888888888
+ */
+#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
+#define LCD_LINE3_SIZE 12
+
+ _SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
+ _SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
+ _SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
+ _SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
+ _SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
+ _SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
+ _SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
+ _SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
+ _SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
+ _SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
+ _SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
+ _SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
+
+/* Line 4
+ *
+ * The LED, DIALTONE and RINGTONE are implemented as icons and use the same
+ * sysfs interface.
+ */
+#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
+
+ _PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
+ _PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
+ _PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
+
+#undef _SEG
+#undef _PIC
+#endif /* _SEG && _PIC */
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 35d998c3e57..2ccc114b3ff 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -2,7 +2,7 @@
# Mouse driver configuration
#
menuconfig INPUT_MOUSE
- bool "Mouse"
+ bool "Mice"
default y
help
Say Y here, and a list of supported mice will be displayed.
@@ -19,7 +19,7 @@ config MOUSE_PS2
select SERIO_LIBPS2
select SERIO_I8042 if X86_PC
select SERIO_GSCPS2 if GSC
- ---help---
+ help
Say Y here if you have a PS/2 mouse connected to your system. This
includes the standard 2 or 3-button PS/2 mouse, as well as PS/2
mice with wheels and extra buttons, Microsoft, Logitech or Genius
@@ -37,10 +37,69 @@ config MOUSE_PS2
To compile this driver as a module, choose M here: the
module will be called psmouse.
+config MOUSE_PS2_ALPS
+ bool "ALPS PS/2 mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have an ALPS PS/2 touchpad connected to
+ your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_LOGIPS2PP
+ bool "Logictech PS/2++ mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a Logictech PS/2++ mouse connected to
+ your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_SYNAPTICS
+ bool "Synaptics PS/2 mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a Synaptics PS/2 TouchPad connected to
+ your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_LIFEBOOK
+ bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a Fujitsu B-series Lifebook PS/2
+ TouchScreen connected to your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_TRACKPOINT
+ bool "IBM Trackpoint PS/2 mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have an IBM Trackpoint PS/2 mouse connected
+ to your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_TOUCHKIT
+ bool "eGalax TouchKit PS/2 protocol extension"
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have an eGalax TouchKit PS/2 touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
config MOUSE_SERIAL
tristate "Serial mouse"
select SERIO
- ---help---
+ help
Say Y here if you have a serial (RS-232, COM port) mouse connected
to your system. This includes Sun, MouseSystems, Microsoft,
Logitech and all other compatible serial mice.
@@ -50,6 +109,26 @@ config MOUSE_SERIAL
To compile this driver as a module, choose M here: the
module will be called sermouse.
+config MOUSE_APPLETOUCH
+ tristate "Apple USB Touchpad support"
+ select USB
+ help
+ Say Y here if you want to use an Apple USB Touchpad.
+
+ These are the touchpads that can be found on post-February 2005
+ Apple Powerbooks (prior models have a Synaptics touchpad connected
+ to the ADB bus).
+
+ This driver provides a basic mouse driver but can be interfaced
+ with the synaptics X11 driver to provide acceleration and
+ scrolling in X11.
+
+ For further information, see
+ <file:Documentation/input/appletouch.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called appletouch.
+
config MOUSE_INPORT
tristate "InPort/MS/ATIXL busmouse"
depends on ISA
@@ -96,6 +175,17 @@ config MOUSE_AMIGA
To compile this driver as a module, choose M here: the
module will be called amimouse.
+config MOUSE_ATARI
+ tristate "Atari mouse"
+ depends on ATARI
+ select ATARI_KBD_CORE
+ help
+ Say Y here if you have an Atari and want its native mouse
+ supported by the kernel.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atarimouse.
+
config MOUSE_RISCPC
tristate "Acorn RiscPC mouse"
depends on ARCH_ACORN
@@ -118,7 +208,7 @@ config MOUSE_VSXXXAA
digitizer (VSXXX-AB) DEC produced.
config MOUSE_HIL
- tristate "HIL pointers (mice etc)."
+ tristate "HIL pointers (mice etc)."
depends on GSC || HP300
select HP_SDC
select HIL_MLC
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 21a1de61a79..aa4ba878533 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -5,6 +5,8 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
+obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
+obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
@@ -14,4 +16,10 @@ obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
-psmouse-objs := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o trackpoint.o
+psmouse-objs := psmouse-base.o synaptics.o
+
+psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
+psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
+psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
+psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
+psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 4e71a66fc7f..cf3e4664e72 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -424,14 +424,15 @@ int alps_init(struct psmouse *psmouse)
struct input_dev *dev1 = psmouse->dev, *dev2;
int version;
- psmouse->private = priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
+ priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
dev2 = input_allocate_device();
if (!priv || !dev2)
goto init_fail;
priv->dev2 = dev2;
- if (!(priv->i = alps_get_model(psmouse, &version)))
+ priv->i = alps_get_model(psmouse, &version);
+ if (!priv->i)
goto init_fail;
if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1))
@@ -480,7 +481,8 @@ int alps_init(struct psmouse *psmouse)
dev2->relbit[LONG(REL_X)] |= BIT(REL_X) | BIT(REL_Y);
dev2->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
- input_register_device(priv->dev2);
+ if (input_register_device(priv->dev2))
+ goto init_fail;
psmouse->protocol_handler = alps_process_byte;
psmouse->poll = alps_poll;
@@ -491,9 +493,11 @@ int alps_init(struct psmouse *psmouse)
/* We are having trouble resyncing ALPS touchpads so disable it for now */
psmouse->resync_time = 0;
+ psmouse->private = priv;
return 0;
init_fail:
+ psmouse_reset(psmouse);
input_free_device(dev2);
kfree(priv);
return -1;
@@ -504,7 +508,8 @@ int alps_detect(struct psmouse *psmouse, int set_properties)
int version;
const struct alps_model_info *model;
- if (!(model = alps_get_model(psmouse, &version)))
+ model = alps_get_model(psmouse, &version);
+ if (!model)
return -1;
if (set_properties) {
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 69db7325a49..4bbddc99962 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -12,9 +12,6 @@
#ifndef _ALPS_H
#define _ALPS_H
-int alps_detect(struct psmouse *psmouse, int set_properties);
-int alps_init(struct psmouse *psmouse);
-
struct alps_model_info {
unsigned char signature[3];
unsigned char byte0, mask0;
@@ -23,10 +20,23 @@ struct alps_model_info {
struct alps_data {
struct input_dev *dev2; /* Relative device */
- char name[32]; /* Name */
char phys[32]; /* Phys */
const struct alps_model_info *i;/* Info */
int prev_fin; /* Finger bit from previous packet */
};
+#ifdef CONFIG_MOUSE_PS2_ALPS
+int alps_detect(struct psmouse *psmouse, int set_properties);
+int alps_init(struct psmouse *psmouse);
+#else
+inline int alps_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+inline int alps_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ALPS */
+
#endif
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
new file mode 100644
index 00000000000..e3215267db1
--- /dev/null
+++ b/drivers/input/mouse/appletouch.c
@@ -0,0 +1,706 @@
+/*
+ * Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
+ *
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
+ *
+ * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+/* Apple has powerbooks which have the keyboard with different Product IDs */
+#define APPLE_VENDOR_ID 0x05AC
+
+/* These names come from Info.plist in AppleUSBTrackpad.kext */
+#define FOUNTAIN_ANSI_PRODUCT_ID 0x020E
+#define FOUNTAIN_ISO_PRODUCT_ID 0x020F
+
+#define FOUNTAIN_TP_ONLY_PRODUCT_ID 0x030A
+
+#define GEYSER1_TP_ONLY_PRODUCT_ID 0x030B
+
+#define GEYSER_ANSI_PRODUCT_ID 0x0214
+#define GEYSER_ISO_PRODUCT_ID 0x0215
+#define GEYSER_JIS_PRODUCT_ID 0x0216
+
+/* MacBook devices */
+#define GEYSER3_ANSI_PRODUCT_ID 0x0217
+#define GEYSER3_ISO_PRODUCT_ID 0x0218
+#define GEYSER3_JIS_PRODUCT_ID 0x0219
+
+/*
+ * Geyser IV: same as Geyser III according to Info.plist in AppleUSBTrackpad.kext
+ * -> same IOClass (AppleUSBGrIIITrackpad), same acceleration tables
+ */
+#define GEYSER4_ANSI_PRODUCT_ID 0x021A
+#define GEYSER4_ISO_PRODUCT_ID 0x021B
+#define GEYSER4_JIS_PRODUCT_ID 0x021C
+
+#define ATP_DEVICE(prod) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS | \
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
+ .idVendor = APPLE_VENDOR_ID, \
+ .idProduct = (prod), \
+ .bInterfaceClass = 0x03, \
+ .bInterfaceProtocol = 0x02
+
+/* table of devices that work with this driver */
+static struct usb_device_id atp_table [] = {
+ { ATP_DEVICE(FOUNTAIN_ANSI_PRODUCT_ID) },
+ { ATP_DEVICE(FOUNTAIN_ISO_PRODUCT_ID) },
+ { ATP_DEVICE(FOUNTAIN_TP_ONLY_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER1_TP_ONLY_PRODUCT_ID) },
+
+ /* PowerBooks Oct 2005 */
+ { ATP_DEVICE(GEYSER_ANSI_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) },
+
+ /* Core Duo MacBook & MacBook Pro */
+ { ATP_DEVICE(GEYSER3_ANSI_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER3_ISO_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER3_JIS_PRODUCT_ID) },
+
+ /* Core2 Duo MacBook & MacBook Pro */
+ { ATP_DEVICE(GEYSER4_ANSI_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER4_ISO_PRODUCT_ID) },
+ { ATP_DEVICE(GEYSER4_JIS_PRODUCT_ID) },
+
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE (usb, atp_table);
+
+/*
+ * number of sensors. Note that only 16 instead of 26 X (horizontal)
+ * sensors exist on 12" and 15" PowerBooks. All models have 16 Y
+ * (vertical) sensors.
+ */
+#define ATP_XSENSORS 26
+#define ATP_YSENSORS 16
+
+/* amount of fuzz this touchpad generates */
+#define ATP_FUZZ 16
+
+/* maximum pressure this driver will report */
+#define ATP_PRESSURE 300
+/*
+ * multiplication factor for the X and Y coordinates.
+ * We try to keep the touchpad aspect ratio while still doing only simple
+ * arithmetics.
+ * The factors below give coordinates like:
+ * 0 <= x < 960 on 12" and 15" Powerbooks
+ * 0 <= x < 1600 on 17" Powerbooks
+ * 0 <= y < 646
+ */
+#define ATP_XFACT 64
+#define ATP_YFACT 43
+
+/*
+ * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
+ * ignored.
+ */
+#define ATP_THRESHOLD 5
+
+/* MacBook Pro (Geyser 3 & 4) initialization constants */
+#define ATP_GEYSER3_MODE_READ_REQUEST_ID 1
+#define ATP_GEYSER3_MODE_WRITE_REQUEST_ID 9
+#define ATP_GEYSER3_MODE_REQUEST_VALUE 0x300
+#define ATP_GEYSER3_MODE_REQUEST_INDEX 0
+#define ATP_GEYSER3_MODE_VENDOR_VALUE 0x04
+
+/* Structure to hold all of our device specific stuff */
+struct atp {
+ char phys[64];
+ struct usb_device * udev; /* usb device */
+ struct urb * urb; /* usb request block */
+ signed char * data; /* transferred data */
+ int open; /* non-zero if opened */
+ struct input_dev *input; /* input dev */
+ int valid; /* are the sensors valid ? */
+ int x_old; /* last reported x/y, */
+ int y_old; /* used for smoothing */
+ /* current value of the sensors */
+ signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
+ /* last value of the sensors */
+ signed char xy_old[ATP_XSENSORS + ATP_YSENSORS];
+ /* accumulated sensors */
+ int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
+ int overflowwarn; /* overflow warning printed? */
+ int datalen; /* size of an USB urb transfer */
+};
+
+#define dbg_dump(msg, tab) \
+ if (debug > 1) { \
+ int i; \
+ printk("appletouch: %s %lld", msg, (long long)jiffies); \
+ for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) \
+ printk(" %02x", tab[i]); \
+ printk("\n"); \
+ }
+
+#define dprintk(format, a...) \
+ do { \
+ if (debug) printk(format, ##a); \
+ } while (0)
+
+MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann");
+MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Make the threshold a module parameter
+ */
+static int threshold = ATP_THRESHOLD;
+module_param(threshold, int, 0644);
+MODULE_PARM_DESC(threshold, "Discards any change in data from a sensor (trackpad has hundreds of these sensors) less than this value");
+
+static int debug = 1;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activate debugging output");
+
+/* Checks if the device a Geyser 2 (ANSI, ISO, JIS) */
+static inline int atp_is_geyser_2(struct atp *dev)
+{
+ u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+ return (productId == GEYSER_ANSI_PRODUCT_ID) ||
+ (productId == GEYSER_ISO_PRODUCT_ID) ||
+ (productId == GEYSER_JIS_PRODUCT_ID);
+}
+
+static inline int atp_is_geyser_3(struct atp *dev)
+{
+ u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+ return (productId == GEYSER3_ANSI_PRODUCT_ID) ||
+ (productId == GEYSER3_ISO_PRODUCT_ID) ||
+ (productId == GEYSER3_JIS_PRODUCT_ID) ||
+ (productId == GEYSER4_ANSI_PRODUCT_ID) ||
+ (productId == GEYSER4_ISO_PRODUCT_ID) ||
+ (productId == GEYSER4_JIS_PRODUCT_ID);
+}
+
+static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
+ int *z, int *fingers)
+{
+ int i;
+ /* values to calculate mean */
+ int pcum = 0, psum = 0;
+ int is_increasing = 0;
+
+ *fingers = 0;
+
+ for (i = 0; i < nb_sensors; i++) {
+ if (xy_sensors[i] < threshold) {
+ if (is_increasing)
+ is_increasing = 0;
+
+ continue;
+ }
+
+ /*
+ * Makes the finger detection more versatile. For example,
+ * two fingers with no gap will be detected. Also, my
+ * tests show it less likely to have intermittent loss
+ * of multiple finger readings while moving around (scrolling).
+ *
+ * Changes the multiple finger detection to counting humps on
+ * sensors (transitions from nonincreasing to increasing)
+ * instead of counting transitions from low sensors (no
+ * finger reading) to high sensors (finger above
+ * sensor)
+ *
+ * - Jason Parekh <jasonparekh@gmail.com>
+ */
+ if (i < 1 || (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
+ (*fingers)++;
+ is_increasing = 1;
+ } else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) {
+ is_increasing = 0;
+ }
+
+ /*
+ * Subtracts threshold so a high sensor that just passes the threshold
+ * won't skew the calculated absolute coordinate. Fixes an issue
+ * where slowly moving the mouse would occassionaly jump a number of
+ * pixels (let me restate--slowly moving the mouse makes this issue
+ * most apparent).
+ */
+ pcum += (xy_sensors[i] - threshold) * i;
+ psum += (xy_sensors[i] - threshold);
+ }
+
+ if (psum > 0) {
+ *z = psum;
+ return pcum * fact / psum;
+ }
+
+ return 0;
+}
+
+static inline void atp_report_fingers(struct input_dev *input, int fingers)
+{
+ input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
+ input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
+ input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
+}
+
+static void atp_complete(struct urb* urb)
+{
+ int x, y, x_z, y_z, x_f, y_f;
+ int retval, i, j;
+ struct atp *dev = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -EOVERFLOW:
+ if(!dev->overflowwarn) {
+ printk("appletouch: OVERFLOW with data "
+ "length %d, actual length is %d\n",
+ dev->datalen, dev->urb->actual_length);
+ dev->overflowwarn = 1;
+ }
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ /* drop incomplete datasets */
+ if (dev->urb->actual_length != dev->datalen) {
+ dprintk("appletouch: incomplete data package"
+ " (first byte: %d, length: %d).\n",
+ dev->data[0], dev->urb->actual_length);
+ goto exit;
+ }
+
+ /* reorder the sensors values */
+ if (atp_is_geyser_3(dev)) {
+ memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
+
+ /*
+ * The values are laid out like this:
+ * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
+ * '-' is an unused value.
+ */
+
+ /* read X values */
+ for (i = 0, j = 19; i < 20; i += 2, j += 3) {
+ dev->xy_cur[i] = dev->data[j + 1];
+ dev->xy_cur[i + 1] = dev->data[j + 2];
+ }
+ /* read Y values */
+ for (i = 0, j = 1; i < 9; i += 2, j += 3) {
+ dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
+ dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
+ }
+ } else if (atp_is_geyser_2(dev)) {
+ memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
+
+ /*
+ * The values are laid out like this:
+ * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
+ * '-' is an unused value.
+ */
+
+ /* read X values */
+ for (i = 0, j = 19; i < 20; i += 2, j += 3) {
+ dev->xy_cur[i] = dev->data[j];
+ dev->xy_cur[i + 1] = dev->data[j + 1];
+ }
+
+ /* read Y values */
+ for (i = 0, j = 1; i < 9; i += 2, j += 3) {
+ dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
+ dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
+ }
+ } else {
+ for (i = 0; i < 8; i++) {
+ /* X values */
+ dev->xy_cur[i ] = dev->data[5 * i + 2];
+ dev->xy_cur[i + 8] = dev->data[5 * i + 4];
+ dev->xy_cur[i + 16] = dev->data[5 * i + 42];
+ if (i < 2)
+ dev->xy_cur[i + 24] = dev->data[5 * i + 44];
+
+ /* Y values */
+ dev->xy_cur[i + 26] = dev->data[5 * i + 1];
+ dev->xy_cur[i + 34] = dev->data[5 * i + 3];
+ }
+ }
+
+ dbg_dump("sample", dev->xy_cur);
+
+ if (!dev->valid) {
+ /* first sample */
+ dev->valid = 1;
+ dev->x_old = dev->y_old = -1;
+ memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+
+ if (atp_is_geyser_3(dev)) /* No 17" Macbooks (yet) */
+ goto exit;
+
+ /* 17" Powerbooks have extra X sensors */
+ for (i = (atp_is_geyser_2(dev)?15:16); i < ATP_XSENSORS; i++) {
+ if (!dev->xy_cur[i]) continue;
+
+ printk("appletouch: 17\" model detected.\n");
+ if(atp_is_geyser_2(dev))
+ input_set_abs_params(dev->input, ABS_X, 0,
+ (20 - 1) *
+ ATP_XFACT - 1,
+ ATP_FUZZ, 0);
+ else
+ input_set_abs_params(dev->input, ABS_X, 0,
+ (ATP_XSENSORS - 1) *
+ ATP_XFACT - 1,
+ ATP_FUZZ, 0);
+
+ break;
+ }
+
+ goto exit;
+ }
+
+ for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
+ /* accumulate the change */
+ signed char change = dev->xy_old[i] - dev->xy_cur[i];
+ dev->xy_acc[i] -= change;
+
+ /* prevent down drifting */
+ if (dev->xy_acc[i] < 0)
+ dev->xy_acc[i] = 0;
+ }
+
+ memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+
+ dbg_dump("accumulator", dev->xy_acc);
+
+ x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
+ ATP_XFACT, &x_z, &x_f);
+ y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
+ ATP_YFACT, &y_z, &y_f);
+
+ if (x && y) {
+ if (dev->x_old != -1) {
+ x = (dev->x_old * 3 + x) >> 2;
+ y = (dev->y_old * 3 + y) >> 2;
+ dev->x_old = x;
+ dev->y_old = y;
+
+ if (debug > 1)
+ printk("appletouch: X: %3d Y: %3d "
+ "Xz: %3d Yz: %3d\n",
+ x, y, x_z, y_z);
+
+ input_report_key(dev->input, BTN_TOUCH, 1);
+ input_report_abs(dev->input, ABS_X, x);
+ input_report_abs(dev->input, ABS_Y, y);
+ input_report_abs(dev->input, ABS_PRESSURE,
+ min(ATP_PRESSURE, x_z + y_z));
+ atp_report_fingers(dev->input, max(x_f, y_f));
+ }
+ dev->x_old = x;
+ dev->y_old = y;
+ }
+ else if (!x && !y) {
+
+ dev->x_old = dev->y_old = -1;
+ input_report_key(dev->input, BTN_TOUCH, 0);
+ input_report_abs(dev->input, ABS_PRESSURE, 0);
+ atp_report_fingers(dev->input, 0);
+
+ /* reset the accumulator on release */
+ memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
+ }
+
+ input_report_key(dev->input, BTN_LEFT,
+ !!dev->data[dev->datalen - 1]);
+
+ input_sync(dev->input);
+
+exit:
+ retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+ if (retval) {
+ err("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+ }
+}
+
+static int atp_open(struct input_dev *input)
+{
+ struct atp *dev = input_get_drvdata(input);
+
+ if (usb_submit_urb(dev->urb, GFP_ATOMIC))
+ return -EIO;
+
+ dev->open = 1;
+ return 0;
+}
+
+static void atp_close(struct input_dev *input)
+{
+ struct atp *dev = input_get_drvdata(input);
+
+ usb_kill_urb(dev->urb);
+ dev->open = 0;
+}
+
+static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+ struct atp *dev;
+ struct input_dev *input_dev;
+ struct usb_device *udev = interface_to_usbdev(iface);
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int int_in_endpointAddr = 0;
+ int i, error = -ENOMEM;
+
+ /* set up the endpoint information */
+ /* use only the first interrupt-in endpoint */
+ iface_desc = iface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
+ /* we found an interrupt in endpoint */
+ int_in_endpointAddr = endpoint->bEndpointAddress;
+ break;
+ }
+ }
+ if (!int_in_endpointAddr) {
+ err("Could not find int-in endpoint");
+ return -EIO;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!dev || !input_dev) {
+ err("Out of memory");
+ goto err_free_devs;
+ }
+
+ dev->udev = udev;
+ dev->input = input_dev;
+ dev->overflowwarn = 0;
+ if (atp_is_geyser_3(dev))
+ dev->datalen = 64;
+ else if (atp_is_geyser_2(dev))
+ dev->datalen = 64;
+ else
+ dev->datalen = 81;
+
+ if (atp_is_geyser_3(dev)) {
+ /*
+ * By default Geyser 3 device sends standard USB HID mouse
+ * packets (Report ID 2). This code changes device mode, so it
+ * sends raw sensor reports (Report ID 5).
+ */
+ char data[8];
+ int size;
+
+ size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ ATP_GEYSER3_MODE_READ_REQUEST_ID,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ATP_GEYSER3_MODE_REQUEST_VALUE,
+ ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
+
+ if (size != 8) {
+ err("Could not do mode read request from device"
+ " (Geyser 3 mode)");
+ goto err_free_devs;
+ }
+
+ /* Apply the mode switch */
+ data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;
+
+ size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ATP_GEYSER3_MODE_REQUEST_VALUE,
+ ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
+
+ if (size != 8) {
+ err("Could not do mode write request to device"
+ " (Geyser 3 mode)");
+ goto err_free_devs;
+ }
+ printk("appletouch Geyser 3 inited.\n");
+ }
+
+ dev->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb)
+ goto err_free_devs;
+
+ dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
+ &dev->urb->transfer_dma);
+ if (!dev->data)
+ goto err_free_urb;
+
+ usb_fill_int_urb(dev->urb, udev,
+ usb_rcvintpipe(udev, int_in_endpointAddr),
+ dev->data, dev->datalen, atp_complete, dev, 1);
+
+ usb_make_path(udev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ input_dev->name = "appletouch";
+ input_dev->phys = dev->phys;
+ usb_to_input_id(dev->udev, &input_dev->id);
+ input_dev->dev.parent = &iface->dev;
+
+ input_set_drvdata(input_dev, dev);
+
+ input_dev->open = atp_open;
+ input_dev->close = atp_close;
+
+ set_bit(EV_ABS, input_dev->evbit);
+
+ if (atp_is_geyser_3(dev)) {
+ /*
+ * MacBook have 20 X sensors, 10 Y sensors
+ */
+ input_set_abs_params(input_dev, ABS_X, 0,
+ ((20 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ ((10 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
+ } else if (atp_is_geyser_2(dev)) {
+ /*
+ * Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected
+ * later.
+ */
+ input_set_abs_params(input_dev, ABS_X, 0,
+ ((15 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ ((9 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
+ } else {
+ /*
+ * 12" and 15" Powerbooks only have 16 x sensors,
+ * 17" models are detected later.
+ */
+ input_set_abs_params(input_dev, ABS_X, 0,
+ (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ (ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
+ }
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
+
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(BTN_TOUCH, input_dev->keybit);
+ set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+ set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ set_bit(BTN_LEFT, input_dev->keybit);
+
+ error = input_register_device(dev->input);
+ if (error)
+ goto err_free_buffer;
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(iface, dev);
+
+ return 0;
+
+ err_free_buffer:
+ usb_buffer_free(dev->udev, dev->datalen,
+ dev->data, dev->urb->transfer_dma);
+ err_free_urb:
+ usb_free_urb(dev->urb);
+ err_free_devs:
+ usb_set_intfdata(iface, NULL);
+ kfree(dev);
+ input_free_device(input_dev);
+ return error;
+}
+
+static void atp_disconnect(struct usb_interface *iface)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+
+ usb_set_intfdata(iface, NULL);
+ if (dev) {
+ usb_kill_urb(dev->urb);
+ input_unregister_device(dev->input);
+ usb_buffer_free(dev->udev, dev->datalen,
+ dev->data, dev->urb->transfer_dma);
+ usb_free_urb(dev->urb);
+ kfree(dev);
+ }
+ printk(KERN_INFO "input: appletouch disconnected\n");
+}
+
+static int atp_suspend(struct usb_interface *iface, pm_message_t message)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+ usb_kill_urb(dev->urb);
+ dev->valid = 0;
+ return 0;
+}
+
+static int atp_resume(struct usb_interface *iface)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+ if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
+ return -EIO;
+
+ return 0;
+}
+
+static struct usb_driver atp_driver = {
+ .name = "appletouch",
+ .probe = atp_probe,
+ .disconnect = atp_disconnect,
+ .suspend = atp_suspend,
+ .resume = atp_resume,
+ .id_table = atp_table,
+};
+
+static int __init atp_init(void)
+{
+ return usb_register(&atp_driver);
+}
+
+static void __exit atp_exit(void)
+{
+ usb_deregister(&atp_driver);
+}
+
+module_init(atp_init);
+module_exit(atp_exit);
diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c
new file mode 100644
index 00000000000..43ab6566fb6
--- /dev/null
+++ b/drivers/input/mouse/atarimouse.c
@@ -0,0 +1,160 @@
+/*
+ * Atari mouse driver for Linux/m68k
+ *
+ * Copyright (c) 2005 Michael Schmitz
+ *
+ * Based on:
+ * Amiga mouse driver for Linux/m68k
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik
+ *
+ */
+/*
+ * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
+ * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
+ * interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
+ * This driver only deals with handing key events off to the input layer.
+ *
+ * Largely based on the old:
+ *
+ * Atari Mouse Driver for Linux
+ * by Robert de Vries (robert@and.nl) 19Jul93
+ *
+ * 16 Nov 1994 Andreas Schwab
+ * Compatibility with busmouse
+ * Support for three button mouse (shamelessly stolen from MiNT)
+ * third button wired to one of the joystick directions on joystick 1
+ *
+ * 1996/02/11 Andreas Schwab
+ * Module support
+ * Allow multiple open's
+ *
+ * Converted to use new generic busmouse code. 5 Apr 1998
+ * Russell King <rmk@arm.uk.linux.org>
+ */
+
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/atarihw.h>
+#include <asm/atarikb.h>
+#include <asm/atariints.h>
+
+MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>");
+MODULE_DESCRIPTION("Atari mouse driver");
+MODULE_LICENSE("GPL");
+
+static int mouse_threshold[2] = {2,2};
+
+#ifdef __MODULE__
+MODULE_PARM(mouse_threshold, "2i");
+#endif
+#ifdef FIXED_ATARI_JOYSTICK
+extern int atari_mouse_buttons;
+#endif
+static int atamouse_used = 0;
+
+static struct input_dev *atamouse_dev;
+
+static void atamouse_interrupt(char *buf)
+{
+ int buttons, dx, dy;
+
+/* ikbd_mouse_disable(); */
+
+ buttons = (buf[0] & 1) | ((buf[0] & 2) << 1);
+#ifdef FIXED_ATARI_JOYSTICK
+ buttons |= atari_mouse_buttons & 2;
+ atari_mouse_buttons = buttons;
+#endif
+/* ikbd_mouse_rel_pos(); */
+
+ /* only relative events get here */
+ dx = buf[1];
+ dy = -buf[2];
+
+ input_report_rel(atamouse_dev, REL_X, dx);
+ input_report_rel(atamouse_dev, REL_Y, dy);
+
+ input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x1);
+ input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2);
+ input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x4);
+
+ input_sync(atamouse_dev);
+
+ return;
+}
+
+static int atamouse_open(struct input_dev *dev)
+{
+ if (atamouse_used++)
+ return 0;
+
+#ifdef FIXED_ATARI_JOYSTICK
+ atari_mouse_buttons = 0;
+#endif
+ ikbd_mouse_y0_top();
+ ikbd_mouse_thresh(mouse_threshold[0], mouse_threshold[1]);
+ ikbd_mouse_rel_pos();
+ atari_input_mouse_interrupt_hook = atamouse_interrupt;
+ return 0;
+}
+
+static void atamouse_close(struct input_dev *dev)
+{
+ if (!--atamouse_used) {
+ ikbd_mouse_disable();
+ atari_mouse_interrupt_hook = NULL;
+ }
+}
+
+static int __init atamouse_init(void)
+{
+ if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
+ return -ENODEV;
+
+ if (!(atamouse_dev = input_allocate_device()))
+ return -ENOMEM;
+
+ if (!(atari_keyb_init()))
+ return -ENODEV;
+
+ atamouse_dev->name = "Atari mouse";
+ atamouse_dev->phys = "atamouse/input0";
+ atamouse_dev->id.bustype = BUS_ATARI;
+ atamouse_dev->id.vendor = 0x0001;
+ atamouse_dev->id.product = 0x0002;
+ atamouse_dev->id.version = 0x0100;
+
+ atamouse_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ atamouse_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ atamouse_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+ atamouse_dev->open = atamouse_open;
+ atamouse_dev->close = atamouse_close;
+
+ input_register_device(atamouse_dev);
+
+ printk(KERN_INFO "input: %s at keyboard ACIA\n", atamouse_dev->name);
+ return 0;
+}
+
+static void __exit atamouse_exit(void)
+{
+ input_unregister_device(atamouse_dev);
+}
+
+module_init(atamouse_init);
+module_exit(atamouse_exit);
diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c
index bfb174fe323..449bf4dcbbc 100644
--- a/drivers/input/mouse/hil_ptr.c
+++ b/drivers/input/mouse/hil_ptr.c
@@ -88,10 +88,12 @@ static void hil_ptr_process_record(struct hil_ptr *ptr)
idx = ptr->idx4/4;
p = data[idx - 1];
- if ((p & ~HIL_CMDCT_POL) ==
- (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report;
- if ((p & ~HIL_CMDCT_RPL) ==
- (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report;
+ if ((p & ~HIL_CMDCT_POL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
+ goto report;
+ if ((p & ~HIL_CMDCT_RPL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
+ goto report;
/* Not a poll response. See if we are loading config records. */
switch (p & HIL_PKT_DATA_MASK) {
@@ -101,27 +103,32 @@ static void hil_ptr_process_record(struct hil_ptr *ptr)
for (; i < HIL_PTR_MAX_LENGTH; i++)
ptr->idd[i] = 0;
break;
+
case HIL_CMD_RSC:
for (i = 0; i < idx; i++)
ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
for (; i < HIL_PTR_MAX_LENGTH; i++)
ptr->rsc[i] = 0;
break;
+
case HIL_CMD_EXD:
for (i = 0; i < idx; i++)
ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
for (; i < HIL_PTR_MAX_LENGTH; i++)
ptr->exd[i] = 0;
break;
+
case HIL_CMD_RNM:
for (i = 0; i < idx; i++)
ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
for (; i < HIL_PTR_MAX_LENGTH + 1; i++)
- ptr->rnm[i] = '\0';
+ ptr->rnm[i] = 0;
break;
+
default:
/* These occur when device isn't present */
- if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break;
+ if (p == (HIL_ERR_INT | HIL_PKT_CMD))
+ break;
/* Anything else we'd like to know about. */
printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
break;
@@ -130,7 +137,8 @@ static void hil_ptr_process_record(struct hil_ptr *ptr)
report:
if ((p & HIL_CMDCT_POL) != idx - 1) {
- printk(KERN_WARNING PREFIX "Malformed poll packet %x (idx = %i)\n", p, idx);
+ printk(KERN_WARNING PREFIX
+ "Malformed poll packet %x (idx = %i)\n", p, idx);
goto out;
}
@@ -139,7 +147,7 @@ static void hil_ptr_process_record(struct hil_ptr *ptr)
laxis += i;
ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
- absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS;
+ absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS;
for (cnt = 1; i < laxis; i++) {
unsigned int lo,hi,val;
@@ -157,7 +165,8 @@ static void hil_ptr_process_record(struct hil_ptr *ptr)
input_report_abs(dev, ABS_X + i, val);
} else {
val = (int) (((int8_t)lo) | ((int8_t)hi<<8));
- if (i%3) val *= -1;
+ if (i%3)
+ val *= -1;
input_report_rel(dev, REL_X + i, val);
}
}
@@ -168,10 +177,11 @@ static void hil_ptr_process_record(struct hil_ptr *ptr)
btn = ptr->data[cnt++];
up = btn & 1;
btn &= 0xfe;
- if (btn == 0x8e) {
+ if (btn == 0x8e)
continue; /* TODO: proximity == touch? */
- }
- else if ((btn > 0x8c) || (btn < 0x80)) continue;
+ else
+ if ((btn > 0x8c) || (btn < 0x80))
+ continue;
btn = (btn - 0x80) >> 1;
btn = ptr->btnmap[btn];
input_report_key(dev, btn, !up);
@@ -182,14 +192,14 @@ static void hil_ptr_process_record(struct hil_ptr *ptr)
up(&ptr->sem);
}
-static void hil_ptr_process_err(struct hil_ptr *ptr) {
+static void hil_ptr_process_err(struct hil_ptr *ptr)
+{
printk(KERN_WARNING PREFIX "errored HIL packet\n");
ptr->idx4 = 0;
up(&ptr->sem);
- return;
}
-static irqreturn_t hil_ptr_interrupt(struct serio *serio,
+static irqreturn_t hil_ptr_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct hil_ptr *ptr;
@@ -197,29 +207,29 @@ static irqreturn_t hil_ptr_interrupt(struct serio *serio,
int idx;
ptr = serio_get_drvdata(serio);
- if (ptr == NULL) {
- BUG();
- return IRQ_HANDLED;
- }
+ BUG_ON(ptr == NULL);
if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) {
hil_ptr_process_err(ptr);
return IRQ_HANDLED;
}
idx = ptr->idx4/4;
- if (!(ptr->idx4 % 4)) ptr->data[idx] = 0;
+ if (!(ptr->idx4 % 4))
+ ptr->data[idx] = 0;
packet = ptr->data[idx];
packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8);
ptr->data[idx] = packet;
/* Records of N 4-byte hil_packets must terminate with a command. */
- if ((++(ptr->idx4)) % 4) return IRQ_HANDLED;
+ if ((++(ptr->idx4)) % 4)
+ return IRQ_HANDLED;
if ((packet & 0xffff0000) != HIL_ERR_INT) {
hil_ptr_process_err(ptr);
return IRQ_HANDLED;
}
- if (packet & HIL_PKT_CMD)
+ if (packet & HIL_PKT_CMD)
hil_ptr_process_record(ptr);
+
return IRQ_HANDLED;
}
@@ -228,10 +238,7 @@ static void hil_ptr_disconnect(struct serio *serio)
struct hil_ptr *ptr;
ptr = serio_get_drvdata(serio);
- if (ptr == NULL) {
- BUG();
- return;
- }
+ BUG_ON(ptr == NULL);
serio_close(serio);
input_unregister_device(ptr->dev);
@@ -241,7 +248,7 @@ static void hil_ptr_disconnect(struct serio *serio)
static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
{
struct hil_ptr *ptr;
- char *txt;
+ const char *txt;
unsigned int i, naxsets, btntype;
uint8_t did, *idd;
@@ -252,42 +259,40 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
if (!ptr->dev)
goto bail0;
- ptr->dev->private = ptr;
-
if (serio_open(serio, driver))
goto bail1;
serio_set_drvdata(serio, ptr);
ptr->serio = serio;
- init_MUTEX_LOCKED(&(ptr->sem));
+ init_MUTEX_LOCKED(&ptr->sem);
/* Get device info. MLC driver supplies devid/status/etc. */
serio->write(serio, 0);
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_IDD);
- down(&(ptr->sem));
+ down(&ptr->sem);
serio->write(serio, 0);
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_RSC);
- down(&(ptr->sem));
+ down(&ptr->sem);
serio->write(serio, 0);
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_RNM);
- down(&(ptr->sem));
+ down(&ptr->sem);
serio->write(serio, 0);
serio->write(serio, 0);
serio->write(serio, HIL_PKT_CMD >> 8);
serio->write(serio, HIL_CMD_EXD);
- down(&(ptr->sem));
+ down(&ptr->sem);
- up(&(ptr->sem));
+ up(&ptr->sem);
did = ptr->idd[0];
idd = ptr->idd + 1;
@@ -301,12 +306,12 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
ptr->dev->evbit[0] = BIT(EV_ABS);
txt = "absolute";
}
- if (!ptr->dev->evbit[0]) {
+ if (!ptr->dev->evbit[0])
goto bail2;
- }
ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
- if (ptr->nbtn) ptr->dev->evbit[0] |= BIT(EV_KEY);
+ if (ptr->nbtn)
+ ptr->dev->evbit[0] |= BIT(EV_KEY);
naxsets = HIL_IDD_NUM_AXSETS(*idd);
ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
@@ -315,7 +320,7 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
did, txt);
printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n",
ptr->nbtn, naxsets, ptr->naxes);
-
+
btntype = BTN_MISC;
if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
#ifdef TABLET_SIMULATES_MOUSE
@@ -325,7 +330,7 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
#endif
if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
btntype = BTN_TOUCH;
-
+
if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
btntype = BTN_MOUSE;
@@ -341,12 +346,10 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
}
if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
- for (i = 0; i < ptr->naxes; i++) {
+ for (i = 0; i < ptr->naxes; i++)
set_bit(REL_X + i, ptr->dev->relbit);
- }
- for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
+ for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++)
set_bit(REL_X + i, ptr->dev->relbit);
- }
} else {
for (i = 0; i < ptr->naxes; i++) {
set_bit(ABS_X + i, ptr->dev->absbit);
@@ -375,7 +378,7 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
ptr->dev->id.vendor = PCI_VENDOR_ID_HP;
ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */
ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */
- ptr->dev->cdev.dev = &serio->dev;
+ ptr->dev->dev.parent = &serio->dev;
input_register_device(ptr->dev);
printk(KERN_INFO "input: %s (%s), ID: %d\n",
@@ -419,11 +422,11 @@ static int __init hil_ptr_init(void)
{
return serio_register_driver(&hil_ptr_serio_driver);
}
-
+
static void __exit hil_ptr_exit(void)
{
serio_unregister_driver(&hil_ptr_serio_driver);
}
-
+
module_init(hil_ptr_init);
module_exit(hil_ptr_exit);
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index 29542f0631c..1740cadd959 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -20,6 +20,27 @@
#include "psmouse.h"
#include "lifebook.h"
+struct lifebook_data {
+ struct input_dev *dev2; /* Relative device */
+ char phys[32];
+};
+
+static const char *desired_serio_phys;
+
+static int lifebook_set_serio_phys(struct dmi_system_id *d)
+{
+ desired_serio_phys = d->driver_data;
+ return 0;
+}
+
+static unsigned char lifebook_use_6byte_proto;
+
+static int lifebook_set_6byte_proto(struct dmi_system_id *d)
+{
+ lifebook_use_6byte_proto = 1;
+ return 0;
+}
+
static struct dmi_system_id lifebook_dmi_table[] = {
{
.ident = "FLORA-ie 55mi",
@@ -56,6 +77,24 @@ static struct dmi_system_id lifebook_dmi_table[] = {
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
},
+ .callback = lifebook_set_serio_phys,
+ .driver_data = "isa0060/serio3",
+ },
+ {
+ .ident = "Panasonic CF-28",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"),
+ },
+ .callback = lifebook_set_6byte_proto,
+ },
+ {
+ .ident = "Panasonic CF-29",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
+ },
+ .callback = lifebook_set_6byte_proto,
},
{
.ident = "Lifebook B142",
@@ -68,30 +107,70 @@ static struct dmi_system_id lifebook_dmi_table[] = {
static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
{
+ struct lifebook_data *priv = psmouse->private;
+ struct input_dev *dev1 = psmouse->dev;
+ struct input_dev *dev2 = priv->dev2;
unsigned char *packet = psmouse->packet;
- struct input_dev *dev = psmouse->dev;
+ int relative_packet = packet[0] & 0x08;
- if (psmouse->pktcnt != 3)
- return PSMOUSE_GOOD_DATA;
+ if (relative_packet || !lifebook_use_6byte_proto) {
+ if (psmouse->pktcnt != 3)
+ return PSMOUSE_GOOD_DATA;
+ } else {
+ switch (psmouse->pktcnt) {
+ case 1:
+ return (packet[0] & 0xf8) == 0x00 ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+ case 2:
+ return PSMOUSE_GOOD_DATA;
+ case 3:
+ return ((packet[2] & 0x30) << 2) == (packet[2] & 0xc0) ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+ case 4:
+ return (packet[3] & 0xf8) == 0xc0 ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+ case 5:
+ return (packet[4] & 0xc0) == (packet[2] & 0xc0) ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+ case 6:
+ if (((packet[5] & 0x30) << 2) != (packet[5] & 0xc0))
+ return PSMOUSE_BAD_DATA;
+ if ((packet[5] & 0xc0) != (packet[1] & 0xc0))
+ return PSMOUSE_BAD_DATA;
+ break; /* report data */
+ }
+ }
- /* calculate X and Y */
- if ((packet[0] & 0x08) == 0x00) {
- input_report_abs(dev, ABS_X,
+ if (relative_packet) {
+ if (!dev2)
+ printk(KERN_WARNING "lifebook.c: got relative packet "
+ "but no relative device set up\n");
+ } else if (lifebook_use_6byte_proto) {
+ input_report_abs(dev1, ABS_X,
+ ((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f));
+ input_report_abs(dev1, ABS_Y,
+ 4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f)));
+ } else {
+ input_report_abs(dev1, ABS_X,
(packet[1] | ((packet[0] & 0x30) << 4)));
- input_report_abs(dev, ABS_Y,
+ input_report_abs(dev1, ABS_Y,
1024 - (packet[2] | ((packet[0] & 0xC0) << 2)));
- } else {
- input_report_rel(dev, REL_X,
- ((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
- input_report_rel(dev, REL_Y,
- -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
}
- input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
- input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
- input_report_key(dev, BTN_TOUCH, packet[0] & 0x04);
+ input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04);
+ input_sync(dev1);
- input_sync(dev);
+ if (dev2) {
+ if (relative_packet) {
+ input_report_rel(dev2, REL_X,
+ ((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
+ input_report_rel(dev2, REL_Y,
+ -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
+ }
+ input_report_key(dev2, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02);
+ input_sync(dev2);
+ }
return PSMOUSE_FULL_PACKET;
}
@@ -109,12 +188,20 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
you leave this call out the touchsreen will never send
absolute coordinates
*/
- param = 0x07;
+ param = lifebook_use_6byte_proto ? 0x08 : 0x07;
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
return 0;
}
+static void lifebook_relative_mode(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param = 0x06;
+
+ ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+}
+
static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
static const unsigned char params[] = { 0, 1, 2, 2, 3 };
@@ -131,6 +218,8 @@ static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolu
static void lifebook_disconnect(struct psmouse *psmouse)
{
psmouse_reset(psmouse);
+ kfree(psmouse->private);
+ psmouse->private = NULL;
}
int lifebook_detect(struct psmouse *psmouse, int set_properties)
@@ -138,6 +227,10 @@ int lifebook_detect(struct psmouse *psmouse, int set_properties)
if (!dmi_check_system(lifebook_dmi_table))
return -1;
+ if (desired_serio_phys &&
+ strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
+ return -1;
+
if (set_properties) {
psmouse->vendor = "Fujitsu";
psmouse->name = "Lifebook TouchScreen";
@@ -146,24 +239,78 @@ int lifebook_detect(struct psmouse *psmouse, int set_properties)
return 0;
}
+static int lifebook_create_relative_device(struct psmouse *psmouse)
+{
+ struct input_dev *dev2;
+ struct lifebook_data *priv;
+ int error = -ENOMEM;
+
+ priv = kzalloc(sizeof(struct lifebook_data), GFP_KERNEL);
+ dev2 = input_allocate_device();
+ if (!priv || !dev2)
+ goto err_out;
+
+ priv->dev2 = dev2;
+ snprintf(priv->phys, sizeof(priv->phys),
+ "%s/input1", psmouse->ps2dev.serio->phys);
+
+ dev2->phys = priv->phys;
+ dev2->name = "PS/2 Touchpad";
+ dev2->id.bustype = BUS_I8042;
+ dev2->id.vendor = 0x0002;
+ dev2->id.product = PSMOUSE_LIFEBOOK;
+ dev2->id.version = 0x0000;
+ dev2->dev.parent = &psmouse->ps2dev.serio->dev;
+
+ dev2->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ dev2->relbit[LONG(REL_X)] = BIT(REL_X) | BIT(REL_Y);
+ dev2->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
+
+ error = input_register_device(priv->dev2);
+ if (error)
+ goto err_out;
+
+ psmouse->private = priv;
+ return 0;
+
+ err_out:
+ input_free_device(dev2);
+ kfree(priv);
+ return error;
+}
+
int lifebook_init(struct psmouse *psmouse)
{
- struct input_dev *input_dev = psmouse->dev;
+ struct input_dev *dev1 = psmouse->dev;
+ int max_coord = lifebook_use_6byte_proto ? 1024 : 4096;
if (lifebook_absolute_mode(psmouse))
return -1;
- input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL);
- input_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
- input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
- input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
- input_set_abs_params(input_dev, ABS_X, 0, 1024, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, 1024, 0, 0);
+ dev1->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+ dev1->relbit[0] = 0;
+ dev1->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
+ input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
+
+ if (!desired_serio_phys) {
+ if (lifebook_create_relative_device(psmouse)) {
+ lifebook_relative_mode(psmouse);
+ return -1;
+ }
+ }
psmouse->protocol_handler = lifebook_process_byte;
psmouse->set_resolution = lifebook_set_resolution;
psmouse->disconnect = lifebook_disconnect;
psmouse->reconnect = lifebook_absolute_mode;
+
+ psmouse->model = lifebook_use_6byte_proto ? 6 : 3;
+
+ /*
+ * Use packet size = 3 even when using 6-byte protocol because
+ * that's what POLL will return on Lifebooks (according to spec).
+ */
psmouse->pktsize = 3;
return 0;
diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h
index be1c0943825..c1647cf036c 100644
--- a/drivers/input/mouse/lifebook.h
+++ b/drivers/input/mouse/lifebook.h
@@ -11,7 +11,18 @@
#ifndef _LIFEBOOK_H
#define _LIFEBOOK_H
+#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
int lifebook_detect(struct psmouse *psmouse, int set_properties);
int lifebook_init(struct psmouse *psmouse);
+#else
+inline int lifebook_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+inline int lifebook_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif
#endif
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index d3ddea26b8c..9df74b72e6c 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -200,6 +200,7 @@ static void ps2pp_disconnect(struct psmouse *psmouse)
static const struct ps2pp_info *get_model_info(unsigned char model)
{
static const struct ps2pp_info ps2pp_list[] = {
+ { 1, 0, 0 }, /* Simple 2-button mouse */
{ 12, 0, PS2PP_SIDE_BTN},
{ 13, 0, 0 },
{ 15, PS2PP_KIND_MX, /* MX1000 */
@@ -338,12 +339,12 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
param[1] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
- if (!param[1])
- return -1;
-
model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
buttons = param[1];
+ if (!model || !buttons)
+ return -1;
+
if ((model_info = get_model_info(model)) != NULL) {
/*
diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h
index 64a8ec52ea6..6e5712525fd 100644
--- a/drivers/input/mouse/logips2pp.h
+++ b/drivers/input/mouse/logips2pp.h
@@ -11,6 +11,13 @@
#ifndef _LOGIPS2PP_H
#define _LOGIPS2PP_H
+#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
int ps2pp_init(struct psmouse *psmouse, int set_properties);
+#else
+inline int ps2pp_init(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_LOGIPS2PP */
#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 0fe5869d7d4..f15f695777f 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -28,6 +28,7 @@
#include "alps.h"
#include "lifebook.h"
#include "trackpoint.h"
+#include "touchkit_ps2.h"
#define DRIVER_DESC "PS/2 mouse driver"
@@ -569,7 +570,9 @@ static int psmouse_extensions(struct psmouse *psmouse,
return PSMOUSE_THINKPS;
/*
- * Try Synaptics TouchPad
+ * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol
+ * support is disabled in config - we need to know if it is synaptics so we
+ * can reset it properly after probing for intellimouse.
*/
if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) {
synaptics_hardware = 1;
@@ -605,14 +608,20 @@ static int psmouse_extensions(struct psmouse *psmouse,
}
}
- if (max_proto > PSMOUSE_IMEX && genius_detect(psmouse, set_properties) == 0)
- return PSMOUSE_GENPS;
+ if (max_proto > PSMOUSE_IMEX) {
+
+ if (genius_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_GENPS;
- if (max_proto > PSMOUSE_IMEX && ps2pp_init(psmouse, set_properties) == 0)
- return PSMOUSE_PS2PP;
+ if (ps2pp_init(psmouse, set_properties) == 0)
+ return PSMOUSE_PS2PP;
- if (max_proto > PSMOUSE_IMEX && trackpoint_detect(psmouse, set_properties) == 0)
- return PSMOUSE_TRACKPOINT;
+ if (trackpoint_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_TRACKPOINT;
+
+ if (touchkit_ps2_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_TOUCHKIT_PS2;
+ }
/*
* Reset to defaults in case the device got confused by extended
@@ -654,12 +663,14 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.maxproto = 1,
.detect = ps2bare_detect,
},
+#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
{
.type = PSMOUSE_PS2PP,
.name = "PS2++",
.alias = "logitech",
.detect = ps2pp_init,
},
+#endif
{
.type = PSMOUSE_THINKPS,
.name = "ThinkPS/2",
@@ -686,6 +697,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.maxproto = 1,
.detect = im_explorer_detect,
},
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
{
.type = PSMOUSE_SYNAPTICS,
.name = "SynPS/2",
@@ -693,6 +705,8 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.detect = synaptics_detect,
.init = synaptics_init,
},
+#endif
+#ifdef CONFIG_MOUSE_PS2_ALPS
{
.type = PSMOUSE_ALPS,
.name = "AlpsPS/2",
@@ -700,18 +714,31 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.detect = alps_detect,
.init = alps_init,
},
+#endif
+#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
{
.type = PSMOUSE_LIFEBOOK,
.name = "LBPS/2",
.alias = "lifebook",
.init = lifebook_init,
},
+#endif
+#ifdef CONFIG_MOUSE_PS2_TRACKPOINT
{
.type = PSMOUSE_TRACKPOINT,
.name = "TPPS/2",
.alias = "trackpoint",
.detect = trackpoint_detect,
},
+#endif
+#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
+ {
+ .type = PSMOUSE_TOUCHKIT_PS2,
+ .name = "touchkitPS/2",
+ .alias = "touchkit",
+ .detect = touchkit_ps2_detect,
+ },
+#endif
{
.type = PSMOUSE_AUTO,
.name = "auto",
@@ -823,12 +850,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
static void psmouse_initialize(struct psmouse *psmouse)
{
/*
- * We set the mouse into streaming mode.
- */
-
- ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM);
-
-/*
* We set the mouse report rate, resolution and scaling.
*/
@@ -1062,8 +1083,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse
{
struct input_dev *input_dev = psmouse->dev;
- input_dev->private = psmouse;
- input_dev->cdev.dev = &psmouse->ps2dev.serio->dev;
+ input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index cf1de95b6f2..3964e8acbc5 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -87,6 +87,7 @@ enum psmouse_type {
PSMOUSE_ALPS,
PSMOUSE_LIFEBOOK,
PSMOUSE_TRACKPOINT,
+ PSMOUSE_TOUCHKIT_PS2,
PSMOUSE_AUTO /* This one should always be last */
};
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
index a85d74710b4..77b8ee2b965 100644
--- a/drivers/input/mouse/sermouse.c
+++ b/drivers/input/mouse/sermouse.c
@@ -69,7 +69,8 @@ static void sermouse_process_msc(struct sermouse *sermouse, signed char data)
switch (sermouse->count) {
case 0:
- if ((data & 0xf8) != 0x80) return;
+ if ((data & 0xf8) != 0x80)
+ return;
input_report_key(dev, BTN_LEFT, !(data & 4));
input_report_key(dev, BTN_RIGHT, !(data & 1));
input_report_key(dev, BTN_MIDDLE, !(data & 2));
@@ -107,7 +108,10 @@ static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
struct input_dev *dev = sermouse->dev;
signed char *buf = sermouse->buf;
- if (data & 0x40) sermouse->count = 0;
+ if (data & 0x40)
+ sermouse->count = 0;
+ else if (sermouse->count == 0)
+ return;
switch (sermouse->count) {
@@ -169,7 +173,8 @@ static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
case 5:
case 7: /* Ignore anything besides MZ++ */
- if (sermouse->type != SERIO_MZPP) break;
+ if (sermouse->type != SERIO_MZPP)
+ break;
switch (buf[1]) {
@@ -206,13 +211,16 @@ static irqreturn_t sermouse_interrupt(struct serio *serio,
{
struct sermouse *sermouse = serio_get_drvdata(serio);
- if (time_after(jiffies, sermouse->last + HZ/10)) sermouse->count = 0;
+ if (time_after(jiffies, sermouse->last + HZ/10))
+ sermouse->count = 0;
+
sermouse->last = jiffies;
if (sermouse->type > SERIO_SUN)
sermouse_process_ms(sermouse, data);
else
sermouse_process_msc(sermouse, data);
+
return IRQ_HANDLED;
}
@@ -258,12 +266,11 @@ static int sermouse_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = sermouse->type;
input_dev->id.product = c;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
- input_dev->private = sermouse;
if (c & 0x01) set_bit(BTN_MIDDLE, input_dev->keybit);
if (c & 0x02) set_bit(BTN_SIDE, input_dev->keybit);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index f0f9413d762..666ad3a53fd 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -40,33 +40,70 @@
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448
+
/*****************************************************************************
- * Synaptics communications functions
+ * Stuff we need even when we do not want native Synaptics support
****************************************************************************/
/*
- * Send a command to the synpatics touchpad by special commands
+ * Set the synaptics touchpad mode byte by special commands
*/
-static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
+static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
{
- if (psmouse_sliced_command(psmouse, c))
+ unsigned char param[1];
+
+ if (psmouse_sliced_command(psmouse, mode))
return -1;
- if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+ param[0] = SYN_PS_SET_MODE2;
+ if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
return -1;
return 0;
}
+int synaptics_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[4];
+
+ param[0] = 0;
+
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+ if (param[1] != 0x47)
+ return -ENODEV;
+
+ if (set_properties) {
+ psmouse->vendor = "Synaptics";
+ psmouse->name = "TouchPad";
+ }
+
+ return 0;
+}
+
+void synaptics_reset(struct psmouse *psmouse)
+{
+ /* reset touchpad back to relative mode, gestures enabled */
+ synaptics_mode_cmd(psmouse, 0);
+}
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+
+/*****************************************************************************
+ * Synaptics communications functions
+ ****************************************************************************/
+
/*
- * Set the synaptics touchpad mode byte by special commands
+ * Send a command to the synpatics touchpad by special commands
*/
-static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
+static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
{
- unsigned char param[1];
-
- if (psmouse_sliced_command(psmouse, mode))
+ if (psmouse_sliced_command(psmouse, c))
return -1;
- param[0] = SYN_PS_SET_MODE2;
- if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
+ if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
return 0;
}
@@ -148,7 +185,7 @@ static int synaptics_query_hardware(struct psmouse *psmouse)
int retries = 0;
while ((retries++ < 3) && psmouse_reset(psmouse))
- printk(KERN_ERR "synaptics reset failed\n");
+ /* empty */;
if (synaptics_identify(psmouse))
return -1;
@@ -529,12 +566,6 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
clear_bit(REL_Y, dev->relbit);
}
-void synaptics_reset(struct psmouse *psmouse)
-{
- /* reset touchpad back to relative mode, gestures enabled */
- synaptics_mode_cmd(psmouse, 0);
-}
-
static void synaptics_disconnect(struct psmouse *psmouse)
{
synaptics_reset(psmouse);
@@ -569,30 +600,6 @@ static int synaptics_reconnect(struct psmouse *psmouse)
return 0;
}
-int synaptics_detect(struct psmouse *psmouse, int set_properties)
-{
- struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char param[4];
-
- param[0] = 0;
-
- ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
- ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
- ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
- ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
- ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
-
- if (param[1] != 0x47)
- return -1;
-
- if (set_properties) {
- psmouse->vendor = "Synaptics";
- psmouse->name = "TouchPad";
- }
-
- return 0;
-}
-
#if defined(__i386__)
#include <linux/dmi.h>
static struct dmi_system_id toshiba_dmi_table[] = {
@@ -648,6 +655,16 @@ int synaptics_init(struct psmouse *psmouse)
set_input_params(psmouse->dev, priv);
+ /*
+ * Encode touchpad model so that it can be used to set
+ * input device->id.version and be visible to userspace.
+ * Because version is __u16 we have to drop something.
+ * Hardware info bits seem to be good candidates as they
+ * are documented to be for Synaptics corp. internal use.
+ */
+ psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
+ (priv->model_id & 0x000000ff);
+
psmouse->protocol_handler = synaptics_process_byte;
psmouse->set_rate = synaptics_set_rate;
psmouse->disconnect = synaptics_disconnect;
@@ -680,4 +697,12 @@ int synaptics_init(struct psmouse *psmouse)
return -1;
}
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS */
+
+int synaptics_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 68fff1dcd7d..02aa4cf7bc7 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -9,10 +9,6 @@
#ifndef _SYNAPTICS_H
#define _SYNAPTICS_H
-extern int synaptics_detect(struct psmouse *psmouse, int set_properties);
-extern int synaptics_init(struct psmouse *psmouse);
-extern void synaptics_reset(struct psmouse *psmouse);
-
/* synaptics queries */
#define SYN_QUE_IDENTIFY 0x00
#define SYN_QUE_MODES 0x01
@@ -62,9 +58,9 @@ extern void synaptics_reset(struct psmouse *psmouse);
#define SYN_MODE_WMODE(m) ((m) & (1 << 0))
/* synaptics identify query bits */
-#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f)
-#define SYN_ID_MAJOR(i) ((i) & 0x0f)
-#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff)
+#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f)
+#define SYN_ID_MAJOR(i) ((i) & 0x0f)
+#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff)
#define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47)
/* synaptics special commands */
@@ -98,8 +94,8 @@ struct synaptics_hw_state {
struct synaptics_data {
/* Data read from the touchpad */
unsigned long int model_id; /* Model-ID */
- unsigned long int capabilities; /* Capabilities */
- unsigned long int ext_cap; /* Extended Capabilities */
+ unsigned long int capabilities; /* Capabilities */
+ unsigned long int ext_cap; /* Extended Capabilities */
unsigned long int identity; /* Identification */
unsigned char pkt_type; /* packet type - old, new, etc */
@@ -107,4 +103,8 @@ struct synaptics_data {
int scroll;
};
+int synaptics_detect(struct psmouse *psmouse, int set_properties);
+int synaptics_init(struct psmouse *psmouse);
+void synaptics_reset(struct psmouse *psmouse);
+
#endif /* _SYNAPTICS_H */
diff --git a/drivers/input/mouse/touchkit_ps2.c b/drivers/input/mouse/touchkit_ps2.c
new file mode 100644
index 00000000000..7b977fd2357
--- /dev/null
+++ b/drivers/input/mouse/touchkit_ps2.c
@@ -0,0 +1,100 @@
+/* ----------------------------------------------------------------------------
+ * touchkit_ps2.c -- Driver for eGalax TouchKit PS/2 Touchscreens
+ *
+ * Copyright (C) 2005 by Stefan Lucke
+ * Copyright (C) 2004 by Daniel Ritz
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon touchkitusb.c
+ *
+ * Vendor documentation is available in support section of:
+ * http://www.egalax.com.tw/
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+
+#include "psmouse.h"
+#include "touchkit_ps2.h"
+
+#define TOUCHKIT_MAX_XC 0x07ff
+#define TOUCHKIT_MAX_YC 0x07ff
+
+#define TOUCHKIT_CMD 0x0a
+#define TOUCHKIT_CMD_LENGTH 1
+
+#define TOUCHKIT_CMD_ACTIVE 'A'
+#define TOUCHKIT_CMD_FIRMWARE_VERSION 'D'
+#define TOUCHKIT_CMD_CONTROLLER_TYPE 'E'
+
+#define TOUCHKIT_SEND_PARMS(s, r, c) ((s) << 12 | (r) << 8 | (c))
+
+#define TOUCHKIT_GET_TOUCHED(packet) (((packet)[0]) & 0x01)
+#define TOUCHKIT_GET_X(packet) (((packet)[1] << 7) | (packet)[2])
+#define TOUCHKIT_GET_Y(packet) (((packet)[3] << 7) | (packet)[4])
+
+static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse)
+{
+ unsigned char *packet = psmouse->packet;
+ struct input_dev *dev = psmouse->dev;
+
+ if (psmouse->pktcnt != 5)
+ return PSMOUSE_GOOD_DATA;
+
+ input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet));
+ input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet));
+ input_report_key(dev, BTN_TOUCH, TOUCHKIT_GET_TOUCHED(packet));
+ input_sync(dev);
+
+ return PSMOUSE_FULL_PACKET;
+}
+
+int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char param[3];
+ int command;
+
+ param[0] = TOUCHKIT_CMD_LENGTH;
+ param[1] = TOUCHKIT_CMD_ACTIVE;
+ command = TOUCHKIT_SEND_PARMS(2, 3, TOUCHKIT_CMD);
+
+ if (ps2_command(&psmouse->ps2dev, param, command))
+ return -ENODEV;
+
+ if (param[0] != TOUCHKIT_CMD || param[1] != 0x01 ||
+ param[2] != TOUCHKIT_CMD_ACTIVE)
+ return -ENODEV;
+
+ if (set_properties) {
+ dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ set_bit(BTN_TOUCH, dev->keybit);
+ input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0);
+
+ psmouse->vendor = "eGalax";
+ psmouse->name = "Touchscreen";
+ psmouse->protocol_handler = touchkit_ps2_process_byte;
+ psmouse->pktsize = 5;
+ }
+
+ return 0;
+}
diff --git a/drivers/input/mouse/touchkit_ps2.h b/drivers/input/mouse/touchkit_ps2.h
new file mode 100644
index 00000000000..61e9dfd8419
--- /dev/null
+++ b/drivers/input/mouse/touchkit_ps2.h
@@ -0,0 +1,24 @@
+/* ----------------------------------------------------------------------------
+ * touchkit_ps2.h -- Driver for eGalax TouchKit PS/2 Touchscreens
+ *
+ * Copyright (C) 2005 by Stefan Lucke
+ * Copyright (c) 2005 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _TOUCHKIT_PS2_H
+#define _TOUCHKIT_PS2_H
+
+#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
+int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties);
+#else
+inline int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_TOUCHKIT */
+
+#endif
diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h
index 050298b1a09..c10a6e7d010 100644
--- a/drivers/input/mouse/trackpoint.h
+++ b/drivers/input/mouse/trackpoint.h
@@ -142,6 +142,13 @@ struct trackpoint_data
unsigned char ext_dev;
};
-extern int trackpoint_detect(struct psmouse *psmouse, int set_properties);
+#ifdef CONFIG_MOUSE_PS2_TRACKPOINT
+int trackpoint_detect(struct psmouse *psmouse, int set_properties);
+#else
+inline int trackpoint_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_TRACKPOINT */
#endif /* _TRACKPOINT_H */
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
index c3d64fcc858..4a321576f34 100644
--- a/drivers/input/mouse/vsxxxaa.c
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -508,8 +508,7 @@ vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
input_dev->id.bustype = BUS_RS232;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = mouse;
+ input_dev->dev.parent = &serio->dev;
set_bit (EV_KEY, input_dev->evbit); /* We have buttons */
set_bit (EV_REL, input_dev->evbit);
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index 664bcc8116f..8675f950939 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -19,7 +19,6 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/input.h>
-#include <linux/smp_lock.h>
#include <linux/random.h>
#include <linux/major.h>
#include <linux/device.h>
@@ -63,9 +62,12 @@ struct mousedev {
int minor;
char name[16];
wait_queue_head_t wait;
- struct list_head list;
+ struct list_head client_list;
struct input_handle handle;
+ struct list_head mixdev_node;
+ int mixdev_open;
+
struct mousedev_hw_data packet;
unsigned int pkt_count;
int old_x[4], old_y[4];
@@ -85,7 +87,7 @@ struct mousedev_motion {
};
#define PACKET_QUEUE_LEN 16
-struct mousedev_list {
+struct mousedev_client {
struct fasync_struct *fasync;
struct mousedev *mousedev;
struct list_head node;
@@ -111,6 +113,7 @@ static struct input_handler mousedev_handler;
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
static struct mousedev mousedev_mix;
+static LIST_HEAD(mousedev_mix_list);
#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
@@ -120,32 +123,33 @@ static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mous
int size, tmp;
enum { FRACTION_DENOM = 128 };
- if (mousedev->touch) {
- size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
- if (size == 0)
- size = 256 * 2;
-
- switch (code) {
- case ABS_X:
- fx(0) = value;
- if (mousedev->pkt_count >= 2) {
- tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size;
- tmp += mousedev->frac_dx;
- mousedev->packet.dx = tmp / FRACTION_DENOM;
- mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM;
- }
- break;
+ switch (code) {
+ case ABS_X:
+ fx(0) = value;
+ if (mousedev->touch && mousedev->pkt_count >= 2) {
+ size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+ if (size == 0)
+ size = 256 * 2;
+ tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size;
+ tmp += mousedev->frac_dx;
+ mousedev->packet.dx = tmp / FRACTION_DENOM;
+ mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM;
+ }
+ break;
- case ABS_Y:
- fy(0) = value;
- if (mousedev->pkt_count >= 2) {
- tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size;
- tmp += mousedev->frac_dy;
- mousedev->packet.dy = tmp / FRACTION_DENOM;
- mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM;
- }
- break;
- }
+ case ABS_Y:
+ fy(0) = value;
+ if (mousedev->touch && mousedev->pkt_count >= 2) {
+ /* use X size to keep the same scale */
+ size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+ if (size == 0)
+ size = 256 * 2;
+ tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size;
+ tmp += mousedev->frac_dy;
+ mousedev->packet.dy = tmp / FRACTION_DENOM;
+ mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM;
+ }
+ break;
}
}
@@ -223,47 +227,47 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int
static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet)
{
- struct mousedev_list *list;
+ struct mousedev_client *client;
struct mousedev_motion *p;
unsigned long flags;
int wake_readers = 0;
- list_for_each_entry(list, &mousedev->list, node) {
- spin_lock_irqsave(&list->packet_lock, flags);
+ list_for_each_entry(client, &mousedev->client_list, node) {
+ spin_lock_irqsave(&client->packet_lock, flags);
- p = &list->packets[list->head];
- if (list->ready && p->buttons != mousedev->packet.buttons) {
- unsigned int new_head = (list->head + 1) % PACKET_QUEUE_LEN;
- if (new_head != list->tail) {
- p = &list->packets[list->head = new_head];
+ p = &client->packets[client->head];
+ if (client->ready && p->buttons != mousedev->packet.buttons) {
+ unsigned int new_head = (client->head + 1) % PACKET_QUEUE_LEN;
+ if (new_head != client->tail) {
+ p = &client->packets[client->head = new_head];
memset(p, 0, sizeof(struct mousedev_motion));
}
}
if (packet->abs_event) {
- p->dx += packet->x - list->pos_x;
- p->dy += packet->y - list->pos_y;
- list->pos_x = packet->x;
- list->pos_y = packet->y;
+ p->dx += packet->x - client->pos_x;
+ p->dy += packet->y - client->pos_y;
+ client->pos_x = packet->x;
+ client->pos_y = packet->y;
}
- list->pos_x += packet->dx;
- list->pos_x = list->pos_x < 0 ? 0 : (list->pos_x >= xres ? xres : list->pos_x);
- list->pos_y += packet->dy;
- list->pos_y = list->pos_y < 0 ? 0 : (list->pos_y >= yres ? yres : list->pos_y);
+ client->pos_x += packet->dx;
+ client->pos_x = client->pos_x < 0 ? 0 : (client->pos_x >= xres ? xres : client->pos_x);
+ client->pos_y += packet->dy;
+ client->pos_y = client->pos_y < 0 ? 0 : (client->pos_y >= yres ? yres : client->pos_y);
p->dx += packet->dx;
p->dy += packet->dy;
p->dz += packet->dz;
p->buttons = mousedev->packet.buttons;
- if (p->dx || p->dy || p->dz || p->buttons != list->last_buttons)
- list->ready = 1;
+ if (p->dx || p->dy || p->dz || p->buttons != client->last_buttons)
+ client->ready = 1;
- spin_unlock_irqrestore(&list->packet_lock, flags);
+ spin_unlock_irqrestore(&client->packet_lock, flags);
- if (list->ready) {
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ if (client->ready) {
+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
wake_readers = 1;
}
}
@@ -351,9 +355,9 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
static int mousedev_fasync(int fd, struct file *file, int on)
{
int retval;
- struct mousedev_list *list = file->private_data;
+ struct mousedev_client *client = file->private_data;
- retval = fasync_helper(fd, file, on, &list->fasync);
+ retval = fasync_helper(fd, file, on, &client->fasync);
return retval < 0 ? retval : 0;
}
@@ -364,50 +368,95 @@ static void mousedev_free(struct mousedev *mousedev)
kfree(mousedev);
}
-static void mixdev_release(void)
+static int mixdev_add_device(struct mousedev *mousedev)
{
- struct input_handle *handle;
+ int error;
- list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
- struct mousedev *mousedev = handle->private;
+ if (mousedev_mix.open) {
+ error = input_open_device(&mousedev->handle);
+ if (error)
+ return error;
- if (!mousedev->open) {
- if (mousedev->exist)
- input_close_device(&mousedev->handle);
- else
- mousedev_free(mousedev);
+ mousedev->open++;
+ mousedev->mixdev_open++;
+ }
+
+ list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
+
+ return 0;
+}
+
+static void mixdev_remove_device(struct mousedev *mousedev)
+{
+ if (mousedev->mixdev_open) {
+ mousedev->mixdev_open = 0;
+ if (!--mousedev->open && mousedev->exist)
+ input_close_device(&mousedev->handle);
+ }
+
+ list_del_init(&mousedev->mixdev_node);
+}
+
+static void mixdev_open_devices(void)
+{
+ struct mousedev *mousedev;
+
+ list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
+ if (mousedev->exist && !mousedev->open) {
+ if (input_open_device(&mousedev->handle))
+ continue;
+
+ mousedev->open++;
+ mousedev->mixdev_open++;
+ }
+ }
+}
+
+static void mixdev_close_devices(void)
+{
+ struct mousedev *mousedev, *next;
+
+ list_for_each_entry_safe(mousedev, next, &mousedev_mix_list, mixdev_node) {
+ if (mousedev->mixdev_open) {
+ mousedev->mixdev_open = 0;
+ if (!--mousedev->open) {
+ if (mousedev->exist)
+ input_close_device(&mousedev->handle);
+ else
+ mousedev_free(mousedev);
+ }
}
}
}
-static int mousedev_release(struct inode * inode, struct file * file)
+static int mousedev_release(struct inode *inode, struct file *file)
{
- struct mousedev_list *list = file->private_data;
+ struct mousedev_client *client = file->private_data;
+ struct mousedev *mousedev = client->mousedev;
mousedev_fasync(-1, file, 0);
- list_del(&list->node);
+ list_del(&client->node);
+ kfree(client);
- if (!--list->mousedev->open) {
- if (list->mousedev->minor == MOUSEDEV_MIX)
- mixdev_release();
- else if (!mousedev_mix.open) {
- if (list->mousedev->exist)
- input_close_device(&list->mousedev->handle);
- else
- mousedev_free(list->mousedev);
- }
+ if (!--mousedev->open) {
+ if (mousedev->minor == MOUSEDEV_MIX)
+ mixdev_close_devices();
+ else if (mousedev->exist)
+ input_close_device(&mousedev->handle);
+ else
+ mousedev_free(mousedev);
}
- kfree(list);
return 0;
}
-static int mousedev_open(struct inode * inode, struct file * file)
+
+static int mousedev_open(struct inode *inode, struct file *file)
{
- struct mousedev_list *list;
- struct input_handle *handle;
+ struct mousedev_client *client;
struct mousedev *mousedev;
+ int error;
int i;
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
@@ -417,31 +466,37 @@ static int mousedev_open(struct inode * inode, struct file * file)
#endif
i = iminor(inode) - MOUSEDEV_MINOR_BASE;
- if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
+ if (i >= MOUSEDEV_MINORS)
+ return -ENODEV;
+
+ mousedev = mousedev_table[i];
+ if (!mousedev)
return -ENODEV;
- if (!(list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
+ client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
+ if (!client)
return -ENOMEM;
- spin_lock_init(&list->packet_lock);
- list->pos_x = xres / 2;
- list->pos_y = yres / 2;
- list->mousedev = mousedev_table[i];
- list_add_tail(&list->node, &mousedev_table[i]->list);
- file->private_data = list;
-
- if (!list->mousedev->open++) {
- if (list->mousedev->minor == MOUSEDEV_MIX) {
- list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
- mousedev = handle->private;
- if (!mousedev->open && mousedev->exist)
- input_open_device(handle);
+ spin_lock_init(&client->packet_lock);
+ client->pos_x = xres / 2;
+ client->pos_y = yres / 2;
+ client->mousedev = mousedev;
+ list_add_tail(&client->node, &mousedev->client_list);
+
+ if (!mousedev->open++) {
+ if (mousedev->minor == MOUSEDEV_MIX)
+ mixdev_open_devices();
+ else if (mousedev->exist) {
+ error = input_open_device(&mousedev->handle);
+ if (error) {
+ list_del(&client->node);
+ kfree(client);
+ return error;
}
- } else
- if (!mousedev_mix.open && list->mousedev->exist)
- input_open_device(&list->mousedev->handle);
+ }
}
+ file->private_data = client;
return 0;
}
@@ -450,13 +505,13 @@ static inline int mousedev_limit_delta(int delta, int limit)
return delta > limit ? limit : (delta < -limit ? -limit : delta);
}
-static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data)
+static void mousedev_packet(struct mousedev_client *client, signed char *ps2_data)
{
struct mousedev_motion *p;
unsigned long flags;
- spin_lock_irqsave(&list->packet_lock, flags);
- p = &list->packets[list->tail];
+ spin_lock_irqsave(&client->packet_lock, flags);
+ p = &client->packets[client->tail];
ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
ps2_data[1] = mousedev_limit_delta(p->dx, 127);
@@ -464,44 +519,44 @@ static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data)
p->dx -= ps2_data[1];
p->dy -= ps2_data[2];
- switch (list->mode) {
+ switch (client->mode) {
case MOUSEDEV_EMUL_EXPS:
ps2_data[3] = mousedev_limit_delta(p->dz, 7);
p->dz -= ps2_data[3];
ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
- list->bufsiz = 4;
+ client->bufsiz = 4;
break;
case MOUSEDEV_EMUL_IMPS:
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
ps2_data[3] = mousedev_limit_delta(p->dz, 127);
p->dz -= ps2_data[3];
- list->bufsiz = 4;
+ client->bufsiz = 4;
break;
case MOUSEDEV_EMUL_PS2:
default:
ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
p->dz = 0;
- list->bufsiz = 3;
+ client->bufsiz = 3;
break;
}
if (!p->dx && !p->dy && !p->dz) {
- if (list->tail == list->head) {
- list->ready = 0;
- list->last_buttons = p->buttons;
+ if (client->tail == client->head) {
+ client->ready = 0;
+ client->last_buttons = p->buttons;
} else
- list->tail = (list->tail + 1) % PACKET_QUEUE_LEN;
+ client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
}
- spin_unlock_irqrestore(&list->packet_lock, flags);
+ spin_unlock_irqrestore(&client->packet_lock, flags);
}
-static ssize_t mousedev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+static ssize_t mousedev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
- struct mousedev_list *list = file->private_data;
+ struct mousedev_client *client = file->private_data;
unsigned char c;
unsigned int i;
@@ -510,95 +565,95 @@ static ssize_t mousedev_write(struct file * file, const char __user * buffer, si
if (get_user(c, buffer + i))
return -EFAULT;
- if (c == mousedev_imex_seq[list->imexseq]) {
- if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
- list->imexseq = 0;
- list->mode = MOUSEDEV_EMUL_EXPS;
+ if (c == mousedev_imex_seq[client->imexseq]) {
+ if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
+ client->imexseq = 0;
+ client->mode = MOUSEDEV_EMUL_EXPS;
}
} else
- list->imexseq = 0;
+ client->imexseq = 0;
- if (c == mousedev_imps_seq[list->impsseq]) {
- if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
- list->impsseq = 0;
- list->mode = MOUSEDEV_EMUL_IMPS;
+ if (c == mousedev_imps_seq[client->impsseq]) {
+ if (++client->impsseq == MOUSEDEV_SEQ_LEN) {
+ client->impsseq = 0;
+ client->mode = MOUSEDEV_EMUL_IMPS;
}
} else
- list->impsseq = 0;
+ client->impsseq = 0;
- list->ps2[0] = 0xfa;
+ client->ps2[0] = 0xfa;
switch (c) {
case 0xeb: /* Poll */
- mousedev_packet(list, &list->ps2[1]);
- list->bufsiz++; /* account for leading ACK */
+ mousedev_packet(client, &client->ps2[1]);
+ client->bufsiz++; /* account for leading ACK */
break;
case 0xf2: /* Get ID */
- switch (list->mode) {
- case MOUSEDEV_EMUL_PS2: list->ps2[1] = 0; break;
- case MOUSEDEV_EMUL_IMPS: list->ps2[1] = 3; break;
- case MOUSEDEV_EMUL_EXPS: list->ps2[1] = 4; break;
+ switch (client->mode) {
+ case MOUSEDEV_EMUL_PS2: client->ps2[1] = 0; break;
+ case MOUSEDEV_EMUL_IMPS: client->ps2[1] = 3; break;
+ case MOUSEDEV_EMUL_EXPS: client->ps2[1] = 4; break;
}
- list->bufsiz = 2;
+ client->bufsiz = 2;
break;
case 0xe9: /* Get info */
- list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
- list->bufsiz = 4;
+ client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200;
+ client->bufsiz = 4;
break;
case 0xff: /* Reset */
- list->impsseq = list->imexseq = 0;
- list->mode = MOUSEDEV_EMUL_PS2;
- list->ps2[1] = 0xaa; list->ps2[2] = 0x00;
- list->bufsiz = 3;
+ client->impsseq = client->imexseq = 0;
+ client->mode = MOUSEDEV_EMUL_PS2;
+ client->ps2[1] = 0xaa; client->ps2[2] = 0x00;
+ client->bufsiz = 3;
break;
default:
- list->bufsiz = 1;
+ client->bufsiz = 1;
break;
}
- list->buffer = list->bufsiz;
+ client->buffer = client->bufsiz;
}
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
- wake_up_interruptible(&list->mousedev->wait);
+ wake_up_interruptible(&client->mousedev->wait);
return count;
}
-static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
+static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
- struct mousedev_list *list = file->private_data;
+ struct mousedev_client *client = file->private_data;
int retval = 0;
- if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK))
+ if (!client->ready && !client->buffer && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
- retval = wait_event_interruptible(list->mousedev->wait,
- !list->mousedev->exist || list->ready || list->buffer);
+ retval = wait_event_interruptible(client->mousedev->wait,
+ !client->mousedev->exist || client->ready || client->buffer);
if (retval)
return retval;
- if (!list->mousedev->exist)
+ if (!client->mousedev->exist)
return -ENODEV;
- if (!list->buffer && list->ready) {
- mousedev_packet(list, list->ps2);
- list->buffer = list->bufsiz;
+ if (!client->buffer && client->ready) {
+ mousedev_packet(client, client->ps2);
+ client->buffer = client->bufsiz;
}
- if (count > list->buffer)
- count = list->buffer;
+ if (count > client->buffer)
+ count = client->buffer;
- list->buffer -= count;
+ client->buffer -= count;
- if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer - count, count))
+ if (copy_to_user(buffer, client->ps2 + client->bufsiz - client->buffer - count, count))
return -EFAULT;
return count;
@@ -607,11 +662,12 @@ static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t co
/* No kernel lock - fine */
static unsigned int mousedev_poll(struct file *file, poll_table *wait)
{
- struct mousedev_list *list = file->private_data;
+ struct mousedev_client *client = file->private_data;
+ struct mousedev *mousedev = client->mousedev;
- poll_wait(file, &list->mousedev->wait, wait);
- return ((list->ready || list->buffer) ? (POLLIN | POLLRDNORM) : 0) |
- (list->mousedev->exist ? 0 : (POLLHUP | POLLERR));
+ poll_wait(file, &mousedev->wait, wait);
+ return ((client->ready || client->buffer) ? (POLLIN | POLLRDNORM) : 0) |
+ (mousedev->exist ? 0 : (POLLHUP | POLLERR));
}
static const struct file_operations mousedev_fops = {
@@ -624,23 +680,27 @@ static const struct file_operations mousedev_fops = {
.fasync = mousedev_fasync,
};
-static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
+static int mousedev_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct mousedev *mousedev;
struct class_device *cdev;
- int minor = 0;
+ dev_t devt;
+ int minor;
+ int error;
for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
if (minor == MOUSEDEV_MINORS) {
printk(KERN_ERR "mousedev: no more free mousedev devices\n");
- return NULL;
+ return -ENFILE;
}
- if (!(mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL)))
- return NULL;
+ mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);
+ if (!mousedev)
+ return -ENOMEM;
- INIT_LIST_HEAD(&mousedev->list);
+ INIT_LIST_HEAD(&mousedev->client_list);
+ INIT_LIST_HEAD(&mousedev->mixdev_node);
init_waitqueue_head(&mousedev->wait);
mousedev->minor = minor;
@@ -651,42 +711,66 @@ static struct input_handle *mousedev_connect(struct input_handler *handler, stru
mousedev->handle.private = mousedev;
sprintf(mousedev->name, "mouse%d", minor);
- if (mousedev_mix.open)
- input_open_device(&mousedev->handle);
-
mousedev_table[minor] = mousedev;
- cdev = class_device_create(&input_class, &dev->cdev,
- MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
- dev->cdev.dev, mousedev->name);
+ devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
+
+ cdev = class_device_create(&input_class, &dev->cdev, devt,
+ dev->cdev.dev, mousedev->name);
+ if (IS_ERR(cdev)) {
+ error = PTR_ERR(cdev);
+ goto err_free_mousedev;
+ }
/* temporary symlink to keep userspace happy */
- sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
- mousedev->name);
+ error = sysfs_create_link(&input_class.subsys.kobj,
+ &cdev->kobj, mousedev->name);
+ if (error)
+ goto err_cdev_destroy;
+
+ error = input_register_handle(&mousedev->handle);
+ if (error)
+ goto err_remove_link;
+
+ error = mixdev_add_device(mousedev);
+ if (error)
+ goto err_unregister_handle;
+
+ return 0;
- return &mousedev->handle;
+ err_unregister_handle:
+ input_unregister_handle(&mousedev->handle);
+ err_remove_link:
+ sysfs_remove_link(&input_class.subsys.kobj, mousedev->name);
+ err_cdev_destroy:
+ class_device_destroy(&input_class, devt);
+ err_free_mousedev:
+ mousedev_table[minor] = NULL;
+ kfree(mousedev);
+ return error;
}
static void mousedev_disconnect(struct input_handle *handle)
{
struct mousedev *mousedev = handle->private;
- struct mousedev_list *list;
+ struct mousedev_client *client;
- sysfs_remove_link(&input_class.subsys.kset.kobj, mousedev->name);
+ input_unregister_handle(handle);
+
+ sysfs_remove_link(&input_class.subsys.kobj, mousedev->name);
class_device_destroy(&input_class,
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + mousedev->minor));
mousedev->exist = 0;
+ mixdev_remove_device(mousedev);
+
if (mousedev->open) {
input_close_device(handle);
wake_up_interruptible(&mousedev->wait);
- list_for_each_entry(list, &mousedev->list, node)
- kill_fasync(&list->fasync, SIGIO, POLL_HUP);
- } else {
- if (mousedev_mix.open)
- input_close_device(handle);
+ list_for_each_entry(client, &mousedev->client_list, node)
+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+ } else
mousedev_free(mousedev);
- }
}
static const struct input_device_id mousedev_ids[] = {
@@ -714,7 +798,7 @@ static const struct input_device_id mousedev_ids[] = {
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) },
}, /* A touchpad */
- { }, /* Terminating entry */
+ { }, /* Terminating entry */
};
MODULE_DEVICE_TABLE(input, mousedev_ids);
@@ -746,7 +830,7 @@ static int __init mousedev_init(void)
return error;
memset(&mousedev_mix, 0, sizeof(struct mousedev));
- INIT_LIST_HEAD(&mousedev_mix.list);
+ INIT_LIST_HEAD(&mousedev_mix.client_list);
init_waitqueue_head(&mousedev_mix.wait);
mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
mousedev_mix.exist = 1;
diff --git a/drivers/input/power.c b/drivers/input/power.c
deleted file mode 100644
index ee82464a2fa..00000000000
--- a/drivers/input/power.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * $Id: power.c,v 1.10 2001/09/25 09:17:15 vojtech Exp $
- *
- * Copyright (c) 2001 "Crazy" James Simmons
- *
- * Input driver Power Management.
- *
- * Sponsored by Transvirtual Technology.
- */
-
-/*
- * 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 <jsimmons@transvirtual.com>.
- */
-
-#include <linux/module.h>
-#include <linux/input.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/tty.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-
-static struct input_handler power_handler;
-
-/*
- * Power management can't be done in a interrupt context. So we have to
- * use keventd.
- */
-static int suspend_button_pushed = 0;
-static void suspend_button_task_handler(void *data)
-{
- udelay(200); /* debounce */
- suspend_button_pushed = 0;
-}
-
-static DECLARE_WORK(suspend_button_task, suspend_button_task_handler, NULL);
-
-static void power_event(struct input_handle *handle, unsigned int type,
- unsigned int code, int down)
-{
- struct input_dev *dev = handle->dev;
-
- printk("Entering power_event\n");
-
- if (type == EV_PWR) {
- switch (code) {
- case KEY_SUSPEND:
- printk("Powering down entire device\n");
-
- if (!suspend_button_pushed) {
- suspend_button_pushed = 1;
- schedule_work(&suspend_button_task);
- }
- break;
- case KEY_POWER:
- /* Hum power down the machine. */
- break;
- default:
- return;
- }
- }
-
- if (type == EV_KEY) {
- switch (code) {
- case KEY_SUSPEND:
- printk("Powering down input device\n");
- /* This is risky. See pm.h for details. */
- if (dev->state != PM_RESUME)
- dev->state = PM_RESUME;
- else
- dev->state = PM_SUSPEND;
- pm_send(dev->pm_dev, dev->state, dev);
- break;
- case KEY_POWER:
- /* Turn the input device off completely ? */
- break;
- default:
- return;
- }
- }
- return;
-}
-
-static struct input_handle *power_connect(struct input_handler *handler,
- struct input_dev *dev,
- const struct input_device_id *id)
-{
- struct input_handle *handle;
-
- if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL)))
- return NULL;
-
- handle->dev = dev;
- handle->handler = handler;
-
- input_open_device(handle);
-
- printk(KERN_INFO "power.c: Adding power management to input layer\n");
- return handle;
-}
-
-static void power_disconnect(struct input_handle *handle)
-{
- input_close_device(handle);
- kfree(handle);
-}
-
-static const struct input_device_id power_ids[] = {
- {
- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
- .evbit = { BIT(EV_KEY) },
- .keybit = { [LONG(KEY_SUSPEND)] = BIT(KEY_SUSPEND) }
- },
- {
- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
- .evbit = { BIT(EV_KEY) },
- .keybit = { [LONG(KEY_POWER)] = BIT(KEY_POWER) }
- },
- {
- .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
- .evbit = { BIT(EV_PWR) },
- },
- { }, /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(input, power_ids);
-
-static struct input_handler power_handler = {
- .event = power_event,
- .connect = power_connect,
- .disconnect = power_disconnect,
- .name = "power",
- .id_table = power_ids,
-};
-
-static int __init power_init(void)
-{
- return input_register_handler(&power_handler);
-}
-
-static void __exit power_exit(void)
-{
- input_unregister_handler(&power_handler);
-}
-
-module_init(power_init);
-module_exit(power_exit);
-
-MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
-MODULE_DESCRIPTION("Input Power Management driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c
index 4fa93ff3091..93a1a6ba216 100644
--- a/drivers/input/serio/hil_mlc.c
+++ b/drivers/input/serio/hil_mlc.c
@@ -32,11 +32,11 @@
*
* Driver theory of operation:
*
- * Some access methods and an ISR is defined by the sub-driver
- * (e.g. hp_sdc_mlc.c). These methods are expected to provide a
- * few bits of logic in addition to raw access to the HIL MLC,
- * specifically, the ISR, which is entirely registered by the
- * sub-driver and invoked directly, must check for record
+ * Some access methods and an ISR is defined by the sub-driver
+ * (e.g. hp_sdc_mlc.c). These methods are expected to provide a
+ * few bits of logic in addition to raw access to the HIL MLC,
+ * specifically, the ISR, which is entirely registered by the
+ * sub-driver and invoked directly, must check for record
* termination or packet match, at which point a semaphore must
* be cleared and then the hil_mlcs_tasklet must be scheduled.
*
@@ -47,7 +47,7 @@
* itself if output is pending. (This rescheduling should be replaced
* at some point with a sub-driver-specific mechanism.)
*
- * A timer task prods the tasklet once per second to prevent
+ * A timer task prods the tasklet once per second to prevent
* hangups when attached devices do not return expected data
* and to initiate probes of the loop for new devices.
*/
@@ -83,69 +83,85 @@ DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);
/********************** Device info/instance management **********************/
-static void hil_mlc_clear_di_map (hil_mlc *mlc, int val) {
+static void hil_mlc_clear_di_map(hil_mlc *mlc, int val)
+{
int j;
- for (j = val; j < 7 ; j++) {
+
+ for (j = val; j < 7 ; j++)
mlc->di_map[j] = -1;
- }
}
-static void hil_mlc_clear_di_scratch (hil_mlc *mlc) {
- memset(&(mlc->di_scratch), 0, sizeof(mlc->di_scratch));
+static void hil_mlc_clear_di_scratch(hil_mlc *mlc)
+{
+ memset(&mlc->di_scratch, 0, sizeof(mlc->di_scratch));
}
-static void hil_mlc_copy_di_scratch (hil_mlc *mlc, int idx) {
- memcpy(&(mlc->di[idx]), &(mlc->di_scratch), sizeof(mlc->di_scratch));
+static void hil_mlc_copy_di_scratch(hil_mlc *mlc, int idx)
+{
+ memcpy(&mlc->di[idx], &mlc->di_scratch, sizeof(mlc->di_scratch));
}
-static int hil_mlc_match_di_scratch (hil_mlc *mlc) {
+static int hil_mlc_match_di_scratch(hil_mlc *mlc)
+{
int idx;
for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
- int j, found;
+ int j, found = 0;
/* In-use slots are not eligible. */
- found = 0;
- for (j = 0; j < 7 ; j++) {
- if (mlc->di_map[j] == idx) found++;
- }
- if (found) continue;
- if (!memcmp(mlc->di + idx,
- &(mlc->di_scratch),
- sizeof(mlc->di_scratch))) break;
+ for (j = 0; j < 7 ; j++)
+ if (mlc->di_map[j] == idx)
+ found++;
+
+ if (found)
+ continue;
+
+ if (!memcmp(mlc->di + idx, &mlc->di_scratch,
+ sizeof(mlc->di_scratch)))
+ break;
}
- return((idx >= HIL_MLC_DEVMEM) ? -1 : idx);
+ return idx >= HIL_MLC_DEVMEM ? -1 : idx;
}
-static int hil_mlc_find_free_di(hil_mlc *mlc) {
+static int hil_mlc_find_free_di(hil_mlc *mlc)
+{
int idx;
- /* TODO: Pick all-zero slots first, failing that,
- * randomize the slot picked among those eligible.
+
+ /* TODO: Pick all-zero slots first, failing that,
+ * randomize the slot picked among those eligible.
*/
for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
- int j, found;
- found = 0;
- for (j = 0; j < 7 ; j++) {
- if (mlc->di_map[j] == idx) found++;
- }
- if (!found) break;
+ int j, found = 0;
+
+ for (j = 0; j < 7 ; j++)
+ if (mlc->di_map[j] == idx)
+ found++;
+
+ if (!found)
+ break;
}
- return(idx); /* Note: It is guaranteed at least one above will match */
+
+ return idx; /* Note: It is guaranteed at least one above will match */
}
-static inline void hil_mlc_clean_serio_map(hil_mlc *mlc) {
+static inline void hil_mlc_clean_serio_map(hil_mlc *mlc)
+{
int idx;
+
for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
- int j, found;
- found = 0;
- for (j = 0; j < 7 ; j++) {
- if (mlc->di_map[j] == idx) found++;
- }
- if (!found) mlc->serio_map[idx].di_revmap = -1;
+ int j, found = 0;
+
+ for (j = 0; j < 7 ; j++)
+ if (mlc->di_map[j] == idx)
+ found++;
+
+ if (!found)
+ mlc->serio_map[idx].di_revmap = -1;
}
}
-static void hil_mlc_send_polls(hil_mlc *mlc) {
+static void hil_mlc_send_polls(hil_mlc *mlc)
+{
int did, i, cnt;
struct serio *serio;
struct serio_driver *drv;
@@ -157,26 +173,31 @@ static void hil_mlc_send_polls(hil_mlc *mlc) {
while (mlc->icount < 15 - i) {
hil_packet p;
+
p = mlc->ipacket[i];
if (did != (p & HIL_PKT_ADDR_MASK) >> 8) {
- if (drv == NULL || drv->interrupt == NULL) goto skip;
+ if (drv && drv->interrupt) {
+ drv->interrupt(serio, 0, 0);
+ drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
+ drv->interrupt(serio, HIL_PKT_CMD >> 8, 0);
+ drv->interrupt(serio, HIL_CMD_POL + cnt, 0);
+ }
- drv->interrupt(serio, 0, 0);
- drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
- drv->interrupt(serio, HIL_PKT_CMD >> 8, 0);
- drv->interrupt(serio, HIL_CMD_POL + cnt, 0);
- skip:
did = (p & HIL_PKT_ADDR_MASK) >> 8;
serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL;
drv = (serio != NULL) ? serio->drv : NULL;
cnt = 0;
}
- cnt++; i++;
- if (drv == NULL || drv->interrupt == NULL) continue;
- drv->interrupt(serio, (p >> 24), 0);
- drv->interrupt(serio, (p >> 16) & 0xff, 0);
- drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0);
- drv->interrupt(serio, p & 0xff, 0);
+
+ cnt++;
+ i++;
+
+ if (drv && drv->interrupt) {
+ drv->interrupt(serio, (p >> 24), 0);
+ drv->interrupt(serio, (p >> 16) & 0xff, 0);
+ drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0);
+ drv->interrupt(serio, p & 0xff, 0);
+ }
}
}
@@ -215,12 +236,16 @@ static void hil_mlc_send_polls(hil_mlc *mlc) {
#define HILSEN_DOZE (HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK)
#define HILSEN_SLEEP (HILSEN_SAME | HILSEN_BREAK)
-static int hilse_match(hil_mlc *mlc, int unused) {
+static int hilse_match(hil_mlc *mlc, int unused)
+{
int rc;
+
rc = hil_mlc_match_di_scratch(mlc);
if (rc == -1) {
rc = hil_mlc_find_free_di(mlc);
- if (rc == -1) goto err;
+ if (rc == -1)
+ goto err;
+
#ifdef HIL_MLC_DEBUG
printk(KERN_DEBUG PREFIX "new in slot %i\n", rc);
#endif
@@ -231,6 +256,7 @@ static int hilse_match(hil_mlc *mlc, int unused) {
serio_rescan(mlc->serio[rc]);
return -1;
}
+
mlc->di_map[mlc->ddi] = rc;
#ifdef HIL_MLC_DEBUG
printk(KERN_DEBUG PREFIX "same in slot %i\n", rc);
@@ -238,152 +264,177 @@ static int hilse_match(hil_mlc *mlc, int unused) {
mlc->serio_map[rc].di_revmap = mlc->ddi;
hil_mlc_clean_serio_map(mlc);
return 0;
+
err:
printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n");
return 1;
}
/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */
-static int hilse_init_lcv(hil_mlc *mlc, int unused) {
+static int hilse_init_lcv(hil_mlc *mlc, int unused)
+{
struct timeval tv;
do_gettimeofday(&tv);
- if(mlc->lcv == 0) goto restart; /* First init, no need to dally */
- if(tv.tv_sec - mlc->lcv_tv.tv_sec < 5) return -1;
- restart:
+ if (mlc->lcv && (tv.tv_sec - mlc->lcv_tv.tv_sec) < 5)
+ return -1;
+
mlc->lcv_tv = tv;
mlc->lcv = 0;
+
return 0;
}
-static int hilse_inc_lcv(hil_mlc *mlc, int lim) {
- if (mlc->lcv++ >= lim) return -1;
- return 0;
+static int hilse_inc_lcv(hil_mlc *mlc, int lim)
+{
+ return mlc->lcv++ >= lim ? -1 : 0;
}
#if 0
-static int hilse_set_lcv(hil_mlc *mlc, int val) {
+static int hilse_set_lcv(hil_mlc *mlc, int val)
+{
mlc->lcv = val;
+
return 0;
}
#endif
/* Management of the discovered device index (zero based, -1 means no devs) */
-static int hilse_set_ddi(hil_mlc *mlc, int val) {
+static int hilse_set_ddi(hil_mlc *mlc, int val)
+{
mlc->ddi = val;
hil_mlc_clear_di_map(mlc, val + 1);
+
return 0;
}
-static int hilse_dec_ddi(hil_mlc *mlc, int unused) {
+static int hilse_dec_ddi(hil_mlc *mlc, int unused)
+{
mlc->ddi--;
- if (mlc->ddi <= -1) {
+ if (mlc->ddi <= -1) {
mlc->ddi = -1;
hil_mlc_clear_di_map(mlc, 0);
return -1;
}
hil_mlc_clear_di_map(mlc, mlc->ddi + 1);
+
return 0;
}
-static int hilse_inc_ddi(hil_mlc *mlc, int unused) {
- if (mlc->ddi >= 6) {
- BUG();
- return -1;
- }
+static int hilse_inc_ddi(hil_mlc *mlc, int unused)
+{
+ BUG_ON(mlc->ddi >= 6);
mlc->ddi++;
+
return 0;
}
-static int hilse_take_idd(hil_mlc *mlc, int unused) {
+static int hilse_take_idd(hil_mlc *mlc, int unused)
+{
int i;
- /* Help the state engine:
- * Is this a real IDD response or just an echo?
+ /* Help the state engine:
+ * Is this a real IDD response or just an echo?
*
- * Real IDD response does not start with a command.
+ * Real IDD response does not start with a command.
*/
- if (mlc->ipacket[0] & HIL_PKT_CMD) goto bail;
+ if (mlc->ipacket[0] & HIL_PKT_CMD)
+ goto bail;
+
/* Should have the command echoed further down. */
for (i = 1; i < 16; i++) {
- if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) ==
+ if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) ==
(mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) &&
- (mlc->ipacket[i] & HIL_PKT_CMD) &&
+ (mlc->ipacket[i] & HIL_PKT_CMD) &&
((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD))
break;
}
- if (i > 15) goto bail;
+ if (i > 15)
+ goto bail;
+
/* And the rest of the packets should still be clear. */
- while (++i < 16) {
- if (mlc->ipacket[i]) break;
- }
- if (i < 16) goto bail;
- for (i = 0; i < 16; i++) {
- mlc->di_scratch.idd[i] =
+ while (++i < 16)
+ if (mlc->ipacket[i])
+ break;
+
+ if (i < 16)
+ goto bail;
+
+ for (i = 0; i < 16; i++)
+ mlc->di_scratch.idd[i] =
mlc->ipacket[i] & HIL_PKT_DATA_MASK;
- }
+
/* Next step is to see if RSC supported */
- if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC)
+ if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC)
return HILSEN_NEXT;
- if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
+
+ if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
return HILSEN_DOWN | 4;
+
return 0;
+
bail:
mlc->ddi--;
+
return -1; /* This should send us off to ACF */
}
-static int hilse_take_rsc(hil_mlc *mlc, int unused) {
+static int hilse_take_rsc(hil_mlc *mlc, int unused)
+{
int i;
- for (i = 0; i < 16; i++) {
- mlc->di_scratch.rsc[i] =
+ for (i = 0; i < 16; i++)
+ mlc->di_scratch.rsc[i] =
mlc->ipacket[i] & HIL_PKT_DATA_MASK;
- }
+
/* Next step is to see if EXD supported (IDD has already been read) */
- if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
+ if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD)
return HILSEN_NEXT;
+
return 0;
}
-static int hilse_take_exd(hil_mlc *mlc, int unused) {
+static int hilse_take_exd(hil_mlc *mlc, int unused)
+{
int i;
- for (i = 0; i < 16; i++) {
- mlc->di_scratch.exd[i] =
+ for (i = 0; i < 16; i++)
+ mlc->di_scratch.exd[i] =
mlc->ipacket[i] & HIL_PKT_DATA_MASK;
- }
+
/* Next step is to see if RNM supported. */
- if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM)
+ if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM)
return HILSEN_NEXT;
+
return 0;
}
-static int hilse_take_rnm(hil_mlc *mlc, int unused) {
+static int hilse_take_rnm(hil_mlc *mlc, int unused)
+{
int i;
- for (i = 0; i < 16; i++) {
- mlc->di_scratch.rnm[i] =
+ for (i = 0; i < 16; i++)
+ mlc->di_scratch.rnm[i] =
mlc->ipacket[i] & HIL_PKT_DATA_MASK;
- }
- do {
- char nam[17];
- snprintf(nam, 16, "%s", mlc->di_scratch.rnm);
- nam[16] = '\0';
- printk(KERN_INFO PREFIX "Device name gotten: %s\n", nam);
- } while (0);
+
+ printk(KERN_INFO PREFIX "Device name gotten: %16s\n",
+ mlc->di_scratch.rnm);
+
return 0;
}
-static int hilse_operate(hil_mlc *mlc, int repoll) {
+static int hilse_operate(hil_mlc *mlc, int repoll)
+{
- if (mlc->opercnt == 0) hil_mlcs_probe = 0;
+ if (mlc->opercnt == 0)
+ hil_mlcs_probe = 0;
mlc->opercnt = 1;
hil_mlc_send_polls(mlc);
- if (!hil_mlcs_probe) return 0;
+ if (!hil_mlcs_probe)
+ return 0;
hil_mlcs_probe = 0;
mlc->opercnt = 0;
return 1;
@@ -408,7 +459,7 @@ static int hilse_operate(hil_mlc *mlc, int repoll) {
#define OUT_LAST(pack) \
{ HILSE_OUT_LAST, { .packet = pack }, 0, 0, 0, 0 },
-struct hilse_node hil_mlc_se[HILSEN_END] = {
+const struct hilse_node hil_mlc_se[HILSEN_END] = {
/* 0 HILSEN_START */
FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0)
@@ -428,7 +479,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = {
EXPECT(HIL_ERR_INT | TEST_PACKET(0xa),
2000, HILSEN_NEXT, HILSEN_RESTART, HILSEN_RESTART)
OUT(HIL_CTRL_ONLY | 0) /* Disable test mode */
-
+
/* 9 HILSEN_DHR */
FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0)
@@ -439,7 +490,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = {
IN(300000, HILSEN_DHR2, HILSEN_DHR2, HILSEN_NEXT)
/* 14 HILSEN_IFC */
- OUT(HIL_PKT_CMD | HIL_CMD_IFC)
+ OUT(HIL_PKT_CMD | HIL_CMD_IFC)
EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
20000, HILSEN_DISC, HILSEN_DHR2, HILSEN_NEXT )
@@ -455,7 +506,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = {
/* 18 HILSEN_HEAL */
OUT_LAST(HIL_CMD_ELB)
- EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT,
+ EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT,
20000, HILSEN_REPOLL, HILSEN_DSR, HILSEN_NEXT)
FUNC(hilse_dec_ddi, 0, HILSEN_HEAL, HILSEN_NEXT, 0)
@@ -503,7 +554,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = {
/* 44 HILSEN_PROBE */
OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT)
- IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT)
+ IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT)
OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT)
OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
@@ -514,7 +565,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = {
/* 52 HILSEN_DSR */
FUNC(hilse_set_ddi, -1, HILSEN_NEXT, 0, 0)
OUT(HIL_PKT_CMD | HIL_CMD_DSR)
- IN(20000, HILSEN_DHR, HILSEN_DHR, HILSEN_IFC)
+ IN(20000, HILSEN_DHR, HILSEN_DHR, HILSEN_IFC)
/* 55 HILSEN_REPOLL */
OUT(HIL_PKT_CMD | HIL_CMD_RPL)
@@ -523,14 +574,15 @@ struct hilse_node hil_mlc_se[HILSEN_END] = {
FUNC(hilse_operate, 1, HILSEN_OPERATE, HILSEN_IFC, HILSEN_PROBE)
/* 58 HILSEN_IFCACF */
- OUT(HIL_PKT_CMD | HIL_CMD_IFC)
+ OUT(HIL_PKT_CMD | HIL_CMD_IFC)
EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
20000, HILSEN_ACF2, HILSEN_DHR2, HILSEN_HEAL)
/* 60 HILSEN_END */
};
-static inline void hilse_setup_input(hil_mlc *mlc, struct hilse_node *node) {
+static inline void hilse_setup_input(hil_mlc *mlc, const struct hilse_node *node)
+{
switch (node->act) {
case HILSE_EXPECT_DISC:
@@ -555,29 +607,27 @@ static inline void hilse_setup_input(hil_mlc *mlc, struct hilse_node *node) {
do_gettimeofday(&(mlc->instart));
mlc->icount = 15;
memset(mlc->ipacket, 0, 16 * sizeof(hil_packet));
- BUG_ON(down_trylock(&(mlc->isem)));
-
- return;
+ BUG_ON(down_trylock(&mlc->isem));
}
#ifdef HIL_MLC_DEBUG
-static int doze = 0;
+static int doze;
static int seidx; /* For debug */
-static int kick = 1;
#endif
-static int hilse_donode (hil_mlc *mlc) {
- struct hilse_node *node;
+static int hilse_donode(hil_mlc *mlc)
+{
+ const struct hilse_node *node;
int nextidx = 0;
int sched_long = 0;
unsigned long flags;
#ifdef HIL_MLC_DEBUG
- if (mlc->seidx && (mlc->seidx != seidx) && mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) {
- printk(KERN_DEBUG PREFIX "z%i \n%s {%i}", doze, kick ? "K" : "", mlc->seidx);
+ if (mlc->seidx && mlc->seidx != seidx &&
+ mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) {
+ printk(KERN_DEBUG PREFIX "z%i \n {%i}", doze, mlc->seidx);
doze = 0;
}
- kick = 0;
seidx = mlc->seidx;
#endif
@@ -588,52 +638,61 @@ static int hilse_donode (hil_mlc *mlc) {
hil_packet pack;
case HILSE_FUNC:
- if (node->object.func == NULL) break;
+ BUG_ON(node->object.func == NULL);
rc = node->object.func(mlc, node->arg);
- nextidx = (rc > 0) ? node->ugly :
+ nextidx = (rc > 0) ? node->ugly :
((rc < 0) ? node->bad : node->good);
- if (nextidx == HILSEN_FOLLOW) nextidx = rc;
+ if (nextidx == HILSEN_FOLLOW)
+ nextidx = rc;
break;
+
case HILSE_EXPECT_LAST:
case HILSE_EXPECT_DISC:
case HILSE_EXPECT:
case HILSE_IN:
/* Already set up from previous HILSE_OUT_* */
- write_lock_irqsave(&(mlc->lock), flags);
+ write_lock_irqsave(&mlc->lock, flags);
rc = mlc->in(mlc, node->arg);
if (rc == 2) {
nextidx = HILSEN_DOZE;
sched_long = 1;
- write_unlock_irqrestore(&(mlc->lock), flags);
+ write_unlock_irqrestore(&mlc->lock, flags);
break;
}
- if (rc == 1) nextidx = node->ugly;
- else if (rc == 0) nextidx = node->good;
- else nextidx = node->bad;
+ if (rc == 1)
+ nextidx = node->ugly;
+ else if (rc == 0)
+ nextidx = node->good;
+ else
+ nextidx = node->bad;
mlc->istarted = 0;
- write_unlock_irqrestore(&(mlc->lock), flags);
+ write_unlock_irqrestore(&mlc->lock, flags);
break;
+
case HILSE_OUT_LAST:
- write_lock_irqsave(&(mlc->lock), flags);
+ write_lock_irqsave(&mlc->lock, flags);
pack = node->object.packet;
pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
goto out;
+
case HILSE_OUT_DISC:
- write_lock_irqsave(&(mlc->lock), flags);
+ write_lock_irqsave(&mlc->lock, flags);
pack = node->object.packet;
pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
goto out;
+
case HILSE_OUT:
- write_lock_irqsave(&(mlc->lock), flags);
+ write_lock_irqsave(&mlc->lock, flags);
pack = node->object.packet;
out:
- if (mlc->istarted) goto out2;
+ if (mlc->istarted)
+ goto out2;
/* Prepare to receive input */
if ((node + 1)->act & HILSE_IN)
hilse_setup_input(mlc, node + 1);
out2:
- write_unlock_irqrestore(&(mlc->lock), flags);
+ write_unlock_irqrestore(&mlc->lock, flags);
if (down_trylock(&mlc->osem)) {
nextidx = HILSEN_DOZE;
@@ -641,60 +700,71 @@ static int hilse_donode (hil_mlc *mlc) {
}
up(&mlc->osem);
- write_lock_irqsave(&(mlc->lock), flags);
- if (!(mlc->ostarted)) {
+ write_lock_irqsave(&mlc->lock, flags);
+ if (!mlc->ostarted) {
mlc->ostarted = 1;
mlc->opacket = pack;
mlc->out(mlc);
nextidx = HILSEN_DOZE;
- write_unlock_irqrestore(&(mlc->lock), flags);
+ write_unlock_irqrestore(&mlc->lock, flags);
break;
}
mlc->ostarted = 0;
do_gettimeofday(&(mlc->instart));
- write_unlock_irqrestore(&(mlc->lock), flags);
+ write_unlock_irqrestore(&mlc->lock, flags);
nextidx = HILSEN_NEXT;
break;
+
case HILSE_CTS:
+ write_lock_irqsave(&mlc->lock, flags);
nextidx = mlc->cts(mlc) ? node->bad : node->good;
+ write_unlock_irqrestore(&mlc->lock, flags);
break;
+
default:
BUG();
- nextidx = 0;
- break;
}
#ifdef HIL_MLC_DEBUG
- if (nextidx == HILSEN_DOZE) doze++;
+ if (nextidx == HILSEN_DOZE)
+ doze++;
#endif
while (nextidx & HILSEN_SCHED) {
struct timeval tv;
- if (!sched_long) goto sched;
+ if (!sched_long)
+ goto sched;
do_gettimeofday(&tv);
- tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec);
+ tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec);
tv.tv_usec -= mlc->instart.tv_usec;
if (tv.tv_usec >= mlc->intimeout) goto sched;
- tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / 1000000;
+ tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / USEC_PER_SEC;
if (!tv.tv_usec) goto sched;
mod_timer(&hil_mlcs_kicker, jiffies + tv.tv_usec);
break;
sched:
tasklet_schedule(&hil_mlcs_tasklet);
break;
- }
- if (nextidx & HILSEN_DOWN) mlc->seidx += nextidx & HILSEN_MASK;
- else if (nextidx & HILSEN_UP) mlc->seidx -= nextidx & HILSEN_MASK;
- else mlc->seidx = nextidx & HILSEN_MASK;
+ }
+
+ if (nextidx & HILSEN_DOWN)
+ mlc->seidx += nextidx & HILSEN_MASK;
+ else if (nextidx & HILSEN_UP)
+ mlc->seidx -= nextidx & HILSEN_MASK;
+ else
+ mlc->seidx = nextidx & HILSEN_MASK;
+
+ if (nextidx & HILSEN_BREAK)
+ return 1;
- if (nextidx & HILSEN_BREAK) return 1;
return 0;
}
/******************** tasklet context functions **************************/
-static void hil_mlcs_process(unsigned long unused) {
+static void hil_mlcs_process(unsigned long unused)
+{
struct list_head *tmp;
read_lock(&hil_mlcs_lock);
@@ -702,19 +772,20 @@ static void hil_mlcs_process(unsigned long unused) {
struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
while (hilse_donode(mlc) == 0) {
#ifdef HIL_MLC_DEBUG
- if (mlc->seidx != 41 &&
- mlc->seidx != 42 &&
- mlc->seidx != 43)
- printk(KERN_DEBUG PREFIX " + ");
+ if (mlc->seidx != 41 &&
+ mlc->seidx != 42 &&
+ mlc->seidx != 43)
+ printk(KERN_DEBUG PREFIX " + ");
#endif
- };
+ }
}
read_unlock(&hil_mlcs_lock);
}
/************************* Keepalive timer task *********************/
-void hil_mlcs_timer (unsigned long data) {
+void hil_mlcs_timer(unsigned long data)
+{
hil_mlcs_probe = 1;
tasklet_schedule(&hil_mlcs_tasklet);
/* Re-insert the periodic task. */
@@ -724,28 +795,25 @@ void hil_mlcs_timer (unsigned long data) {
/******************** user/kernel context functions **********************/
-static int hil_mlc_serio_write(struct serio *serio, unsigned char c) {
+static int hil_mlc_serio_write(struct serio *serio, unsigned char c)
+{
struct hil_mlc_serio_map *map;
struct hil_mlc *mlc;
struct serio_driver *drv;
uint8_t *idx, *last;
map = serio->port_data;
- if (map == NULL) {
- BUG();
- return -EIO;
- }
+ BUG_ON(map == NULL);
+
mlc = map->mlc;
- if (mlc == NULL) {
- BUG();
- return -EIO;
- }
- mlc->serio_opacket[map->didx] |=
+ BUG_ON(mlc == NULL);
+
+ mlc->serio_opacket[map->didx] |=
((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx]));
if (mlc->serio_oidx[map->didx] >= 3) {
/* for now only commands */
- if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD))
+ if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD))
return -EIO;
switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) {
case HIL_CMD_IDD:
@@ -771,12 +839,11 @@ static int hil_mlc_serio_write(struct serio *serio, unsigned char c) {
return -EIO;
emu:
drv = serio->drv;
- if (drv == NULL) {
- BUG();
- return -EIO;
- }
+ BUG_ON(drv == NULL);
+
last = idx + 15;
- while ((last != idx) && (*last == 0)) last--;
+ while ((last != idx) && (*last == 0))
+ last--;
while (idx != last) {
drv->interrupt(serio, 0, 0);
@@ -789,14 +856,15 @@ static int hil_mlc_serio_write(struct serio *serio, unsigned char c) {
drv->interrupt(serio, HIL_ERR_INT >> 16, 0);
drv->interrupt(serio, HIL_PKT_CMD >> 8, 0);
drv->interrupt(serio, *idx, 0);
-
+
mlc->serio_oidx[map->didx] = 0;
mlc->serio_opacket[map->didx] = 0;
return 0;
}
-static int hil_mlc_serio_open(struct serio *serio) {
+static int hil_mlc_serio_open(struct serio *serio)
+{
struct hil_mlc_serio_map *map;
struct hil_mlc *mlc;
@@ -804,67 +872,57 @@ static int hil_mlc_serio_open(struct serio *serio) {
return -EBUSY;
map = serio->port_data;
- if (map == NULL) {
- BUG();
- return -ENODEV;
- }
+ BUG_ON(map == NULL);
+
mlc = map->mlc;
- if (mlc == NULL) {
- BUG();
- return -ENODEV;
- }
+ BUG_ON(mlc == NULL);
return 0;
}
-static void hil_mlc_serio_close(struct serio *serio) {
+static void hil_mlc_serio_close(struct serio *serio)
+{
struct hil_mlc_serio_map *map;
struct hil_mlc *mlc;
map = serio->port_data;
- if (map == NULL) {
- BUG();
- return;
- }
+ BUG_ON(map == NULL);
+
mlc = map->mlc;
- if (mlc == NULL) {
- BUG();
- return;
- }
+ BUG_ON(mlc == NULL);
serio_set_drvdata(serio, NULL);
serio->drv = NULL;
/* TODO wake up interruptable */
}
-static struct serio_device_id hil_mlc_serio_id = {
+static const struct serio_device_id hil_mlc_serio_id = {
.type = SERIO_HIL_MLC,
.proto = SERIO_HIL,
.extra = SERIO_ANY,
.id = SERIO_ANY,
};
-int hil_mlc_register(hil_mlc *mlc) {
+int hil_mlc_register(hil_mlc *mlc)
+{
int i;
- unsigned long flags;
+ unsigned long flags;
- if (mlc == NULL) {
- return -EINVAL;
- }
+ BUG_ON(mlc == NULL);
mlc->istarted = 0;
- mlc->ostarted = 0;
+ mlc->ostarted = 0;
- rwlock_init(&mlc->lock);
- init_MUTEX(&(mlc->osem));
+ rwlock_init(&mlc->lock);
+ init_MUTEX(&mlc->osem);
- init_MUTEX(&(mlc->isem));
- mlc->icount = -1;
- mlc->imatch = 0;
+ init_MUTEX(&mlc->isem);
+ mlc->icount = -1;
+ mlc->imatch = 0;
mlc->opercnt = 0;
- init_MUTEX_LOCKED(&(mlc->csem));
+ init_MUTEX_LOCKED(&(mlc->csem));
hil_mlc_clear_di_scratch(mlc);
hil_mlc_clear_di_map(mlc, 0);
@@ -873,6 +931,8 @@ int hil_mlc_register(hil_mlc *mlc) {
hil_mlc_copy_di_scratch(mlc, i);
mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL);
mlc->serio[i] = mlc_serio;
+ snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i);
+ snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i);
mlc_serio->id = hil_mlc_serio_id;
mlc_serio->write = hil_mlc_serio_write;
mlc_serio->open = hil_mlc_serio_open;
@@ -897,19 +957,18 @@ int hil_mlc_register(hil_mlc *mlc) {
return 0;
}
-int hil_mlc_unregister(hil_mlc *mlc) {
+int hil_mlc_unregister(hil_mlc *mlc)
+{
struct list_head *tmp;
- unsigned long flags;
+ unsigned long flags;
int i;
- if (mlc == NULL)
- return -EINVAL;
+ BUG_ON(mlc == NULL);
write_lock_irqsave(&hil_mlcs_lock, flags);
- list_for_each(tmp, &hil_mlcs) {
+ list_for_each(tmp, &hil_mlcs)
if (list_entry(tmp, hil_mlc, list) == mlc)
goto found;
- }
/* not found in list */
write_unlock_irqrestore(&hil_mlcs_lock, flags);
@@ -918,7 +977,7 @@ int hil_mlc_unregister(hil_mlc *mlc) {
found:
list_del(tmp);
- write_unlock_irqrestore(&hil_mlcs_lock, flags);
+ write_unlock_irqrestore(&hil_mlcs_lock, flags);
for (i = 0; i < HIL_MLC_DEVMEM; i++) {
serio_unregister_port(mlc->serio[i]);
@@ -942,7 +1001,7 @@ static int __init hil_mlc_init(void)
return 0;
}
-
+
static void __exit hil_mlc_exit(void)
{
del_timer(&hil_mlcs_kicker);
@@ -950,6 +1009,6 @@ static void __exit hil_mlc_exit(void)
tasklet_disable(&hil_mlcs_tasklet);
tasklet_kill(&hil_mlcs_tasklet);
}
-
+
module_init(hil_mlc_init);
module_exit(hil_mlc_exit);
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index b57370dc4e3..6af199805ff 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -34,27 +34,27 @@
*
* Driver theory of operation:
*
- * hp_sdc_put does all writing to the SDC. ISR can run on a different
- * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time
+ * hp_sdc_put does all writing to the SDC. ISR can run on a different
+ * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time
* (it cannot really benefit from SMP anyway.) A tasket fit this perfectly.
*
- * All data coming back from the SDC is sent via interrupt and can be read
- * fully in the ISR, so there are no latency/throughput problems there.
- * The problem is with output, due to the slow clock speed of the SDC
- * compared to the CPU. This should not be too horrible most of the time,
- * but if used with HIL devices that support the multibyte transfer command,
- * keeping outbound throughput flowing at the 6500KBps that the HIL is
+ * All data coming back from the SDC is sent via interrupt and can be read
+ * fully in the ISR, so there are no latency/throughput problems there.
+ * The problem is with output, due to the slow clock speed of the SDC
+ * compared to the CPU. This should not be too horrible most of the time,
+ * but if used with HIL devices that support the multibyte transfer command,
+ * keeping outbound throughput flowing at the 6500KBps that the HIL is
* capable of is more than can be done at HZ=100.
*
- * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf
- * is set to 0 when the IBF flag in the status register has cleared. ISR
- * may do this, and may also access the parts of queued transactions related
- * to reading data back from the SDC, but otherwise will not touch the
+ * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf
+ * is set to 0 when the IBF flag in the status register has cleared. ISR
+ * may do this, and may also access the parts of queued transactions related
+ * to reading data back from the SDC, but otherwise will not touch the
* hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1.
*
* The i8042 write index and the values in the 4-byte input buffer
* starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively,
- * to minimize the amount of IO needed to the SDC. However these values
+ * to minimize the amount of IO needed to the SDC. However these values
* do not need to be locked since they are only ever accessed by hp_sdc_put.
*
* A timer task schedules the tasklet once per second just to make
@@ -100,39 +100,46 @@ EXPORT_SYMBOL(hp_sdc_release_timer_irq);
EXPORT_SYMBOL(hp_sdc_release_hil_irq);
EXPORT_SYMBOL(hp_sdc_release_cooked_irq);
+EXPORT_SYMBOL(__hp_sdc_enqueue_transaction);
EXPORT_SYMBOL(hp_sdc_enqueue_transaction);
EXPORT_SYMBOL(hp_sdc_dequeue_transaction);
static hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */
/*************** primitives for use in any context *********************/
-static inline uint8_t hp_sdc_status_in8 (void) {
+static inline uint8_t hp_sdc_status_in8(void)
+{
uint8_t status;
unsigned long flags;
write_lock_irqsave(&hp_sdc.ibf_lock, flags);
status = sdc_readb(hp_sdc.status_io);
- if (!(status & HP_SDC_STATUS_IBF)) hp_sdc.ibf = 0;
+ if (!(status & HP_SDC_STATUS_IBF))
+ hp_sdc.ibf = 0;
write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
return status;
}
-static inline uint8_t hp_sdc_data_in8 (void) {
- return sdc_readb(hp_sdc.data_io);
+static inline uint8_t hp_sdc_data_in8(void)
+{
+ return sdc_readb(hp_sdc.data_io);
}
-static inline void hp_sdc_status_out8 (uint8_t val) {
+static inline void hp_sdc_status_out8(uint8_t val)
+{
unsigned long flags;
write_lock_irqsave(&hp_sdc.ibf_lock, flags);
hp_sdc.ibf = 1;
- if ((val & 0xf0) == 0xe0) hp_sdc.wi = 0xff;
+ if ((val & 0xf0) == 0xe0)
+ hp_sdc.wi = 0xff;
sdc_writeb(val, hp_sdc.status_io);
write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
}
-static inline void hp_sdc_data_out8 (uint8_t val) {
+static inline void hp_sdc_data_out8(uint8_t val)
+{
unsigned long flags;
write_lock_irqsave(&hp_sdc.ibf_lock, flags);
@@ -141,11 +148,12 @@ static inline void hp_sdc_data_out8 (uint8_t val) {
write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
}
-/* Care must be taken to only invoke hp_sdc_spin_ibf when
- * absolutely needed, or in rarely invoked subroutines.
- * Not only does it waste CPU cycles, it also wastes bus cycles.
+/* Care must be taken to only invoke hp_sdc_spin_ibf when
+ * absolutely needed, or in rarely invoked subroutines.
+ * Not only does it waste CPU cycles, it also wastes bus cycles.
*/
-static inline void hp_sdc_spin_ibf(void) {
+static inline void hp_sdc_spin_ibf(void)
+{
unsigned long flags;
rwlock_t *lock;
@@ -158,19 +166,21 @@ static inline void hp_sdc_spin_ibf(void) {
}
read_unlock(lock);
write_lock(lock);
- while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) {};
+ while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF)
+ { }
hp_sdc.ibf = 0;
write_unlock_irqrestore(lock, flags);
}
/************************ Interrupt context functions ************************/
-static void hp_sdc_take (int irq, void *dev_id, uint8_t status, uint8_t data) {
+static void hp_sdc_take(int irq, void *dev_id, uint8_t status, uint8_t data)
+{
hp_sdc_transaction *curr;
read_lock(&hp_sdc.rtq_lock);
if (hp_sdc.rcurr < 0) {
- read_unlock(&hp_sdc.rtq_lock);
+ read_unlock(&hp_sdc.rtq_lock);
return;
}
curr = hp_sdc.tq[hp_sdc.rcurr];
@@ -183,25 +193,27 @@ static void hp_sdc_take (int irq, void *dev_id, uint8_t status, uint8_t data) {
if (hp_sdc.rqty <= 0) {
/* All data has been gathered. */
- if(curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) {
- if (curr->act.semaphore) up(curr->act.semaphore);
- }
- if(curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) {
+ if (curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE)
+ if (curr->act.semaphore)
+ up(curr->act.semaphore);
+
+ if (curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK)
if (curr->act.irqhook)
curr->act.irqhook(irq, dev_id, status, data);
- }
+
curr->actidx = curr->idx;
curr->idx++;
/* Return control of this transaction */
write_lock(&hp_sdc.rtq_lock);
- hp_sdc.rcurr = -1;
+ hp_sdc.rcurr = -1;
hp_sdc.rqty = 0;
write_unlock(&hp_sdc.rtq_lock);
tasklet_schedule(&hp_sdc.task);
}
}
-static irqreturn_t hp_sdc_isr(int irq, void *dev_id) {
+static irqreturn_t hp_sdc_isr(int irq, void *dev_id)
+{
uint8_t status, data;
status = hp_sdc_status_in8();
@@ -209,67 +221,74 @@ static irqreturn_t hp_sdc_isr(int irq, void *dev_id) {
data = hp_sdc_data_in8();
/* For now we are ignoring these until we get the SDC to behave. */
- if (((status & 0xf1) == 0x51) && data == 0x82) {
- return IRQ_HANDLED;
- }
+ if (((status & 0xf1) == 0x51) && data == 0x82)
+ return IRQ_HANDLED;
- switch(status & HP_SDC_STATUS_IRQMASK) {
- case 0: /* This case is not documented. */
+ switch (status & HP_SDC_STATUS_IRQMASK) {
+ case 0: /* This case is not documented. */
break;
- case HP_SDC_STATUS_USERTIMER:
- case HP_SDC_STATUS_PERIODIC:
- case HP_SDC_STATUS_TIMER:
+
+ case HP_SDC_STATUS_USERTIMER:
+ case HP_SDC_STATUS_PERIODIC:
+ case HP_SDC_STATUS_TIMER:
read_lock(&hp_sdc.hook_lock);
- if (hp_sdc.timer != NULL)
+ if (hp_sdc.timer != NULL)
hp_sdc.timer(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
- case HP_SDC_STATUS_REG:
+
+ case HP_SDC_STATUS_REG:
hp_sdc_take(irq, dev_id, status, data);
break;
- case HP_SDC_STATUS_HILCMD:
- case HP_SDC_STATUS_HILDATA:
+
+ case HP_SDC_STATUS_HILCMD:
+ case HP_SDC_STATUS_HILDATA:
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.hil != NULL)
hp_sdc.hil(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
- case HP_SDC_STATUS_PUP:
+
+ case HP_SDC_STATUS_PUP:
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.pup != NULL)
hp_sdc.pup(irq, dev_id, status, data);
- else printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n");
+ else
+ printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n");
read_unlock(&hp_sdc.hook_lock);
break;
- default:
+
+ default:
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.cooked != NULL)
hp_sdc.cooked(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
}
+
return IRQ_HANDLED;
}
-static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id) {
+static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id)
+{
int status;
-
+
status = hp_sdc_status_in8();
printk(KERN_WARNING PREFIX "NMI !\n");
-#if 0
+#if 0
if (status & HP_SDC_NMISTATUS_FHS) {
read_lock(&hp_sdc.hook_lock);
- if (hp_sdc.timer != NULL)
+ if (hp_sdc.timer != NULL)
hp_sdc.timer(irq, dev_id, status, 0);
read_unlock(&hp_sdc.hook_lock);
- }
- else {
+ } else {
/* TODO: pass this on to the HIL handler, or do SAK here? */
printk(KERN_WARNING PREFIX "HIL NMI\n");
}
#endif
+
return IRQ_HANDLED;
}
@@ -278,13 +297,17 @@ static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id) {
unsigned long hp_sdc_put(void);
-static void hp_sdc_tasklet(unsigned long foo) {
-
+static void hp_sdc_tasklet(unsigned long foo)
+{
write_lock_irq(&hp_sdc.rtq_lock);
+
if (hp_sdc.rcurr >= 0) {
struct timeval tv;
+
do_gettimeofday(&tv);
- if (tv.tv_sec > hp_sdc.rtv.tv_sec) tv.tv_usec += 1000000;
+ if (tv.tv_sec > hp_sdc.rtv.tv_sec)
+ tv.tv_usec += USEC_PER_SEC;
+
if (tv.tv_usec - hp_sdc.rtv.tv_usec > HP_SDC_MAX_REG_DELAY) {
hp_sdc_transaction *curr;
uint8_t tmp;
@@ -300,27 +323,29 @@ static void hp_sdc_tasklet(unsigned long foo) {
hp_sdc.rqty = 0;
tmp = curr->seq[curr->actidx];
curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD;
- if(tmp & HP_SDC_ACT_SEMAPHORE) {
- if (curr->act.semaphore)
+ if (tmp & HP_SDC_ACT_SEMAPHORE)
+ if (curr->act.semaphore)
up(curr->act.semaphore);
- }
- if(tmp & HP_SDC_ACT_CALLBACK) {
+
+ if (tmp & HP_SDC_ACT_CALLBACK) {
/* Note this means that irqhooks may be called
* in tasklet/bh context.
*/
- if (curr->act.irqhook)
+ if (curr->act.irqhook)
curr->act.irqhook(0, NULL, 0, 0);
}
+
curr->actidx = curr->idx;
curr->idx++;
- hp_sdc.rcurr = -1;
+ hp_sdc.rcurr = -1;
}
}
write_unlock_irq(&hp_sdc.rtq_lock);
hp_sdc_put();
}
-unsigned long hp_sdc_put(void) {
+unsigned long hp_sdc_put(void)
+{
hp_sdc_transaction *curr;
uint8_t act;
int idx, curridx;
@@ -333,19 +358,24 @@ unsigned long hp_sdc_put(void) {
requires output, so we skip to the administrativa. */
if (hp_sdc.ibf) {
hp_sdc_status_in8();
- if (hp_sdc.ibf) goto finish;
+ if (hp_sdc.ibf)
+ goto finish;
}
anew:
/* See if we are in the middle of a sequence. */
- if (hp_sdc.wcurr < 0) hp_sdc.wcurr = 0;
+ if (hp_sdc.wcurr < 0)
+ hp_sdc.wcurr = 0;
read_lock_irq(&hp_sdc.rtq_lock);
- if (hp_sdc.rcurr == hp_sdc.wcurr) hp_sdc.wcurr++;
+ if (hp_sdc.rcurr == hp_sdc.wcurr)
+ hp_sdc.wcurr++;
read_unlock_irq(&hp_sdc.rtq_lock);
- if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+ if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN)
+ hp_sdc.wcurr = 0;
curridx = hp_sdc.wcurr;
- if (hp_sdc.tq[curridx] != NULL) goto start;
+ if (hp_sdc.tq[curridx] != NULL)
+ goto start;
while (++curridx != hp_sdc.wcurr) {
if (curridx >= HP_SDC_QUEUE_LEN) {
@@ -358,7 +388,8 @@ unsigned long hp_sdc_put(void) {
continue;
}
read_unlock_irq(&hp_sdc.rtq_lock);
- if (hp_sdc.tq[curridx] != NULL) break; /* Found one. */
+ if (hp_sdc.tq[curridx] != NULL)
+ break; /* Found one. */
}
if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */
curridx = -1;
@@ -374,7 +405,8 @@ unsigned long hp_sdc_put(void) {
goto finish;
}
- if (hp_sdc.wcurr == -1) goto done;
+ if (hp_sdc.wcurr == -1)
+ goto done;
curr = hp_sdc.tq[curridx];
idx = curr->actidx;
@@ -383,20 +415,23 @@ unsigned long hp_sdc_put(void) {
hp_sdc.tq[curridx] = NULL;
/* Interleave outbound data between the transactions. */
hp_sdc.wcurr++;
- if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
- goto finish;
+ if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN)
+ hp_sdc.wcurr = 0;
+ goto finish;
}
act = curr->seq[idx];
idx++;
if (curr->idx >= curr->endidx) {
- if (act & HP_SDC_ACT_DEALLOC) kfree(curr);
+ if (act & HP_SDC_ACT_DEALLOC)
+ kfree(curr);
hp_sdc.tq[curridx] = NULL;
/* Interleave outbound data between the transactions. */
hp_sdc.wcurr++;
- if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
- goto finish;
+ if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN)
+ hp_sdc.wcurr = 0;
+ goto finish;
}
while (act & HP_SDC_ACT_PRECMD) {
@@ -409,9 +444,10 @@ unsigned long hp_sdc_put(void) {
curr->idx++;
/* act finished? */
if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD)
- goto actdone;
+ goto actdone;
/* skip quantity field if data-out sequence follows. */
- if (act & HP_SDC_ACT_DATAOUT) curr->idx++;
+ if (act & HP_SDC_ACT_DATAOUT)
+ curr->idx++;
goto finish;
}
if (act & HP_SDC_ACT_DATAOUT) {
@@ -423,15 +459,15 @@ unsigned long hp_sdc_put(void) {
hp_sdc_data_out8(curr->seq[curr->idx]);
curr->idx++;
/* act finished? */
- if ((curr->idx - idx >= qty) &&
- ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT))
+ if (curr->idx - idx >= qty &&
+ (act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT)
goto actdone;
goto finish;
}
idx += qty;
act &= ~HP_SDC_ACT_DATAOUT;
- }
- else while (act & HP_SDC_ACT_DATAREG) {
+ } else
+ while (act & HP_SDC_ACT_DATAREG) {
int mask;
uint8_t w7[4];
@@ -445,26 +481,30 @@ unsigned long hp_sdc_put(void) {
act &= ~HP_SDC_ACT_DATAREG;
break;
}
-
+
w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0];
w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1];
w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2];
w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3];
-
+
if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 ||
- w7[hp_sdc.wi-0x70] == hp_sdc.r7[hp_sdc.wi-0x70]) {
+ w7[hp_sdc.wi - 0x70] == hp_sdc.r7[hp_sdc.wi - 0x70]) {
int i = 0;
- /* Need to point the write index register */
- while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++;
+ /* Need to point the write index register */
+ while (i < 4 && w7[i] == hp_sdc.r7[i])
+ i++;
+
if (i < 4) {
hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i);
hp_sdc.wi = 0x70 + i;
goto finish;
}
+
idx++;
if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG)
goto actdone;
+
curr->idx = idx;
act &= ~HP_SDC_ACT_DATAREG;
break;
@@ -476,12 +516,13 @@ unsigned long hp_sdc_put(void) {
{
int i = 0;
- while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++;
+ while ((i < 4) && w7[i] == hp_sdc.r7[i])
+ i++;
if (i >= 4) {
curr->idx = idx + 1;
- if ((act & HP_SDC_ACT_DURING) ==
+ if ((act & HP_SDC_ACT_DURING) ==
HP_SDC_ACT_DATAREG)
- goto actdone;
+ goto actdone;
}
}
goto finish;
@@ -497,7 +538,7 @@ unsigned long hp_sdc_put(void) {
if (act & HP_SDC_ACT_POSTCMD) {
- uint8_t postcmd;
+ uint8_t postcmd;
/* curr->idx should == idx at this point. */
postcmd = curr->seq[idx];
@@ -505,12 +546,12 @@ unsigned long hp_sdc_put(void) {
if (act & HP_SDC_ACT_DATAIN) {
/* Start a new read */
- hp_sdc.rqty = curr->seq[curr->idx];
+ hp_sdc.rqty = curr->seq[curr->idx];
do_gettimeofday(&hp_sdc.rtv);
curr->idx++;
/* Still need to lock here in case of spurious irq. */
write_lock_irq(&hp_sdc.rtq_lock);
- hp_sdc.rcurr = curridx;
+ hp_sdc.rcurr = curridx;
write_unlock_irq(&hp_sdc.rtq_lock);
hp_sdc_status_out8(postcmd);
goto finish;
@@ -519,75 +560,86 @@ unsigned long hp_sdc_put(void) {
goto actdone;
}
-actdone:
- if (act & HP_SDC_ACT_SEMAPHORE) {
+ actdone:
+ if (act & HP_SDC_ACT_SEMAPHORE)
up(curr->act.semaphore);
- }
- else if (act & HP_SDC_ACT_CALLBACK) {
+ else if (act & HP_SDC_ACT_CALLBACK)
curr->act.irqhook(0,NULL,0,0);
- }
+
if (curr->idx >= curr->endidx) { /* This transaction is over. */
- if (act & HP_SDC_ACT_DEALLOC) kfree(curr);
+ if (act & HP_SDC_ACT_DEALLOC)
+ kfree(curr);
hp_sdc.tq[curridx] = NULL;
- }
- else {
+ } else {
curr->actidx = idx + 1;
curr->idx = idx + 2;
}
/* Interleave outbound data between the transactions. */
hp_sdc.wcurr++;
- if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+ if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN)
+ hp_sdc.wcurr = 0;
finish:
- /* If by some quirk IBF has cleared and our ISR has run to
+ /* If by some quirk IBF has cleared and our ISR has run to
see that that has happened, do it all again. */
- if (!hp_sdc.ibf && limit++ < 20) goto anew;
+ if (!hp_sdc.ibf && limit++ < 20)
+ goto anew;
done:
- if (hp_sdc.wcurr >= 0) tasklet_schedule(&hp_sdc.task);
+ if (hp_sdc.wcurr >= 0)
+ tasklet_schedule(&hp_sdc.task);
write_unlock(&hp_sdc.lock);
+
return 0;
}
/******* Functions called in either user or kernel context ****/
-int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) {
- unsigned long flags;
+int __hp_sdc_enqueue_transaction(hp_sdc_transaction *this)
+{
int i;
if (this == NULL) {
- tasklet_schedule(&hp_sdc.task);
+ BUG();
return -EINVAL;
- };
-
- write_lock_irqsave(&hp_sdc.lock, flags);
+ }
/* Can't have same transaction on queue twice */
- for (i=0; i < HP_SDC_QUEUE_LEN; i++)
- if (hp_sdc.tq[i] == this) goto fail;
+ for (i = 0; i < HP_SDC_QUEUE_LEN; i++)
+ if (hp_sdc.tq[i] == this)
+ goto fail;
this->actidx = 0;
this->idx = 1;
/* Search for empty slot */
- for (i=0; i < HP_SDC_QUEUE_LEN; i++) {
+ for (i = 0; i < HP_SDC_QUEUE_LEN; i++)
if (hp_sdc.tq[i] == NULL) {
hp_sdc.tq[i] = this;
- write_unlock_irqrestore(&hp_sdc.lock, flags);
tasklet_schedule(&hp_sdc.task);
return 0;
}
- }
- write_unlock_irqrestore(&hp_sdc.lock, flags);
+
printk(KERN_WARNING PREFIX "No free slot to add transaction.\n");
return -EBUSY;
fail:
- write_unlock_irqrestore(&hp_sdc.lock,flags);
printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n");
return -EINVAL;
}
-int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) {
+int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) {
+ unsigned long flags;
+ int ret;
+
+ write_lock_irqsave(&hp_sdc.lock, flags);
+ ret = __hp_sdc_enqueue_transaction(this);
+ write_unlock_irqrestore(&hp_sdc.lock,flags);
+
+ return ret;
+}
+
+int hp_sdc_dequeue_transaction(hp_sdc_transaction *this)
+{
unsigned long flags;
int i;
@@ -595,8 +647,9 @@ int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) {
/* TODO: don't remove it if it's not done. */
- for (i=0; i < HP_SDC_QUEUE_LEN; i++)
- if (hp_sdc.tq[i] == this) hp_sdc.tq[i] = NULL;
+ for (i = 0; i < HP_SDC_QUEUE_LEN; i++)
+ if (hp_sdc.tq[i] == this)
+ hp_sdc.tq[i] = NULL;
write_unlock_irqrestore(&hp_sdc.lock, flags);
return 0;
@@ -605,11 +658,11 @@ int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) {
/********************** User context functions **************************/
-int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) {
-
- if (callback == NULL || hp_sdc.dev == NULL) {
+int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback)
+{
+ if (callback == NULL || hp_sdc.dev == NULL)
return -EINVAL;
- }
+
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.timer != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
@@ -629,11 +682,11 @@ int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) {
return 0;
}
-int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) {
-
- if (callback == NULL || hp_sdc.dev == NULL) {
+int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback)
+{
+ if (callback == NULL || hp_sdc.dev == NULL)
return -EINVAL;
- }
+
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.hil != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
@@ -650,11 +703,11 @@ int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) {
return 0;
}
-int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) {
-
- if (callback == NULL || hp_sdc.dev == NULL) {
+int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback)
+{
+ if (callback == NULL || hp_sdc.dev == NULL)
return -EINVAL;
- }
+
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.cooked != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
@@ -672,9 +725,8 @@ int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) {
return 0;
}
-int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) {
-
-
+int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback)
+{
write_lock_irq(&hp_sdc.hook_lock);
if ((callback != hp_sdc.timer) ||
(hp_sdc.timer == NULL)) {
@@ -694,8 +746,8 @@ int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) {
return 0;
}
-int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) {
-
+int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback)
+{
write_lock_irq(&hp_sdc.hook_lock);
if ((callback != hp_sdc.hil) ||
(hp_sdc.hil == NULL)) {
@@ -715,8 +767,8 @@ int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) {
return 0;
}
-int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) {
-
+int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback)
+{
write_lock_irq(&hp_sdc.hook_lock);
if ((callback != hp_sdc.cooked) ||
(hp_sdc.cooked == NULL)) {
@@ -738,7 +790,8 @@ int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) {
/************************* Keepalive timer task *********************/
-void hp_sdc_kicker (unsigned long data) {
+void hp_sdc_kicker (unsigned long data)
+{
tasklet_schedule(&hp_sdc.task);
/* Re-insert the periodic task. */
mod_timer(&hp_sdc.kicker, jiffies + HZ);
@@ -748,12 +801,12 @@ void hp_sdc_kicker (unsigned long data) {
#if defined(__hppa__)
-static struct parisc_device_id hp_sdc_tbl[] = {
+static const struct parisc_device_id hp_sdc_tbl[] = {
{
- .hw_type = HPHW_FIO,
+ .hw_type = HPHW_FIO,
.hversion_rev = HVERSION_REV_ANY_ID,
.hversion = HVERSION_ANY_ID,
- .sversion = 0x73,
+ .sversion = 0x73,
},
{ 0, }
};
@@ -772,16 +825,15 @@ static struct parisc_driver hp_sdc_driver = {
static int __init hp_sdc_init(void)
{
- int i;
char *errstr;
hp_sdc_transaction t_sync;
uint8_t ts_sync[6];
struct semaphore s_sync;
- rwlock_init(&hp_sdc.lock);
- rwlock_init(&hp_sdc.ibf_lock);
- rwlock_init(&hp_sdc.rtq_lock);
- rwlock_init(&hp_sdc.hook_lock);
+ rwlock_init(&hp_sdc.lock);
+ rwlock_init(&hp_sdc.ibf_lock);
+ rwlock_init(&hp_sdc.rtq_lock);
+ rwlock_init(&hp_sdc.hook_lock);
hp_sdc.timer = NULL;
hp_sdc.hil = NULL;
@@ -796,7 +848,8 @@ static int __init hp_sdc_init(void)
hp_sdc.r7[3] = 0xff;
hp_sdc.ibf = 1;
- for (i = 0; i < HP_SDC_QUEUE_LEN; i++) hp_sdc.tq[i] = NULL;
+ memset(&hp_sdc.tq, 0, sizeof(hp_sdc.tq));
+
hp_sdc.wcurr = -1;
hp_sdc.rcurr = -1;
hp_sdc.rqty = 0;
@@ -804,27 +857,32 @@ static int __init hp_sdc_init(void)
hp_sdc.dev_err = -ENODEV;
errstr = "IO not found for";
- if (!hp_sdc.base_io) goto err0;
+ if (!hp_sdc.base_io)
+ goto err0;
errstr = "IRQ not found for";
- if (!hp_sdc.irq) goto err0;
+ if (!hp_sdc.irq)
+ goto err0;
hp_sdc.dev_err = -EBUSY;
#if defined(__hppa__)
errstr = "IO not available for";
- if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) goto err0;
-#endif
+ if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name))
+ goto err0;
+#endif
errstr = "IRQ not available for";
- if(request_irq(hp_sdc.irq, &hp_sdc_isr, 0, "HP SDC",
- (void *) hp_sdc.base_io)) goto err1;
+ if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM,
+ "HP SDC", &hp_sdc))
+ goto err1;
errstr = "NMI not available for";
- if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, 0, "HP SDC NMI",
- (void *) hp_sdc.base_io)) goto err2;
+ if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, IRQF_SHARED,
+ "HP SDC NMI", &hp_sdc))
+ goto err2;
- printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n",
+ printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n",
(void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
hp_sdc_status_in8();
@@ -854,13 +912,14 @@ static int __init hp_sdc_init(void)
hp_sdc.dev_err = 0;
return 0;
err2:
- free_irq(hp_sdc.irq, NULL);
+ free_irq(hp_sdc.irq, &hp_sdc);
err1:
release_region(hp_sdc.data_io, 2);
err0:
- printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n",
+ printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n",
errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
hp_sdc.dev = NULL;
+
return hp_sdc.dev_err;
}
@@ -868,8 +927,10 @@ static int __init hp_sdc_init(void)
static int __init hp_sdc_init_hppa(struct parisc_device *d)
{
- if (!d) return 1;
- if (hp_sdc.dev != NULL) return 1; /* We only expect one SDC */
+ if (!d)
+ return 1;
+ if (hp_sdc.dev != NULL)
+ return 1; /* We only expect one SDC */
hp_sdc.dev = d;
hp_sdc.irq = d->irq;
@@ -898,18 +959,16 @@ static void hp_sdc_exit(void)
/* Wait until we know this has been processed by the i8042 */
hp_sdc_spin_ibf();
- free_irq(hp_sdc.nmi, NULL);
- free_irq(hp_sdc.irq, NULL);
+ free_irq(hp_sdc.nmi, &hp_sdc);
+ free_irq(hp_sdc.irq, &hp_sdc);
write_unlock_irq(&hp_sdc.lock);
del_timer(&hp_sdc.kicker);
tasklet_kill(&hp_sdc.task);
-/* release_region(hp_sdc.data_io, 2); */
-
#if defined(__hppa__)
- if (unregister_parisc_driver(&hp_sdc_driver))
+ if (unregister_parisc_driver(&hp_sdc_driver))
printk(KERN_WARNING PREFIX "Error unregistering HP SDC");
#endif
}
@@ -923,7 +982,7 @@ static int __init hp_sdc_register(void)
mm_segment_t fs;
unsigned char i;
#endif
-
+
hp_sdc.dev = NULL;
hp_sdc.dev_err = 0;
#if defined(__hppa__)
@@ -960,8 +1019,8 @@ static int __init hp_sdc_register(void)
tq_init.seq = tq_init_seq;
tq_init.act.semaphore = &tq_init_sem;
- tq_init_seq[0] =
- HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
+ tq_init_seq[0] =
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
tq_init_seq[1] = HP_SDC_CMD_READ_KCC;
tq_init_seq[2] = 1;
tq_init_seq[3] = 0;
@@ -979,13 +1038,13 @@ static int __init hp_sdc_register(void)
}
hp_sdc.r11 = tq_init_seq[4];
if (hp_sdc.r11 & HP_SDC_CFG_NEW) {
- char *str;
+ const char *str;
printk(KERN_INFO PREFIX "New style SDC\n");
tq_init_seq[1] = HP_SDC_CMD_READ_XTD;
tq_init.actidx = 0;
tq_init.idx = 1;
down(&tq_init_sem);
- hp_sdc_enqueue_transaction(&tq_init);
+ hp_sdc_enqueue_transaction(&tq_init);
down(&tq_init_sem);
up(&tq_init_sem);
if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) {
@@ -995,15 +1054,13 @@ static int __init hp_sdc_register(void)
hp_sdc.r7e = tq_init_seq[4];
HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str)
printk(KERN_INFO PREFIX "Revision: %s\n", str);
- if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) {
+ if (hp_sdc.r7e & HP_SDC_XTD_BEEPER)
printk(KERN_INFO PREFIX "TI SN76494 beeper present\n");
- }
- if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) {
+ if (hp_sdc.r7e & HP_SDC_XTD_BBRTC)
printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n");
- }
printk(KERN_INFO PREFIX "Spunking the self test register to force PUP "
"on next firmware reset.\n");
- tq_init_seq[0] = HP_SDC_ACT_PRECMD |
+ tq_init_seq[0] = HP_SDC_ACT_PRECMD |
HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
tq_init_seq[1] = HP_SDC_CMD_SET_STR;
tq_init_seq[2] = 1;
@@ -1012,14 +1069,12 @@ static int __init hp_sdc_register(void)
tq_init.idx = 1;
tq_init.endidx = 4;
down(&tq_init_sem);
- hp_sdc_enqueue_transaction(&tq_init);
+ hp_sdc_enqueue_transaction(&tq_init);
down(&tq_init_sem);
up(&tq_init_sem);
- }
- else {
- printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n",
+ } else
+ printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n",
(hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087");
- }
return 0;
}
@@ -1027,13 +1082,13 @@ static int __init hp_sdc_register(void)
module_init(hp_sdc_register);
module_exit(hp_sdc_exit);
-/* Timing notes: These measurements taken on my 64MHz 7100-LC (715/64)
+/* Timing notes: These measurements taken on my 64MHz 7100-LC (715/64)
* cycles cycles-adj time
* between two consecutive mfctl(16)'s: 4 n/a 63ns
* hp_sdc_spin_ibf when idle: 119 115 1.7us
* gsc_writeb status register: 83 79 1.2us
* IBF to clear after sending SET_IM: 6204 6006 93us
- * IBF to clear after sending LOAD_RT: 4467 4352 68us
+ * IBF to clear after sending LOAD_RT: 4467 4352 68us
* IBF to clear after sending two LOAD_RTs: 18974 18859 295us
* READ_T1, read status/data, IRQ, call handler: 35564 n/a 556us
* cmd to ~IBF READ_T1 2nd time right after: 5158403 n/a 81ms
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c
index aa4a8a4ccfd..c45ea74d53e 100644
--- a/drivers/input/serio/hp_sdc_mlc.c
+++ b/drivers/input/serio/hp_sdc_mlc.c
@@ -58,12 +58,13 @@ struct hp_sdc_mlc_priv_s {
} hp_sdc_mlc_priv;
/************************* Interrupt context ******************************/
-static void hp_sdc_mlc_isr (int irq, void *dev_id,
- uint8_t status, uint8_t data) {
- int idx;
+static void hp_sdc_mlc_isr (int irq, void *dev_id,
+ uint8_t status, uint8_t data)
+{
+ int idx;
hil_mlc *mlc = &hp_sdc_mlc;
- write_lock(&(mlc->lock));
+ write_lock(&mlc->lock);
if (mlc->icount < 0) {
printk(KERN_WARNING PREFIX "HIL Overflow!\n");
up(&mlc->isem);
@@ -73,239 +74,232 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id,
if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) {
mlc->ipacket[idx] |= data | HIL_ERR_INT;
mlc->icount--;
- if (hp_sdc_mlc_priv.got5x) goto check;
- if (!idx) goto check;
- if ((mlc->ipacket[idx-1] & HIL_PKT_ADDR_MASK) !=
+ if (hp_sdc_mlc_priv.got5x || !idx)
+ goto check;
+ if ((mlc->ipacket[idx - 1] & HIL_PKT_ADDR_MASK) !=
(mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) {
mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK;
- mlc->ipacket[idx] |= (mlc->ipacket[idx-1]
- & HIL_PKT_ADDR_MASK);
+ mlc->ipacket[idx] |= (mlc->ipacket[idx - 1]
+ & HIL_PKT_ADDR_MASK);
}
goto check;
}
/* We know status is 5X */
- if (data & HP_SDC_HIL_ISERR) goto err;
- mlc->ipacket[idx] =
+ if (data & HP_SDC_HIL_ISERR)
+ goto err;
+ mlc->ipacket[idx] =
(data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT;
hp_sdc_mlc_priv.got5x = 1;
goto out;
check:
hp_sdc_mlc_priv.got5x = 0;
- if (mlc->imatch == 0) goto done;
- if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
- && (mlc->ipacket[idx] == (mlc->imatch | idx))) goto done;
- if (mlc->ipacket[idx] == mlc->imatch) goto done;
+ if (mlc->imatch == 0)
+ goto done;
+ if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
+ && (mlc->ipacket[idx] == (mlc->imatch | idx)))
+ goto done;
+ if (mlc->ipacket[idx] == mlc->imatch)
+ goto done;
goto out;
- err:
+ err:
printk(KERN_DEBUG PREFIX "err code %x\n", data);
+
switch (data) {
case HP_SDC_HIL_RC_DONE:
printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n");
break;
+
case HP_SDC_HIL_ERR:
- mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR |
- HIL_ERR_FERR | HIL_ERR_FOF;
+ mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR |
+ HIL_ERR_FERR | HIL_ERR_FOF;
break;
+
case HP_SDC_HIL_TO:
mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR;
break;
+
case HP_SDC_HIL_RC:
printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n");
break;
+
default:
printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data);
break;
}
+
/* No more data will be coming due to an error. */
done:
tasklet_schedule(mlc->tasklet);
- up(&(mlc->isem));
+ up(&mlc->isem);
out:
- write_unlock(&(mlc->lock));
+ write_unlock(&mlc->lock);
}
/******************** Tasklet or userspace context functions ****************/
-static int hp_sdc_mlc_in (hil_mlc *mlc, suseconds_t timeout) {
- unsigned long flags;
+static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout)
+{
struct hp_sdc_mlc_priv_s *priv;
int rc = 2;
priv = mlc->priv;
- write_lock_irqsave(&(mlc->lock), flags);
-
/* Try to down the semaphore */
- if (down_trylock(&(mlc->isem))) {
+ if (down_trylock(&mlc->isem)) {
struct timeval tv;
if (priv->emtestmode) {
- mlc->ipacket[0] =
- HIL_ERR_INT | (mlc->opacket &
- (HIL_PKT_CMD |
- HIL_PKT_ADDR_MASK |
+ mlc->ipacket[0] =
+ HIL_ERR_INT | (mlc->opacket &
+ (HIL_PKT_CMD |
+ HIL_PKT_ADDR_MASK |
HIL_PKT_DATA_MASK));
mlc->icount = 14;
/* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */
goto wasup;
}
do_gettimeofday(&tv);
- tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec);
+ tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec);
if (tv.tv_usec - mlc->instart.tv_usec > mlc->intimeout) {
- /* printk("!%i %i",
- tv.tv_usec - mlc->instart.tv_usec,
- mlc->intimeout);
- */
+ /* printk("!%i %i",
+ tv.tv_usec - mlc->instart.tv_usec,
+ mlc->intimeout);
+ */
rc = 1;
- up(&(mlc->isem));
+ up(&mlc->isem);
}
goto done;
}
wasup:
- up(&(mlc->isem));
+ up(&mlc->isem);
rc = 0;
- goto done;
done:
- write_unlock_irqrestore(&(mlc->lock), flags);
return rc;
}
-static int hp_sdc_mlc_cts (hil_mlc *mlc) {
+static int hp_sdc_mlc_cts(hil_mlc *mlc)
+{
struct hp_sdc_mlc_priv_s *priv;
- unsigned long flags;
- priv = mlc->priv;
-
- write_lock_irqsave(&(mlc->lock), flags);
+ priv = mlc->priv;
/* Try to down the semaphores -- they should be up. */
- if (down_trylock(&(mlc->isem))) {
- BUG();
- goto busy;
- }
- if (down_trylock(&(mlc->osem))) {
- BUG();
- up(&(mlc->isem));
- goto busy;
- }
- up(&(mlc->isem));
- up(&(mlc->osem));
+ BUG_ON(down_trylock(&mlc->isem));
+ BUG_ON(down_trylock(&mlc->osem));
+
+ up(&mlc->isem);
+ up(&mlc->osem);
- if (down_trylock(&(mlc->csem))) {
- if (priv->trans.act.semaphore != &(mlc->csem)) goto poll;
- goto busy;
+ if (down_trylock(&mlc->csem)) {
+ if (priv->trans.act.semaphore != &mlc->csem)
+ goto poll;
+ else
+ goto busy;
}
- if (!(priv->tseq[4] & HP_SDC_USE_LOOP)) goto done;
+
+ if (!(priv->tseq[4] & HP_SDC_USE_LOOP))
+ goto done;
poll:
- priv->trans.act.semaphore = &(mlc->csem);
+ priv->trans.act.semaphore = &mlc->csem;
priv->trans.actidx = 0;
priv->trans.idx = 1;
priv->trans.endidx = 5;
- priv->tseq[0] =
+ priv->tseq[0] =
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
priv->tseq[1] = HP_SDC_CMD_READ_USE;
priv->tseq[2] = 1;
priv->tseq[3] = 0;
priv->tseq[4] = 0;
- hp_sdc_enqueue_transaction(&(priv->trans));
+ __hp_sdc_enqueue_transaction(&priv->trans);
busy:
- write_unlock_irqrestore(&(mlc->lock), flags);
return 1;
done:
- priv->trans.act.semaphore = &(mlc->osem);
- up(&(mlc->csem));
- write_unlock_irqrestore(&(mlc->lock), flags);
+ priv->trans.act.semaphore = &mlc->osem;
+ up(&mlc->csem);
return 0;
}
-static void hp_sdc_mlc_out (hil_mlc *mlc) {
+static void hp_sdc_mlc_out(hil_mlc *mlc)
+{
struct hp_sdc_mlc_priv_s *priv;
- unsigned long flags;
priv = mlc->priv;
- write_lock_irqsave(&(mlc->lock), flags);
-
/* Try to down the semaphore -- it should be up. */
- if (down_trylock(&(mlc->osem))) {
- BUG();
- goto done;
- }
+ BUG_ON(down_trylock(&mlc->osem));
- if (mlc->opacket & HIL_DO_ALTER_CTRL) goto do_control;
+ if (mlc->opacket & HIL_DO_ALTER_CTRL)
+ goto do_control;
do_data:
if (priv->emtestmode) {
- up(&(mlc->osem));
- goto done;
+ up(&mlc->osem);
+ return;
}
/* Shouldn't be sending commands when loop may be busy */
- if (down_trylock(&(mlc->csem))) {
- BUG();
- goto done;
- }
- up(&(mlc->csem));
+ BUG_ON(down_trylock(&mlc->csem));
+ up(&mlc->csem);
priv->trans.actidx = 0;
priv->trans.idx = 1;
- priv->trans.act.semaphore = &(mlc->osem);
+ priv->trans.act.semaphore = &mlc->osem;
priv->trans.endidx = 6;
- priv->tseq[0] =
+ priv->tseq[0] =
HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE;
priv->tseq[1] = 0x7;
- priv->tseq[2] =
- (mlc->opacket &
+ priv->tseq[2] =
+ (mlc->opacket &
(HIL_PKT_ADDR_MASK | HIL_PKT_CMD))
>> HIL_PKT_ADDR_SHIFT;
- priv->tseq[3] =
- (mlc->opacket & HIL_PKT_DATA_MASK)
+ priv->tseq[3] =
+ (mlc->opacket & HIL_PKT_DATA_MASK)
>> HIL_PKT_DATA_SHIFT;
priv->tseq[4] = 0; /* No timeout */
- if (priv->tseq[3] == HIL_CMD_DHR) priv->tseq[4] = 1;
+ if (priv->tseq[3] == HIL_CMD_DHR)
+ priv->tseq[4] = 1;
priv->tseq[5] = HP_SDC_CMD_DO_HIL;
goto enqueue;
do_control:
priv->emtestmode = mlc->opacket & HIL_CTRL_TEST;
-
+
/* we cannot emulate this, it should not be used. */
BUG_ON((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE);
-
- if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY) goto control_only;
- if (mlc->opacket & HIL_CTRL_APE) {
- BUG(); /* Should not send command/data after engaging APE */
- goto done;
- }
- /* Disengaging APE this way would not be valid either since
+
+ if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY)
+ goto control_only;
+
+ /* Should not send command/data after engaging APE */
+ BUG_ON(mlc->opacket & HIL_CTRL_APE);
+
+ /* Disengaging APE this way would not be valid either since
* the loop must be allowed to idle.
*
- * So, it works out that we really never actually send control
- * and data when using SDC, we just send the data.
+ * So, it works out that we really never actually send control
+ * and data when using SDC, we just send the data.
*/
goto do_data;
control_only:
priv->trans.actidx = 0;
priv->trans.idx = 1;
- priv->trans.act.semaphore = &(mlc->osem);
+ priv->trans.act.semaphore = &mlc->osem;
priv->trans.endidx = 4;
- priv->tseq[0] =
+ priv->tseq[0] =
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
priv->tseq[1] = HP_SDC_CMD_SET_LPC;
priv->tseq[2] = 1;
- // priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC;
+ /* priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC; */
priv->tseq[3] = 0;
if (mlc->opacket & HIL_CTRL_APE) {
priv->tseq[3] |= HP_SDC_LPC_APE_IPF;
- down_trylock(&(mlc->csem));
- }
+ down_trylock(&mlc->csem);
+ }
enqueue:
- hp_sdc_enqueue_transaction(&(priv->trans));
- done:
- write_unlock_irqrestore(&(mlc->lock), flags);
+ hp_sdc_enqueue_transaction(&priv->trans);
}
static int __init hp_sdc_mlc_init(void)
@@ -316,18 +310,18 @@ static int __init hp_sdc_mlc_init(void)
hp_sdc_mlc_priv.emtestmode = 0;
hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq;
- hp_sdc_mlc_priv.trans.act.semaphore = &(mlc->osem);
+ hp_sdc_mlc_priv.trans.act.semaphore = &mlc->osem;
hp_sdc_mlc_priv.got5x = 0;
- mlc->cts = &hp_sdc_mlc_cts;
- mlc->in = &hp_sdc_mlc_in;
- mlc->out = &hp_sdc_mlc_out;
+ mlc->cts = &hp_sdc_mlc_cts;
+ mlc->in = &hp_sdc_mlc_in;
+ mlc->out = &hp_sdc_mlc_out;
+ mlc->priv = &hp_sdc_mlc_priv;
if (hil_mlc_register(mlc)) {
printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
goto err0;
}
- mlc->priv = &hp_sdc_mlc_priv;
if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
@@ -335,10 +329,9 @@ static int __init hp_sdc_mlc_init(void)
}
return 0;
err1:
- if (hil_mlc_unregister(mlc)) {
+ if (hil_mlc_unregister(mlc))
printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
"This is bad. Could cause an oops.\n");
- }
err0:
return -EBUSY;
}
@@ -346,14 +339,14 @@ static int __init hp_sdc_mlc_init(void)
static void __exit hp_sdc_mlc_exit(void)
{
hil_mlc *mlc = &hp_sdc_mlc;
- if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr)) {
+
+ if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr))
printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n"
"This is bad. Could cause an oops.\n");
- }
- if (hil_mlc_unregister(mlc)) {
+
+ if (hil_mlc_unregister(mlc))
printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
"This is bad. Could cause an oops.\n");
- }
}
module_init(hp_sdc_mlc_init);
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index d36bd5475b6..6858bc58f0f 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -160,6 +160,28 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
},
},
{
+ /*
+ * No data is coming from the touchscreen unless KBC
+ * is in legacy mode.
+ */
+ .ident = "Panasonic CF-29",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
+ },
+ },
+ {
+ /*
+ * Errors on MUX ports are reported without raising AUXDATA
+ * causing "spurious NAK" messages.
+ */
+ .ident = "HP Pavilion DV4017EA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"),
+ },
+ },
+ {
.ident = "Toshiba P10",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
@@ -280,6 +302,8 @@ static struct pnp_driver i8042_pnp_kbd_driver = {
};
static struct pnp_device_id pnp_aux_devids[] = {
+ { .id = "FJC6000", .driver_data = 0 },
+ { .id = "FJC6001", .driver_data = 0 },
{ .id = "PNP0f03", .driver_data = 0 },
{ .id = "PNP0f0b", .driver_data = 0 },
{ .id = "PNP0f0e", .driver_data = 0 },
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index db9cca3b65e..3888dc307e0 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -526,6 +526,33 @@ static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/*
+ * i8042_toggle_aux - enables or disables AUX port on i8042 via command and
+ * verifies success by readinng CTR. Used when testing for presence of AUX
+ * port.
+ */
+static int __devinit i8042_toggle_aux(int on)
+{
+ unsigned char param;
+ int i;
+
+ if (i8042_command(&param,
+ on ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE))
+ return -1;
+
+ /* some chips need some time to set the I8042_CTR_AUXDIS bit */
+ for (i = 0; i < 100; i++) {
+ udelay(50);
+
+ if (i8042_command(&param, I8042_CMD_CTL_RCTR))
+ return -1;
+
+ if (!(param & I8042_CTR_AUXDIS) == on)
+ return 0;
+ }
+
+ return -1;
+}
/*
* i8042_check_aux() applies as much paranoia as it can at detecting
@@ -580,16 +607,12 @@ static int __devinit i8042_check_aux(void)
* Bit assignment test - filters out PS/2 i8042's in AT mode
*/
- if (i8042_command(&param, I8042_CMD_AUX_DISABLE))
- return -1;
- if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS)) {
+ if (i8042_toggle_aux(0)) {
printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n");
}
- if (i8042_command(&param, I8042_CMD_AUX_ENABLE))
- return -1;
- if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (param & I8042_CTR_AUXDIS))
+ if (i8042_toggle_aux(1))
return -1;
/*
@@ -768,6 +791,13 @@ static void i8042_controller_reset(void)
i8042_flush();
/*
+ * Disable both KBD and AUX interfaces so they don't get in the way
+ */
+
+ i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS;
+ i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT);
+
+/*
* Disable MUX mode if present.
*/
diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig
new file mode 100644
index 00000000000..12dfb0eb326
--- /dev/null
+++ b/drivers/input/tablet/Kconfig
@@ -0,0 +1,74 @@
+#
+# Tablet driver configuration
+#
+menuconfig INPUT_TABLET
+ bool "Tablets"
+ help
+ Say Y here, and a list of supported tablets will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_TABLET
+
+config TABLET_USB_ACECAD
+ tristate "Acecad Flair tablet support (USB)"
+ select USB
+ help
+ Say Y here if you want to use the USB version of the Acecad Flair
+ tablet. Make sure to say Y to "Mouse support"
+ (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+ (CONFIG_INPUT_EVDEV) as well.
+
+ To compile this driver as a module, choose M here: the
+ module will be called acecad.
+
+config TABLET_USB_AIPTEK
+ tristate "Aiptek 6000U/8000U tablet support (USB)"
+ select USB
+ help
+ Say Y here if you want to use the USB version of the Aiptek 6000U
+ or Aiptek 8000U tablet. Make sure to say Y to "Mouse support"
+ (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+ (CONFIG_INPUT_EVDEV) as well.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aiptek.
+
+config TABLET_USB_GTCO
+ tristate "GTCO CalComp/InterWrite USB Support"
+ depends on USB && INPUT
+ help
+ Say Y here if you want to use the USB version of the GTCO
+ CalComp/InterWrite Tablet. Make sure to say Y to "Mouse support"
+ (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+ (CONFIG_INPUT_EVDEV) as well.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gtco.
+
+config TABLET_USB_KBTAB
+ tristate "KB Gear JamStudio tablet support (USB)"
+ select USB
+ help
+ Say Y here if you want to use the USB version of the KB Gear
+ JamStudio tablet. Make sure to say Y to "Mouse support"
+ (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+ (CONFIG_INPUT_EVDEV) as well.
+
+ To compile this driver as a module, choose M here: the
+ module will be called kbtab.
+
+config TABLET_USB_WACOM
+ tristate "Wacom Intuos/Graphire tablet support (USB)"
+ select USB
+ help
+ 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.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wacom.
+
+endif
diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile
new file mode 100644
index 00000000000..ce8b9a9cfa4
--- /dev/null
+++ b/drivers/input/tablet/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the tablet drivers
+#
+
+# Multipart objects.
+wacom-objs := wacom_wac.o wacom_sys.o
+
+obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o
+obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o
+obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o
+obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
+obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o
diff --git a/drivers/input/tablet/acecad.c b/drivers/input/tablet/acecad.c
new file mode 100644
index 00000000000..dd2310458c4
--- /dev/null
+++ b/drivers/input/tablet/acecad.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr>
+ * Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr>
+ *
+ * USB Acecad "Acecad Flair" tablet support
+ *
+ * Changelog:
+ * v3.2 - Added sysfs support
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v3.2"
+#define DRIVER_DESC "USB Acecad Flair tablet driver"
+#define DRIVER_LICENSE "GPL"
+#define DRIVER_AUTHOR "Edouard TISSERANT <edouard.tisserant@wanadoo.fr>"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_ACECAD 0x0460
+#define USB_DEVICE_ID_FLAIR 0x0004
+#define USB_DEVICE_ID_302 0x0008
+
+struct usb_acecad {
+ char name[128];
+ char phys[64];
+ struct usb_device *usbdev;
+ struct input_dev *input;
+ struct urb *irq;
+
+ unsigned char *data;
+ dma_addr_t data_dma;
+};
+
+static void usb_acecad_irq(struct urb *urb)
+{
+ struct usb_acecad *acecad = urb->context;
+ unsigned char *data = acecad->data;
+ struct input_dev *dev = acecad->input;
+ int prox, status;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto resubmit;
+ }
+
+ prox = (data[0] & 0x04) >> 2;
+ input_report_key(dev, BTN_TOOL_PEN, prox);
+
+ if (prox) {
+ int x = data[1] | (data[2] << 8);
+ int y = data[3] | (data[4] << 8);
+ /* Pressure should compute the same way for flair and 302 */
+ int pressure = data[5] | (data[6] << 8);
+ int touch = data[0] & 0x01;
+ int stylus = (data[0] & 0x10) >> 4;
+ int stylus2 = (data[0] & 0x20) >> 5;
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_abs(dev, ABS_PRESSURE, pressure);
+ input_report_key(dev, BTN_TOUCH, touch);
+ input_report_key(dev, BTN_STYLUS, stylus);
+ input_report_key(dev, BTN_STYLUS2, stylus2);
+ }
+
+ /* event termination */
+ input_sync(dev);
+
+resubmit:
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status)
+ err("can't resubmit intr, %s-%s/input0, status %d",
+ acecad->usbdev->bus->bus_name, acecad->usbdev->devpath, status);
+}
+
+static int usb_acecad_open(struct input_dev *dev)
+{
+ struct usb_acecad *acecad = input_get_drvdata(dev);
+
+ acecad->irq->dev = acecad->usbdev;
+ if (usb_submit_urb(acecad->irq, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void usb_acecad_close(struct input_dev *dev)
+{
+ struct usb_acecad *acecad = input_get_drvdata(dev);
+
+ usb_kill_urb(acecad->irq);
+}
+
+static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface = intf->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_acecad *acecad;
+ struct input_dev *input_dev;
+ int pipe, maxp;
+ int err = -ENOMEM;
+
+ if (interface->desc.bNumEndpoints != 1)
+ return -ENODEV;
+
+ endpoint = &interface->endpoint[0].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -ENODEV;
+
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!acecad || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ acecad->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &acecad->data_dma);
+ if (!acecad->data) {
+ err= -ENOMEM;
+ goto fail1;
+ }
+
+ acecad->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!acecad->irq) {
+ err = -ENOMEM;
+ goto fail2;
+ }
+
+ acecad->usbdev = dev;
+ acecad->input = input_dev;
+
+ if (dev->manufacturer)
+ strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name));
+
+ if (dev->product) {
+ if (dev->manufacturer)
+ strlcat(acecad->name, " ", sizeof(acecad->name));
+ strlcat(acecad->name, dev->product, sizeof(acecad->name));
+ }
+
+ usb_make_path(dev, acecad->phys, sizeof(acecad->phys));
+ strlcat(acecad->phys, "/input0", sizeof(acecad->phys));
+
+ input_dev->name = acecad->name;
+ input_dev->phys = acecad->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, acecad);
+
+ input_dev->open = usb_acecad_open;
+ input_dev->close = usb_acecad_close;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+ input_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ input_dev->keybit[LONG(BTN_DIGI)] = BIT(BTN_TOOL_PEN) |BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
+
+ switch (id->driver_info) {
+ case 0:
+ input_dev->absmax[ABS_X] = 5000;
+ input_dev->absmax[ABS_Y] = 3750;
+ input_dev->absmax[ABS_PRESSURE] = 512;
+ if (!strlen(acecad->name))
+ snprintf(acecad->name, sizeof(acecad->name),
+ "USB Acecad Flair Tablet %04x:%04x",
+ le16_to_cpu(dev->descriptor.idVendor),
+ le16_to_cpu(dev->descriptor.idProduct));
+ break;
+ case 1:
+ input_dev->absmax[ABS_X] = 3000;
+ input_dev->absmax[ABS_Y] = 2250;
+ input_dev->absmax[ABS_PRESSURE] = 1024;
+ if (!strlen(acecad->name))
+ snprintf(acecad->name, sizeof(acecad->name),
+ "USB Acecad 302 Tablet %04x:%04x",
+ le16_to_cpu(dev->descriptor.idVendor),
+ le16_to_cpu(dev->descriptor.idProduct));
+ break;
+ }
+
+ input_dev->absfuzz[ABS_X] = 4;
+ input_dev->absfuzz[ABS_Y] = 4;
+
+ usb_fill_int_urb(acecad->irq, dev, pipe,
+ acecad->data, maxp > 8 ? 8 : maxp,
+ usb_acecad_irq, acecad, endpoint->bInterval);
+ acecad->irq->transfer_dma = acecad->data_dma;
+ acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ err = input_register_device(acecad->input);
+ if (err)
+ goto fail2;
+
+ usb_set_intfdata(intf, acecad);
+
+ return 0;
+
+ fail2: usb_buffer_free(dev, 8, acecad->data, acecad->data_dma);
+ fail1: input_free_device(input_dev);
+ kfree(acecad);
+ return err;
+}
+
+static void usb_acecad_disconnect(struct usb_interface *intf)
+{
+ struct usb_acecad *acecad = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (acecad) {
+ usb_kill_urb(acecad->irq);
+ input_unregister_device(acecad->input);
+ usb_free_urb(acecad->irq);
+ usb_buffer_free(interface_to_usbdev(intf), 10, acecad->data, acecad->data_dma);
+ kfree(acecad);
+ }
+}
+
+static struct usb_device_id usb_acecad_id_table [] = {
+ { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 },
+ { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, usb_acecad_id_table);
+
+static struct usb_driver usb_acecad_driver = {
+ .name = "usb_acecad",
+ .probe = usb_acecad_probe,
+ .disconnect = usb_acecad_disconnect,
+ .id_table = usb_acecad_id_table,
+};
+
+static int __init usb_acecad_init(void)
+{
+ int result = usb_register(&usb_acecad_driver);
+ if (result == 0)
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return result;
+}
+
+static void __exit usb_acecad_exit(void)
+{
+ usb_deregister(&usb_acecad_driver);
+}
+
+module_init(usb_acecad_init);
+module_exit(usb_acecad_exit);
diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
new file mode 100644
index 00000000000..cc0a498763d
--- /dev/null
+++ b/drivers/input/tablet/aiptek.c
@@ -0,0 +1,2236 @@
+/*
+ * Native support for the Aiptek HyperPen USB Tablets
+ * (4000U/5000U/6000U/8000U/12000U)
+ *
+ * Copyright (c) 2001 Chris Atenasio <chris@crud.net>
+ * Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net>
+ *
+ * based on wacom.c by
+ * Vojtech Pavlik <vojtech@suse.cz>
+ * Andreas Bach Aaen <abach@stofanet.dk>
+ * Clifford Wolf <clifford@clifford.at>
+ * Sam Mosel <sam.mosel@computer.org>
+ * James E. Blair <corvus@gnu.org>
+ * Daniel Egger <egger@suse.de>
+ *
+ * Many thanks to Oliver Kuechemann for his support.
+ *
+ * ChangeLog:
+ * v0.1 - Initial release
+ * v0.2 - Hack to get around fake event 28's. (Bryan W. Headley)
+ * v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002)
+ * Released to Linux 2.4.19 and 2.5.x
+ * v0.4 - Rewrote substantial portions of the code to deal with
+ * corrected control sequences, timing, dynamic configuration,
+ * support of 6000U - 12000U, procfs, and macro key support
+ * (Jan-1-2003 - Feb-5-2003, Bryan W. Headley)
+ * v1.0 - Added support for diagnostic messages, count of messages
+ * received from URB - Mar-8-2003, Bryan W. Headley
+ * v1.1 - added support for tablet resolution, changed DV and proximity
+ * some corrections - Jun-22-2003, martin schneebacher
+ * - Added support for the sysfs interface, deprecating the
+ * procfs interface for 2.5.x kernel. Also added support for
+ * Wheel command. Bryan W. Headley July-15-2003.
+ * v1.2 - Reworked jitter timer as a kernel thread.
+ * Bryan W. Headley November-28-2003/Jan-10-2004.
+ * v1.3 - Repaired issue of kernel thread going nuts on single-processor
+ * machines, introduced programmableDelay as a command line
+ * parameter. Feb 7 2004, Bryan W. Headley.
+ * v1.4 - Re-wire jitter so it does not require a thread. Courtesy of
+ * Rene van Paassen. Added reporting of physical pointer device
+ * (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know
+ * for reports 1, 6.)
+ * what physical device reports for reports 1, 6.) Also enabled
+ * MOUSE and LENS tool button modes. Renamed "rubber" to "eraser".
+ * Feb 20, 2004, Bryan W. Headley.
+ * v1.5 - Added previousJitterable, so we don't do jitter delay when the
+ * user is holding a button down for periods of time.
+ *
+ * NOTE:
+ * This kernel driver is augmented by the "Aiptek" XFree86 input
+ * driver for your X server, as well as the Gaiptek GUI Front-end
+ * "Tablet Manager".
+ * These three products are highly interactive with one another,
+ * so therefore it's easier to document them all as one subsystem.
+ * Please visit the project's "home page", located at,
+ * http://aiptektablet.sourceforge.net.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.5 (May-15-2004)"
+#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio"
+#define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)"
+
+/*
+ * Aiptek status packet:
+ *
+ * (returned as Report 1 - relative coordinates from mouse and stylus)
+ *
+ * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
+ * byte0 0 0 0 0 0 0 0 1
+ * byte1 0 0 0 0 0 BS2 BS Tip
+ * byte2 X7 X6 X5 X4 X3 X2 X1 X0
+ * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
+ *
+ * (returned as Report 2 - absolute coordinates from the stylus)
+ *
+ * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
+ * byte0 0 0 0 0 0 0 1 0
+ * byte1 X7 X6 X5 X4 X3 X2 X1 X0
+ * byte2 X15 X14 X13 X12 X11 X10 X9 X8
+ * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
+ * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8
+ * byte5 * * * BS2 BS1 Tip IR DV
+ * byte6 P7 P6 P5 P4 P3 P2 P1 P0
+ * byte7 P15 P14 P13 P12 P11 P10 P9 P8
+ *
+ * (returned as Report 3 - absolute coordinates from the mouse)
+ *
+ * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
+ * byte0 0 0 0 0 0 0 1 0
+ * byte1 X7 X6 X5 X4 X3 X2 X1 X0
+ * byte2 X15 X14 X13 X12 X11 X10 X9 X8
+ * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
+ * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8
+ * byte5 * * * BS2 BS1 Tip IR DV
+ * byte6 P7 P6 P5 P4 P3 P2 P1 P0
+ * byte7 P15 P14 P13 P12 P11 P10 P9 P8
+ *
+ * (returned as Report 4 - macrokeys from the stylus)
+ *
+ * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
+ * byte0 0 0 0 0 0 1 0 0
+ * byte1 0 0 0 BS2 BS Tip IR DV
+ * byte2 0 0 0 0 0 0 1 0
+ * byte3 0 0 0 K4 K3 K2 K1 K0
+ * byte4 P7 P6 P5 P4 P3 P2 P1 P0
+ * byte5 P15 P14 P13 P12 P11 P10 P9 P8
+ *
+ * (returned as Report 5 - macrokeys from the mouse)
+ *
+ * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
+ * byte0 0 0 0 0 0 1 0 0
+ * byte1 0 0 0 BS2 BS Tip IR DV
+ * byte2 0 0 0 0 0 0 1 0
+ * byte3 0 0 0 K4 K3 K2 K1 K0
+ * byte4 P7 P6 P5 P4 P3 P2 P1 P0
+ * byte5 P15 P14 P13 P12 P11 P10 P9 P8
+ *
+ * IR: In Range = Proximity on
+ * DV = Data Valid
+ * BS = Barrel Switch (as in, macro keys)
+ * BS2 also referred to as Tablet Pick
+ *
+ * Command Summary:
+ *
+ * Use report_type CONTROL (3)
+ * Use report_id 2
+ *
+ * Command/Data Description Return Bytes Return Value
+ * 0x10/0x00 SwitchToMouse 0
+ * 0x10/0x01 SwitchToTablet 0
+ * 0x18/0x04 SetResolution 0
+ * 0x12/0xFF AutoGainOn 0
+ * 0x17/0x00 FilterOn 0
+ * 0x01/0x00 GetXExtension 2 MaxX
+ * 0x01/0x01 GetYExtension 2 MaxY
+ * 0x02/0x00 GetModelCode 2 ModelCode = LOBYTE
+ * 0x03/0x00 GetODMCode 2 ODMCode
+ * 0x08/0x00 GetPressureLevels 2 =512
+ * 0x04/0x00 GetFirmwareVersion 2 Firmware Version
+ * 0x11/0x02 EnableMacroKeys 0
+ *
+ * To initialize the tablet:
+ *
+ * (1) Send Resolution500LPI (Command)
+ * (2) Query for Model code (Option Report)
+ * (3) Query for ODM code (Option Report)
+ * (4) Query for firmware (Option Report)
+ * (5) Query for GetXExtension (Option Report)
+ * (6) Query for GetYExtension (Option Report)
+ * (7) Query for GetPressureLevels (Option Report)
+ * (8) SwitchToTablet for Absolute coordinates, or
+ * SwitchToMouse for Relative coordinates (Command)
+ * (9) EnableMacroKeys (Command)
+ * (10) FilterOn (Command)
+ * (11) AutoGainOn (Command)
+ *
+ * (Step 9 can be omitted, but you'll then have no function keys.)
+ */
+
+#define USB_VENDOR_ID_AIPTEK 0x08ca
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_SET_REPORT 0x09
+
+ /* PointerMode codes
+ */
+#define AIPTEK_POINTER_ONLY_MOUSE_MODE 0
+#define AIPTEK_POINTER_ONLY_STYLUS_MODE 1
+#define AIPTEK_POINTER_EITHER_MODE 2
+
+#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a) \
+ (a == AIPTEK_POINTER_ONLY_MOUSE_MODE || \
+ a == AIPTEK_POINTER_EITHER_MODE)
+#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a) \
+ (a == AIPTEK_POINTER_ONLY_STYLUS_MODE || \
+ a == AIPTEK_POINTER_EITHER_MODE)
+
+ /* CoordinateMode code
+ */
+#define AIPTEK_COORDINATE_RELATIVE_MODE 0
+#define AIPTEK_COORDINATE_ABSOLUTE_MODE 1
+
+ /* XTilt and YTilt values
+ */
+#define AIPTEK_TILT_MIN (-128)
+#define AIPTEK_TILT_MAX 127
+#define AIPTEK_TILT_DISABLE (-10101)
+
+ /* Wheel values
+ */
+#define AIPTEK_WHEEL_MIN 0
+#define AIPTEK_WHEEL_MAX 1024
+#define AIPTEK_WHEEL_DISABLE (-10101)
+
+ /* ToolCode values, which BTW are 0x140 .. 0x14f
+ * We have things set up such that if TOOL_BUTTON_FIRED_BIT is
+ * not set, we'll send one instance of AIPTEK_TOOL_BUTTON_xxx.
+ *
+ * Whenever the user resets the value, TOOL_BUTTON_FIRED_BIT will
+ * get reset.
+ */
+#define TOOL_BUTTON(x) ((x) & 0x14f)
+#define TOOL_BUTTON_FIRED(x) ((x) & 0x200)
+#define TOOL_BUTTON_FIRED_BIT 0x200
+ /* toolMode codes
+ */
+#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN
+#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN
+#define AIPTEK_TOOL_BUTTON_PENCIL_MODE BTN_TOOL_PENCIL
+#define AIPTEK_TOOL_BUTTON_BRUSH_MODE BTN_TOOL_BRUSH
+#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE BTN_TOOL_AIRBRUSH
+#define AIPTEK_TOOL_BUTTON_ERASER_MODE BTN_TOOL_RUBBER
+#define AIPTEK_TOOL_BUTTON_MOUSE_MODE BTN_TOOL_MOUSE
+#define AIPTEK_TOOL_BUTTON_LENS_MODE BTN_TOOL_LENS
+
+ /* Diagnostic message codes
+ */
+#define AIPTEK_DIAGNOSTIC_NA 0
+#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE 1
+#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2
+#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3
+
+ /* Time to wait (in ms) to help mask hand jittering
+ * when pressing the stylus buttons.
+ */
+#define AIPTEK_JITTER_DELAY_DEFAULT 50
+
+ /* Time to wait (in ms) in-between sending the tablet
+ * a command and beginning the process of reading the return
+ * sequence from the tablet.
+ */
+#define AIPTEK_PROGRAMMABLE_DELAY_25 25
+#define AIPTEK_PROGRAMMABLE_DELAY_50 50
+#define AIPTEK_PROGRAMMABLE_DELAY_100 100
+#define AIPTEK_PROGRAMMABLE_DELAY_200 200
+#define AIPTEK_PROGRAMMABLE_DELAY_300 300
+#define AIPTEK_PROGRAMMABLE_DELAY_400 400
+#define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT AIPTEK_PROGRAMMABLE_DELAY_400
+
+ /* Mouse button programming
+ */
+#define AIPTEK_MOUSE_LEFT_BUTTON 0x01
+#define AIPTEK_MOUSE_RIGHT_BUTTON 0x02
+#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x04
+
+ /* Stylus button programming
+ */
+#define AIPTEK_STYLUS_LOWER_BUTTON 0x08
+#define AIPTEK_STYLUS_UPPER_BUTTON 0x10
+
+ /* Length of incoming packet from the tablet
+ */
+#define AIPTEK_PACKET_LENGTH 8
+
+ /* We report in EV_MISC both the proximity and
+ * whether the report came from the stylus, tablet mouse
+ * or "unknown" -- Unknown when the tablet is in relative
+ * mode, because we only get report 1's.
+ */
+#define AIPTEK_REPORT_TOOL_UNKNOWN 0x10
+#define AIPTEK_REPORT_TOOL_STYLUS 0x20
+#define AIPTEK_REPORT_TOOL_MOUSE 0x40
+
+static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT;
+static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT;
+
+struct aiptek_features {
+ int odmCode; /* Tablet manufacturer code */
+ int modelCode; /* Tablet model code (not unique) */
+ int firmwareCode; /* prom/eeprom version */
+ char usbPath[64 + 1]; /* device's physical usb path */
+ char inputPath[64 + 1]; /* input device path */
+};
+
+struct aiptek_settings {
+ int pointerMode; /* stylus-, mouse-only or either */
+ int coordinateMode; /* absolute/relative coords */
+ int toolMode; /* pen, pencil, brush, etc. tool */
+ int xTilt; /* synthetic xTilt amount */
+ int yTilt; /* synthetic yTilt amount */
+ int wheel; /* synthetic wheel amount */
+ int stylusButtonUpper; /* stylus upper btn delivers... */
+ int stylusButtonLower; /* stylus lower btn delivers... */
+ int mouseButtonLeft; /* mouse left btn delivers... */
+ int mouseButtonMiddle; /* mouse middle btn delivers... */
+ int mouseButtonRight; /* mouse right btn delivers... */
+ int programmableDelay; /* delay for tablet programming */
+ int jitterDelay; /* delay for hand jittering */
+};
+
+struct aiptek {
+ struct input_dev *inputdev; /* input device struct */
+ struct usb_device *usbdev; /* usb device struct */
+ struct urb *urb; /* urb for incoming reports */
+ dma_addr_t data_dma; /* our dma stuffage */
+ struct aiptek_features features; /* tablet's array of features */
+ struct aiptek_settings curSetting; /* tablet's current programmable */
+ struct aiptek_settings newSetting; /* ... and new param settings */
+ unsigned int ifnum; /* interface number for IO */
+ int diagnostic; /* tablet diagnostic codes */
+ unsigned long eventCount; /* event count */
+ int inDelay; /* jitter: in jitter delay? */
+ unsigned long endDelay; /* jitter: time when delay ends */
+ int previousJitterable; /* jitterable prev value */
+ unsigned char *data; /* incoming packet data */
+};
+
+/*
+ * Permit easy lookup of keyboard events to send, versus
+ * the bitmap which comes from the tablet. This hides the
+ * issue that the F_keys are not sequentially numbered.
+ */
+static const int macroKeyEvents[] = {
+ KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
+ KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11,
+ KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17,
+ KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23,
+ KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO,
+ KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0
+};
+
+/***********************************************************************
+ * Relative reports deliver values in 2's complement format to
+ * deal with negative offsets.
+ */
+static int aiptek_convert_from_2s_complement(unsigned char c)
+{
+ int ret;
+ unsigned char b = c;
+ int negate = 0;
+
+ if ((b & 0x80) != 0) {
+ b = ~b;
+ b--;
+ negate = 1;
+ }
+ ret = b;
+ ret = (negate == 1) ? -ret : ret;
+ return ret;
+}
+
+/***********************************************************************
+ * aiptek_irq can receive one of six potential reports.
+ * The documentation for each is in the body of the function.
+ *
+ * The tablet reports on several attributes per invocation of
+ * aiptek_irq. Because the Linux Input Event system allows the
+ * transmission of ONE attribute per input_report_xxx() call,
+ * collation has to be done on the other end to reconstitute
+ * a complete tablet report. Further, the number of Input Event reports
+ * submitted varies, depending on what USB report type, and circumstance.
+ * To deal with this, EV_MSC is used to indicate an 'end-of-report'
+ * message. This has been an undocumented convention understood by the kernel
+ * tablet driver and clients such as gpm and XFree86's tablet drivers.
+ *
+ * Of the information received from the tablet, the one piece I
+ * cannot transmit is the proximity bit (without resorting to an EV_MSC
+ * convention above.) I therefore have taken over REL_MISC and ABS_MISC
+ * (for relative and absolute reports, respectively) for communicating
+ * Proximity. Why two events? I thought it interesting to know if the
+ * Proximity event occurred while the tablet was in absolute or relative
+ * mode.
+ *
+ * Other tablets use the notion of a certain minimum stylus pressure
+ * to infer proximity. While that could have been done, that is yet
+ * another 'by convention' behavior, the documentation for which
+ * would be spread between two (or more) pieces of software.
+ *
+ * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and
+ * replaced with the input_sync() method (which emits EV_SYN.)
+ */
+
+static void aiptek_irq(struct urb *urb)
+{
+ struct aiptek *aiptek = urb->context;
+ unsigned char *data = aiptek->data;
+ struct input_dev *inputdev = aiptek->inputdev;
+ int jitterable = 0;
+ int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck;
+
+ switch (urb->status) {
+ case 0:
+ /* Success */
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ return;
+
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ /* See if we are in a delay loop -- throw out report if true.
+ */
+ if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) {
+ goto exit;
+ }
+
+ aiptek->inDelay = 0;
+ aiptek->eventCount++;
+
+ /* Report 1 delivers relative coordinates with either a stylus
+ * or the mouse. You do not know, however, which input
+ * tool generated the event.
+ */
+ if (data[0] == 1) {
+ if (aiptek->curSetting.coordinateMode ==
+ AIPTEK_COORDINATE_ABSOLUTE_MODE) {
+ aiptek->diagnostic =
+ AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE;
+ } else {
+ x = aiptek_convert_from_2s_complement(data[2]);
+ y = aiptek_convert_from_2s_complement(data[3]);
+
+ /* jitterable keeps track of whether any button has been pressed.
+ * We're also using it to remap the physical mouse button mask
+ * to pseudo-settings. (We don't specifically care about it's
+ * value after moving/transposing mouse button bitmasks, except
+ * that a non-zero value indicates that one or more
+ * mouse button was pressed.)
+ */
+ jitterable = data[5] & 0x07;
+
+ left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
+ right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
+ middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+
+ input_report_key(inputdev, BTN_LEFT, left);
+ input_report_key(inputdev, BTN_MIDDLE, middle);
+ input_report_key(inputdev, BTN_RIGHT, right);
+ input_report_rel(inputdev, REL_X, x);
+ input_report_rel(inputdev, REL_Y, y);
+ input_report_rel(inputdev, REL_MISC, 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
+
+ /* Wheel support is in the form of a single-event
+ * firing.
+ */
+ if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
+ input_report_rel(inputdev, REL_WHEEL,
+ aiptek->curSetting.wheel);
+ aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+ }
+ input_sync(inputdev);
+ }
+ }
+ /* Report 2 is delivered only by the stylus, and delivers
+ * absolute coordinates.
+ */
+ else if (data[0] == 2) {
+ if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
+ aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
+ } else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE
+ (aiptek->curSetting.pointerMode)) {
+ aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
+ } else {
+ x = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
+ y = le16_to_cpu(get_unaligned((__le16 *) (data + 3)));
+ z = le16_to_cpu(get_unaligned((__le16 *) (data + 6)));
+
+ p = (data[5] & 0x01) != 0 ? 1 : 0;
+ dv = (data[5] & 0x02) != 0 ? 1 : 0;
+ tip = (data[5] & 0x04) != 0 ? 1 : 0;
+
+ /* Use jitterable to re-arrange button masks
+ */
+ jitterable = data[5] & 0x18;
+
+ bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
+ pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
+
+ /* dv indicates 'data valid' (e.g., the tablet is in sync
+ * and has delivered a "correct" report) We will ignore
+ * all 'bad' reports...
+ */
+ if (dv != 0) {
+ /* If we've not already sent a tool_button_?? code, do
+ * so now. Then set FIRED_BIT so it won't be resent unless
+ * the user forces FIRED_BIT off.
+ */
+ if (TOOL_BUTTON_FIRED
+ (aiptek->curSetting.toolMode) == 0) {
+ input_report_key(inputdev,
+ TOOL_BUTTON(aiptek->curSetting.toolMode),
+ 1);
+ aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ }
+
+ if (p != 0) {
+ input_report_abs(inputdev, ABS_X, x);
+ input_report_abs(inputdev, ABS_Y, y);
+ input_report_abs(inputdev, ABS_PRESSURE, z);
+
+ input_report_key(inputdev, BTN_TOUCH, tip);
+ input_report_key(inputdev, BTN_STYLUS, bs);
+ input_report_key(inputdev, BTN_STYLUS2, pck);
+
+ if (aiptek->curSetting.xTilt !=
+ AIPTEK_TILT_DISABLE) {
+ input_report_abs(inputdev,
+ ABS_TILT_X,
+ aiptek->curSetting.xTilt);
+ }
+ if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) {
+ input_report_abs(inputdev,
+ ABS_TILT_Y,
+ aiptek->curSetting.yTilt);
+ }
+
+ /* Wheel support is in the form of a single-event
+ * firing.
+ */
+ if (aiptek->curSetting.wheel !=
+ AIPTEK_WHEEL_DISABLE) {
+ input_report_abs(inputdev,
+ ABS_WHEEL,
+ aiptek->curSetting.wheel);
+ aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+ }
+ }
+ input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS);
+ input_sync(inputdev);
+ }
+ }
+ }
+ /* Report 3's come from the mouse in absolute mode.
+ */
+ else if (data[0] == 3) {
+ if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
+ aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
+ } else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE
+ (aiptek->curSetting.pointerMode)) {
+ aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
+ } else {
+ x = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
+ y = le16_to_cpu(get_unaligned((__le16 *) (data + 3)));
+
+ jitterable = data[5] & 0x1c;
+
+ p = (data[5] & 0x01) != 0 ? 1 : 0;
+ dv = (data[5] & 0x02) != 0 ? 1 : 0;
+ left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
+ right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
+ middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+
+ if (dv != 0) {
+ /* If we've not already sent a tool_button_?? code, do
+ * so now. Then set FIRED_BIT so it won't be resent unless
+ * the user forces FIRED_BIT off.
+ */
+ if (TOOL_BUTTON_FIRED
+ (aiptek->curSetting.toolMode) == 0) {
+ input_report_key(inputdev,
+ TOOL_BUTTON(aiptek->curSetting.toolMode),
+ 1);
+ aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ }
+
+ if (p != 0) {
+ input_report_abs(inputdev, ABS_X, x);
+ input_report_abs(inputdev, ABS_Y, y);
+
+ input_report_key(inputdev, BTN_LEFT, left);
+ input_report_key(inputdev, BTN_MIDDLE, middle);
+ input_report_key(inputdev, BTN_RIGHT, right);
+
+ /* Wheel support is in the form of a single-event
+ * firing.
+ */
+ if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
+ input_report_abs(inputdev,
+ ABS_WHEEL,
+ aiptek->curSetting.wheel);
+ aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+ }
+ }
+ input_report_rel(inputdev, REL_MISC, p | AIPTEK_REPORT_TOOL_MOUSE);
+ input_sync(inputdev);
+ }
+ }
+ }
+ /* Report 4s come from the macro keys when pressed by stylus
+ */
+ else if (data[0] == 4) {
+ jitterable = data[1] & 0x18;
+
+ p = (data[1] & 0x01) != 0 ? 1 : 0;
+ dv = (data[1] & 0x02) != 0 ? 1 : 0;
+ tip = (data[1] & 0x04) != 0 ? 1 : 0;
+ bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
+ pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
+
+ macro = data[3];
+ z = le16_to_cpu(get_unaligned((__le16 *) (data + 4)));
+
+ if (dv != 0) {
+ /* If we've not already sent a tool_button_?? code, do
+ * so now. Then set FIRED_BIT so it won't be resent unless
+ * the user forces FIRED_BIT off.
+ */
+ if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
+ input_report_key(inputdev,
+ TOOL_BUTTON(aiptek->curSetting.toolMode),
+ 1);
+ aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ }
+
+ if (p != 0) {
+ input_report_key(inputdev, BTN_TOUCH, tip);
+ input_report_key(inputdev, BTN_STYLUS, bs);
+ input_report_key(inputdev, BTN_STYLUS2, pck);
+ input_report_abs(inputdev, ABS_PRESSURE, z);
+ }
+
+ /* For safety, we're sending key 'break' codes for the
+ * neighboring macro keys.
+ */
+ if (macro > 0) {
+ input_report_key(inputdev,
+ macroKeyEvents[macro - 1], 0);
+ }
+ if (macro < 25) {
+ input_report_key(inputdev,
+ macroKeyEvents[macro + 1], 0);
+ }
+ input_report_key(inputdev, macroKeyEvents[macro], p);
+ input_report_abs(inputdev, ABS_MISC,
+ p | AIPTEK_REPORT_TOOL_STYLUS);
+ input_sync(inputdev);
+ }
+ }
+ /* Report 5s come from the macro keys when pressed by mouse
+ */
+ else if (data[0] == 5) {
+ jitterable = data[1] & 0x1c;
+
+ p = (data[1] & 0x01) != 0 ? 1 : 0;
+ dv = (data[1] & 0x02) != 0 ? 1 : 0;
+ left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
+ right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
+ middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+ macro = data[3];
+
+ if (dv != 0) {
+ /* If we've not already sent a tool_button_?? code, do
+ * so now. Then set FIRED_BIT so it won't be resent unless
+ * the user forces FIRED_BIT off.
+ */
+ if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
+ input_report_key(inputdev,
+ TOOL_BUTTON(aiptek->curSetting.toolMode),
+ 1);
+ aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ }
+
+ if (p != 0) {
+ input_report_key(inputdev, BTN_LEFT, left);
+ input_report_key(inputdev, BTN_MIDDLE, middle);
+ input_report_key(inputdev, BTN_RIGHT, right);
+ }
+
+ /* For safety, we're sending key 'break' codes for the
+ * neighboring macro keys.
+ */
+ if (macro > 0) {
+ input_report_key(inputdev,
+ macroKeyEvents[macro - 1], 0);
+ }
+ if (macro < 25) {
+ input_report_key(inputdev,
+ macroKeyEvents[macro + 1], 0);
+ }
+
+ input_report_key(inputdev, macroKeyEvents[macro], 1);
+ input_report_rel(inputdev, ABS_MISC,
+ p | AIPTEK_REPORT_TOOL_MOUSE);
+ input_sync(inputdev);
+ }
+ }
+ /* We have no idea which tool can generate a report 6. Theoretically,
+ * neither need to, having been given reports 4 & 5 for such use.
+ * However, report 6 is the 'official-looking' report for macroKeys;
+ * reports 4 & 5 supposively are used to support unnamed, unknown
+ * hat switches (which just so happen to be the macroKeys.)
+ */
+ else if (data[0] == 6) {
+ macro = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
+ if (macro > 0) {
+ input_report_key(inputdev, macroKeyEvents[macro - 1],
+ 0);
+ }
+ if (macro < 25) {
+ input_report_key(inputdev, macroKeyEvents[macro + 1],
+ 0);
+ }
+
+ /* If we've not already sent a tool_button_?? code, do
+ * so now. Then set FIRED_BIT so it won't be resent unless
+ * the user forces FIRED_BIT off.
+ */
+ if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
+ input_report_key(inputdev,
+ TOOL_BUTTON(aiptek->curSetting.
+ toolMode), 1);
+ aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+ }
+
+ input_report_key(inputdev, macroKeyEvents[macro], 1);
+ input_report_abs(inputdev, ABS_MISC,
+ 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
+ input_sync(inputdev);
+ } else {
+ dbg("Unknown report %d", data[0]);
+ }
+
+ /* Jitter may occur when the user presses a button on the stlyus
+ * or the mouse. What we do to prevent that is wait 'x' milliseconds
+ * following a 'jitterable' event, which should give the hand some time
+ * stabilize itself.
+ *
+ * We just introduced aiptek->previousJitterable to carry forth the
+ * notion that jitter occurs when the button state changes from on to off:
+ * a person drawing, holding a button down is not subject to jittering.
+ * With that in mind, changing from upper button depressed to lower button
+ * WILL transition through a jitter delay.
+ */
+
+ if (aiptek->previousJitterable != jitterable &&
+ aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) {
+ aiptek->endDelay = jiffies +
+ ((aiptek->curSetting.jitterDelay * HZ) / 1000);
+ aiptek->inDelay = 1;
+ }
+ aiptek->previousJitterable = jitterable;
+
+exit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval != 0) {
+ err("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+ }
+}
+
+/***********************************************************************
+ * These are the USB id's known so far. We do not identify them to
+ * specific Aiptek model numbers, because there has been overlaps,
+ * use, and reuse of id's in existing models. Certain models have
+ * been known to use more than one ID, indicative perhaps of
+ * manufacturing revisions. In any event, we consider these
+ * IDs to not be model-specific nor unique.
+ */
+static const struct usb_device_id aiptek_ids[] = {
+ {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)},
+ {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)},
+ {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)},
+ {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)},
+ {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)},
+ {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)},
+ {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, aiptek_ids);
+
+/***********************************************************************
+ * Open an instance of the tablet driver.
+ */
+static int aiptek_open(struct input_dev *inputdev)
+{
+ struct aiptek *aiptek = input_get_drvdata(inputdev);
+
+ aiptek->urb->dev = aiptek->usbdev;
+ if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0)
+ return -EIO;
+
+ return 0;
+}
+
+/***********************************************************************
+ * Close an instance of the tablet driver.
+ */
+static void aiptek_close(struct input_dev *inputdev)
+{
+ struct aiptek *aiptek = input_get_drvdata(inputdev);
+
+ usb_kill_urb(aiptek->urb);
+}
+
+/***********************************************************************
+ * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x,
+ * where they were known as usb_set_report and usb_get_report.
+ */
+static int
+aiptek_set_report(struct aiptek *aiptek,
+ unsigned char report_type,
+ unsigned char report_id, void *buffer, int size)
+{
+ return usb_control_msg(aiptek->usbdev,
+ usb_sndctrlpipe(aiptek->usbdev, 0),
+ USB_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+ USB_DIR_OUT, (report_type << 8) + report_id,
+ aiptek->ifnum, buffer, size, 5000);
+}
+
+static int
+aiptek_get_report(struct aiptek *aiptek,
+ unsigned char report_type,
+ unsigned char report_id, void *buffer, int size)
+{
+ return usb_control_msg(aiptek->usbdev,
+ usb_rcvctrlpipe(aiptek->usbdev, 0),
+ USB_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+ USB_DIR_IN, (report_type << 8) + report_id,
+ aiptek->ifnum, buffer, size, 5000);
+}
+
+/***********************************************************************
+ * Send a command to the tablet.
+ */
+static int
+aiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data)
+{
+ const int sizeof_buf = 3 * sizeof(u8);
+ int ret;
+ u8 *buf;
+
+ buf = kmalloc(sizeof_buf, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = 2;
+ buf[1] = command;
+ buf[2] = data;
+
+ if ((ret =
+ aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
+ dbg("aiptek_program: failed, tried to send: 0x%02x 0x%02x",
+ command, data);
+ }
+ kfree(buf);
+ return ret < 0 ? ret : 0;
+}
+
+/***********************************************************************
+ * Retrieve information from the tablet. Querying info is defined as first
+ * sending the {command,data} sequence as a command, followed by a wait
+ * (aka, "programmaticDelay") and then a "read" request.
+ */
+static int
+aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data)
+{
+ const int sizeof_buf = 3 * sizeof(u8);
+ int ret;
+ u8 *buf;
+
+ buf = kmalloc(sizeof_buf, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = 2;
+ buf[1] = command;
+ buf[2] = data;
+
+ if (aiptek_command(aiptek, command, data) != 0) {
+ kfree(buf);
+ return -EIO;
+ }
+ msleep(aiptek->curSetting.programmableDelay);
+
+ if ((ret =
+ aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
+ dbg("aiptek_query failed: returned 0x%02x 0x%02x 0x%02x",
+ buf[0], buf[1], buf[2]);
+ ret = -EIO;
+ } else {
+ ret = le16_to_cpu(get_unaligned((__le16 *) (buf + 1)));
+ }
+ kfree(buf);
+ return ret;
+}
+
+/***********************************************************************
+ * Program the tablet into either absolute or relative mode.
+ * We also get information about the tablet's size.
+ */
+static int aiptek_program_tablet(struct aiptek *aiptek)
+{
+ int ret;
+ /* Execute Resolution500LPI */
+ if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0)
+ return ret;
+
+ /* Query getModelCode */
+ if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0)
+ return ret;
+ aiptek->features.modelCode = ret & 0xff;
+
+ /* Query getODMCode */
+ if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0)
+ return ret;
+ aiptek->features.odmCode = ret;
+
+ /* Query getFirmwareCode */
+ if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0)
+ return ret;
+ aiptek->features.firmwareCode = ret;
+
+ /* Query getXextension */
+ if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0)
+ return ret;
+ aiptek->inputdev->absmin[ABS_X] = 0;
+ aiptek->inputdev->absmax[ABS_X] = ret - 1;
+
+ /* Query getYextension */
+ if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0)
+ return ret;
+ aiptek->inputdev->absmin[ABS_Y] = 0;
+ aiptek->inputdev->absmax[ABS_Y] = ret - 1;
+
+ /* Query getPressureLevels */
+ if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0)
+ return ret;
+ aiptek->inputdev->absmin[ABS_PRESSURE] = 0;
+ aiptek->inputdev->absmax[ABS_PRESSURE] = ret - 1;
+
+ /* Depending on whether we are in absolute or relative mode, we will
+ * do a switchToTablet(absolute) or switchToMouse(relative) command.
+ */
+ if (aiptek->curSetting.coordinateMode ==
+ AIPTEK_COORDINATE_ABSOLUTE_MODE) {
+ /* Execute switchToTablet */
+ if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) {
+ return ret;
+ }
+ } else {
+ /* Execute switchToMouse */
+ if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) {
+ return ret;
+ }
+ }
+
+ /* Enable the macro keys */
+ if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0)
+ return ret;
+#if 0
+ /* Execute FilterOn */
+ if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0)
+ return ret;
+#endif
+
+ /* Execute AutoGainOn */
+ if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0)
+ return ret;
+
+ /* Reset the eventCount, so we track events from last (re)programming
+ */
+ aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA;
+ aiptek->eventCount = 0;
+
+ return 0;
+}
+
+/***********************************************************************
+ * Sysfs functions. Sysfs prefers that individually-tunable parameters
+ * exist in their separate pseudo-files. Summary data that is immutable
+ * may exist in a singular file so long as you don't define a writeable
+ * interface.
+ */
+
+/***********************************************************************
+ * support the 'size' file -- display support
+ */
+static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%dx%d\n",
+ aiptek->inputdev->absmax[ABS_X] + 1,
+ aiptek->inputdev->absmax[ABS_Y] + 1);
+}
+
+/* These structs define the sysfs files, param #1 is the name of the
+ * file, param 2 is the file permissions, param 3 & 4 are to the
+ * output generator and input parser routines. Absence of a routine is
+ * permitted -- it only means can't either 'cat' the file, or send data
+ * to it.
+ */
+static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL);
+
+/***********************************************************************
+ * support routines for the 'product_id' file
+ */
+static ssize_t show_tabletProductId(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n",
+ aiptek->inputdev->id.product);
+}
+
+static DEVICE_ATTR(product_id, S_IRUGO, show_tabletProductId, NULL);
+
+/***********************************************************************
+ * support routines for the 'vendor_id' file
+ */
+static ssize_t show_tabletVendorId(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->inputdev->id.vendor);
+}
+
+static DEVICE_ATTR(vendor_id, S_IRUGO, show_tabletVendorId, NULL);
+
+/***********************************************************************
+ * support routines for the 'vendor' file
+ */
+static ssize_t show_tabletManufacturer(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ int retval;
+
+ if (aiptek == NULL)
+ return 0;
+
+ retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->manufacturer);
+ return retval;
+}
+
+static DEVICE_ATTR(vendor, S_IRUGO, show_tabletManufacturer, NULL);
+
+/***********************************************************************
+ * support routines for the 'product' file
+ */
+static ssize_t show_tabletProduct(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ int retval;
+
+ if (aiptek == NULL)
+ return 0;
+
+ retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->product);
+ return retval;
+}
+
+static DEVICE_ATTR(product, S_IRUGO, show_tabletProduct, NULL);
+
+/***********************************************************************
+ * support routines for the 'pointer_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *s;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (aiptek->curSetting.pointerMode) {
+ case AIPTEK_POINTER_ONLY_STYLUS_MODE:
+ s = "stylus";
+ break;
+
+ case AIPTEK_POINTER_ONLY_MOUSE_MODE:
+ s = "mouse";
+ break;
+
+ case AIPTEK_POINTER_EITHER_MODE:
+ s = "either";
+ break;
+
+ default:
+ s = "unknown";
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "stylus") == 0) {
+ aiptek->newSetting.pointerMode =
+ AIPTEK_POINTER_ONLY_STYLUS_MODE;
+ } else if (strcmp(buf, "mouse") == 0) {
+ aiptek->newSetting.pointerMode = AIPTEK_POINTER_ONLY_MOUSE_MODE;
+ } else if (strcmp(buf, "either") == 0) {
+ aiptek->newSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(pointer_mode,
+ S_IRUGO | S_IWUGO,
+ show_tabletPointerMode, store_tabletPointerMode);
+
+/***********************************************************************
+ * support routines for the 'coordinate_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *s;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (aiptek->curSetting.coordinateMode) {
+ case AIPTEK_COORDINATE_ABSOLUTE_MODE:
+ s = "absolute";
+ break;
+
+ case AIPTEK_COORDINATE_RELATIVE_MODE:
+ s = "relative";
+ break;
+
+ default:
+ s = "unknown";
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "absolute") == 0) {
+ aiptek->newSetting.pointerMode =
+ AIPTEK_COORDINATE_ABSOLUTE_MODE;
+ } else if (strcmp(buf, "relative") == 0) {
+ aiptek->newSetting.pointerMode =
+ AIPTEK_COORDINATE_RELATIVE_MODE;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(coordinate_mode,
+ S_IRUGO | S_IWUGO,
+ show_tabletCoordinateMode, store_tabletCoordinateMode);
+
+/***********************************************************************
+ * support routines for the 'tool_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *s;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (TOOL_BUTTON(aiptek->curSetting.toolMode)) {
+ case AIPTEK_TOOL_BUTTON_MOUSE_MODE:
+ s = "mouse";
+ break;
+
+ case AIPTEK_TOOL_BUTTON_ERASER_MODE:
+ s = "eraser";
+ break;
+
+ case AIPTEK_TOOL_BUTTON_PENCIL_MODE:
+ s = "pencil";
+ break;
+
+ case AIPTEK_TOOL_BUTTON_PEN_MODE:
+ s = "pen";
+ break;
+
+ case AIPTEK_TOOL_BUTTON_BRUSH_MODE:
+ s = "brush";
+ break;
+
+ case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE:
+ s = "airbrush";
+ break;
+
+ case AIPTEK_TOOL_BUTTON_LENS_MODE:
+ s = "lens";
+ break;
+
+ default:
+ s = "unknown";
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "mouse") == 0) {
+ aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_MOUSE_MODE;
+ } else if (strcmp(buf, "eraser") == 0) {
+ aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_ERASER_MODE;
+ } else if (strcmp(buf, "pencil") == 0) {
+ aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PENCIL_MODE;
+ } else if (strcmp(buf, "pen") == 0) {
+ aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
+ } else if (strcmp(buf, "brush") == 0) {
+ aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_BRUSH_MODE;
+ } else if (strcmp(buf, "airbrush") == 0) {
+ aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE;
+ } else if (strcmp(buf, "lens") == 0) {
+ aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_LENS_MODE;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(tool_mode,
+ S_IRUGO | S_IWUGO,
+ show_tabletToolMode, store_tabletToolMode);
+
+/***********************************************************************
+ * support routines for the 'xtilt' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) {
+ return snprintf(buf, PAGE_SIZE, "disable\n");
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ aiptek->curSetting.xTilt);
+ }
+}
+
+static ssize_t
+store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ int x;
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "disable") == 0) {
+ aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE;
+ } else {
+ x = (int)simple_strtol(buf, NULL, 10);
+ if (x >= AIPTEK_TILT_MIN && x <= AIPTEK_TILT_MAX) {
+ aiptek->newSetting.xTilt = x;
+ }
+ }
+ return count;
+}
+
+static DEVICE_ATTR(xtilt,
+ S_IRUGO | S_IWUGO, show_tabletXtilt, store_tabletXtilt);
+
+/***********************************************************************
+ * support routines for the 'ytilt' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) {
+ return snprintf(buf, PAGE_SIZE, "disable\n");
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ aiptek->curSetting.yTilt);
+ }
+}
+
+static ssize_t
+store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ int y;
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "disable") == 0) {
+ aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE;
+ } else {
+ y = (int)simple_strtol(buf, NULL, 10);
+ if (y >= AIPTEK_TILT_MIN && y <= AIPTEK_TILT_MAX) {
+ aiptek->newSetting.yTilt = y;
+ }
+ }
+ return count;
+}
+
+static DEVICE_ATTR(ytilt,
+ S_IRUGO | S_IWUGO, show_tabletYtilt, store_tabletYtilt);
+
+/***********************************************************************
+ * support routines for the 'jitter' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay);
+}
+
+static ssize_t
+store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ aiptek->newSetting.jitterDelay = (int)simple_strtol(buf, NULL, 10);
+ return count;
+}
+
+static DEVICE_ATTR(jitter,
+ S_IRUGO | S_IWUGO,
+ show_tabletJitterDelay, store_tabletJitterDelay);
+
+/***********************************************************************
+ * support routines for the 'delay' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ aiptek->curSetting.programmableDelay);
+}
+
+static ssize_t
+store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, NULL, 10);
+ return count;
+}
+
+static DEVICE_ATTR(delay,
+ S_IRUGO | S_IWUGO,
+ show_tabletProgrammableDelay, store_tabletProgrammableDelay);
+
+/***********************************************************************
+ * support routines for the 'input_path' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletInputDevice(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "/dev/input/%s\n",
+ aiptek->features.inputPath);
+}
+
+static DEVICE_ATTR(input_path, S_IRUGO, show_tabletInputDevice, NULL);
+
+/***********************************************************************
+ * support routines for the 'event_count' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount);
+}
+
+static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL);
+
+/***********************************************************************
+ * support routines for the 'diagnostic' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *retMsg;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (aiptek->diagnostic) {
+ case AIPTEK_DIAGNOSTIC_NA:
+ retMsg = "no errors\n";
+ break;
+
+ case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE:
+ retMsg = "Error: receiving relative reports\n";
+ break;
+
+ case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE:
+ retMsg = "Error: receiving absolute reports\n";
+ break;
+
+ case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED:
+ if (aiptek->curSetting.pointerMode ==
+ AIPTEK_POINTER_ONLY_MOUSE_MODE) {
+ retMsg = "Error: receiving stylus reports\n";
+ } else {
+ retMsg = "Error: receiving mouse reports\n";
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ return snprintf(buf, PAGE_SIZE, retMsg);
+}
+
+static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL);
+
+/***********************************************************************
+ * support routines for the 'stylus_upper' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *s;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (aiptek->curSetting.stylusButtonUpper) {
+ case AIPTEK_STYLUS_UPPER_BUTTON:
+ s = "upper";
+ break;
+
+ case AIPTEK_STYLUS_LOWER_BUTTON:
+ s = "lower";
+ break;
+
+ default:
+ s = "unknown";
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "upper") == 0) {
+ aiptek->newSetting.stylusButtonUpper =
+ AIPTEK_STYLUS_UPPER_BUTTON;
+ } else if (strcmp(buf, "lower") == 0) {
+ aiptek->newSetting.stylusButtonUpper =
+ AIPTEK_STYLUS_LOWER_BUTTON;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(stylus_upper,
+ S_IRUGO | S_IWUGO,
+ show_tabletStylusUpper, store_tabletStylusUpper);
+
+/***********************************************************************
+ * support routines for the 'stylus_lower' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *s;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (aiptek->curSetting.stylusButtonLower) {
+ case AIPTEK_STYLUS_UPPER_BUTTON:
+ s = "upper";
+ break;
+
+ case AIPTEK_STYLUS_LOWER_BUTTON:
+ s = "lower";
+ break;
+
+ default:
+ s = "unknown";
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "upper") == 0) {
+ aiptek->newSetting.stylusButtonLower =
+ AIPTEK_STYLUS_UPPER_BUTTON;
+ } else if (strcmp(buf, "lower") == 0) {
+ aiptek->newSetting.stylusButtonLower =
+ AIPTEK_STYLUS_LOWER_BUTTON;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(stylus_lower,
+ S_IRUGO | S_IWUGO,
+ show_tabletStylusLower, store_tabletStylusLower);
+
+/***********************************************************************
+ * support routines for the 'mouse_left' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *s;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (aiptek->curSetting.mouseButtonLeft) {
+ case AIPTEK_MOUSE_LEFT_BUTTON:
+ s = "left";
+ break;
+
+ case AIPTEK_MOUSE_MIDDLE_BUTTON:
+ s = "middle";
+ break;
+
+ case AIPTEK_MOUSE_RIGHT_BUTTON:
+ s = "right";
+ break;
+
+ default:
+ s = "unknown";
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "left") == 0) {
+ aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
+ } else if (strcmp(buf, "middle") == 0) {
+ aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_MIDDLE_BUTTON;
+ } else if (strcmp(buf, "right") == 0) {
+ aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_RIGHT_BUTTON;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(mouse_left,
+ S_IRUGO | S_IWUGO,
+ show_tabletMouseLeft, store_tabletMouseLeft);
+
+/***********************************************************************
+ * support routines for the 'mouse_middle' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *s;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (aiptek->curSetting.mouseButtonMiddle) {
+ case AIPTEK_MOUSE_LEFT_BUTTON:
+ s = "left";
+ break;
+
+ case AIPTEK_MOUSE_MIDDLE_BUTTON:
+ s = "middle";
+ break;
+
+ case AIPTEK_MOUSE_RIGHT_BUTTON:
+ s = "right";
+ break;
+
+ default:
+ s = "unknown";
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "left") == 0) {
+ aiptek->newSetting.mouseButtonMiddle = AIPTEK_MOUSE_LEFT_BUTTON;
+ } else if (strcmp(buf, "middle") == 0) {
+ aiptek->newSetting.mouseButtonMiddle =
+ AIPTEK_MOUSE_MIDDLE_BUTTON;
+ } else if (strcmp(buf, "right") == 0) {
+ aiptek->newSetting.mouseButtonMiddle =
+ AIPTEK_MOUSE_RIGHT_BUTTON;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(mouse_middle,
+ S_IRUGO | S_IWUGO,
+ show_tabletMouseMiddle, store_tabletMouseMiddle);
+
+/***********************************************************************
+ * support routines for the 'mouse_right' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+ char *s;
+
+ if (aiptek == NULL)
+ return 0;
+
+ switch (aiptek->curSetting.mouseButtonRight) {
+ case AIPTEK_MOUSE_LEFT_BUTTON:
+ s = "left";
+ break;
+
+ case AIPTEK_MOUSE_MIDDLE_BUTTON:
+ s = "middle";
+ break;
+
+ case AIPTEK_MOUSE_RIGHT_BUTTON:
+ s = "right";
+ break;
+
+ default:
+ s = "unknown";
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (strcmp(buf, "left") == 0) {
+ aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_LEFT_BUTTON;
+ } else if (strcmp(buf, "middle") == 0) {
+ aiptek->newSetting.mouseButtonRight =
+ AIPTEK_MOUSE_MIDDLE_BUTTON;
+ } else if (strcmp(buf, "right") == 0) {
+ aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(mouse_right,
+ S_IRUGO | S_IWUGO,
+ show_tabletMouseRight, store_tabletMouseRight);
+
+/***********************************************************************
+ * support routines for the 'wheel' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) {
+ return snprintf(buf, PAGE_SIZE, "disable\n");
+ } else {
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ aiptek->curSetting.wheel);
+ }
+}
+
+static ssize_t
+store_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10);
+ return count;
+}
+
+static DEVICE_ATTR(wheel,
+ S_IRUGO | S_IWUGO, show_tabletWheel, store_tabletWheel);
+
+/***********************************************************************
+ * support routines for the 'execute' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ /* There is nothing useful to display, so a one-line manual
+ * is in order...
+ */
+ return snprintf(buf, PAGE_SIZE,
+ "Write anything to this file to program your tablet.\n");
+}
+
+static ssize_t
+store_tabletExecute(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ /* We do not care what you write to this file. Merely the action
+ * of writing to this file triggers a tablet reprogramming.
+ */
+ memcpy(&aiptek->curSetting, &aiptek->newSetting,
+ sizeof(struct aiptek_settings));
+
+ if (aiptek_program_tablet(aiptek) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static DEVICE_ATTR(execute,
+ S_IRUGO | S_IWUGO, show_tabletExecute, store_tabletExecute);
+
+/***********************************************************************
+ * support routines for the 'odm_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode);
+}
+
+static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL);
+
+/***********************************************************************
+ * support routines for the 'model_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode);
+}
+
+static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL);
+
+/***********************************************************************
+ * support routines for the 'firmware_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aiptek *aiptek = dev_get_drvdata(dev);
+
+ if (aiptek == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%04x\n",
+ aiptek->features.firmwareCode);
+}
+
+static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL);
+
+/***********************************************************************
+ * This routine removes all existing sysfs files managed by this device
+ * driver.
+ */
+static void aiptek_delete_files(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_size);
+ device_remove_file(dev, &dev_attr_product_id);
+ device_remove_file(dev, &dev_attr_vendor_id);
+ device_remove_file(dev, &dev_attr_vendor);
+ device_remove_file(dev, &dev_attr_product);
+ device_remove_file(dev, &dev_attr_pointer_mode);
+ device_remove_file(dev, &dev_attr_coordinate_mode);
+ device_remove_file(dev, &dev_attr_tool_mode);
+ device_remove_file(dev, &dev_attr_xtilt);
+ device_remove_file(dev, &dev_attr_ytilt);
+ device_remove_file(dev, &dev_attr_jitter);
+ device_remove_file(dev, &dev_attr_delay);
+ device_remove_file(dev, &dev_attr_input_path);
+ device_remove_file(dev, &dev_attr_event_count);
+ device_remove_file(dev, &dev_attr_diagnostic);
+ device_remove_file(dev, &dev_attr_odm_code);
+ device_remove_file(dev, &dev_attr_model_code);
+ device_remove_file(dev, &dev_attr_firmware_code);
+ device_remove_file(dev, &dev_attr_stylus_lower);
+ device_remove_file(dev, &dev_attr_stylus_upper);
+ device_remove_file(dev, &dev_attr_mouse_left);
+ device_remove_file(dev, &dev_attr_mouse_middle);
+ device_remove_file(dev, &dev_attr_mouse_right);
+ device_remove_file(dev, &dev_attr_wheel);
+ device_remove_file(dev, &dev_attr_execute);
+}
+
+/***********************************************************************
+ * This routine creates the sysfs files managed by this device
+ * driver.
+ */
+static int aiptek_add_files(struct device *dev)
+{
+ int ret;
+
+ if ((ret = device_create_file(dev, &dev_attr_size)) ||
+ (ret = device_create_file(dev, &dev_attr_product_id)) ||
+ (ret = device_create_file(dev, &dev_attr_vendor_id)) ||
+ (ret = device_create_file(dev, &dev_attr_vendor)) ||
+ (ret = device_create_file(dev, &dev_attr_product)) ||
+ (ret = device_create_file(dev, &dev_attr_pointer_mode)) ||
+ (ret = device_create_file(dev, &dev_attr_coordinate_mode)) ||
+ (ret = device_create_file(dev, &dev_attr_tool_mode)) ||
+ (ret = device_create_file(dev, &dev_attr_xtilt)) ||
+ (ret = device_create_file(dev, &dev_attr_ytilt)) ||
+ (ret = device_create_file(dev, &dev_attr_jitter)) ||
+ (ret = device_create_file(dev, &dev_attr_delay)) ||
+ (ret = device_create_file(dev, &dev_attr_input_path)) ||
+ (ret = device_create_file(dev, &dev_attr_event_count)) ||
+ (ret = device_create_file(dev, &dev_attr_diagnostic)) ||
+ (ret = device_create_file(dev, &dev_attr_odm_code)) ||
+ (ret = device_create_file(dev, &dev_attr_model_code)) ||
+ (ret = device_create_file(dev, &dev_attr_firmware_code)) ||
+ (ret = device_create_file(dev, &dev_attr_stylus_lower)) ||
+ (ret = device_create_file(dev, &dev_attr_stylus_upper)) ||
+ (ret = device_create_file(dev, &dev_attr_mouse_left)) ||
+ (ret = device_create_file(dev, &dev_attr_mouse_middle)) ||
+ (ret = device_create_file(dev, &dev_attr_mouse_right)) ||
+ (ret = device_create_file(dev, &dev_attr_wheel)) ||
+ (ret = device_create_file(dev, &dev_attr_execute))) {
+ err("aiptek: killing own sysfs device files\n");
+ aiptek_delete_files(dev);
+ }
+ return ret;
+}
+
+/***********************************************************************
+ * This routine is called when a tablet has been identified. It basically
+ * sets up the tablet and the driver's internal structures.
+ */
+static int
+aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct aiptek *aiptek;
+ struct input_dev *inputdev;
+ struct input_handle *inputhandle;
+ struct list_head *node, *next;
+ int i;
+ int speeds[] = { 0,
+ AIPTEK_PROGRAMMABLE_DELAY_50,
+ AIPTEK_PROGRAMMABLE_DELAY_400,
+ AIPTEK_PROGRAMMABLE_DELAY_25,
+ AIPTEK_PROGRAMMABLE_DELAY_100,
+ AIPTEK_PROGRAMMABLE_DELAY_200,
+ AIPTEK_PROGRAMMABLE_DELAY_300
+ };
+ int err = -ENOMEM;
+
+ /* programmableDelay is where the command-line specified
+ * delay is kept. We make it the first element of speeds[],
+ * so therefore, your override speed is tried first, then the
+ * remainder. Note that the default value of 400ms will be tried
+ * if you do not specify any command line parameter.
+ */
+ speeds[0] = programmableDelay;
+
+ aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL);
+ inputdev = input_allocate_device();
+ if (!aiptek || !inputdev)
+ goto fail1;
+
+ aiptek->data = usb_buffer_alloc(usbdev, AIPTEK_PACKET_LENGTH,
+ GFP_ATOMIC, &aiptek->data_dma);
+ if (!aiptek->data)
+ goto fail1;
+
+ aiptek->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!aiptek->urb)
+ goto fail2;
+
+ aiptek->inputdev = inputdev;
+ aiptek->usbdev = usbdev;
+ aiptek->ifnum = intf->altsetting[0].desc.bInterfaceNumber;
+ aiptek->inDelay = 0;
+ aiptek->endDelay = 0;
+ aiptek->previousJitterable = 0;
+
+ /* Set up the curSettings struct. Said struct contains the current
+ * programmable parameters. The newSetting struct contains changes
+ * the user makes to the settings via the sysfs interface. Those
+ * changes are not "committed" to curSettings until the user
+ * writes to the sysfs/.../execute file.
+ */
+ aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
+ aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE;
+ aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
+ aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE;
+ aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE;
+ aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
+ aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON;
+ aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
+ aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON;
+ aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON;
+ aiptek->curSetting.jitterDelay = jitterDelay;
+ aiptek->curSetting.programmableDelay = programmableDelay;
+
+ /* Both structs should have equivalent settings
+ */
+ aiptek->newSetting = aiptek->curSetting;
+
+ /* Determine the usb devices' physical path.
+ * Asketh not why we always pretend we're using "../input0",
+ * but I suspect this will have to be refactored one
+ * day if a single USB device can be a keyboard & a mouse
+ * & a tablet, and the inputX number actually will tell
+ * us something...
+ */
+ usb_make_path(usbdev, aiptek->features.usbPath,
+ sizeof(aiptek->features.usbPath));
+ strlcat(aiptek->features.usbPath, "/input0",
+ sizeof(aiptek->features.usbPath));
+
+ /* Set up client data, pointers to open and close routines
+ * for the input device.
+ */
+ inputdev->name = "Aiptek";
+ inputdev->phys = aiptek->features.usbPath;
+ usb_to_input_id(usbdev, &inputdev->id);
+ inputdev->dev.parent = &intf->dev;
+
+ input_set_drvdata(inputdev, aiptek);
+
+ inputdev->open = aiptek_open;
+ inputdev->close = aiptek_close;
+
+ /* Now program the capacities of the tablet, in terms of being
+ * an input device.
+ */
+ inputdev->evbit[0] |= BIT(EV_KEY)
+ | BIT(EV_ABS)
+ | BIT(EV_REL)
+ | BIT(EV_MSC);
+
+ inputdev->absbit[0] |= BIT(ABS_MISC);
+
+ inputdev->relbit[0] |=
+ (BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) | BIT(REL_MISC));
+
+ inputdev->keybit[LONG(BTN_LEFT)] |=
+ (BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE));
+
+ inputdev->keybit[LONG(BTN_DIGI)] |=
+ (BIT(BTN_TOOL_PEN) |
+ BIT(BTN_TOOL_RUBBER) |
+ BIT(BTN_TOOL_PENCIL) |
+ BIT(BTN_TOOL_AIRBRUSH) |
+ BIT(BTN_TOOL_BRUSH) |
+ BIT(BTN_TOOL_MOUSE) |
+ BIT(BTN_TOOL_LENS) |
+ BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2));
+
+ inputdev->mscbit[0] = BIT(MSC_SERIAL);
+
+ /* Programming the tablet macro keys needs to be done with a for loop
+ * as the keycodes are discontiguous.
+ */
+ for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i)
+ set_bit(macroKeyEvents[i], inputdev->keybit);
+
+ /*
+ * Program the input device coordinate capacities. We do not yet
+ * know what maximum X, Y, and Z values are, so we're putting fake
+ * values in. Later, we'll ask the tablet to put in the correct
+ * values.
+ */
+ input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0);
+ input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0);
+ input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0);
+ input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
+ input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
+ input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0);
+
+ endpoint = &intf->altsetting[0].endpoint[0].desc;
+
+ /* Go set up our URB, which is called when the tablet receives
+ * input.
+ */
+ usb_fill_int_urb(aiptek->urb,
+ aiptek->usbdev,
+ usb_rcvintpipe(aiptek->usbdev,
+ endpoint->bEndpointAddress),
+ aiptek->data, 8, aiptek_irq, aiptek,
+ endpoint->bInterval);
+
+ aiptek->urb->transfer_dma = aiptek->data_dma;
+ aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* Program the tablet. This sets the tablet up in the mode
+ * specified in newSetting, and also queries the tablet's
+ * physical capacities.
+ *
+ * Sanity check: if a tablet doesn't like the slow programmatic
+ * delay, we often get sizes of 0x0. Let's use that as an indicator
+ * to try faster delays, up to 25 ms. If that logic fails, well, you'll
+ * have to explain to us how your tablet thinks it's 0x0, and yet that's
+ * not an error :-)
+ */
+
+ for (i = 0; i < ARRAY_SIZE(speeds); ++i) {
+ aiptek->curSetting.programmableDelay = speeds[i];
+ (void)aiptek_program_tablet(aiptek);
+ if (aiptek->inputdev->absmax[ABS_X] > 0) {
+ info("input: Aiptek using %d ms programming speed\n",
+ aiptek->curSetting.programmableDelay);
+ break;
+ }
+ }
+
+ /* Register the tablet as an Input Device
+ */
+ err = input_register_device(aiptek->inputdev);
+ if (err)
+ goto fail2;
+
+ /* We now will look for the evdev device which is mapped to
+ * the tablet. The partial name is kept in the link list of
+ * input_handles associated with this input device.
+ * What identifies an evdev input_handler is that it begins
+ * with 'event', continues with a digit, and that in turn
+ * is mapped to input/eventN.
+ */
+ list_for_each_safe(node, next, &inputdev->h_list) {
+ inputhandle = to_handle(node);
+ if (strncmp(inputhandle->name, "event", 5) == 0) {
+ strcpy(aiptek->features.inputPath, inputhandle->name);
+ break;
+ }
+ }
+
+ /* Associate this driver's struct with the usb interface.
+ */
+ usb_set_intfdata(intf, aiptek);
+
+ /* Set up the sysfs files
+ */
+ aiptek_add_files(&intf->dev);
+
+ /* Make sure the evdev module is loaded. Assuming evdev IS a module :-)
+ */
+ if (request_module("evdev") != 0)
+ info("aiptek: error loading 'evdev' module");
+
+ return 0;
+
+ fail2: usb_buffer_free(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data,
+ aiptek->data_dma);
+ fail1: input_free_device(inputdev);
+ kfree(aiptek);
+ return err;
+}
+
+/***********************************************************************
+ * Deal with tablet disconnecting from the system.
+ */
+static void aiptek_disconnect(struct usb_interface *intf)
+{
+ struct aiptek *aiptek = usb_get_intfdata(intf);
+
+ /* Disassociate driver's struct with usb interface
+ */
+ usb_set_intfdata(intf, NULL);
+ if (aiptek != NULL) {
+ /* Free & unhook everything from the system.
+ */
+ usb_kill_urb(aiptek->urb);
+ input_unregister_device(aiptek->inputdev);
+ aiptek_delete_files(&intf->dev);
+ usb_free_urb(aiptek->urb);
+ usb_buffer_free(interface_to_usbdev(intf),
+ AIPTEK_PACKET_LENGTH,
+ aiptek->data, aiptek->data_dma);
+ kfree(aiptek);
+ }
+}
+
+static struct usb_driver aiptek_driver = {
+ .name = "aiptek",
+ .probe = aiptek_probe,
+ .disconnect = aiptek_disconnect,
+ .id_table = aiptek_ids,
+};
+
+static int __init aiptek_init(void)
+{
+ int result = usb_register(&aiptek_driver);
+ if (result == 0) {
+ info(DRIVER_VERSION ": " DRIVER_AUTHOR);
+ info(DRIVER_DESC);
+ }
+ return result;
+}
+
+static void __exit aiptek_exit(void)
+{
+ usb_deregister(&aiptek_driver);
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(programmableDelay, int, 0);
+MODULE_PARM_DESC(programmableDelay, "delay used during tablet programming");
+module_param(jitterDelay, int, 0);
+MODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay");
+
+module_init(aiptek_init);
+module_exit(aiptek_exit);
diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c
new file mode 100644
index 00000000000..b2ca10f2fe0
--- /dev/null
+++ b/drivers/input/tablet/gtco.c
@@ -0,0 +1,1055 @@
+/* -*- linux-c -*-
+
+GTCO digitizer USB driver
+
+Use the err(), dbg() and info() macros from usb.h for system logging
+
+TO CHECK: Is pressure done right on report 5?
+
+Copyright (C) 2006 GTCO CalComp
+
+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; version 2
+of the License.
+
+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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of GTCO-CalComp not be used in advertising
+or publicity pertaining to distribution of the software without specific,
+written prior permission. GTCO-CalComp makes no representations about the
+suitability of this software for any purpose. It is provided "as is"
+without express or implied warranty.
+
+GTCO-CALCOMP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL GTCO-CALCOMP BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+GTCO CalComp, Inc.
+7125 Riverwood Drive
+Columbia, MD 21046
+
+Jeremy Roberson jroberson@gtcocalcomp.com
+Scott Hill shill@gtcocalcomp.com
+*/
+
+
+
+/*#define DEBUG*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+
+#include <linux/version.h>
+#include <linux/usb/input.h>
+
+/* Version with a Major number of 2 is for kernel inclusion only. */
+#define GTCO_VERSION "2.00.0006"
+
+
+/* MACROS */
+
+#define VENDOR_ID_GTCO 0x078C
+#define PID_400 0x400
+#define PID_401 0x401
+#define PID_1000 0x1000
+#define PID_1001 0x1001
+#define PID_1002 0x1002
+
+/* Max size of a single report */
+#define REPORT_MAX_SIZE 10
+
+
+/* Bitmask whether pen is in range */
+#define MASK_INRANGE 0x20
+#define MASK_BUTTON 0x01F
+
+#define PATHLENGTH 64
+
+/* DATA STRUCTURES */
+
+/* Device table */
+static struct usb_device_id gtco_usbid_table [] = {
+ { USB_DEVICE(VENDOR_ID_GTCO, PID_400) },
+ { USB_DEVICE(VENDOR_ID_GTCO, PID_401) },
+ { USB_DEVICE(VENDOR_ID_GTCO, PID_1000) },
+ { USB_DEVICE(VENDOR_ID_GTCO, PID_1001) },
+ { USB_DEVICE(VENDOR_ID_GTCO, PID_1002) },
+ { }
+};
+MODULE_DEVICE_TABLE (usb, gtco_usbid_table);
+
+
+/* Structure to hold all of our device specific stuff */
+struct gtco {
+
+ struct input_dev *inputdevice; /* input device struct pointer */
+ struct usb_device *usbdev; /* the usb device for this device */
+ struct urb *urbinfo; /* urb for incoming reports */
+ dma_addr_t buf_dma; /* dma addr of the data buffer*/
+ unsigned char * buffer; /* databuffer for reports */
+
+ char usbpath[PATHLENGTH];
+ int openCount;
+
+ /* Information pulled from Report Descriptor */
+ u32 usage;
+ u32 min_X;
+ u32 max_X;
+ u32 min_Y;
+ u32 max_Y;
+ s8 mintilt_X;
+ s8 maxtilt_X;
+ s8 mintilt_Y;
+ s8 maxtilt_Y;
+ u32 maxpressure;
+ u32 minpressure;
+};
+
+
+
+/* Code for parsing the HID REPORT DESCRIPTOR */
+
+/* From HID1.11 spec */
+struct hid_descriptor
+{
+ struct usb_descriptor_header header;
+ __le16 bcdHID;
+ u8 bCountryCode;
+ u8 bNumDescriptors;
+ u8 bDescriptorType;
+ __le16 wDescriptorLength;
+} __attribute__ ((packed));
+
+
+#define HID_DESCRIPTOR_SIZE 9
+#define HID_DEVICE_TYPE 33
+#define REPORT_DEVICE_TYPE 34
+
+
+#define PREF_TAG(x) ((x)>>4)
+#define PREF_TYPE(x) ((x>>2)&0x03)
+#define PREF_SIZE(x) ((x)&0x03)
+
+#define TYPE_MAIN 0
+#define TYPE_GLOBAL 1
+#define TYPE_LOCAL 2
+#define TYPE_RESERVED 3
+
+#define TAG_MAIN_INPUT 0x8
+#define TAG_MAIN_OUTPUT 0x9
+#define TAG_MAIN_FEATURE 0xB
+#define TAG_MAIN_COL_START 0xA
+#define TAG_MAIN_COL_END 0xC
+
+#define TAG_GLOB_USAGE 0
+#define TAG_GLOB_LOG_MIN 1
+#define TAG_GLOB_LOG_MAX 2
+#define TAG_GLOB_PHYS_MIN 3
+#define TAG_GLOB_PHYS_MAX 4
+#define TAG_GLOB_UNIT_EXP 5
+#define TAG_GLOB_UNIT 6
+#define TAG_GLOB_REPORT_SZ 7
+#define TAG_GLOB_REPORT_ID 8
+#define TAG_GLOB_REPORT_CNT 9
+#define TAG_GLOB_PUSH 10
+#define TAG_GLOB_POP 11
+
+#define TAG_GLOB_MAX 12
+
+#define DIGITIZER_USAGE_TIP_PRESSURE 0x30
+#define DIGITIZER_USAGE_TILT_X 0x3D
+#define DIGITIZER_USAGE_TILT_Y 0x3E
+
+
+/*
+ * This is an abbreviated parser for the HID Report Descriptor. We
+ * know what devices we are talking to, so this is by no means meant
+ * to be generic. We can make some safe assumptions:
+ *
+ * - We know there are no LONG tags, all short
+ * - We know that we have no MAIN Feature and MAIN Output items
+ * - We know what the IRQ reports are supposed to look like.
+ *
+ * The main purpose of this is to use the HID report desc to figure
+ * out the mins and maxs of the fields in the IRQ reports. The IRQ
+ * reports for 400/401 change slightly if the max X is bigger than 64K.
+ *
+ */
+static void parse_hid_report_descriptor(struct gtco *device, char * report,
+ int length)
+{
+ int x, i = 0;
+
+ /* Tag primitive vars */
+ __u8 prefix;
+ __u8 size;
+ __u8 tag;
+ __u8 type;
+ __u8 data = 0;
+ __u16 data16 = 0;
+ __u32 data32 = 0;
+
+ /* For parsing logic */
+ int inputnum = 0;
+ __u32 usage = 0;
+
+ /* Global Values, indexed by TAG */
+ __u32 globalval[TAG_GLOB_MAX];
+ __u32 oldval[TAG_GLOB_MAX];
+
+ /* Debug stuff */
+ char maintype = 'x';
+ char globtype[12];
+ int indent = 0;
+ char indentstr[10] = "";
+
+
+ dbg("======>>>>>>PARSE<<<<<<======");
+
+ /* Walk this report and pull out the info we need */
+ while (i < length) {
+ prefix = report[i];
+
+ /* Skip over prefix */
+ i++;
+
+ /* Determine data size and save the data in the proper variable */
+ size = PREF_SIZE(prefix);
+ switch (size) {
+ case 1:
+ data = report[i];
+ break;
+ case 2:
+ data16 = le16_to_cpu(get_unaligned((__le16 *)&report[i]));
+ break;
+ case 3:
+ size = 4;
+ data32 = le32_to_cpu(get_unaligned((__le32 *)&report[i]));
+ break;
+ }
+
+ /* Skip size of data */
+ i += size;
+
+ /* What we do depends on the tag type */
+ tag = PREF_TAG(prefix);
+ type = PREF_TYPE(prefix);
+ switch (type) {
+ case TYPE_MAIN:
+ strcpy(globtype, "");
+ switch (tag) {
+
+ case TAG_MAIN_INPUT:
+ /*
+ * The INPUT MAIN tag signifies this is
+ * information from a report. We need to
+ * figure out what it is and store the
+ * min/max values
+ */
+
+ maintype = 'I';
+ if (data == 2)
+ strcpy(globtype, "Variable");
+ else if (data == 3)
+ strcpy(globtype, "Var|Const");
+
+ dbg("::::: Saving Report: %d input #%d Max: 0x%X(%d) Min:0x%X(%d) of %d bits",
+ globalval[TAG_GLOB_REPORT_ID], inputnum,
+ globalval[TAG_GLOB_LOG_MAX], globalval[TAG_GLOB_LOG_MAX],
+ globalval[TAG_GLOB_LOG_MIN], globalval[TAG_GLOB_LOG_MIN],
+ globalval[TAG_GLOB_REPORT_SZ] * globalval[TAG_GLOB_REPORT_CNT]);
+
+
+ /*
+ We can assume that the first two input items
+ are always the X and Y coordinates. After
+ that, we look for everything else by
+ local usage value
+ */
+ switch (inputnum) {
+ case 0: /* X coord */
+ dbg("GER: X Usage: 0x%x", usage);
+ if (device->max_X == 0) {
+ device->max_X = globalval[TAG_GLOB_LOG_MAX];
+ device->min_X = globalval[TAG_GLOB_LOG_MIN];
+ }
+ break;
+
+ case 1: /* Y coord */
+ dbg("GER: Y Usage: 0x%x", usage);
+ if (device->max_Y == 0) {
+ device->max_Y = globalval[TAG_GLOB_LOG_MAX];
+ device->min_Y = globalval[TAG_GLOB_LOG_MIN];
+ }
+ break;
+
+ default:
+ /* Tilt X */
+ if (usage == DIGITIZER_USAGE_TILT_X) {
+ if (device->maxtilt_X == 0) {
+ device->maxtilt_X = globalval[TAG_GLOB_LOG_MAX];
+ device->mintilt_X = globalval[TAG_GLOB_LOG_MIN];
+ }
+ }
+
+ /* Tilt Y */
+ if (usage == DIGITIZER_USAGE_TILT_Y) {
+ if (device->maxtilt_Y == 0) {
+ device->maxtilt_Y = globalval[TAG_GLOB_LOG_MAX];
+ device->mintilt_Y = globalval[TAG_GLOB_LOG_MIN];
+ }
+ }
+
+ /* Pressure */
+ if (usage == DIGITIZER_USAGE_TIP_PRESSURE) {
+ if (device->maxpressure == 0) {
+ device->maxpressure = globalval[TAG_GLOB_LOG_MAX];
+ device->minpressure = globalval[TAG_GLOB_LOG_MIN];
+ }
+ }
+
+ break;
+ }
+
+ inputnum++;
+ break;
+
+ case TAG_MAIN_OUTPUT:
+ maintype = 'O';
+ break;
+
+ case TAG_MAIN_FEATURE:
+ maintype = 'F';
+ break;
+
+ case TAG_MAIN_COL_START:
+ maintype = 'S';
+
+ if (data == 0) {
+ dbg("======>>>>>> Physical");
+ strcpy(globtype, "Physical");
+ } else
+ dbg("======>>>>>>");
+
+ /* Indent the debug output */
+ indent++;
+ for (x = 0; x < indent; x++)
+ indentstr[x] = '-';
+ indentstr[x] = 0;
+
+ /* Save global tags */
+ for (x = 0; x < TAG_GLOB_MAX; x++)
+ oldval[x] = globalval[x];
+
+ break;
+
+ case TAG_MAIN_COL_END:
+ dbg("<<<<<<======");
+ maintype = 'E';
+ indent--;
+ for (x = 0; x < indent; x++)
+ indentstr[x] = '-';
+ indentstr[x] = 0;
+
+ /* Copy global tags back */
+ for (x = 0; x < TAG_GLOB_MAX; x++)
+ globalval[x] = oldval[x];
+
+ break;
+ }
+
+ switch (size) {
+ case 1:
+ dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
+ indentstr, tag, maintype, size, globtype, data);
+ break;
+
+ case 2:
+ dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
+ indentstr, tag, maintype, size, globtype, data16);
+ break;
+
+ case 4:
+ dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x",
+ indentstr, tag, maintype, size, globtype, data32);
+ break;
+ }
+ break;
+
+ case TYPE_GLOBAL:
+ switch (tag) {
+ case TAG_GLOB_USAGE:
+ /*
+ * First time we hit the global usage tag,
+ * it should tell us the type of device
+ */
+ if (device->usage == 0)
+ device->usage = data;
+
+ strcpy(globtype, "USAGE");
+ break;
+
+ case TAG_GLOB_LOG_MIN:
+ strcpy(globtype, "LOG_MIN");
+ break;
+
+ case TAG_GLOB_LOG_MAX:
+ strcpy(globtype, "LOG_MAX");
+ break;
+
+ case TAG_GLOB_PHYS_MIN:
+ strcpy(globtype, "PHYS_MIN");
+ break;
+
+ case TAG_GLOB_PHYS_MAX:
+ strcpy(globtype, "PHYS_MAX");
+ break;
+
+ case TAG_GLOB_UNIT_EXP:
+ strcpy(globtype, "EXP");
+ break;
+
+ case TAG_GLOB_UNIT:
+ strcpy(globtype, "UNIT");
+ break;
+
+ case TAG_GLOB_REPORT_SZ:
+ strcpy(globtype, "REPORT_SZ");
+ break;
+
+ case TAG_GLOB_REPORT_ID:
+ strcpy(globtype, "REPORT_ID");
+ /* New report, restart numbering */
+ inputnum = 0;
+ break;
+
+ case TAG_GLOB_REPORT_CNT:
+ strcpy(globtype, "REPORT_CNT");
+ break;
+
+ case TAG_GLOB_PUSH:
+ strcpy(globtype, "PUSH");
+ break;
+
+ case TAG_GLOB_POP:
+ strcpy(globtype, "POP");
+ break;
+ }
+
+ /* Check to make sure we have a good tag number
+ so we don't overflow array */
+ if (tag < TAG_GLOB_MAX) {
+ switch (size) {
+ case 1:
+ dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
+ indentstr, globtype, tag, size, data);
+ globalval[tag] = data;
+ break;
+
+ case 2:
+ dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
+ indentstr, globtype, tag, size, data16);
+ globalval[tag] = data16;
+ break;
+
+ case 4:
+ dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",
+ indentstr, globtype, tag, size, data32);
+ globalval[tag] = data32;
+ break;
+ }
+ } else {
+ dbg("%sGLOBALTAG: ILLEGAL TAG:%d SIZE: %d ",
+ indentstr, tag, size);
+ }
+ break;
+
+ case TYPE_LOCAL:
+ switch (tag) {
+ case TAG_GLOB_USAGE:
+ strcpy(globtype, "USAGE");
+ /* Always 1 byte */
+ usage = data;
+ break;
+
+ case TAG_GLOB_LOG_MIN:
+ strcpy(globtype, "MIN");
+ break;
+
+ case TAG_GLOB_LOG_MAX:
+ strcpy(globtype, "MAX");
+ break;
+
+ default:
+ strcpy(globtype, "UNKNOWN");
+ break;
+ }
+
+ switch (size) {
+ case 1:
+ dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
+ indentstr, tag, globtype, size, data);
+ break;
+
+ case 2:
+ dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
+ indentstr, tag, globtype, size, data16);
+ break;
+
+ case 4:
+ dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x",
+ indentstr, tag, globtype, size, data32);
+ break;
+ }
+
+ break;
+ }
+ }
+}
+
+/* INPUT DRIVER Routines */
+
+/*
+ * Called when opening the input device. This will submit the URB to
+ * the usb system so we start getting reports
+ */
+static int gtco_input_open(struct input_dev *inputdev)
+{
+ struct gtco *device = input_get_drvdata(inputdev);
+
+ device->urbinfo->dev = device->usbdev;
+ if (usb_submit_urb(device->urbinfo, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Called when closing the input device. This will unlink the URB
+ */
+static void gtco_input_close(struct input_dev *inputdev)
+{
+ struct gtco *device = input_get_drvdata(inputdev);
+
+ usb_kill_urb(device->urbinfo);
+}
+
+
+/*
+ * Setup input device capabilities. Tell the input system what this
+ * device is capable of generating.
+ *
+ * This information is based on what is read from the HID report and
+ * placed in the struct gtco structure
+ *
+ */
+static void gtco_setup_caps(struct input_dev *inputdev)
+{
+ struct gtco *device = input_get_drvdata(inputdev);
+
+ /* Which events */
+ inputdev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);
+
+ /* Misc event menu block */
+ inputdev->mscbit[0] = BIT(MSC_SCAN)|BIT(MSC_SERIAL)|BIT(MSC_RAW) ;
+
+ /* Absolute values based on HID report info */
+ input_set_abs_params(inputdev, ABS_X, device->min_X, device->max_X,
+ 0, 0);
+ input_set_abs_params(inputdev, ABS_Y, device->min_Y, device->max_Y,
+ 0, 0);
+
+ /* Proximity */
+ input_set_abs_params(inputdev, ABS_DISTANCE, 0, 1, 0, 0);
+
+ /* Tilt & pressure */
+ input_set_abs_params(inputdev, ABS_TILT_X, device->mintilt_X,
+ device->maxtilt_X, 0, 0);
+ input_set_abs_params(inputdev, ABS_TILT_Y, device->mintilt_Y,
+ device->maxtilt_Y, 0, 0);
+ input_set_abs_params(inputdev, ABS_PRESSURE, device->minpressure,
+ device->maxpressure, 0, 0);
+
+ /* Transducer */
+ input_set_abs_params(inputdev, ABS_MISC, 0, 0xFF, 0, 0);
+}
+
+/* USB Routines */
+
+/*
+ * URB callback routine. Called when we get IRQ reports from the
+ * digitizer.
+ *
+ * This bridges the USB and input device worlds. It generates events
+ * on the input device based on the USB reports.
+ */
+static void gtco_urb_callback(struct urb *urbinfo)
+{
+ struct gtco *device = urbinfo->context;
+ struct input_dev *inputdev;
+ int rc;
+ u32 val = 0;
+ s8 valsigned = 0;
+ char le_buffer[2];
+
+ inputdev = device->inputdevice;
+
+ /* Was callback OK? */
+ if (urbinfo->status == -ECONNRESET ||
+ urbinfo->status == -ENOENT ||
+ urbinfo->status == -ESHUTDOWN) {
+
+ /* Shutdown is occurring. Return and don't queue up any more */
+ return;
+ }
+
+ if (urbinfo->status != 0) {
+ /*
+ * Some unknown error. Hopefully temporary. Just go and
+ * requeue an URB
+ */
+ goto resubmit;
+ }
+
+ /*
+ * Good URB, now process
+ */
+
+ /* PID dependent when we interpret the report */
+ if (inputdev->id.product == PID_1000 ||
+ inputdev->id.product == PID_1001 ||
+ inputdev->id.product == PID_1002) {
+
+ /*
+ * Switch on the report ID
+ * Conveniently, the reports have more information, the higher
+ * the report number. We can just fall through the case
+ * statements if we start with the highest number report
+ */
+ switch (device->buffer[0]) {
+ case 5:
+ /* Pressure is 9 bits */
+ val = ((u16)(device->buffer[8]) << 1);
+ val |= (u16)(device->buffer[7] >> 7);
+ input_report_abs(inputdev, ABS_PRESSURE,
+ device->buffer[8]);
+
+ /* Mask out the Y tilt value used for pressure */
+ device->buffer[7] = (u8)((device->buffer[7]) & 0x7F);
+
+ /* Fall thru */
+ case 4:
+ /* Tilt */
+
+ /* Sign extend these 7 bit numbers. */
+ if (device->buffer[6] & 0x40)
+ device->buffer[6] |= 0x80;
+
+ if (device->buffer[7] & 0x40)
+ device->buffer[7] |= 0x80;
+
+
+ valsigned = (device->buffer[6]);
+ input_report_abs(inputdev, ABS_TILT_X, (s32)valsigned);
+
+ valsigned = (device->buffer[7]);
+ input_report_abs(inputdev, ABS_TILT_Y, (s32)valsigned);
+
+ /* Fall thru */
+ case 2:
+ case 3:
+ /* Convert buttons, only 5 bits possible */
+ val = (device->buffer[5]) & MASK_BUTTON;
+
+ /* We don't apply any meaning to the bitmask,
+ just report */
+ input_event(inputdev, EV_MSC, MSC_SERIAL, val);
+
+ /* Fall thru */
+ case 1:
+ /* All reports have X and Y coords in the same place */
+ val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[1]));
+ input_report_abs(inputdev, ABS_X, val);
+
+ val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[3]));
+ input_report_abs(inputdev, ABS_Y, val);
+
+ /* Ditto for proximity bit */
+ val = device->buffer[5] & MASK_INRANGE ? 1 : 0;
+ input_report_abs(inputdev, ABS_DISTANCE, val);
+
+ /* Report 1 is an exception to how we handle buttons */
+ /* Buttons are an index, not a bitmask */
+ if (device->buffer[0] == 1) {
+
+ /*
+ * Convert buttons, 5 bit index
+ * Report value of index set as one,
+ * the rest as 0
+ */
+ val = device->buffer[5] & MASK_BUTTON;
+ dbg("======>>>>>>REPORT 1: val 0x%X(%d)",
+ val, val);
+
+ /*
+ * We don't apply any meaning to the button
+ * index, just report it
+ */
+ input_event(inputdev, EV_MSC, MSC_SERIAL, val);
+ }
+ break;
+
+ case 7:
+ /* Menu blocks */
+ input_event(inputdev, EV_MSC, MSC_SCAN,
+ device->buffer[1]);
+ break;
+ }
+ }
+
+ /* Other pid class */
+ if (inputdev->id.product == PID_400 ||
+ inputdev->id.product == PID_401) {
+
+ /* Report 2 */
+ if (device->buffer[0] == 2) {
+ /* Menu blocks */
+ input_event(inputdev, EV_MSC, MSC_SCAN, device->buffer[1]);
+ }
+
+ /* Report 1 */
+ if (device->buffer[0] == 1) {
+ char buttonbyte;
+
+ /* IF X max > 64K, we still a bit from the y report */
+ if (device->max_X > 0x10000) {
+
+ val = (u16)(((u16)(device->buffer[2] << 8)) | (u8)device->buffer[1]);
+ val |= (u32)(((u8)device->buffer[3] & 0x1) << 16);
+
+ input_report_abs(inputdev, ABS_X, val);
+
+ le_buffer[0] = (u8)((u8)(device->buffer[3]) >> 1);
+ le_buffer[0] |= (u8)((device->buffer[3] & 0x1) << 7);
+
+ le_buffer[1] = (u8)(device->buffer[4] >> 1);
+ le_buffer[1] |= (u8)((device->buffer[5] & 0x1) << 7);
+
+ val = le16_to_cpu(get_unaligned((__le16 *)le_buffer));
+ input_report_abs(inputdev, ABS_Y, val);
+
+ /*
+ * Shift the button byte right by one to
+ * make it look like the standard report
+ */
+ buttonbyte = device->buffer[5] >> 1;
+ } else {
+
+ val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[1]));
+ input_report_abs(inputdev, ABS_X, val);
+
+ val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[3]));
+ input_report_abs(inputdev, ABS_Y, val);
+
+ buttonbyte = device->buffer[5];
+ }
+
+ /* BUTTONS and PROXIMITY */
+ val = buttonbyte & MASK_INRANGE ? 1 : 0;
+ input_report_abs(inputdev, ABS_DISTANCE, val);
+
+ /* Convert buttons, only 4 bits possible */
+ val = buttonbyte & 0x0F;
+#ifdef USE_BUTTONS
+ for (i = 0; i < 5; i++)
+ input_report_key(inputdev, BTN_DIGI + i, val & (1 << i));
+#else
+ /* We don't apply any meaning to the bitmask, just report */
+ input_event(inputdev, EV_MSC, MSC_SERIAL, val);
+#endif
+
+ /* TRANSDUCER */
+ input_report_abs(inputdev, ABS_MISC, device->buffer[6]);
+ }
+ }
+
+ /* Everybody gets report ID's */
+ input_event(inputdev, EV_MSC, MSC_RAW, device->buffer[0]);
+
+ /* Sync it up */
+ input_sync(inputdev);
+
+ resubmit:
+ rc = usb_submit_urb(urbinfo, GFP_ATOMIC);
+ if (rc != 0)
+ err("usb_submit_urb failed rc=0x%x", rc);
+}
+
+/*
+ * The probe routine. This is called when the kernel find the matching USB
+ * vendor/product. We do the following:
+ *
+ * - Allocate mem for a local structure to manage the device
+ * - Request a HID Report Descriptor from the device and parse it to
+ * find out the device parameters
+ * - Create an input device and assign it attributes
+ * - Allocate an URB so the device can talk to us when the input
+ * queue is open
+ */
+static int gtco_probe(struct usb_interface *usbinterface,
+ const struct usb_device_id *id)
+{
+
+ struct gtco *gtco;
+ struct input_dev *input_dev;
+ struct hid_descriptor *hid_desc;
+ char *report = NULL;
+ int result = 0, retry;
+ int error;
+ struct usb_endpoint_descriptor *endpoint;
+
+ /* Allocate memory for device structure */
+ gtco = kzalloc(sizeof(struct gtco), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!gtco || !input_dev) {
+ err("No more memory");
+ error = -ENOMEM;
+ goto err_free_devs;
+ }
+
+ /* Set pointer to the input device */
+ gtco->inputdevice = input_dev;
+
+ /* Save interface information */
+ gtco->usbdev = usb_get_dev(interface_to_usbdev(usbinterface));
+
+ /* Allocate some data for incoming reports */
+ gtco->buffer = usb_buffer_alloc(gtco->usbdev, REPORT_MAX_SIZE,
+ GFP_KERNEL, &gtco->buf_dma);
+ if (!gtco->buffer) {
+ err("No more memory for us buffers");
+ error = -ENOMEM;
+ goto err_free_devs;
+ }
+
+ /* Allocate URB for reports */
+ gtco->urbinfo = usb_alloc_urb(0, GFP_KERNEL);
+ if (!gtco->urbinfo) {
+ err("Failed to allocate URB");
+ return -ENOMEM;
+ goto err_free_buf;
+ }
+
+ /*
+ * The endpoint is always altsetting 0, we know this since we know
+ * this device only has one interrupt endpoint
+ */
+ endpoint = &usbinterface->altsetting[0].endpoint[0].desc;
+
+ /* Some debug */
+ dbg("gtco # interfaces: %d", usbinterface->num_altsetting);
+ dbg("num endpoints: %d", usbinterface->cur_altsetting->desc.bNumEndpoints);
+ dbg("interface class: %d", usbinterface->cur_altsetting->desc.bInterfaceClass);
+ dbg("endpoint: attribute:0x%x type:0x%x", endpoint->bmAttributes, endpoint->bDescriptorType);
+ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
+ dbg("endpoint: we have interrupt endpoint\n");
+
+ dbg("endpoint extra len:%d ", usbinterface->altsetting[0].extralen);
+
+ /*
+ * Find the HID descriptor so we can find out the size of the
+ * HID report descriptor
+ */
+ if (usb_get_extra_descriptor(usbinterface->cur_altsetting,
+ HID_DEVICE_TYPE, &hid_desc) != 0){
+ err("Can't retrieve exta USB descriptor to get hid report descriptor length");
+ error = -EIO;
+ goto err_free_urb;
+ }
+
+ dbg("Extra descriptor success: type:%d len:%d",
+ hid_desc->bDescriptorType, hid_desc->wDescriptorLength);
+
+ report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL);
+ if (!report) {
+ err("No more memory for report");
+ error = -ENOMEM;
+ goto err_free_urb;
+ }
+
+ /* Couple of tries to get reply */
+ for (retry = 0; retry < 3; retry++) {
+ result = usb_control_msg(gtco->usbdev,
+ usb_rcvctrlpipe(gtco->usbdev, 0),
+ USB_REQ_GET_DESCRIPTOR,
+ USB_RECIP_INTERFACE | USB_DIR_IN,
+ REPORT_DEVICE_TYPE << 8,
+ 0, /* interface */
+ report,
+ hid_desc->wDescriptorLength,
+ 5000); /* 5 secs */
+
+ if (result == hid_desc->wDescriptorLength)
+ break;
+ }
+
+ /* If we didn't get the report, fail */
+ dbg("usb_control_msg result: :%d", result);
+ if (result != hid_desc->wDescriptorLength) {
+ err("Failed to get HID Report Descriptor of size: %d",
+ hid_desc->wDescriptorLength);
+ error = -EIO;
+ goto err_free_urb;
+ }
+
+ /* Now we parse the report */
+ parse_hid_report_descriptor(gtco, report, result);
+
+ /* Now we delete it */
+ kfree(report);
+
+ /* Create a device file node */
+ usb_make_path(gtco->usbdev, gtco->usbpath, sizeof(gtco->usbpath));
+ strlcat(gtco->usbpath, "/input0", sizeof(gtco->usbpath));
+
+ /* Set Input device functions */
+ input_dev->open = gtco_input_open;
+ input_dev->close = gtco_input_close;
+
+ /* Set input device information */
+ input_dev->name = "GTCO_CalComp";
+ input_dev->phys = gtco->usbpath;
+
+ input_set_drvdata(input_dev, gtco);
+
+ /* Now set up all the input device capabilities */
+ gtco_setup_caps(input_dev);
+
+ /* Set input device required ID information */
+ usb_to_input_id(gtco->usbdev, &input_dev->id);
+ input_dev->dev.parent = &usbinterface->dev;
+
+ /* Setup the URB, it will be posted later on open of input device */
+ endpoint = &usbinterface->altsetting[0].endpoint[0].desc;
+
+ usb_fill_int_urb(gtco->urbinfo,
+ gtco->usbdev,
+ usb_rcvintpipe(gtco->usbdev,
+ endpoint->bEndpointAddress),
+ gtco->buffer,
+ REPORT_MAX_SIZE,
+ gtco_urb_callback,
+ gtco,
+ endpoint->bInterval);
+
+ gtco->urbinfo->transfer_dma = gtco->buf_dma;
+ gtco->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* Save gtco pointer in USB interface gtco */
+ usb_set_intfdata(usbinterface, gtco);
+
+ /* All done, now register the input device */
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_free_urb;
+
+ return 0;
+
+ err_free_urb:
+ usb_free_urb(gtco->urbinfo);
+ err_free_buf:
+ usb_buffer_free(gtco->usbdev, REPORT_MAX_SIZE,
+ gtco->buffer, gtco->buf_dma);
+ err_free_devs:
+ kfree(report);
+ input_free_device(input_dev);
+ kfree(gtco);
+ return error;
+}
+
+/*
+ * This function is a standard USB function called when the USB device
+ * is disconnected. We will get rid of the URV, de-register the input
+ * device, and free up allocated memory
+ */
+static void gtco_disconnect(struct usb_interface *interface)
+{
+ /* Grab private device ptr */
+ struct gtco *gtco = usb_get_intfdata(interface);
+
+ /* Now reverse all the registration stuff */
+ if (gtco) {
+ input_unregister_device(gtco->inputdevice);
+ usb_kill_urb(gtco->urbinfo);
+ usb_free_urb(gtco->urbinfo);
+ usb_buffer_free(gtco->usbdev, REPORT_MAX_SIZE,
+ gtco->buffer, gtco->buf_dma);
+ kfree(gtco);
+ }
+
+ info("gtco driver disconnected");
+}
+
+/* STANDARD MODULE LOAD ROUTINES */
+
+static struct usb_driver gtco_driverinfo_table = {
+ .name = "gtco",
+ .id_table = gtco_usbid_table,
+ .probe = gtco_probe,
+ .disconnect = gtco_disconnect,
+};
+
+/*
+ * Register this module with the USB subsystem
+ */
+static int __init gtco_init(void)
+{
+ int error;
+
+ error = usb_register(&gtco_driverinfo_table);
+ if (error) {
+ err("usb_register() failed rc=0x%x", error);
+ return error;
+ }
+
+ printk("GTCO usb driver version: %s", GTCO_VERSION);
+ return 0;
+}
+
+/*
+ * Deregister this module with the USB subsystem
+ */
+static void __exit gtco_exit(void)
+{
+ usb_deregister(&gtco_driverinfo_table);
+}
+
+module_init(gtco_init);
+module_exit(gtco_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c
new file mode 100644
index 00000000000..91e6d00d4a4
--- /dev/null
+++ b/drivers/input/tablet/kbtab.c
@@ -0,0 +1,226 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ * v0.0.1 - Original, extremely basic version, 2.4.xx only
+ * v0.0.2 - Updated, works with 2.5.62 and 2.4.20;
+ * - added pressure-threshold modules param code from
+ * Alex Perry <alex.perry@ieee.org>
+ */
+
+#define DRIVER_VERSION "v0.0.2"
+#define DRIVER_AUTHOR "Josh Myer <josh@joshisanerd.com>"
+#define DRIVER_DESC "USB KB Gear JamStudio Tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_KBGEAR 0x084e
+
+static int kb_pressure_click = 0x10;
+module_param(kb_pressure_click, int, 0);
+MODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks");
+
+struct kbtab {
+ unsigned char *data;
+ dma_addr_t data_dma;
+ struct input_dev *dev;
+ struct usb_device *usbdev;
+ struct urb *irq;
+ int x, y;
+ int button;
+ int pressure;
+ __u32 serial[2];
+ char phys[32];
+};
+
+static void kbtab_irq(struct urb *urb)
+{
+ struct kbtab *kbtab = urb->context;
+ unsigned char *data = kbtab->data;
+ struct input_dev *dev = kbtab->dev;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ kbtab->x = le16_to_cpu(get_unaligned((__le16 *) &data[1]));
+ kbtab->y = le16_to_cpu(get_unaligned((__le16 *) &data[3]));
+
+ kbtab->pressure = (data[5]);
+
+ input_report_key(dev, BTN_TOOL_PEN, 1);
+
+ input_report_abs(dev, ABS_X, kbtab->x);
+ input_report_abs(dev, ABS_Y, kbtab->y);
+
+ /*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
+ input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
+
+ if (-1 == kb_pressure_click) {
+ input_report_abs(dev, ABS_PRESSURE, kbtab->pressure);
+ } else {
+ input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0);
+ };
+
+ input_sync(dev);
+
+ exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+}
+
+static struct usb_device_id kbtab_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, kbtab_ids);
+
+static int kbtab_open(struct input_dev *dev)
+{
+ struct kbtab *kbtab = input_get_drvdata(dev);
+
+ kbtab->irq->dev = kbtab->usbdev;
+ if (usb_submit_urb(kbtab->irq, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void kbtab_close(struct input_dev *dev)
+{
+ struct kbtab *kbtab = input_get_drvdata(dev);
+
+ usb_kill_urb(kbtab->irq);
+}
+
+static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct kbtab *kbtab;
+ struct input_dev *input_dev;
+ int error = -ENOMEM;
+
+ kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!kbtab || !input_dev)
+ goto fail1;
+
+ kbtab->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &kbtab->data_dma);
+ if (!kbtab->data)
+ goto fail1;
+
+ kbtab->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!kbtab->irq)
+ goto fail2;
+
+ kbtab->usbdev = dev;
+ kbtab->dev = input_dev;
+
+ usb_make_path(dev, kbtab->phys, sizeof(kbtab->phys));
+ strlcat(kbtab->phys, "/input0", sizeof(kbtab->phys));
+
+ input_dev->name = "KB Gear Tablet";
+ input_dev->phys = kbtab->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, kbtab);
+
+ input_dev->open = kbtab_open;
+ input_dev->close = kbtab_close;
+
+ input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH);
+ input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+ input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(kbtab->irq, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ kbtab->data, 8,
+ kbtab_irq, kbtab, endpoint->bInterval);
+ kbtab->irq->transfer_dma = kbtab->data_dma;
+ kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ error = input_register_device(kbtab->dev);
+ if (error)
+ goto fail3;
+
+ usb_set_intfdata(intf, kbtab);
+
+ return 0;
+
+ fail3: usb_free_urb(kbtab->irq);
+ fail2: usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma);
+ fail1: input_free_device(input_dev);
+ kfree(kbtab);
+ return error;
+}
+
+static void kbtab_disconnect(struct usb_interface *intf)
+{
+ struct kbtab *kbtab = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (kbtab) {
+ usb_kill_urb(kbtab->irq);
+ input_unregister_device(kbtab->dev);
+ usb_free_urb(kbtab->irq);
+ usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma);
+ kfree(kbtab);
+ }
+}
+
+static struct usb_driver kbtab_driver = {
+ .name = "kbtab",
+ .probe = kbtab_probe,
+ .disconnect = kbtab_disconnect,
+ .id_table = kbtab_ids,
+};
+
+static int __init kbtab_init(void)
+{
+ int retval;
+ retval = usb_register(&kbtab_driver);
+ if (retval)
+ goto out;
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+out:
+ return retval;
+}
+
+static void __exit kbtab_exit(void)
+{
+ usb_deregister(&kbtab_driver);
+}
+
+module_init(kbtab_init);
+module_exit(kbtab_exit);
diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h
new file mode 100644
index 00000000000..ef01a807ec0
--- /dev/null
+++ b/drivers/input/tablet/wacom.h
@@ -0,0 +1,131 @@
+/*
+ * drivers/input/tablet/wacom.h
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support
+ *
+ * Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
+ * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
+ * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
+ * Copyright (c) 2000 James E. Blair <corvus@gnu.org>
+ * Copyright (c) 2000 Daniel Egger <egger@suse.de>
+ * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
+ * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
+ * Copyright (c) 2002-2006 Ping Cheng <pingc@wacom.com>
+ *
+ * ChangeLog:
+ * v0.1 (vp) - Initial release
+ * v0.2 (aba) - Support for all buttons / combinations
+ * v0.3 (vp) - Support for Intuos added
+ * v0.4 (sm) - Support for more Intuos models, menustrip
+ * relative mode, proximity.
+ * v0.5 (vp) - Big cleanup, nifty features removed,
+ * they belong in userspace
+ * v1.8 (vp) - Submit URB only when operating, moved to CVS,
+ * use input_report_key instead of report_btn and
+ * other cleanups
+ * v1.11 (vp) - Add URB ->dev setting for new kernels
+ * v1.11 (jb) - Add support for the 4D Mouse & Lens
+ * v1.12 (de) - Add support for two more inking pen IDs
+ * v1.14 (vp) - Use new USB device id probing scheme.
+ * Fix Wacom Graphire mouse wheel
+ * v1.18 (vp) - Fix mouse wheel direction
+ * Make mouse relative
+ * v1.20 (fl) - Report tool id for Intuos devices
+ * - Multi tools support
+ * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
+ * - Add PL models support
+ * - Fix Wacom Graphire mouse wheel again
+ * v1.21 (vp) - Removed protocol descriptions
+ * - Added MISC_SERIAL for tool serial numbers
+ * (gb) - Identify version on module load.
+ * v1.21.1 (fl) - added Graphire2 support
+ * v1.21.2 (fl) - added Intuos2 support
+ * - added all the PL ids
+ * v1.21.3 (fl) - added another eraser id from Neil Okamoto
+ * - added smooth filter for Graphire from Peri Hankey
+ * - added PenPartner support from Olaf van Es
+ * - new tool ids from Ole Martin Bjoerndalen
+ * v1.29 (pc) - Add support for more tablets
+ * - Fix pressure reporting
+ * v1.30 (vp) - Merge 2.4 and 2.5 drivers
+ * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
+ * - Cleanups here and there
+ * v1.30.1 (pi) - Added Graphire3 support
+ * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
+ * v1.43 (pc) - Added support for Cintiq 21UX
+ * - Fixed a Graphire bug
+ * - Merged wacom_intuos3_irq into wacom_intuos_irq
+ * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
+ * - Report Device IDs
+ * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
+ * - Minor data report fix
+ * v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
+ * - where wacom_sys.c deals with system specific code,
+ * - and wacom_wac.c deals with Wacom specific code
+ * - Support Intuos3 4x6
+ */
+
+/*
+ * 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.
+ */
+#ifndef WACOM_H
+#define WACOM_H
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.46"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_WACOM 0x056a
+
+struct wacom {
+ dma_addr_t data_dma;
+ struct input_dev *dev;
+ struct usb_device *usbdev;
+ struct urb *irq;
+ struct wacom_wac * wacom_wac;
+ char phys[32];
+};
+
+struct wacom_combo {
+ struct wacom * wacom;
+ struct urb * urb;
+};
+
+extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo);
+extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data);
+extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data);
+extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data);
+extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value);
+extern void wacom_input_sync(void *wcombo);
+extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern __u16 wacom_le16_to_cpu(unsigned char *data);
+extern __u16 wacom_be16_to_cpu(unsigned char *data);
+extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id);
+extern const struct usb_device_id * get_device_table(void);
+
+#endif
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
new file mode 100644
index 00000000000..83bddef6606
--- /dev/null
+++ b/drivers/input/tablet/wacom_sys.c
@@ -0,0 +1,318 @@
+/*
+ * drivers/input/tablet/wacom_sys.c
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support - system specific code
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "wacom.h"
+#include "wacom_wac.h"
+
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_SET_REPORT 0x09
+
+static int usb_get_report(struct usb_interface *intf, unsigned char type,
+ unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(interface_to_usbdev(intf),
+ usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
+ USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+ buf, size, 100);
+}
+
+static int usb_set_report(struct usb_interface *intf, unsigned char type,
+ unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(interface_to_usbdev(intf),
+ usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+ USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+ buf, size, 1000);
+}
+
+static struct input_dev * get_input_dev(struct wacom_combo *wcombo)
+{
+ return wcombo->wacom->dev;
+}
+
+static void wacom_sys_irq(struct urb *urb)
+{
+ struct wacom *wacom = urb->context;
+ struct wacom_combo wcombo;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ wcombo.wacom = wacom;
+ wcombo.urb = urb;
+
+ if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo))
+ input_sync(get_input_dev(&wcombo));
+
+ exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+}
+
+void wacom_report_key(void *wcombo, unsigned int key_type, int key_data)
+{
+ input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data);
+ return;
+}
+
+void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data)
+{
+ input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data);
+ return;
+}
+
+void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data)
+{
+ input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data);
+ return;
+}
+
+void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value)
+{
+ input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value);
+ return;
+}
+
+__u16 wacom_be16_to_cpu(unsigned char *data)
+{
+ __u16 value;
+ value = be16_to_cpu(*(__be16 *) data);
+ return value;
+}
+
+__u16 wacom_le16_to_cpu(unsigned char *data)
+{
+ __u16 value;
+ value = le16_to_cpu(*(__le16 *) data);
+ return value;
+}
+
+void wacom_input_sync(void *wcombo)
+{
+ input_sync(get_input_dev((struct wacom_combo *)wcombo));
+ return;
+}
+
+static int wacom_open(struct input_dev *dev)
+{
+ struct wacom *wacom = input_get_drvdata(dev);
+
+ wacom->irq->dev = wacom->usbdev;
+ if (usb_submit_urb(wacom->irq, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+ struct wacom *wacom = input_get_drvdata(dev);
+
+ usb_kill_urb(wacom->irq);
+}
+
+void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_MSC);
+ input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_4);
+}
+
+void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_REL);
+ input_dev->relbit[0] |= BIT(REL_WHEEL);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
+}
+
+void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3);
+ input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
+}
+
+void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+ input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
+}
+
+void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
+ input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+ input_dev->relbit[0] |= BIT(REL_WHEEL);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
+ | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
+ input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
+ input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
+}
+
+void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
+}
+
+void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER);
+}
+
+static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct wacom *wacom;
+ struct wacom_wac *wacom_wac;
+ struct input_dev *input_dev;
+ int error = -ENOMEM;
+ char rep_data[2], limit = 0;
+
+ wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+ wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!wacom || !input_dev || !wacom_wac)
+ goto fail1;
+
+ wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
+ if (!wacom_wac->data)
+ goto fail1;
+
+ wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!wacom->irq)
+ goto fail2;
+
+ wacom->usbdev = dev;
+ wacom->dev = input_dev;
+ usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
+ strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
+
+ wacom_wac->features = get_wacom_feature(id);
+ BUG_ON(wacom_wac->features->pktlen > 10);
+
+ input_dev->name = wacom_wac->features->name;
+ wacom->wacom_wac = wacom_wac;
+ usb_to_input_id(dev, &input_dev->id);
+
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, wacom);
+
+ input_dev->open = wacom_open;
+ input_dev->close = wacom_close;
+
+ input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
+ input_set_abs_params(input_dev, ABS_X, 0, wacom_wac->features->x_max, 4, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, wacom_wac->features->y_max, 4, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_wac->features->pressure_max, 0, 0);
+ input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
+
+ wacom_init_input_dev(input_dev, wacom_wac);
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(wacom->irq, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ wacom_wac->data, wacom_wac->features->pktlen,
+ wacom_sys_irq, wacom, endpoint->bInterval);
+ wacom->irq->transfer_dma = wacom->data_dma;
+ wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ error = input_register_device(wacom->dev);
+ if (error)
+ goto fail3;
+
+ /* Ask the tablet to report tablet data. Repeat until it succeeds */
+ do {
+ rep_data[0] = 2;
+ rep_data[1] = 2;
+ usb_set_report(intf, 3, 2, rep_data, 2);
+ usb_get_report(intf, 3, 2, rep_data, 2);
+ } while (rep_data[1] != 2 && limit++ < 5);
+
+ usb_set_intfdata(intf, wacom);
+ return 0;
+
+ fail3: usb_free_urb(wacom->irq);
+ fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
+ fail1: input_free_device(input_dev);
+ kfree(wacom);
+ kfree(wacom_wac);
+ return error;
+}
+
+static void wacom_disconnect(struct usb_interface *intf)
+{
+ struct wacom *wacom = usb_get_intfdata (intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (wacom) {
+ usb_kill_urb(wacom->irq);
+ input_unregister_device(wacom->dev);
+ usb_free_urb(wacom->irq);
+ usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
+ kfree(wacom->wacom_wac);
+ kfree(wacom);
+ }
+}
+
+static struct usb_driver wacom_driver = {
+ .name = "wacom",
+ .probe = wacom_probe,
+ .disconnect = wacom_disconnect,
+};
+
+static int __init wacom_init(void)
+{
+ int result;
+ wacom_driver.id_table = get_device_table();
+ result = usb_register(&wacom_driver);
+ if (result == 0)
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return result;
+}
+
+static void __exit wacom_exit(void)
+{
+ usb_deregister(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
new file mode 100644
index 00000000000..7661f03a2db
--- /dev/null
+++ b/drivers/input/tablet/wacom_wac.c
@@ -0,0 +1,675 @@
+/*
+ * drivers/input/tablet/wacom_wac.c
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support - Wacom specific code
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include "wacom.h"
+#include "wacom_wac.h"
+
+static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+
+ switch (data[0]) {
+ case 1:
+ if (data[5] & 0x80) {
+ wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
+ wacom_report_key(wcombo, wacom->tool[0], 1);
+ wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
+ wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127));
+ wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
+ } else {
+ wacom_report_key(wcombo, wacom->tool[0], 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */
+ wacom_report_abs(wcombo, ABS_PRESSURE, -1);
+ wacom_report_key(wcombo, BTN_TOUCH, 0);
+ }
+ break;
+ case 2:
+ wacom_report_key(wcombo, BTN_TOOL_PEN, 1);
+ wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
+ wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
+ wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
+ break;
+ default:
+ printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
+ return 0;
+ }
+ return 1;
+}
+
+static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int prox, id, pressure;
+
+ if (data[0] != 2) {
+ dbg("wacom_pl_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ prox = data[1] & 0x40;
+
+ id = ERASER_DEVICE_ID;
+ if (prox) {
+
+ pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+ if (wacom->features->pressure_max > 255)
+ pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+ pressure += (wacom->features->pressure_max + 1) / 2;
+
+ /*
+ * if going from out of proximity into proximity select between the eraser
+ * and the pen based on the state of the stylus2 button, choose eraser if
+ * pressed else choose pen. if not a proximity change from out to in, send
+ * an out of proximity for previous tool then a in for new tool.
+ */
+ if (!wacom->tool[0]) {
+ /* Eraser bit set for DTF */
+ if (data[1] & 0x10)
+ wacom->tool[1] = BTN_TOOL_RUBBER;
+ else
+ /* Going into proximity select tool */
+ wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ } else {
+ /* was entered with stylus2 pressed */
+ if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
+ /* report out proximity for previous tool */
+ wacom_report_key(wcombo, wacom->tool[1], 0);
+ wacom_input_sync(wcombo);
+ wacom->tool[1] = BTN_TOOL_PEN;
+ return 0;
+ }
+ }
+ if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+ /* Unknown tool selected default to pen tool */
+ wacom->tool[1] = BTN_TOOL_PEN;
+ id = STYLUS_DEVICE_ID;
+ }
+ wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+ wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+ wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
+
+ wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08);
+ wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10);
+ /* Only allow the stylus2 button to be reported for the pen tool. */
+ wacom_report_key(wcombo, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ } else {
+ /* report proximity-out of a (valid) tool */
+ if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+ /* Unknown tool selected default to pen tool */
+ wacom->tool[1] = BTN_TOOL_PEN;
+ }
+ wacom_report_key(wcombo, wacom->tool[1], prox);
+ }
+
+ wacom->tool[0] = prox; /* Save proximity state */
+ return 1;
+}
+
+static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int id;
+
+ if (data[0] != 2) {
+ printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
+ return 0;
+ }
+
+ if (data[1] & 0x04) {
+ wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20);
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08);
+ id = ERASER_DEVICE_ID;
+ } else {
+ wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20);
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
+ id = STYLUS_DEVICE_ID;
+ }
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10);
+ return 1;
+}
+
+static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int x, y, id, rw;
+
+ if (data[0] != 2) {
+ dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ id = STYLUS_DEVICE_ID;
+ if (data[1] & 0x80) { /* in prox */
+
+ switch ((data[1] >> 5) & 3) {
+
+ case 0: /* Pen */
+ wacom->tool[0] = BTN_TOOL_PEN;
+ break;
+
+ case 1: /* Rubber */
+ wacom->tool[0] = BTN_TOOL_RUBBER;
+ id = ERASER_DEVICE_ID;
+ break;
+
+ case 2: /* Mouse with wheel */
+ wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04);
+ if (wacom->features->type == WACOM_G4) {
+ rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
+ wacom_report_rel(wcombo, REL_WHEEL, -rw);
+ } else
+ wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]);
+ /* fall through */
+
+ case 3: /* Mouse without wheel */
+ wacom->tool[0] = BTN_TOOL_MOUSE;
+ id = CURSOR_DEVICE_ID;
+ wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01);
+ wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
+ if (wacom->features->type == WACOM_G4)
+ wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f);
+ else
+ wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f);
+ break;
+ }
+ x = wacom_le16_to_cpu(&data[2]);
+ y = wacom_le16_to_cpu(&data[4]);
+ wacom_report_abs(wcombo, ABS_X, x);
+ wacom_report_abs(wcombo, ABS_Y, y);
+ if (wacom->tool[0] != BTN_TOOL_MOUSE) {
+ wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
+ }
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ wacom_report_key(wcombo, wacom->tool[0], 1);
+ } else if (!(data[1] & 0x90)) {
+ wacom_report_abs(wcombo, ABS_X, 0);
+ wacom_report_abs(wcombo, ABS_Y, 0);
+ if (wacom->tool[0] == BTN_TOOL_MOUSE) {
+ wacom_report_key(wcombo, BTN_LEFT, 0);
+ wacom_report_key(wcombo, BTN_RIGHT, 0);
+ wacom_report_abs(wcombo, ABS_DISTANCE, 0);
+ } else {
+ wacom_report_abs(wcombo, ABS_PRESSURE, 0);
+ wacom_report_key(wcombo, BTN_TOUCH, 0);
+ wacom_report_key(wcombo, BTN_STYLUS, 0);
+ wacom_report_key(wcombo, BTN_STYLUS2, 0);
+ }
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+ wacom_report_key(wcombo, wacom->tool[0], 0);
+ }
+
+ /* send pad data */
+ if (wacom->features->type == WACOM_G4) {
+ if (data[7] & 0xf8) {
+ wacom_input_sync(wcombo); /* sync last event */
+ wacom->id[1] = 1;
+ wacom->serial[1] = (data[7] & 0xf8);
+ wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
+ wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
+ rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
+ wacom_report_rel(wcombo, REL_WHEEL, rw);
+ wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0);
+ wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+ } else if (wacom->id[1]) {
+ wacom_input_sync(wcombo); /* sync last event */
+ wacom->id[1] = 0;
+ wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
+ wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
+ wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+ }
+ }
+ return 1;
+}
+
+static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int idx;
+
+ /* tool number */
+ idx = data[1] & 0x01;
+
+ /* Enter report */
+ if ((data[1] & 0xfc) == 0xc0) {
+ /* serial number of the tool */
+ wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
+ (data[4] << 20) + (data[5] << 12) +
+ (data[6] << 4) + (data[7] >> 4);
+
+ wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
+ switch (wacom->id[idx]) {
+ case 0x812: /* Inking pen */
+ case 0x801: /* Intuos3 Inking pen */
+ case 0x012:
+ wacom->tool[idx] = BTN_TOOL_PENCIL;
+ break;
+ case 0x822: /* Pen */
+ case 0x842:
+ case 0x852:
+ case 0x823: /* Intuos3 Grip Pen */
+ case 0x813: /* Intuos3 Classic Pen */
+ case 0x885: /* Intuos3 Marker Pen */
+ case 0x022:
+ wacom->tool[idx] = BTN_TOOL_PEN;
+ break;
+ case 0x832: /* Stroke pen */
+ case 0x032:
+ wacom->tool[idx] = BTN_TOOL_BRUSH;
+ break;
+ case 0x007: /* Mouse 4D and 2D */
+ case 0x09c:
+ case 0x094:
+ case 0x017: /* Intuos3 2D Mouse */
+ wacom->tool[idx] = BTN_TOOL_MOUSE;
+ break;
+ case 0x096: /* Lens cursor */
+ case 0x097: /* Intuos3 Lens cursor */
+ wacom->tool[idx] = BTN_TOOL_LENS;
+ break;
+ case 0x82a: /* Eraser */
+ case 0x85a:
+ case 0x91a:
+ case 0xd1a:
+ case 0x0fa:
+ case 0x82b: /* Intuos3 Grip Pen Eraser */
+ case 0x81b: /* Intuos3 Classic Pen Eraser */
+ case 0x91b: /* Intuos3 Airbrush Eraser */
+ wacom->tool[idx] = BTN_TOOL_RUBBER;
+ break;
+ case 0xd12:
+ case 0x912:
+ case 0x112:
+ case 0x913: /* Intuos3 Airbrush */
+ wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
+ break;
+ default: /* Unknown tool */
+ wacom->tool[idx] = BTN_TOOL_PEN;
+ }
+ return 1;
+ }
+
+ /* Exit report */
+ if ((data[1] & 0xfe) == 0x80) {
+ wacom_report_abs(wcombo, ABS_X, 0);
+ wacom_report_abs(wcombo, ABS_Y, 0);
+ wacom_report_abs(wcombo, ABS_DISTANCE, 0);
+ if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
+ wacom_report_key(wcombo, BTN_LEFT, 0);
+ wacom_report_key(wcombo, BTN_MIDDLE, 0);
+ wacom_report_key(wcombo, BTN_RIGHT, 0);
+ wacom_report_key(wcombo, BTN_SIDE, 0);
+ wacom_report_key(wcombo, BTN_EXTRA, 0);
+ wacom_report_abs(wcombo, ABS_THROTTLE, 0);
+ wacom_report_abs(wcombo, ABS_RZ, 0);
+ } else {
+ wacom_report_abs(wcombo, ABS_PRESSURE, 0);
+ wacom_report_abs(wcombo, ABS_TILT_X, 0);
+ wacom_report_abs(wcombo, ABS_TILT_Y, 0);
+ wacom_report_key(wcombo, BTN_STYLUS, 0);
+ wacom_report_key(wcombo, BTN_STYLUS2, 0);
+ wacom_report_key(wcombo, BTN_TOUCH, 0);
+ wacom_report_abs(wcombo, ABS_WHEEL, 0);
+ }
+ wacom_report_key(wcombo, wacom->tool[idx], 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 2;
+ }
+ return 0;
+}
+
+static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ unsigned int t;
+
+ /* general pen packet */
+ if ((data[1] & 0xb8) == 0xa0) {
+ t = (data[6] << 2) | ((data[7] >> 6) & 3);
+ wacom_report_abs(wcombo, ABS_PRESSURE, t);
+ wacom_report_abs(wcombo, ABS_TILT_X,
+ ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+ wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4);
+ wacom_report_key(wcombo, BTN_TOUCH, t > 10);
+ }
+
+ /* airbrush second packet */
+ if ((data[1] & 0xbc) == 0xb4) {
+ wacom_report_abs(wcombo, ABS_WHEEL,
+ (data[6] << 2) | ((data[7] >> 6) & 3));
+ wacom_report_abs(wcombo, ABS_TILT_X,
+ ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+ wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
+ }
+ return;
+}
+
+static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ unsigned int t;
+ int idx, result;
+
+ if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
+ dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ /* tool number */
+ idx = data[1] & 0x01;
+
+ /* pad packets. Works as a second tool and is always in prox */
+ if (data[0] == 12) {
+ /* initiate the pad as a device */
+ if (wacom->tool[1] != BTN_TOOL_FINGER)
+ wacom->tool[1] = BTN_TOOL_FINGER;
+
+ wacom_report_key(wcombo, BTN_0, (data[5] & 0x01));
+ wacom_report_key(wcombo, BTN_1, (data[5] & 0x02));
+ wacom_report_key(wcombo, BTN_2, (data[5] & 0x04));
+ wacom_report_key(wcombo, BTN_3, (data[5] & 0x08));
+ wacom_report_key(wcombo, BTN_4, (data[6] & 0x01));
+ wacom_report_key(wcombo, BTN_5, (data[6] & 0x02));
+ wacom_report_key(wcombo, BTN_6, (data[6] & 0x04));
+ wacom_report_key(wcombo, BTN_7, (data[6] & 0x08));
+ wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
+ wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
+
+ if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) |
+ data[2] | (data[3] & 0x1f) | data[4])
+ wacom_report_key(wcombo, wacom->tool[1], 1);
+ else
+ wacom_report_key(wcombo, wacom->tool[1], 0);
+ wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
+ return 1;
+ }
+
+ /* process in/out prox events */
+ result = wacom_intuos_inout(wacom, wcombo);
+ if (result)
+ return result-1;
+
+ /* Only large I3 and I1 & I2 support Lense Cursor */
+ if((wacom->tool[idx] == BTN_TOOL_LENS)
+ && ((wacom->features->type == INTUOS3)
+ || (wacom->features->type == INTUOS3S)))
+ return 0;
+
+ /* Cintiq doesn't send data when RDY bit isn't set */
+ if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
+ return 0;
+
+ if (wacom->features->type >= INTUOS3S) {
+ wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
+ wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
+ wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
+ } else {
+ wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4]));
+ wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
+ }
+
+ /* process general packets */
+ wacom_intuos_general(wacom, wcombo);
+
+ /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
+ if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
+
+ if (data[1] & 0x02) {
+ /* Rotation packet */
+ if (wacom->features->type >= INTUOS3S) {
+ /* I3 marker pen rotation reported as wheel
+ * due to valuator limitation
+ */
+ t = (data[6] << 3) | ((data[7] >> 5) & 7);
+ t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
+ ((t-1) / 2 + 450)) : (450 - t / 2) ;
+ wacom_report_abs(wcombo, ABS_WHEEL, t);
+ } else {
+ /* 4D mouse rotation packet */
+ t = (data[6] << 3) | ((data[7] >> 5) & 7);
+ wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ?
+ ((t - 1) / 2) : -t / 2);
+ }
+
+ } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3S) {
+ /* 4D mouse packet */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
+
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x20);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x10);
+ t = (data[6] << 2) | ((data[7] >> 6) & 3);
+ wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
+
+ } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
+ /* 2D mouse packet */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x10);
+ wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01)
+ - ((data[8] & 0x02) >> 1));
+
+ /* I3 2D mouse side buttons */
+ if (wacom->features->type >= INTUOS3S && wacom->features->type <= INTUOS3L) {
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20);
+ }
+
+ } else if (wacom->features->type < INTUOS3S || wacom->features->type == INTUOS3L) {
+ /* Lens cursor packets */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x10);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x08);
+ }
+ }
+
+ wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
+ wacom_report_key(wcombo, wacom->tool[idx], 1);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 1;
+}
+
+int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
+{
+ switch (wacom_wac->features->type) {
+ case PENPARTNER:
+ return (wacom_penpartner_irq(wacom_wac, wcombo));
+ break;
+ case PL:
+ return (wacom_pl_irq(wacom_wac, wcombo));
+ break;
+ case WACOM_G4:
+ case GRAPHIRE:
+ return (wacom_graphire_irq(wacom_wac, wcombo));
+ break;
+ case PTU:
+ return (wacom_ptu_irq(wacom_wac, wcombo));
+ break;
+ case INTUOS:
+ case INTUOS3S:
+ case INTUOS3:
+ case INTUOS3L:
+ case CINTIQ:
+ return (wacom_intuos_irq(wacom_wac, wcombo));
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ switch (wacom_wac->features->type) {
+ case WACOM_G4:
+ input_dev_g4(input_dev, wacom_wac);
+ /* fall through */
+ case GRAPHIRE:
+ input_dev_g(input_dev, wacom_wac);
+ break;
+ case INTUOS3:
+ case INTUOS3L:
+ case CINTIQ:
+ input_dev_i3(input_dev, wacom_wac);
+ /* fall through */
+ case INTUOS3S:
+ input_dev_i3s(input_dev, wacom_wac);
+ case INTUOS:
+ input_dev_i(input_dev, wacom_wac);
+ break;
+ case PL:
+ case PTU:
+ input_dev_pl(input_dev, wacom_wac);
+ break;
+ case PENPARTNER:
+ input_dev_pt(input_dev, wacom_wac);
+ break;
+ }
+ return;
+}
+
+static struct wacom_features wacom_features[] = {
+ { "Wacom Penpartner", 7, 5040, 3780, 255, 0, PENPARTNER },
+ { "Wacom Graphire", 8, 10206, 7422, 511, 63, GRAPHIRE },
+ { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 63, GRAPHIRE },
+ { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 63, GRAPHIRE },
+ { "Wacom Graphire3", 8, 10208, 7424, 511, 63, GRAPHIRE },
+ { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 63, GRAPHIRE },
+ { "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 63, WACOM_G4 },
+ { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 63, WACOM_G4 },
+ { "Wacom Volito", 8, 5104, 3712, 511, 63, GRAPHIRE },
+ { "Wacom PenStation2", 8, 3250, 2320, 255, 63, GRAPHIRE },
+ { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE },
+ { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE },
+ { "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE },
+ { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
+ { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
+ { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
+ { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
+ { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
+ { "Wacom PL400", 8, 5408, 4056, 255, 0, PL },
+ { "Wacom PL500", 8, 6144, 4608, 255, 0, PL },
+ { "Wacom PL600", 8, 6126, 4604, 255, 0, PL },
+ { "Wacom PL600SX", 8, 6260, 5016, 255, 0, PL },
+ { "Wacom PL550", 8, 6144, 4608, 511, 0, PL },
+ { "Wacom PL800", 8, 7220, 5780, 511, 0, PL },
+ { "Wacom PL700", 8, 6758, 5406, 511, 0, PL },
+ { "Wacom PL510", 8, 6282, 4762, 511, 0, PL },
+ { "Wacom DTU710", 8, 34080, 27660, 511, 0, PL },
+ { "Wacom DTF521", 8, 6282, 4762, 511, 0, PL },
+ { "Wacom DTF720", 8, 6858, 5506, 511, 0, PL },
+ { "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU },
+ { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS },
+ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
+ { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 31, INTUOS },
+ { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
+ { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
+ { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S },
+ { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 },
+ { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 63, INTUOS3 },
+ { "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 63, INTUOS3L },
+ { "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 63, INTUOS3L },
+ { "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 },
+ { "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S },
+ { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ },
+ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS },
+ { }
+};
+
+static struct usb_device_id wacom_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC4) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB7) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
+ { }
+};
+
+const struct usb_device_id * get_device_table(void) {
+ const struct usb_device_id * id_table = wacom_ids;
+ return id_table;
+}
+
+struct wacom_features * get_wacom_feature(const struct usb_device_id * id) {
+ int index = id - wacom_ids;
+ struct wacom_features *wf = &wacom_features[index];
+ return wf;
+}
+
+MODULE_DEVICE_TABLE(usb, wacom_ids);
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
new file mode 100644
index 00000000000..a5e12e8756d
--- /dev/null
+++ b/drivers/input/tablet/wacom_wac.h
@@ -0,0 +1,49 @@
+/*
+ * drivers/input/tablet/wacom_wac.h
+ *
+ * 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.
+ */
+#ifndef WACOM_WAC_H
+#define WACOM_WAC_H
+
+#define STYLUS_DEVICE_ID 0x02
+#define CURSOR_DEVICE_ID 0x06
+#define ERASER_DEVICE_ID 0x0A
+#define PAD_DEVICE_ID 0x0F
+
+enum {
+ PENPARTNER = 0,
+ GRAPHIRE,
+ WACOM_G4,
+ PTU,
+ PL,
+ INTUOS,
+ INTUOS3S,
+ INTUOS3,
+ INTUOS3L,
+ CINTIQ,
+ MAX_TYPE
+};
+
+struct wacom_features {
+ char *name;
+ int pktlen;
+ int x_max;
+ int y_max;
+ int pressure_max;
+ int distance_max;
+ int type;
+};
+
+struct wacom_wac {
+ unsigned char *data;
+ int tool[2];
+ int id[2];
+ __u32 serial[2];
+ struct wacom_features *features;
+};
+
+#endif
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 971618059a6..5e640aeb03c 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1,5 +1,5 @@
#
-# Mouse driver configuration
+# Touchscreen driver configuration
#
menuconfig INPUT_TOUCHSCREEN
bool "Touchscreens"
@@ -44,9 +44,9 @@ config TOUCHSCREEN_BITSY
config TOUCHSCREEN_CORGI
tristate "SharpSL (Corgi and Spitz series) touchscreen driver"
depends on PXA_SHARPSL
- default y
+ default y
help
- Say Y here to enable the driver for the touchscreen on the
+ Say Y here to enable the driver for the touchscreen on the
Sharp SL-C7xx and SL-Cxx00 series of PDAs.
If unsure, say N.
@@ -164,4 +164,58 @@ config TOUCHSCREEN_UCB1400
To compile this driver as a module, choose M here: the
module will be called ucb1400_ts.
+config TOUCHSCREEN_USB_COMPOSITE
+ tristate "USB Touchscreen Driver"
+ select USB
+ help
+ USB Touchscreen driver for:
+ - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
+ - PanJit TouchSet USB
+ - 3M MicroTouch USB (EX II series)
+ - ITM
+ - some other eTurboTouch
+ - Gunze AHL61
+ - DMC TSC-10/25
+
+ Have a look at <http://linux.chapter7.ch/touchkit/> for
+ a usage description and the required user-space stuff.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbtouchscreen.
+
+config TOUCHSCREEN_USB_EGALAX
+ default y
+ bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_PANJIT
+ default y
+ bool "PanJit device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_3M
+ default y
+ bool "3M/Microtouch EX II series device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ITM
+ default y
+ bool "ITM device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETURBO
+ default y
+ bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_GUNZE
+ default y
+ bool "Gunze AHL61 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_DMC_TSC10
+ default y
+ bool "DMC TSC-10/25 device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 30e6e2217a1..2f86d6ad06d 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -1,17 +1,18 @@
#
-# Makefile for the mouse drivers.
+# Makefile for the touchscreen drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
-obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
-obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
-obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
-obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
-obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
-obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
-obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
+obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
+obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
+obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 0a26e066354..693e3b2a65a 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -39,7 +39,8 @@
/*
* This code has been heavily tested on a Nokia 770, and lightly
* tested on other ads7846 devices (OSK/Mistral, Lubbock).
- * Support for ads7843 and ads7845 has only been stubbed in.
+ * Support for ads7843 tested on Atmel at91sam926x-EK.
+ * Support for ads7845 has only been stubbed in.
*
* IRQ handling needs a workaround because of a shortcoming in handling
* edge triggered IRQs on some platforms like the OMAP1/2. These
@@ -246,18 +247,16 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
/* REVISIT: take a few more samples, and compare ... */
- /* maybe off internal vREF */
- if (use_internal) {
- req->ref_off = REF_OFF;
- req->xfer[4].tx_buf = &req->ref_off;
- req->xfer[4].len = 1;
- spi_message_add_tail(&req->xfer[4], &req->msg);
-
- req->xfer[5].rx_buf = &req->scratch;
- req->xfer[5].len = 2;
- CS_CHANGE(req->xfer[5]);
- spi_message_add_tail(&req->xfer[5], &req->msg);
- }
+ /* converter in low power mode & enable PENIRQ */
+ req->ref_off = PWRDOWN;
+ req->xfer[4].tx_buf = &req->ref_off;
+ req->xfer[4].len = 1;
+ spi_message_add_tail(&req->xfer[4], &req->msg);
+
+ req->xfer[5].rx_buf = &req->scratch;
+ req->xfer[5].len = 2;
+ CS_CHANGE(req->xfer[5]);
+ spi_message_add_tail(&req->xfer[5], &req->msg);
ts->irq_disabled = 1;
disable_irq(spi->irq);
@@ -536,6 +535,9 @@ static void ads7846_rx(void *ads)
} else
Rt = 0;
+ if (ts->model == 7843)
+ Rt = ts->pressure_max / 2;
+
/* Sample found inconsistent by debouncing or pressure is beyond
* the maximum. Don't report it to user space, repeat at least
* once more the measurement
@@ -897,7 +899,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
input_dev->name = "ADS784x Touchscreen";
input_dev->phys = ts->phys;
- input_dev->cdev.dev = &spi->dev;
+ input_dev->dev.parent = &spi->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c
index e2945582828..e6a31d11878 100644
--- a/drivers/input/touchscreen/corgi_ts.c
+++ b/drivers/input/touchscreen/corgi_ts.c
@@ -300,8 +300,7 @@ static int __init corgits_probe(struct platform_device *pdev)
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0002;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &pdev->dev;
- input_dev->private = corgi_ts;
+ input_dev->dev.parent = &pdev->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
index 9d61cd133d0..557d781719f 100644
--- a/drivers/input/touchscreen/elo.c
+++ b/drivers/input/touchscreen/elo.c
@@ -312,14 +312,13 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
init_completion(&elo->cmd_done);
snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys);
- input_dev->private = elo;
input_dev->name = "Elo Serial TouchScreen";
input_dev->phys = elo->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_ELO;
input_dev->id.product = elo->id;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
index 9157eb148e8..39d602600d7 100644
--- a/drivers/input/touchscreen/gunze.c
+++ b/drivers/input/touchscreen/gunze.c
@@ -130,13 +130,13 @@ static int gunze_connect(struct serio *serio, struct serio_driver *drv)
gunze->dev = input_dev;
snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys);
- input_dev->private = gunze;
input_dev->name = "Gunze AHL-51S TouchScreen";
input_dev->phys = gunze->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_GUNZE;
input_dev->id.product = 0x0051;
input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0);
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
index c4116d4f64e..09ed7803cb8 100644
--- a/drivers/input/touchscreen/h3600_ts_input.c
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -147,7 +147,7 @@ enum flite_pwr {
unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr)
{
unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness;
- struct h3600_dev *ts = dev->private;
+ struct h3600_dev *ts = input_get_drvdata(dev);
/* Must be in this order */
ts->serio->write(ts->serio, 1);
@@ -260,7 +260,7 @@ static int h3600ts_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
#if 0
- struct h3600_dev *ts = dev->private;
+ struct h3600_dev *ts = input_get_drvdata(dev);
switch (type) {
case EV_LED: {
@@ -367,8 +367,9 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
input_dev->id.vendor = SERIO_H3600;
input_dev->id.product = 0x0666; /* FIXME !!! We can ask the hardware */
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
- input_dev->private = ts;
+ input_dev->dev.parent = &serio->dev;
+
+ input_set_drvdata(input_dev, ts);
input_dev->event = h3600ts_event;
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
index 24908747274..61c15024c2a 100644
--- a/drivers/input/touchscreen/hp680_ts_input.c
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -21,7 +21,7 @@
static void do_softint(void *data);
static struct input_dev *hp680_ts_dev;
-static DECLARE_WORK(work, do_softint, 0);
+static DECLARE_WORK(work, do_softint);
static void do_softint(void *data)
{
diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c
index c3c2d735d0e..4ec3b1f940c 100644
--- a/drivers/input/touchscreen/mtouch.c
+++ b/drivers/input/touchscreen/mtouch.c
@@ -144,13 +144,13 @@ static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
mtouch->dev = input_dev;
snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys);
- input_dev->private = mtouch;
input_dev->name = "MicroTouch Serial TouchScreen";
input_dev->phys = mtouch->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_MICROTOUCH;
input_dev->id.product = 0;
input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0);
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
index bd2767991ae..f2c0d3c7149 100644
--- a/drivers/input/touchscreen/penmount.c
+++ b/drivers/input/touchscreen/penmount.c
@@ -105,14 +105,13 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
pm->dev = input_dev;
snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
- input_dev->private = pm;
input_dev->name = "Penmount Serial TouchScreen";
input_dev->phys = pm->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_PENMOUNT;
input_dev->id.product = 0;
input_dev->id.version = 0x0100;
- input_dev->cdev.dev = &serio->dev;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
index 35ba46c6ad2..3def7bb1df4 100644
--- a/drivers/input/touchscreen/touchright.c
+++ b/drivers/input/touchscreen/touchright.c
@@ -118,13 +118,13 @@ static int tr_connect(struct serio *serio, struct serio_driver *drv)
tr->dev = input_dev;
snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys);
- input_dev->private = tr;
input_dev->name = "Touchright Serial TouchScreen";
input_dev->phys = tr->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_TOUCHRIGHT;
input_dev->id.product = 0;
input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0);
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
index 4dc073dacab..ac4bdcf1866 100644
--- a/drivers/input/touchscreen/touchwin.c
+++ b/drivers/input/touchscreen/touchwin.c
@@ -125,13 +125,13 @@ static int tw_connect(struct serio *serio, struct serio_driver *drv)
tw->dev = input_dev;
snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys);
- input_dev->private = tw;
input_dev->name = "Touchwindow Serial TouchScreen";
input_dev->phys = tw->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_TOUCHWIN;
input_dev->id.product = 0;
input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0);
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
index e8606c48c9c..6582816a047 100644
--- a/drivers/input/touchscreen/ucb1400_ts.c
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -97,6 +97,8 @@ struct ucb1400 {
};
static int adcsync;
+static int ts_delay = 55; /* us */
+static int ts_delay_pressure; /* us */
static inline u16 ucb1400_reg_read(struct ucb1400 *ucb, u16 reg)
{
@@ -159,6 +161,7 @@ static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb)
UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+ udelay(ts_delay_pressure);
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY);
}
@@ -180,7 +183,7 @@ static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb)
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
- udelay(55);
+ udelay(ts_delay);
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY);
}
@@ -203,7 +206,7 @@ static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400 *ucb)
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
- udelay(55);
+ udelay(ts_delay);
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPX);
}
@@ -369,7 +372,7 @@ static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid)
static int ucb1400_ts_open(struct input_dev *idev)
{
- struct ucb1400 *ucb = idev->private;
+ struct ucb1400 *ucb = input_get_drvdata(idev);
int ret = 0;
BUG_ON(ucb->ts_task);
@@ -385,7 +388,7 @@ static int ucb1400_ts_open(struct input_dev *idev)
static void ucb1400_ts_close(struct input_dev *idev)
{
- struct ucb1400 *ucb = idev->private;
+ struct ucb1400 *ucb = input_get_drvdata(idev);
if (ucb->ts_task)
kthread_stop(ucb->ts_task);
@@ -507,8 +510,9 @@ static int ucb1400_ts_probe(struct device *dev)
}
printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq);
- idev->private = ucb;
- idev->cdev.dev = dev;
+ input_set_drvdata(idev, ucb);
+
+ idev->dev.parent = dev;
idev->name = "UCB1400 touchscreen interface";
idev->id.vendor = ucb1400_reg_read(ucb, AC97_VENDOR_ID1);
idev->id.product = id;
@@ -571,7 +575,15 @@ static void __exit ucb1400_ts_exit(void)
driver_unregister(&ucb1400_ts_driver);
}
-module_param(adcsync, int, 0444);
+module_param(adcsync, bool, 0444);
+MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin.");
+
+module_param(ts_delay, int, 0444);
+MODULE_PARM_DESC(ts_delay, "Delay between panel setup and position read. Default = 55us.");
+
+module_param(ts_delay_pressure, int, 0444);
+MODULE_PARM_DESC(ts_delay_pressure,
+ "delay between panel setup and pressure read. Default = 0us.");
module_init(ucb1400_ts_init);
module_exit(ucb1400_ts_exit);
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
new file mode 100644
index 00000000000..8e18e6c6477
--- /dev/null
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -0,0 +1,839 @@
+/******************************************************************************
+ * usbtouchscreen.c
+ * Driver for USB Touchscreens, supporting those devices:
+ * - eGalax Touchkit
+ * includes eTurboTouch CT-410/510/700
+ * - 3M/Microtouch EX II series
+ * - ITM
+ * - PanJit TouchSet
+ * - eTurboTouch
+ * - Gunze AHL61
+ * - DMC TSC-10/25
+ *
+ * Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Driver is based on touchkitusb.c
+ * - ITM parts are from itmtouch.c
+ * - 3M parts are from mtouchusb.c
+ * - PanJit parts are from an unmerged driver by Lanslott Gish
+ * - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged
+ * driver from Marius Vollmer
+ *
+ *****************************************************************************/
+
+//#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+
+
+#define DRIVER_VERSION "v0.5"
+#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
+#define DRIVER_DESC "USB Touchscreen Driver"
+
+static int swap_xy;
+module_param(swap_xy, bool, 0644);
+MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
+
+/* device specifc data/functions */
+struct usbtouch_usb;
+struct usbtouch_device_info {
+ int min_xc, max_xc;
+ int min_yc, max_yc;
+ int min_press, max_press;
+ int rept_size;
+ int flags;
+
+ void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
+ int (*get_pkt_len) (unsigned char *pkt, int len);
+ int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt);
+ int (*init) (struct usbtouch_usb *usbtouch);
+};
+
+#define USBTOUCH_FLG_BUFFER 0x01
+
+
+/* a usbtouch device */
+struct usbtouch_usb {
+ unsigned char *data;
+ dma_addr_t data_dma;
+ unsigned char *buffer;
+ int buf_len;
+ struct urb *irq;
+ struct usb_device *udev;
+ struct input_dev *input;
+ struct usbtouch_device_info *type;
+ char name[128];
+ char phys[64];
+
+ int x, y;
+ int touch, press;
+};
+
+
+#if defined(CONFIG_USB_TOUCHSCREEN_EGALAX) || defined(CONFIG_USB_TOUCHSCREEN_ETURBO)
+#define MULTI_PACKET
+#endif
+
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len);
+#endif
+
+/* device types */
+enum {
+ DEVTPYE_DUMMY = -1,
+ DEVTYPE_EGALAX,
+ DEVTYPE_PANJIT,
+ DEVTYPE_3M,
+ DEVTYPE_ITM,
+ DEVTYPE_ETURBO,
+ DEVTYPE_GUNZE,
+ DEVTYPE_DMC_TSC10,
+};
+
+static struct usb_device_id usbtouch_devices[] = {
+#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
+ {USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
+ {USB_DEVICE(0x134c, 0x0001), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0002), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0003), .driver_info = DEVTYPE_PANJIT},
+ {USB_DEVICE(0x134c, 0x0004), .driver_info = DEVTYPE_PANJIT},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_3M
+ {USB_DEVICE(0x0596, 0x0001), .driver_info = DEVTYPE_3M},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_ITM
+ {USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+ {USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+ {USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
+ {USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10},
+#endif
+
+ {}
+};
+
+
+/*****************************************************************************
+ * eGalax part
+ */
+
+#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
+
+#define EGALAX_PKT_TYPE_MASK 0xFE
+#define EGALAX_PKT_TYPE_REPT 0x80
+#define EGALAX_PKT_TYPE_DIAG 0x0A
+
+static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
+ return 0;
+
+ dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F);
+ dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F);
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+
+static int egalax_get_pkt_len(unsigned char *buf, int len)
+{
+ switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
+ case EGALAX_PKT_TYPE_REPT:
+ return 5;
+
+ case EGALAX_PKT_TYPE_DIAG:
+ if (len < 2)
+ return -1;
+
+ return buf[1] + 2;
+ }
+
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * PanJit Part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
+static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
+ dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * 3M/Microtouch Part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_3M
+
+#define MTOUCHUSB_ASYNC_REPORT 1
+#define MTOUCHUSB_RESET 7
+#define MTOUCHUSB_REQ_CTRLLR_ID 10
+
+static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = (pkt[8] << 8) | pkt[7];
+ dev->y = (pkt[10] << 8) | pkt[9];
+ dev->touch = (pkt[2] & 0x40) ? 1 : 0;
+
+ return 1;
+}
+
+static int mtouch_init(struct usbtouch_usb *usbtouch)
+{
+ int ret, i;
+
+ ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
+ MTOUCHUSB_RESET,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d",
+ __FUNCTION__, ret);
+ if (ret < 0)
+ return ret;
+ msleep(150);
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
+ MTOUCHUSB_ASYNC_REPORT,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
+ __FUNCTION__, ret);
+ if (ret >= 0)
+ break;
+ if (ret != -EPIPE)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * ITM Part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_ITM
+static int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ int touch;
+ /*
+ * ITM devices report invalid x/y data if not touched.
+ * if the screen was touched before but is not touched any more
+ * report touch as 0 with the last valid x/y data once. then stop
+ * reporting data until touched again.
+ */
+ dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F);
+
+ touch = ~pkt[7] & 0x20;
+ if (!touch) {
+ if (dev->touch) {
+ dev->touch = 0;
+ return 1;
+ }
+
+ return 0;
+ }
+
+ dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F);
+ dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F);
+ dev->touch = touch;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * eTurboTouch part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+static int eturbo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ unsigned int shift;
+
+ /* packets should start with sync */
+ if (!(pkt[0] & 0x80))
+ return 0;
+
+ shift = (6 - (pkt[0] & 0x03));
+ dev->x = ((pkt[3] << 7) | pkt[4]) >> shift;
+ dev->y = ((pkt[1] << 7) | pkt[2]) >> shift;
+ dev->touch = (pkt[0] & 0x10) ? 1 : 0;
+
+ return 1;
+}
+
+static int eturbo_get_pkt_len(unsigned char *buf, int len)
+{
+ if (buf[0] & 0x80)
+ return 5;
+ if (buf[0] == 0x01)
+ return 3;
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * Gunze part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
+ return 0;
+
+ dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
+ dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
+ dev->touch = pkt[0] & 0x20;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * DMC TSC-10/25 Part
+ *
+ * Documentation about the controller and it's protocol can be found at
+ * http://www.dmccoltd.com/files/controler/tsc10usb_pi_e.pdf
+ * http://www.dmccoltd.com/files/controler/tsc25_usb_e.pdf
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
+
+/* supported data rates. currently using 130 */
+#define TSC10_RATE_POINT 0x50
+#define TSC10_RATE_30 0x40
+#define TSC10_RATE_50 0x41
+#define TSC10_RATE_80 0x42
+#define TSC10_RATE_100 0x43
+#define TSC10_RATE_130 0x44
+#define TSC10_RATE_150 0x45
+
+/* commands */
+#define TSC10_CMD_RESET 0x55
+#define TSC10_CMD_RATE 0x05
+#define TSC10_CMD_DATA1 0x01
+
+static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
+{
+ struct usb_device *dev = usbtouch->udev;
+ int ret;
+ unsigned char buf[2];
+
+ /* reset */
+ buf[0] = buf[1] = 0xFF;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_RESET,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ return ret;
+ if (buf[0] != 0x06 || buf[1] != 0x00)
+ return -ENODEV;
+
+ /* set coordinate output rate */
+ buf[0] = buf[1] = 0xFF;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_RATE,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ return ret;
+ if (buf[0] != 0x06 || buf[1] != 0x00)
+ return -ENODEV;
+
+ /* start sending data */
+ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
+ TSC10_CMD_DATA1,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+
+static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[2] & 0x03) << 8) | pkt[1];
+ dev->y = ((pkt[4] & 0x03) << 8) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
+ * the different device descriptors
+ */
+static struct usbtouch_device_info usbtouch_dev_info[] = {
+#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
+ [DEVTYPE_EGALAX] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 16,
+ .flags = USBTOUCH_FLG_BUFFER,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = egalax_get_pkt_len,
+ .read_data = egalax_read_data,
+ },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
+ [DEVTYPE_PANJIT] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 8,
+ .read_data = panjit_read_data,
+ },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_3M
+ [DEVTYPE_3M] = {
+ .min_xc = 0x0,
+ .max_xc = 0x4000,
+ .min_yc = 0x0,
+ .max_yc = 0x4000,
+ .rept_size = 11,
+ .read_data = mtouch_read_data,
+ .init = mtouch_init,
+ },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_ITM
+ [DEVTYPE_ITM] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .max_press = 0xff,
+ .rept_size = 8,
+ .read_data = itm_read_data,
+ },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+ [DEVTYPE_ETURBO] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 8,
+ .flags = USBTOUCH_FLG_BUFFER,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = eturbo_get_pkt_len,
+ .read_data = eturbo_read_data,
+ },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+ [DEVTYPE_GUNZE] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 4,
+ .read_data = gunze_read_data,
+ },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10
+ [DEVTYPE_DMC_TSC10] = {
+ .min_xc = 0x0,
+ .max_xc = 0x03ff,
+ .min_yc = 0x0,
+ .max_yc = 0x03ff,
+ .rept_size = 5,
+ .init = dmc_tsc10_init,
+ .read_data = dmc_tsc10_read_data,
+ },
+#endif
+};
+
+
+/*****************************************************************************
+ * Generic Part
+ */
+static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len)
+{
+ struct usbtouch_device_info *type = usbtouch->type;
+
+ if (!type->read_data(usbtouch, pkt))
+ return;
+
+ input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch);
+
+ if (swap_xy) {
+ input_report_abs(usbtouch->input, ABS_X, usbtouch->y);
+ input_report_abs(usbtouch->input, ABS_Y, usbtouch->x);
+ } else {
+ input_report_abs(usbtouch->input, ABS_X, usbtouch->x);
+ input_report_abs(usbtouch->input, ABS_Y, usbtouch->y);
+ }
+ if (type->max_press)
+ input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press);
+ input_sync(usbtouch->input);
+}
+
+
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ unsigned char *pkt, int len)
+{
+ unsigned char *buffer;
+ int pkt_len, pos, buf_len, tmp;
+
+ /* process buffer */
+ if (unlikely(usbtouch->buf_len)) {
+ /* try to get size */
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+
+ /* drop? */
+ if (unlikely(!pkt_len))
+ goto out_flush_buf;
+
+ /* need to append -pkt_len bytes before able to get size */
+ if (unlikely(pkt_len < 0)) {
+ int append = -pkt_len;
+ if (unlikely(append > len))
+ append = len;
+ if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
+ usbtouch->buf_len += append;
+
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+ if (pkt_len < 0)
+ return;
+ }
+
+ /* append */
+ tmp = pkt_len - usbtouch->buf_len;
+ if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
+ usbtouch_process_pkt(usbtouch, usbtouch->buffer, pkt_len);
+
+ buffer = pkt + tmp;
+ buf_len = len - tmp;
+ } else {
+ buffer = pkt;
+ buf_len = len;
+ }
+
+ /* loop over the received packet, process */
+ pos = 0;
+ while (pos < buf_len) {
+ /* get packet len */
+ pkt_len = usbtouch->type->get_pkt_len(buffer + pos, len);
+
+ /* unknown packet: drop everything */
+ if (unlikely(!pkt_len))
+ goto out_flush_buf;
+
+ /* full packet: process */
+ if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
+ usbtouch_process_pkt(usbtouch, buffer + pos, pkt_len);
+ } else {
+ /* incomplete packet: save in buffer */
+ memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
+ usbtouch->buf_len = buf_len - pos;
+ return;
+ }
+ pos += pkt_len;
+ }
+
+out_flush_buf:
+ usbtouch->buf_len = 0;
+ return;
+}
+#endif
+
+
+static void usbtouch_irq(struct urb *urb)
+{
+ struct usbtouch_usb *usbtouch = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ETIME:
+ /* this urb is timing out */
+ dbg("%s - urb timed out - was the device unplugged?",
+ __FUNCTION__);
+ return;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
+
+exit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result: %d",
+ __FUNCTION__, retval);
+}
+
+static int usbtouch_open(struct input_dev *input)
+{
+ struct usbtouch_usb *usbtouch = input_get_drvdata(input);
+
+ usbtouch->irq->dev = usbtouch->udev;
+
+ if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void usbtouch_close(struct input_dev *input)
+{
+ struct usbtouch_usb *usbtouch = input_get_drvdata(input);
+
+ usb_kill_urb(usbtouch->irq);
+}
+
+
+static void usbtouch_free_buffers(struct usb_device *udev,
+ struct usbtouch_usb *usbtouch)
+{
+ usb_buffer_free(udev, usbtouch->type->rept_size,
+ usbtouch->data, usbtouch->data_dma);
+ kfree(usbtouch->buffer);
+}
+
+
+static int usbtouch_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usbtouch_usb *usbtouch;
+ struct input_dev *input_dev;
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usbtouch_device_info *type;
+ int err = -ENOMEM;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+
+ usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!usbtouch || !input_dev)
+ goto out_free;
+
+ type = &usbtouch_dev_info[id->driver_info];
+ usbtouch->type = type;
+ if (!type->process_pkt)
+ type->process_pkt = usbtouch_process_pkt;
+
+ usbtouch->data = usb_buffer_alloc(udev, type->rept_size,
+ GFP_KERNEL, &usbtouch->data_dma);
+ if (!usbtouch->data)
+ goto out_free;
+
+ if (type->flags & USBTOUCH_FLG_BUFFER) {
+ usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);
+ if (!usbtouch->buffer)
+ goto out_free_buffers;
+ }
+
+ usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usbtouch->irq) {
+ dbg("%s - usb_alloc_urb failed: usbtouch->irq", __FUNCTION__);
+ goto out_free_buffers;
+ }
+
+ usbtouch->udev = udev;
+ usbtouch->input = input_dev;
+
+ if (udev->manufacturer)
+ strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));
+
+ if (udev->product) {
+ if (udev->manufacturer)
+ strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
+ strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
+ }
+
+ if (!strlen(usbtouch->name))
+ snprintf(usbtouch->name, sizeof(usbtouch->name),
+ "USB Touchscreen %04x:%04x",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));
+ strlcpy(usbtouch->phys, "/input0", sizeof(usbtouch->phys));
+
+ input_dev->name = usbtouch->name;
+ input_dev->phys = usbtouch->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, usbtouch);
+
+ input_dev->open = usbtouch_open;
+ input_dev->close = usbtouch_close;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0);
+ if (type->max_press)
+ input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
+ type->max_press, 0, 0);
+
+ usb_fill_int_urb(usbtouch->irq, usbtouch->udev,
+ usb_rcvintpipe(usbtouch->udev, endpoint->bEndpointAddress),
+ usbtouch->data, type->rept_size,
+ usbtouch_irq, usbtouch, endpoint->bInterval);
+
+ usbtouch->irq->dev = usbtouch->udev;
+ usbtouch->irq->transfer_dma = usbtouch->data_dma;
+ usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* device specific init */
+ if (type->init) {
+ err = type->init(usbtouch);
+ if (err) {
+ dbg("%s - type->init() failed, err: %d", __FUNCTION__, err);
+ goto out_free_buffers;
+ }
+ }
+
+ err = input_register_device(usbtouch->input);
+ if (err) {
+ dbg("%s - input_register_device failed, err: %d", __FUNCTION__, err);
+ goto out_free_buffers;
+ }
+
+ usb_set_intfdata(intf, usbtouch);
+
+ return 0;
+
+out_free_buffers:
+ usbtouch_free_buffers(udev, usbtouch);
+out_free:
+ input_free_device(input_dev);
+ kfree(usbtouch);
+ return err;
+}
+
+static void usbtouch_disconnect(struct usb_interface *intf)
+{
+ struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+
+ dbg("%s - called", __FUNCTION__);
+
+ if (!usbtouch)
+ return;
+
+ dbg("%s - usbtouch is initialized, cleaning up", __FUNCTION__);
+ usb_set_intfdata(intf, NULL);
+ usb_kill_urb(usbtouch->irq);
+ input_unregister_device(usbtouch->input);
+ usb_free_urb(usbtouch->irq);
+ usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
+ kfree(usbtouch);
+}
+
+MODULE_DEVICE_TABLE(usb, usbtouch_devices);
+
+static struct usb_driver usbtouch_driver = {
+ .name = "usbtouchscreen",
+ .probe = usbtouch_probe,
+ .disconnect = usbtouch_disconnect,
+ .id_table = usbtouch_devices,
+};
+
+static int __init usbtouch_init(void)
+{
+ return usb_register(&usbtouch_driver);
+}
+
+static void __exit usbtouch_cleanup(void)
+{
+ usb_deregister(&usbtouch_driver);
+}
+
+module_init(usbtouch_init);
+module_exit(usbtouch_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("touchkitusb");
+MODULE_ALIAS("itmtouch");
+MODULE_ALIAS("mtouchusb");
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c
index 0300dca8591..8238b13874c 100644
--- a/drivers/input/tsdev.c
+++ b/drivers/input/tsdev.c
@@ -48,7 +48,6 @@
#include <linux/init.h>
#include <linux/input.h>
#include <linux/major.h>
-#include <linux/smp_lock.h>
#include <linux/random.h>
#include <linux/time.h>
#include <linux/device.h>
@@ -111,13 +110,13 @@ struct tsdev {
int minor;
char name[8];
wait_queue_head_t wait;
- struct list_head list;
+ struct list_head client_list;
struct input_handle handle;
int x, y, pressure;
struct ts_calibration cal;
};
-struct tsdev_list {
+struct tsdev_client {
struct fasync_struct *fasync;
struct list_head node;
struct tsdev *tsdev;
@@ -139,38 +138,49 @@ static struct tsdev *tsdev_table[TSDEV_MINORS/2];
static int tsdev_fasync(int fd, struct file *file, int on)
{
- struct tsdev_list *list = file->private_data;
+ struct tsdev_client *client = file->private_data;
int retval;
- retval = fasync_helper(fd, file, on, &list->fasync);
+ retval = fasync_helper(fd, file, on, &client->fasync);
return retval < 0 ? retval : 0;
}
static int tsdev_open(struct inode *inode, struct file *file)
{
int i = iminor(inode) - TSDEV_MINOR_BASE;
- struct tsdev_list *list;
+ struct tsdev_client *client;
+ struct tsdev *tsdev;
+ int error;
printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled "
"for removal.\nSee Documentation/feature-removal-schedule.txt "
"for details.\n");
- if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK])
+ if (i >= TSDEV_MINORS)
+ return -ENODEV;
+
+ tsdev = tsdev_table[i & TSDEV_MINOR_MASK];
+ if (!tsdev || !tsdev->exist)
return -ENODEV;
- if (!(list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL)))
+ client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL);
+ if (!client)
return -ENOMEM;
- list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0;
+ client->tsdev = tsdev;
+ client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0;
+ list_add_tail(&client->node, &tsdev->client_list);
- i &= TSDEV_MINOR_MASK;
- list->tsdev = tsdev_table[i];
- list_add_tail(&list->node, &tsdev_table[i]->list);
- file->private_data = list;
+ if (!tsdev->open++ && tsdev->exist) {
+ error = input_open_device(&tsdev->handle);
+ if (error) {
+ list_del(&client->node);
+ kfree(client);
+ return error;
+ }
+ }
- if (!list->tsdev->open++)
- if (list->tsdev->exist)
- input_open_device(&list->tsdev->handle);
+ file->private_data = client;
return 0;
}
@@ -182,45 +192,48 @@ static void tsdev_free(struct tsdev *tsdev)
static int tsdev_release(struct inode *inode, struct file *file)
{
- struct tsdev_list *list = file->private_data;
+ struct tsdev_client *client = file->private_data;
+ struct tsdev *tsdev = client->tsdev;
tsdev_fasync(-1, file, 0);
- list_del(&list->node);
- if (!--list->tsdev->open) {
- if (list->tsdev->exist)
- input_close_device(&list->tsdev->handle);
+ list_del(&client->node);
+ kfree(client);
+
+ if (!--tsdev->open) {
+ if (tsdev->exist)
+ input_close_device(&tsdev->handle);
else
- tsdev_free(list->tsdev);
+ tsdev_free(tsdev);
}
- kfree(list);
+
return 0;
}
static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
- loff_t * ppos)
+ loff_t *ppos)
{
- struct tsdev_list *list = file->private_data;
+ struct tsdev_client *client = file->private_data;
+ struct tsdev *tsdev = client->tsdev;
int retval = 0;
- if (list->head == list->tail && list->tsdev->exist && (file->f_flags & O_NONBLOCK))
+ if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
- retval = wait_event_interruptible(list->tsdev->wait,
- list->head != list->tail || !list->tsdev->exist);
-
+ retval = wait_event_interruptible(tsdev->wait,
+ client->head != client->tail || !tsdev->exist);
if (retval)
return retval;
- if (!list->tsdev->exist)
+ if (!tsdev->exist)
return -ENODEV;
- while (list->head != list->tail &&
+ while (client->head != client->tail &&
retval + sizeof (struct ts_event) <= count) {
- if (copy_to_user (buffer + retval, list->event + list->tail,
+ if (copy_to_user (buffer + retval, client->event + client->tail,
sizeof (struct ts_event)))
return -EFAULT;
- list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
+ client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
retval += sizeof (struct ts_event);
}
@@ -228,32 +241,33 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
}
/* No kernel lock - fine */
-static unsigned int tsdev_poll(struct file *file, poll_table * wait)
+static unsigned int tsdev_poll(struct file *file, poll_table *wait)
{
- struct tsdev_list *list = file->private_data;
+ struct tsdev_client *client = file->private_data;
+ struct tsdev *tsdev = client->tsdev;
- poll_wait(file, &list->tsdev->wait, wait);
- return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
- (list->tsdev->exist ? 0 : (POLLHUP | POLLERR));
+ poll_wait(file, &tsdev->wait, wait);
+ return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
+ (tsdev->exist ? 0 : (POLLHUP | POLLERR));
}
static int tsdev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct tsdev_list *list = file->private_data;
- struct tsdev *tsdev = list->tsdev;
+ struct tsdev_client *client = file->private_data;
+ struct tsdev *tsdev = client->tsdev;
int retval = 0;
switch (cmd) {
case TS_GET_CAL:
- if (copy_to_user ((void __user *)arg, &tsdev->cal,
- sizeof (struct ts_calibration)))
+ if (copy_to_user((void __user *)arg, &tsdev->cal,
+ sizeof (struct ts_calibration)))
retval = -EFAULT;
break;
case TS_SET_CAL:
- if (copy_from_user (&tsdev->cal, (void __user *)arg,
- sizeof (struct ts_calibration)))
+ if (copy_from_user(&tsdev->cal, (void __user *)arg,
+ sizeof (struct ts_calibration)))
retval = -EFAULT;
break;
@@ -279,7 +293,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
struct tsdev *tsdev = handle->private;
- struct tsdev_list *list;
+ struct tsdev_client *client;
struct timeval time;
switch (type) {
@@ -343,18 +357,18 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
if (type != EV_SYN || code != SYN_REPORT)
return;
- list_for_each_entry(list, &tsdev->list, node) {
+ list_for_each_entry(client, &tsdev->client_list, node) {
int x, y, tmp;
do_gettimeofday(&time);
- list->event[list->head].millisecs = time.tv_usec / 100;
- list->event[list->head].pressure = tsdev->pressure;
+ client->event[client->head].millisecs = time.tv_usec / 100;
+ client->event[client->head].pressure = tsdev->pressure;
x = tsdev->x;
y = tsdev->y;
/* Calibration */
- if (!list->raw) {
+ if (!client->raw) {
x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans;
y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans;
if (tsdev->cal.xyswap) {
@@ -362,33 +376,35 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
}
}
- list->event[list->head].x = x;
- list->event[list->head].y = y;
- list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);
- kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ client->event[client->head].x = x;
+ client->event[client->head].y = y;
+ client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1);
+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&tsdev->wait);
}
-static struct input_handle *tsdev_connect(struct input_handler *handler,
- struct input_dev *dev,
- const struct input_device_id *id)
+static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct tsdev *tsdev;
struct class_device *cdev;
+ dev_t devt;
int minor, delta;
+ int error;
for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++);
if (minor >= TSDEV_MINORS / 2) {
printk(KERN_ERR
"tsdev: You have way too many touchscreens\n");
- return NULL;
+ return -ENFILE;
}
- if (!(tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL)))
- return NULL;
+ tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL);
+ if (!tsdev)
+ return -ENOMEM;
- INIT_LIST_HEAD(&tsdev->list);
+ INIT_LIST_HEAD(&tsdev->client_list);
init_waitqueue_head(&tsdev->wait);
sprintf(tsdev->name, "ts%d", minor);
@@ -415,23 +431,45 @@ static struct input_handle *tsdev_connect(struct input_handler *handler,
tsdev_table[minor] = tsdev;
- cdev = class_device_create(&input_class, &dev->cdev,
- MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor),
- dev->cdev.dev, tsdev->name);
+ devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor),
+
+ cdev = class_device_create(&input_class, &dev->cdev, devt,
+ dev->cdev.dev, tsdev->name);
+ if (IS_ERR(cdev)) {
+ error = PTR_ERR(cdev);
+ goto err_free_tsdev;
+ }
/* temporary symlink to keep userspace happy */
- sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
- tsdev->name);
+ error = sysfs_create_link(&input_class.subsys.kobj,
+ &cdev->kobj, tsdev->name);
+ if (error)
+ goto err_cdev_destroy;
+
+ error = input_register_handle(&tsdev->handle);
+ if (error)
+ goto err_remove_link;
- return &tsdev->handle;
+ return 0;
+
+ err_remove_link:
+ sysfs_remove_link(&input_class.subsys.kobj, tsdev->name);
+ err_cdev_destroy:
+ class_device_destroy(&input_class, devt);
+ err_free_tsdev:
+ tsdev_table[minor] = NULL;
+ kfree(tsdev);
+ return error;
}
static void tsdev_disconnect(struct input_handle *handle)
{
struct tsdev *tsdev = handle->private;
- struct tsdev_list *list;
+ struct tsdev_client *client;
+
+ input_unregister_handle(handle);
- sysfs_remove_link(&input_class.subsys.kset.kobj, tsdev->name);
+ sysfs_remove_link(&input_class.subsys.kobj, tsdev->name);
class_device_destroy(&input_class,
MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + tsdev->minor));
tsdev->exist = 0;
@@ -439,8 +477,8 @@ static void tsdev_disconnect(struct input_handle *handle)
if (tsdev->open) {
input_close_device(handle);
wake_up_interruptible(&tsdev->wait);
- list_for_each_entry(list, &tsdev->list, node)
- kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+ list_for_each_entry(client, &tsdev->client_list, node)
+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
} else
tsdev_free(tsdev);
}