diff options
Diffstat (limited to 'drivers/input')
38 files changed, 2089 insertions, 1220 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index be6b93c20f6..ab4b2d9b532 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -30,6 +30,7 @@ struct evdev { wait_queue_head_t wait; struct evdev_client *grab; struct list_head client_list; + struct device dev; }; struct evdev_client { @@ -94,8 +95,10 @@ static int evdev_flush(struct file *file, fl_owner_t id) return input_flush_device(&evdev->handle, file); } -static void evdev_free(struct evdev *evdev) +static void evdev_free(struct device *dev) { + struct evdev *evdev = container_of(dev, struct evdev, dev); + evdev_table[evdev->minor] = NULL; kfree(evdev); } @@ -114,12 +117,10 @@ static int evdev_release(struct inode *inode, struct file *file) list_del(&client->node); kfree(client); - if (!--evdev->open) { - if (evdev->exist) - input_close_device(&evdev->handle); - else - evdev_free(evdev); - } + if (!--evdev->open && evdev->exist) + input_close_device(&evdev->handle); + + put_device(&evdev->dev); return 0; } @@ -139,24 +140,32 @@ static int evdev_open(struct inode *inode, struct file *file) if (!evdev || !evdev->exist) return -ENODEV; + get_device(&evdev->dev); + client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); - if (!client) - return -ENOMEM; + if (!client) { + error = -ENOMEM; + goto err_put_evdev; + } client->evdev = evdev; list_add_tail(&client->node, &evdev->client_list); if (!evdev->open++ && evdev->exist) { error = input_open_device(&evdev->handle); - if (error) { - list_del(&client->node); - kfree(client); - return error; - } + if (error) + goto err_free_client; } file->private_data = client; return 0; + + err_free_client: + list_del(&client->node); + kfree(client); + err_put_evdev: + put_device(&evdev->dev); + return error; } #ifdef CONFIG_COMPAT @@ -625,8 +634,6 @@ 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; @@ -649,38 +656,32 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, evdev->handle.name = evdev->name; evdev->handle.handler = handler; evdev->handle.private = evdev; - sprintf(evdev->name, "event%d", minor); - - evdev_table[minor] = evdev; + snprintf(evdev->name, sizeof(evdev->name), "event%d", minor); - devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), + snprintf(evdev->dev.bus_id, sizeof(evdev->dev.bus_id), + "event%d", minor); + evdev->dev.class = &input_class; + evdev->dev.parent = &dev->dev; + evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); + evdev->dev.release = evdev_free; + device_initialize(&evdev->dev); - 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; - } + evdev_table[minor] = evdev; - /* temporary symlink to keep userspace happy */ - error = sysfs_create_link(&input_class.subsys.kobj, - &cdev->kobj, evdev->name); + error = device_add(&evdev->dev); if (error) - goto err_cdev_destroy; + goto err_free_evdev; error = input_register_handle(&evdev->handle); if (error) - goto err_remove_link; + goto err_delete_evdev; return 0; - err_remove_link: - sysfs_remove_link(&input_class.subsys.kobj, evdev->name); - err_cdev_destroy: - class_device_destroy(&input_class, devt); + err_delete_evdev: + device_del(&evdev->dev); err_free_evdev: - kfree(evdev); - evdev_table[minor] = NULL; + put_device(&evdev->dev); return error; } @@ -690,10 +691,8 @@ static void evdev_disconnect(struct input_handle *handle) struct evdev_client *client; input_unregister_handle(handle); + device_del(&evdev->dev); - 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; if (evdev->open) { @@ -702,8 +701,9 @@ static void evdev_disconnect(struct input_handle *handle) list_for_each_entry(client, &evdev->client_list, node) kill_fasync(&client->fasync, SIGIO, POLL_HUP); wake_up_interruptible(&evdev->wait); - } else - evdev_free(evdev); + } + + put_device(&evdev->dev); } static const struct input_device_id evdev_ids[] = { diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index bd686a2a517..20896d5e5f0 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -445,6 +445,7 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent) static int gameport_thread(void *nothing) { + set_freezable(); do { gameport_handle_event(); wait_event_interruptible(gameport_wait, diff --git a/drivers/input/input.c b/drivers/input/input.c index ccd8abafcb7..5fe75558662 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -442,7 +442,7 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han printk(KERN_ERR "input: failed to attach handler %s to device %s, " "error: %d\n", - handler->name, kobject_name(&dev->cdev.kobj), error); + handler->name, kobject_name(&dev->dev.kobj), error); return error; } @@ -471,37 +471,16 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) return 0; } -static struct list_head *list_get_nth_element(struct list_head *list, loff_t *pos) -{ - struct list_head *node; - loff_t i = 0; - - list_for_each(node, list) - if (i++ == *pos) - return node; - - return NULL; -} - -static struct list_head *list_get_next_element(struct list_head *list, struct list_head *element, loff_t *pos) -{ - if (element->next == list) - return NULL; - - ++(*pos); - return element->next; -} - static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) { /* acquire lock here ... Yes, we do need locking, I knowi, I know... */ - return list_get_nth_element(&input_dev_list, pos); + return seq_list_start(&input_dev_list, *pos); } static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) { - return list_get_next_element(&input_dev_list, v, pos); + return seq_list_next(v, &input_dev_list, pos); } static void input_devices_seq_stop(struct seq_file *seq, void *v) @@ -527,7 +506,7 @@ static void input_seq_print_bitmap(struct seq_file *seq, const char *name, static int input_devices_seq_show(struct seq_file *seq, void *v) { struct input_dev *dev = container_of(v, struct input_dev, node); - const char *path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); + const char *path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); struct input_handle *handle; seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", @@ -592,13 +571,13 @@ static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) { /* acquire lock here ... Yes, we do need locking, I knowi, I know... */ seq->private = (void *)(unsigned long)*pos; - return list_get_nth_element(&input_handler_list, pos); + return seq_list_start(&input_handler_list, *pos); } static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) { seq->private = (void *)(unsigned long)(*pos + 1); - return list_get_next_element(&input_handler_list, v, pos); + return seq_list_next(v, &input_handler_list, pos); } static void input_handlers_seq_stop(struct seq_file *seq, void *v) @@ -682,15 +661,17 @@ static inline int input_proc_init(void) { return 0; } static inline void input_proc_exit(void) { } #endif -#define INPUT_DEV_STRING_ATTR_SHOW(name) \ -static ssize_t input_dev_show_##name(struct class_device *dev, char *buf) \ -{ \ - struct input_dev *input_dev = to_input_dev(dev); \ - \ - return scnprintf(buf, PAGE_SIZE, "%s\n", \ - input_dev->name ? input_dev->name : ""); \ -} \ -static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL); +#define INPUT_DEV_STRING_ATTR_SHOW(name) \ +static ssize_t input_dev_show_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + \ + return scnprintf(buf, PAGE_SIZE, "%s\n", \ + input_dev->name ? input_dev->name : ""); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL) INPUT_DEV_STRING_ATTR_SHOW(name); INPUT_DEV_STRING_ATTR_SHOW(phys); @@ -744,7 +725,9 @@ static int input_print_modalias(char *buf, int size, struct input_dev *id, return len; } -static ssize_t input_dev_show_modalias(struct class_device *dev, char *buf) +static ssize_t input_dev_show_modalias(struct device *dev, + struct device_attribute *attr, + char *buf) { struct input_dev *id = to_input_dev(dev); ssize_t len; @@ -753,13 +736,13 @@ static ssize_t input_dev_show_modalias(struct class_device *dev, char *buf) return min_t(int, len, PAGE_SIZE); } -static CLASS_DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL); +static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL); static struct attribute *input_dev_attrs[] = { - &class_device_attr_name.attr, - &class_device_attr_phys.attr, - &class_device_attr_uniq.attr, - &class_device_attr_modalias.attr, + &dev_attr_name.attr, + &dev_attr_phys.attr, + &dev_attr_uniq.attr, + &dev_attr_modalias.attr, NULL }; @@ -767,13 +750,15 @@ static struct attribute_group input_dev_attr_group = { .attrs = input_dev_attrs, }; -#define INPUT_DEV_ID_ATTR(name) \ -static ssize_t input_dev_show_id_##name(struct class_device *dev, char *buf) \ -{ \ - struct input_dev *input_dev = to_input_dev(dev); \ - return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name); \ -} \ -static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL); +#define INPUT_DEV_ID_ATTR(name) \ +static ssize_t input_dev_show_id_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL) INPUT_DEV_ID_ATTR(bustype); INPUT_DEV_ID_ATTR(vendor); @@ -781,10 +766,10 @@ INPUT_DEV_ID_ATTR(product); INPUT_DEV_ID_ATTR(version); static struct attribute *input_dev_id_attrs[] = { - &class_device_attr_bustype.attr, - &class_device_attr_vendor.attr, - &class_device_attr_product.attr, - &class_device_attr_version.attr, + &dev_attr_bustype.attr, + &dev_attr_vendor.attr, + &dev_attr_product.attr, + &dev_attr_version.attr, NULL }; @@ -813,15 +798,17 @@ static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, return len; } -#define INPUT_DEV_CAP_ATTR(ev, bm) \ -static ssize_t input_dev_show_cap_##bm(struct class_device *dev, char *buf) \ -{ \ - struct input_dev *input_dev = to_input_dev(dev); \ - int len = input_print_bitmap(buf, PAGE_SIZE, \ - input_dev->bm##bit, ev##_MAX, 1); \ - return min_t(int, len, PAGE_SIZE); \ -} \ -static CLASS_DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL); +#define INPUT_DEV_CAP_ATTR(ev, bm) \ +static ssize_t input_dev_show_cap_##bm(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + int len = input_print_bitmap(buf, PAGE_SIZE, \ + input_dev->bm##bit, ev##_MAX, 1); \ + return min_t(int, len, PAGE_SIZE); \ +} \ +static DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL) INPUT_DEV_CAP_ATTR(EV, ev); INPUT_DEV_CAP_ATTR(KEY, key); @@ -834,15 +821,15 @@ INPUT_DEV_CAP_ATTR(FF, ff); INPUT_DEV_CAP_ATTR(SW, sw); static struct attribute *input_dev_caps_attrs[] = { - &class_device_attr_ev.attr, - &class_device_attr_key.attr, - &class_device_attr_rel.attr, - &class_device_attr_abs.attr, - &class_device_attr_msc.attr, - &class_device_attr_led.attr, - &class_device_attr_snd.attr, - &class_device_attr_ff.attr, - &class_device_attr_sw.attr, + &dev_attr_ev.attr, + &dev_attr_key.attr, + &dev_attr_rel.attr, + &dev_attr_abs.attr, + &dev_attr_msc.attr, + &dev_attr_led.attr, + &dev_attr_snd.attr, + &dev_attr_ff.attr, + &dev_attr_sw.attr, NULL }; @@ -858,9 +845,9 @@ static struct attribute_group *input_dev_attr_groups[] = { NULL }; -static void input_dev_release(struct class_device *class_dev) +static void input_dev_release(struct device *device) { - struct input_dev *dev = to_input_dev(class_dev); + struct input_dev *dev = to_input_dev(device); input_ff_destroy(dev); kfree(dev); @@ -947,10 +934,10 @@ static int input_add_uevent_modalias_var(char **envp, int num_envp, int *cur_ind return err; \ } while (0) -static int input_dev_uevent(struct class_device *cdev, char **envp, +static int input_dev_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size) { - struct input_dev *dev = to_input_dev(cdev); + struct input_dev *dev = to_input_dev(device); int i = 0; int len = 0; @@ -988,10 +975,14 @@ static int input_dev_uevent(struct class_device *cdev, char **envp, return 0; } +static struct device_type input_dev_type = { + .groups = input_dev_attr_groups, + .release = input_dev_release, + .uevent = input_dev_uevent, +}; + struct class input_class = { - .name = "input", - .release = input_dev_release, - .uevent = input_dev_uevent, + .name = "input", }; EXPORT_SYMBOL_GPL(input_class); @@ -1010,9 +1001,9 @@ 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); + dev->dev.type = &input_dev_type; + dev->dev.class = &input_class; + device_initialize(&dev->dev); mutex_init(&dev->mutex); INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); @@ -1131,17 +1122,17 @@ int input_register_device(struct input_dev *dev) list_add_tail(&dev->node, &input_dev_list); - snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id), + snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); - if (!dev->cdev.dev) - dev->cdev.dev = dev->dev.parent; + if (dev->cdev.dev) + dev->dev.parent = dev->cdev.dev; - error = class_device_add(&dev->cdev); + error = device_add(&dev->dev); if (error) return error; - path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); + path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); @@ -1173,7 +1164,7 @@ void input_unregister_device(struct input_dev *dev) list_del_init(&dev->node); - class_device_unregister(&dev->cdev); + device_unregister(&dev->dev); input_wakeup_procfs_readers(); } diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 10e3b7bc925..a9a0180bfd4 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -43,6 +43,8 @@ struct joydev { struct input_handle handle; wait_queue_head_t wait; struct list_head client_list; + struct device dev; + struct js_corr corr[ABS_MAX + 1]; struct JS_DATA_SAVE_TYPE glue; int nabs; @@ -138,8 +140,10 @@ static int joydev_fasync(int fd, struct file *file, int on) return retval < 0 ? retval : 0; } -static void joydev_free(struct joydev *joydev) +static void joydev_free(struct device *dev) { + struct joydev *joydev = container_of(dev, struct joydev, dev); + joydev_table[joydev->minor] = NULL; kfree(joydev); } @@ -154,12 +158,10 @@ static int joydev_release(struct inode *inode, struct file *file) list_del(&client->node); kfree(client); - if (!--joydev->open) { - if (joydev->exist) - input_close_device(&joydev->handle); - else - joydev_free(joydev); - } + if (!--joydev->open && joydev->exist) + input_close_device(&joydev->handle); + + put_device(&joydev->dev); return 0; } @@ -178,24 +180,32 @@ static int joydev_open(struct inode *inode, struct file *file) if (!joydev || !joydev->exist) return -ENODEV; + get_device(&joydev->dev); + client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); - if (!client) - return -ENOMEM; + if (!client) { + error = -ENOMEM; + goto err_put_joydev; + } client->joydev = joydev; list_add_tail(&client->node, &joydev->client_list); if (!joydev->open++ && joydev->exist) { error = input_open_device(&joydev->handle); - if (error) { - list_del(&client->node); - kfree(client); - return error; - } + if (error) + goto err_free_client; } file->private_data = client; return 0; + + err_free_client: + list_del(&client->node); + kfree(client); + err_put_joydev: + put_device(&joydev->dev); + return error; } static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) @@ -481,8 +491,6 @@ 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; @@ -505,7 +513,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, joydev->handle.name = joydev->name; joydev->handle.handler = handler; joydev->handle.private = joydev; - sprintf(joydev->name, "js%d", minor); + snprintf(joydev->name, sizeof(joydev->name), "js%d", minor); for (i = 0; i < ABS_MAX + 1; i++) if (test_bit(i, dev->absbit)) { @@ -547,36 +555,30 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); } - joydev_table[minor] = joydev; - - devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), + snprintf(joydev->dev.bus_id, sizeof(joydev->dev.bus_id), + "js%d", minor); + joydev->dev.class = &input_class; + joydev->dev.parent = &dev->dev; + joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); + joydev->dev.release = joydev_free; + device_initialize(&joydev->dev); - 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; - } + joydev_table[minor] = joydev; - /* temporary symlink to keep userspace happy */ - error = sysfs_create_link(&input_class.subsys.kobj, - &cdev->kobj, joydev->name); + error = device_add(&joydev->dev); if (error) - goto err_cdev_destroy; + goto err_free_joydev; error = input_register_handle(&joydev->handle); if (error) - goto err_remove_link; + goto err_delete_joydev; return 0; - err_remove_link: - sysfs_remove_link(&input_class.subsys.kobj, joydev->name); - err_cdev_destroy: - class_device_destroy(&input_class, devt); + err_delete_joydev: + device_del(&joydev->dev); err_free_joydev: - joydev_table[minor] = NULL; - kfree(joydev); + put_device(&joydev->dev); return error; } @@ -587,9 +589,8 @@ static void joydev_disconnect(struct input_handle *handle) struct joydev_client *client; input_unregister_handle(handle); + device_del(&joydev->dev); - 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) { @@ -597,8 +598,9 @@ static void joydev_disconnect(struct input_handle *handle) list_for_each_entry(client, &joydev->client_list, node) kill_fasync(&client->fasync, SIGIO, POLL_HUP); wake_up_interruptible(&joydev->wait); - } else - joydev_free(joydev); + } + + put_device(&joydev->dev); } static const struct input_device_id joydev_blacklist[] = { diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index b0023452ec9..e2abe18e575 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -268,4 +268,18 @@ config JOYSTICK_XPAD To compile this driver as a module, choose M here: the module will be called xpad. +config JOYSTICK_XPAD_FF + bool "X-Box gamepad rumble support" + depends on JOYSTICK_XPAD && INPUT + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you want to take advantage of xbox 360 rumble features. + +config JOYSTICK_XPAD_LEDS + bool "LED Support for Xbox360 controller 'BigX' LED" + depends on LEDS_CLASS && JOYSTICK_XPAD + ---help--- + This option enables support for the LED which surrounds the Big X on + XBox 360 controller. + endif diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c index 555319e6378..4ed3a3eadf1 100644 --- a/drivers/input/joystick/grip_mp.c +++ b/drivers/input/joystick/grip_mp.c @@ -320,10 +320,10 @@ static int multiport_io(struct gameport* gameport, int sendflags, int sendcode, static int dig_mode_start(struct gameport *gameport, u32 *packet) { - int i, seq_len = sizeof(init_seq)/sizeof(int); + int i; int flags, tries = 0, bads = 0; - for (i = 0; i < seq_len; i++) { /* Send magic sequence */ + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { /* Send magic sequence */ if (init_seq[i]) gameport_trigger(gameport); udelay(GRIP_INIT_DELAY); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 8c8cd95a698..28080395899 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -8,6 +8,7 @@ * Ivan Hawkes <blackhawk@ivanhawkes.com> * 2005 Dominic Cerquetti <binary1230@yahoo.com> * 2006 Adam Buchbinder <adam.buchbinder@gmail.com> + * 2007 Jan Kratochvil <honza@jikos.cz> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,6 +29,7 @@ * - 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 + * - Xbox 360 information http://www.free60.org/wiki/Gamepad * * Thanks to: * - ITO Takayuki for providing essential xpad information on his website @@ -88,6 +90,9 @@ #define MAP_DPAD_TO_AXES 1 #define MAP_DPAD_UNKNOWN -1 +#define XTYPE_XBOX 0 +#define XTYPE_XBOX360 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"); @@ -97,40 +102,42 @@ static const struct xpad_device { u16 idProduct; char *name; u8 dpad_mapping; + u8 xtype; } 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 } + { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, + { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_XBOX } }; static const signed short xpad_btn[] = { @@ -146,6 +153,12 @@ static const signed short xpad_btn_pad[] = { -1 /* terminating entry */ }; +static const signed short xpad360_btn[] = { /* buttons for x360 controller */ + BTN_TL, BTN_TR, /* Button LB/RB */ + BTN_MODE, /* The big X button */ + -1 +}; + static const signed short xpad_abs[] = { ABS_X, ABS_Y, /* left stick */ ABS_RX, ABS_RY, /* right stick */ @@ -159,8 +172,12 @@ static const signed short xpad_abs_pad[] = { -1 /* terminating entry */ }; +/* Xbox 360 has a vendor-specific (sub)class, so we cannot match it with only + * USB_INTERFACE_INFO, more to that this device has 4 InterfaceProtocols, + * but we need only one of them. */ static struct usb_device_id xpad_table [] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ + { USB_DEVICE_INTERFACE_PROTOCOL(0x045e, 0x028e, 1) }, /* X-Box 360 controller */ { } }; @@ -174,9 +191,21 @@ struct usb_xpad { unsigned char *idata; /* input data */ dma_addr_t idata_dma; - char phys[65]; /* physical device path */ +#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) + struct urb *irq_out; /* urb for interrupt out report */ + unsigned char *odata; /* output data */ + dma_addr_t odata_dma; + struct mutex odata_mutex; +#endif + +#if defined(CONFIG_JOYSTICK_XPAD_LEDS) + struct xpad_led *led; +#endif + + char phys[64]; /* physical device path */ int dpad_mapping; /* map d-pad to buttons or to axes */ + int xtype; /* type of xbox device */ }; /* @@ -212,8 +241,8 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d } 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 + 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 */ @@ -235,6 +264,64 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d input_sync(dev); } +/* + * xpad360_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. It is version for xbox 360 controller + * + * The used report descriptor was taken from: + * http://www.free60.org/wiki/Gamepad + */ + +static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) +{ + struct input_dev *dev = xpad->dev; + + /* 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 if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) { + /* dpad as buttons (right, left, down, up) */ + 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 */ + input_report_key(dev, BTN_START, data[2] & 0x10); + input_report_key(dev, BTN_BACK, data[2] & 0x20); + + /* stick press left/right */ + input_report_key(dev, BTN_THUMBL, data[2] & 0x40); + input_report_key(dev, BTN_THUMBR, data[2] & 0x80); + + /* buttons A,B,X,Y,TL,TR and MODE */ + input_report_key(dev, BTN_A, data[3] & 0x10); + input_report_key(dev, BTN_B, data[3] & 0x20); + input_report_key(dev, BTN_X, data[3] & 0x40); + input_report_key(dev, BTN_Y, data[3] & 0x80); + input_report_key(dev, BTN_TL, data[3] & 0x01); + input_report_key(dev, BTN_TR, data[3] & 0x02); + input_report_key(dev, BTN_MODE, data[3] & 0x04); + + /* left stick */ + input_report_abs(dev, ABS_X, (__s16) (((__s16)data[7] << 8) | (__s16)data[6])); + input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[9] << 8) | (__s16)data[8])); + + /* right stick */ + input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[11] << 8) | (__s16)data[10])); + input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[13] << 8) | (__s16)data[12])); + + /* triggers left/right */ + input_report_abs(dev, ABS_Z, data[4]); + input_report_abs(dev, ABS_RZ, data[5]); + + input_sync(dev); +} + static void xpad_irq_in(struct urb *urb) { struct usb_xpad *xpad = urb->context; @@ -255,7 +342,10 @@ static void xpad_irq_in(struct urb *urb) goto exit; } - xpad_process_packet(xpad, 0, xpad->idata); + if (xpad->xtype == XTYPE_XBOX360) + xpad360_process_packet(xpad, 0, xpad->idata); + else + xpad_process_packet(xpad, 0, xpad->idata); exit: retval = usb_submit_urb (urb, GFP_ATOMIC); @@ -264,7 +354,205 @@ exit: __FUNCTION__, retval); } -static int xpad_open (struct input_dev *dev) +#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) +static void xpad_irq_out(struct urb *urb) +{ + 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; + } + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + +static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) +{ + struct usb_endpoint_descriptor *ep_irq_out; + int error = -ENOMEM; + + if (xpad->xtype != XTYPE_XBOX360) + return 0; + + xpad->odata = usb_buffer_alloc(xpad->udev, XPAD_PKT_LEN, + GFP_ATOMIC, &xpad->odata_dma ); + if (!xpad->odata) + goto fail1; + + mutex_init(&xpad->odata_mutex); + + xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->irq_out) + goto fail2; + + ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; + usb_fill_int_urb(xpad->irq_out, xpad->udev, + usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), + xpad->odata, XPAD_PKT_LEN, + xpad_irq_out, xpad, ep_irq_out->bInterval); + xpad->irq_out->transfer_dma = xpad->odata_dma; + xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return 0; + + fail2: usb_buffer_free(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); + fail1: return error; +} + +static void xpad_stop_output(struct usb_xpad *xpad) +{ + if (xpad->xtype == XTYPE_XBOX360) + usb_kill_urb(xpad->irq_out); +} + +static void xpad_deinit_output(struct usb_xpad *xpad) +{ + if (xpad->xtype == XTYPE_XBOX360) { + usb_free_urb(xpad->irq_out); + usb_buffer_free(xpad->udev, XPAD_PKT_LEN, + xpad->odata, xpad->odata_dma); + } +} +#else +static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } +static void xpad_deinit_output(struct usb_xpad *xpad) {} +static void xpad_stop_output(struct usb_xpad *xpad) {} +#endif + +#ifdef CONFIG_JOYSTICK_XPAD_FF +static int xpad_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + if (effect->type == FF_RUMBLE) { + __u16 strong = effect->u.rumble.strong_magnitude; + __u16 weak = effect->u.rumble.weak_magnitude; + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x08; + xpad->odata[2] = 0x00; + xpad->odata[3] = strong / 256; + xpad->odata[4] = weak / 256; + xpad->odata[5] = 0x00; + xpad->odata[6] = 0x00; + xpad->odata[7] = 0x00; + usb_submit_urb(xpad->irq_out, GFP_KERNEL); + } + + return 0; +} + +static int xpad_init_ff(struct usb_xpad *xpad) +{ + input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); + + return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); +} + +#else +static int xpad_init_ff(struct usb_xpad *xpad) { return 0; } +#endif + +#if defined(CONFIG_JOYSTICK_XPAD_LEDS) +#include <linux/leds.h> + +struct xpad_led { + char name[16]; + struct led_classdev led_cdev; + struct usb_xpad *xpad; +}; + +static void xpad_send_led_command(struct usb_xpad *xpad, int command) +{ + if (command >= 0 && command < 14) { + mutex_lock(&xpad->odata_mutex); + xpad->odata[0] = 0x01; + xpad->odata[1] = 0x03; + xpad->odata[2] = command; + usb_submit_urb(xpad->irq_out, GFP_KERNEL); + mutex_unlock(&xpad->odata_mutex); + } +} + +static void xpad_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct xpad_led *xpad_led = container_of(led_cdev, + struct xpad_led, led_cdev); + + xpad_send_led_command(xpad_led->xpad, value); +} + +static int xpad_led_probe(struct usb_xpad *xpad) +{ + static atomic_t led_seq = ATOMIC_INIT(0); + long led_no; + struct xpad_led *led; + struct led_classdev *led_cdev; + int error; + + if (xpad->xtype != XTYPE_XBOX360) + return 0; + + xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led_no = (long)atomic_inc_return(&led_seq) - 1; + + snprintf(led->name, sizeof(led->name), "xpad%ld", led_no); + led->xpad = xpad; + + led_cdev = &led->led_cdev; + led_cdev->name = led->name; + led_cdev->brightness_set = xpad_led_set; + + error = led_classdev_register(&xpad->udev->dev, led_cdev); + if (error) { + kfree(led); + xpad->led = NULL; + return error; + } + + /* + * Light up the segment corresponding to controller number + */ + xpad_send_led_command(xpad, (led_no % 4) + 2); + + return 0; +} + +static void xpad_led_disconnect(struct usb_xpad *xpad) +{ + struct xpad_led *xpad_led = xpad->led; + + if (xpad_led) { + led_classdev_unregister(&xpad_led->led_cdev); + kfree(xpad_led->name); + } +} +#else +static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } +static void xpad_led_disconnect(struct usb_xpad *xpad) { } +#endif + + +static int xpad_open(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); @@ -275,11 +563,12 @@ static int xpad_open (struct input_dev *dev) return 0; } -static void xpad_close (struct input_dev *dev) +static void xpad_close(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); usb_kill_urb(xpad->irq_in); + xpad_stop_output(xpad); } static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) @@ -335,6 +624,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->udev = udev; xpad->dpad_mapping = xpad_device[i].dpad_mapping; + xpad->xtype = xpad_device[i].xtype; if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) xpad->dpad_mapping = dpad_to_buttons; xpad->dev = input_dev; @@ -356,6 +646,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id /* set up buttons */ for (i = 0; xpad_btn[i] >= 0; i++) set_bit(xpad_btn[i], input_dev->keybit); + if (xpad->xtype == XTYPE_XBOX360) + for (i = 0; xpad360_btn[i] >= 0; i++) + set_bit(xpad360_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); @@ -367,6 +660,18 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id for (i = 0; xpad_abs_pad[i] >= 0; i++) xpad_set_up_abs(input_dev, xpad_abs_pad[i]); + error = xpad_init_output(intf, xpad); + if (error) + goto fail2; + + error = xpad_init_ff(xpad); + if (error) + goto fail3; + + error = xpad_led_probe(xpad); + if (error) + goto fail3; + ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; usb_fill_int_urb(xpad->irq_in, udev, usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), @@ -377,12 +682,13 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id error = input_register_device(xpad->dev); if (error) - goto fail3; + goto fail4; usb_set_intfdata(intf, xpad); return 0; - fail3: usb_free_urb(xpad->irq_in); + fail4: usb_free_urb(xpad->irq_in); + fail3: xpad_deinit_output(xpad); fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); fail1: input_free_device(input_dev); kfree(xpad); @@ -396,10 +702,11 @@ static void xpad_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (xpad) { - usb_kill_urb(xpad->irq_in); + xpad_led_disconnect(xpad); input_unregister_device(xpad->dev); + xpad_deinit_output(xpad); usb_free_urb(xpad->irq_in); - usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, + usb_buffer_free(xpad->udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); kfree(xpad); } diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index be1fe46cd30..41fc3d03b6e 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -89,7 +89,7 @@ static unsigned char atkbd_set2_keycode[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, - 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 159, 0,115, 0,164, 0, 0,116,158, 0,172,166, 0, 0, 0,142, 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, @@ -111,7 +111,7 @@ static unsigned char atkbd_set3_keycode[512] = { 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55,183, 184,185,186,187, 74, 94, 92, 93, 0, 0, 0,125,126,127,112, 0, - 0,139,150,163,165,115,152,150,166,140,160,154,113,114,167,168, + 0,139,172,163,165,115,152,172,166,140,160,154,113,114,167,168, 148,149,147,140 }; @@ -219,7 +219,8 @@ struct atkbd { unsigned long time; unsigned long err_count; - struct work_struct event_work; + struct delayed_work event_work; + unsigned long event_jiffies; struct mutex event_mutex; unsigned long event_mask; }; @@ -408,9 +409,10 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, goto out; case ATKBD_RET_ACK: case ATKBD_RET_NAK: - printk(KERN_WARNING "atkbd.c: Spurious %s on %s. " - "Some program might be trying access hardware directly.\n", - data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); + if (printk_ratelimit()) + printk(KERN_WARNING "atkbd.c: Spurious %s on %s. " + "Some program might be trying access hardware directly.\n", + data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); goto out; case ATKBD_RET_HANGEUL: case ATKBD_RET_HANJA: @@ -565,7 +567,7 @@ static int atkbd_set_leds(struct atkbd *atkbd) static void atkbd_event_work(struct work_struct *work) { - struct atkbd *atkbd = container_of(work, struct atkbd, event_work); + struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work); mutex_lock(&atkbd->event_mutex); @@ -579,12 +581,30 @@ static void atkbd_event_work(struct work_struct *work) } /* + * Schedule switch for execution. We need to throttle requests, + * otherwise keyboard may become unresponsive. + */ +static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit) +{ + unsigned long delay = msecs_to_jiffies(50); + + if (time_after(jiffies, atkbd->event_jiffies + delay)) + delay = 0; + + atkbd->event_jiffies = jiffies; + set_bit(event_bit, &atkbd->event_mask); + wmb(); + schedule_delayed_work(&atkbd->event_work, delay); +} + +/* * Event callback from the input module. Events that change the state of * the hardware are processed here. If action can not be performed in * interrupt context it is offloaded to atkbd_event_work. */ -static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +static int atkbd_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) { struct atkbd *atkbd = input_get_drvdata(dev); @@ -594,19 +614,12 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co switch (type) { case EV_LED: - set_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask); - wmb(); - schedule_work(&atkbd->event_work); + atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT); return 0; case EV_REP: - - if (!atkbd->softrepeat) { - set_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask); - wmb(); - schedule_work(&atkbd->event_work); - } - + if (!atkbd->softrepeat) + atkbd_schedule_event_work(atkbd, ATKBD_REP_EVENT_BIT); return 0; } @@ -940,7 +953,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) atkbd->dev = dev; ps2_init(&atkbd->ps2dev, serio); - INIT_WORK(&atkbd->event_work, atkbd_event_work); + INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work); mutex_init(&atkbd->event_mutex); switch (serio->id.type) { diff --git a/drivers/input/keyboard/pxa27x_keyboard.c b/drivers/input/keyboard/pxa27x_keyboard.c index f9e82c9ca42..ebe5eacf299 100644 --- a/drivers/input/keyboard/pxa27x_keyboard.c +++ b/drivers/input/keyboard/pxa27x_keyboard.c @@ -140,7 +140,7 @@ static int pxakbd_resume(struct platform_device *pdev) KPREC = pdata->reg_kprec; /* Enable unit clock */ - pxa_set_cken(CKEN19_KEYPAD, 1); + pxa_set_cken(CKEN_KEYPAD, 1); } mutex_unlock(&input_dev->mutex); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 88e29074ac9..9b26574f146 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -65,9 +65,13 @@ config INPUT_COBALT_BTNS config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86 && !X86_64 + select INPUT_POLLDEV + select NEW_LEDS + select LEDS_CLASS help Say Y here for support of Winstron laptop button interface, used on - laptops of various brands, including Acer and Fujitsu-Siemens. + laptops of various brands, including Acer and Fujitsu-Siemens. If + available, mail and wifi leds will be controlable via /sys/class/leds. To compile this driver as a module, choose M here: the module will be called wistron_btns. diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index 31989dcd922..906bf5e8de8 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -24,7 +24,12 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("PC Speaker beeper driver"); MODULE_LICENSE("GPL"); -static DEFINE_SPINLOCK(i8253_beep_lock); +#ifdef CONFIG_X86 +/* Use the global PIT lock ! */ +#include <asm/i8253.h> +#else +static DEFINE_SPINLOCK(i8253_lock); +#endif static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { @@ -43,7 +48,7 @@ static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int c if (value > 20 && value < 32767) count = PIT_TICK_RATE / value; - spin_lock_irqsave(&i8253_beep_lock, flags); + spin_lock_irqsave(&i8253_lock, flags); if (count) { /* enable counter 2 */ @@ -58,7 +63,7 @@ static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int c outb(inb_p(0x61) & 0xFC, 0x61); } - spin_unlock_irqrestore(&i8253_beep_lock, flags); + spin_unlock_irqrestore(&i8253_lock, flags); return 0; } diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 961aad7a047..60121f10f8d 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -20,37 +20,31 @@ #include <linux/io.h> #include <linux/dmi.h> #include <linux/init.h> -#include <linux/input.h> +#include <linux/input-polldev.h> #include <linux/interrupt.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/mc146818rtc.h> #include <linux/module.h> #include <linux/preempt.h> #include <linux/string.h> -#include <linux/timer.h> #include <linux/types.h> #include <linux/platform_device.h> +#include <linux/leds.h> -/* - * Number of attempts to read data from queue per poll; - * the queue can hold up to 31 entries - */ -#define MAX_POLL_ITERATIONS 64 - -#define POLL_FREQUENCY 10 /* Number of polls per second */ - -#if POLL_FREQUENCY > HZ -#error "POLL_FREQUENCY too high" -#endif +/* How often we poll keys - msecs */ +#define POLL_INTERVAL_DEFAULT 500 /* when idle */ +#define POLL_INTERVAL_BURST 100 /* when a key was recently pressed */ /* BIOS subsystem IDs */ #define WIFI 0x35 #define BLUETOOTH 0x34 +#define MAIL_LED 0x31 MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>"); MODULE_DESCRIPTION("Wistron laptop button driver"); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.2"); +MODULE_VERSION("0.3"); static int force; /* = 0; */ module_param(force, bool, 0); @@ -248,9 +242,10 @@ enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH }; #define FE_WIFI_LED 0x02 #define FE_UNTESTED 0x80 -static const struct key_entry *keymap; /* = NULL; Current key map */ +static struct key_entry *keymap; /* = NULL; Current key map */ static int have_wifi; static int have_bluetooth; +static int have_leds; static int __init dmi_matched(struct dmi_system_id *dmi) { @@ -263,6 +258,8 @@ static int __init dmi_matched(struct dmi_system_id *dmi) else if (key->type == KE_BLUETOOTH) have_bluetooth = 1; } + have_leds = key->code & (FE_MAIL_LED | FE_WIFI_LED); + return 1; } @@ -966,118 +963,163 @@ static int __init select_keymap(void) /* Input layer interface */ -static struct input_dev *input_dev; +static struct input_polled_dev *wistron_idev; +static unsigned long jiffies_last_press; +static int wifi_enabled; +static int bluetooth_enabled; -static int __devinit setup_input_dev(void) +static void report_key(struct input_dev *dev, unsigned int keycode) { - const struct key_entry *key; - int error; + input_report_key(dev, keycode, 1); + input_sync(dev); + input_report_key(dev, keycode, 0); + input_sync(dev); +} - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; +static void report_switch(struct input_dev *dev, unsigned int code, int value) +{ + input_report_switch(dev, code, value); + input_sync(dev); +} - input_dev->name = "Wistron laptop buttons"; - input_dev->phys = "wistron/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->cdev.dev = &wistron_device->dev; - for (key = keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, input_dev->evbit); - set_bit(key->keycode, input_dev->keybit); - break; + /* led management */ +static void wistron_mail_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + bios_set_state(MAIL_LED, (value != LED_OFF) ? 1 : 0); +} - case KE_SW: - set_bit(EV_SW, input_dev->evbit); - set_bit(key->sw.code, input_dev->swbit); - break; +/* same as setting up wifi card, but for laptops on which the led is managed */ +static void wistron_wifi_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + bios_set_state(WIFI, (value != LED_OFF) ? 1 : 0); +} - default: - ; - } - } +static struct led_classdev wistron_mail_led = { + .name = "mail:green", + .brightness_set = wistron_mail_led_set, +}; - /* 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"); +static struct led_classdev wistron_wifi_led = { + .name = "wifi:red", + .brightness_set = wistron_wifi_led_set, +}; - error = input_register_device(input_dev); - if (error) { - input_free_device(input_dev); - return error; +static void __devinit wistron_led_init(struct device *parent) +{ + if (have_leds & FE_WIFI_LED) { + u16 wifi = bios_get_default_setting(WIFI); + if (wifi & 1) { + wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF; + if (led_classdev_register(parent, &wistron_wifi_led)) + have_leds &= ~FE_WIFI_LED; + else + bios_set_state(WIFI, wistron_wifi_led.brightness); + + } else + have_leds &= ~FE_WIFI_LED; } - return 0; + if (have_leds & FE_MAIL_LED) { + /* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */ + wistron_mail_led.brightness = LED_OFF; + if (led_classdev_register(parent, &wistron_mail_led)) + have_leds &= ~FE_MAIL_LED; + else + bios_set_state(MAIL_LED, wistron_mail_led.brightness); + } } -static void report_key(unsigned keycode) +static void __devexit wistron_led_remove(void) { - input_report_key(input_dev, keycode, 1); - input_sync(input_dev); - input_report_key(input_dev, keycode, 0); - input_sync(input_dev); + if (have_leds & FE_MAIL_LED) + led_classdev_unregister(&wistron_mail_led); + + if (have_leds & FE_WIFI_LED) + led_classdev_unregister(&wistron_wifi_led); } -static void report_switch(unsigned code, int value) +static inline void wistron_led_suspend(void) { - input_report_switch(input_dev, code, value); - input_sync(input_dev); + if (have_leds & FE_MAIL_LED) + led_classdev_suspend(&wistron_mail_led); + + if (have_leds & FE_WIFI_LED) + led_classdev_suspend(&wistron_wifi_led); } - /* Driver core */ +static inline void wistron_led_resume(void) +{ + if (have_leds & FE_MAIL_LED) + led_classdev_resume(&wistron_mail_led); -static int wifi_enabled; -static int bluetooth_enabled; + if (have_leds & FE_WIFI_LED) + led_classdev_resume(&wistron_wifi_led); +} + +static struct key_entry *wistron_get_entry_by_scancode(int code) +{ + struct key_entry *key; -static void poll_bios(unsigned long); + for (key = keymap; key->type != KE_END; key++) + if (code == key->code) + return key; -static struct timer_list poll_timer = TIMER_INITIALIZER(poll_bios, 0, 0); + return NULL; +} -static void handle_key(u8 code) +static struct key_entry *wistron_get_entry_by_keycode(int keycode) { - const struct key_entry *key; + struct key_entry *key; - for (key = keymap; key->type != KE_END; key++) { - if (code == key->code) { - switch (key->type) { - case KE_KEY: - report_key(key->keycode); - break; + for (key = keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; - case KE_SW: - report_switch(key->sw.code, key->sw.value); - break; + return NULL; +} - case KE_WIFI: - if (have_wifi) { - wifi_enabled = !wifi_enabled; - bios_set_state(WIFI, wifi_enabled); - } - break; +static void handle_key(u8 code) +{ + const struct key_entry *key = wistron_get_entry_by_scancode(code); - case KE_BLUETOOTH: - if (have_bluetooth) { - bluetooth_enabled = !bluetooth_enabled; - bios_set_state(BLUETOOTH, bluetooth_enabled); - } - break; + if (key) { + switch (key->type) { + case KE_KEY: + report_key(wistron_idev->input, key->keycode); + break; - case KE_END: - break; - default: - BUG(); + case KE_SW: + report_switch(wistron_idev->input, + key->sw.code, key->sw.value); + break; + + case KE_WIFI: + if (have_wifi) { + wifi_enabled = !wifi_enabled; + bios_set_state(WIFI, wifi_enabled); + } + break; + + case KE_BLUETOOTH: + if (have_bluetooth) { + bluetooth_enabled = !bluetooth_enabled; + bios_set_state(BLUETOOTH, bluetooth_enabled); } - return; + break; + + default: + BUG(); } - } - printk(KERN_NOTICE "wistron_btns: Unknown key code %02X\n", code); + jiffies_last_press = jiffies; + } else + printk(KERN_NOTICE + "wistron_btns: Unknown key code %02X\n", code); } -static void poll_bios(unsigned long discard) +static void poll_bios(bool discard) { u8 qlen; u16 val; @@ -1090,15 +1132,118 @@ static void poll_bios(unsigned long discard) if (val != 0 && !discard) handle_key((u8)val); } +} + +static void wistron_flush(struct input_polled_dev *dev) +{ + /* Flush stale event queue */ + poll_bios(true); +} + +static void wistron_poll(struct input_polled_dev *dev) +{ + poll_bios(false); + + /* Increase poll frequency if user is currently pressing keys (< 2s ago) */ + if (time_before(jiffies, jiffies_last_press + 2 * HZ)) + dev->poll_interval = POLL_INTERVAL_BURST; + else + dev->poll_interval = POLL_INTERVAL_DEFAULT; +} + +static int wistron_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + const struct key_entry *key = wistron_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int wistron_setkeycode(struct input_dev *dev, int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = wistron_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!wistron_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } - mod_timer(&poll_timer, jiffies + HZ / POLL_FREQUENCY); + return -EINVAL; } +static int __devinit setup_input_dev(void) +{ + const struct key_entry *key; + struct input_dev *input_dev; + int error; + + wistron_idev = input_allocate_polled_device(); + if (!wistron_idev) + return -ENOMEM; + + wistron_idev->flush = wistron_flush; + wistron_idev->poll = wistron_poll; + wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT; + + input_dev = wistron_idev->input; + input_dev->name = "Wistron laptop buttons"; + input_dev->phys = "wistron/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &wistron_device->dev; + + input_dev->getkeycode = wistron_getkeycode; + input_dev->setkeycode = wistron_setkeycode; + + for (key = keymap; key->type != KE_END; key++) { + 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: + break; + } + } + + /* 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_polled_device(wistron_idev); + if (error) { + input_free_polled_device(wistron_idev); + return error; + } + + return 0; +} + +/* Driver core */ + static int __devinit wistron_probe(struct platform_device *dev) { - int err = setup_input_dev(); - if (err) - return err; + int err; bios_attach(); cmos_address = bios_get_cmos_address(); @@ -1125,15 +1270,21 @@ static int __devinit wistron_probe(struct platform_device *dev) bios_set_state(BLUETOOTH, bluetooth_enabled); } - poll_bios(1); /* Flush stale event queue and arm timer */ + wistron_led_init(&dev->dev); + err = setup_input_dev(); + if (err) { + bios_detach(); + return err; + } return 0; } static int __devexit wistron_remove(struct platform_device *dev) { - del_timer_sync(&poll_timer); - input_unregister_device(input_dev); + wistron_led_remove(); + input_unregister_polled_device(wistron_idev); + input_free_polled_device(wistron_idev); bios_detach(); return 0; @@ -1142,14 +1293,13 @@ static int __devexit wistron_remove(struct platform_device *dev) #ifdef CONFIG_PM static int wistron_suspend(struct platform_device *dev, pm_message_t state) { - del_timer_sync(&poll_timer); - if (have_wifi) bios_set_state(WIFI, 0); if (have_bluetooth) bios_set_state(BLUETOOTH, 0); + wistron_led_suspend(); return 0; } @@ -1161,7 +1311,8 @@ static int wistron_resume(struct platform_device *dev) if (have_bluetooth) bios_set_state(BLUETOOTH, bluetooth_enabled); - poll_bios(1); + wistron_led_resume(); + poll_bios(true); return 0; } diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 50e06e8dd05..7bbea097cda 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -216,4 +216,20 @@ config MOUSE_HIL help Say Y here to support HIL pointers. +config MOUSE_GPIO + tristate "GPIO mouse" + depends on GENERIC_GPIO + select INPUT_POLLDEV + help + This driver simulates a mouse on GPIO lines of various CPUs (and some + other chips). + + Say Y here if your device has buttons or a simple joystick connected + directly to GPIO lines. Your board-specific setup logic must also + provide a platform device and platform data saying which GPIOs are + used. + + To compile this driver as a module, choose M here: the + module will be called gpio_mouse. + endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index aa4ba878533..9e6e3633082 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o +obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o psmouse-objs := psmouse-base.o synaptics.o diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index e3215267db1..2bea1b2c631 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -155,6 +155,8 @@ struct atp { int xy_acc[ATP_XSENSORS + ATP_YSENSORS]; int overflowwarn; /* overflow warning printed? */ int datalen; /* size of an USB urb transfer */ + int idlecount; /* number of empty packets */ + struct work_struct work; }; #define dbg_dump(msg, tab) \ @@ -208,6 +210,55 @@ static inline int atp_is_geyser_3(struct atp *dev) (productId == GEYSER4_JIS_PRODUCT_ID); } +/* + * 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). + */ +static int atp_geyser3_init(struct usb_device *udev) +{ + 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)"); + return -EIO; + } + + /* 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)"); + return -EIO; + } + return 0; +} + +/* Reinitialise the device if it's a geyser 3 */ +static void atp_reinit(struct work_struct *work) +{ + struct atp *dev = container_of(work, struct atp, work); + struct usb_device *udev = dev->udev; + + dev->idlecount = 0; + atp_geyser3_init(udev); +} + static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, int *z, int *fingers) { @@ -439,8 +490,8 @@ static void atp_complete(struct urb* urb) } dev->x_old = x; dev->y_old = y; - } - else if (!x && !y) { + + } else if (!x && !y) { dev->x_old = dev->y_old = -1; input_report_key(dev->input, BTN_TOUCH, 0); @@ -449,11 +500,21 @@ static void atp_complete(struct urb* urb) /* 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]); + /* Geyser 3 will continue to send packets continually after + the first touch unless reinitialised. Do so if it's been + idle for a while in order to avoid waking the kernel up + several hundred times a second */ + if (atp_is_geyser_3(dev)) { + dev->idlecount++; + if (dev->idlecount == 10) { + dev->valid = 0; + schedule_work(&dev->work); + } + } + } + input_report_key(dev->input, BTN_LEFT, dev->data[dev->datalen - 1] & 1); input_sync(dev->input); exit: @@ -480,6 +541,7 @@ static void atp_close(struct input_dev *input) struct atp *dev = input_get_drvdata(input); usb_kill_urb(dev->urb); + cancel_work_sync(&dev->work); dev->open = 0; } @@ -528,40 +590,10 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id 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)"); + /* switch to raw sensor mode */ + if (atp_geyser3_init(udev)) 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"); } @@ -636,6 +668,8 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id /* save our data pointer in this interface device */ usb_set_intfdata(iface, dev); + INIT_WORK(&dev->work, atp_reinit); + return 0; err_free_buffer: @@ -669,14 +703,17 @@ static void atp_disconnect(struct usb_interface *iface) 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; diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c new file mode 100644 index 00000000000..0936d6ba015 --- /dev/null +++ b/drivers/input/mouse/gpio_mouse.c @@ -0,0 +1,196 @@ +/* + * Driver for simulating a mouse on GPIO lines. + * + * Copyright (C) 2007 Atmel Corporation + * + * 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/init.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/input-polldev.h> +#include <linux/gpio_mouse.h> + +#include <asm/gpio.h> + +/* + * Timer function which is run every scan_ms ms when the device is opened. + * The dev input varaible is set to the the input_dev pointer. + */ +static void gpio_mouse_scan(struct input_polled_dev *dev) +{ + struct gpio_mouse_platform_data *gpio = dev->private; + struct input_dev *input = dev->input; + int x, y; + + if (gpio->bleft >= 0) + input_report_key(input, BTN_LEFT, + gpio_get_value(gpio->bleft) ^ gpio->polarity); + if (gpio->bmiddle >= 0) + input_report_key(input, BTN_MIDDLE, + gpio_get_value(gpio->bmiddle) ^ gpio->polarity); + if (gpio->bright >= 0) + input_report_key(input, BTN_RIGHT, + gpio_get_value(gpio->bright) ^ gpio->polarity); + + x = (gpio_get_value(gpio->right) ^ gpio->polarity) + - (gpio_get_value(gpio->left) ^ gpio->polarity); + y = (gpio_get_value(gpio->down) ^ gpio->polarity) + - (gpio_get_value(gpio->up) ^ gpio->polarity); + + input_report_rel(input, REL_X, x); + input_report_rel(input, REL_Y, y); + input_sync(input); +} + +static int __init gpio_mouse_probe(struct platform_device *pdev) +{ + struct gpio_mouse_platform_data *pdata = pdev->dev.platform_data; + struct input_polled_dev *input_poll; + struct input_dev *input; + int pin, i; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + error = -ENXIO; + goto out; + } + + if (pdata->scan_ms < 0) { + dev_err(&pdev->dev, "invalid scan time\n"); + error = -EINVAL; + goto out; + } + + for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) { + pin = pdata->pins[i]; + + if (pin < 0) { + + if (i <= GPIO_MOUSE_PIN_RIGHT) { + /* Mouse direction is required. */ + dev_err(&pdev->dev, + "missing GPIO for directions\n"); + error = -EINVAL; + goto out_free_gpios; + } + + if (i == GPIO_MOUSE_PIN_BLEFT) + dev_dbg(&pdev->dev, "no left button defined\n"); + + } else { + error = gpio_request(pin, "gpio_mouse"); + if (error) { + dev_err(&pdev->dev, "fail %d pin (%d idx)\n", + pin, i); + goto out_free_gpios; + } + + gpio_direction_input(pin); + } + } + + input_poll = input_allocate_polled_device(); + if (!input_poll) { + dev_err(&pdev->dev, "not enough memory for input device\n"); + error = -ENOMEM; + goto out_free_gpios; + } + + platform_set_drvdata(pdev, input_poll); + + /* set input-polldev handlers */ + input_poll->private = pdata; + input_poll->poll = gpio_mouse_scan; + input_poll->poll_interval = pdata->scan_ms; + + input = input_poll->input; + input->name = pdev->name; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_REL, REL_X); + input_set_capability(input, EV_REL, REL_Y); + if (pdata->bleft >= 0) + input_set_capability(input, EV_KEY, BTN_LEFT); + if (pdata->bmiddle >= 0) + input_set_capability(input, EV_KEY, BTN_MIDDLE); + if (pdata->bright >= 0) + input_set_capability(input, EV_KEY, BTN_RIGHT); + + error = input_register_polled_device(input_poll); + if (error) { + dev_err(&pdev->dev, "could not register input device\n"); + goto out_free_polldev; + } + + dev_dbg(&pdev->dev, "%d ms scan time, buttons: %s%s%s\n", + pdata->scan_ms, + pdata->bleft < 0 ? "" : "left ", + pdata->bmiddle < 0 ? "" : "middle ", + pdata->bright < 0 ? "" : "right"); + + return 0; + + out_free_polldev: + input_free_polled_device(input_poll); + platform_set_drvdata(pdev, NULL); + + out_free_gpios: + while (--i >= 0) { + pin = pdata->pins[i]; + if (pin) + gpio_free(pin); + } + out: + return error; +} + +static int __devexit gpio_mouse_remove(struct platform_device *pdev) +{ + struct input_polled_dev *input = platform_get_drvdata(pdev); + struct gpio_mouse_platform_data *pdata = input->private; + int pin, i; + + input_unregister_polled_device(input); + input_free_polled_device(input); + + for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) { + pin = pdata->pins[i]; + if (pin >= 0) + gpio_free(pin); + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +struct platform_driver gpio_mouse_device_driver = { + .remove = __devexit_p(gpio_mouse_remove), + .driver = { + .name = "gpio_mouse", + } +}; + +static int __init gpio_mouse_init(void) +{ + return platform_driver_probe(&gpio_mouse_device_driver, + gpio_mouse_probe); +} +module_init(gpio_mouse_init); + +static void __exit gpio_mouse_exit(void) +{ + platform_driver_unregister(&gpio_mouse_device_driver); +} +module_exit(gpio_mouse_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); +MODULE_DESCRIPTION("GPIO mouse driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 1740cadd959..91109b49fde 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -109,7 +109,7 @@ 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; + struct input_dev *dev2 = priv ? priv->dev2 : NULL; unsigned char *packet = psmouse->packet; int relative_packet = packet[0] & 0x08; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index f15f695777f..b9f0fb2530e 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -178,6 +178,15 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) } /* + * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first + * byte. + */ + if (psmouse->type == PSMOUSE_CORTRON) { + input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); + packet[0] |= 0x08; + } + +/* * Generic PS/2 Mouse */ @@ -539,6 +548,20 @@ static int ps2bare_detect(struct psmouse *psmouse, int set_properties) return 0; } +/* + * Cortron PS/2 protocol detection. There's no special way to detect it, so it + * must be forced by sysfs protocol writing. + */ +static int cortron_detect(struct psmouse *psmouse, int set_properties) +{ + if (set_properties) { + psmouse->vendor = "Cortron"; + psmouse->name = "PS/2 Trackball"; + set_bit(BTN_SIDE, psmouse->dev->keybit); + } + + return 0; +} /* * psmouse_extensions() probes for any extensions to the basic PS/2 protocol @@ -740,6 +763,12 @@ static const struct psmouse_protocol psmouse_protocols[] = { }, #endif { + .type = PSMOUSE_CORTRON, + .name = "CortronPS/2", + .alias = "cortps", + .detect = cortron_detect, + }, + { .type = PSMOUSE_AUTO, .name = "auto", .alias = "any", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 3964e8acbc5..1317bdd8cc7 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -88,6 +88,7 @@ enum psmouse_type { PSMOUSE_LIFEBOOK, PSMOUSE_TRACKPOINT, PSMOUSE_TOUCHKIT_PS2, + PSMOUSE_CORTRON, PSMOUSE_AUTO /* This one should always be last */ }; @@ -118,7 +119,6 @@ static struct psmouse_attribute psmouse_attr_##_name = { \ .attr = { \ .name = __stringify(_name), \ .mode = _mode, \ - .owner = THIS_MODULE, \ }, \ .show = psmouse_attr_show_helper, \ .store = psmouse_attr_set_helper, \ diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 3f4866d8d18..9173916b8be 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -64,6 +64,7 @@ struct mousedev { wait_queue_head_t wait; struct list_head client_list; struct input_handle handle; + struct device dev; struct list_head mixdev_node; int mixdev_open; @@ -112,7 +113,7 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; static struct input_handler mousedev_handler; static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; -static struct mousedev mousedev_mix; +static struct mousedev *mousedev_mix; static LIST_HEAD(mousedev_mix_list); #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) @@ -218,10 +219,10 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int if (value) { set_bit(index, &mousedev->packet.buttons); - set_bit(index, &mousedev_mix.packet.buttons); + set_bit(index, &mousedev_mix->packet.buttons); } else { clear_bit(index, &mousedev->packet.buttons); - clear_bit(index, &mousedev_mix.packet.buttons); + clear_bit(index, &mousedev_mix->packet.buttons); } } @@ -287,11 +288,11 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) * motion packet so we won't mess current position. */ set_bit(0, &mousedev->packet.buttons); - set_bit(0, &mousedev_mix.packet.buttons); - mousedev_notify_readers(mousedev, &mousedev_mix.packet); - mousedev_notify_readers(&mousedev_mix, &mousedev_mix.packet); + set_bit(0, &mousedev_mix->packet.buttons); + mousedev_notify_readers(mousedev, &mousedev_mix->packet); + mousedev_notify_readers(mousedev_mix, &mousedev_mix->packet); clear_bit(0, &mousedev->packet.buttons); - clear_bit(0, &mousedev_mix.packet.buttons); + clear_bit(0, &mousedev_mix->packet.buttons); } mousedev->touch = mousedev->pkt_count = 0; mousedev->frac_dx = 0; @@ -343,7 +344,7 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig } mousedev_notify_readers(mousedev, &mousedev->packet); - mousedev_notify_readers(&mousedev_mix, &mousedev->packet); + mousedev_notify_readers(mousedev_mix, &mousedev->packet); mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0; mousedev->packet.abs_event = 0; @@ -362,8 +363,10 @@ static int mousedev_fasync(int fd, struct file *file, int on) return retval < 0 ? retval : 0; } -static void mousedev_free(struct mousedev *mousedev) +static void mousedev_free(struct device *dev) { + struct mousedev *mousedev = container_of(dev, struct mousedev, dev); + mousedev_table[mousedev->minor] = NULL; kfree(mousedev); } @@ -372,15 +375,16 @@ static int mixdev_add_device(struct mousedev *mousedev) { int error; - if (mousedev_mix.open) { + if (mousedev_mix->open) { error = input_open_device(&mousedev->handle); if (error) return error; mousedev->open++; - mousedev->mixdev_open++; + mousedev->mixdev_open = 1; } + get_device(&mousedev->dev); list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); return 0; @@ -395,36 +399,40 @@ static void mixdev_remove_device(struct mousedev *mousedev) } list_del_init(&mousedev->mixdev_node); + put_device(&mousedev->dev); } static void mixdev_open_devices(void) { struct mousedev *mousedev; + if (mousedev_mix->open++) + return; + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { - if (mousedev->exist && !mousedev->open) { - if (input_open_device(&mousedev->handle)) - continue; + if (!mousedev->mixdev_open) { + if (!mousedev->open && mousedev->exist) + if (input_open_device(&mousedev->handle)) + continue; mousedev->open++; - mousedev->mixdev_open++; + mousedev->mixdev_open = 1; } } } static void mixdev_close_devices(void) { - struct mousedev *mousedev, *next; + struct mousedev *mousedev; - list_for_each_entry_safe(mousedev, next, &mousedev_mix_list, mixdev_node) { + if (--mousedev_mix->open) + return; + + list_for_each_entry(mousedev, &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); - } + if (!--mousedev->open && mousedev->exist) + input_close_device(&mousedev->handle); } } } @@ -439,14 +447,12 @@ static int mousedev_release(struct inode *inode, struct file *file) list_del(&client->node); kfree(client); - if (!--mousedev->open) { - if (mousedev->minor == MOUSEDEV_MIX) - mixdev_close_devices(); - else if (mousedev->exist) - input_close_device(&mousedev->handle); - else - mousedev_free(mousedev); - } + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_close_devices(); + else if (!--mousedev->open && mousedev->exist) + input_close_device(&mousedev->handle); + + put_device(&mousedev->dev); return 0; } @@ -473,9 +479,13 @@ static int mousedev_open(struct inode *inode, struct file *file) if (!mousedev) return -ENODEV; + get_device(&mousedev->dev); + client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); - if (!client) - return -ENOMEM; + if (!client) { + error = -ENOMEM; + goto err_put_mousedev; + } spin_lock_init(&client->packet_lock); client->pos_x = xres / 2; @@ -483,21 +493,23 @@ static int mousedev_open(struct inode *inode, struct file *file) 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; - } - } + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_open_devices(); + else if (!mousedev->open++ && mousedev->exist) { + error = input_open_device(&mousedev->handle); + if (error) + goto err_free_client; } file->private_data = client; return 0; + + err_free_client: + list_del(&client->node); + kfree(client); + err_put_mousedev: + put_device(&mousedev->dev); + return error; } static inline int mousedev_limit_delta(int delta, int limit) @@ -680,57 +692,96 @@ static const struct file_operations mousedev_fops = { .fasync = mousedev_fasync, }; -static int mousedev_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static struct mousedev *mousedev_create(struct input_dev *dev, + struct input_handler *handler, + int minor) { struct mousedev *mousedev; - struct class_device *cdev; - 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 -ENFILE; - } - mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); - if (!mousedev) - return -ENOMEM; + if (!mousedev) { + error = -ENOMEM; + goto err_out; + } INIT_LIST_HEAD(&mousedev->client_list); INIT_LIST_HEAD(&mousedev->mixdev_node); init_waitqueue_head(&mousedev->wait); + if (minor == MOUSEDEV_MIX) + strlcpy(mousedev->name, "mice", sizeof(mousedev->name)); + else + snprintf(mousedev->name, sizeof(mousedev->name), + "mouse%d", minor); + mousedev->minor = minor; mousedev->exist = 1; mousedev->handle.dev = dev; mousedev->handle.name = mousedev->name; mousedev->handle.handler = handler; mousedev->handle.private = mousedev; - sprintf(mousedev->name, "mouse%d", minor); - mousedev_table[minor] = mousedev; + strlcpy(mousedev->dev.bus_id, mousedev->name, + sizeof(mousedev->dev.bus_id)); + mousedev->dev.class = &input_class; + if (dev) + mousedev->dev.parent = &dev->dev; + mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor); + mousedev->dev.release = mousedev_free; + device_initialize(&mousedev->dev); - devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor), + mousedev_table[minor] = mousedev; - cdev = class_device_create(&input_class, &dev->cdev, devt, - dev->cdev.dev, mousedev->name); - if (IS_ERR(cdev)) { - error = PTR_ERR(cdev); + error = device_add(&mousedev->dev); + if (error) goto err_free_mousedev; + + return mousedev; + + err_free_mousedev: + put_device(&mousedev->dev); + err_out: + return ERR_PTR(error); +} + +static void mousedev_destroy(struct mousedev *mousedev) +{ + struct mousedev_client *client; + + device_del(&mousedev->dev); + mousedev->exist = 0; + + if (mousedev->open) { + input_close_device(&mousedev->handle); + list_for_each_entry(client, &mousedev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + wake_up_interruptible(&mousedev->wait); } - /* temporary symlink to keep userspace happy */ - error = sysfs_create_link(&input_class.subsys.kobj, - &cdev->kobj, mousedev->name); - if (error) - goto err_cdev_destroy; + put_device(&mousedev->dev); +} + +static int mousedev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct mousedev *mousedev; + 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 -ENFILE; + } + + mousedev = mousedev_create(dev, handler, minor); + if (IS_ERR(mousedev)) + return PTR_ERR(mousedev); error = input_register_handle(&mousedev->handle); if (error) - goto err_remove_link; + goto err_delete_mousedev; error = mixdev_add_device(mousedev); if (error) @@ -740,37 +791,18 @@ static int mousedev_connect(struct input_handler *handler, struct input_dev *dev 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); + err_delete_mousedev: + device_unregister(&mousedev->dev); return error; } static void mousedev_disconnect(struct input_handle *handle) { struct mousedev *mousedev = handle->private; - struct mousedev_client *client; - - 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); - list_for_each_entry(client, &mousedev->client_list, node) - kill_fasync(&client->fasync, SIGIO, POLL_HUP); - wake_up_interruptible(&mousedev->wait); - } else - mousedev_free(mousedev); + input_unregister_handle(handle); + mousedev_destroy(mousedev); } static const struct input_device_id mousedev_ids[] = { @@ -822,25 +854,16 @@ static int psaux_registered; static int __init mousedev_init(void) { - struct class_device *cdev; int error; + mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX); + if (IS_ERR(mousedev_mix)) + return PTR_ERR(mousedev_mix); + error = input_register_handler(&mousedev_handler); - if (error) + if (error) { + mousedev_destroy(mousedev_mix); return error; - - memset(&mousedev_mix, 0, sizeof(struct mousedev)); - INIT_LIST_HEAD(&mousedev_mix.client_list); - init_waitqueue_head(&mousedev_mix.wait); - mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; - mousedev_mix.exist = 1; - mousedev_mix.minor = MOUSEDEV_MIX; - - cdev = class_device_create(&input_class, NULL, - MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice"); - if (IS_ERR(cdev)) { - input_unregister_handler(&mousedev_handler); - return PTR_ERR(cdev); } #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX @@ -863,9 +886,8 @@ static void __exit mousedev_exit(void) if (psaux_registered) misc_deregister(&psaux_mouse); #endif - class_device_destroy(&input_class, - MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX)); input_unregister_handler(&mousedev_handler); + mousedev_destroy(mousedev_mix); } module_init(mousedev_init); diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index 5a7b49c3553..b10ffae7c39 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -117,15 +117,13 @@ static int amba_kmi_probe(struct amba_device *dev, void *id) if (ret) return ret; - kmi = kmalloc(sizeof(struct amba_kmi_port), GFP_KERNEL); - io = kmalloc(sizeof(struct serio), GFP_KERNEL); + kmi = kzalloc(sizeof(struct amba_kmi_port), GFP_KERNEL); + io = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!kmi || !io) { ret = -ENOMEM; goto out; } - memset(kmi, 0, sizeof(struct amba_kmi_port)); - memset(io, 0, sizeof(struct serio)); io->id.type = SERIO_8042; io->write = amba_kmi_write; diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index f4a2517925e..702a526cf45 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -200,6 +200,17 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, }, { + /* + * Like DV4017EA does not raise AUXERR for errors on MUX ports. + */ + .ident = "HP Pavilion ZT1000", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), + }, + }, + { .ident = "Toshiba P10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), @@ -355,6 +366,7 @@ static void i8042_pnp_exit(void) static int __init i8042_pnp_init(void) { char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 }; + int pnp_data_busted = 0; int err; if (i8042_nopnp) { @@ -402,27 +414,48 @@ static int __init i8042_pnp_init(void) #endif if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) && - i8042_pnp_data_reg != i8042_data_reg) || !i8042_pnp_data_reg) { - printk(KERN_WARNING "PNP: PS/2 controller has invalid data port %#x; using default %#x\n", + i8042_pnp_data_reg != i8042_data_reg) || + !i8042_pnp_data_reg) { + printk(KERN_WARNING + "PNP: PS/2 controller has invalid data port %#x; " + "using default %#x\n", i8042_pnp_data_reg, i8042_data_reg); i8042_pnp_data_reg = i8042_data_reg; + pnp_data_busted = 1; } if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) && - i8042_pnp_command_reg != i8042_command_reg) || !i8042_pnp_command_reg) { - printk(KERN_WARNING "PNP: PS/2 controller has invalid command port %#x; using default %#x\n", + i8042_pnp_command_reg != i8042_command_reg) || + !i8042_pnp_command_reg) { + printk(KERN_WARNING + "PNP: PS/2 controller has invalid command port %#x; " + "using default %#x\n", i8042_pnp_command_reg, i8042_command_reg); i8042_pnp_command_reg = i8042_command_reg; + pnp_data_busted = 1; } if (!i8042_nokbd && !i8042_pnp_kbd_irq) { - printk(KERN_WARNING "PNP: PS/2 controller doesn't have KBD irq; using default %d\n", i8042_kbd_irq); + printk(KERN_WARNING + "PNP: PS/2 controller doesn't have KBD irq; " + "using default %d\n", i8042_kbd_irq); i8042_pnp_kbd_irq = i8042_kbd_irq; + pnp_data_busted = 1; } if (!i8042_noaux && !i8042_pnp_aux_irq) { - printk(KERN_WARNING "PNP: PS/2 controller doesn't have AUX irq; using default %d\n", i8042_aux_irq); - i8042_pnp_aux_irq = i8042_aux_irq; + if (!pnp_data_busted && i8042_pnp_kbd_irq) { + printk(KERN_WARNING + "PNP: PS/2 appears to have AUX port disabled, " + "if this is incorrect please boot with " + "i8042.nopnp\n"); + i8042_noaux = 1; + } else { + printk(KERN_WARNING + "PNP: PS/2 controller doesn't have AUX irq; " + "using default %d\n", i8042_aux_irq); + i8042_pnp_aux_irq = i8042_aux_irq; + } } i8042_data_reg = i8042_pnp_data_reg; diff --git a/drivers/input/serio/pcips2.c b/drivers/input/serio/pcips2.c index ea5e3c6ddb6..1b404f9e3bf 100644 --- a/drivers/input/serio/pcips2.c +++ b/drivers/input/serio/pcips2.c @@ -140,15 +140,13 @@ static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_i if (ret) goto disable; - ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL); - serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!ps2if || !serio) { ret = -ENOMEM; goto release; } - memset(ps2if, 0, sizeof(struct pcips2_data)); - memset(serio, 0, sizeof(struct serio)); serio->id.type = SERIO_8042; serio->write = pcips2_write; diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index d31ece8f68e..2ad88780a17 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -234,15 +234,13 @@ static int __devinit ps2_probe(struct sa1111_dev *dev) struct serio *serio; int ret; - ps2if = kmalloc(sizeof(struct ps2if), GFP_KERNEL); - serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!ps2if || !serio) { ret = -ENOMEM; goto free; } - memset(ps2if, 0, sizeof(struct ps2if)); - memset(serio, 0, sizeof(struct serio)); serio->id.type = SERIO_8042; serio->write = ps2_write; diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 5895202b972..372ca493119 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -384,6 +384,7 @@ static struct serio *serio_get_pending_child(struct serio *parent) static int serio_thread(void *nothing) { + set_freezable(); do { serio_handle_event(); wait_event_interruptible(serio_wait, @@ -769,8 +770,10 @@ static int serio_driver_remove(struct device *dev) static void serio_cleanup(struct serio *serio) { + mutex_lock(&serio->drv_mutex); if (serio->drv && serio->drv->cleanup) serio->drv->cleanup(serio); + mutex_unlock(&serio->drv_mutex); } static void serio_shutdown(struct device *dev) diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index 887357666c6..0403622ae26 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -160,7 +160,7 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t cou { struct serio_raw_list *list = file->private_data; struct serio_raw *serio_raw = list->serio_raw; - char c; + char uninitialized_var(c); ssize_t retval = 0; if (!serio_raw->serio) diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index cc0a498763d..94683f58c9e 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -82,8 +82,8 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.5 (May-15-2004)" -#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio" +#define DRIVER_VERSION "v2.3 (May 2, 2007)" +#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen" #define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)" /* @@ -112,7 +112,7 @@ * (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 + * byte0 0 0 0 0 0 0 1 1 * 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 @@ -134,7 +134,7 @@ * (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 + * byte0 0 0 0 0 0 1 0 1 * 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 @@ -218,15 +218,9 @@ #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. + * We have things set up such that if the tool button has changed, + * the tools 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 @@ -264,9 +258,9 @@ /* Mouse button programming */ -#define AIPTEK_MOUSE_LEFT_BUTTON 0x01 -#define AIPTEK_MOUSE_RIGHT_BUTTON 0x02 -#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x04 +#define AIPTEK_MOUSE_LEFT_BUTTON 0x04 +#define AIPTEK_MOUSE_RIGHT_BUTTON 0x08 +#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x10 /* Stylus button programming */ @@ -294,7 +288,6 @@ struct aiptek_features { 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 { @@ -327,9 +320,32 @@ struct aiptek { int inDelay; /* jitter: in jitter delay? */ unsigned long endDelay; /* jitter: time when delay ends */ int previousJitterable; /* jitterable prev value */ + + int lastMacro; /* macro key to reset */ + int previousToolMode; /* pen, pencil, brush, etc. tool */ unsigned char *data; /* incoming packet data */ }; +static const int eventTypes[] = { + EV_KEY, EV_ABS, EV_REL, EV_MSC, +}; + +static const int absEvents[] = { + ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y, + ABS_WHEEL, ABS_MISC, +}; + +static const int relEvents[] = { + REL_X, REL_Y, REL_WHEEL, +}; + +static const int buttonEvents[] = { + BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, + BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH, + BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH, + BTN_STYLUS, BTN_STYLUS2, +}; + /* * Permit easy lookup of keyboard events to send, versus * the bitmap which comes from the tablet. This hides the @@ -345,23 +361,39 @@ static const int macroKeyEvents[] = { }; /*********************************************************************** - * Relative reports deliver values in 2's complement format to - * deal with negative offsets. + * Map values to strings and back. Every map shoudl have the following + * as its last element: { NULL, AIPTEK_INVALID_VALUE }. */ -static int aiptek_convert_from_2s_complement(unsigned char c) +#define AIPTEK_INVALID_VALUE -1 + +struct aiptek_map { + const char *string; + int value; +}; + +static int map_str_to_val(const struct aiptek_map *map, const char *str, size_t count) { - int ret; - unsigned char b = c; - int negate = 0; + const struct aiptek_map *p; - if ((b & 0x80) != 0) { - b = ~b; - b--; - negate = 1; - } - ret = b; - ret = (negate == 1) ? -ret : ret; - return ret; + if (str[count - 1] == '\n') + count--; + + for (p = map; p->string; p++) + if (!strncmp(str, p->string, count)) + return p->value; + + return AIPTEK_INVALID_VALUE; +} + +static const char *map_val_to_str(const struct aiptek_map *map, int val) +{ + const struct aiptek_map *p; + + for (p = map; p->value != AIPTEK_INVALID_VALUE; p++) + if (val == p->value) + return p->string; + + return "unknown"; } /*********************************************************************** @@ -385,6 +417,9 @@ static int aiptek_convert_from_2s_complement(unsigned char c) * Proximity. Why two events? I thought it interesting to know if the * Proximity event occurred while the tablet was in absolute or relative * mode. + * Update: REL_MISC proved not to be such a good idea. With REL_MISC you + * get an event transmitted each time. ABS_MISC works better, since it + * can be set and re-set. Thus, only using ABS_MISC from now on. * * Other tablets use the notion of a certain minimum stylus pressure * to infer proximity. While that could have been done, that is yet @@ -441,8 +476,8 @@ static void aiptek_irq(struct urb *urb) 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]); + x = (signed char) data[2]; + y = (signed char) data[3]; /* jitterable keeps track of whether any button has been pressed. * We're also using it to remap the physical mouse button mask @@ -451,18 +486,20 @@ static void aiptek_irq(struct urb *urb) * that a non-zero value indicates that one or more * mouse button was pressed.) */ - jitterable = data[5] & 0x07; + jitterable = data[1] & 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; + left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0; + right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0; + middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 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_abs(inputdev, ABS_MISC, + 1 | AIPTEK_REPORT_TOOL_UNKNOWN); 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. @@ -472,6 +509,11 @@ static void aiptek_irq(struct urb *urb) aiptek->curSetting.wheel); aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; } + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } input_sync(inputdev); } } @@ -489,8 +531,8 @@ static void aiptek_irq(struct urb *urb) 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; + dv = (data[5] & 0x01) != 0 ? 1 : 0; + p = (data[5] & 0x02) != 0 ? 1 : 0; tip = (data[5] & 0x04) != 0 ? 1 : 0; /* Use jitterable to re-arrange button masks @@ -505,16 +547,18 @@ static void aiptek_irq(struct urb *urb) * 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 the selected tool changed, reset the old + * tool key, and set the new one. */ - if (TOOL_BUTTON_FIRED - (aiptek->curSetting.toolMode) == 0) { + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting.toolMode), + aiptek->curSetting.toolMode, 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + aiptek->previousToolMode = + aiptek->curSetting.toolMode; } if (p != 0) { @@ -550,6 +594,11 @@ static void aiptek_irq(struct urb *urb) } } input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS); + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } input_sync(inputdev); } } @@ -568,23 +617,25 @@ static void aiptek_irq(struct urb *urb) jitterable = data[5] & 0x1c; - p = (data[5] & 0x01) != 0 ? 1 : 0; - dv = (data[5] & 0x02) != 0 ? 1 : 0; + dv = (data[5] & 0x01) != 0 ? 1 : 0; + p = (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 the selected tool changed, reset the old + * tool key, and set the new one. */ - if (TOOL_BUTTON_FIRED - (aiptek->curSetting.toolMode) == 0) { + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting.toolMode), + aiptek->curSetting.toolMode, 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + aiptek->previousToolMode = + aiptek->curSetting.toolMode; } if (p != 0) { @@ -605,7 +656,12 @@ static void aiptek_irq(struct urb *urb) aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; } } - input_report_rel(inputdev, REL_MISC, p | AIPTEK_REPORT_TOOL_MOUSE); + input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_MOUSE); + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } input_sync(inputdev); } } @@ -615,98 +671,83 @@ static void aiptek_irq(struct urb *urb) else if (data[0] == 4) { jitterable = data[1] & 0x18; - p = (data[1] & 0x01) != 0 ? 1 : 0; - dv = (data[1] & 0x02) != 0 ? 1 : 0; + dv = (data[1] & 0x01) != 0 ? 1 : 0; + p = (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]; + macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1; 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 (dv) { + /* If the selected tool changed, reset the old + * tool key, and set the new one. */ - if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) { + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting.toolMode), + aiptek->curSetting.toolMode, 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + aiptek->previousToolMode = + aiptek->curSetting.toolMode; } + } - 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); - } + if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { + input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } - /* 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); + if (macro != -1 && macro != aiptek->lastMacro) { + input_report_key(inputdev, macroKeyEvents[macro], 1); + aiptek->lastMacro = macro; } + 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; + dv = (data[1] & 0x01) != 0 ? 1 : 0; + p = (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]; + macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 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 (dv) { + /* If the selected tool changed, reset the old + * tool key, and set the new one. */ - 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); + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); + input_report_key(inputdev, + aiptek->curSetting.toolMode, 1); + aiptek->previousToolMode = aiptek->curSetting.toolMode; } + } - /* 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); - } + if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { + input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } + if (macro != -1 && macro != aiptek->lastMacro) { input_report_key(inputdev, macroKeyEvents[macro], 1); - input_report_rel(inputdev, ABS_MISC, - p | AIPTEK_REPORT_TOOL_MOUSE); - input_sync(inputdev); + aiptek->lastMacro = macro; } + + input_report_abs(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. @@ -725,15 +766,18 @@ static void aiptek_irq(struct urb *urb) 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) { + /* If the selected tool changed, reset the old + tool key, and set the new one. + */ + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting. - toolMode), 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + aiptek->curSetting.toolMode, + 1); + aiptek->previousToolMode = + aiptek->curSetting.toolMode; } input_report_key(inputdev, macroKeyEvents[macro], 1); @@ -1007,9 +1051,6 @@ static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr { 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); @@ -1024,117 +1065,35 @@ static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr 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 struct aiptek_map pointer_mode_map[] = { + { "stylus", AIPTEK_POINTER_ONLY_STYLUS_MODE }, + { "mouse", AIPTEK_POINTER_ONLY_MOUSE_MODE }, + { "either", AIPTEK_POINTER_EITHER_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + 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); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(pointer_mode_map, + aiptek->curSetting.pointerMode)); } 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; + int new_mode = map_str_to_val(pointer_mode_map, buf, count); - 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; - } + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.pointerMode = new_mode; return count; } @@ -1146,44 +1105,32 @@ static DEVICE_ATTR(pointer_mode, * support routines for the 'coordinate_mode' file. Note that this file * both displays current setting and allows reprogramming. */ + +static struct aiptek_map coordinate_mode_map[] = { + { "absolute", AIPTEK_COORDINATE_ABSOLUTE_MODE }, + { "relative", AIPTEK_COORDINATE_RELATIVE_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + 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); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(coordinate_mode_map, + aiptek->curSetting.coordinateMode)); } 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; + int new_mode = map_str_to_val(coordinate_mode_map, buf, count); - 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; - } + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.coordinateMode = new_mode; return count; } @@ -1195,73 +1142,37 @@ static DEVICE_ATTR(coordinate_mode, * support routines for the 'tool_mode' file. Note that this file * both displays current setting and allows reprogramming. */ + +static struct aiptek_map tool_mode_map[] = { + { "mouse", AIPTEK_TOOL_BUTTON_MOUSE_MODE }, + { "eraser", AIPTEK_TOOL_BUTTON_ERASER_MODE }, + { "pencil", AIPTEK_TOOL_BUTTON_PENCIL_MODE }, + { "pen", AIPTEK_TOOL_BUTTON_PEN_MODE }, + { "brush", AIPTEK_TOOL_BUTTON_BRUSH_MODE }, + { "airbrush", AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE }, + { "lens", AIPTEK_TOOL_BUTTON_LENS_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + 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); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(tool_mode_map, + aiptek->curSetting.toolMode)); } 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; + int new_mode = map_str_to_val(tool_mode_map, buf, count); - 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; - } + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + aiptek->newSetting.toolMode = new_mode; return count; } @@ -1277,9 +1188,6 @@ static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *att { 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 { @@ -1294,9 +1202,6 @@ store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char 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 { @@ -1319,9 +1224,6 @@ static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *att { 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 { @@ -1336,9 +1238,6 @@ store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char 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 { @@ -1361,9 +1260,6 @@ static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribut { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay); } @@ -1372,9 +1268,6 @@ store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - aiptek->newSetting.jitterDelay = (int)simple_strtol(buf, NULL, 10); return count; } @@ -1391,9 +1284,6 @@ static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_at { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.programmableDelay); } @@ -1403,9 +1293,6 @@ store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, NULL, 10); return count; } @@ -1415,23 +1302,6 @@ static DEVICE_ATTR(delay, 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. */ @@ -1439,9 +1309,6 @@ static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attri { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount); } @@ -1456,9 +1323,6 @@ static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_at 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"; @@ -1493,45 +1357,32 @@ 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 struct aiptek_map stylus_button_map[] = { + { "upper", AIPTEK_STYLUS_UPPER_BUTTON }, + { "lower", AIPTEK_STYLUS_LOWER_BUTTON }, + { NULL, AIPTEK_INVALID_VALUE } +}; + 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); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(stylus_button_map, + aiptek->curSetting.stylusButtonUpper)); } 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); + int new_button = map_str_to_val(stylus_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - 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; - } + aiptek->newSetting.stylusButtonUpper = new_button; return count; } @@ -1543,45 +1394,26 @@ static DEVICE_ATTR(stylus_upper, * 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); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(stylus_button_map, + aiptek->curSetting.stylusButtonLower)); } 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); + int new_button = map_str_to_val(stylus_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - 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; - } + aiptek->newSetting.stylusButtonLower = new_button; return count; } @@ -1593,49 +1425,33 @@ static DEVICE_ATTR(stylus_lower, * support routines for the 'mouse_left' file. Note that this file * both displays current setting and allows for setting changing. */ + +static struct aiptek_map mouse_button_map[] = { + { "left", AIPTEK_MOUSE_LEFT_BUTTON }, + { "middle", AIPTEK_MOUSE_MIDDLE_BUTTON }, + { "right", AIPTEK_MOUSE_RIGHT_BUTTON }, + { NULL, AIPTEK_INVALID_VALUE } +}; + 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); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonLeft)); } 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); + int new_button = map_str_to_val(mouse_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - 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; - } + aiptek->newSetting.mouseButtonLeft = new_button; return count; } @@ -1650,48 +1466,22 @@ static DEVICE_ATTR(mouse_left, 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); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonMiddle)); } 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); + int new_button = map_str_to_val(mouse_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - 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; - } + aiptek->newSetting.mouseButtonMiddle = new_button; return count; } @@ -1706,47 +1496,22 @@ static DEVICE_ATTR(mouse_middle, 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); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonRight)); } 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); + int new_button = map_str_to_val(mouse_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - 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; - } + aiptek->newSetting.mouseButtonRight = new_button; return count; } @@ -1762,9 +1527,6 @@ static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *att { 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 { @@ -1778,9 +1540,6 @@ store_tabletWheel(struct device *dev, struct device_attribute *attr, const char { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10); return count; } @@ -1794,11 +1553,6 @@ static DEVICE_ATTR(wheel, */ 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... */ @@ -1811,9 +1565,6 @@ store_tabletExecute(struct device *dev, struct device_attribute *attr, const cha { 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. */ @@ -1837,9 +1588,6 @@ static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *a { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode); } @@ -1853,9 +1601,6 @@ static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode); } @@ -1869,86 +1614,39 @@ static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *at { 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; +static struct attribute *aiptek_attributes[] = { + &dev_attr_size.attr, + &dev_attr_pointer_mode.attr, + &dev_attr_coordinate_mode.attr, + &dev_attr_tool_mode.attr, + &dev_attr_xtilt.attr, + &dev_attr_ytilt.attr, + &dev_attr_jitter.attr, + &dev_attr_delay.attr, + &dev_attr_event_count.attr, + &dev_attr_diagnostic.attr, + &dev_attr_odm_code.attr, + &dev_attr_model_code.attr, + &dev_attr_firmware_code.attr, + &dev_attr_stylus_lower.attr, + &dev_attr_stylus_upper.attr, + &dev_attr_mouse_left.attr, + &dev_attr_mouse_middle.attr, + &dev_attr_mouse_right.attr, + &dev_attr_wheel.attr, + &dev_attr_execute.attr, + NULL +}; - 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; -} +static struct attribute_group aiptek_attribute_group = { + .attrs = aiptek_attributes, +}; /*********************************************************************** * This routine is called when a tablet has been identified. It basically @@ -1961,8 +1659,6 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) 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, @@ -1984,17 +1680,23 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL); inputdev = input_allocate_device(); - if (!aiptek || !inputdev) + if (!aiptek || !inputdev) { + warn("aiptek: cannot allocate memory or input device"); goto fail1; + } aiptek->data = usb_buffer_alloc(usbdev, AIPTEK_PACKET_LENGTH, GFP_ATOMIC, &aiptek->data_dma); - if (!aiptek->data) + if (!aiptek->data) { + warn("aiptek: cannot allocate usb buffer"); goto fail1; + } aiptek->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!aiptek->urb) + if (!aiptek->urb) { + warn("aiptek: cannot allocate urb"); goto fail2; + } aiptek->inputdev = inputdev; aiptek->usbdev = usbdev; @@ -2002,6 +1704,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) aiptek->inDelay = 0; aiptek->endDelay = 0; aiptek->previousJitterable = 0; + aiptek->lastMacro = -1; /* Set up the curSettings struct. Said struct contains the current * programmable parameters. The newSetting struct contains changes @@ -2054,36 +1757,23 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) /* 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); + for (i = 0; i < ARRAY_SIZE(eventTypes); ++i) + __set_bit(eventTypes[i], inputdev->evbit); - inputdev->relbit[0] |= - (BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) | BIT(REL_MISC)); + for (i = 0; i < ARRAY_SIZE(absEvents); ++i) + __set_bit(absEvents[i], inputdev->absbit); - inputdev->keybit[LONG(BTN_LEFT)] |= - (BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE)); + for (i = 0; i < ARRAY_SIZE(relEvents); ++i) + __set_bit(relEvents[i], inputdev->relbit); - 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)); + __set_bit(MSC_SERIAL, inputdev->mscbit); - inputdev->mscbit[0] = BIT(MSC_SERIAL); + /* Set up key and button codes */ + for (i = 0; i < ARRAY_SIZE(buttonEvents); ++i) + __set_bit(buttonEvents[i], inputdev->keybit); - /* 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); + __set_bit(macroKeyEvents[i], inputdev->keybit); /* * Program the input device coordinate capacities. We do not yet @@ -2134,25 +1824,11 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) } } - /* Register the tablet as an Input Device - */ - err = input_register_device(aiptek->inputdev); - if (err) + /* Murphy says that some day someone will have a tablet that fails the + above test. That's you, Frederic Rodrigo */ + if (i == ARRAY_SIZE(speeds)) { + info("input: Aiptek tried all speeds, no sane response"); 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. @@ -2161,18 +1837,27 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) /* Set up the sysfs files */ - aiptek_add_files(&intf->dev); + err = sysfs_create_group(&intf->dev.kobj, &aiptek_attribute_group); + if (err) { + warn("aiptek: cannot create sysfs group err: %d", err); + goto fail3; + } - /* Make sure the evdev module is loaded. Assuming evdev IS a module :-) + /* Register the tablet as an Input Device */ - if (request_module("evdev") != 0) - info("aiptek: error loading 'evdev' module"); - + err = input_register_device(aiptek->inputdev); + if (err) { + warn("aiptek: input_register_device returned err: %d", err); + goto fail4; + } return 0; + fail4: sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); + fail3: usb_free_urb(aiptek->urb); fail2: usb_buffer_free(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data, aiptek->data_dma); - fail1: input_free_device(inputdev); + fail1: usb_set_intfdata(intf, NULL); + input_free_device(inputdev); kfree(aiptek); return err; } @@ -2192,7 +1877,7 @@ static void aiptek_disconnect(struct usb_interface *intf) */ usb_kill_urb(aiptek->urb); input_unregister_device(aiptek->inputdev); - aiptek_delete_files(&intf->dev); + sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); usb_free_urb(aiptek->urb); usb_buffer_free(interface_to_usbdev(intf), AIPTEK_PACKET_LENGTH, diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index ef01a807ec0..6542edb6f76 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -11,7 +11,7 @@ * 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> + * Copyright (c) 2002-2007 Ping Cheng <pingc@wacom.com> * * ChangeLog: * v0.1 (vp) - Initial release @@ -62,8 +62,9 @@ * - 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 + * - and wacom_wac.c deals with Wacom specific code * - Support Intuos3 4x6 + * v1.47 (pc) - Added support for Bamboo */ /* @@ -84,7 +85,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.46" +#define DRIVER_VERSION "v1.47" #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" #define DRIVER_LICENSE "GPL" @@ -123,6 +124,7 @@ extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wa 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 void input_dev_mo(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); diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 83bddef6606..064e123c9b7 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -138,6 +138,12 @@ static void wacom_close(struct input_dev *dev) usb_kill_urb(wacom->irq); } +void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_1) | BIT(BTN_5); + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); +} + void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { input_dev->evbit[0] |= BIT(EV_MSC); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 7661f03a2db..fc03ba256f4 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -178,7 +178,8 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) case 2: /* Mouse with wheel */ wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04); - if (wacom->features->type == WACOM_G4) { + if (wacom->features->type == WACOM_G4 || + wacom->features->type == WACOM_MO) { rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03); wacom_report_rel(wcombo, REL_WHEEL, -rw); } else @@ -190,7 +191,8 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) 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) + if (wacom->features->type == WACOM_G4 || + wacom->features->type == WACOM_MO) wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f); else wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f); @@ -226,7 +228,8 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) } /* send pad data */ - if (wacom->features->type == WACOM_G4) { + switch (wacom->features->type) { + case WACOM_G4: if (data[7] & 0xf8) { wacom_input_sync(wcombo); /* sync last event */ wacom->id[1] = 1; @@ -247,6 +250,33 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) wacom_report_abs(wcombo, ABS_MISC, 0); wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); } + break; + case WACOM_MO: + if ((data[7] & 0xf8) || (data[8] & 0x80)) { + 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] & 0x08)); + wacom_report_key(wcombo, BTN_1, (data[7] & 0x20)); + wacom_report_key(wcombo, BTN_4, (data[7] & 0x10)); + wacom_report_key(wcombo, BTN_5, (data[7] & 0x40)); + wacom_report_abs(wcombo, ABS_WHEEL, (data[8] & 0x7f)); + 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] & 0x08)); + wacom_report_key(wcombo, BTN_1, (data[7] & 0x20)); + wacom_report_key(wcombo, BTN_4, (data[7] & 0x10)); + wacom_report_key(wcombo, BTN_5, (data[7] & 0x40)); + wacom_report_abs(wcombo, ABS_WHEEL, (data[8] & 0x7f)); + wacom_report_key(wcombo, BTN_TOOL_FINGER, 0); + wacom_report_abs(wcombo, ABS_MISC, 0); + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); + } + break; } return 1; } @@ -331,7 +361,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) wacom_report_key(wcombo, BTN_EXTRA, 0); wacom_report_abs(wcombo, ABS_THROTTLE, 0); wacom_report_abs(wcombo, ABS_RZ, 0); - } else { + } else { wacom_report_abs(wcombo, ABS_PRESSURE, 0); wacom_report_abs(wcombo, ABS_TILT_X, 0); wacom_report_abs(wcombo, ABS_TILT_Y, 0); @@ -423,9 +453,9 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) return result-1; /* Only large I3 and I1 & I2 support Lense Cursor */ - if((wacom->tool[idx] == BTN_TOOL_LENS) + if ((wacom->tool[idx] == BTN_TOOL_LENS) && ((wacom->features->type == INTUOS3) - || (wacom->features->type == INTUOS3S))) + || (wacom->features->type == INTUOS3S))) return 0; /* Cintiq doesn't send data when RDY bit isn't set */ @@ -517,6 +547,7 @@ int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo) break; case WACOM_G4: case GRAPHIRE: + case WACOM_MO: return (wacom_graphire_irq(wacom_wac, wcombo)); break; case PTU: @@ -538,6 +569,8 @@ int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo) void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { switch (wacom_wac->features->type) { + case WACOM_MO: + input_dev_mo(input_dev, wacom_wac); case WACOM_G4: input_dev_g4(input_dev, wacom_wac); /* fall through */ @@ -579,6 +612,7 @@ static struct wacom_features wacom_features[] = { { "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 Bamboo", 9, 14760, 9225, 511, 63, WACOM_MO }, { "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 }, @@ -627,6 +661,7 @@ static struct usb_device_id wacom_ids[] = { { 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, 0x65) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index a5e12e8756d..a302e229bb8 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -25,6 +25,7 @@ enum { INTUOS3, INTUOS3L, CINTIQ, + WACOM_MO, MAX_TYPE }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index e5cca9bd040..f929fcdbae2 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -54,6 +54,19 @@ config TOUCHSCREEN_CORGI To compile this driver as a module, choose M here: the module will be called corgi_ts. +config TOUCHSCREEN_FUJITSU + tristate "Fujitsu serial touchscreen" + select SERIO + help + Say Y here if you have the Fujitsu touchscreen (such as one + installed in Lifebook P series laptop) connected to your + system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called fujitsu-ts. + config TOUCHSCREEN_GUNZE tristate "Gunze AHL-51S touchscreen" select SERIO @@ -177,6 +190,7 @@ config TOUCHSCREEN_USB_COMPOSITE - some other eTurboTouch - Gunze AHL61 - DMC TSC-10/25 + - IRTOUCHSYSTEMS/UNITOP Have a look at <http://linux.chapter7.ch/touchkit/> for a usage description and the required user-space stuff. @@ -219,4 +233,9 @@ config TOUCHSCREEN_USB_DMC_TSC10 bool "DMC TSC-10/25 device support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_USB_IRTOUCH + default y + bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 2f86d6ad06d..5de8933c499 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -9,6 +9,7 @@ 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_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 1c9069cd3ba..96581d08774 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -95,7 +95,7 @@ struct ads7846 { u16 dummy; /* for the pwrdown read */ struct ts_event tc; - struct spi_transfer xfer[10]; + struct spi_transfer xfer[18]; struct spi_message msg[5]; struct spi_message *last_msg; int msg_idx; @@ -107,6 +107,8 @@ struct ads7846 { u16 debounce_tol; u16 debounce_rep; + u16 penirq_recheck_delay_usecs; + spinlock_t lock; struct hrtimer timer; unsigned pendown:1; /* P: lock */ @@ -553,6 +555,15 @@ static void ads7846_rx(void *ads) return; } + /* Maybe check the pendown state before reporting. This discards + * false readings when the pen is lifted. + */ + if (ts->penirq_recheck_delay_usecs) { + udelay(ts->penirq_recheck_delay_usecs); + if (!ts->get_pendown_state()) + Rt = 0; + } + /* NOTE: We can't rely on the pressure to determine the pen down * state, even this controller has a pressure sensor. The pressure * value can fluctuate for quite a while after lifting the pen and @@ -896,6 +907,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->filter = ads7846_no_filter; ts->get_pendown_state = pdata->get_pendown_state; + if (pdata->penirq_recheck_delay_usecs) + ts->penirq_recheck_delay_usecs = + pdata->penirq_recheck_delay_usecs; + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); input_dev->name = "ADS784x Touchscreen"; @@ -936,6 +951,24 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); + /* the first sample after switching drivers can be low quality; + * optionally discard it, using a second one after the signals + * have had enough time to stabilize. + */ + if (pdata->settle_delay_usecs) { + x->delay_usecs = pdata->settle_delay_usecs; + + x++; + x->tx_buf = &ts->read_y; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &ts->tc.y; + x->len = 2; + spi_message_add_tail(x, m); + } + m->complete = ads7846_rx_val; m->context = ts; @@ -954,6 +987,21 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); + /* ... maybe discard first sample ... */ + if (pdata->settle_delay_usecs) { + x->delay_usecs = pdata->settle_delay_usecs; + + x++; + x->tx_buf = &ts->read_x; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &ts->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + } + m->complete = ads7846_rx_val; m->context = ts; @@ -973,6 +1021,21 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); + /* ... maybe discard first sample ... */ + if (pdata->settle_delay_usecs) { + x->delay_usecs = pdata->settle_delay_usecs; + + x++; + x->tx_buf = &ts->read_z1; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &ts->tc.z1; + x->len = 2; + spi_message_add_tail(x, m); + } + m->complete = ads7846_rx_val; m->context = ts; @@ -990,6 +1053,21 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); + /* ... maybe discard first sample ... */ + if (pdata->settle_delay_usecs) { + x->delay_usecs = pdata->settle_delay_usecs; + + x++; + x->tx_buf = &ts->read_z2; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &ts->tc.z2; + x->len = 2; + spi_message_add_tail(x, m); + } + m->complete = ads7846_rx_val; m->context = ts; } diff --git a/drivers/input/touchscreen/fujitsu_ts.c b/drivers/input/touchscreen/fujitsu_ts.c new file mode 100644 index 00000000000..daf7a4afc93 --- /dev/null +++ b/drivers/input/touchscreen/fujitsu_ts.c @@ -0,0 +1,189 @@ +/* + * Fujitsu serial touchscreen driver + * + * Copyright (c) Dmitry Torokhov <dtor@mail.ru> + */ + +/* + * 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/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +#define DRIVER_DESC "Fujitsu serial touchscreen driver" + +MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define FUJITSU_LENGTH 5 + +/* + * Per-touchscreen data. + */ +struct fujitsu { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[FUJITSU_LENGTH]; + char phys[32]; +}; + +/* + * Decode serial data (5 bytes per packet) + * First byte + * 1 C 0 0 R S S S + * Where C is 1 while in calibration mode (which we don't use) + * R is 1 when no coordinate corection was done. + * S are button state + */ +static irqreturn_t fujitsu_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct fujitsu *fujitsu = serio_get_drvdata(serio); + struct input_dev *dev = fujitsu->dev; + + if (fujitsu->idx == 0) { + /* resync skip until start of frame */ + if ((data & 0xf0) != 0x80) + return IRQ_HANDLED; + } else { + /* resync skip garbage */ + if (data & 0x80) { + fujitsu->idx = 0; + return IRQ_HANDLED; + } + } + + fujitsu->data[fujitsu->idx++] = data; + if (fujitsu->idx == FUJITSU_LENGTH) { + input_report_abs(dev, ABS_X, + (fujitsu->data[2] << 7) | fujitsu->data[1]); + input_report_abs(dev, ABS_Y, + (fujitsu->data[4] << 7) | fujitsu->data[3]); + input_report_key(dev, BTN_TOUCH, + (fujitsu->data[0] & 0x03) != 2); + input_sync(dev); + fujitsu->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * fujitsu_disconnect() is the opposite of fujitsu_connect() + */ +static void fujitsu_disconnect(struct serio *serio) +{ + struct fujitsu *fujitsu = serio_get_drvdata(serio); + + input_get_device(fujitsu->dev); + input_unregister_device(fujitsu->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(fujitsu->dev); + kfree(fujitsu); +} + +/* + * fujitsu_connect() is the routine that is called when someone adds a + * new serio device that supports the Fujitsu protocol and registers it + * as input device. + */ +static int fujitsu_connect(struct serio *serio, struct serio_driver *drv) +{ + struct fujitsu *fujitsu; + struct input_dev *input_dev; + int err; + + fujitsu = kzalloc(sizeof(struct fujitsu), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!fujitsu || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + fujitsu->serio = serio; + fujitsu->dev = input_dev; + snprintf(fujitsu->phys, sizeof(fujitsu->phys), + "%s/input0", serio->phys); + + input_dev->name = "Fujitsu Serial Touchscreen"; + input_dev->phys = fujitsu->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_FUJITSU; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + 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, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 4096, 0, 0); + serio_set_drvdata(serio, fujitsu); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(fujitsu->dev); + if (err) + goto fail3; + + return 0; + + fail3: + serio_close(serio); + fail2: + serio_set_drvdata(serio, NULL); + fail1: + input_free_device(input_dev); + kfree(fujitsu); + return err; +} + +/* + * The serio driver structure. + */ +static struct serio_device_id fujitsu_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_FUJITSU, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, fujitsu_serio_ids); + +static struct serio_driver fujitsu_drv = { + .driver = { + .name = "fujitsu_ts", + }, + .description = DRIVER_DESC, + .id_table = fujitsu_serio_ids, + .interrupt = fujitsu_interrupt, + .connect = fujitsu_connect, + .disconnect = fujitsu_disconnect, +}; + +static int __init fujitsu_init(void) +{ + return serio_register_driver(&fujitsu_drv); +} + +static void __exit fujitsu_exit(void) +{ + serio_unregister_driver(&fujitsu_drv); +} + +module_init(fujitsu_init); +module_exit(fujitsu_exit); diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index f0cbcdb008e..36f94401915 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -292,6 +292,7 @@ static int ucb1400_ts_thread(void *_ucb) sched_setscheduler(tsk, SCHED_FIFO, ¶m); + set_freezable(); while (!kthread_should_stop()) { unsigned int x, y, p; long timeout; diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index e3f22852bd0..b407028ffc5 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -9,6 +9,7 @@ * - eTurboTouch * - Gunze AHL61 * - DMC TSC-10/25 + * - IRTOUCHSYSTEMS/UNITOP * * Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) @@ -110,6 +111,7 @@ enum { DEVTYPE_ETURBO, DEVTYPE_GUNZE, DEVTYPE_DMC_TSC10, + DEVTYPE_IRTOUCH, }; static struct usb_device_id usbtouch_devices[] = { @@ -150,6 +152,11 @@ static struct usb_device_id usbtouch_devices[] = { {USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10}, #endif +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH + {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, + {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, +#endif + {} }; @@ -416,6 +423,21 @@ static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt) /***************************************************************************** + * IRTOUCH Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH +static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = (pkt[3] << 8) | pkt[2]; + dev->y = (pkt[5] << 8) | pkt[4]; + dev->touch = (pkt[1] & 0x03) ? 1 : 0; + + return 1; +} +#endif + + +/***************************************************************************** * the different device descriptors */ static struct usbtouch_device_info usbtouch_dev_info[] = { @@ -504,6 +526,17 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .read_data = dmc_tsc10_read_data, }, #endif + +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH + [DEVTYPE_IRTOUCH] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 8, + .read_data = irtouch_read_data, + }, +#endif }; diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index 2db364898e1..d2f882e98e5 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c @@ -109,9 +109,11 @@ struct tsdev { int open; int minor; char name[8]; + struct input_handle handle; wait_queue_head_t wait; struct list_head client_list; - struct input_handle handle; + struct device dev; + int x, y, pressure; struct ts_calibration cal; }; @@ -163,9 +165,13 @@ static int tsdev_open(struct inode *inode, struct file *file) if (!tsdev || !tsdev->exist) return -ENODEV; + get_device(&tsdev->dev); + client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL); - if (!client) - return -ENOMEM; + if (!client) { + error = -ENOMEM; + goto err_put_tsdev; + } client->tsdev = tsdev; client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0; @@ -173,19 +179,25 @@ static int tsdev_open(struct inode *inode, struct file *file) if (!tsdev->open++ && tsdev->exist) { error = input_open_device(&tsdev->handle); - if (error) { - list_del(&client->node); - kfree(client); - return error; - } + if (error) + goto err_free_client; } file->private_data = client; return 0; + + err_free_client: + list_del(&client->node); + kfree(client); + err_put_tsdev: + put_device(&tsdev->dev); + return error; } -static void tsdev_free(struct tsdev *tsdev) +static void tsdev_free(struct device *dev) { + struct tsdev *tsdev = container_of(dev, struct tsdev, dev); + tsdev_table[tsdev->minor] = NULL; kfree(tsdev); } @@ -200,12 +212,10 @@ static int tsdev_release(struct inode *inode, struct file *file) list_del(&client->node); kfree(client); - if (!--tsdev->open) { - if (tsdev->exist) - input_close_device(&tsdev->handle); - else - tsdev_free(tsdev); - } + if (!--tsdev->open && tsdev->exist) + input_close_device(&tsdev->handle); + + put_device(&tsdev->dev); return 0; } @@ -361,7 +371,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, int x, y, tmp; do_gettimeofday(&time); - client->event[client->head].millisecs = time.tv_usec / 100; + client->event[client->head].millisecs = time.tv_usec / 1000; client->event[client->head].pressure = tsdev->pressure; x = tsdev->x; @@ -388,8 +398,6 @@ 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; @@ -407,14 +415,13 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, INIT_LIST_HEAD(&tsdev->client_list); init_waitqueue_head(&tsdev->wait); - sprintf(tsdev->name, "ts%d", minor); - tsdev->exist = 1; tsdev->minor = minor; tsdev->handle.dev = dev; tsdev->handle.name = tsdev->name; tsdev->handle.handler = handler; tsdev->handle.private = tsdev; + snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor); /* Precompute the rough calibration matrix */ delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1; @@ -429,36 +436,30 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, tsdev->cal.yscale = (yres << 8) / delta; tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8); - tsdev_table[minor] = tsdev; - - devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), + snprintf(tsdev->dev.bus_id, sizeof(tsdev->dev.bus_id), + "ts%d", minor); + tsdev->dev.class = &input_class; + tsdev->dev.parent = &dev->dev; + tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor); + tsdev->dev.release = tsdev_free; + device_initialize(&tsdev->dev); - 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; - } + tsdev_table[minor] = tsdev; - /* temporary symlink to keep userspace happy */ - error = sysfs_create_link(&input_class.subsys.kobj, - &cdev->kobj, tsdev->name); + error = device_add(&tsdev->dev); if (error) - goto err_cdev_destroy; + goto err_free_tsdev; error = input_register_handle(&tsdev->handle); if (error) - goto err_remove_link; + goto err_delete_tsdev; return 0; - err_remove_link: - sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); - err_cdev_destroy: - class_device_destroy(&input_class, devt); + err_delete_tsdev: + device_del(&tsdev->dev); err_free_tsdev: - tsdev_table[minor] = NULL; - kfree(tsdev); + put_device(&tsdev->dev); return error; } @@ -468,10 +469,8 @@ static void tsdev_disconnect(struct input_handle *handle) struct tsdev_client *client; input_unregister_handle(handle); + device_del(&tsdev->dev); - 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; if (tsdev->open) { @@ -479,8 +478,9 @@ static void tsdev_disconnect(struct input_handle *handle) list_for_each_entry(client, &tsdev->client_list, node) kill_fasync(&client->fasync, SIGIO, POLL_HUP); wake_up_interruptible(&tsdev->wait); - } else - tsdev_free(tsdev); + } + + put_device(&tsdev->dev); } static const struct input_device_id tsdev_ids[] = { |