diff options
Diffstat (limited to 'drivers/input')
123 files changed, 15122 insertions, 1766 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 96232313b1b..0e9b69535ad 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -153,6 +153,8 @@ source "drivers/input/mouse/Kconfig" source "drivers/input/joystick/Kconfig" +source "drivers/input/tablet/Kconfig" + source "drivers/input/touchscreen/Kconfig" source "drivers/input/misc/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index da575deb3c7..8a2dd987546 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -13,12 +13,12 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o obj-$(CONFIG_INPUT_TSDEV) += tsdev.o -obj-$(CONFIG_INPUT_POWER) += power.o obj-$(CONFIG_INPUT_EVBUG) += evbug.o obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/ obj-$(CONFIG_INPUT_MOUSE) += mouse/ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/ +obj-$(CONFIG_INPUT_TABLET) += tablet/ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index 5a9653c3128..c21f2f12723 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -38,31 +38,43 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Input driver event debug module"); MODULE_LICENSE("GPL"); -static char evbug_name[] = "evbug"; - static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", handle->dev->phys, type, code, value); } -static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static int evbug_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct input_handle *handle; + int error; - if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL))) - return NULL; + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; handle->dev = dev; handle->handler = handler; - handle->name = evbug_name; + handle->name = "evbug"; + + error = input_register_handle(handle); + if (error) + goto err_free_handle; - input_open_device(handle); + error = input_open_device(handle); + if (error) + goto err_unregister_handle; printk(KERN_DEBUG "evbug.c: Connected device: \"%s\", %s\n", dev->name, dev->phys); - return handle; + return 0; + + err_unregister_handle: + input_unregister_handle(handle); + err_free_handle: + kfree(handle); + return error; } static void evbug_disconnect(struct input_handle *handle) @@ -70,7 +82,7 @@ static void evbug_disconnect(struct input_handle *handle) printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n", handle->dev->phys); input_close_device(handle); - + input_unregister_handle(handle); kfree(handle); } diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 6439f378f6c..55a72592704 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -18,7 +18,6 @@ #include <linux/init.h> #include <linux/input.h> #include <linux/major.h> -#include <linux/smp_lock.h> #include <linux/device.h> #include <linux/compat.h> @@ -29,11 +28,11 @@ struct evdev { char name[16]; struct input_handle handle; wait_queue_head_t wait; - struct evdev_list *grab; - struct list_head list; + struct evdev_client *grab; + struct list_head client_list; }; -struct evdev_list { +struct evdev_client { struct input_event buffer[EVDEV_BUFFER_SIZE]; int head; int tail; @@ -47,28 +46,28 @@ static struct evdev *evdev_table[EVDEV_MINORS]; static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct evdev *evdev = handle->private; - struct evdev_list *list; + struct evdev_client *client; if (evdev->grab) { - list = evdev->grab; + client = evdev->grab; - do_gettimeofday(&list->buffer[list->head].time); - list->buffer[list->head].type = type; - list->buffer[list->head].code = code; - list->buffer[list->head].value = value; - list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); + do_gettimeofday(&client->buffer[client->head].time); + client->buffer[client->head].type = type; + client->buffer[client->head].code = code; + client->buffer[client->head].value = value; + client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1); - kill_fasync(&list->fasync, SIGIO, POLL_IN); + kill_fasync(&client->fasync, SIGIO, POLL_IN); } else - list_for_each_entry(list, &evdev->list, node) { + list_for_each_entry(client, &evdev->client_list, node) { - do_gettimeofday(&list->buffer[list->head].time); - list->buffer[list->head].type = type; - list->buffer[list->head].code = code; - list->buffer[list->head].value = value; - list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); + do_gettimeofday(&client->buffer[client->head].time); + client->buffer[client->head].type = type; + client->buffer[client->head].code = code; + client->buffer[client->head].value = value; + client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1); - kill_fasync(&list->fasync, SIGIO, POLL_IN); + kill_fasync(&client->fasync, SIGIO, POLL_IN); } wake_up_interruptible(&evdev->wait); @@ -76,22 +75,23 @@ static void evdev_event(struct input_handle *handle, unsigned int type, unsigned static int evdev_fasync(int fd, struct file *file, int on) { + struct evdev_client *client = file->private_data; int retval; - struct evdev_list *list = file->private_data; - retval = fasync_helper(fd, file, on, &list->fasync); + retval = fasync_helper(fd, file, on, &client->fasync); return retval < 0 ? retval : 0; } static int evdev_flush(struct file *file, fl_owner_t id) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; - if (!list->evdev->exist) + if (!evdev->exist) return -ENODEV; - return input_flush_device(&list->evdev->handle, file); + return input_flush_device(&evdev->handle, file); } static void evdev_free(struct evdev *evdev) @@ -100,48 +100,62 @@ static void evdev_free(struct evdev *evdev) kfree(evdev); } -static int evdev_release(struct inode * inode, struct file * file) +static int evdev_release(struct inode *inode, struct file *file) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; - if (list->evdev->grab == list) { - input_release_device(&list->evdev->handle); - list->evdev->grab = NULL; + if (evdev->grab == client) { + input_release_device(&evdev->handle); + evdev->grab = NULL; } evdev_fasync(-1, file, 0); - list_del(&list->node); + list_del(&client->node); + kfree(client); - if (!--list->evdev->open) { - if (list->evdev->exist) - input_close_device(&list->evdev->handle); + if (!--evdev->open) { + if (evdev->exist) + input_close_device(&evdev->handle); else - evdev_free(list->evdev); + evdev_free(evdev); } - kfree(list); return 0; } -static int evdev_open(struct inode * inode, struct file * file) +static int evdev_open(struct inode *inode, struct file *file) { - struct evdev_list *list; + struct evdev_client *client; + struct evdev *evdev; int i = iminor(inode) - EVDEV_MINOR_BASE; + int error; - if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist) + if (i >= EVDEV_MINORS) return -ENODEV; - if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL))) + evdev = evdev_table[i]; + + if (!evdev || !evdev->exist) + return -ENODEV; + + client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); + if (!client) return -ENOMEM; - list->evdev = evdev_table[i]; - list_add_tail(&list->node, &evdev_table[i]->list); - file->private_data = list; + client->evdev = evdev; + list_add_tail(&client->node, &evdev->client_list); - if (!list->evdev->open++) - if (list->evdev->exist) - input_open_device(&list->evdev->handle); + if (!evdev->open++ && evdev->exist) { + error = input_open_device(&evdev->handle); + if (error) { + list_del(&client->node); + kfree(client); + return error; + } + } + file->private_data = client; return 0; } @@ -243,54 +257,55 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev #endif /* CONFIG_COMPAT */ -static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) +static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; struct input_event event; int retval = 0; - if (!list->evdev->exist) + if (!evdev->exist) return -ENODEV; while (retval < count) { if (evdev_event_from_user(buffer + retval, &event)) return -EFAULT; - input_inject_event(&list->evdev->handle, event.type, event.code, event.value); + input_inject_event(&evdev->handle, event.type, event.code, event.value); retval += evdev_event_size(); } return retval; } -static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) +static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; int retval; if (count < evdev_event_size()) return -EINVAL; - if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK)) + if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->evdev->wait, - list->head != list->tail || (!list->evdev->exist)); - + retval = wait_event_interruptible(evdev->wait, + client->head != client->tail || !evdev->exist); if (retval) return retval; - if (!list->evdev->exist) + if (!evdev->exist) return -ENODEV; - while (list->head != list->tail && retval + evdev_event_size() <= count) { + while (client->head != client->tail && retval + evdev_event_size() <= count) { - struct input_event *event = (struct input_event *) list->buffer + list->tail; + struct input_event *event = (struct input_event *) client->buffer + client->tail; if (evdev_event_to_user(buffer + retval, event)) return -EFAULT; - list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); + client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1); retval += evdev_event_size(); } @@ -300,11 +315,12 @@ static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count /* No kernel lock - fine */ static unsigned int evdev_poll(struct file *file, poll_table *wait) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; - poll_wait(file, &list->evdev->wait, wait); - return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) | - (list->evdev->exist ? 0 : (POLLHUP | POLLERR)); + poll_wait(file, &evdev->wait, wait); + return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) | + (evdev->exist ? 0 : (POLLHUP | POLLERR)); } #ifdef CONFIG_COMPAT @@ -387,8 +403,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p) static long evdev_ioctl_handler(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { - struct evdev_list *list = file->private_data; - struct evdev *evdev = list->evdev; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; struct input_dev *dev = evdev->handle.dev; struct input_absinfo abs; struct ff_effect effect; @@ -434,32 +450,21 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, case EVIOCGKEYCODE: if (get_user(t, ip)) return -EFAULT; - if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) - return -EINVAL; - if (put_user(INPUT_KEYCODE(dev, t), ip + 1)) + + error = dev->getkeycode(dev, t, &v); + if (error) + return error; + + if (put_user(v, ip + 1)) return -EFAULT; + return 0; case EVIOCSKEYCODE: - if (get_user(t, ip)) + if (get_user(t, ip) || get_user(v, ip + 1)) return -EFAULT; - if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) - return -EINVAL; - if (get_user(v, ip + 1)) - return -EFAULT; - if (v < 0 || v > KEY_MAX) - return -EINVAL; - if (dev->keycodesize < sizeof(v) && (v >> (dev->keycodesize * 8))) - return -EINVAL; - - u = SET_INPUT_KEYCODE(dev, t, v); - clear_bit(u, dev->keybit); - set_bit(v, dev->keybit); - for (i = 0; i < dev->keycodemax; i++) - if (INPUT_KEYCODE(dev, i) == u) - set_bit(u, dev->keybit); - return 0; + return dev->setkeycode(dev, t, v); case EVIOCSFF: if (copy_from_user(&effect, p, sizeof(effect))) @@ -487,10 +492,10 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, return -EBUSY; if (input_grab_device(&evdev->handle)) return -EBUSY; - evdev->grab = list; + evdev->grab = client; return 0; } else { - if (evdev->grab != list) + if (evdev->grab != client) return -EINVAL; input_release_device(&evdev->handle); evdev->grab = NULL; @@ -506,7 +511,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { - long *bits; + unsigned long *bits; int len; switch (_IOC_NR(cmd) & EV_MAX) { @@ -551,7 +556,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { - int t = _IOC_NR(cmd) & ABS_MAX; + t = _IOC_NR(cmd) & ABS_MAX; abs.value = dev->abs[t]; abs.minimum = dev->absmin[t]; @@ -571,7 +576,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { - int t = _IOC_NR(cmd) & ABS_MAX; + t = _IOC_NR(cmd) & ABS_MAX; if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) return -EFAULT; @@ -616,23 +621,26 @@ static const struct file_operations evdev_fops = { .flush = evdev_flush }; -static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static int evdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct evdev *evdev; struct class_device *cdev; + dev_t devt; int minor; + int error; for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); if (minor == EVDEV_MINORS) { printk(KERN_ERR "evdev: no more free evdev devices\n"); - return NULL; + return -ENFILE; } - if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL))) - return NULL; + evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); + if (!evdev) + return -ENOMEM; - INIT_LIST_HEAD(&evdev->list); + INIT_LIST_HEAD(&evdev->client_list); init_waitqueue_head(&evdev->wait); evdev->exist = 1; @@ -645,23 +653,45 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct evdev_table[minor] = evdev; - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), - dev->cdev.dev, evdev->name); + devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), + + cdev = class_device_create(&input_class, &dev->cdev, devt, + dev->cdev.dev, evdev->name); + if (IS_ERR(cdev)) { + error = PTR_ERR(cdev); + goto err_free_evdev; + } /* temporary symlink to keep userspace happy */ - sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, - evdev->name); + error = sysfs_create_link(&input_class.subsys.kobj, + &cdev->kobj, evdev->name); + if (error) + goto err_cdev_destroy; + + error = input_register_handle(&evdev->handle); + if (error) + goto err_remove_link; - return &evdev->handle; + return 0; + + err_remove_link: + sysfs_remove_link(&input_class.subsys.kobj, evdev->name); + err_cdev_destroy: + class_device_destroy(&input_class, devt); + err_free_evdev: + kfree(evdev); + evdev_table[minor] = NULL; + return error; } static void evdev_disconnect(struct input_handle *handle) { struct evdev *evdev = handle->private; - struct evdev_list *list; + struct evdev_client *client; + + input_unregister_handle(handle); - sysfs_remove_link(&input_class.subsys.kset.kobj, evdev->name); + sysfs_remove_link(&input_class.subsys.kobj, evdev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor)); evdev->exist = 0; @@ -670,8 +700,8 @@ static void evdev_disconnect(struct input_handle *handle) input_flush_device(handle, NULL); input_close_device(handle); wake_up_interruptible(&evdev->wait); - list_for_each_entry(list, &evdev->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_HUP); + list_for_each_entry(client, &evdev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); } else evdev_free(evdev); } diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index 783b3412cea..eebc72465fc 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -281,7 +281,8 @@ int input_ff_event(struct input_dev *dev, unsigned int type, break; default: - ff->playback(dev, code, value); + if (check_effect_access(ff, code, NULL) == 0) + ff->playback(dev, code, value); break; } diff --git a/drivers/input/input.c b/drivers/input/input.c index a9a706f8fff..ccd8abafcb7 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -11,7 +11,6 @@ */ #include <linux/init.h> -#include <linux/smp_lock.h> #include <linux/input.h> #include <linux/module.h> #include <linux/random.h> @@ -299,12 +298,87 @@ void input_close_device(struct input_handle *handle) } EXPORT_SYMBOL(input_close_device); -static void input_link_handle(struct input_handle *handle) +static int input_fetch_keycode(struct input_dev *dev, int scancode) { - list_add_tail(&handle->d_node, &handle->dev->h_list); - list_add_tail(&handle->h_node, &handle->handler->h_list); + switch (dev->keycodesize) { + case 1: + return ((u8 *)dev->keycode)[scancode]; + + case 2: + return ((u16 *)dev->keycode)[scancode]; + + default: + return ((u32 *)dev->keycode)[scancode]; + } +} + +static int input_default_getkeycode(struct input_dev *dev, + int scancode, int *keycode) +{ + if (!dev->keycodesize) + return -EINVAL; + + if (scancode < 0 || scancode >= dev->keycodemax) + return -EINVAL; + + *keycode = input_fetch_keycode(dev, scancode); + + return 0; +} + +static int input_default_setkeycode(struct input_dev *dev, + int scancode, int keycode) +{ + int old_keycode; + int i; + + if (scancode < 0 || scancode >= dev->keycodemax) + return -EINVAL; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + if (!dev->keycodesize) + return -EINVAL; + + if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8))) + return -EINVAL; + + switch (dev->keycodesize) { + case 1: { + u8 *k = (u8 *)dev->keycode; + old_keycode = k[scancode]; + k[scancode] = keycode; + break; + } + case 2: { + u16 *k = (u16 *)dev->keycode; + old_keycode = k[scancode]; + k[scancode] = keycode; + break; + } + default: { + u32 *k = (u32 *)dev->keycode; + old_keycode = k[scancode]; + k[scancode] = keycode; + break; + } + } + + clear_bit(old_keycode, dev->keybit); + set_bit(keycode, dev->keybit); + + for (i = 0; i < dev->keycodemax; i++) { + if (input_fetch_keycode(dev, i) == old_keycode) { + set_bit(old_keycode, dev->keybit); + break; /* Setting the bit twice is useless, so break */ + } + } + + return 0; } + #define MATCH_BIT(bit, max) \ for (i = 0; i < NBITS(max); i++) \ if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \ @@ -351,6 +425,29 @@ static const struct input_device_id *input_match_device(const struct input_devic return NULL; } +static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) +{ + const struct input_device_id *id; + int error; + + if (handler->blacklist && input_match_device(handler->blacklist, dev)) + return -ENODEV; + + id = input_match_device(handler->id_table, dev); + if (!id) + return -ENODEV; + + error = handler->connect(handler, dev, id); + if (error && error != -ENODEV) + printk(KERN_ERR + "input: failed to attach handler %s to device %s, " + "error: %d\n", + handler->name, kobject_name(&dev->cdev.kobj), error); + + return error; +} + + #ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_bus_input_dir; @@ -439,6 +536,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : ""); seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : ""); seq_printf(seq, "S: Sysfs=%s\n", path ? path : ""); + seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : ""); seq_printf(seq, "H: Handlers="); list_for_each_entry(handle, &dev->h_list, d_node) @@ -753,6 +851,13 @@ static struct attribute_group input_dev_caps_attr_group = { .attrs = input_dev_caps_attrs, }; +static struct attribute_group *input_dev_attr_groups[] = { + &input_dev_attr_group, + &input_dev_id_attr_group, + &input_dev_caps_attr_group, + NULL +}; + static void input_dev_release(struct class_device *class_dev) { struct input_dev *dev = to_input_dev(class_dev); @@ -906,6 +1011,7 @@ struct input_dev *input_allocate_device(void) dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); if (dev) { dev->cdev.class = &input_class; + dev->cdev.groups = input_dev_attr_groups; class_device_initialize(&dev->cdev); mutex_init(&dev->mutex); INIT_LIST_HEAD(&dev->h_list); @@ -934,23 +1040,71 @@ EXPORT_SYMBOL(input_allocate_device); */ void input_free_device(struct input_dev *dev) { - if (dev) { - - mutex_lock(&dev->mutex); - dev->name = dev->phys = dev->uniq = NULL; - mutex_unlock(&dev->mutex); - + if (dev) input_put_device(dev); - } } EXPORT_SYMBOL(input_free_device); +/** + * input_set_capability - mark device as capable of a certain event + * @dev: device that is capable of emitting or accepting event + * @type: type of the event (EV_KEY, EV_REL, etc...) + * @code: event code + * + * In addition to setting up corresponding bit in appropriate capability + * bitmap the function also adjusts dev->evbit. + */ +void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code) +{ + switch (type) { + case EV_KEY: + __set_bit(code, dev->keybit); + break; + + case EV_REL: + __set_bit(code, dev->relbit); + break; + + case EV_ABS: + __set_bit(code, dev->absbit); + break; + + case EV_MSC: + __set_bit(code, dev->mscbit); + break; + + case EV_SW: + __set_bit(code, dev->swbit); + break; + + case EV_LED: + __set_bit(code, dev->ledbit); + break; + + case EV_SND: + __set_bit(code, dev->sndbit); + break; + + case EV_FF: + __set_bit(code, dev->ffbit); + break; + + default: + printk(KERN_ERR + "input_set_capability: unknown type %u (code %u)\n", + type, code); + dump_stack(); + return; + } + + __set_bit(type, dev->evbit); +} +EXPORT_SYMBOL(input_set_capability); + int input_register_device(struct input_dev *dev) { static atomic_t input_no = ATOMIC_INIT(0); - struct input_handle *handle; struct input_handler *handler; - const struct input_device_id *id; const char *path; int error; @@ -969,55 +1123,41 @@ int input_register_device(struct input_dev *dev) dev->rep[REP_PERIOD] = 33; } + if (!dev->getkeycode) + dev->getkeycode = input_default_getkeycode; + + if (!dev->setkeycode) + dev->setkeycode = input_default_setkeycode; + list_add_tail(&dev->node, &input_dev_list); snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id), "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); + if (!dev->cdev.dev) + dev->cdev.dev = dev->dev.parent; + error = class_device_add(&dev->cdev); if (error) return error; - error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group); - if (error) - goto fail1; - - error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group); - if (error) - goto fail2; - - error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group); - if (error) - goto fail3; - path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); list_for_each_entry(handler, &input_handler_list, node) - if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) - if ((id = input_match_device(handler->id_table, dev))) - if ((handle = handler->connect(handler, dev, id))) { - input_link_handle(handle); - if (handler->start) - handler->start(handle); - } + input_attach_handler(dev, handler); input_wakeup_procfs_readers(); return 0; - - fail3: sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group); - fail2: sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group); - fail1: class_device_del(&dev->cdev); - return error; } EXPORT_SYMBOL(input_register_device); void input_unregister_device(struct input_dev *dev) { - struct list_head *node, *next; + struct input_handle *handle, *next; int code; for (code = 0; code <= KEY_MAX; code++) @@ -1027,19 +1167,12 @@ void input_unregister_device(struct input_dev *dev) del_timer_sync(&dev->timer); - list_for_each_safe(node, next, &dev->h_list) { - struct input_handle * handle = to_handle(node); - list_del_init(&handle->d_node); - list_del_init(&handle->h_node); + list_for_each_entry_safe(handle, next, &dev->h_list, d_node) handle->handler->disconnect(handle); - } + WARN_ON(!list_empty(&dev->h_list)); list_del_init(&dev->node); - sysfs_remove_group(&dev->cdev.kobj, &input_dev_caps_attr_group); - sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group); - sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group); - class_device_unregister(&dev->cdev); input_wakeup_procfs_readers(); @@ -1049,8 +1182,6 @@ EXPORT_SYMBOL(input_unregister_device); int input_register_handler(struct input_handler *handler) { struct input_dev *dev; - struct input_handle *handle; - const struct input_device_id *id; INIT_LIST_HEAD(&handler->h_list); @@ -1064,13 +1195,7 @@ int input_register_handler(struct input_handler *handler) list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node) - if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) - if ((id = input_match_device(handler->id_table, dev))) - if ((handle = handler->connect(handler, dev, id))) { - input_link_handle(handle); - if (handler->start) - handler->start(handle); - } + input_attach_handler(dev, handler); input_wakeup_procfs_readers(); return 0; @@ -1079,14 +1204,11 @@ EXPORT_SYMBOL(input_register_handler); void input_unregister_handler(struct input_handler *handler) { - struct list_head *node, *next; + struct input_handle *handle, *next; - list_for_each_safe(node, next, &handler->h_list) { - struct input_handle * handle = to_handle_h(node); - list_del_init(&handle->h_node); - list_del_init(&handle->d_node); + list_for_each_entry_safe(handle, next, &handler->h_list, h_node) handler->disconnect(handle); - } + WARN_ON(!list_empty(&handler->h_list)); list_del_init(&handler->node); @@ -1097,6 +1219,27 @@ void input_unregister_handler(struct input_handler *handler) } EXPORT_SYMBOL(input_unregister_handler); +int input_register_handle(struct input_handle *handle) +{ + struct input_handler *handler = handle->handler; + + list_add_tail(&handle->d_node, &handle->dev->h_list); + list_add_tail(&handle->h_node, &handler->h_list); + + if (handler->start) + handler->start(handle); + + return 0; +} +EXPORT_SYMBOL(input_register_handle); + +void input_unregister_handle(struct input_handle *handle) +{ + list_del_init(&handle->h_node); + list_del_init(&handle->d_node); +} +EXPORT_SYMBOL(input_unregister_handle); + static int input_open_file(struct inode *inode, struct file *file) { struct input_handler *handler = input_table[iminor(inode) >> 5]; diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 9f3529ad3fd..06f0541b24d 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -24,7 +24,6 @@ #include <linux/module.h> #include <linux/poll.h> #include <linux/init.h> -#include <linux/smp_lock.h> #include <linux/device.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); @@ -43,7 +42,7 @@ struct joydev { char name[16]; struct input_handle handle; wait_queue_head_t wait; - struct list_head list; + struct list_head client_list; struct js_corr corr[ABS_MAX + 1]; struct JS_DATA_SAVE_TYPE glue; int nabs; @@ -55,7 +54,7 @@ struct joydev { __s16 abs[ABS_MAX + 1]; }; -struct joydev_list { +struct joydev_client { struct js_event buffer[JOYDEV_BUFFER_SIZE]; int head; int tail; @@ -87,7 +86,7 @@ static int joydev_correct(int value, struct js_corr *corr) static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct joydev *joydev = handle->private; - struct joydev_list *list; + struct joydev_client *client; struct js_event event; switch (type) { @@ -115,15 +114,15 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne event.time = jiffies_to_msecs(jiffies); - list_for_each_entry(list, &joydev->list, node) { + list_for_each_entry(client, &joydev->client_list, node) { - memcpy(list->buffer + list->head, &event, sizeof(struct js_event)); + memcpy(client->buffer + client->head, &event, sizeof(struct js_event)); - if (list->startup == joydev->nabs + joydev->nkey) - if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) - list->startup = 0; + if (client->startup == joydev->nabs + joydev->nkey) + if (client->tail == (client->head = (client->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) + client->startup = 0; - kill_fasync(&list->fasync, SIGIO, POLL_IN); + kill_fasync(&client->fasync, SIGIO, POLL_IN); } wake_up_interruptible(&joydev->wait); @@ -132,9 +131,9 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne static int joydev_fasync(int fd, struct file *file, int on) { int retval; - struct joydev_list *list = file->private_data; + struct joydev_client *client = file->private_data; - retval = fasync_helper(fd, file, on, &list->fasync); + retval = fasync_helper(fd, file, on, &client->fasync); return retval < 0 ? retval : 0; } @@ -145,60 +144,73 @@ static void joydev_free(struct joydev *joydev) kfree(joydev); } -static int joydev_release(struct inode * inode, struct file * file) +static int joydev_release(struct inode *inode, struct file *file) { - struct joydev_list *list = file->private_data; + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; joydev_fasync(-1, file, 0); - list_del(&list->node); + list_del(&client->node); + kfree(client); - if (!--list->joydev->open) { - if (list->joydev->exist) - input_close_device(&list->joydev->handle); + if (!--joydev->open) { + if (joydev->exist) + input_close_device(&joydev->handle); else - joydev_free(list->joydev); + joydev_free(joydev); } - kfree(list); return 0; } static int joydev_open(struct inode *inode, struct file *file) { - struct joydev_list *list; + struct joydev_client *client; + struct joydev *joydev; int i = iminor(inode) - JOYDEV_MINOR_BASE; + int error; + + if (i >= JOYDEV_MINORS) + return -ENODEV; - if (i >= JOYDEV_MINORS || !joydev_table[i]) + joydev = joydev_table[i]; + if (!joydev || !joydev->exist) return -ENODEV; - if (!(list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL))) + client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); + if (!client) return -ENOMEM; - list->joydev = joydev_table[i]; - list_add_tail(&list->node, &joydev_table[i]->list); - file->private_data = list; + client->joydev = joydev; + list_add_tail(&client->node, &joydev->client_list); - if (!list->joydev->open++) - if (list->joydev->exist) - input_open_device(&list->joydev->handle); + if (!joydev->open++ && joydev->exist) { + error = input_open_device(&joydev->handle); + if (error) { + list_del(&client->node); + kfree(client); + return error; + } + } + file->private_data = client; return 0; } -static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) +static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { return -EINVAL; } static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct joydev_list *list = file->private_data; - struct joydev *joydev = list->joydev; + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; struct input_dev *input = joydev->handle.dev; int retval = 0; - if (!list->joydev->exist) + if (!joydev->exist) return -ENODEV; if (count < sizeof(struct js_event)) @@ -217,56 +229,55 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) return -EFAULT; - list->startup = 0; - list->tail = list->head; + client->startup = 0; + client->tail = client->head; return sizeof(struct JS_DATA_TYPE); } - if (list->startup == joydev->nabs + joydev->nkey && - list->head == list->tail && (file->f_flags & O_NONBLOCK)) + if (client->startup == joydev->nabs + joydev->nkey && + client->head == client->tail && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->joydev->wait, - !list->joydev->exist || - list->startup < joydev->nabs + joydev->nkey || - list->head != list->tail); - + retval = wait_event_interruptible(joydev->wait, + !joydev->exist || + client->startup < joydev->nabs + joydev->nkey || + client->head != client->tail); if (retval) return retval; - if (!list->joydev->exist) + if (!joydev->exist) return -ENODEV; - while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { + while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { struct js_event event; event.time = jiffies_to_msecs(jiffies); - if (list->startup < joydev->nkey) { + if (client->startup < joydev->nkey) { event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; - event.number = list->startup; + event.number = client->startup; event.value = !!test_bit(joydev->keypam[event.number], input->key); } else { event.type = JS_EVENT_AXIS | JS_EVENT_INIT; - event.number = list->startup - joydev->nkey; + event.number = client->startup - joydev->nkey; event.value = joydev->abs[event.number]; } if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) return -EFAULT; - list->startup++; + client->startup++; retval += sizeof(struct js_event); } - while (list->head != list->tail && retval + sizeof(struct js_event) <= count) { + while (client->head != client->tail && retval + sizeof(struct js_event) <= count) { - if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event))) + if (copy_to_user(buf + retval, client->buffer + client->tail, sizeof(struct js_event))) return -EFAULT; - list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); + client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); retval += sizeof(struct js_event); } @@ -276,11 +287,12 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo /* No kernel lock - fine */ static unsigned int joydev_poll(struct file *file, poll_table *wait) { - struct joydev_list *list = file->private_data; + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; - poll_wait(file, &list->joydev->wait, wait); - return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ? - (POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR)); + poll_wait(file, &joydev->wait, wait); + return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ? + (POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR)); } static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) @@ -374,8 +386,8 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u #ifdef CONFIG_COMPAT static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct joydev_list *list = file->private_data; - struct joydev *joydev = list->joydev; + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; void __user *argp = (void __user *)arg; s32 tmp32; struct JS_DATA_SAVE_TYPE_32 ds32; @@ -428,8 +440,8 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct joydev_list *list = file->private_data; - struct joydev *joydev = list->joydev; + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; void __user *argp = (void __user *)arg; if (!joydev->exist) @@ -465,23 +477,26 @@ static const struct file_operations joydev_fops = { .fasync = joydev_fasync, }; -static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static int joydev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct joydev *joydev; struct class_device *cdev; + dev_t devt; int i, j, t, minor; + int error; for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); if (minor == JOYDEV_MINORS) { printk(KERN_ERR "joydev: no more free joydev devices\n"); - return NULL; + return -ENFILE; } - if (!(joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL))) - return NULL; + joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL); + if (!joydev) + return -ENOMEM; - INIT_LIST_HEAD(&joydev->list); + INIT_LIST_HEAD(&joydev->client_list); init_waitqueue_head(&joydev->wait); joydev->minor = minor; @@ -534,31 +549,54 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct joydev_table[minor] = joydev; - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), - dev->cdev.dev, joydev->name); + devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), + + cdev = class_device_create(&input_class, &dev->cdev, devt, + dev->cdev.dev, joydev->name); + if (IS_ERR(cdev)) { + error = PTR_ERR(cdev); + goto err_free_joydev; + } /* temporary symlink to keep userspace happy */ - sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, - joydev->name); + error = sysfs_create_link(&input_class.subsys.kobj, + &cdev->kobj, joydev->name); + if (error) + goto err_cdev_destroy; + + error = input_register_handle(&joydev->handle); + if (error) + goto err_remove_link; + + return 0; - return &joydev->handle; + err_remove_link: + sysfs_remove_link(&input_class.subsys.kobj, joydev->name); + err_cdev_destroy: + class_device_destroy(&input_class, devt); + err_free_joydev: + joydev_table[minor] = NULL; + kfree(joydev); + return error; } + static void joydev_disconnect(struct input_handle *handle) { struct joydev *joydev = handle->private; - struct joydev_list *list; + struct joydev_client *client; + + input_unregister_handle(handle); - sysfs_remove_link(&input_class.subsys.kset.kobj, joydev->name); + sysfs_remove_link(&input_class.subsys.kobj, joydev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor)); joydev->exist = 0; if (joydev->open) { input_close_device(handle); wake_up_interruptible(&joydev->wait); - list_for_each_entry(list, &joydev->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_HUP); + list_for_each_entry(client, &joydev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); } else joydev_free(joydev); } diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 271263443c3..82f563e24fd 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -2,7 +2,7 @@ # Joystick driver configuration # menuconfig INPUT_JOYSTICK - bool "Joysticks" + bool "Joysticks/Gamepads" help If you have a joystick, 6dof controller, gamepad, steering wheel, weapon control system or something like that you can say Y here @@ -196,7 +196,7 @@ config JOYSTICK_TWIDJOY config JOYSTICK_DB9 tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads" depends on PARPORT - ---help--- + help Say Y here if you have a Sega Master System gamepad, Sega Genesis gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga, Commodore, Amstrad CPC joystick connected to your parallel port. @@ -253,4 +253,18 @@ config JOYSTICK_JOYDUMP To compile this driver as a module, choose M here: the module will be called joydump. +config JOYSTICK_XPAD + tristate "X-Box gamepad support" + select USB + help + Say Y here if you want to use the X-Box pad with your computer. + Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV) + and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well. + + For information about how to connect the X-Box pad to USB, see + <file:Documentation/input/xpad.txt>. + + To compile this driver as a module, choose M here: the + module will be called xpad. + endif diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index 5231f6ff75b..e855abb0cc5 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -26,5 +26,6 @@ obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o +obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/ diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c index b11a4bbc84c..ff701ab10d7 100644 --- a/drivers/input/joystick/a3d.c +++ b/drivers/input/joystick/a3d.c @@ -241,7 +241,7 @@ static void a3d_adc_close(struct gameport *gameport) static int a3d_open(struct input_dev *dev) { - struct a3d *a3d = dev->private; + struct a3d *a3d = input_get_drvdata(dev); gameport_start_polling(a3d->gameport); return 0; @@ -253,7 +253,7 @@ static int a3d_open(struct input_dev *dev) static void a3d_close(struct input_dev *dev) { - struct a3d *a3d = dev->private; + struct a3d *a3d = input_get_drvdata(dev); gameport_stop_polling(a3d->gameport); } @@ -314,11 +314,12 @@ static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv) input_dev->id.vendor = GAMEPORT_ID_VENDOR_MADCATZ; input_dev->id.product = a3d->mode; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &gameport->dev; - input_dev->private = a3d; + input_dev->dev.parent = &gameport->dev; input_dev->open = a3d_open; input_dev->close = a3d_close; + input_set_drvdata(input_dev, a3d); + if (a3d->mode == A3D_MODE_PXL) { int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER }; diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c index 6279ced8a35..28140c4a110 100644 --- a/drivers/input/joystick/adi.c +++ b/drivers/input/joystick/adi.c @@ -290,7 +290,7 @@ static void adi_poll(struct gameport *gameport) static int adi_open(struct input_dev *dev) { - struct adi_port *port = dev->private; + struct adi_port *port = input_get_drvdata(dev); gameport_start_polling(port->gameport); return 0; @@ -302,7 +302,7 @@ static int adi_open(struct input_dev *dev) static void adi_close(struct input_dev *dev) { - struct adi_port *port = dev->private; + struct adi_port *port = input_get_drvdata(dev); gameport_stop_polling(port->gameport); } @@ -424,8 +424,9 @@ static int adi_init_input(struct adi *adi, struct adi_port *port, int half) input_dev->id.vendor = GAMEPORT_ID_VENDOR_LOGITECH; input_dev->id.product = adi->id; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &port->gameport->dev; - input_dev->private = port; + input_dev->dev.parent = &port->gameport->dev; + + input_set_drvdata(input_dev, port); input_dev->open = adi_open; input_dev->close = adi_close; diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index 51f1e4bfff3..bdd157c1ebf 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -53,7 +53,7 @@ MODULE_LICENSE("GPL"); #define ANALOG_PORTS 16 static char *js[ANALOG_PORTS]; -static int js_nargs; +static unsigned int js_nargs; static int analog_options[ANALOG_PORTS]; module_param_array_named(map, js, charp, &js_nargs, 0); MODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities"); @@ -343,7 +343,7 @@ static void analog_poll(struct gameport *gameport) static int analog_open(struct input_dev *dev) { - struct analog_port *port = dev->private; + struct analog_port *port = input_get_drvdata(dev); gameport_start_polling(port->gameport); return 0; @@ -355,7 +355,7 @@ static int analog_open(struct input_dev *dev) static void analog_close(struct input_dev *dev) { - struct analog_port *port = dev->private; + struct analog_port *port = input_get_drvdata(dev); gameport_stop_polling(port->gameport); } @@ -449,10 +449,13 @@ static int analog_init_device(struct analog_port *port, struct analog *analog, i input_dev->id.vendor = GAMEPORT_ID_VENDOR_ANALOG; input_dev->id.product = analog->mask >> 4; input_dev->id.version = 0x0100; + input_dev->dev.parent = &port->gameport->dev; + + input_set_drvdata(input_dev, port); input_dev->open = analog_open; input_dev->close = analog_close; - input_dev->private = port; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); for (i = j = 0; i < 4; i++) diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c index 034ec39c251..d3352a849b8 100644 --- a/drivers/input/joystick/cobra.c +++ b/drivers/input/joystick/cobra.c @@ -142,7 +142,7 @@ static void cobra_poll(struct gameport *gameport) static int cobra_open(struct input_dev *dev) { - struct cobra *cobra = dev->private; + struct cobra *cobra = input_get_drvdata(dev); gameport_start_polling(cobra->gameport); return 0; @@ -150,7 +150,7 @@ static int cobra_open(struct input_dev *dev) static void cobra_close(struct input_dev *dev) { - struct cobra *cobra = dev->private; + struct cobra *cobra = input_get_drvdata(dev); gameport_stop_polling(cobra->gameport); } @@ -211,8 +211,9 @@ static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv) input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE; input_dev->id.product = 0x0008; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &gameport->dev; - input_dev->private = cobra; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, cobra); input_dev->open = cobra_open; input_dev->close = cobra_close; diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index b41bd2eb37d..86ad1027e12 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -46,17 +46,17 @@ MODULE_LICENSE("GPL"); struct db9_config { int args[2]; - int nargs; + unsigned int nargs; }; #define DB9_MAX_PORTS 3 -static struct db9_config db9[DB9_MAX_PORTS] __initdata; +static struct db9_config db9_cfg[DB9_MAX_PORTS] __initdata; -module_param_array_named(dev, db9[0].args, int, &db9[0].nargs, 0); +module_param_array_named(dev, db9_cfg[0].args, int, &db9_cfg[0].nargs, 0); MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)"); -module_param_array_named(dev2, db9[1].args, int, &db9[0].nargs, 0); +module_param_array_named(dev2, db9_cfg[1].args, int, &db9_cfg[0].nargs, 0); MODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)"); -module_param_array_named(dev3, db9[2].args, int, &db9[2].nargs, 0); +module_param_array_named(dev3, db9_cfg[2].args, int, &db9_cfg[2].nargs, 0); MODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)"); #define DB9_ARG_PARPORT 0 @@ -518,7 +518,7 @@ static void db9_timer(unsigned long private) static int db9_open(struct input_dev *dev) { - struct db9 *db9 = dev->private; + struct db9 *db9 = input_get_drvdata(dev); struct parport *port = db9->pd->port; int err; @@ -542,7 +542,7 @@ static int db9_open(struct input_dev *dev) static void db9_close(struct input_dev *dev) { - struct db9 *db9 = dev->private; + struct db9 *db9 = input_get_drvdata(dev); struct parport *port = db9->pd->port; mutex_lock(&db9->mutex); @@ -625,7 +625,8 @@ static struct db9 __init *db9_probe(int parport, int mode) input_dev->id.vendor = 0x0002; input_dev->id.product = mode; input_dev->id.version = 0x0100; - input_dev->private = db9; + + input_set_drvdata(input_dev, db9); input_dev->open = db9_open; input_dev->close = db9_close; @@ -679,17 +680,17 @@ static int __init db9_init(void) int err = 0; for (i = 0; i < DB9_MAX_PORTS; i++) { - if (db9[i].nargs == 0 || db9[i].args[DB9_ARG_PARPORT] < 0) + if (db9_cfg[i].nargs == 0 || db9_cfg[i].args[DB9_ARG_PARPORT] < 0) continue; - if (db9[i].nargs < 2) { + if (db9_cfg[i].nargs < 2) { printk(KERN_ERR "db9.c: Device type must be specified.\n"); err = -EINVAL; break; } - db9_base[i] = db9_probe(db9[i].args[DB9_ARG_PARPORT], - db9[i].args[DB9_ARG_MODE]); + db9_base[i] = db9_probe(db9_cfg[i].args[DB9_ARG_PARPORT], + db9_cfg[i].args[DB9_ARG_MODE]); if (IS_ERR(db9_base[i])) { err = PTR_ERR(db9_base[i]); break; diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index 711e4b3e9e6..1a452e0e5f2 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -48,16 +48,16 @@ MODULE_LICENSE("GPL"); struct gc_config { int args[GC_MAX_DEVICES + 1]; - int nargs; + unsigned int nargs; }; -static struct gc_config gc[GC_MAX_PORTS] __initdata; +static struct gc_config gc_cfg[GC_MAX_PORTS] __initdata; -module_param_array_named(map, gc[0].args, int, &gc[0].nargs, 0); +module_param_array_named(map, gc_cfg[0].args, int, &gc_cfg[0].nargs, 0); MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)"); -module_param_array_named(map2, gc[1].args, int, &gc[1].nargs, 0); +module_param_array_named(map2, gc_cfg[1].args, int, &gc_cfg[1].nargs, 0); MODULE_PARM_DESC(map2, "Describes second set of devices"); -module_param_array_named(map3, gc[2].args, int, &gc[2].nargs, 0); +module_param_array_named(map3, gc_cfg[2].args, int, &gc_cfg[2].nargs, 0); MODULE_PARM_DESC(map3, "Describes third set of devices"); /* see also gs_psx_delay parameter in PSX support section */ @@ -591,7 +591,7 @@ static void gc_timer(unsigned long private) static int gc_open(struct input_dev *dev) { - struct gc *gc = dev->private; + struct gc *gc = input_get_drvdata(dev); int err; err = mutex_lock_interruptible(&gc->mutex); @@ -610,7 +610,7 @@ static int gc_open(struct input_dev *dev) static void gc_close(struct input_dev *dev) { - struct gc *gc = dev->private; + struct gc *gc = input_get_drvdata(dev); mutex_lock(&gc->mutex); if (!--gc->used) { @@ -646,7 +646,8 @@ static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type) input_dev->id.vendor = 0x0001; input_dev->id.product = pad_type; input_dev->id.version = 0x0100; - input_dev->private = gc; + + input_set_drvdata(input_dev, gc); input_dev->open = gc_open; input_dev->close = gc_close; @@ -809,16 +810,17 @@ static int __init gc_init(void) int err = 0; for (i = 0; i < GC_MAX_PORTS; i++) { - if (gc[i].nargs == 0 || gc[i].args[0] < 0) + if (gc_cfg[i].nargs == 0 || gc_cfg[i].args[0] < 0) continue; - if (gc[i].nargs < 2) { + if (gc_cfg[i].nargs < 2) { printk(KERN_ERR "gamecon.c: at least one device must be specified\n"); err = -EINVAL; break; } - gc_base[i] = gc_probe(gc[i].args[0], gc[i].args + 1, gc[i].nargs - 1); + gc_base[i] = gc_probe(gc_cfg[i].args[0], + gc_cfg[i].args + 1, gc_cfg[i].nargs - 1); if (IS_ERR(gc_base[i])) { err = PTR_ERR(gc_base[i]); break; diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index bacbab5d1b6..d514aebf755 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -220,7 +220,7 @@ static void gf2k_poll(struct gameport *gameport) static int gf2k_open(struct input_dev *dev) { - struct gf2k *gf2k = dev->private; + struct gf2k *gf2k = input_get_drvdata(dev); gameport_start_polling(gf2k->gameport); return 0; @@ -228,7 +228,7 @@ static int gf2k_open(struct input_dev *dev) static void gf2k_close(struct input_dev *dev) { - struct gf2k *gf2k = dev->private; + struct gf2k *gf2k = input_get_drvdata(dev); gameport_stop_polling(gf2k->gameport); } @@ -308,11 +308,13 @@ static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv) input_dev->id.vendor = GAMEPORT_ID_VENDOR_GENIUS; input_dev->id.product = gf2k->id; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &gameport->dev; - input_dev->private = gf2k; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, gf2k); input_dev->open = gf2k_open; input_dev->close = gf2k_close; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); for (i = 0; i < gf2k_axes[gf2k->id]; i++) diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c index 17a90c436de..73eb5ab6f14 100644 --- a/drivers/input/joystick/grip.c +++ b/drivers/input/joystick/grip.c @@ -285,7 +285,7 @@ static void grip_poll(struct gameport *gameport) static int grip_open(struct input_dev *dev) { - struct grip *grip = dev->private; + struct grip *grip = input_get_drvdata(dev); gameport_start_polling(grip->gameport); return 0; @@ -293,7 +293,7 @@ static int grip_open(struct input_dev *dev) static void grip_close(struct input_dev *dev) { - struct grip *grip = dev->private; + struct grip *grip = input_get_drvdata(dev); gameport_stop_polling(grip->gameport); } @@ -363,8 +363,9 @@ static int grip_connect(struct gameport *gameport, struct gameport_driver *drv) input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS; input_dev->id.product = grip->mode[i]; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &gameport->dev; - input_dev->private = grip; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, grip); input_dev->open = grip_open; input_dev->close = grip_close; diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c index 8120a9c4077..555319e6378 100644 --- a/drivers/input/joystick/grip_mp.c +++ b/drivers/input/joystick/grip_mp.c @@ -562,7 +562,7 @@ static void grip_poll(struct gameport *gameport) static int grip_open(struct input_dev *dev) { - struct grip_mp *grip = dev->private; + struct grip_mp *grip = input_get_drvdata(dev); gameport_start_polling(grip->gameport); return 0; @@ -574,9 +574,9 @@ static int grip_open(struct input_dev *dev) static void grip_close(struct input_dev *dev) { - struct grip_mp *grip = dev->private; + struct grip_mp *grip = input_get_drvdata(dev); - gameport_start_polling(grip->gameport); + gameport_stop_polling(grip->gameport); } /* @@ -599,8 +599,9 @@ static int register_slot(int slot, struct grip_mp *grip) input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS; input_dev->id.product = 0x0100 + port->mode; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &grip->gameport->dev; - input_dev->private = grip; + input_dev->dev.parent = &grip->gameport->dev; + + input_set_drvdata(input_dev, grip); input_dev->open = grip_open; input_dev->close = grip_close; diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c index dbc5d92858b..d4e8073caf2 100644 --- a/drivers/input/joystick/guillemot.c +++ b/drivers/input/joystick/guillemot.c @@ -156,7 +156,7 @@ static void guillemot_poll(struct gameport *gameport) static int guillemot_open(struct input_dev *dev) { - struct guillemot *guillemot = dev->private; + struct guillemot *guillemot = input_get_drvdata(dev); gameport_start_polling(guillemot->gameport); return 0; @@ -168,7 +168,7 @@ static int guillemot_open(struct input_dev *dev) static void guillemot_close(struct input_dev *dev) { - struct guillemot *guillemot = dev->private; + struct guillemot *guillemot = input_get_drvdata(dev); gameport_stop_polling(guillemot->gameport); } @@ -231,8 +231,9 @@ static int guillemot_connect(struct gameport *gameport, struct gameport_driver * input_dev->id.vendor = GAMEPORT_ID_VENDOR_GUILLEMOT; input_dev->id.product = guillemot_type[i].id; input_dev->id.version = (int)data[14] << 8 | data[15]; - input_dev->cdev.dev = &gameport->dev; - input_dev->private = guillemot; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, guillemot); input_dev->open = guillemot_open; input_dev->close = guillemot_close; diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index 8fb0c19cc60..f2a4381d0ab 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -2,7 +2,7 @@ * $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $ * * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz> - * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com> + * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com> * * USB/RS232 I-Force joysticks and wheels. */ @@ -205,7 +205,7 @@ static int need_condition_modifier(struct ff_effect *old, struct ff_effect *new) int i; if (new->type != FF_SPRING && new->type != FF_FRICTION) { - printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n"); + warn("bad effect type in need_condition_modifier"); return 0; } @@ -227,7 +227,7 @@ static int need_condition_modifier(struct ff_effect *old, struct ff_effect *new) static int need_magnitude_modifier(struct ff_effect *old, struct ff_effect *effect) { if (effect->type != FF_CONSTANT) { - printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n"); + warn("bad effect type in need_envelope_modifier"); return 0; } @@ -258,7 +258,7 @@ static int need_envelope_modifier(struct ff_effect *old, struct ff_effect *effec break; default: - printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n"); + warn("bad effect type in need_envelope_modifier"); } return 0; @@ -271,7 +271,7 @@ static int need_envelope_modifier(struct ff_effect *old, struct ff_effect *effec static int need_period_modifier(struct ff_effect *old, struct ff_effect *new) { if (new->type != FF_PERIODIC) { - printk(KERN_WARNING "iforce.c: bad effect type in need_period_modifier\n"); + warn("bad effect type in need_period_modifier"); return 0; } return (old->u.periodic.period != new->u.periodic.period diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index 3393a37fec3..fb129c479a6 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -2,7 +2,7 @@ * $Id: iforce-main.c,v 1.19 2002/07/07 10:22:50 jdeneux Exp $ * * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz> - * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com> + * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com> * * USB/RS232 I-Force joysticks and wheels. */ @@ -29,7 +29,7 @@ #include "iforce.h" -MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <deneux@ifrance.com>"); +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>"); MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); MODULE_LICENSE("GPL"); @@ -220,7 +220,7 @@ static void iforce_release(struct input_dev *dev) /* Check: no effects should be present in memory */ for (i = 0; i < dev->ff->max_effects; i++) { if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) { - printk(KERN_WARNING "iforce_release: Device still owns effects\n"); + warn("iforce_release: Device still owns effects"); break; } } @@ -232,7 +232,7 @@ static void iforce_release(struct input_dev *dev) switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: - usb_unlink_urb(iforce->irq); + usb_kill_urb(iforce->irq); /* The device was unplugged before the file * was released */ @@ -287,13 +287,13 @@ int iforce_init_device(struct iforce *iforce) #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: input_dev->id.bustype = BUS_USB; - input_dev->cdev.dev = &iforce->usbdev->dev; + input_dev->dev.parent = &iforce->usbdev->dev; break; #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 case IFORCE_232: input_dev->id.bustype = BUS_RS232; - input_dev->cdev.dev = &iforce->serio->dev; + input_dev->dev.parent = &iforce->serio->dev; break; #endif } @@ -324,7 +324,7 @@ int iforce_init_device(struct iforce *iforce) break; if (i == 20) { /* 5 seconds */ - printk(KERN_ERR "iforce-main.c: Timeout waiting for response from device.\n"); + err("Timeout waiting for response from device."); error = -ENODEV; goto fail; } @@ -336,26 +336,26 @@ int iforce_init_device(struct iforce *iforce) if (!iforce_get_id_packet(iforce, "M")) input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1]; else - printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet M\n"); + warn("Device does not respond to id packet M"); if (!iforce_get_id_packet(iforce, "P")) input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1]; else - printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet P\n"); + warn("Device does not respond to id packet P"); if (!iforce_get_id_packet(iforce, "B")) iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; else - printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n"); + warn("Device does not respond to id packet B"); if (!iforce_get_id_packet(iforce, "N")) ff_effects = iforce->edata[1]; else - printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n"); + warn("Device does not respond to id packet N"); /* Check if the device can store more effects than the driver can really handle */ if (ff_effects > IFORCE_EFFECTS_MAX) { - printk(KERN_WARNING "iforce: Limiting number of effects to %d (device reports %d)\n", + warn("Limiting number of effects to %d (device reports %d)", IFORCE_EFFECTS_MAX, ff_effects); ff_effects = IFORCE_EFFECTS_MAX; } @@ -457,8 +457,6 @@ int iforce_init_device(struct iforce *iforce) if (error) goto fail; - printk(KERN_DEBUG "iforce->dev->open = %p\n", iforce->dev->open); - return 0; fail: input_free_device(input_dev); diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 808f05932a6..21c4e13d3a5 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -2,7 +2,7 @@ * $Id: iforce-packets.c,v 1.16 2002/07/07 10:22:50 jdeneux Exp $ * * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz> - * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com> + * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com> * * USB/RS232 I-Force joysticks and wheels. */ @@ -39,10 +39,10 @@ void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) { int i; - printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd); + printk(KERN_DEBUG __FILE__ ": %s cmd = %04x, data = ", msg, cmd); for (i = 0; i < LO(cmd); i++) printk("%02x ", data[i]); - printk(")\n"); + printk("\n"); } /* @@ -65,8 +65,9 @@ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) head = iforce->xmit.head; tail = iforce->xmit.tail; + if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) { - printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n"); + warn("not enough space in xmit buffer to send new packet"); spin_unlock_irqrestore(&iforce->xmit_lock, flags); return -1; } @@ -126,8 +127,6 @@ int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value) { unsigned char data[3]; -printk(KERN_DEBUG "iforce-packets.c: control_playback %d %d\n", id, value); - data[0] = LO(id); data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0; data[2] = LO(value); @@ -151,7 +150,7 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr) return 0; } } - printk(KERN_WARNING "iforce-packets.c: unused effect %04x updated !!!\n", addr); + warn("unused effect %04x updated !!!", addr); return -1; } @@ -162,7 +161,7 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) static int being_used = 0; if (being_used) - printk(KERN_WARNING "iforce-packets.c: re-entrant call to iforce_process %d\n", being_used); + warn("re-entrant call to iforce_process %d", being_used); being_used++; #ifdef CONFIG_JOYSTICK_IFORCE_232 @@ -266,7 +265,7 @@ int iforce_get_id_packet(struct iforce *iforce, char *packet) return -1; } #else - printk(KERN_ERR "iforce_get_id_packet: iforce->bus = USB!\n"); + err("iforce_get_id_packet: iforce->bus = USB!"); #endif break; @@ -284,13 +283,12 @@ int iforce_get_id_packet(struct iforce *iforce, char *packet) return -1; } #else - printk(KERN_ERR "iforce_get_id_packet: iforce->bus = SERIO!\n"); + err("iforce_get_id_packet: iforce->bus = SERIO!"); #endif break; default: - printk(KERN_ERR "iforce_get_id_packet: iforce->bus = %d\n", - iforce->bus); + err("iforce_get_id_packet: iforce->bus = %d", iforce->bus); break; } diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c index ec4be535f48..7b4bc19cef2 100644 --- a/drivers/input/joystick/iforce/iforce-serio.c +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -2,7 +2,7 @@ * $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz> - * Copyright (c) 2001 Johann Deneux <deneux@ifrance.com> + * Copyright (c) 2001, 2007 Johann Deneux <johann.deneux@gmail.com> * * USB/RS232 I-Force joysticks and wheels. */ diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 80cdebcbcb9..750099d8e3c 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -2,7 +2,7 @@ * $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $ * * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz> - * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com> + * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com> * * USB/RS232 I-Force joysticks and wheels. */ @@ -65,7 +65,7 @@ void iforce_usb_xmit(struct iforce *iforce) XMIT_INC(iforce->xmit.tail, n); if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) { - printk(KERN_WARNING "iforce-usb.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n); + warn("usb_submit_urb failed %d\n", n); } /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended. @@ -110,7 +110,7 @@ static void iforce_usb_out(struct urb *urb) struct iforce *iforce = urb->context; if (urb->status) { - printk(KERN_DEBUG "iforce_usb_out: urb->status %d, exiting", urb->status); + dbg("urb->status %d, exiting", urb->status); return; } @@ -190,10 +190,9 @@ fail: /* Called by iforce_delete() */ void iforce_usb_delete(struct iforce* iforce) { - usb_unlink_urb(iforce->irq); -/* Is it ok to unlink those ? */ - usb_unlink_urb(iforce->out); - usb_unlink_urb(iforce->ctrl); + usb_kill_urb(iforce->irq); + usb_kill_urb(iforce->out); + usb_kill_urb(iforce->ctrl); usb_free_urb(iforce->irq); usb_free_urb(iforce->out); diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h index ffaeaefa1a4..40a853ac21c 100644 --- a/drivers/input/joystick/iforce/iforce.h +++ b/drivers/input/joystick/iforce/iforce.h @@ -2,7 +2,7 @@ * $Id: iforce.h,v 1.13 2002/07/07 10:22:50 jdeneux Exp $ * * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz> - * Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com> + * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com> * * USB/RS232 I-Force joysticks and wheels. */ @@ -124,7 +124,7 @@ struct iforce { /* Buffer used for asynchronous sending of bytes to the device */ struct circ_buf xmit; unsigned char xmit_data[XMIT_SIZE]; - long xmit_flags[1]; + unsigned long xmit_flags[1]; /* Force Feedback */ wait_queue_head_t wait; diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c index fec8b3d0967..1aec1e9d7c5 100644 --- a/drivers/input/joystick/interact.c +++ b/drivers/input/joystick/interact.c @@ -185,7 +185,7 @@ static void interact_poll(struct gameport *gameport) static int interact_open(struct input_dev *dev) { - struct interact *interact = dev->private; + struct interact *interact = input_get_drvdata(dev); gameport_start_polling(interact->gameport); return 0; @@ -197,7 +197,7 @@ static int interact_open(struct input_dev *dev) static void interact_close(struct input_dev *dev) { - struct interact *interact = dev->private; + struct interact *interact = input_get_drvdata(dev); gameport_stop_polling(interact->gameport); } @@ -262,7 +262,9 @@ static int interact_connect(struct gameport *gameport, struct gameport_driver *d input_dev->id.vendor = GAMEPORT_ID_VENDOR_INTERACT; input_dev->id.product = interact_type[i].id; input_dev->id.version = 0x0100; - input_dev->private = interact; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, interact); input_dev->open = interact_open; input_dev->close = interact_close; diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c index 4112789f119..b35604ee43a 100644 --- a/drivers/input/joystick/magellan.c +++ b/drivers/input/joystick/magellan.c @@ -168,8 +168,7 @@ static int magellan_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_MAGELLAN; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = magellan; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c index e58b22c018e..2adf73f63c9 100644 --- a/drivers/input/joystick/sidewinder.c +++ b/drivers/input/joystick/sidewinder.c @@ -509,7 +509,7 @@ static void sw_poll(struct gameport *gameport) static int sw_open(struct input_dev *dev) { - struct sw *sw = dev->private; + struct sw *sw = input_get_drvdata(dev); gameport_start_polling(sw->gameport); return 0; @@ -517,7 +517,7 @@ static int sw_open(struct input_dev *dev) static void sw_close(struct input_dev *dev) { - struct sw *sw = dev->private; + struct sw *sw = input_get_drvdata(dev); gameport_stop_polling(sw->gameport); } @@ -751,8 +751,9 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv) input_dev->id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT; input_dev->id.product = sw->type; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &gameport->dev; - input_dev->private = sw; + input_dev->dev.parent = &gameport->dev; + + input_set_drvdata(input_dev, sw); input_dev->open = sw_open; input_dev->close = sw_close; diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c index 08bf113e62e..abb7c4cf54a 100644 --- a/drivers/input/joystick/spaceball.c +++ b/drivers/input/joystick/spaceball.c @@ -226,8 +226,7 @@ static int spaceball_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_SPACEBALL; input_dev->id.product = id; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = spaceball; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c index c9c79211af7..c4937f1e837 100644 --- a/drivers/input/joystick/spaceorb.c +++ b/drivers/input/joystick/spaceorb.c @@ -183,8 +183,7 @@ static int spaceorb_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_SPACEORB; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = spaceorb; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c index ecb0916215f..8581ee991d4 100644 --- a/drivers/input/joystick/stinger.c +++ b/drivers/input/joystick/stinger.c @@ -154,8 +154,7 @@ static int stinger_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_STINGER; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = stinger; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_A)] = BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_X) | diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index bb23ed2a04a..3b36ee04f72 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -265,7 +265,7 @@ static void tmdc_poll(struct gameport *gameport) static int tmdc_open(struct input_dev *dev) { - struct tmdc *tmdc = dev->private; + struct tmdc *tmdc = input_get_drvdata(dev); gameport_start_polling(tmdc->gameport); return 0; @@ -273,7 +273,7 @@ static int tmdc_open(struct input_dev *dev) static void tmdc_close(struct input_dev *dev) { - struct tmdc *tmdc = dev->private; + struct tmdc *tmdc = input_get_drvdata(dev); gameport_stop_polling(tmdc->gameport); } @@ -326,8 +326,9 @@ static int tmdc_setup_port(struct tmdc *tmdc, int idx, unsigned char *data) input_dev->id.vendor = GAMEPORT_ID_VENDOR_THRUSTMASTER; input_dev->id.product = model->id; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &tmdc->gameport->dev; - input_dev->private = tmdc; + input_dev->dev.parent = &tmdc->gameport->dev; + + input_set_drvdata(input_dev, tmdc); input_dev->open = tmdc_open; input_dev->close = tmdc_close; diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index 037d3487fcc..8381c6f1437 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -48,16 +48,16 @@ MODULE_LICENSE("GPL"); struct tgfx_config { int args[TGFX_MAX_DEVICES + 1]; - int nargs; + unsigned int nargs; }; -static struct tgfx_config tgfx[TGFX_MAX_PORTS] __initdata; +static struct tgfx_config tgfx_cfg[TGFX_MAX_PORTS] __initdata; -module_param_array_named(map, tgfx[0].args, int, &tgfx[0].nargs, 0); +module_param_array_named(map, tgfx_cfg[0].args, int, &tgfx_cfg[0].nargs, 0); MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>"); -module_param_array_named(map2, tgfx[1].args, int, &tgfx[1].nargs, 0); +module_param_array_named(map2, tgfx_cfg[1].args, int, &tgfx_cfg[1].nargs, 0); MODULE_PARM_DESC(map2, "Describes second set of devices"); -module_param_array_named(map3, tgfx[2].args, int, &tgfx[2].nargs, 0); +module_param_array_named(map3, tgfx_cfg[2].args, int, &tgfx_cfg[2].nargs, 0); MODULE_PARM_DESC(map3, "Describes third set of devices"); #define TGFX_REFRESH_TIME HZ/100 /* 10 ms */ @@ -122,7 +122,7 @@ static void tgfx_timer(unsigned long private) static int tgfx_open(struct input_dev *dev) { - struct tgfx *tgfx = dev->private; + struct tgfx *tgfx = input_get_drvdata(dev); int err; err = mutex_lock_interruptible(&tgfx->sem); @@ -141,7 +141,7 @@ static int tgfx_open(struct input_dev *dev) static void tgfx_close(struct input_dev *dev) { - struct tgfx *tgfx = dev->private; + struct tgfx *tgfx = input_get_drvdata(dev); mutex_lock(&tgfx->sem); if (!--tgfx->used) { @@ -224,7 +224,8 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs) input_dev->id.product = n_buttons[i]; input_dev->id.version = 0x0100; - input_dev->private = tgfx; + input_set_drvdata(input_dev, tgfx); + input_dev->open = tgfx_open; input_dev->close = tgfx_close; @@ -282,16 +283,18 @@ static int __init tgfx_init(void) int err = 0; for (i = 0; i < TGFX_MAX_PORTS; i++) { - if (tgfx[i].nargs == 0 || tgfx[i].args[0] < 0) + if (tgfx_cfg[i].nargs == 0 || tgfx_cfg[i].args[0] < 0) continue; - if (tgfx[i].nargs < 2) { + if (tgfx_cfg[i].nargs < 2) { printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n"); err = -EINVAL; break; } - tgfx_base[i] = tgfx_probe(tgfx[i].args[0], tgfx[i].args + 1, tgfx[i].nargs - 1); + tgfx_base[i] = tgfx_probe(tgfx_cfg[i].args[0], + tgfx_cfg[i].args + 1, + tgfx_cfg[i].nargs - 1); if (IS_ERR(tgfx_base[i])) { err = PTR_ERR(tgfx_base[i]); break; diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c index 9cf17d6ced8..c91504ec38e 100644 --- a/drivers/input/joystick/twidjoy.c +++ b/drivers/input/joystick/twidjoy.c @@ -205,11 +205,9 @@ static int twidjoy_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_TWIDJOY; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = twidjoy; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y); input_set_abs_params(input_dev, ABS_X, -50, 50, 4, 4); input_set_abs_params(input_dev, ABS_Y, -50, 50, 4, 4); diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c index 29d339acf43..4e85f72eefd 100644 --- a/drivers/input/joystick/warrior.c +++ b/drivers/input/joystick/warrior.c @@ -160,8 +160,7 @@ static int warrior_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_WARRIOR; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = warrior; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TRIGGER)] = BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_TOP2); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c new file mode 100644 index 00000000000..8c8cd95a698 --- /dev/null +++ b/drivers/input/joystick/xpad.c @@ -0,0 +1,433 @@ +/* + * X-Box gamepad - v0.0.6 + * + * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de> + * 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>, + * Steven Toth <steve@toth.demon.co.uk>, + * Franz Lehner <franz@caos.at>, + * Ivan Hawkes <blackhawk@ivanhawkes.com> + * 2005 Dominic Cerquetti <binary1230@yahoo.com> + * 2006 Adam Buchbinder <adam.buchbinder@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This driver is based on: + * - information from http://euc.jp/periphs/xbox-controller.ja.html + * - the iForce driver drivers/char/joystick/iforce.c + * - the skeleton-driver drivers/usb/usb-skeleton.c + * + * Thanks to: + * - ITO Takayuki for providing essential xpad information on his website + * - Vojtech Pavlik - iforce driver / input subsystem + * - Greg Kroah-Hartman - usb-skeleton driver + * - XBOX Linux project - extra USB id's + * + * TODO: + * - fine tune axes (especially trigger axes) + * - fix "analog" buttons (reported as digital now) + * - get rumble working + * - need USB IDs for other dance pads + * + * History: + * + * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller" + * + * 2002-07-02 - 0.0.2 : basic working version + * - all axes and 9 of the 10 buttons work (german InterAct device) + * - the black button does not work + * + * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik + * - indentation fixes + * - usb + input init sequence fixes + * + * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3 + * - verified the lack of HID and report descriptors + * - verified that ALL buttons WORK + * - fixed d-pad to axes mapping + * + * 2002-07-17 - 0.0.5 : simplified d-pad handling + * + * 2004-10-02 - 0.0.6 : DDR pad support + * - borrowed from the XBOX linux kernel + * - USB id's for commonly used dance pads are present + * - dance pads will map D-PAD to buttons, not axes + * - pass the module paramater 'dpad_to_buttons' to force + * the D-PAD to map to buttons if your pad is not detected + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/usb/input.h> + +#define DRIVER_VERSION "v0.0.6" +#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>" +#define DRIVER_DESC "X-Box pad driver" + +#define XPAD_PKT_LEN 32 + +/* xbox d-pads should map to buttons, as is required for DDR pads + but we map them to axes when possible to simplify things */ +#define MAP_DPAD_TO_BUTTONS 0 +#define MAP_DPAD_TO_AXES 1 +#define MAP_DPAD_UNKNOWN -1 + +static int dpad_to_buttons; +module_param(dpad_to_buttons, bool, S_IRUGO); +MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads"); + +static const struct xpad_device { + u16 idVendor; + u16 idProduct; + char *name; + u8 dpad_mapping; +} xpad_device[] = { + { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES }, + { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES }, + { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES }, + { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES }, + { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS }, + { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES }, + { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES }, + { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES }, + { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES }, + { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES }, + { 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES }, + { 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES }, + { 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES }, + { 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES }, + { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS }, + { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES }, + { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS }, + { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES }, + { 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES }, + { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES }, + { 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES }, + { 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES}, + { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", MAP_DPAD_TO_AXES }, + { 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES }, + { 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES }, + { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES }, + { 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES }, + { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES }, + { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES }, + { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS }, + { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS }, + { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES }, + { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN } +}; + +static const signed short xpad_btn[] = { + BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* "analog" buttons */ + BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ + -1 /* terminating entry */ +}; + +/* only used if MAP_DPAD_TO_BUTTONS */ +static const signed short xpad_btn_pad[] = { + BTN_LEFT, BTN_RIGHT, /* d-pad left, right */ + BTN_0, BTN_1, /* d-pad up, down (XXX names??) */ + -1 /* terminating entry */ +}; + +static const signed short xpad_abs[] = { + ABS_X, ABS_Y, /* left stick */ + ABS_RX, ABS_RY, /* right stick */ + ABS_Z, ABS_RZ, /* triggers left/right */ + -1 /* terminating entry */ +}; + +/* only used if MAP_DPAD_TO_AXES */ +static const signed short xpad_abs_pad[] = { + ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */ + -1 /* terminating entry */ +}; + +static struct usb_device_id xpad_table [] = { + { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ + { } +}; + +MODULE_DEVICE_TABLE (usb, xpad_table); + +struct usb_xpad { + struct input_dev *dev; /* input device interface */ + struct usb_device *udev; /* usb device */ + + struct urb *irq_in; /* urb for interrupt in report */ + unsigned char *idata; /* input data */ + dma_addr_t idata_dma; + + char phys[65]; /* physical device path */ + + int dpad_mapping; /* map d-pad to buttons or to axes */ +}; + +/* + * xpad_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. + * + * The used report descriptor was taken from ITO Takayukis website: + * http://euc.jp/periphs/xbox-controller.ja.html + */ + +static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) +{ + struct input_dev *dev = xpad->dev; + + /* left stick */ + input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12])); + input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14])); + + /* right stick */ + input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16])); + input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18])); + + /* triggers left/right */ + input_report_abs(dev, ABS_Z, data[10]); + input_report_abs(dev, ABS_RZ, data[11]); + + /* digital pad */ + if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) { + input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); + input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); + } else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ { + input_report_key(dev, BTN_LEFT, data[2] & 0x04); + input_report_key(dev, BTN_RIGHT, data[2] & 0x08); + input_report_key(dev, BTN_0, data[2] & 0x01); // up + input_report_key(dev, BTN_1, data[2] & 0x02); // down + } + + /* start/back buttons and stick press left/right */ + input_report_key(dev, BTN_START, data[2] & 0x10); + input_report_key(dev, BTN_BACK, data[2] & 0x20); + input_report_key(dev, BTN_THUMBL, data[2] & 0x40); + input_report_key(dev, BTN_THUMBR, data[2] & 0x80); + + /* "analog" buttons A, B, X, Y */ + input_report_key(dev, BTN_A, data[4]); + input_report_key(dev, BTN_B, data[5]); + input_report_key(dev, BTN_X, data[6]); + input_report_key(dev, BTN_Y, data[7]); + + /* "analog" buttons black, white */ + input_report_key(dev, BTN_C, data[8]); + input_report_key(dev, BTN_Z, data[9]); + + input_sync(dev); +} + +static void xpad_irq_in(struct urb *urb) +{ + struct usb_xpad *xpad = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + xpad_process_packet(xpad, 0, xpad->idata); + +exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + +static int xpad_open (struct input_dev *dev) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + xpad->irq_in->dev = xpad->udev; + if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void xpad_close (struct input_dev *dev) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + usb_kill_urb(xpad->irq_in); +} + +static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) +{ + set_bit(abs, input_dev->absbit); + + switch (abs) { + case ABS_X: + case ABS_Y: + case ABS_RX: + case ABS_RY: /* the two sticks */ + input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128); + break; + case ABS_Z: + case ABS_RZ: /* the triggers */ + input_set_abs_params(input_dev, abs, 0, 255, 0, 0); + break; + case ABS_HAT0X: + case ABS_HAT0Y: /* the d-pad (only if MAP_DPAD_TO_AXES) */ + input_set_abs_params(input_dev, abs, -1, 1, 0, 0); + break; + } +} + +static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev (intf); + struct usb_xpad *xpad; + struct input_dev *input_dev; + struct usb_endpoint_descriptor *ep_irq_in; + int i; + int error = -ENOMEM; + + for (i = 0; xpad_device[i].idVendor; i++) { + if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) && + (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct)) + break; + } + + xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!xpad || !input_dev) + goto fail1; + + xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN, + GFP_ATOMIC, &xpad->idata_dma); + if (!xpad->idata) + goto fail1; + + xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->irq_in) + goto fail2; + + xpad->udev = udev; + xpad->dpad_mapping = xpad_device[i].dpad_mapping; + if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) + xpad->dpad_mapping = dpad_to_buttons; + xpad->dev = input_dev; + usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); + strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); + + input_dev->name = xpad_device[i].name; + input_dev->phys = xpad->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, xpad); + + input_dev->open = xpad_open; + input_dev->close = xpad_close; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + /* set up buttons */ + for (i = 0; xpad_btn[i] >= 0; i++) + set_bit(xpad_btn[i], input_dev->keybit); + if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) + for (i = 0; xpad_btn_pad[i] >= 0; i++) + set_bit(xpad_btn_pad[i], input_dev->keybit); + + /* set up axes */ + for (i = 0; xpad_abs[i] >= 0; i++) + xpad_set_up_abs(input_dev, xpad_abs[i]); + if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) + for (i = 0; xpad_abs_pad[i] >= 0; i++) + xpad_set_up_abs(input_dev, xpad_abs_pad[i]); + + ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; + usb_fill_int_urb(xpad->irq_in, udev, + usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), + xpad->idata, XPAD_PKT_LEN, xpad_irq_in, + xpad, ep_irq_in->bInterval); + xpad->irq_in->transfer_dma = xpad->idata_dma; + xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(xpad->dev); + if (error) + goto fail3; + + usb_set_intfdata(intf, xpad); + return 0; + + fail3: usb_free_urb(xpad->irq_in); + fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); + fail1: input_free_device(input_dev); + kfree(xpad); + return error; + +} + +static void xpad_disconnect(struct usb_interface *intf) +{ + struct usb_xpad *xpad = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (xpad) { + usb_kill_urb(xpad->irq_in); + input_unregister_device(xpad->dev); + usb_free_urb(xpad->irq_in); + usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, + xpad->idata, xpad->idata_dma); + kfree(xpad); + } +} + +static struct usb_driver xpad_driver = { + .name = "xpad", + .probe = xpad_probe, + .disconnect = xpad_disconnect, + .id_table = xpad_table, +}; + +static int __init usb_xpad_init(void) +{ + int result = usb_register(&xpad_driver); + if (result == 0) + info(DRIVER_DESC ":" DRIVER_VERSION); + return result; +} + +static void __exit usb_xpad_exit(void) +{ + usb_deregister(&xpad_driver); +} + +module_init(usb_xpad_init); +module_exit(usb_xpad_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index f17e9c7d4b3..bd707b86c11 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -164,6 +164,17 @@ config KEYBOARD_AMIGA To compile this driver as a module, choose M here: the module will be called amikbd. +config KEYBOARD_ATARI + tristate "Atari keyboard" + depends on ATARI + select ATARI_KBD_CORE + help + Say Y here if you are running Linux on any Atari and have a keyboard + attached. + + To compile this driver as a module, choose M here: the + module will be called atakbd. + config KEYBOARD_HIL_OLD tristate "HP HIL keyboard support (simple driver)" depends on GSC || HP300 @@ -203,9 +214,19 @@ config KEYBOARD_OMAP To compile this driver as a module, choose M here: the module will be called omap-keypad. +config KEYBOARD_PXA27x + tristate "PXA27x keyboard support" + depends on PXA27x + help + Enable support for PXA27x matrix keyboard controller + + To compile this driver as a module, choose M here: the + module will be called pxa27x_keyboard. + config KEYBOARD_AAED2000 tristate "AAED-2000 keyboard" depends on MACH_AAED2000 + select INPUT_POLLDEV default y help Say Y here to enable the keyboard on the Agilent AAED-2000 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 586a0fe53be..28d211b87b1 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o +obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o @@ -17,6 +18,7 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c index 65fcb6af63a..63d6ead6b87 100644 --- a/drivers/input/keyboard/aaed2000_kbd.c +++ b/drivers/input/keyboard/aaed2000_kbd.c @@ -14,12 +14,11 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/init.h> -#include <linux/input.h> +#include <linux/input-polldev.h> #include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/workqueue.h> #include <asm/arch/hardware.h> #include <asm/arch/aaed2000.h> @@ -46,8 +45,7 @@ static unsigned char aaedkbd_keycode[NR_SCANCODES] = { struct aaedkbd { unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)]; - struct input_dev *input; - struct work_struct workq; + struct input_polled_dev *poll_dev; int kbdscan_state[KB_COLS]; int kbdscan_count[KB_COLS]; }; @@ -64,14 +62,15 @@ static void aaedkbd_report_col(struct aaedkbd *aaedkbd, scancode = SCANCODE(row, col); pressed = rowd & KB_ROWMASK(row); - input_report_key(aaedkbd->input, aaedkbd->keycode[scancode], pressed); + input_report_key(aaedkbd->poll_dev->input, + aaedkbd->keycode[scancode], pressed); } } /* Scan the hardware keyboard and push any changes up through the input layer */ -static void aaedkbd_work(void *data) +static void aaedkbd_poll(struct input_polled_dev *dev) { - struct aaedkbd *aaedkbd = data; + struct aaedkbd *aaedkbd = dev->private; unsigned int col, rowd; col = 0; @@ -90,59 +89,41 @@ static void aaedkbd_work(void *data) } while (col < KB_COLS); AAEC_GPIO_KSCAN = 0x07; - input_sync(aaedkbd->input); - - schedule_delayed_work(&aaedkbd->workq, msecs_to_jiffies(SCAN_INTERVAL)); -} - -static int aaedkbd_open(struct input_dev *indev) -{ - struct aaedkbd *aaedkbd = indev->private; - - schedule_delayed_work(&aaedkbd->workq, msecs_to_jiffies(SCAN_INTERVAL)); - - return 0; -} - -static void aaedkbd_close(struct input_dev *indev) -{ - struct aaedkbd *aaedkbd = indev->private; - - cancel_delayed_work(&aaedkbd->workq); - flush_scheduled_work(); + input_sync(dev->input); } static int __devinit aaedkbd_probe(struct platform_device *pdev) { struct aaedkbd *aaedkbd; + struct input_polled_dev *poll_dev; struct input_dev *input_dev; int i; int error; aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!aaedkbd || !input_dev) { + poll_dev = input_allocate_polled_device(); + if (!aaedkbd || !poll_dev) { error = -ENOMEM; goto fail; } platform_set_drvdata(pdev, aaedkbd); - aaedkbd->input = input_dev; - - /* Init keyboard rescan workqueue */ - INIT_WORK(&aaedkbd->workq, aaedkbd_work, aaedkbd); - + aaedkbd->poll_dev = poll_dev; memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode)); + poll_dev->private = aaedkbd; + poll_dev->poll = aaedkbd_poll; + poll_dev->poll_interval = SCAN_INTERVAL; + + input_dev = poll_dev->input; input_dev->name = "AAED-2000 Keyboard"; input_dev->phys = "aaedkbd/input0"; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &pdev->dev; - input_dev->private = aaedkbd; + input_dev->dev.parent = &pdev->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); input_dev->keycode = aaedkbd->keycode; @@ -153,17 +134,14 @@ static int __devinit aaedkbd_probe(struct platform_device *pdev) set_bit(aaedkbd->keycode[i], input_dev->keybit); clear_bit(0, input_dev->keybit); - input_dev->open = aaedkbd_open; - input_dev->close = aaedkbd_close; - - error = input_register_device(aaedkbd->input); + error = input_register_polled_device(aaedkbd->poll_dev); if (error) goto fail; return 0; fail: kfree(aaedkbd); - input_free_device(input_dev); + input_free_polled_device(poll_dev); return error; } @@ -171,7 +149,8 @@ static int __devexit aaedkbd_remove(struct platform_device *pdev) { struct aaedkbd *aaedkbd = platform_get_drvdata(pdev); - input_unregister_device(aaedkbd->input); + input_unregister_polled_device(aaedkbd->poll_dev); + input_free_polled_device(aaedkbd->poll_dev); kfree(aaedkbd); return 0; diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c new file mode 100644 index 00000000000..ded1d6ac6ff --- /dev/null +++ b/drivers/input/keyboard/atakbd.c @@ -0,0 +1,134 @@ +/* + * atakbd.c + * + * Copyright (c) 2005 Michael Schmitz + * + * Based on amikbd.c, which is + * + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Hamish Macdonald + */ + +/* + * Atari keyboard driver for Linux/m68k + * + * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c + * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard + * interrupt is shared with the MIDI ACIA so MIDI data also get handled there). + * This driver only deals with handing key events off to the input layer. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/interrupt.h> + +#include <asm/atariints.h> +#include <asm/atarihw.h> +#include <asm/atarikb.h> +#include <asm/irq.h> + +MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>"); +MODULE_DESCRIPTION("Atari keyboard driver"); +MODULE_LICENSE("GPL"); + +static unsigned char atakbd_keycode[0x72]; + +static struct input_dev *atakbd_dev; + +static void atakbd_interrupt(unsigned char scancode, char down) +{ + + if (scancode < 0x72) { /* scancodes < 0xf2 are keys */ + + // report raw events here? + + scancode = atakbd_keycode[scancode]; + + if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */ + input_report_key(atakbd_dev, scancode, 1); + input_report_key(atakbd_dev, scancode, 0); + input_sync(atakbd_dev); + } else { + input_report_key(atakbd_dev, scancode, down); + input_sync(atakbd_dev); + } + } else /* scancodes >= 0xf2 are mouse data, most likely */ + printk(KERN_INFO "atakbd: unhandled scancode %x\n", scancode); + + return; +} + +static int __init atakbd_init(void) +{ + int i; + + if (!ATARIHW_PRESENT(ST_MFP)) + return -EIO; + + // TODO: request_mem_region if not done in arch code + + if (!(atakbd_dev = input_allocate_device())) + return -ENOMEM; + + // need to init core driver if not already done so + if (atari_keyb_init()) + return -ENODEV; + + atakbd_dev->name = "Atari Keyboard"; + atakbd_dev->phys = "atakbd/input0"; + atakbd_dev->id.bustype = BUS_ATARI; + atakbd_dev->id.vendor = 0x0001; + atakbd_dev->id.product = 0x0001; + atakbd_dev->id.version = 0x0100; + + atakbd_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + atakbd_dev->keycode = atakbd_keycode; + atakbd_dev->keycodesize = sizeof(unsigned char); + atakbd_dev->keycodemax = ARRAY_SIZE(atakbd_keycode); + + for (i = 1; i < 0x72; i++) { + atakbd_keycode[i] = i; + set_bit(atakbd_keycode[i], atakbd_dev->keybit); + } + + input_register_device(atakbd_dev); + + atari_input_keyboard_interrupt_hook = atakbd_interrupt; + + printk(KERN_INFO "input: %s at IKBD ACIA\n", atakbd_dev->name); + + return 0; +} + +static void __exit atakbd_exit(void) +{ + atari_input_keyboard_interrupt_hook = NULL; + input_unregister_device(atakbd_dev); +} + +module_init(atakbd_init); +module_exit(atakbd_exit); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 663877076bc..be1fe46cd30 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -586,7 +586,7 @@ static void atkbd_event_work(struct work_struct *work) static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct atkbd *atkbd = dev->private; + struct atkbd *atkbd = input_get_drvdata(dev); if (!atkbd->write) return -1; @@ -883,8 +883,9 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd) input_dev->id.product = atkbd->translated ? 1 : atkbd->set; input_dev->id.version = atkbd->id; input_dev->event = atkbd_event; - input_dev->private = atkbd; - input_dev->cdev.dev = &atkbd->ps2dev.serio->dev; + input_dev->dev.parent = &atkbd->ps2dev.serio->dev; + + input_set_drvdata(input_dev, atkbd); input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_MSC); diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c index 1016c94e65d..6578bfff644 100644 --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -323,8 +323,7 @@ static int __init corgikbd_probe(struct platform_device *pdev) input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &pdev->dev; - input_dev->private = corgikbd; + input_dev->dev.parent = &pdev->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW); input_dev->keycode = corgikbd->keycode; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index ccf6df387b6..739212252b0 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -35,11 +35,14 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) struct input_dev *input = platform_get_drvdata(pdev); for (i = 0; i < pdata->nbuttons; i++) { - int gpio = pdata->buttons[i].gpio; + struct gpio_keys_button *button = &pdata->buttons[i]; + int gpio = button->gpio; + if (irq == gpio_to_irq(gpio)) { - int state = (gpio_get_value(gpio) ? 1 : 0) ^ (pdata->buttons[i].active_low); + unsigned int type = button->type ?: EV_KEY; + int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low; - input_report_key(input, pdata->buttons[i].keycode, state); + input_event(input, type, button->code, !!state); input_sync(input); } } @@ -63,8 +66,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) input->name = pdev->name; input->phys = "gpio-keys/input0"; - input->cdev.dev = &pdev->dev; - input->private = pdata; + input->dev.parent = &pdev->dev; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; @@ -72,19 +74,21 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) input->id.version = 0x0100; for (i = 0; i < pdata->nbuttons; i++) { - int code = pdata->buttons[i].keycode; - int irq = gpio_to_irq(pdata->buttons[i].gpio); + struct gpio_keys_button *button = &pdata->buttons[i]; + int irq = gpio_to_irq(button->gpio); + unsigned int type = button->type ?: EV_KEY; set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM, - pdata->buttons[i].desc ? pdata->buttons[i].desc : "gpio_keys", + button->desc ? button->desc : "gpio_keys", pdev); if (error) { printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n", irq, error); goto fail; } - set_bit(code, input->keybit); + + input_set_capability(input, type, button->code); } error = input_register_device(input); diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index 7cc9728b04d..cdd254f2e6c 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -51,7 +51,7 @@ MODULE_LICENSE("Dual BSD/GPL"); #define HIL_KBD_SET1_UPBIT 0x01 #define HIL_KBD_SET1_SHIFT 1 -static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] = +static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly = { HIL_KEYCODES_SET1 }; #define HIL_KBD_SET2_UPBIT 0x01 @@ -60,10 +60,10 @@ static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] = #define HIL_KBD_SET3_UPBIT 0x80 #define HIL_KBD_SET3_SHIFT 0 -static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] = +static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] __read_mostly = { HIL_KEYCODES_SET3 }; -static char hil_language[][16] = { HIL_LOCALE_MAP }; +static const char hil_language[][16] = { HIL_LOCALE_MAP }; struct hil_kbd { struct input_dev *dev; @@ -94,10 +94,12 @@ static void hil_kbd_process_record(struct hil_kbd *kbd) idx = kbd->idx4/4; p = data[idx - 1]; - if ((p & ~HIL_CMDCT_POL) == - (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report; - if ((p & ~HIL_CMDCT_RPL) == - (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report; + if ((p & ~HIL_CMDCT_POL) == + (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) + goto report; + if ((p & ~HIL_CMDCT_RPL) == + (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) + goto report; /* Not a poll response. See if we are loading config records. */ switch (p & HIL_PKT_DATA_MASK) { @@ -107,27 +109,32 @@ static void hil_kbd_process_record(struct hil_kbd *kbd) for (; i < HIL_KBD_MAX_LENGTH; i++) kbd->idd[i] = 0; break; + case HIL_CMD_RSC: for (i = 0; i < idx; i++) kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK; for (; i < HIL_KBD_MAX_LENGTH; i++) kbd->rsc[i] = 0; break; + case HIL_CMD_EXD: for (i = 0; i < idx; i++) kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK; for (; i < HIL_KBD_MAX_LENGTH; i++) kbd->exd[i] = 0; break; + case HIL_CMD_RNM: for (i = 0; i < idx; i++) kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK; for (; i < HIL_KBD_MAX_LENGTH + 1; i++) kbd->rnm[i] = '\0'; break; + default: /* These occur when device isn't present */ - if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break; + if (p == (HIL_ERR_INT | HIL_PKT_CMD)) + break; /* Anything else we'd like to know about. */ printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p); break; @@ -139,16 +146,19 @@ static void hil_kbd_process_record(struct hil_kbd *kbd) switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) { case HIL_POL_CHARTYPE_NONE: break; + case HIL_POL_CHARTYPE_ASCII: while (cnt < idx - 1) input_report_key(dev, kbd->data[cnt++] & 0x7f, 1); break; + case HIL_POL_CHARTYPE_RSVD1: case HIL_POL_CHARTYPE_RSVD2: case HIL_POL_CHARTYPE_BINARY: while (cnt < idx - 1) input_report_key(dev, kbd->data[cnt++], 1); break; + case HIL_POL_CHARTYPE_SET1: while (cnt < idx - 1) { unsigned int key; @@ -161,6 +171,7 @@ static void hil_kbd_process_record(struct hil_kbd *kbd) input_report_key(dev, key, !up); } break; + case HIL_POL_CHARTYPE_SET2: while (cnt < idx - 1) { unsigned int key; @@ -173,6 +184,7 @@ static void hil_kbd_process_record(struct hil_kbd *kbd) input_report_key(dev, key, !up); } break; + case HIL_POL_CHARTYPE_SET3: while (cnt < idx - 1) { unsigned int key; @@ -191,42 +203,43 @@ static void hil_kbd_process_record(struct hil_kbd *kbd) up(&kbd->sem); } -static void hil_kbd_process_err(struct hil_kbd *kbd) { +static void hil_kbd_process_err(struct hil_kbd *kbd) +{ printk(KERN_WARNING PREFIX "errored HIL packet\n"); kbd->idx4 = 0; up(&kbd->sem); } -static irqreturn_t hil_kbd_interrupt(struct serio *serio, - unsigned char data, unsigned int flags) +static irqreturn_t hil_kbd_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) { struct hil_kbd *kbd; hil_packet packet; int idx; kbd = serio_get_drvdata(serio); - if (kbd == NULL) { - BUG(); - return IRQ_HANDLED; - } + BUG_ON(kbd == NULL); if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) { hil_kbd_process_err(kbd); return IRQ_HANDLED; } idx = kbd->idx4/4; - if (!(kbd->idx4 % 4)) kbd->data[idx] = 0; + if (!(kbd->idx4 % 4)) + kbd->data[idx] = 0; packet = kbd->data[idx]; packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8); kbd->data[idx] = packet; /* Records of N 4-byte hil_packets must terminate with a command. */ - if ((++(kbd->idx4)) % 4) return IRQ_HANDLED; + if ((++(kbd->idx4)) % 4) + return IRQ_HANDLED; if ((packet & 0xffff0000) != HIL_ERR_INT) { hil_kbd_process_err(kbd); return IRQ_HANDLED; } - if (packet & HIL_PKT_CMD) hil_kbd_process_record(kbd); + if (packet & HIL_PKT_CMD) + hil_kbd_process_record(kbd); return IRQ_HANDLED; } @@ -235,10 +248,7 @@ static void hil_kbd_disconnect(struct serio *serio) struct hil_kbd *kbd; kbd = serio_get_drvdata(serio); - if (kbd == NULL) { - BUG(); - return; - } + BUG_ON(kbd == NULL); serio_close(serio); input_unregister_device(kbd->dev); @@ -259,42 +269,40 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) if (!kbd->dev) goto bail0; - kbd->dev->private = kbd; - if (serio_open(serio, drv)) goto bail1; serio_set_drvdata(serio, kbd); kbd->serio = serio; - init_MUTEX_LOCKED(&(kbd->sem)); + init_MUTEX_LOCKED(&kbd->sem); /* Get device info. MLC driver supplies devid/status/etc. */ serio->write(serio, 0); serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_IDD); - down(&(kbd->sem)); + down(&kbd->sem); serio->write(serio, 0); serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_RSC); - down(&(kbd->sem)); + down(&kbd->sem); serio->write(serio, 0); serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_RNM); - down(&(kbd->sem)); + down(&kbd->sem); serio->write(serio, 0); serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_EXD); - down(&(kbd->sem)); + down(&kbd->sem); - up(&(kbd->sem)); + up(&kbd->sem); did = kbd->idd[0]; idd = kbd->idd + 1; @@ -310,12 +318,11 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) goto bail2; } - if(HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) { + if (HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) { printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n"); goto bail2; } - kbd->dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); kbd->dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); kbd->dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; @@ -328,7 +335,7 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) kbd->dev->id.vendor = PCI_VENDOR_ID_HP; kbd->dev->id.product = 0x0001; /* TODO: get from kbd->rsc */ kbd->dev->id.version = 0x0100; /* TODO: get from kbd->rsc */ - kbd->dev->cdev.dev = &serio->dev; + kbd->dev->dev.parent = &serio->dev; for (i = 0; i < 128; i++) { set_bit(hil_kbd_set1[i], kbd->dev->keybit); @@ -344,8 +351,8 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */ - down(&(kbd->sem)); - up(&(kbd->sem)); + down(&kbd->sem); + up(&kbd->sem); return 0; bail2: @@ -368,26 +375,26 @@ static struct serio_device_id hil_kbd_ids[] = { { 0 } }; -struct serio_driver hil_kbd_serio_drv = { +static struct serio_driver hil_kbd_serio_drv = { .driver = { .name = "hil_kbd", }, .description = "HP HIL keyboard driver", .id_table = hil_kbd_ids, - .connect = hil_kbd_connect, - .disconnect = hil_kbd_disconnect, - .interrupt = hil_kbd_interrupt + .connect = hil_kbd_connect, + .disconnect = hil_kbd_disconnect, + .interrupt = hil_kbd_interrupt }; static int __init hil_kbd_init(void) { return serio_register_driver(&hil_kbd_serio_drv); } - + static void __exit hil_kbd_exit(void) { serio_unregister_driver(&hil_kbd_serio_drv); } - + module_init(hil_kbd_init); module_exit(hil_kbd_exit); diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index 4de4dc297d5..499b6974457 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -3,7 +3,7 @@ * * Copyright (C) 1998 Philip Blundell <philb@gnu.org> * Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai> - * Copyright (C) 1999-2006 Helge Deller <deller@gmx.de> + * Copyright (C) 1999-2007 Helge Deller <deller@gmx.de> * * Very basic HP Human Interface Loop (HIL) driver. * This driver handles the keyboard on HP300 (m68k) and on some @@ -52,7 +52,7 @@ MODULE_LICENSE("GPL v2"); #elif defined(CONFIG_HP300) - #define HILBASE 0xf0428000 /* HP300 (m86k) port address */ + #define HILBASE 0xf0428000UL /* HP300 (m68k) port address */ #define HIL_DATA 0x1 #define HIL_CMD 0x3 #define HIL_IRQ 2 @@ -89,7 +89,7 @@ MODULE_LICENSE("GPL v2"); #define HIL_READKBDSADR 0xF9 #define HIL_WRITEKBDSADR 0xE9 -static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] = +static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly = { HIL_KEYCODES_SET1 }; /* HIL structure */ @@ -211,10 +211,10 @@ hil_keyb_init(void) return -ENODEV; /* already initialized */ } + spin_lock_init(&hil_dev.lock); hil_dev.dev = input_allocate_device(); if (!hil_dev.dev) return -ENOMEM; - hil_dev.dev->private = &hil_dev; #if defined(CONFIG_HP300) if (!hwreg_present((void *)(HILBASE + HIL_DATA))) { diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c index 3d4d0a0ede2..1b08f4e79dd 100644 --- a/drivers/input/keyboard/lkkbd.c +++ b/drivers/input/keyboard/lkkbd.c @@ -515,7 +515,7 @@ static int lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct lkkbd *lk = dev->private; + struct lkkbd *lk = input_get_drvdata (dev); unsigned char leds_on = 0; unsigned char leds_off = 0; @@ -666,9 +666,10 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_LKKBD; input_dev->id.product = 0; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; + input_dev->dev.parent = &serio->dev; input_dev->event = lkkbd_event; - input_dev->private = lk; + + input_set_drvdata (input_dev, lk); set_bit (EV_KEY, input_dev->evbit); set_bit (EV_LED, input_dev->evbit); diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c index 2ade5186cc4..7a41b271f22 100644 --- a/drivers/input/keyboard/locomokbd.c +++ b/drivers/input/keyboard/locomokbd.c @@ -231,7 +231,7 @@ static int locomokbd_probe(struct locomo_dev *dev) input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->private = locomokbd; + input_dev->dev.parent = &dev->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); input_dev->keycode = locomokbd->keycode; diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c index aa29b50765c..b97a41e3ee5 100644 --- a/drivers/input/keyboard/newtonkbd.c +++ b/drivers/input/keyboard/newtonkbd.c @@ -104,8 +104,7 @@ static int nkbd_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_NEWTON; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = nkbd; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); input_dev->keycode = nkbd->keycode; diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 5680a6d95b2..3a228634f10 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -370,8 +370,7 @@ static int __init omap_kp_probe(struct platform_device *pdev) set_bit(keymap[i] & KEY_MAX, input_dev->keybit); input_dev->name = "omap-keypad"; input_dev->phys = "omap-keypad/input0"; - input_dev->cdev.dev = &pdev->dev; - input_dev->private = omap_kp; + input_dev->dev.parent = &pdev->dev; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; diff --git a/drivers/input/keyboard/pxa27x_keyboard.c b/drivers/input/keyboard/pxa27x_keyboard.c new file mode 100644 index 00000000000..06eaf766d9d --- /dev/null +++ b/drivers/input/keyboard/pxa27x_keyboard.c @@ -0,0 +1,258 @@ +/* + * linux/drivers/input/keyboard/pxa27x_keyboard.c + * + * Driver for the pxa27x matrix keyboard controller. + * + * Created: Feb 22, 2007 + * Author: Rodolfo Giometti <giometti@linux.it> + * + * Based on a previous implementations by Kevin O'Connor + * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and + * on some suggestions by Nicolas Pitre <nico@cam.org>. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/device.h> +#include <linux/platform_device.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <asm/arch/hardware.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/irqs.h> +#include <asm/arch/pxa27x_keyboard.h> + +#define DRIVER_NAME "pxa27x-keyboard" + +#define KPASMKP(col) (col/2 == 0 ? KPASMKP0 : \ + col/2 == 1 ? KPASMKP1 : \ + col/2 == 2 ? KPASMKP2 : KPASMKP3) +#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2))) + +static irqreturn_t pxakbd_irq_handler(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input_dev = platform_get_drvdata(pdev); + unsigned long kpc = KPC; + int p, row, col, rel; + + if (kpc & KPC_DI) { + unsigned long kpdk = KPDK; + + if (!(kpdk & KPDK_DKP)) { + /* better luck next time */ + } else if (kpc & KPC_REE0) { + unsigned long kprec = KPREC; + KPREC = 0x7f; + + if (kprec & KPREC_OF0) + rel = (kprec & 0xff) + 0x7f; + else if (kprec & KPREC_UF0) + rel = (kprec & 0xff) - 0x7f - 0xff; + else + rel = (kprec & 0xff) - 0x7f; + + if (rel) { + input_report_rel(input_dev, REL_WHEEL, rel); + input_sync(input_dev); + } + } + } + + if (kpc & KPC_MI) { + /* report the status of every button */ + for (row = 0; row < pdata->nr_rows; row++) { + for (col = 0; col < pdata->nr_cols; col++) { + p = KPASMKP(col) & KPASMKPx_MKC(row, col) ? + 1 : 0; + pr_debug("keycode %x - pressed %x\n", + pdata->keycodes[row][col], p); + input_report_key(input_dev, + pdata->keycodes[row][col], p); + } + } + input_sync(input_dev); + } + + return IRQ_HANDLED; +} + +static int pxakbd_open(struct input_dev *dev) +{ + /* Set keypad control register */ + KPC |= (KPC_ASACT | + KPC_MS_ALL | + (2 << 6) | KPC_REE0 | KPC_DK_DEB_SEL | + KPC_ME | KPC_MIE | KPC_DE | KPC_DIE); + + KPC &= ~KPC_AS; /* disable automatic scan */ + KPC &= ~KPC_IMKP; /* do not ignore multiple keypresses */ + + /* Set rotary count to mid-point value */ + KPREC = 0x7F; + + /* Enable unit clock */ + pxa_set_cken(CKEN19_KEYPAD, 1); + + return 0; +} + +static void pxakbd_close(struct input_dev *dev) +{ + /* Disable clock unit */ + pxa_set_cken(CKEN19_KEYPAD, 0); +} + +#ifdef CONFIG_PM +static int pxakbd_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; + + /* Save controller status */ + pdata->reg_kpc = KPC; + pdata->reg_kprec = KPREC; + + return 0; +} + +static int pxakbd_resume(struct platform_device *pdev) +{ + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input_dev = platform_get_drvdata(pdev); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) { + /* Restore controller status */ + KPC = pdata->reg_kpc; + KPREC = pdata->reg_kprec; + + /* Enable unit clock */ + pxa_set_cken(CKEN19_KEYPAD, 1); + } + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#else +#define pxakbd_suspend NULL +#define pxakbd_resume NULL +#endif + +static int __devinit pxakbd_probe(struct platform_device *pdev) +{ + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input_dev; + int i, row, col, error; + + /* Create and register the input driver. */ + input_dev = input_allocate_device(); + if (!input_dev) { + printk(KERN_ERR "Cannot request keypad device\n"); + return -ENOMEM; + } + + input_dev->name = DRIVER_NAME; + input_dev->id.bustype = BUS_HOST; + input_dev->open = pxakbd_open; + input_dev->close = pxakbd_close; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL); + input_dev->relbit[LONG(REL_WHEEL)] = BIT(REL_WHEEL); + for (row = 0; row < pdata->nr_rows; row++) { + for (col = 0; col < pdata->nr_cols; col++) { + int code = pdata->keycodes[row][col]; + if (code > 0) + set_bit(code, input_dev->keybit); + } + } + + error = request_irq(IRQ_KEYPAD, pxakbd_irq_handler, IRQF_DISABLED, + DRIVER_NAME, pdev); + if (error) { + printk(KERN_ERR "Cannot request keypad IRQ\n"); + pxa_set_cken(CKEN19_KEYPAD, 0); + goto err_free_dev; + } + + platform_set_drvdata(pdev, input_dev); + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + /* Setup GPIOs. */ + for (i = 0; i < pdata->nr_rows + pdata->nr_cols; i++) + pxa_gpio_mode(pdata->gpio_modes[i]); + + /* + * Store rows/cols info into keyboard registers. + */ + + KPC |= (pdata->nr_rows - 1) << 26; + KPC |= (pdata->nr_cols - 1) << 23; + + for (col = 0; col < pdata->nr_cols; col++) + KPC |= KPC_MS0 << col; + + return 0; + + err_free_irq: + platform_set_drvdata(pdev, NULL); + free_irq(IRQ_KEYPAD, pdev); + err_free_dev: + input_free_device(input_dev); + return error; +} + +static int __devexit pxakbd_remove(struct platform_device *pdev) +{ + struct input_dev *input_dev = platform_get_drvdata(pdev); + + input_unregister_device(input_dev); + free_irq(IRQ_KEYPAD, pdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver pxakbd_driver = { + .probe = pxakbd_probe, + .remove = __devexit_p(pxakbd_remove), + .suspend = pxakbd_suspend, + .resume = pxakbd_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init pxakbd_init(void) +{ + return platform_driver_register(&pxakbd_driver); +} + +static void __exit pxakbd_exit(void) +{ + platform_driver_unregister(&pxakbd_driver); +} + +module_init(pxakbd_init); +module_exit(pxakbd_exit); + +MODULE_DESCRIPTION("PXA27x Matrix Keyboard Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c index 8a2166c77ff..41b80385476 100644 --- a/drivers/input/keyboard/spitzkbd.c +++ b/drivers/input/keyboard/spitzkbd.c @@ -372,10 +372,9 @@ static int __init spitzkbd_probe(struct platform_device *dev) spitzkbd->input = input_dev; - input_dev->private = spitzkbd; input_dev->name = "Spitz Keyboard"; input_dev->phys = spitzkbd->phys; - input_dev->cdev.dev = &dev->dev; + input_dev->dev.parent = &dev->dev; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c index f7b5c5b8145..b44b0684d54 100644 --- a/drivers/input/keyboard/stowaway.c +++ b/drivers/input/keyboard/stowaway.c @@ -108,8 +108,7 @@ static int skbd_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_STOWAWAY; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = skbd; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); input_dev->keycode = skbd->keycode; diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index cc023836641..1d4e39624cf 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -146,7 +146,7 @@ out: static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct sunkbd *sunkbd = dev->private; + struct sunkbd *sunkbd = input_get_drvdata(dev); switch (type) { @@ -271,8 +271,10 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_SUNKBD; input_dev->id.product = sunkbd->type; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = sunkbd; + input_dev->dev.parent = &serio->dev; + + input_set_drvdata(input_dev, sunkbd); + input_dev->event = sunkbd_event; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP); diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c index a8209343213..f3a56eb58ed 100644 --- a/drivers/input/keyboard/xtkbd.c +++ b/drivers/input/keyboard/xtkbd.c @@ -108,8 +108,7 @@ static int xtkbd_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = xtkbd; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); input_dev->keycode = xtkbd->keycode; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 41b42587f5e..6013ace94d9 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -40,6 +40,28 @@ config INPUT_M68K_BEEP tristate "M68k Beeper support" depends on M68K +config INPUT_IXP4XX_BEEPER + tristate "IXP4XX Beeper support" + depends on ARCH_IXP4XX + help + If you say yes here, you can connect a beeper to the + ixp4xx gpio pins. This is used by the LinkSys NSLU2. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called ixp4xx-beeper. + +config INPUT_COBALT_BTNS + tristate "Cobalt button interface" + depends on MIPS_COBALT + select INPUT_POLLDEV + help + Say Y here if you want to support MIPS Cobalt button interface. + + To compile this driver as a module, choose M here: the + module will be called cobalt_btns. + config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86 && !X86_64 @@ -60,17 +82,79 @@ config INPUT_ATLAS_BTNS To compile this driver as a module, choose M here: the module will be called atlas_btns. -config INPUT_IXP4XX_BEEPER - tristate "IXP4XX Beeper support" - depends on ARCH_IXP4XX +config INPUT_ATI_REMOTE + tristate "ATI / X10 USB RF remote control" + select USB help - If you say yes here, you can connect a beeper to the - ixp4xx gpio pins. This is used by the LinkSys NSLU2. + Say Y here if you want to use an ATI or X10 "Lola" USB remote control. + These are RF remotes with USB receivers. + The ATI remote comes with many of ATI's All-In-Wonder video cards. + The X10 "Lola" remote is available at: + <http://www.x10.com/products/lola_sg1.htm> + This driver provides mouse pointer, left and right mouse buttons, + and maps all the other remote buttons to keypress events. + + To compile this driver as a module, choose M here: the module will be + called ati_remote. + +config INPUT_ATI_REMOTE2 + tristate "ATI / Philips USB RF remote control" + select USB + help + Say Y here if you want to use an ATI or Philips USB RF remote control. + These are RF remotes with USB receivers. + ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards + and is also available as a separate product. + This driver provides mouse pointer, left and right mouse buttons, + and maps all the other remote buttons to keypress events. + + To compile this driver as a module, choose M here: the module will be + called ati_remote2. + +config INPUT_KEYSPAN_REMOTE + tristate "Keyspan DMR USB remote control (EXPERIMENTAL)" + depends on EXPERIMENTAL + select USB + help + Say Y here if you want to use a Keyspan DMR USB remote control. + Currently only the UIA-11 type of receiver has been tested. The tag + on the receiver that connects to the USB port should have a P/N that + will tell you what type of DMR you have. The UIA-10 type is not + supported at this time. This driver maps all buttons to keypress + events. - If unsure, say Y. + To compile this driver as a module, choose M here: the module will + be called keyspan_remote. + +config INPUT_POWERMATE + tristate "Griffin PowerMate and Contour Jog support" + select USB + help + Say Y here if you want to use Griffin PowerMate or Contour Jog devices. + These are aluminum dials which can measure clockwise and anticlockwise + rotation. The dial also acts as a pushbutton. The base contains an LED + which can be instructed to pulse or to switch to a particular intensity. + + You can download userspace tools from + <http://sowerbutts.com/powermate/>. To compile this driver as a module, choose M here: the - module will be called ixp4xx-beeper. + module will be called powermate. + +config INPUT_YEALINK + tristate "Yealink usb-p1k voip phone" + depends EXPERIMENTAL + select USB + help + Say Y here if you want to enable keyboard and LCD functions of the + Yealink usb-p1k usb phones. The audio part is enabled by the generic + usb sound driver, so you might want to enable that as well. + + For information about how to use these additional functions, see + <file:Documentation/input/yealink.txt>. + + To compile this driver as a module, choose M here: the module will be + called yealink. config INPUT_UINPUT tristate "User level driver support" @@ -81,8 +165,19 @@ config INPUT_UINPUT To compile this driver as a module, choose M here: the module will be called uinput. +config INPUT_POLLDEV + tristate "Polled input device skeleton" + help + Say Y here if you are using a driver for an input + device that periodically polls hardware state. This + option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. + + To compile this driver as a module, choose M here: the + module will be called input-polldev. + config HP_SDC_RTC - tristate "HP SDC Real Time Clock" + tristate "HP SDC Real Time Clock" depends on GSC || HP300 select HP_SDC help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index e0a8d58c9e9..8b2f7799e25 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -4,11 +4,18 @@ # Each configuration option enables a list of files. +obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o -obj-$(CONFIG_INPUT_UINPUT) += uinput.o +obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o +obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o +obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o +obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o +obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o +obj-$(CONFIG_INPUT_POWERMATE) += powermate.o +obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o -obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o +obj-$(CONFIG_INPUT_UINPUT) += uinput.o diff --git a/drivers/input/misc/ati_remote.c b/drivers/input/misc/ati_remote.c new file mode 100644 index 00000000000..471aab20644 --- /dev/null +++ b/drivers/input/misc/ati_remote.c @@ -0,0 +1,862 @@ +/* + * USB ATI Remote support + * + * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net> + * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev + * + * This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including + * porting to the 2.6 kernel interfaces, along with other modification + * to better match the style of the existing usb/input drivers. However, the + * protocol and hardware handling is essentially unchanged from 2.1.1. + * + * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by + * Vojtech Pavlik. + * + * Changes: + * + * Feb 2004: Torrey Hoffman <thoffman@arnor.net> + * Version 2.2.0 + * Jun 2004: Torrey Hoffman <thoffman@arnor.net> + * Version 2.2.1 + * Added key repeat support contributed by: + * Vincent Vanackere <vanackere@lif.univ-mrs.fr> + * Added support for the "Lola" remote contributed by: + * Seth Cohn <sethcohn@yahoo.com> + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * Hardware & software notes + * + * These remote controls are distributed by ATI as part of their + * "All-In-Wonder" video card packages. The receiver self-identifies as a + * "USB Receiver" with manufacturer "X10 Wireless Technology Inc". + * + * The "Lola" remote is available from X10. See: + * http://www.x10.com/products/lola_sg1.htm + * The Lola is similar to the ATI remote but has no mouse support, and slightly + * different keys. + * + * It is possible to use multiple receivers and remotes on multiple computers + * simultaneously by configuring them to use specific channels. + * + * The RF protocol used by the remote supports 16 distinct channels, 1 to 16. + * Actually, it may even support more, at least in some revisions of the + * hardware. + * + * Each remote can be configured to transmit on one channel as follows: + * - Press and hold the "hand icon" button. + * - When the red LED starts to blink, let go of the "hand icon" button. + * - When it stops blinking, input the channel code as two digits, from 01 + * to 16, and press the hand icon again. + * + * The timing can be a little tricky. Try loading the module with debug=1 + * to have the kernel print out messages about the remote control number + * and mask. Note: debugging prints remote numbers as zero-based hexadecimal. + * + * The driver has a "channel_mask" parameter. This bitmask specifies which + * channels will be ignored by the module. To mask out channels, just add + * all the 2^channel_number values together. + * + * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote + * ignore signals coming from remote controls transmitting on channel 4, but + * accept all other channels. + * + * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be + * ignored. + * + * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this + * parameter are unused. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/usb/input.h> +#include <linux/wait.h> +#include <linux/jiffies.h> + +/* + * Module and Version Information, Module Parameters + */ + +#define ATI_REMOTE_VENDOR_ID 0x0bc7 +#define ATI_REMOTE_PRODUCT_ID 0x004 +#define LOLA_REMOTE_PRODUCT_ID 0x002 +#define MEDION_REMOTE_PRODUCT_ID 0x006 + +#define DRIVER_VERSION "2.2.1" +#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>" +#define DRIVER_DESC "ATI/X10 RF USB Remote Control" + +#define NAME_BUFSIZE 80 /* size of product name, path buffers */ +#define DATA_BUFSIZE 63 /* size of URB data buffers */ + +/* + * Duplicate event filtering time. + * Sequential, identical KIND_FILTERED inputs with less than + * FILTER_TIME milliseconds between them are considered as repeat + * events. The hardware generates 5 events for the first keypress + * and we have to take this into account for an accurate repeat + * behaviour. + */ +#define FILTER_TIME 60 /* msec */ +#define REPEAT_DELAY 500 /* msec */ + +static unsigned long channel_mask; +module_param(channel_mask, ulong, 0644); +MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); + +static int repeat_filter = FILTER_TIME; +module_param(repeat_filter, int, 0644); +MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec"); + +static int repeat_delay = REPEAT_DELAY; +module_param(repeat_delay, int, 0644); +MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec"); + +#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) +#undef err +#define err(format, arg...) printk(KERN_ERR format , ## arg) + +static struct usb_device_id ati_remote_table[] = { + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ati_remote_table); + +/* Get hi and low bytes of a 16-bits int */ +#define HI(a) ((unsigned char)((a) >> 8)) +#define LO(a) ((unsigned char)((a) & 0xff)) + +#define SEND_FLAG_IN_PROGRESS 1 +#define SEND_FLAG_COMPLETE 2 + +/* Device initialization strings */ +static char init1[] = { 0x01, 0x00, 0x20, 0x14 }; +static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 }; + +struct ati_remote { + struct input_dev *idev; + struct usb_device *udev; + struct usb_interface *interface; + + struct urb *irq_urb; + struct urb *out_urb; + struct usb_endpoint_descriptor *endpoint_in; + struct usb_endpoint_descriptor *endpoint_out; + unsigned char *inbuf; + unsigned char *outbuf; + dma_addr_t inbuf_dma; + dma_addr_t outbuf_dma; + + unsigned char old_data[2]; /* Detect duplicate events */ + unsigned long old_jiffies; + unsigned long acc_jiffies; /* handle acceleration */ + unsigned long first_jiffies; + + unsigned int repeat_count; + + char name[NAME_BUFSIZE]; + char phys[NAME_BUFSIZE]; + + wait_queue_head_t wait; + int send_flags; +}; + +/* "Kinds" of messages sent from the hardware to the driver. */ +#define KIND_END 0 +#define KIND_LITERAL 1 /* Simply pass to input system */ +#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */ +#define KIND_LU 3 /* Directional keypad diagonals - left up, */ +#define KIND_RU 4 /* right up, */ +#define KIND_LD 5 /* left down, */ +#define KIND_RD 6 /* right down */ +#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/ + +/* Translation table from hardware messages to input events. */ +static const struct { + short kind; + unsigned char data1, data2; + int type; + unsigned int code; + int value; +} ati_remote_tbl[] = { + /* Directional control pad axes */ + {KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */ + {KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */ + {KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */ + {KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */ + /* Directional control pad diagonals */ + {KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */ + {KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */ + {KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */ + {KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */ + + /* "Mouse button" buttons */ + {KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */ + {KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */ + {KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */ + {KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */ + + /* Artificial "doubleclick" events are generated by the hardware. + * They are mapped to the "side" and "extra" mouse buttons here. */ + {KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */ + {KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */ + + /* keyboard. */ + {KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1}, + {KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1}, + {KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1}, + {KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1}, + {KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1}, + {KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1}, + {KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1}, + {KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1}, + {KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1}, + {KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1}, + {KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1}, + {KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1}, + {KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1}, + {KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1}, + {KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1}, + {KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1}, + + /* "special" keys */ + {KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */ + {KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */ + {KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */ + {KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */ + {KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */ + {KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */ + {KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */ + {KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */ + {KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */ + {KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */ + {KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */ + {KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */ + {KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */ + {KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */ + {KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */ + {KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */ + {KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */ + {KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */ + {KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */ + {KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */ + {KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */ + {KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */ + {KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */ + {KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */ + {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */ + {KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */ + {KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */ + {KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */ + {KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */ + {KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */ + {KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */ + {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */ + + {KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0} +}; + +/* Local function prototypes */ +static void ati_remote_dump (unsigned char *data, unsigned int actual_length); +static int ati_remote_open (struct input_dev *inputdev); +static void ati_remote_close (struct input_dev *inputdev); +static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data); +static void ati_remote_irq_out (struct urb *urb); +static void ati_remote_irq_in (struct urb *urb); +static void ati_remote_input_report (struct urb *urb); +static int ati_remote_initialize (struct ati_remote *ati_remote); +static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id); +static void ati_remote_disconnect (struct usb_interface *interface); + +/* usb specific object to register with the usb subsystem */ +static struct usb_driver ati_remote_driver = { + .name = "ati_remote", + .probe = ati_remote_probe, + .disconnect = ati_remote_disconnect, + .id_table = ati_remote_table, +}; + +/* + * ati_remote_dump_input + */ +static void ati_remote_dump(unsigned char *data, unsigned int len) +{ + if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00)) + warn("Weird byte 0x%02x", data[0]); + else if (len == 4) + warn("Weird key %02x %02x %02x %02x", + data[0], data[1], data[2], data[3]); + else + warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...", + len, data[0], data[1], data[2], data[3], data[4], data[5]); +} + +/* + * ati_remote_open + */ +static int ati_remote_open(struct input_dev *inputdev) +{ + struct ati_remote *ati_remote = input_get_drvdata(inputdev); + + /* On first open, submit the read urb which was set up previously. */ + ati_remote->irq_urb->dev = ati_remote->udev; + if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) { + dev_err(&ati_remote->interface->dev, + "%s: usb_submit_urb failed!\n", __FUNCTION__); + return -EIO; + } + + return 0; +} + +/* + * ati_remote_close + */ +static void ati_remote_close(struct input_dev *inputdev) +{ + struct ati_remote *ati_remote = input_get_drvdata(inputdev); + + usb_kill_urb(ati_remote->irq_urb); +} + +/* + * ati_remote_irq_out + */ +static void ati_remote_irq_out(struct urb *urb) +{ + struct ati_remote *ati_remote = urb->context; + + if (urb->status) { + dev_dbg(&ati_remote->interface->dev, "%s: status %d\n", + __FUNCTION__, urb->status); + return; + } + + ati_remote->send_flags |= SEND_FLAG_COMPLETE; + wmb(); + wake_up(&ati_remote->wait); +} + +/* + * ati_remote_sendpacket + * + * Used to send device initialization strings + */ +static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data) +{ + int retval = 0; + + /* Set up out_urb */ + memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd)); + ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd); + + ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1; + ati_remote->out_urb->dev = ati_remote->udev; + ati_remote->send_flags = SEND_FLAG_IN_PROGRESS; + + retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC); + if (retval) { + dev_dbg(&ati_remote->interface->dev, + "sendpacket: usb_submit_urb failed: %d\n", retval); + return retval; + } + + wait_event_timeout(ati_remote->wait, + ((ati_remote->out_urb->status != -EINPROGRESS) || + (ati_remote->send_flags & SEND_FLAG_COMPLETE)), + HZ); + usb_kill_urb(ati_remote->out_urb); + + return retval; +} + +/* + * ati_remote_event_lookup + */ +static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2) +{ + int i; + + for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { + /* + * Decide if the table entry matches the remote input. + */ + if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) && + ((((ati_remote_tbl[i].data1 >> 4) - + (d1 >> 4) + rem) & 0x0f) == 0x0f) && + (ati_remote_tbl[i].data2 == d2)) + return i; + + } + return -1; +} + +/* + * ati_remote_compute_accel + * + * Implements acceleration curve for directional control pad + * If elapsed time since last event is > 1/4 second, user "stopped", + * so reset acceleration. Otherwise, user is probably holding the control + * pad down, so we increase acceleration, ramping up over two seconds to + * a maximum speed. + */ +static int ati_remote_compute_accel(struct ati_remote *ati_remote) +{ + static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; + unsigned long now = jiffies; + int acc; + + if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) { + acc = 1; + ati_remote->acc_jiffies = now; + } + else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125))) + acc = accel[0]; + else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250))) + acc = accel[1]; + else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500))) + acc = accel[2]; + else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000))) + acc = accel[3]; + else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500))) + acc = accel[4]; + else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000))) + acc = accel[5]; + else + acc = accel[6]; + + return acc; +} + +/* + * ati_remote_report_input + */ +static void ati_remote_input_report(struct urb *urb) +{ + struct ati_remote *ati_remote = urb->context; + unsigned char *data= ati_remote->inbuf; + struct input_dev *dev = ati_remote->idev; + int index, acc; + int remote_num; + + /* Deal with strange looking inputs */ + if ( (urb->actual_length != 4) || (data[0] != 0x14) || + ((data[3] & 0x0f) != 0x00) ) { + ati_remote_dump(data, urb->actual_length); + return; + } + + /* Mask unwanted remote channels. */ + /* note: remote_num is 0-based, channel 1 on remote == 0 here */ + remote_num = (data[3] >> 4) & 0x0f; + if (channel_mask & (1 << (remote_num + 1))) { + dbginfo(&ati_remote->interface->dev, + "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n", + remote_num, data[1], data[2], channel_mask); + return; + } + + /* Look up event code index in translation table */ + index = ati_remote_event_lookup(remote_num, data[1], data[2]); + if (index < 0) { + dev_warn(&ati_remote->interface->dev, + "Unknown input from channel 0x%02x: data %02x,%02x\n", + remote_num, data[1], data[2]); + return; + } + dbginfo(&ati_remote->interface->dev, + "channel 0x%02x; data %02x,%02x; index %d; keycode %d\n", + remote_num, data[1], data[2], index, ati_remote_tbl[index].code); + + if (ati_remote_tbl[index].kind == KIND_LITERAL) { + input_event(dev, ati_remote_tbl[index].type, + ati_remote_tbl[index].code, + ati_remote_tbl[index].value); + input_sync(dev); + + ati_remote->old_jiffies = jiffies; + return; + } + + if (ati_remote_tbl[index].kind == KIND_FILTERED) { + unsigned long now = jiffies; + + /* Filter duplicate events which happen "too close" together. */ + if (ati_remote->old_data[0] == data[1] && + ati_remote->old_data[1] == data[2] && + time_before(now, ati_remote->old_jiffies + + msecs_to_jiffies(repeat_filter))) { + ati_remote->repeat_count++; + } else { + ati_remote->repeat_count = 0; + ati_remote->first_jiffies = now; + } + + ati_remote->old_data[0] = data[1]; + ati_remote->old_data[1] = data[2]; + ati_remote->old_jiffies = now; + + /* Ensure we skip at least the 4 first duplicate events (generated + * by a single keypress), and continue skipping until repeat_delay + * msecs have passed + */ + if (ati_remote->repeat_count > 0 && + (ati_remote->repeat_count < 5 || + time_before(now, ati_remote->first_jiffies + + msecs_to_jiffies(repeat_delay)))) + return; + + + input_event(dev, ati_remote_tbl[index].type, + ati_remote_tbl[index].code, 1); + input_sync(dev); + input_event(dev, ati_remote_tbl[index].type, + ati_remote_tbl[index].code, 0); + input_sync(dev); + + } else { + + /* + * Other event kinds are from the directional control pad, and have an + * acceleration factor applied to them. Without this acceleration, the + * control pad is mostly unusable. + */ + acc = ati_remote_compute_accel(ati_remote); + + switch (ati_remote_tbl[index].kind) { + case KIND_ACCEL: + input_event(dev, ati_remote_tbl[index].type, + ati_remote_tbl[index].code, + ati_remote_tbl[index].value * acc); + break; + case KIND_LU: + input_report_rel(dev, REL_X, -acc); + input_report_rel(dev, REL_Y, -acc); + break; + case KIND_RU: + input_report_rel(dev, REL_X, acc); + input_report_rel(dev, REL_Y, -acc); + break; + case KIND_LD: + input_report_rel(dev, REL_X, -acc); + input_report_rel(dev, REL_Y, acc); + break; + case KIND_RD: + input_report_rel(dev, REL_X, acc); + input_report_rel(dev, REL_Y, acc); + break; + default: + dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", + ati_remote_tbl[index].kind); + } + input_sync(dev); + + ati_remote->old_jiffies = jiffies; + ati_remote->old_data[0] = data[1]; + ati_remote->old_data[1] = data[2]; + } +} + +/* + * ati_remote_irq_in + */ +static void ati_remote_irq_in(struct urb *urb) +{ + struct ati_remote *ati_remote = urb->context; + int retval; + + switch (urb->status) { + case 0: /* success */ + ati_remote_input_report(urb); + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n", + __FUNCTION__); + return; + default: /* error */ + dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n", + __FUNCTION__, urb->status); + } + + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n", + __FUNCTION__, retval); +} + +/* + * ati_remote_alloc_buffers + */ +static int ati_remote_alloc_buffers(struct usb_device *udev, + struct ati_remote *ati_remote) +{ + ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC, + &ati_remote->inbuf_dma); + if (!ati_remote->inbuf) + return -1; + + ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC, + &ati_remote->outbuf_dma); + if (!ati_remote->outbuf) + return -1; + + ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ati_remote->irq_urb) + return -1; + + ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ati_remote->out_urb) + return -1; + + return 0; +} + +/* + * ati_remote_free_buffers + */ +static void ati_remote_free_buffers(struct ati_remote *ati_remote) +{ + usb_free_urb(ati_remote->irq_urb); + usb_free_urb(ati_remote->out_urb); + + usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, + ati_remote->inbuf, ati_remote->inbuf_dma); + + usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, + ati_remote->outbuf, ati_remote->outbuf_dma); +} + +static void ati_remote_input_init(struct ati_remote *ati_remote) +{ + struct input_dev *idev = ati_remote->idev; + int i; + + idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) | + BIT(BTN_SIDE) | BIT(BTN_EXTRA) ); + idev->relbit[0] = BIT(REL_X) | BIT(REL_Y); + for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) + if (ati_remote_tbl[i].type == EV_KEY) + set_bit(ati_remote_tbl[i].code, idev->keybit); + + input_set_drvdata(idev, ati_remote); + + idev->open = ati_remote_open; + idev->close = ati_remote_close; + + idev->name = ati_remote->name; + idev->phys = ati_remote->phys; + + usb_to_input_id(ati_remote->udev, &idev->id); + idev->dev.parent = &ati_remote->udev->dev; +} + +static int ati_remote_initialize(struct ati_remote *ati_remote) +{ + struct usb_device *udev = ati_remote->udev; + int pipe, maxp; + + init_waitqueue_head(&ati_remote->wait); + + /* Set up irq_urb */ + pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; + + usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, + maxp, ati_remote_irq_in, ati_remote, + ati_remote->endpoint_in->bInterval); + ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma; + ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* Set up out_urb */ + pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; + + usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, + maxp, ati_remote_irq_out, ati_remote, + ati_remote->endpoint_out->bInterval); + ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma; + ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* send initialization strings */ + if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) || + (ati_remote_sendpacket(ati_remote, 0x8007, init2))) { + dev_err(&ati_remote->interface->dev, + "Initializing ati_remote hardware failed.\n"); + return -EIO; + } + + return 0; +} + +/* + * ati_remote_probe + */ +static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_host = interface->cur_altsetting; + struct usb_endpoint_descriptor *endpoint_in, *endpoint_out; + struct ati_remote *ati_remote; + struct input_dev *input_dev; + int err = -ENOMEM; + + if (iface_host->desc.bNumEndpoints != 2) { + err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__); + return -ENODEV; + } + + endpoint_in = &iface_host->endpoint[0].desc; + endpoint_out = &iface_host->endpoint[1].desc; + + if (!usb_endpoint_is_int_in(endpoint_in)) { + err("%s: Unexpected endpoint_in\n", __FUNCTION__); + return -ENODEV; + } + if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) { + err("%s: endpoint_in message size==0? \n", __FUNCTION__); + return -ENODEV; + } + + ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ati_remote || !input_dev) + goto fail1; + + /* Allocate URB buffers, URBs */ + if (ati_remote_alloc_buffers(udev, ati_remote)) + goto fail2; + + ati_remote->endpoint_in = endpoint_in; + ati_remote->endpoint_out = endpoint_out; + ati_remote->udev = udev; + ati_remote->idev = input_dev; + ati_remote->interface = interface; + + usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys)); + strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys)); + + if (udev->manufacturer) + strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name)); + + if (udev->product) + snprintf(ati_remote->name, sizeof(ati_remote->name), + "%s %s", ati_remote->name, udev->product); + + if (!strlen(ati_remote->name)) + snprintf(ati_remote->name, sizeof(ati_remote->name), + DRIVER_DESC "(%04x,%04x)", + le16_to_cpu(ati_remote->udev->descriptor.idVendor), + le16_to_cpu(ati_remote->udev->descriptor.idProduct)); + + ati_remote_input_init(ati_remote); + + /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ + err = ati_remote_initialize(ati_remote); + if (err) + goto fail3; + + /* Set up and register input device */ + err = input_register_device(ati_remote->idev); + if (err) + goto fail3; + + usb_set_intfdata(interface, ati_remote); + return 0; + + fail3: usb_kill_urb(ati_remote->irq_urb); + usb_kill_urb(ati_remote->out_urb); + fail2: ati_remote_free_buffers(ati_remote); + fail1: input_free_device(input_dev); + kfree(ati_remote); + return err; +} + +/* + * ati_remote_disconnect + */ +static void ati_remote_disconnect(struct usb_interface *interface) +{ + struct ati_remote *ati_remote; + + ati_remote = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + if (!ati_remote) { + warn("%s - null device?\n", __FUNCTION__); + return; + } + + usb_kill_urb(ati_remote->irq_urb); + usb_kill_urb(ati_remote->out_urb); + input_unregister_device(ati_remote->idev); + ati_remote_free_buffers(ati_remote); + kfree(ati_remote); +} + +/* + * ati_remote_init + */ +static int __init ati_remote_init(void) +{ + int result; + + result = usb_register(&ati_remote_driver); + if (result) + err("usb_register error #%d\n", result); + else + info("Registered USB driver " DRIVER_DESC " v. " DRIVER_VERSION); + + return result; +} + +/* + * ati_remote_exit + */ +static void __exit ati_remote_exit(void) +{ + usb_deregister(&ati_remote_driver); +} + +/* + * module specification + */ + +module_init(ati_remote_init); +module_exit(ati_remote_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c new file mode 100644 index 00000000000..1031543e5c3 --- /dev/null +++ b/drivers/input/misc/ati_remote2.c @@ -0,0 +1,543 @@ +/* + * ati_remote2 - ATI/Philips USB RF remote driver + * + * Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi> + * Copyright (C) 2007 Peter Stokes <linux@dadeos.freeserve.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#include <linux/usb/input.h> + +#define DRIVER_DESC "ATI/Philips USB RF remote driver" +#define DRIVER_VERSION "0.2" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>"); +MODULE_LICENSE("GPL"); + +/* + * ATI Remote Wonder II Channel Configuration + * + * The remote control can by assigned one of sixteen "channels" in order to facilitate + * the use of multiple remote controls within range of each other. + * A remote's "channel" may be altered by pressing and holding the "PC" button for + * approximately 3 seconds, after which the button will slowly flash the count of the + * currently configured "channel", using the numeric keypad enter a number between 1 and + * 16 and then the "PC" button again, the button will slowly flash the count of the + * newly configured "channel". + */ + +static unsigned int channel_mask = 0xFFFF; +module_param(channel_mask, uint, 0644); +MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>"); + +static unsigned int mode_mask = 0x1F; +module_param(mode_mask, uint, 0644); +MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); + +static struct usb_device_id ati_remote2_id_table[] = { + { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */ + { } +}; +MODULE_DEVICE_TABLE(usb, ati_remote2_id_table); + +static struct { + int hw_code; + int key_code; +} ati_remote2_key_table[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0c, KEY_POWER }, + { 0x0d, KEY_MUTE }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x20, KEY_CHANNELUP }, + { 0x21, KEY_CHANNELDOWN }, + { 0x28, KEY_FORWARD }, + { 0x29, KEY_REWIND }, + { 0x2c, KEY_PLAY }, + { 0x30, KEY_PAUSE }, + { 0x31, KEY_STOP }, + { 0x37, KEY_RECORD }, + { 0x38, KEY_DVD }, + { 0x39, KEY_TV }, + { 0x54, KEY_MENU }, + { 0x58, KEY_UP }, + { 0x59, KEY_DOWN }, + { 0x5a, KEY_LEFT }, + { 0x5b, KEY_RIGHT }, + { 0x5c, KEY_OK }, + { 0x78, KEY_A }, + { 0x79, KEY_B }, + { 0x7a, KEY_C }, + { 0x7b, KEY_D }, + { 0x7c, KEY_E }, + { 0x7d, KEY_F }, + { 0x82, KEY_ENTER }, + { 0x8e, KEY_VENDOR }, + { 0x96, KEY_COFFEE }, + { 0xa9, BTN_LEFT }, + { 0xaa, BTN_RIGHT }, + { 0xbe, KEY_QUESTION }, + { 0xd5, KEY_FRONT }, + { 0xd0, KEY_EDIT }, + { 0xf9, KEY_INFO }, + { (0x00 << 8) | 0x3f, KEY_PROG1 }, + { (0x01 << 8) | 0x3f, KEY_PROG2 }, + { (0x02 << 8) | 0x3f, KEY_PROG3 }, + { (0x03 << 8) | 0x3f, KEY_PROG4 }, + { (0x04 << 8) | 0x3f, KEY_PC }, + { 0, KEY_RESERVED } +}; + +struct ati_remote2 { + struct input_dev *idev; + struct usb_device *udev; + + struct usb_interface *intf[2]; + struct usb_endpoint_descriptor *ep[2]; + struct urb *urb[2]; + void *buf[2]; + dma_addr_t buf_dma[2]; + + unsigned long jiffies; + int mode; + + char name[64]; + char phys[64]; +}; + +static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id); +static void ati_remote2_disconnect(struct usb_interface *interface); + +static struct usb_driver ati_remote2_driver = { + .name = "ati_remote2", + .probe = ati_remote2_probe, + .disconnect = ati_remote2_disconnect, + .id_table = ati_remote2_id_table, +}; + +static int ati_remote2_open(struct input_dev *idev) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + int r; + + r = usb_submit_urb(ar2->urb[0], GFP_KERNEL); + if (r) { + dev_err(&ar2->intf[0]->dev, + "%s: usb_submit_urb() = %d\n", __FUNCTION__, r); + return r; + } + r = usb_submit_urb(ar2->urb[1], GFP_KERNEL); + if (r) { + usb_kill_urb(ar2->urb[0]); + dev_err(&ar2->intf[1]->dev, + "%s: usb_submit_urb() = %d\n", __FUNCTION__, r); + return r; + } + + return 0; +} + +static void ati_remote2_close(struct input_dev *idev) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + + usb_kill_urb(ar2->urb[0]); + usb_kill_urb(ar2->urb[1]); +} + +static void ati_remote2_input_mouse(struct ati_remote2 *ar2) +{ + struct input_dev *idev = ar2->idev; + u8 *data = ar2->buf[0]; + int channel, mode; + + channel = data[0] >> 4; + + if (!((1 << channel) & channel_mask)) + return; + + mode = data[0] & 0x0F; + + if (mode > 4) { + dev_err(&ar2->intf[0]->dev, + "Unknown mode byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + if (!((1 << mode) & mode_mask)) + return; + + input_event(idev, EV_REL, REL_X, (s8) data[1]); + input_event(idev, EV_REL, REL_Y, (s8) data[2]); + input_sync(idev); +} + +static int ati_remote2_lookup(unsigned int hw_code) +{ + int i; + + for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) + if (ati_remote2_key_table[i].hw_code == hw_code) + return i; + + return -1; +} + +static void ati_remote2_input_key(struct ati_remote2 *ar2) +{ + struct input_dev *idev = ar2->idev; + u8 *data = ar2->buf[1]; + int channel, mode, hw_code, index; + + channel = data[0] >> 4; + + if (!((1 << channel) & channel_mask)) + return; + + mode = data[0] & 0x0F; + + if (mode > 4) { + dev_err(&ar2->intf[1]->dev, + "Unknown mode byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + hw_code = data[2]; + /* + * Mode keys (AUX1-AUX4, PC) all generate the same code byte. + * Use the mode byte to figure out which one was pressed. + */ + if (hw_code == 0x3f) { + /* + * For some incomprehensible reason the mouse pad generates + * events which look identical to the events from the last + * pressed mode key. Naturally we don't want to generate key + * events for the mouse pad so we filter out any subsequent + * events from the same mode key. + */ + if (ar2->mode == mode) + return; + + if (data[1] == 0) + ar2->mode = mode; + + hw_code |= mode << 8; + } + + if (!((1 << mode) & mode_mask)) + return; + + index = ati_remote2_lookup(hw_code); + if (index < 0) { + dev_err(&ar2->intf[1]->dev, + "Unknown code byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + switch (data[1]) { + case 0: /* release */ + break; + case 1: /* press */ + ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]); + break; + case 2: /* repeat */ + + /* No repeat for mouse buttons. */ + if (ati_remote2_key_table[index].key_code == BTN_LEFT || + ati_remote2_key_table[index].key_code == BTN_RIGHT) + return; + + if (!time_after_eq(jiffies, ar2->jiffies)) + return; + + ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]); + break; + default: + dev_err(&ar2->intf[1]->dev, + "Unknown state byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]); + input_sync(idev); +} + +static void ati_remote2_complete_mouse(struct urb *urb) +{ + struct ati_remote2 *ar2 = urb->context; + int r; + + switch (urb->status) { + case 0: + ati_remote2_input_mouse(ar2); + break; + case -ENOENT: + case -EILSEQ: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&ar2->intf[0]->dev, + "%s(): urb status = %d\n", __FUNCTION__, urb->status); + return; + default: + dev_err(&ar2->intf[0]->dev, + "%s(): urb status = %d\n", __FUNCTION__, urb->status); + } + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + dev_err(&ar2->intf[0]->dev, + "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); +} + +static void ati_remote2_complete_key(struct urb *urb) +{ + struct ati_remote2 *ar2 = urb->context; + int r; + + switch (urb->status) { + case 0: + ati_remote2_input_key(ar2); + break; + case -ENOENT: + case -EILSEQ: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&ar2->intf[1]->dev, + "%s(): urb status = %d\n", __FUNCTION__, urb->status); + return; + default: + dev_err(&ar2->intf[1]->dev, + "%s(): urb status = %d\n", __FUNCTION__, urb->status); + } + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + dev_err(&ar2->intf[1]->dev, + "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); +} + +static int ati_remote2_input_init(struct ati_remote2 *ar2) +{ + struct input_dev *idev; + int i, retval; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + ar2->idev = idev; + input_set_drvdata(idev, ar2); + + idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL); + idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); + idev->relbit[0] = BIT(REL_X) | BIT(REL_Y); + for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) + set_bit(ati_remote2_key_table[i].key_code, idev->keybit); + + idev->rep[REP_DELAY] = 250; + idev->rep[REP_PERIOD] = 33; + + idev->open = ati_remote2_open; + idev->close = ati_remote2_close; + + idev->name = ar2->name; + idev->phys = ar2->phys; + + usb_to_input_id(ar2->udev, &idev->id); + idev->dev.parent = &ar2->udev->dev; + + retval = input_register_device(idev); + if (retval) + input_free_device(idev); + + return retval; +} + +static int ati_remote2_urb_init(struct ati_remote2 *ar2) +{ + struct usb_device *udev = ar2->udev; + int i, pipe, maxp; + + for (i = 0; i < 2; i++) { + ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]); + if (!ar2->buf[i]) + return -ENOMEM; + + ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ar2->urb[i]) + return -ENOMEM; + + pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = maxp > 4 ? 4 : maxp; + + usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp, + i ? ati_remote2_complete_key : ati_remote2_complete_mouse, + ar2, ar2->ep[i]->bInterval); + ar2->urb[i]->transfer_dma = ar2->buf_dma[i]; + ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + + return 0; +} + +static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2) +{ + int i; + + for (i = 0; i < 2; i++) { + usb_free_urb(ar2->urb[i]); + usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]); + } +} + +static int ati_remote2_setup(struct ati_remote2 *ar2) +{ + int r, i, channel; + + /* + * Configure receiver to only accept input from remote "channel" + * channel == 0 -> Accept input from any remote channel + * channel == 1 -> Only accept input from remote channel 1 + * channel == 2 -> Only accept input from remote channel 2 + * ... + * channel == 16 -> Only accept input from remote channel 16 + */ + + channel = 0; + for (i = 0; i < 16; i++) { + if ((1 << i) & channel_mask) { + if (!(~(1 << i) & 0xFFFF & channel_mask)) + channel = i + 1; + break; + } + } + + r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0), + 0x20, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (r) { + dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n", + __FUNCTION__, r); + return r; + } + + return 0; +} + +static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *alt = interface->cur_altsetting; + struct ati_remote2 *ar2; + int r; + + if (alt->desc.bInterfaceNumber) + return -ENODEV; + + ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL); + if (!ar2) + return -ENOMEM; + + ar2->udev = udev; + + ar2->intf[0] = interface; + ar2->ep[0] = &alt->endpoint[0].desc; + + ar2->intf[1] = usb_ifnum_to_if(udev, 1); + r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2); + if (r) + goto fail1; + alt = ar2->intf[1]->cur_altsetting; + ar2->ep[1] = &alt->endpoint[0].desc; + + r = ati_remote2_urb_init(ar2); + if (r) + goto fail2; + + r = ati_remote2_setup(ar2); + if (r) + goto fail2; + + usb_make_path(udev, ar2->phys, sizeof(ar2->phys)); + strlcat(ar2->phys, "/input0", sizeof(ar2->phys)); + + strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name)); + + r = ati_remote2_input_init(ar2); + if (r) + goto fail2; + + usb_set_intfdata(interface, ar2); + + return 0; + + fail2: + ati_remote2_urb_cleanup(ar2); + + usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); + fail1: + kfree(ar2); + + return r; +} + +static void ati_remote2_disconnect(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + + if (alt->desc.bInterfaceNumber) + return; + + ar2 = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + input_unregister_device(ar2->idev); + + ati_remote2_urb_cleanup(ar2); + + usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); + + kfree(ar2); +} + +static int __init ati_remote2_init(void) +{ + int r; + + r = usb_register(&ati_remote2_driver); + if (r) + printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r); + else + printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n"); + + return r; +} + +static void __exit ati_remote2_exit(void) +{ + usb_deregister(&ati_remote2_driver); +} + +module_init(ati_remote2_init); +module_exit(ati_remote2_exit); diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c new file mode 100644 index 00000000000..064b0793601 --- /dev/null +++ b/drivers/input/misc/cobalt_btns.c @@ -0,0 +1,172 @@ +/* + * Cobalt button interface driver. + * + * Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/init.h> +#include <linux/input-polldev.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#define BUTTONS_POLL_INTERVAL 30 /* msec */ +#define BUTTONS_COUNT_THRESHOLD 3 +#define BUTTONS_STATUS_MASK 0xfe000000 + +struct buttons_dev { + struct input_polled_dev *poll_dev; + void __iomem *reg; +}; + +struct buttons_map { + uint32_t mask; + int keycode; + int count; +}; + +static struct buttons_map buttons_map[] = { + { 0x02000000, KEY_RESTART, }, + { 0x04000000, KEY_LEFT, }, + { 0x08000000, KEY_UP, }, + { 0x10000000, KEY_DOWN, }, + { 0x20000000, KEY_RIGHT, }, + { 0x40000000, KEY_ENTER, }, + { 0x80000000, KEY_SELECT, }, +}; + +static void handle_buttons(struct input_polled_dev *dev) +{ + struct buttons_map *button = buttons_map; + struct buttons_dev *bdev = dev->private; + struct input_dev *input = dev->input; + uint32_t status; + int i; + + status = readl(bdev->reg); + status = ~status & BUTTONS_STATUS_MASK; + + for (i = 0; i < ARRAY_SIZE(buttons_map); i++) { + if (status & button->mask) { + button->count++; + } else { + if (button->count >= BUTTONS_COUNT_THRESHOLD) { + input_report_key(input, button->keycode, 0); + input_sync(input); + } + button->count = 0; + } + + if (button->count == BUTTONS_COUNT_THRESHOLD) { + input_report_key(input, button->keycode, 1); + input_sync(input); + } + + button++; + } +} + +static int __devinit cobalt_buttons_probe(struct platform_device *pdev) +{ + struct buttons_dev *bdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + struct resource *res; + int error, i; + + bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL); + poll_dev = input_allocate_polled_device(); + if (!bdev || !poll_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + poll_dev->private = bdev; + poll_dev->poll = handle_buttons; + poll_dev->poll_interval = BUTTONS_POLL_INTERVAL; + + input = poll_dev->input; + input->name = "Cobalt buttons"; + input->phys = "cobalt/input0"; + input->id.bustype = BUS_HOST; + input->cdev.dev = &pdev->dev; + + input->evbit[0] = BIT(EV_KEY); + for (i = 0; i < ARRAY_SIZE(buttons_map); i++) { + set_bit(buttons_map[i].keycode, input->keybit); + buttons_map[i].count = 0; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + error = -EBUSY; + goto err_free_mem; + } + + bdev->poll_dev = poll_dev; + bdev->reg = ioremap(res->start, res->end - res->start + 1); + dev_set_drvdata(&pdev->dev, bdev); + + error = input_register_polled_device(poll_dev); + if (error) + goto err_iounmap; + + return 0; + + err_iounmap: + iounmap(bdev->reg); + err_free_mem: + input_free_polled_device(poll_dev); + kfree(bdev); + dev_set_drvdata(&pdev->dev, NULL); + return error; +} + +static int __devexit cobalt_buttons_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct buttons_dev *bdev = dev_get_drvdata(dev); + + input_unregister_polled_device(bdev->poll_dev); + input_free_polled_device(bdev->poll_dev); + iounmap(bdev->reg); + kfree(bdev); + dev_set_drvdata(dev, NULL); + + return 0; +} + +static struct platform_driver cobalt_buttons_driver = { + .probe = cobalt_buttons_probe, + .remove = __devexit_p(cobalt_buttons_remove), + .driver = { + .name = "Cobalt buttons", + .owner = THIS_MODULE, + }, +}; + +static int __init cobalt_buttons_init(void) +{ + return platform_driver_register(&cobalt_buttons_driver); +} + +static void __exit cobalt_buttons_exit(void) +{ + platform_driver_unregister(&cobalt_buttons_driver); +} + +module_init(cobalt_buttons_init); +module_exit(cobalt_buttons_exit); diff --git a/drivers/input/misc/input-polldev.c b/drivers/input/misc/input-polldev.c new file mode 100644 index 00000000000..1b2b9c9c5d8 --- /dev/null +++ b/drivers/input/misc/input-polldev.c @@ -0,0 +1,171 @@ +/* + * Generic implementation of a polled input device + + * Copyright (c) 2007 Dmitry Torokhov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/input-polldev.h> + +static DEFINE_MUTEX(polldev_mutex); +static int polldev_users; +static struct workqueue_struct *polldev_wq; + +static int input_polldev_start_workqueue(void) +{ + int retval; + + retval = mutex_lock_interruptible(&polldev_mutex); + if (retval) + return retval; + + if (!polldev_users) { + polldev_wq = create_singlethread_workqueue("ipolldevd"); + if (!polldev_wq) { + printk(KERN_ERR "input-polldev: failed to create " + "ipolldevd workqueue\n"); + retval = -ENOMEM; + goto out; + } + } + + polldev_users++; + + out: + mutex_unlock(&polldev_mutex); + return retval; +} + +static void input_polldev_stop_workqueue(void) +{ + mutex_lock(&polldev_mutex); + + if (!--polldev_users) + destroy_workqueue(polldev_wq); + + mutex_unlock(&polldev_mutex); +} + +static void input_polled_device_work(struct work_struct *work) +{ + struct input_polled_dev *dev = + container_of(work, struct input_polled_dev, work.work); + + dev->poll(dev); + queue_delayed_work(polldev_wq, &dev->work, + msecs_to_jiffies(dev->poll_interval)); +} + +static int input_open_polled_device(struct input_dev *input) +{ + struct input_polled_dev *dev = input->private; + int error; + + error = input_polldev_start_workqueue(); + if (error) + return error; + + if (dev->flush) + dev->flush(dev); + + queue_delayed_work(polldev_wq, &dev->work, + msecs_to_jiffies(dev->poll_interval)); + + return 0; +} + +static void input_close_polled_device(struct input_dev *input) +{ + struct input_polled_dev *dev = input->private; + + cancel_rearming_delayed_workqueue(polldev_wq, &dev->work); + input_polldev_stop_workqueue(); +} + +/** + * input_allocate_polled_device - allocated memory polled device + * + * The function allocates memory for a polled device and also + * for an input device associated with this polled device. + */ +struct input_polled_dev *input_allocate_polled_device(void) +{ + struct input_polled_dev *dev; + + dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL); + if (!dev) + return NULL; + + dev->input = input_allocate_device(); + if (!dev->input) { + kfree(dev); + return NULL; + } + + return dev; +} +EXPORT_SYMBOL(input_allocate_polled_device); + +/** + * input_free_polled_device - free memory allocated for polled device + * @dev: device to free + * + * The function frees memory allocated for polling device and drops + * reference to the associated input device (if present). + */ +void input_free_polled_device(struct input_polled_dev *dev) +{ + if (dev) { + input_free_device(dev->input); + kfree(dev); + } +} +EXPORT_SYMBOL(input_free_polled_device); + +/** + * input_register_polled_device - register polled device + * @dev: device to register + * + * The function registers previously initialized polled input device + * with input layer. The device should be allocated with call to + * input_allocate_polled_device(). Callers should also set up poll() + * method and set up capabilities (id, name, phys, bits) of the + * corresponing input_dev structure. + */ +int input_register_polled_device(struct input_polled_dev *dev) +{ + struct input_dev *input = dev->input; + + INIT_DELAYED_WORK(&dev->work, input_polled_device_work); + if (!dev->poll_interval) + dev->poll_interval = 500; + input->private = dev; + input->open = input_open_polled_device; + input->close = input_close_polled_device; + + return input_register_device(input); +} +EXPORT_SYMBOL(input_register_polled_device); + +/** + * input_unregister_polled_device - unregister polled device + * @dev: device to unregister + * + * The function unregisters previously registered polled input + * device from input layer. Polling is stopped and device is + * ready to be freed with call to input_free_polled_device(). + * Callers should not attempt to access dev->input pointer + * after calling this function. + */ +void input_unregister_polled_device(struct input_polled_dev *dev) +{ + input_unregister_device(dev->input); + dev->input = NULL; +} +EXPORT_SYMBOL(input_unregister_polled_device); + diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 105c6fc2782..3d4b619dada 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -51,7 +51,7 @@ static void ixp4xx_spkr_control(unsigned int pin, unsigned int count) static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - unsigned int pin = (unsigned int) dev->private; + unsigned int pin = (unsigned int) input_get_drvdata(input_dev); unsigned int count = 0; if (type != EV_SND) @@ -99,14 +99,15 @@ static int __devinit ixp4xx_spkr_probe(struct platform_device *dev) if (!input_dev) return -ENOMEM; - input_dev->private = (void *) dev->id; + input_set_drvdata(input_dev, (void *) dev->id); + input_dev->name = "ixp4xx beeper", input_dev->phys = "ixp4xx/gpio"; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x001f; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &dev->dev; + input_dev->dev.parent = &dev->dev; input_dev->evbit[0] = BIT(EV_SND); input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); @@ -136,7 +137,7 @@ static int __devinit ixp4xx_spkr_probe(struct platform_device *dev) static int __devexit ixp4xx_spkr_remove(struct platform_device *dev) { struct input_dev *input_dev = platform_get_drvdata(dev); - unsigned int pin = (unsigned int) input_dev->private; + unsigned int pin = (unsigned int) input_get_drvdata(input_dev); input_unregister_device(input_dev); platform_set_drvdata(dev, NULL); @@ -153,7 +154,7 @@ static int __devexit ixp4xx_spkr_remove(struct platform_device *dev) static void ixp4xx_spkr_shutdown(struct platform_device *dev) { struct input_dev *input_dev = platform_get_drvdata(dev); - unsigned int pin = (unsigned int) input_dev->private; + unsigned int pin = (unsigned int) input_get_drvdata(input_dev); /* turn off the speaker */ disable_irq(IRQ_IXP4XX_TIMER2); diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c new file mode 100644 index 00000000000..1bffc9fa98c --- /dev/null +++ b/drivers/input/misc/keyspan_remote.c @@ -0,0 +1,592 @@ +/* + * keyspan_remote: USB driver for the Keyspan DMR + * + * Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * This driver has been put together with the support of Innosys, Inc. + * and Keyspan, Inc the manufacturers of the Keyspan USB DMR product. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/usb/input.h> + +#define DRIVER_VERSION "v0.1" +#define DRIVER_AUTHOR "Michael Downey <downey@zymeta.com>" +#define DRIVER_DESC "Driver for the USB Keyspan remote control." +#define DRIVER_LICENSE "GPL" + +/* Parameters that can be passed to the driver. */ +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); + +/* Vendor and product ids */ +#define USB_KEYSPAN_VENDOR_ID 0x06CD +#define USB_KEYSPAN_PRODUCT_UIA11 0x0202 + +/* Defines for converting the data from the remote. */ +#define ZERO 0x18 +#define ZERO_MASK 0x1F /* 5 bits for a 0 */ +#define ONE 0x3C +#define ONE_MASK 0x3F /* 6 bits for a 1 */ +#define SYNC 0x3F80 +#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */ +#define STOP 0x00 +#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */ +#define GAP 0xFF + +#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */ + +/* table of devices that work with this driver */ +static struct usb_device_id keyspan_table[] = { + { USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) }, + { } /* Terminating entry */ +}; + +/* Structure to store all the real stuff that a remote sends to us. */ +struct keyspan_message { + u16 system; + u8 button; + u8 toggle; +}; + +/* Structure used for all the bit testing magic needed to be done. */ +struct bit_tester { + u32 tester; + int len; + int pos; + int bits_left; + u8 buffer[32]; +}; + +/* Structure to hold all of our driver specific stuff */ +struct usb_keyspan { + char name[128]; + char phys[64]; + struct usb_device* udev; + struct input_dev *input; + struct usb_interface* interface; + struct usb_endpoint_descriptor* in_endpoint; + struct urb* irq_urb; + int open; + dma_addr_t in_dma; + unsigned char* in_buffer; + + /* variables used to parse messages from remote. */ + struct bit_tester data; + int stage; + int toggle; +}; + +/* + * Table that maps the 31 possible keycodes to input keys. + * Currently there are 15 and 17 button models so RESERVED codes + * are blank areas in the mapping. + */ +static const int keyspan_key_table[] = { + KEY_RESERVED, /* 0 is just a place holder. */ + KEY_RESERVED, + KEY_STOP, + KEY_PLAYCD, + KEY_RESERVED, + KEY_PREVIOUSSONG, + KEY_REWIND, + KEY_FORWARD, + KEY_NEXTSONG, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_PAUSE, + KEY_VOLUMEUP, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_VOLUMEDOWN, + KEY_RESERVED, + KEY_UP, + KEY_RESERVED, + KEY_MUTE, + KEY_LEFT, + KEY_ENTER, + KEY_RIGHT, + KEY_RESERVED, + KEY_RESERVED, + KEY_DOWN, + KEY_RESERVED, + KEY_KPASTERISK, + KEY_RESERVED, + KEY_MENU +}; + +static struct usb_driver keyspan_driver; + +/* + * Debug routine that prints out what we've received from the remote. + */ +static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/ +{ + char codes[4 * RECV_SIZE]; + int i; + + for (i = 0; i < RECV_SIZE; i++) + snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]); + + dev_info(&dev->udev->dev, "%s\n", codes); +} + +/* + * Routine that manages the bit_tester structure. It makes sure that there are + * at least bits_needed bits loaded into the tester. + */ +static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed) +{ + if (dev->data.bits_left >= bits_needed) + return 0; + + /* + * Somehow we've missed the last message. The message will be repeated + * though so it's not too big a deal + */ + if (dev->data.pos >= dev->data.len) { + dev_dbg(&dev->udev->dev, + "%s - Error ran out of data. pos: %d, len: %d\n", + __FUNCTION__, dev->data.pos, dev->data.len); + return -1; + } + + /* Load as much as we can into the tester. */ + while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) && + (dev->data.pos < dev->data.len)) { + dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left); + dev->data.bits_left += 8; + } + + return 0; +} + +/* + * Routine that handles all the logic needed to parse out the message from the remote. + */ +static void keyspan_check_data(struct usb_keyspan *remote) +{ + int i; + int found = 0; + struct keyspan_message message; + + switch(remote->stage) { + case 0: + /* + * In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler. + * So the first byte that isn't a FF should be the start of a new message. + */ + for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i); + + if (i < RECV_SIZE) { + memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE); + remote->data.len = RECV_SIZE; + remote->data.pos = 0; + remote->data.tester = 0; + remote->data.bits_left = 0; + remote->stage = 1; + } + break; + + case 1: + /* + * Stage 1 we should have 16 bytes and should be able to detect a + * SYNC. The SYNC is 14 bits, 7 0's and then 7 1's. + */ + memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE); + remote->data.len += RECV_SIZE; + + found = 0; + while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) { + for (i = 0; i < 8; ++i) { + if (keyspan_load_tester(remote, 14) != 0) { + remote->stage = 0; + return; + } + + if ((remote->data.tester & SYNC_MASK) == SYNC) { + remote->data.tester = remote->data.tester >> 14; + remote->data.bits_left -= 14; + found = 1; + break; + } else { + remote->data.tester = remote->data.tester >> 1; + --remote->data.bits_left; + } + } + } + + if (!found) { + remote->stage = 0; + remote->data.len = 0; + } else { + remote->stage = 2; + } + break; + + case 2: + /* + * Stage 2 we should have 24 bytes which will be enough for a full + * message. We need to parse out the system code, button code, + * toggle code, and stop. + */ + memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE); + remote->data.len += RECV_SIZE; + + message.system = 0; + for (i = 0; i < 9; i++) { + keyspan_load_tester(remote, 6); + + if ((remote->data.tester & ZERO_MASK) == ZERO) { + message.system = message.system << 1; + remote->data.tester = remote->data.tester >> 5; + remote->data.bits_left -= 5; + } else if ((remote->data.tester & ONE_MASK) == ONE) { + message.system = (message.system << 1) + 1; + remote->data.tester = remote->data.tester >> 6; + remote->data.bits_left -= 6; + } else { + err("%s - Unknown sequence found in system data.\n", __FUNCTION__); + remote->stage = 0; + return; + } + } + + message.button = 0; + for (i = 0; i < 5; i++) { + keyspan_load_tester(remote, 6); + + if ((remote->data.tester & ZERO_MASK) == ZERO) { + message.button = message.button << 1; + remote->data.tester = remote->data.tester >> 5; + remote->data.bits_left -= 5; + } else if ((remote->data.tester & ONE_MASK) == ONE) { + message.button = (message.button << 1) + 1; + remote->data.tester = remote->data.tester >> 6; + remote->data.bits_left -= 6; + } else { + err("%s - Unknown sequence found in button data.\n", __FUNCTION__); + remote->stage = 0; + return; + } + } + + keyspan_load_tester(remote, 6); + if ((remote->data.tester & ZERO_MASK) == ZERO) { + message.toggle = 0; + remote->data.tester = remote->data.tester >> 5; + remote->data.bits_left -= 5; + } else if ((remote->data.tester & ONE_MASK) == ONE) { + message.toggle = 1; + remote->data.tester = remote->data.tester >> 6; + remote->data.bits_left -= 6; + } else { + err("%s - Error in message, invalid toggle.\n", __FUNCTION__); + remote->stage = 0; + return; + } + + keyspan_load_tester(remote, 5); + if ((remote->data.tester & STOP_MASK) == STOP) { + remote->data.tester = remote->data.tester >> 5; + remote->data.bits_left -= 5; + } else { + err("Bad message recieved, no stop bit found.\n"); + } + + dev_dbg(&remote->udev->dev, + "%s found valid message: system: %d, button: %d, toggle: %d\n", + __FUNCTION__, message.system, message.button, message.toggle); + + if (message.toggle != remote->toggle) { + input_report_key(remote->input, keyspan_key_table[message.button], 1); + input_report_key(remote->input, keyspan_key_table[message.button], 0); + input_sync(remote->input); + remote->toggle = message.toggle; + } + + remote->stage = 0; + break; + } +} + +/* + * Routine for sending all the initialization messages to the remote. + */ +static int keyspan_setup(struct usb_device* dev) +{ + int retval = 0; + + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x11, 0x40, 0x5601, 0x0, NULL, 0, 0); + if (retval) { + dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n", + __FUNCTION__, retval); + return(retval); + } + + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x44, 0x40, 0x0, 0x0, NULL, 0, 0); + if (retval) { + dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n", + __FUNCTION__, retval); + return(retval); + } + + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x22, 0x40, 0x0, 0x0, NULL, 0, 0); + if (retval) { + dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n", + __FUNCTION__, retval); + return(retval); + } + + dev_dbg(&dev->dev, "%s - Setup complete.\n", __FUNCTION__); + return(retval); +} + +/* + * Routine used to handle a new message that has come in. + */ +static void keyspan_irq_recv(struct urb *urb) +{ + struct usb_keyspan *dev = urb->context; + int retval; + + /* Check our status in case we need to bail out early. */ + switch (urb->status) { + case 0: + break; + + /* Device went away so don't keep trying to read from it. */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + goto resubmit; + break; + } + + if (debug) + keyspan_print(dev); + + keyspan_check_data(dev); + +resubmit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result: %d", __FUNCTION__, retval); +} + +static int keyspan_open(struct input_dev *dev) +{ + struct usb_keyspan *remote = input_get_drvdata(dev); + + remote->irq_urb->dev = remote->udev; + if (usb_submit_urb(remote->irq_urb, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void keyspan_close(struct input_dev *dev) +{ + struct usb_keyspan *remote = input_get_drvdata(dev); + + usb_kill_urb(remote->irq_urb); +} + +static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface) +{ + + struct usb_endpoint_descriptor *endpoint; + int i; + + for (i = 0; i < iface->desc.bNumEndpoints; ++i) { + endpoint = &iface->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + /* we found our interrupt in endpoint */ + return endpoint; + } + } + + return NULL; +} + +/* + * Routine that sets up the driver to handle a specific USB device detected on the bus. + */ +static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_endpoint_descriptor *endpoint; + struct usb_keyspan *remote; + struct input_dev *input_dev; + int i, error; + + endpoint = keyspan_get_in_endpoint(interface->cur_altsetting); + if (!endpoint) + return -ENODEV; + + remote = kzalloc(sizeof(*remote), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!remote || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + remote->udev = udev; + remote->input = input_dev; + remote->interface = interface; + remote->in_endpoint = endpoint; + remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */ + + remote->in_buffer = usb_buffer_alloc(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma); + if (!remote->in_buffer) { + error = -ENOMEM; + goto fail1; + } + + remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!remote->irq_urb) { + error = -ENOMEM; + goto fail2; + } + + error = keyspan_setup(udev); + if (error) { + error = -ENODEV; + goto fail3; + } + + if (udev->manufacturer) + strlcpy(remote->name, udev->manufacturer, sizeof(remote->name)); + + if (udev->product) { + if (udev->manufacturer) + strlcat(remote->name, " ", sizeof(remote->name)); + strlcat(remote->name, udev->product, sizeof(remote->name)); + } + + if (!strlen(remote->name)) + snprintf(remote->name, sizeof(remote->name), + "USB Keyspan Remote %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + usb_make_path(udev, remote->phys, sizeof(remote->phys)); + strlcat(remote->phys, "/input0", sizeof(remote->phys)); + + input_dev->name = remote->name; + input_dev->phys = remote->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &interface->dev; + + input_dev->evbit[0] = BIT(EV_KEY); /* We will only report KEY events. */ + for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++) + if (keyspan_key_table[i] != KEY_RESERVED) + set_bit(keyspan_key_table[i], input_dev->keybit); + + input_set_drvdata(input_dev, remote); + + input_dev->open = keyspan_open; + input_dev->close = keyspan_close; + + /* + * Initialize the URB to access the device. The urb gets sent to the device in keyspan_open() + */ + usb_fill_int_urb(remote->irq_urb, + remote->udev, usb_rcvintpipe(remote->udev, remote->in_endpoint->bEndpointAddress), + remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote, + remote->in_endpoint->bInterval); + remote->irq_urb->transfer_dma = remote->in_dma; + remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* we can register the device now, as it is ready */ + error = input_register_device(remote->input); + if (error) + goto fail3; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, remote); + + return 0; + + fail3: usb_free_urb(remote->irq_urb); + fail2: usb_buffer_free(udev, RECV_SIZE, remote->in_buffer, remote->in_dma); + fail1: kfree(remote); + input_free_device(input_dev); + + return error; +} + +/* + * Routine called when a device is disconnected from the USB. + */ +static void keyspan_disconnect(struct usb_interface *interface) +{ + struct usb_keyspan *remote; + + remote = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (remote) { /* We have a valid driver structure so clean up everything we allocated. */ + input_unregister_device(remote->input); + usb_kill_urb(remote->irq_urb); + usb_free_urb(remote->irq_urb); + usb_buffer_free(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma); + kfree(remote); + } +} + +/* + * Standard driver set up sections + */ +static struct usb_driver keyspan_driver = +{ + .name = "keyspan_remote", + .probe = keyspan_probe, + .disconnect = keyspan_disconnect, + .id_table = keyspan_table +}; + +static int __init usb_keyspan_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&keyspan_driver); + if (result) + err("usb_register failed. Error number %d\n", result); + + return result; +} + +static void __exit usb_keyspan_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&keyspan_driver); +} + +module_init(usb_keyspan_init); +module_exit(usb_keyspan_exit); + +MODULE_DEVICE_TABLE(usb, keyspan_table); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c index 8d6c3837bad..e9f26e766b4 100644 --- a/drivers/input/misc/m68kspkr.c +++ b/drivers/input/misc/m68kspkr.c @@ -63,7 +63,7 @@ static int __devinit m68kspkr_probe(struct platform_device *dev) input_dev->id.vendor = 0x001f; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &dev->dev; + input_dev->dev.parent = &dev->dev; input_dev->evbit[0] = BIT(EV_SND); input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); diff --git a/drivers/input/misc/map_to_7segment.h b/drivers/input/misc/map_to_7segment.h new file mode 100644 index 00000000000..a424094d9fe --- /dev/null +++ b/drivers/input/misc/map_to_7segment.h @@ -0,0 +1,189 @@ +/* + * drivers/usb/input/map_to_7segment.h + * + * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MAP_TO_7SEGMENT_H +#define MAP_TO_7SEGMENT_H + +/* This file provides translation primitives and tables for the conversion + * of (ASCII) characters to a 7-segments notation. + * + * The 7 segment's wikipedia notation below is used as standard. + * See: http://en.wikipedia.org/wiki/Seven_segment_display + * + * Notation: +-a-+ + * f b + * +-g-+ + * e c + * +-d-+ + * + * Usage: + * + * Register a map variable, and fill it with a character set: + * static SEG7_DEFAULT_MAP(map_seg7); + * + * + * Then use for conversion: + * seg7 = map_to_seg7(&map_seg7, some_char); + * ... + * + * In device drivers it is recommended, if required, to make the char map + * accessible via the sysfs interface using the following scheme: + * + * static ssize_t show_map(struct device *dev, char *buf) { + * memcpy(buf, &map_seg7, sizeof(map_seg7)); + * return sizeof(map_seg7); + * } + * static ssize_t store_map(struct device *dev, const char *buf, size_t cnt) { + * if(cnt != sizeof(map_seg7)) + * return -EINVAL; + * memcpy(&map_seg7, buf, cnt); + * return cnt; + * } + * static DEVICE_ATTR(map_seg7, PERMS_RW, show_map, store_map); + * + * History: + * 2005-05-31 RFC linux-kernel@vger.kernel.org + */ +#include <linux/errno.h> + + +#define BIT_SEG7_A 0 +#define BIT_SEG7_B 1 +#define BIT_SEG7_C 2 +#define BIT_SEG7_D 3 +#define BIT_SEG7_E 4 +#define BIT_SEG7_F 5 +#define BIT_SEG7_G 6 +#define BIT_SEG7_RESERVED 7 + +struct seg7_conversion_map { + unsigned char table[128]; +}; + +static inline int map_to_seg7(struct seg7_conversion_map *map, int c) +{ + return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL; +} + +#define SEG7_CONVERSION_MAP(_name, _map) \ + struct seg7_conversion_map _name = { .table = { _map } } + +/* + * It is recommended to use a facility that allows user space to redefine + * custom character sets for LCD devices. Please use a sysfs interface + * as described above. + */ +#define MAP_TO_SEG7_SYSFS_FILE "map_seg7" + +/******************************************************************************* + * ASCII conversion table + ******************************************************************************/ + +#define _SEG7(l,a,b,c,d,e,f,g) \ + ( a<<BIT_SEG7_A | b<<BIT_SEG7_B | c<<BIT_SEG7_C | d<<BIT_SEG7_D | \ + e<<BIT_SEG7_E | f<<BIT_SEG7_F | g<<BIT_SEG7_G ) + +#define _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + +#define _MAP_33_47_ASCII_SEG7_SYMBOL \ + _SEG7('!',0,0,0,0,1,1,0), _SEG7('"',0,1,0,0,0,1,0), _SEG7('#',0,1,1,0,1,1,0),\ + _SEG7('$',1,0,1,1,0,1,1), _SEG7('%',0,0,1,0,0,1,0), _SEG7('&',1,0,1,1,1,1,1),\ + _SEG7('\'',0,0,0,0,0,1,0),_SEG7('(',1,0,0,1,1,1,0), _SEG7(')',1,1,1,1,0,0,0),\ + _SEG7('*',0,1,1,0,1,1,1), _SEG7('+',0,1,1,0,0,0,1), _SEG7(',',0,0,0,0,1,0,0),\ + _SEG7('-',0,0,0,0,0,0,1), _SEG7('.',0,0,0,0,1,0,0), _SEG7('/',0,1,0,0,1,0,1), + +#define _MAP_48_57_ASCII_SEG7_NUMERIC \ + _SEG7('0',1,1,1,1,1,1,0), _SEG7('1',0,1,1,0,0,0,0), _SEG7('2',1,1,0,1,1,0,1),\ + _SEG7('3',1,1,1,1,0,0,1), _SEG7('4',0,1,1,0,0,1,1), _SEG7('5',1,0,1,1,0,1,1),\ + _SEG7('6',1,0,1,1,1,1,1), _SEG7('7',1,1,1,0,0,0,0), _SEG7('8',1,1,1,1,1,1,1),\ + _SEG7('9',1,1,1,1,0,1,1), + +#define _MAP_58_64_ASCII_SEG7_SYMBOL \ + _SEG7(':',0,0,0,1,0,0,1), _SEG7(';',0,0,0,1,0,0,1), _SEG7('<',1,0,0,0,0,1,1),\ + _SEG7('=',0,0,0,1,0,0,1), _SEG7('>',1,1,0,0,0,0,1), _SEG7('?',1,1,1,0,0,1,0),\ + _SEG7('@',1,1,0,1,1,1,1), + +#define _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \ + _SEG7('A',1,1,1,0,1,1,1), _SEG7('B',1,1,1,1,1,1,1), _SEG7('C',1,0,0,1,1,1,0),\ + _SEG7('D',1,1,1,1,1,1,0), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\ + _SEG7('G',1,1,1,1,0,1,1), _SEG7('H',0,1,1,0,1,1,1), _SEG7('I',0,1,1,0,0,0,0),\ + _SEG7('J',0,1,1,1,0,0,0), _SEG7('K',0,1,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\ + _SEG7('M',1,1,1,0,1,1,0), _SEG7('N',1,1,1,0,1,1,0), _SEG7('O',1,1,1,1,1,1,0),\ + _SEG7('P',1,1,0,0,1,1,1), _SEG7('Q',1,1,1,1,1,1,0), _SEG7('R',1,1,1,0,1,1,1),\ + _SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('U',0,1,1,1,1,1,0),\ + _SEG7('V',0,1,1,1,1,1,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\ + _SEG7('Y',0,1,1,0,0,1,1), _SEG7('Z',1,1,0,1,1,0,1), + +#define _MAP_91_96_ASCII_SEG7_SYMBOL \ + _SEG7('[',1,0,0,1,1,1,0), _SEG7('\\',0,0,1,0,0,1,1),_SEG7(']',1,1,1,1,0,0,0),\ + _SEG7('^',1,1,0,0,0,1,0), _SEG7('_',0,0,0,1,0,0,0), _SEG7('`',0,1,0,0,0,0,0), + +#define _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \ + _SEG7('A',1,1,1,0,1,1,1), _SEG7('b',0,0,1,1,1,1,1), _SEG7('c',0,0,0,1,1,0,1),\ + _SEG7('d',0,1,1,1,1,0,1), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\ + _SEG7('G',1,1,1,1,0,1,1), _SEG7('h',0,0,1,0,1,1,1), _SEG7('i',0,0,1,0,0,0,0),\ + _SEG7('j',0,0,1,1,0,0,0), _SEG7('k',0,0,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\ + _SEG7('M',1,1,1,0,1,1,0), _SEG7('n',0,0,1,0,1,0,1), _SEG7('o',0,0,1,1,1,0,1),\ + _SEG7('P',1,1,0,0,1,1,1), _SEG7('q',1,1,1,0,0,1,1), _SEG7('r',0,0,0,0,1,0,1),\ + _SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('u',0,0,1,1,1,0,0),\ + _SEG7('v',0,0,1,1,1,0,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\ + _SEG7('y',0,1,1,1,0,1,1), _SEG7('Z',1,1,0,1,1,0,1), + +#define _MAP_123_126_ASCII_SEG7_SYMBOL \ + _SEG7('{',1,0,0,1,1,1,0), _SEG7('|',0,0,0,0,1,1,0), _SEG7('}',1,1,1,1,0,0,0),\ + _SEG7('~',1,0,0,0,0,0,0), + +/* Maps */ + +/* This set tries to map as close as possible to the visible characteristics + * of the ASCII symbol, lowercase and uppercase letters may differ in + * presentation on the display. + */ +#define MAP_ASCII7SEG_ALPHANUM \ + _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \ + _MAP_33_47_ASCII_SEG7_SYMBOL \ + _MAP_48_57_ASCII_SEG7_NUMERIC \ + _MAP_58_64_ASCII_SEG7_SYMBOL \ + _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \ + _MAP_91_96_ASCII_SEG7_SYMBOL \ + _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \ + _MAP_123_126_ASCII_SEG7_SYMBOL + +/* This set tries to map as close as possible to the symbolic characteristics + * of the ASCII character for maximum discrimination. + * For now this means all alpha chars are in lower case representations. + * (This for example facilitates the use of hex numbers with uppercase input.) + */ +#define MAP_ASCII7SEG_ALPHANUM_LC \ + _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \ + _MAP_33_47_ASCII_SEG7_SYMBOL \ + _MAP_48_57_ASCII_SEG7_NUMERIC \ + _MAP_58_64_ASCII_SEG7_SYMBOL \ + _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \ + _MAP_91_96_ASCII_SEG7_SYMBOL \ + _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \ + _MAP_123_126_ASCII_SEG7_SYMBOL + +#define SEG7_DEFAULT_MAP(_name) \ + SEG7_CONVERSION_MAP(_name,MAP_ASCII7SEG_ALPHANUM) + +#endif /* MAP_TO_7SEGMENT_H */ + diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index afd322185bb..31989dcd922 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -78,7 +78,7 @@ static int __devinit pcspkr_probe(struct platform_device *dev) pcspkr_dev->id.vendor = 0x001f; pcspkr_dev->id.product = 0x0001; pcspkr_dev->id.version = 0x0100; - pcspkr_dev->cdev.dev = &dev->dev; + pcspkr_dev->dev.parent = &dev->dev; pcspkr_dev->evbit[0] = BIT(EV_SND); pcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c new file mode 100644 index 00000000000..448a470d28f --- /dev/null +++ b/drivers/input/misc/powermate.c @@ -0,0 +1,463 @@ +/* + * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial. + * + * v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com> + * + * This device is a anodised aluminium knob which connects over USB. It can measure + * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with + * a spring for automatic release. The base contains a pair of LEDs which illuminate + * the translucent base. It rotates without limit and reports its relative rotation + * back to the host when polled by the USB controller. + * + * Testing with the knob I have has shown that it measures approximately 94 "clicks" + * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was + * a variable speed cordless electric drill) has shown that the device can measure + * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from + * the host. If it counts more than 7 clicks before it is polled, it will wrap back + * to zero and start counting again. This was at quite high speed, however, almost + * certainly faster than the human hand could turn it. Griffin say that it loses a + * pulse or two on a direction change; the granularity is so fine that I never + * noticed this in practice. + * + * The device's microcontroller can be programmed to set the LED to either a constant + * intensity, or to a rhythmic pulsing. Several patterns and speeds are available. + * + * Griffin were very happy to provide documentation and free hardware for development. + * + * Some userspace tools are available on the web: http://sowerbutts.com/powermate/ + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/usb/input.h> + +#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */ +#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */ +#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */ + +#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */ +#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */ + +/* these are the command codes we send to the device */ +#define SET_STATIC_BRIGHTNESS 0x01 +#define SET_PULSE_ASLEEP 0x02 +#define SET_PULSE_AWAKE 0x03 +#define SET_PULSE_MODE 0x04 + +/* these refer to bits in the powermate_device's requires_update field. */ +#define UPDATE_STATIC_BRIGHTNESS (1<<0) +#define UPDATE_PULSE_ASLEEP (1<<1) +#define UPDATE_PULSE_AWAKE (1<<2) +#define UPDATE_PULSE_MODE (1<<3) + +/* at least two versions of the hardware exist, with differing payload + sizes. the first three bytes always contain the "interesting" data in + the relevant format. */ +#define POWERMATE_PAYLOAD_SIZE_MAX 6 +#define POWERMATE_PAYLOAD_SIZE_MIN 3 +struct powermate_device { + signed char *data; + dma_addr_t data_dma; + struct urb *irq, *config; + struct usb_ctrlrequest *configcr; + dma_addr_t configcr_dma; + struct usb_device *udev; + struct input_dev *input; + spinlock_t lock; + int static_brightness; + int pulse_speed; + int pulse_table; + int pulse_asleep; + int pulse_awake; + int requires_update; // physical settings which are out of sync + char phys[64]; +}; + +static char pm_name_powermate[] = "Griffin PowerMate"; +static char pm_name_soundknob[] = "Griffin SoundKnob"; + +static void powermate_config_complete(struct urb *urb); + +/* Callback for data arriving from the PowerMate over the USB interrupt pipe */ +static void powermate_irq(struct urb *urb) +{ + struct powermate_device *pm = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + /* handle updates to device state */ + input_report_key(pm->input, BTN_0, pm->data[0] & 0x01); + input_report_rel(pm->input, REL_DIAL, pm->data[1]); + input_sync(pm->input); + +exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + +/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */ +static void powermate_sync_state(struct powermate_device *pm) +{ + if (pm->requires_update == 0) + return; /* no updates are required */ + if (pm->config->status == -EINPROGRESS) + return; /* an update is already in progress; it'll issue this update when it completes */ + + if (pm->requires_update & UPDATE_PULSE_ASLEEP){ + pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP ); + pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 ); + pm->requires_update &= ~UPDATE_PULSE_ASLEEP; + }else if (pm->requires_update & UPDATE_PULSE_AWAKE){ + pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE ); + pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 ); + pm->requires_update &= ~UPDATE_PULSE_AWAKE; + }else if (pm->requires_update & UPDATE_PULSE_MODE){ + int op, arg; + /* the powermate takes an operation and an argument for its pulse algorithm. + the operation can be: + 0: divide the speed + 1: pulse at normal speed + 2: multiply the speed + the argument only has an effect for operations 0 and 2, and ranges between + 1 (least effect) to 255 (maximum effect). + + thus, several states are equivalent and are coalesced into one state. + + we map this onto a range from 0 to 510, with: + 0 -- 254 -- use divide (0 = slowest) + 255 -- use normal speed + 256 -- 510 -- use multiple (510 = fastest). + + Only values of 'arg' quite close to 255 are particularly useful/spectacular. + */ + if (pm->pulse_speed < 255) { + op = 0; // divide + arg = 255 - pm->pulse_speed; + } else if (pm->pulse_speed > 255) { + op = 2; // multiply + arg = pm->pulse_speed - 255; + } else { + op = 1; // normal speed + arg = 0; // can be any value + } + pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE ); + pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op ); + pm->requires_update &= ~UPDATE_PULSE_MODE; + } else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) { + pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS ); + pm->configcr->wIndex = cpu_to_le16( pm->static_brightness ); + pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS; + } else { + printk(KERN_ERR "powermate: unknown update required"); + pm->requires_update = 0; /* fudge the bug */ + return; + } + +/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */ + + pm->configcr->bRequestType = 0x41; /* vendor request */ + pm->configcr->bRequest = 0x01; + pm->configcr->wLength = 0; + + usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0), + (void *) pm->configcr, NULL, 0, + powermate_config_complete, pm); + pm->config->setup_dma = pm->configcr_dma; + pm->config->transfer_flags |= URB_NO_SETUP_DMA_MAP; + + if (usb_submit_urb(pm->config, GFP_ATOMIC)) + printk(KERN_ERR "powermate: usb_submit_urb(config) failed"); +} + +/* Called when our asynchronous control message completes. We may need to issue another immediately */ +static void powermate_config_complete(struct urb *urb) +{ + struct powermate_device *pm = urb->context; + unsigned long flags; + + if (urb->status) + printk(KERN_ERR "powermate: config urb returned %d\n", urb->status); + + spin_lock_irqsave(&pm->lock, flags); + powermate_sync_state(pm); + spin_unlock_irqrestore(&pm->lock, flags); +} + +/* Set the LED up as described and begin the sync with the hardware if required */ +static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, + int pulse_table, int pulse_asleep, int pulse_awake) +{ + unsigned long flags; + + if (pulse_speed < 0) + pulse_speed = 0; + if (pulse_table < 0) + pulse_table = 0; + if (pulse_speed > 510) + pulse_speed = 510; + if (pulse_table > 2) + pulse_table = 2; + + pulse_asleep = !!pulse_asleep; + pulse_awake = !!pulse_awake; + + + spin_lock_irqsave(&pm->lock, flags); + + /* mark state updates which are required */ + if (static_brightness != pm->static_brightness) { + pm->static_brightness = static_brightness; + pm->requires_update |= UPDATE_STATIC_BRIGHTNESS; + } + if (pulse_asleep != pm->pulse_asleep) { + pm->pulse_asleep = pulse_asleep; + pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS); + } + if (pulse_awake != pm->pulse_awake) { + pm->pulse_awake = pulse_awake; + pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS); + } + if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) { + pm->pulse_speed = pulse_speed; + pm->pulse_table = pulse_table; + pm->requires_update |= UPDATE_PULSE_MODE; + } + + powermate_sync_state(pm); + + spin_unlock_irqrestore(&pm->lock, flags); +} + +/* Callback from the Input layer when an event arrives from userspace to configure the LED */ +static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value) +{ + unsigned int command = (unsigned int)_value; + struct powermate_device *pm = input_get_drvdata(dev); + + if (type == EV_MSC && code == MSC_PULSELED){ + /* + bits 0- 7: 8 bits: LED brightness + bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster. + bits 17-18: 2 bits: pulse table (0, 1, 2 valid) + bit 19: 1 bit : pulse whilst asleep? + bit 20: 1 bit : pulse constantly? + */ + int static_brightness = command & 0xFF; // bits 0-7 + int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16 + int pulse_table = (command >> 17) & 0x3; // bits 17-18 + int pulse_asleep = (command >> 19) & 0x1; // bit 19 + int pulse_awake = (command >> 20) & 0x1; // bit 20 + + powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake); + } + + return 0; +} + +static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm) +{ + pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX, + GFP_ATOMIC, &pm->data_dma); + if (!pm->data) + return -1; + + pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)), + GFP_ATOMIC, &pm->configcr_dma); + if (!pm->configcr) + return -1; + + return 0; +} + +static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm) +{ + usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX, + pm->data, pm->data_dma); + usb_buffer_free(udev, sizeof(*(pm->configcr)), + pm->configcr, pm->configcr_dma); +} + +/* Called whenever a USB device matching one in our supported devices table is connected */ +static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev (intf); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct powermate_device *pm; + struct input_dev *input_dev; + int pipe, maxp; + int error = -ENOMEM; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -EIO; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, interface->desc.bInterfaceNumber, NULL, 0, + USB_CTRL_SET_TIMEOUT); + + pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pm || !input_dev) + goto fail1; + + if (powermate_alloc_buffers(udev, pm)) + goto fail2; + + pm->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!pm->irq) + goto fail2; + + pm->config = usb_alloc_urb(0, GFP_KERNEL); + if (!pm->config) + goto fail3; + + pm->udev = udev; + pm->input = input_dev; + + usb_make_path(udev, pm->phys, sizeof(pm->phys)); + strlcpy(pm->phys, "/input0", sizeof(pm->phys)); + + spin_lock_init(&pm->lock); + + switch (le16_to_cpu(udev->descriptor.idProduct)) { + case POWERMATE_PRODUCT_NEW: + input_dev->name = pm_name_powermate; + break; + case POWERMATE_PRODUCT_OLD: + input_dev->name = pm_name_soundknob; + break; + default: + input_dev->name = pm_name_soundknob; + printk(KERN_WARNING "powermate: unknown product id %04x\n", + le16_to_cpu(udev->descriptor.idProduct)); + } + + input_dev->phys = pm->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, pm); + + input_dev->event = powermate_input_event; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_MSC); + input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); + input_dev->relbit[LONG(REL_DIAL)] = BIT(REL_DIAL); + input_dev->mscbit[LONG(MSC_PULSELED)] = BIT(MSC_PULSELED); + + /* get a handle to the interrupt data pipe */ + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + + if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) { + printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n", + POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp); + maxp = POWERMATE_PAYLOAD_SIZE_MAX; + } + + usb_fill_int_urb(pm->irq, udev, pipe, pm->data, + maxp, powermate_irq, + pm, endpoint->bInterval); + pm->irq->transfer_dma = pm->data_dma; + pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* register our interrupt URB with the USB system */ + if (usb_submit_urb(pm->irq, GFP_KERNEL)) { + error = -EIO; + goto fail4; + } + + error = input_register_device(pm->input); + if (error) + goto fail5; + + + /* force an update of everything */ + pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS; + powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters + + usb_set_intfdata(intf, pm); + return 0; + + fail5: usb_kill_urb(pm->irq); + fail4: usb_free_urb(pm->config); + fail3: usb_free_urb(pm->irq); + fail2: powermate_free_buffers(udev, pm); + fail1: input_free_device(input_dev); + kfree(pm); + return error; +} + +/* Called when a USB device we've accepted ownership of is removed */ +static void powermate_disconnect(struct usb_interface *intf) +{ + struct powermate_device *pm = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (pm) { + pm->requires_update = 0; + usb_kill_urb(pm->irq); + input_unregister_device(pm->input); + usb_free_urb(pm->irq); + usb_free_urb(pm->config); + powermate_free_buffers(interface_to_usbdev(intf), pm); + + kfree(pm); + } +} + +static struct usb_device_id powermate_devices [] = { + { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) }, + { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) }, + { USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, powermate_devices); + +static struct usb_driver powermate_driver = { + .name = "powermate", + .probe = powermate_probe, + .disconnect = powermate_disconnect, + .id_table = powermate_devices, +}; + +static int __init powermate_init(void) +{ + return usb_register(&powermate_driver); +} + +static void __exit powermate_cleanup(void) +{ + usb_deregister(&powermate_driver); +} + +module_init(powermate_init); +module_exit(powermate_cleanup); + +MODULE_AUTHOR( "William R Sowerbutts" ); +MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" ); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 106c94f33b9..e36ec1d92be 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -28,7 +28,7 @@ struct sparcspkr_state { static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct sparcspkr_state *state = dev_get_drvdata(dev->cdev.dev); + struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); unsigned int count = 0; unsigned long flags; @@ -61,7 +61,7 @@ static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned in static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct sparcspkr_state *state = dev_get_drvdata(dev->cdev.dev); + struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent); unsigned int count = 0; unsigned long flags; @@ -113,7 +113,7 @@ static int __devinit sparcspkr_probe(struct device *dev) input_dev->id.vendor = 0x001f; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->cdev.dev = dev; + input_dev->dev.parent = dev; input_dev->evbit[0] = BIT(EV_SND); input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 42556232c52..a56ad4ba8fe 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -33,7 +33,6 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/input.h> #include <linux/smp_lock.h> #include <linux/fs.h> #include <linux/miscdevice.h> @@ -41,9 +40,7 @@ static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct uinput_device *udev; - - udev = dev->private; + struct uinput_device *udev = input_get_drvdata(dev); udev->buff[udev->head].type = type; udev->buff[udev->head].code = code; @@ -136,7 +133,7 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff request.u.upload.effect = effect; request.u.upload.old = old; - retval = uinput_request_reserve_slot(dev->private, &request); + retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request); if (!retval) retval = uinput_request_submit(dev, &request); @@ -156,7 +153,7 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) request.code = UI_FF_ERASE; request.u.effect_id = effect_id; - retval = uinput_request_reserve_slot(dev->private, &request); + retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request); if (!retval) retval = uinput_request_submit(dev, &request); @@ -274,7 +271,7 @@ static int uinput_allocate_device(struct uinput_device *udev) return -ENOMEM; udev->dev->event = uinput_dev_event; - udev->dev->private = udev; + input_set_drvdata(udev->dev, udev); return 0; } diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index e1183aeb8ed..961aad7a047 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -50,7 +50,7 @@ MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>"); MODULE_DESCRIPTION("Wistron laptop button driver"); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.2"); static int force; /* = 0; */ module_param(force, bool, 0); @@ -58,7 +58,7 @@ MODULE_PARM_DESC(force, "Load even if computer is not in database"); static char *keymap_name; /* = NULL; */ module_param_named(keymap, keymap_name, charp, 0); -MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected"); +MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected [generic, 1557/MS2141]"); static struct platform_device *wistron_device; @@ -233,10 +233,20 @@ static void bios_set_state(u8 subsys, int enable) struct key_entry { char type; /* See KE_* below */ u8 code; - unsigned keycode; /* For KE_KEY */ + union { + u16 keycode; /* For KE_KEY */ + struct { /* For KE_SW */ + u8 code; + u8 value; + } sw; + }; }; -enum { KE_END, KE_KEY, KE_WIFI, KE_BLUETOOTH }; +enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH }; + +#define FE_MAIL_LED 0x01 +#define FE_WIFI_LED 0x02 +#define FE_UNTESTED 0x80 static const struct key_entry *keymap; /* = NULL; Current key map */ static int have_wifi; @@ -256,93 +266,341 @@ static int __init dmi_matched(struct dmi_system_id *dmi) return 1; } -static struct key_entry keymap_empty[] = { +static struct key_entry keymap_empty[] __initdata = { { KE_END, 0 } }; -static struct key_entry keymap_fs_amilo_pro_v2000[] = { - { KE_KEY, 0x01, KEY_HELP }, - { KE_KEY, 0x11, KEY_PROG1 }, - { KE_KEY, 0x12, KEY_PROG2 }, - { KE_WIFI, 0x30, 0 }, - { KE_KEY, 0x31, KEY_MAIL }, - { KE_KEY, 0x36, KEY_WWW }, +static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, { KE_END, 0 } }; -static struct key_entry keymap_fujitsu_n3510[] = { - { KE_KEY, 0x11, KEY_PROG1 }, - { KE_KEY, 0x12, KEY_PROG2 }, - { KE_KEY, 0x36, KEY_WWW }, - { KE_KEY, 0x31, KEY_MAIL }, - { KE_KEY, 0x71, KEY_STOPCD }, - { KE_KEY, 0x72, KEY_PLAYPAUSE }, - { KE_KEY, 0x74, KEY_REWIND }, - { KE_KEY, 0x78, KEY_FORWARD }, +static struct key_entry keymap_fujitsu_n3510[] __initdata = { + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x71, {KEY_STOPCD} }, + { KE_KEY, 0x72, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x74, {KEY_REWIND} }, + { KE_KEY, 0x78, {KEY_FORWARD} }, { KE_END, 0 } }; -static struct key_entry keymap_wistron_ms2111[] = { - { KE_KEY, 0x11, KEY_PROG1 }, - { KE_KEY, 0x12, KEY_PROG2 }, - { KE_KEY, 0x13, KEY_PROG3 }, - { KE_KEY, 0x31, KEY_MAIL }, - { KE_KEY, 0x36, KEY_WWW }, - { KE_END, 0 } +static struct key_entry keymap_wistron_ms2111[] __initdata = { + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, FE_MAIL_LED } +}; + +static struct key_entry keymap_wistron_md40100[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */ + { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED } }; -static struct key_entry keymap_wistron_ms2141[] = { - { KE_KEY, 0x11, KEY_PROG1 }, - { KE_KEY, 0x12, KEY_PROG2 }, - { KE_WIFI, 0x30, 0 }, - { KE_KEY, 0x22, KEY_REWIND }, - { KE_KEY, 0x23, KEY_FORWARD }, - { KE_KEY, 0x24, KEY_PLAYPAUSE }, - { KE_KEY, 0x25, KEY_STOPCD }, - { KE_KEY, 0x31, KEY_MAIL }, - { KE_KEY, 0x36, KEY_WWW }, +static struct key_entry keymap_wistron_ms2141[] __initdata = { + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, { KE_END, 0 } }; -static struct key_entry keymap_acer_aspire_1500[] = { - { KE_KEY, 0x11, KEY_PROG1 }, - { KE_KEY, 0x12, KEY_PROG2 }, - { KE_WIFI, 0x30, 0 }, - { KE_KEY, 0x31, KEY_MAIL }, - { KE_KEY, 0x36, KEY_WWW }, - { KE_BLUETOOTH, 0x44, 0 }, - { KE_END, 0 } +static struct key_entry keymap_acer_aspire_1500[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x49, {KEY_CONFIG} }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_UNTESTED } }; -static struct key_entry keymap_acer_travelmate_240[] = { - { KE_KEY, 0x31, KEY_MAIL }, - { KE_KEY, 0x36, KEY_WWW }, - { KE_KEY, 0x11, KEY_PROG1 }, - { KE_KEY, 0x12, KEY_PROG2 }, - { KE_BLUETOOTH, 0x44, 0 }, - { KE_WIFI, 0x30, 0 }, - { KE_END, 0 } +static struct key_entry keymap_acer_aspire_1600[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x49, {KEY_CONFIG} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +/* 3020 has been tested */ +static struct key_entry keymap_acer_aspire_5020[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */ + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x6a, {KEY_CONFIG} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_2410[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x6d, {KEY_POWER} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x6a, {KEY_CONFIG} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_110[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */ + { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */ + { KE_WIFI, 0x30 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_300[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } }; -static struct key_entry keymap_aopen_1559as[] = { - { KE_KEY, 0x01, KEY_HELP }, - { KE_KEY, 0x06, KEY_PROG3 }, - { KE_KEY, 0x11, KEY_PROG1 }, - { KE_KEY, 0x12, KEY_PROG2 }, - { KE_WIFI, 0x30, 0 }, - { KE_KEY, 0x31, KEY_MAIL }, - { KE_KEY, 0x36, KEY_WWW }, +static struct key_entry keymap_acer_travelmate_380[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, /* not 370 */ + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +/* unusual map */ +static struct key_entry keymap_acer_travelmate_220[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_MAIL} }, + { KE_KEY, 0x12, {KEY_WWW} }, + { KE_KEY, 0x13, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_PROG1} }, + { KE_END, FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_230[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_240[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_BLUETOOTH, 0x44 }, + { KE_WIFI, 0x30 }, + { KE_END, FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_350[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_MAIL} }, + { KE_KEY, 0x14, {KEY_PROG3} }, + { KE_KEY, 0x15, {KEY_WWW} }, + { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_acer_travelmate_360[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_MAIL} }, + { KE_KEY, 0x14, {KEY_PROG3} }, + { KE_KEY, 0x15, {KEY_WWW} }, + { KE_KEY, 0x40, {KEY_WLAN} }, + { KE_END, FE_WIFI_LED | FE_UNTESTED } /* no mail led */ +}; + +/* Wifi subsystem only activates the led. Therefore we need to pass + * wifi event as a normal key, then userspace can really change the wifi state. + * TODO we need to export led state to userspace (wifi and mail) */ +static struct key_entry keymap_acer_travelmate_610[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x14, {KEY_MAIL} }, + { KE_KEY, 0x15, {KEY_WWW} }, + { KE_KEY, 0x40, {KEY_WLAN} }, + { KE_END, FE_MAIL_LED | FE_WIFI_LED } +}; + +static struct key_entry keymap_acer_travelmate_630[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x08, {KEY_MUTE} }, /* not 620 */ + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_aopen_1559as[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x06, {KEY_PROG3} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, { KE_END, 0 }, }; -static struct key_entry keymap_fs_amilo_d88x0[] = { - { KE_KEY, 0x01, KEY_HELP }, - { KE_KEY, 0x08, KEY_MUTE }, - { KE_KEY, 0x31, KEY_MAIL }, - { KE_KEY, 0x36, KEY_WWW }, - { KE_KEY, 0x11, KEY_PROG1 }, - { KE_KEY, 0x12, KEY_PROG2 }, - { KE_KEY, 0x13, KEY_PROG3 }, +static struct key_entry keymap_fs_amilo_d88x0[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_wistron_md2900[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_END, FE_MAIL_LED | FE_UNTESTED } +}; + +static struct key_entry keymap_wistron_md96500[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */ + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, + { KE_END, FE_UNTESTED } +}; + +static struct key_entry keymap_wistron_generic[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x02, {KEY_CONFIG} }, + { KE_KEY, 0x03, {KEY_POWER} }, + { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */ + { KE_KEY, 0x08, {KEY_MUTE} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_KEY, 0x13, {KEY_PROG3} }, + { KE_KEY, 0x14, {KEY_MAIL} }, + { KE_KEY, 0x15, {KEY_WWW} }, + { KE_KEY, 0x20, {KEY_VOLUMEUP} }, + { KE_KEY, 0x21, {KEY_VOLUMEDOWN} }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */ + { KE_KEY, 0x40, {KEY_WLAN} }, + { KE_KEY, 0x49, {KEY_CONFIG} }, + { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */ + { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */ + { KE_KEY, 0x6a, {KEY_CONFIG} }, + { KE_KEY, 0x6d, {KEY_POWER} }, + { KE_KEY, 0x71, {KEY_STOPCD} }, + { KE_KEY, 0x72, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x74, {KEY_REWIND} }, + { KE_KEY, 0x78, {KEY_FORWARD} }, + { KE_WIFI, 0x30 }, + { KE_BLUETOOTH, 0x44 }, { KE_END, 0 } }; @@ -390,6 +648,133 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, { .callback = dmi_matched, + .ident = "Acer Aspire 1600", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"), + }, + .driver_data = keymap_acer_aspire_1600 + }, + { + .callback = dmi_matched, + .ident = "Acer Aspire 3020", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"), + }, + .driver_data = keymap_acer_aspire_5020 + }, + { + .callback = dmi_matched, + .ident = "Acer Aspire 5020", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"), + }, + .driver_data = keymap_acer_aspire_5020 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 2100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"), + }, + .driver_data = keymap_acer_aspire_5020 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 2410", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"), + }, + .driver_data = keymap_acer_travelmate_2410 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate C300", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"), + }, + .driver_data = keymap_acer_travelmate_300 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate C100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"), + }, + .driver_data = keymap_acer_travelmate_300 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate C110", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"), + }, + .driver_data = keymap_acer_travelmate_110 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 380", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"), + }, + .driver_data = keymap_acer_travelmate_380 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 370", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"), + }, + .driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */ + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 220", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"), + }, + .driver_data = keymap_acer_travelmate_220 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 260", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"), + }, + .driver_data = keymap_acer_travelmate_220 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 230", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"), + /* acerhk looks for "TravelMate F4..." ?! */ + }, + .driver_data = keymap_acer_travelmate_230 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 280", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"), + }, + .driver_data = keymap_acer_travelmate_230 + }, + { + .callback = dmi_matched, .ident = "Acer TravelMate 240", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), @@ -399,6 +784,15 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, { .callback = dmi_matched, + .ident = "Acer TravelMate 250", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"), + }, + .driver_data = keymap_acer_travelmate_240 + }, + { + .callback = dmi_matched, .ident = "Acer TravelMate 2424NWXCi", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), @@ -408,6 +802,51 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, { .callback = dmi_matched, + .ident = "Acer TravelMate 350", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"), + }, + .driver_data = keymap_acer_travelmate_350 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 360", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), + }, + .driver_data = keymap_acer_travelmate_360 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 610", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ACER"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"), + }, + .driver_data = keymap_acer_travelmate_610 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 620", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"), + }, + .driver_data = keymap_acer_travelmate_630 + }, + { + .callback = dmi_matched, + .ident = "Acer TravelMate 630", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"), + }, + .driver_data = keymap_acer_travelmate_630 + }, + { + .callback = dmi_matched, .ident = "AOpen 1559AS", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "E2U"), @@ -426,6 +865,51 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, { .callback = dmi_matched, + .ident = "Medion MD 40100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), + DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"), + }, + .driver_data = keymap_wistron_md40100 + }, + { + .callback = dmi_matched, + .ident = "Medion MD 2900", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"), + }, + .driver_data = keymap_wistron_md2900 + }, + { + .callback = dmi_matched, + .ident = "Medion MD 96500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"), + }, + .driver_data = keymap_wistron_md96500 + }, + { + .callback = dmi_matched, + .ident = "Medion MD 95400", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"), + }, + .driver_data = keymap_wistron_md96500 + }, + { + .callback = dmi_matched, + .ident = "Fujitsu Siemens Amilo D7820", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */ + DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"), + }, + .driver_data = keymap_fs_amilo_d88x0 + }, + { + .callback = dmi_matched, .ident = "Fujitsu Siemens Amilo D88x0", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), @@ -436,17 +920,39 @@ static struct dmi_system_id dmi_ids[] __initdata = { { NULL, } }; +/* Copy the good keymap, as the original ones are free'd */ +static int __init copy_keymap(void) +{ + const struct key_entry *key; + struct key_entry *new_keymap; + unsigned int length = 1; + + for (key = keymap; key->type != KE_END; key++) + length++; + + new_keymap = kmalloc(length * sizeof(struct key_entry), GFP_KERNEL); + if (!new_keymap) + return -ENOMEM; + + memcpy(new_keymap, keymap, length * sizeof(struct key_entry)); + keymap = new_keymap; + + return 0; +} + static int __init select_keymap(void) { + dmi_check_system(dmi_ids); if (keymap_name != NULL) { if (strcmp (keymap_name, "1557/MS2141") == 0) keymap = keymap_wistron_ms2141; + else if (strcmp (keymap_name, "generic") == 0) + keymap = keymap_wistron_generic; else { printk(KERN_ERR "wistron_btns: Keymap unknown\n"); return -EINVAL; } } - dmi_check_system(dmi_ids); if (keymap == NULL) { if (!force) { printk(KERN_ERR "wistron_btns: System unknown\n"); @@ -454,7 +960,8 @@ static int __init select_keymap(void) } keymap = keymap_empty; } - return 0; + + return copy_keymap(); } /* Input layer interface */ @@ -476,12 +983,28 @@ static int __devinit setup_input_dev(void) input_dev->cdev.dev = &wistron_device->dev; for (key = keymap; key->type != KE_END; key++) { - if (key->type == KE_KEY) { - input_dev->evbit[LONG(EV_KEY)] = BIT(EV_KEY); - set_bit(key->keycode, input_dev->keybit); + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, input_dev->evbit); + set_bit(key->keycode, input_dev->keybit); + break; + + case KE_SW: + set_bit(EV_SW, input_dev->evbit); + set_bit(key->sw.code, input_dev->swbit); + break; + + default: + ; } } + /* reads information flags on KE_END */ + if (key->code & FE_UNTESTED) + printk(KERN_WARNING "Untested laptop multimedia keys, " + "please report success or failure to eric.piel" + "@tremplin-utc.net\n"); + error = input_register_device(input_dev); if (error) { input_free_device(input_dev); @@ -499,6 +1022,12 @@ static void report_key(unsigned keycode) input_sync(input_dev); } +static void report_switch(unsigned code, int value) +{ + input_report_switch(input_dev, code, value); + input_sync(input_dev); +} + /* Driver core */ static int wifi_enabled; @@ -519,6 +1048,10 @@ static void handle_key(u8 code) report_key(key->keycode); break; + case KE_SW: + report_switch(key->sw.code, key->sw.value); + break; + case KE_WIFI: if (have_wifi) { wifi_enabled = !wifi_enabled; @@ -534,6 +1067,7 @@ static void handle_key(u8 code) break; case KE_END: + break; default: BUG(); } @@ -690,6 +1224,7 @@ static void __exit wb_module_exit(void) platform_device_unregister(wistron_device); platform_driver_unregister(&wistron_driver); unmap_bios(); + kfree(keymap); } module_init(wb_module_init); diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c new file mode 100644 index 00000000000..ab15880fd56 --- /dev/null +++ b/drivers/input/misc/yealink.c @@ -0,0 +1,1004 @@ +/* + * drivers/usb/input/yealink.c + * + * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Description: + * Driver for the USB-P1K voip usb phone. + * This device is produced by Yealink Network Technology Co Ltd + * but may be branded under several names: + * - Yealink usb-p1k + * - Tiptel 115 + * - ... + * + * This driver is based on: + * - the usbb2k-api http://savannah.nongnu.org/projects/usbb2k-api/ + * - information from http://memeteau.free.fr/usbb2k + * - the xpad-driver drivers/input/joystick/xpad.c + * + * Thanks to: + * - Olivier Vandorpe, for providing the usbb2k-api. + * - Martin Diehl, for spotting my memory allocation bug. + * + * History: + * 20050527 henk First version, functional keyboard. Keyboard events + * will pop-up on the ../input/eventX bus. + * 20050531 henk Added led, LCD, dialtone and sysfs interface. + * 20050610 henk Cleanups, make it ready for public consumption. + * 20050630 henk Cleanups, fixes in response to comments. + * 20050701 henk sysfs write serialisation, fix potential unload races + * 20050801 henk Added ringtone, restructure USB + * 20050816 henk Merge 2.6.13-rc6 + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/rwsem.h> +#include <linux/usb/input.h> + +#include "map_to_7segment.h" +#include "yealink.h" + +#define DRIVER_VERSION "yld-20051230" +#define DRIVER_AUTHOR "Henk Vergonet" +#define DRIVER_DESC "Yealink phone driver" + +#define YEALINK_POLLING_FREQUENCY 10 /* in [Hz] */ + +struct yld_status { + u8 lcd[24]; + u8 led; + u8 dialtone; + u8 ringtone; + u8 keynum; +} __attribute__ ((packed)); + +/* + * Register the LCD segment and icon map + */ +#define _LOC(k,l) { .a = (k), .m = (l) } +#define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm) \ + { .type = (t), \ + .u = { .s = { _LOC(a, am), _LOC(b, bm), _LOC(c, cm), \ + _LOC(d, dm), _LOC(e, em), _LOC(g, gm), \ + _LOC(f, fm) } } } +#define _PIC(t, h, hm, n) \ + { .type = (t), \ + .u = { .p = { .name = (n), .a = (h), .m = (hm) } } } + +static const struct lcd_segment_map { + char type; + union { + struct pictogram_map { + u8 a,m; + char name[10]; + } p; + struct segment_map { + u8 a,m; + } s[7]; + } u; +} lcdMap[] = { +#include "yealink.h" +}; + +struct yealink_dev { + struct input_dev *idev; /* input device */ + struct usb_device *udev; /* usb device */ + + /* irq input channel */ + struct yld_ctl_packet *irq_data; + dma_addr_t irq_dma; + struct urb *urb_irq; + + /* control output channel */ + struct yld_ctl_packet *ctl_data; + dma_addr_t ctl_dma; + struct usb_ctrlrequest *ctl_req; + dma_addr_t ctl_req_dma; + struct urb *urb_ctl; + + char phys[64]; /* physical device path */ + + u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */ + int key_code; /* last reported key */ + + int stat_ix; + union { + struct yld_status s; + u8 b[sizeof(struct yld_status)]; + } master, copy; +}; + + +/******************************************************************************* + * Yealink lcd interface + ******************************************************************************/ + +/* + * Register a default 7 segment character set + */ +static SEG7_DEFAULT_MAP(map_seg7); + + /* Display a char, + * char '\9' and '\n' are placeholders and do not overwrite the original text. + * A space will always hide an icon. + */ +static int setChar(struct yealink_dev *yld, int el, int chr) +{ + int i, a, m, val; + + if (el >= ARRAY_SIZE(lcdMap)) + return -EINVAL; + + if (chr == '\t' || chr == '\n') + return 0; + + yld->lcdMap[el] = chr; + + if (lcdMap[el].type == '.') { + a = lcdMap[el].u.p.a; + m = lcdMap[el].u.p.m; + if (chr != ' ') + yld->master.b[a] |= m; + else + yld->master.b[a] &= ~m; + return 0; + } + + val = map_to_seg7(&map_seg7, chr); + for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) { + m = lcdMap[el].u.s[i].m; + + if (m == 0) + continue; + + a = lcdMap[el].u.s[i].a; + if (val & 1) + yld->master.b[a] |= m; + else + yld->master.b[a] &= ~m; + val = val >> 1; + } + return 0; +}; + +/******************************************************************************* + * Yealink key interface + ******************************************************************************/ + +/* Map device buttons to internal key events. + * + * USB-P1K button layout: + * + * up + * IN OUT + * down + * + * pickup C hangup + * 1 2 3 + * 4 5 6 + * 7 8 9 + * * 0 # + * + * The "up" and "down" keys, are symbolised by arrows on the button. + * The "pickup" and "hangup" keys are symbolised by a green and red phone + * on the button. + */ +static int map_p1k_to_key(int scancode) +{ + switch(scancode) { /* phone key: */ + case 0x23: return KEY_LEFT; /* IN */ + case 0x33: return KEY_UP; /* up */ + case 0x04: return KEY_RIGHT; /* OUT */ + case 0x24: return KEY_DOWN; /* down */ + case 0x03: return KEY_ENTER; /* pickup */ + case 0x14: return KEY_BACKSPACE; /* C */ + case 0x13: return KEY_ESC; /* hangup */ + case 0x00: return KEY_1; /* 1 */ + case 0x01: return KEY_2; /* 2 */ + case 0x02: return KEY_3; /* 3 */ + case 0x10: return KEY_4; /* 4 */ + case 0x11: return KEY_5; /* 5 */ + case 0x12: return KEY_6; /* 6 */ + case 0x20: return KEY_7; /* 7 */ + case 0x21: return KEY_8; /* 8 */ + case 0x22: return KEY_9; /* 9 */ + case 0x30: return KEY_KPASTERISK; /* * */ + case 0x31: return KEY_0; /* 0 */ + case 0x32: return KEY_LEFTSHIFT | + KEY_3 << 8; /* # */ + } + return -EINVAL; +} + +/* Completes a request by converting the data into events for the + * input subsystem. + * + * The key parameter can be cascaded: key2 << 8 | key1 + */ +static void report_key(struct yealink_dev *yld, int key) +{ + struct input_dev *idev = yld->idev; + + if (yld->key_code >= 0) { + /* old key up */ + input_report_key(idev, yld->key_code & 0xff, 0); + if (yld->key_code >> 8) + input_report_key(idev, yld->key_code >> 8, 0); + } + + yld->key_code = key; + if (key >= 0) { + /* new valid key */ + input_report_key(idev, key & 0xff, 1); + if (key >> 8) + input_report_key(idev, key >> 8, 1); + } + input_sync(idev); +} + +/******************************************************************************* + * Yealink usb communication interface + ******************************************************************************/ + +static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p) +{ + u8 *buf = (u8 *)p; + int i; + u8 sum = 0; + + for(i=0; i<USB_PKT_LEN-1; i++) + sum -= buf[i]; + p->sum = sum; + return usb_control_msg(yld->udev, + usb_sndctrlpipe(yld->udev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x200, 3, + p, sizeof(*p), + USB_CTRL_SET_TIMEOUT); +} + +static u8 default_ringtone[] = { + 0xEF, /* volume [0-255] */ + 0xFB, 0x1E, 0x00, 0x0C, /* 1250 [hz], 12/100 [s] */ + 0xFC, 0x18, 0x00, 0x0C, /* 1000 [hz], 12/100 [s] */ + 0xFB, 0x1E, 0x00, 0x0C, + 0xFC, 0x18, 0x00, 0x0C, + 0xFB, 0x1E, 0x00, 0x0C, + 0xFC, 0x18, 0x00, 0x0C, + 0xFB, 0x1E, 0x00, 0x0C, + 0xFC, 0x18, 0x00, 0x0C, + 0xFF, 0xFF, 0x01, 0x90, /* silent, 400/100 [s] */ + 0x00, 0x00 /* end of sequence */ +}; + +static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size) +{ + struct yld_ctl_packet *p = yld->ctl_data; + int ix, len; + + if (size <= 0) + return -EINVAL; + + /* Set the ringtone volume */ + memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); + yld->ctl_data->cmd = CMD_RING_VOLUME; + yld->ctl_data->size = 1; + yld->ctl_data->data[0] = buf[0]; + yealink_cmd(yld, p); + + buf++; + size--; + + p->cmd = CMD_RING_NOTE; + ix = 0; + while (size != ix) { + len = size - ix; + if (len > sizeof(p->data)) + len = sizeof(p->data); + p->size = len; + p->offset = cpu_to_be16(ix); + memcpy(p->data, &buf[ix], len); + yealink_cmd(yld, p); + ix += len; + } + return 0; +} + +/* keep stat_master & stat_copy in sync. + */ +static int yealink_do_idle_tasks(struct yealink_dev *yld) +{ + u8 val; + int i, ix, len; + + ix = yld->stat_ix; + + memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); + yld->ctl_data->cmd = CMD_KEYPRESS; + yld->ctl_data->size = 1; + yld->ctl_data->sum = 0xff - CMD_KEYPRESS; + + /* If state update pointer wraps do a KEYPRESS first. */ + if (ix >= sizeof(yld->master)) { + yld->stat_ix = 0; + return 0; + } + + /* find update candidates: copy != master */ + do { + val = yld->master.b[ix]; + if (val != yld->copy.b[ix]) + goto send_update; + } while (++ix < sizeof(yld->master)); + + /* nothing todo, wait a bit and poll for a KEYPRESS */ + yld->stat_ix = 0; + /* TODO how can we wait abit. ?? + * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY); + */ + return 0; + +send_update: + + /* Setup an appropriate update request */ + yld->copy.b[ix] = val; + yld->ctl_data->data[0] = val; + + switch(ix) { + case offsetof(struct yld_status, led): + yld->ctl_data->cmd = CMD_LED; + yld->ctl_data->sum = -1 - CMD_LED - val; + break; + case offsetof(struct yld_status, dialtone): + yld->ctl_data->cmd = CMD_DIALTONE; + yld->ctl_data->sum = -1 - CMD_DIALTONE - val; + break; + case offsetof(struct yld_status, ringtone): + yld->ctl_data->cmd = CMD_RINGTONE; + yld->ctl_data->sum = -1 - CMD_RINGTONE - val; + break; + case offsetof(struct yld_status, keynum): + val--; + val &= 0x1f; + yld->ctl_data->cmd = CMD_SCANCODE; + yld->ctl_data->offset = cpu_to_be16(val); + yld->ctl_data->data[0] = 0; + yld->ctl_data->sum = -1 - CMD_SCANCODE - val; + break; + default: + len = sizeof(yld->master.s.lcd) - ix; + if (len > sizeof(yld->ctl_data->data)) + len = sizeof(yld->ctl_data->data); + + /* Combine up to <len> consecutive LCD bytes in a singe request + */ + yld->ctl_data->cmd = CMD_LCD; + yld->ctl_data->offset = cpu_to_be16(ix); + yld->ctl_data->size = len; + yld->ctl_data->sum = -CMD_LCD - ix - val - len; + for(i=1; i<len; i++) { + ix++; + val = yld->master.b[ix]; + yld->copy.b[ix] = val; + yld->ctl_data->data[i] = val; + yld->ctl_data->sum -= val; + } + } + yld->stat_ix = ix + 1; + return 1; +} + +/* Decide on how to handle responses + * + * The state transition diagram is somethhing like: + * + * syncState<--+ + * | | + * | idle + * \|/ | + * init --ok--> waitForKey --ok--> getKey + * ^ ^ | + * | +-------ok-------+ + * error,start + * + */ +static void urb_irq_callback(struct urb *urb) +{ + struct yealink_dev *yld = urb->context; + int ret; + + if (urb->status) + err("%s - urb status %d", __FUNCTION__, urb->status); + + switch (yld->irq_data->cmd) { + case CMD_KEYPRESS: + + yld->master.s.keynum = yld->irq_data->data[0]; + break; + + case CMD_SCANCODE: + dbg("get scancode %x", yld->irq_data->data[0]); + + report_key(yld, map_p1k_to_key(yld->irq_data->data[0])); + break; + + default: + err("unexpected response %x", yld->irq_data->cmd); + } + + yealink_do_idle_tasks(yld); + + ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); + if (ret) + err("%s - usb_submit_urb failed %d", __FUNCTION__, ret); +} + +static void urb_ctl_callback(struct urb *urb) +{ + struct yealink_dev *yld = urb->context; + int ret; + + if (urb->status) + err("%s - urb status %d", __FUNCTION__, urb->status); + + switch (yld->ctl_data->cmd) { + case CMD_KEYPRESS: + case CMD_SCANCODE: + /* ask for a response */ + ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC); + break; + default: + /* send new command */ + yealink_do_idle_tasks(yld); + ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); + } + + if (ret) + err("%s - usb_submit_urb failed %d", __FUNCTION__, ret); +} + +/******************************************************************************* + * input event interface + ******************************************************************************/ + +/* TODO should we issue a ringtone on a SND_BELL event? +static int input_ev(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + + if (type != EV_SND) + return -EINVAL; + + switch (code) { + case SND_BELL: + case SND_TONE: + break; + default: + return -EINVAL; + } + + return 0; +} +*/ + +static int input_open(struct input_dev *dev) +{ + struct yealink_dev *yld = input_get_drvdata(dev); + int i, ret; + + dbg("%s", __FUNCTION__); + + /* force updates to device */ + for (i = 0; i<sizeof(yld->master); i++) + yld->copy.b[i] = ~yld->master.b[i]; + yld->key_code = -1; /* no keys pressed */ + + yealink_set_ringtone(yld, default_ringtone, sizeof(default_ringtone)); + + /* issue INIT */ + memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); + yld->ctl_data->cmd = CMD_INIT; + yld->ctl_data->size = 10; + yld->ctl_data->sum = 0x100-CMD_INIT-10; + if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) { + dbg("%s - usb_submit_urb failed with result %d", + __FUNCTION__, ret); + return ret; + } + return 0; +} + +static void input_close(struct input_dev *dev) +{ + struct yealink_dev *yld = input_get_drvdata(dev); + + usb_kill_urb(yld->urb_ctl); + usb_kill_urb(yld->urb_irq); +} + +/******************************************************************************* + * sysfs interface + ******************************************************************************/ + +static DECLARE_RWSEM(sysfs_rwsema); + +/* Interface to the 7-segments translation table aka. char set. + */ +static ssize_t show_map(struct device *dev, struct device_attribute *attr, + char *buf) +{ + memcpy(buf, &map_seg7, sizeof(map_seg7)); + return sizeof(map_seg7); +} + +static ssize_t store_map(struct device *dev, struct device_attribute *attr, + const char *buf, size_t cnt) +{ + if (cnt != sizeof(map_seg7)) + return -EINVAL; + memcpy(&map_seg7, buf, sizeof(map_seg7)); + return sizeof(map_seg7); +} + +/* Interface to the LCD. + */ + +/* Reading /sys/../lineX will return the format string with its settings: + * + * Example: + * cat ./line3 + * 888888888888 + * Linux Rocks! + */ +static ssize_t show_line(struct device *dev, char *buf, int a, int b) +{ + struct yealink_dev *yld; + int i; + + down_read(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_read(&sysfs_rwsema); + return -ENODEV; + } + + for (i = a; i < b; i++) + *buf++ = lcdMap[i].type; + *buf++ = '\n'; + for (i = a; i < b; i++) + *buf++ = yld->lcdMap[i]; + *buf++ = '\n'; + *buf = 0; + + up_read(&sysfs_rwsema); + return 3 + ((b - a) << 1); +} + +static ssize_t show_line1(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET); +} + +static ssize_t show_line2(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET); +} + +static ssize_t show_line3(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET); +} + +/* Writing to /sys/../lineX will set the coresponding LCD line. + * - Excess characters are ignored. + * - If less characters are written than allowed, the remaining digits are + * unchanged. + * - The '\n' or '\t' char is a placeholder, it does not overwrite the + * original content. + */ +static ssize_t store_line(struct device *dev, const char *buf, size_t count, + int el, size_t len) +{ + struct yealink_dev *yld; + int i; + + down_write(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_write(&sysfs_rwsema); + return -ENODEV; + } + + if (len > count) + len = count; + for (i = 0; i < len; i++) + setChar(yld, el++, buf[i]); + + up_write(&sysfs_rwsema); + return count; +} + +static ssize_t store_line1(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE); +} + +static ssize_t store_line2(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE); +} + +static ssize_t store_line3(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE); +} + +/* Interface to visible and audible "icons", these include: + * pictures on the LCD, the LED, and the dialtone signal. + */ + +/* Get a list of "switchable elements" with their current state. */ +static ssize_t get_icons(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct yealink_dev *yld; + int i, ret = 1; + + down_read(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_read(&sysfs_rwsema); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(lcdMap); i++) { + if (lcdMap[i].type != '.') + continue; + ret += sprintf(&buf[ret], "%s %s\n", + yld->lcdMap[i] == ' ' ? " " : "on", + lcdMap[i].u.p.name); + } + up_read(&sysfs_rwsema); + return ret; +} + +/* Change the visibility of a particular element. */ +static ssize_t set_icon(struct device *dev, const char *buf, size_t count, + int chr) +{ + struct yealink_dev *yld; + int i; + + down_write(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_write(&sysfs_rwsema); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(lcdMap); i++) { + if (lcdMap[i].type != '.') + continue; + if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) { + setChar(yld, i, chr); + break; + } + } + + up_write(&sysfs_rwsema); + return count; +} + +static ssize_t show_icon(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return set_icon(dev, buf, count, buf[0]); +} + +static ssize_t hide_icon(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return set_icon(dev, buf, count, ' '); +} + +/* Upload a ringtone to the device. + */ + +/* Stores raw ringtone data in the phone */ +static ssize_t store_ringtone(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct yealink_dev *yld; + + down_write(&sysfs_rwsema); + yld = dev_get_drvdata(dev); + if (yld == NULL) { + up_write(&sysfs_rwsema); + return -ENODEV; + } + + /* TODO locking with async usb control interface??? */ + yealink_set_ringtone(yld, (char *)buf, count); + up_write(&sysfs_rwsema); + return count; +} + +#define _M444 S_IRUGO +#define _M664 S_IRUGO|S_IWUSR|S_IWGRP +#define _M220 S_IWUSR|S_IWGRP + +static DEVICE_ATTR(map_seg7 , _M664, show_map , store_map ); +static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 ); +static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 ); +static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 ); +static DEVICE_ATTR(get_icons , _M444, get_icons , NULL ); +static DEVICE_ATTR(show_icon , _M220, NULL , show_icon ); +static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon ); +static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone); + +static struct attribute *yld_attributes[] = { + &dev_attr_line1.attr, + &dev_attr_line2.attr, + &dev_attr_line3.attr, + &dev_attr_get_icons.attr, + &dev_attr_show_icon.attr, + &dev_attr_hide_icon.attr, + &dev_attr_map_seg7.attr, + &dev_attr_ringtone.attr, + NULL +}; + +static struct attribute_group yld_attr_group = { + .attrs = yld_attributes +}; + +/******************************************************************************* + * Linux interface and usb initialisation + ******************************************************************************/ + +struct driver_info { + char *name; +}; + +static const struct driver_info info_P1K = { + .name = "Yealink usb-p1k", +}; + +static const struct usb_device_id usb_table [] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x6993, + .idProduct = 0xb001, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t)&info_P1K + }, + { } +}; + +static int usb_cleanup(struct yealink_dev *yld, int err) +{ + if (yld == NULL) + return err; + + usb_kill_urb(yld->urb_irq); /* parameter validation in core/urb */ + usb_kill_urb(yld->urb_ctl); /* parameter validation in core/urb */ + + if (yld->idev) { + if (err) + input_free_device(yld->idev); + else + input_unregister_device(yld->idev); + } + + usb_free_urb(yld->urb_irq); + usb_free_urb(yld->urb_ctl); + + usb_buffer_free(yld->udev, sizeof(*(yld->ctl_req)), + yld->ctl_req, yld->ctl_req_dma); + usb_buffer_free(yld->udev, USB_PKT_LEN, + yld->ctl_data, yld->ctl_dma); + usb_buffer_free(yld->udev, USB_PKT_LEN, + yld->irq_data, yld->irq_dma); + + kfree(yld); + return err; +} + +static void usb_disconnect(struct usb_interface *intf) +{ + struct yealink_dev *yld; + + down_write(&sysfs_rwsema); + yld = usb_get_intfdata(intf); + sysfs_remove_group(&intf->dev.kobj, &yld_attr_group); + usb_set_intfdata(intf, NULL); + up_write(&sysfs_rwsema); + + usb_cleanup(yld, 0); +} + +static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev (intf); + struct driver_info *nfo = (struct driver_info *)id->driver_info; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct yealink_dev *yld; + struct input_dev *input_dev; + int ret, pipe, i; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + yld = kzalloc(sizeof(struct yealink_dev), GFP_KERNEL); + if (!yld) + return -ENOMEM; + + yld->udev = udev; + + yld->idev = input_dev = input_allocate_device(); + if (!input_dev) + return usb_cleanup(yld, -ENOMEM); + + /* allocate usb buffers */ + yld->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN, + GFP_ATOMIC, &yld->irq_dma); + if (yld->irq_data == NULL) + return usb_cleanup(yld, -ENOMEM); + + yld->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN, + GFP_ATOMIC, &yld->ctl_dma); + if (!yld->ctl_data) + return usb_cleanup(yld, -ENOMEM); + + yld->ctl_req = usb_buffer_alloc(udev, sizeof(*(yld->ctl_req)), + GFP_ATOMIC, &yld->ctl_req_dma); + if (yld->ctl_req == NULL) + return usb_cleanup(yld, -ENOMEM); + + /* allocate urb structures */ + yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL); + if (yld->urb_irq == NULL) + return usb_cleanup(yld, -ENOMEM); + + yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL); + if (yld->urb_ctl == NULL) + return usb_cleanup(yld, -ENOMEM); + + /* get a handle to the interrupt data pipe */ + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + if (ret != USB_PKT_LEN) + err("invalid payload size %d, expected %zd", ret, USB_PKT_LEN); + + /* initialise irq urb */ + usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data, + USB_PKT_LEN, + urb_irq_callback, + yld, endpoint->bInterval); + yld->urb_irq->transfer_dma = yld->irq_dma; + yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + yld->urb_irq->dev = udev; + + /* initialise ctl urb */ + yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_OUT; + yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION; + yld->ctl_req->wValue = cpu_to_le16(0x200); + yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); + yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN); + + usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0), + (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN, + urb_ctl_callback, yld); + yld->urb_ctl->setup_dma = yld->ctl_req_dma; + yld->urb_ctl->transfer_dma = yld->ctl_dma; + yld->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP | + URB_NO_TRANSFER_DMA_MAP; + yld->urb_ctl->dev = udev; + + /* find out the physical bus location */ + usb_make_path(udev, yld->phys, sizeof(yld->phys)); + strlcat(yld->phys, "/input0", sizeof(yld->phys)); + + /* register settings for the input device */ + input_dev->name = nfo->name; + input_dev->phys = yld->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, yld); + + input_dev->open = input_open; + input_dev->close = input_close; + /* input_dev->event = input_ev; TODO */ + + /* register available key events */ + input_dev->evbit[0] = BIT(EV_KEY); + for (i = 0; i < 256; i++) { + int k = map_p1k_to_key(i); + if (k >= 0) { + set_bit(k & 0xff, input_dev->keybit); + if (k >> 8) + set_bit(k >> 8, input_dev->keybit); + } + } + + ret = input_register_device(yld->idev); + if (ret) + return usb_cleanup(yld, ret); + + usb_set_intfdata(intf, yld); + + /* clear visible elements */ + for (i = 0; i < ARRAY_SIZE(lcdMap); i++) + setChar(yld, i, ' '); + + /* display driver version on LCD line 3 */ + store_line3(&intf->dev, NULL, + DRIVER_VERSION, sizeof(DRIVER_VERSION)); + + /* Register sysfs hooks (don't care about failure) */ + ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group); + return 0; +} + +static struct usb_driver yealink_driver = { + .name = "yealink", + .probe = usb_probe, + .disconnect = usb_disconnect, + .id_table = usb_table, +}; + +static int __init yealink_dev_init(void) +{ + int ret = usb_register(&yealink_driver); + if (ret == 0) + info(DRIVER_DESC ":" DRIVER_VERSION); + return ret; +} + +static void __exit yealink_dev_exit(void) +{ + usb_deregister(&yealink_driver); +} + +module_init(yealink_dev_init); +module_exit(yealink_dev_exit); + +MODULE_DEVICE_TABLE (usb, usb_table); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/yealink.h b/drivers/input/misc/yealink.h new file mode 100644 index 00000000000..48af0be9cbd --- /dev/null +++ b/drivers/input/misc/yealink.h @@ -0,0 +1,220 @@ +/* + * drivers/usb/input/yealink.h + * + * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef INPUT_YEALINK_H +#define INPUT_YEALINK_H + +/* Using the control channel on interface 3 various aspects of the phone + * can be controlled like LCD, LED, dialtone and the ringtone. + */ + +struct yld_ctl_packet { + u8 cmd; /* command code, see below */ + u8 size; /* 1-11, size of used data bytes. */ + u16 offset; /* internal packet offset */ + u8 data[11]; + s8 sum; /* negative sum of 15 preceding bytes */ +} __attribute__ ((packed)); + +#define USB_PKT_LEN sizeof(struct yld_ctl_packet) + +/* The following yld_ctl_packet's are available: */ + +/* Init registers + * + * cmd 0x8e + * size 10 + * offset 0 + * data 0,0,0,0.... + */ +#define CMD_INIT 0x8e + +/* Request key scan + * + * cmd 0x80 + * size 1 + * offset 0 + * data[0] on return returns the key number, if it changes there's a new + * key pressed. + */ +#define CMD_KEYPRESS 0x80 + +/* Request scancode + * + * cmd 0x81 + * size 1 + * offset key number [0-1f] + * data[0] on return returns the scancode + */ +#define CMD_SCANCODE 0x81 + +/* Set LCD + * + * cmd 0x04 + * size 1-11 + * offset 0-23 + * data segment bits + */ +#define CMD_LCD 0x04 + +/* Set led + * + * cmd 0x05 + * size 1 + * offset 0 + * data[0] 0 OFF / 1 ON + */ +#define CMD_LED 0x05 + +/* Set ringtone volume + * + * cmd 0x11 + * size 1 + * offset 0 + * data[0] 0-0xff volume + */ +#define CMD_RING_VOLUME 0x11 + +/* Set ringtone notes + * + * cmd 0x02 + * size 1-11 + * offset 0-> + * data binary representation LE16(-freq), LE16(duration) .... + */ +#define CMD_RING_NOTE 0x02 + +/* Sound ringtone via the speaker on the back + * + * cmd 0x03 + * size 1 + * offset 0 + * data[0] 0 OFF / 0x24 ON + */ +#define CMD_RINGTONE 0x03 + +/* Sound dial tone via the ear speaker + * + * cmd 0x09 + * size 1 + * offset 0 + * data[0] 0 OFF / 1 ON + */ +#define CMD_DIALTONE 0x09 + +#endif /* INPUT_YEALINK_H */ + + +#if defined(_SEG) && defined(_PIC) +/* This table maps the LCD segments onto individual bit positions in the + * yld_status struct. + */ + +/* LCD, each segment must be driven seperately. + * + * Layout: + * + * |[] [][] [][] [][] in |[][] + * |[] M [][] D [][] : [][] out |[][] + * store + * + * NEW REP SU MO TU WE TH FR SA + * + * [] [] [] [] [] [] [] [] [] [] [] [] + * [] [] [] [] [] [] [] [] [] [] [] [] + */ + +/* Line 1 + * Format : 18.e8.M8.88...188 + * Icon names : M D : IN OUT STORE + */ +#define LCD_LINE1_OFFSET 0 +#define LCD_LINE1_SIZE 17 + +/* Note: first g then f => ! ! */ +/* _SEG( type a b c d e g f ) */ + _SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ), + _SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ), + _PIC('.', 22,1 , "M" ), + _SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ), + _SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ), + _PIC('.', 15,8 , "D" ), + _SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ), + _SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ), + _PIC('.', 11,8 , ":" ), + _SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ), + _SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ), + _PIC('.', 7,1 , "IN" ), + _PIC('.', 7,2 , "OUT" ), + _PIC('.', 7,4 , "STORE" ), + _SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ), + _SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ), + _SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ), + +/* Line 2 + * Format : ......... + * Pict. name : NEW REP SU MO TU WE TH FR SA + */ +#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE +#define LCD_LINE2_SIZE 9 + + _PIC('.', 23,2 , "NEW" ), + _PIC('.', 23,4 , "REP" ), + _PIC('.', 1,8 , "SU" ), + _PIC('.', 1,4 , "MO" ), + _PIC('.', 1,2 , "TU" ), + _PIC('.', 1,1 , "WE" ), + _PIC('.', 0,1 , "TH" ), + _PIC('.', 0,2 , "FR" ), + _PIC('.', 0,4 , "SA" ), + +/* Line 3 + * Format : 888888888888 + */ +#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE +#define LCD_LINE3_SIZE 12 + + _SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ), + _SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ), + _SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ), + _SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ), + _SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ), + _SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ), + _SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ), + _SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ), + _SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ), + _SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ), + _SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ), + _SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ), + +/* Line 4 + * + * The LED, DIALTONE and RINGTONE are implemented as icons and use the same + * sysfs interface. + */ +#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE + + _PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ), + _PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ), + _PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ), + +#undef _SEG +#undef _PIC +#endif /* _SEG && _PIC */ diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 35d998c3e57..2ccc114b3ff 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -2,7 +2,7 @@ # Mouse driver configuration # menuconfig INPUT_MOUSE - bool "Mouse" + bool "Mice" default y help Say Y here, and a list of supported mice will be displayed. @@ -19,7 +19,7 @@ config MOUSE_PS2 select SERIO_LIBPS2 select SERIO_I8042 if X86_PC select SERIO_GSCPS2 if GSC - ---help--- + help Say Y here if you have a PS/2 mouse connected to your system. This includes the standard 2 or 3-button PS/2 mouse, as well as PS/2 mice with wheels and extra buttons, Microsoft, Logitech or Genius @@ -37,10 +37,69 @@ config MOUSE_PS2 To compile this driver as a module, choose M here: the module will be called psmouse. +config MOUSE_PS2_ALPS + bool "ALPS PS/2 mouse protocol extension" if EMBEDDED + default y + depends on MOUSE_PS2 + help + Say Y here if you have an ALPS PS/2 touchpad connected to + your system. + + If unsure, say Y. + +config MOUSE_PS2_LOGIPS2PP + bool "Logictech PS/2++ mouse protocol extension" if EMBEDDED + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Logictech PS/2++ mouse connected to + your system. + + If unsure, say Y. + +config MOUSE_PS2_SYNAPTICS + bool "Synaptics PS/2 mouse protocol extension" if EMBEDDED + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Synaptics PS/2 TouchPad connected to + your system. + + If unsure, say Y. + +config MOUSE_PS2_LIFEBOOK + bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Fujitsu B-series Lifebook PS/2 + TouchScreen connected to your system. + + If unsure, say Y. + +config MOUSE_PS2_TRACKPOINT + bool "IBM Trackpoint PS/2 mouse protocol extension" if EMBEDDED + default y + depends on MOUSE_PS2 + help + Say Y here if you have an IBM Trackpoint PS/2 mouse connected + to your system. + + If unsure, say Y. + +config MOUSE_PS2_TOUCHKIT + bool "eGalax TouchKit PS/2 protocol extension" + depends on MOUSE_PS2 + help + Say Y here if you have an eGalax TouchKit PS/2 touchscreen + connected to your system. + + If unsure, say N. + config MOUSE_SERIAL tristate "Serial mouse" select SERIO - ---help--- + help Say Y here if you have a serial (RS-232, COM port) mouse connected to your system. This includes Sun, MouseSystems, Microsoft, Logitech and all other compatible serial mice. @@ -50,6 +109,26 @@ config MOUSE_SERIAL To compile this driver as a module, choose M here: the module will be called sermouse. +config MOUSE_APPLETOUCH + tristate "Apple USB Touchpad support" + select USB + help + Say Y here if you want to use an Apple USB Touchpad. + + These are the touchpads that can be found on post-February 2005 + Apple Powerbooks (prior models have a Synaptics touchpad connected + to the ADB bus). + + This driver provides a basic mouse driver but can be interfaced + with the synaptics X11 driver to provide acceleration and + scrolling in X11. + + For further information, see + <file:Documentation/input/appletouch.txt>. + + To compile this driver as a module, choose M here: the + module will be called appletouch. + config MOUSE_INPORT tristate "InPort/MS/ATIXL busmouse" depends on ISA @@ -96,6 +175,17 @@ config MOUSE_AMIGA To compile this driver as a module, choose M here: the module will be called amimouse. +config MOUSE_ATARI + tristate "Atari mouse" + depends on ATARI + select ATARI_KBD_CORE + help + Say Y here if you have an Atari and want its native mouse + supported by the kernel. + + To compile this driver as a module, choose M here: the + module will be called atarimouse. + config MOUSE_RISCPC tristate "Acorn RiscPC mouse" depends on ARCH_ACORN @@ -118,7 +208,7 @@ config MOUSE_VSXXXAA digitizer (VSXXX-AB) DEC produced. config MOUSE_HIL - tristate "HIL pointers (mice etc)." + tristate "HIL pointers (mice etc)." depends on GSC || HP300 select HP_SDC select HIL_MLC diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 21a1de61a79..aa4ba878533 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -5,6 +5,8 @@ # Each configuration option enables a list of files. obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o +obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o +obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o @@ -14,4 +16,10 @@ obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o -psmouse-objs := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o trackpoint.o +psmouse-objs := psmouse-base.o synaptics.o + +psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o +psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o +psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o +psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o +psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 4e71a66fc7f..cf3e4664e72 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -424,14 +424,15 @@ int alps_init(struct psmouse *psmouse) struct input_dev *dev1 = psmouse->dev, *dev2; int version; - psmouse->private = priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL); + priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL); dev2 = input_allocate_device(); if (!priv || !dev2) goto init_fail; priv->dev2 = dev2; - if (!(priv->i = alps_get_model(psmouse, &version))) + priv->i = alps_get_model(psmouse, &version); + if (!priv->i) goto init_fail; if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1)) @@ -480,7 +481,8 @@ int alps_init(struct psmouse *psmouse) dev2->relbit[LONG(REL_X)] |= BIT(REL_X) | BIT(REL_Y); dev2->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); - input_register_device(priv->dev2); + if (input_register_device(priv->dev2)) + goto init_fail; psmouse->protocol_handler = alps_process_byte; psmouse->poll = alps_poll; @@ -491,9 +493,11 @@ int alps_init(struct psmouse *psmouse) /* We are having trouble resyncing ALPS touchpads so disable it for now */ psmouse->resync_time = 0; + psmouse->private = priv; return 0; init_fail: + psmouse_reset(psmouse); input_free_device(dev2); kfree(priv); return -1; @@ -504,7 +508,8 @@ int alps_detect(struct psmouse *psmouse, int set_properties) int version; const struct alps_model_info *model; - if (!(model = alps_get_model(psmouse, &version))) + model = alps_get_model(psmouse, &version); + if (!model) return -1; if (set_properties) { diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 69db7325a49..4bbddc99962 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -12,9 +12,6 @@ #ifndef _ALPS_H #define _ALPS_H -int alps_detect(struct psmouse *psmouse, int set_properties); -int alps_init(struct psmouse *psmouse); - struct alps_model_info { unsigned char signature[3]; unsigned char byte0, mask0; @@ -23,10 +20,23 @@ struct alps_model_info { struct alps_data { struct input_dev *dev2; /* Relative device */ - char name[32]; /* Name */ char phys[32]; /* Phys */ const struct alps_model_info *i;/* Info */ int prev_fin; /* Finger bit from previous packet */ }; +#ifdef CONFIG_MOUSE_PS2_ALPS +int alps_detect(struct psmouse *psmouse, int set_properties); +int alps_init(struct psmouse *psmouse); +#else +inline int alps_detect(struct psmouse *psmouse, int set_properties) +{ + return -ENOSYS; +} +inline int alps_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_ALPS */ + #endif diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c new file mode 100644 index 00000000000..e3215267db1 --- /dev/null +++ b/drivers/input/mouse/appletouch.c @@ -0,0 +1,706 @@ +/* + * Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) + * Copyright (C) 2005 Stelian Pop (stelian@popies.net) + * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) + * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) + * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) + * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) + * + * Thanks to Alex Harper <basilisk@foobox.net> for his inputs. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb/input.h> + +/* Apple has powerbooks which have the keyboard with different Product IDs */ +#define APPLE_VENDOR_ID 0x05AC + +/* These names come from Info.plist in AppleUSBTrackpad.kext */ +#define FOUNTAIN_ANSI_PRODUCT_ID 0x020E +#define FOUNTAIN_ISO_PRODUCT_ID 0x020F + +#define FOUNTAIN_TP_ONLY_PRODUCT_ID 0x030A + +#define GEYSER1_TP_ONLY_PRODUCT_ID 0x030B + +#define GEYSER_ANSI_PRODUCT_ID 0x0214 +#define GEYSER_ISO_PRODUCT_ID 0x0215 +#define GEYSER_JIS_PRODUCT_ID 0x0216 + +/* MacBook devices */ +#define GEYSER3_ANSI_PRODUCT_ID 0x0217 +#define GEYSER3_ISO_PRODUCT_ID 0x0218 +#define GEYSER3_JIS_PRODUCT_ID 0x0219 + +/* + * Geyser IV: same as Geyser III according to Info.plist in AppleUSBTrackpad.kext + * -> same IOClass (AppleUSBGrIIITrackpad), same acceleration tables + */ +#define GEYSER4_ANSI_PRODUCT_ID 0x021A +#define GEYSER4_ISO_PRODUCT_ID 0x021B +#define GEYSER4_JIS_PRODUCT_ID 0x021C + +#define ATP_DEVICE(prod) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS | \ + USB_DEVICE_ID_MATCH_INT_PROTOCOL, \ + .idVendor = APPLE_VENDOR_ID, \ + .idProduct = (prod), \ + .bInterfaceClass = 0x03, \ + .bInterfaceProtocol = 0x02 + +/* table of devices that work with this driver */ +static struct usb_device_id atp_table [] = { + { ATP_DEVICE(FOUNTAIN_ANSI_PRODUCT_ID) }, + { ATP_DEVICE(FOUNTAIN_ISO_PRODUCT_ID) }, + { ATP_DEVICE(FOUNTAIN_TP_ONLY_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER1_TP_ONLY_PRODUCT_ID) }, + + /* PowerBooks Oct 2005 */ + { ATP_DEVICE(GEYSER_ANSI_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) }, + + /* Core Duo MacBook & MacBook Pro */ + { ATP_DEVICE(GEYSER3_ANSI_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER3_ISO_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER3_JIS_PRODUCT_ID) }, + + /* Core2 Duo MacBook & MacBook Pro */ + { ATP_DEVICE(GEYSER4_ANSI_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER4_ISO_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER4_JIS_PRODUCT_ID) }, + + /* Terminating entry */ + { } +}; +MODULE_DEVICE_TABLE (usb, atp_table); + +/* + * number of sensors. Note that only 16 instead of 26 X (horizontal) + * sensors exist on 12" and 15" PowerBooks. All models have 16 Y + * (vertical) sensors. + */ +#define ATP_XSENSORS 26 +#define ATP_YSENSORS 16 + +/* amount of fuzz this touchpad generates */ +#define ATP_FUZZ 16 + +/* maximum pressure this driver will report */ +#define ATP_PRESSURE 300 +/* + * multiplication factor for the X and Y coordinates. + * We try to keep the touchpad aspect ratio while still doing only simple + * arithmetics. + * The factors below give coordinates like: + * 0 <= x < 960 on 12" and 15" Powerbooks + * 0 <= x < 1600 on 17" Powerbooks + * 0 <= y < 646 + */ +#define ATP_XFACT 64 +#define ATP_YFACT 43 + +/* + * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is + * ignored. + */ +#define ATP_THRESHOLD 5 + +/* MacBook Pro (Geyser 3 & 4) initialization constants */ +#define ATP_GEYSER3_MODE_READ_REQUEST_ID 1 +#define ATP_GEYSER3_MODE_WRITE_REQUEST_ID 9 +#define ATP_GEYSER3_MODE_REQUEST_VALUE 0x300 +#define ATP_GEYSER3_MODE_REQUEST_INDEX 0 +#define ATP_GEYSER3_MODE_VENDOR_VALUE 0x04 + +/* Structure to hold all of our device specific stuff */ +struct atp { + char phys[64]; + struct usb_device * udev; /* usb device */ + struct urb * urb; /* usb request block */ + signed char * data; /* transferred data */ + int open; /* non-zero if opened */ + struct input_dev *input; /* input dev */ + int valid; /* are the sensors valid ? */ + int x_old; /* last reported x/y, */ + int y_old; /* used for smoothing */ + /* current value of the sensors */ + signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS]; + /* last value of the sensors */ + signed char xy_old[ATP_XSENSORS + ATP_YSENSORS]; + /* accumulated sensors */ + int xy_acc[ATP_XSENSORS + ATP_YSENSORS]; + int overflowwarn; /* overflow warning printed? */ + int datalen; /* size of an USB urb transfer */ +}; + +#define dbg_dump(msg, tab) \ + if (debug > 1) { \ + int i; \ + printk("appletouch: %s %lld", msg, (long long)jiffies); \ + for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) \ + printk(" %02x", tab[i]); \ + printk("\n"); \ + } + +#define dprintk(format, a...) \ + do { \ + if (debug) printk(format, ##a); \ + } while (0) + +MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann"); +MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver"); +MODULE_LICENSE("GPL"); + +/* + * Make the threshold a module parameter + */ +static int threshold = ATP_THRESHOLD; +module_param(threshold, int, 0644); +MODULE_PARM_DESC(threshold, "Discards any change in data from a sensor (trackpad has hundreds of these sensors) less than this value"); + +static int debug = 1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activate debugging output"); + +/* Checks if the device a Geyser 2 (ANSI, ISO, JIS) */ +static inline int atp_is_geyser_2(struct atp *dev) +{ + u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct); + + return (productId == GEYSER_ANSI_PRODUCT_ID) || + (productId == GEYSER_ISO_PRODUCT_ID) || + (productId == GEYSER_JIS_PRODUCT_ID); +} + +static inline int atp_is_geyser_3(struct atp *dev) +{ + u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct); + + return (productId == GEYSER3_ANSI_PRODUCT_ID) || + (productId == GEYSER3_ISO_PRODUCT_ID) || + (productId == GEYSER3_JIS_PRODUCT_ID) || + (productId == GEYSER4_ANSI_PRODUCT_ID) || + (productId == GEYSER4_ISO_PRODUCT_ID) || + (productId == GEYSER4_JIS_PRODUCT_ID); +} + +static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, + int *z, int *fingers) +{ + int i; + /* values to calculate mean */ + int pcum = 0, psum = 0; + int is_increasing = 0; + + *fingers = 0; + + for (i = 0; i < nb_sensors; i++) { + if (xy_sensors[i] < threshold) { + if (is_increasing) + is_increasing = 0; + + continue; + } + + /* + * Makes the finger detection more versatile. For example, + * two fingers with no gap will be detected. Also, my + * tests show it less likely to have intermittent loss + * of multiple finger readings while moving around (scrolling). + * + * Changes the multiple finger detection to counting humps on + * sensors (transitions from nonincreasing to increasing) + * instead of counting transitions from low sensors (no + * finger reading) to high sensors (finger above + * sensor) + * + * - Jason Parekh <jasonparekh@gmail.com> + */ + if (i < 1 || (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) { + (*fingers)++; + is_increasing = 1; + } else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) { + is_increasing = 0; + } + + /* + * Subtracts threshold so a high sensor that just passes the threshold + * won't skew the calculated absolute coordinate. Fixes an issue + * where slowly moving the mouse would occassionaly jump a number of + * pixels (let me restate--slowly moving the mouse makes this issue + * most apparent). + */ + pcum += (xy_sensors[i] - threshold) * i; + psum += (xy_sensors[i] - threshold); + } + + if (psum > 0) { + *z = psum; + return pcum * fact / psum; + } + + return 0; +} + +static inline void atp_report_fingers(struct input_dev *input, int fingers) +{ + input_report_key(input, BTN_TOOL_FINGER, fingers == 1); + input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2); +} + +static void atp_complete(struct urb* urb) +{ + int x, y, x_z, y_z, x_f, y_f; + int retval, i, j; + struct atp *dev = urb->context; + + switch (urb->status) { + case 0: + /* success */ + break; + case -EOVERFLOW: + if(!dev->overflowwarn) { + printk("appletouch: OVERFLOW with data " + "length %d, actual length is %d\n", + dev->datalen, dev->urb->actual_length); + dev->overflowwarn = 1; + } + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* This urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + /* drop incomplete datasets */ + if (dev->urb->actual_length != dev->datalen) { + dprintk("appletouch: incomplete data package" + " (first byte: %d, length: %d).\n", + dev->data[0], dev->urb->actual_length); + goto exit; + } + + /* reorder the sensors values */ + if (atp_is_geyser_3(dev)) { + memset(dev->xy_cur, 0, sizeof(dev->xy_cur)); + + /* + * The values are laid out like this: + * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ... + * '-' is an unused value. + */ + + /* read X values */ + for (i = 0, j = 19; i < 20; i += 2, j += 3) { + dev->xy_cur[i] = dev->data[j + 1]; + dev->xy_cur[i + 1] = dev->data[j + 2]; + } + /* read Y values */ + for (i = 0, j = 1; i < 9; i += 2, j += 3) { + dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1]; + dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2]; + } + } else if (atp_is_geyser_2(dev)) { + memset(dev->xy_cur, 0, sizeof(dev->xy_cur)); + + /* + * The values are laid out like this: + * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ... + * '-' is an unused value. + */ + + /* read X values */ + for (i = 0, j = 19; i < 20; i += 2, j += 3) { + dev->xy_cur[i] = dev->data[j]; + dev->xy_cur[i + 1] = dev->data[j + 1]; + } + + /* read Y values */ + for (i = 0, j = 1; i < 9; i += 2, j += 3) { + dev->xy_cur[ATP_XSENSORS + i] = dev->data[j]; + dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1]; + } + } else { + for (i = 0; i < 8; i++) { + /* X values */ + dev->xy_cur[i ] = dev->data[5 * i + 2]; + dev->xy_cur[i + 8] = dev->data[5 * i + 4]; + dev->xy_cur[i + 16] = dev->data[5 * i + 42]; + if (i < 2) + dev->xy_cur[i + 24] = dev->data[5 * i + 44]; + + /* Y values */ + dev->xy_cur[i + 26] = dev->data[5 * i + 1]; + dev->xy_cur[i + 34] = dev->data[5 * i + 3]; + } + } + + dbg_dump("sample", dev->xy_cur); + + if (!dev->valid) { + /* first sample */ + dev->valid = 1; + dev->x_old = dev->y_old = -1; + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); + + if (atp_is_geyser_3(dev)) /* No 17" Macbooks (yet) */ + goto exit; + + /* 17" Powerbooks have extra X sensors */ + for (i = (atp_is_geyser_2(dev)?15:16); i < ATP_XSENSORS; i++) { + if (!dev->xy_cur[i]) continue; + + printk("appletouch: 17\" model detected.\n"); + if(atp_is_geyser_2(dev)) + input_set_abs_params(dev->input, ABS_X, 0, + (20 - 1) * + ATP_XFACT - 1, + ATP_FUZZ, 0); + else + input_set_abs_params(dev->input, ABS_X, 0, + (ATP_XSENSORS - 1) * + ATP_XFACT - 1, + ATP_FUZZ, 0); + + break; + } + + goto exit; + } + + for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) { + /* accumulate the change */ + signed char change = dev->xy_old[i] - dev->xy_cur[i]; + dev->xy_acc[i] -= change; + + /* prevent down drifting */ + if (dev->xy_acc[i] < 0) + dev->xy_acc[i] = 0; + } + + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); + + dbg_dump("accumulator", dev->xy_acc); + + x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + ATP_XFACT, &x_z, &x_f); + y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + ATP_YFACT, &y_z, &y_f); + + if (x && y) { + if (dev->x_old != -1) { + x = (dev->x_old * 3 + x) >> 2; + y = (dev->y_old * 3 + y) >> 2; + dev->x_old = x; + dev->y_old = y; + + if (debug > 1) + printk("appletouch: X: %3d Y: %3d " + "Xz: %3d Yz: %3d\n", + x, y, x_z, y_z); + + input_report_key(dev->input, BTN_TOUCH, 1); + input_report_abs(dev->input, ABS_X, x); + input_report_abs(dev->input, ABS_Y, y); + input_report_abs(dev->input, ABS_PRESSURE, + min(ATP_PRESSURE, x_z + y_z)); + atp_report_fingers(dev->input, max(x_f, y_f)); + } + dev->x_old = x; + dev->y_old = y; + } + else if (!x && !y) { + + dev->x_old = dev->y_old = -1; + input_report_key(dev->input, BTN_TOUCH, 0); + input_report_abs(dev->input, ABS_PRESSURE, 0); + atp_report_fingers(dev->input, 0); + + /* reset the accumulator on release */ + memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); + } + + input_report_key(dev->input, BTN_LEFT, + !!dev->data[dev->datalen - 1]); + + input_sync(dev->input); + +exit: + retval = usb_submit_urb(dev->urb, GFP_ATOMIC); + if (retval) { + err("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); + } +} + +static int atp_open(struct input_dev *input) +{ + struct atp *dev = input_get_drvdata(input); + + if (usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + + dev->open = 1; + return 0; +} + +static void atp_close(struct input_dev *input) +{ + struct atp *dev = input_get_drvdata(input); + + usb_kill_urb(dev->urb); + dev->open = 0; +} + +static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id) +{ + struct atp *dev; + struct input_dev *input_dev; + struct usb_device *udev = interface_to_usbdev(iface); + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int int_in_endpointAddr = 0; + int i, error = -ENOMEM; + + /* set up the endpoint information */ + /* use only the first interrupt-in endpoint */ + iface_desc = iface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) { + /* we found an interrupt in endpoint */ + int_in_endpointAddr = endpoint->bEndpointAddress; + break; + } + } + if (!int_in_endpointAddr) { + err("Could not find int-in endpoint"); + return -EIO; + } + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(struct atp), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!dev || !input_dev) { + err("Out of memory"); + goto err_free_devs; + } + + dev->udev = udev; + dev->input = input_dev; + dev->overflowwarn = 0; + if (atp_is_geyser_3(dev)) + dev->datalen = 64; + else if (atp_is_geyser_2(dev)) + dev->datalen = 64; + else + dev->datalen = 81; + + if (atp_is_geyser_3(dev)) { + /* + * By default Geyser 3 device sends standard USB HID mouse + * packets (Report ID 2). This code changes device mode, so it + * sends raw sensor reports (Report ID 5). + */ + char data[8]; + int size; + + size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + ATP_GEYSER3_MODE_READ_REQUEST_ID, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ATP_GEYSER3_MODE_REQUEST_VALUE, + ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000); + + if (size != 8) { + err("Could not do mode read request from device" + " (Geyser 3 mode)"); + goto err_free_devs; + } + + /* Apply the mode switch */ + data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE; + + size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + ATP_GEYSER3_MODE_WRITE_REQUEST_ID, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ATP_GEYSER3_MODE_REQUEST_VALUE, + ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000); + + if (size != 8) { + err("Could not do mode write request to device" + " (Geyser 3 mode)"); + goto err_free_devs; + } + printk("appletouch Geyser 3 inited.\n"); + } + + dev->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb) + goto err_free_devs; + + dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL, + &dev->urb->transfer_dma); + if (!dev->data) + goto err_free_urb; + + usb_fill_int_urb(dev->urb, udev, + usb_rcvintpipe(udev, int_in_endpointAddr), + dev->data, dev->datalen, atp_complete, dev, 1); + + usb_make_path(udev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + input_dev->name = "appletouch"; + input_dev->phys = dev->phys; + usb_to_input_id(dev->udev, &input_dev->id); + input_dev->dev.parent = &iface->dev; + + input_set_drvdata(input_dev, dev); + + input_dev->open = atp_open; + input_dev->close = atp_close; + + set_bit(EV_ABS, input_dev->evbit); + + if (atp_is_geyser_3(dev)) { + /* + * MacBook have 20 X sensors, 10 Y sensors + */ + input_set_abs_params(input_dev, ABS_X, 0, + ((20 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0); + input_set_abs_params(input_dev, ABS_Y, 0, + ((10 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0); + } else if (atp_is_geyser_2(dev)) { + /* + * Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected + * later. + */ + input_set_abs_params(input_dev, ABS_X, 0, + ((15 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0); + input_set_abs_params(input_dev, ABS_Y, 0, + ((9 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0); + } else { + /* + * 12" and 15" Powerbooks only have 16 x sensors, + * 17" models are detected later. + */ + input_set_abs_params(input_dev, ABS_X, 0, + (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0); + input_set_abs_params(input_dev, ABS_Y, 0, + (ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0); + } + input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0); + + set_bit(EV_KEY, input_dev->evbit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_TOOL_FINGER, input_dev->keybit); + set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + set_bit(BTN_LEFT, input_dev->keybit); + + error = input_register_device(dev->input); + if (error) + goto err_free_buffer; + + /* save our data pointer in this interface device */ + usb_set_intfdata(iface, dev); + + return 0; + + err_free_buffer: + usb_buffer_free(dev->udev, dev->datalen, + dev->data, dev->urb->transfer_dma); + err_free_urb: + usb_free_urb(dev->urb); + err_free_devs: + usb_set_intfdata(iface, NULL); + kfree(dev); + input_free_device(input_dev); + return error; +} + +static void atp_disconnect(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + + usb_set_intfdata(iface, NULL); + if (dev) { + usb_kill_urb(dev->urb); + input_unregister_device(dev->input); + usb_buffer_free(dev->udev, dev->datalen, + dev->data, dev->urb->transfer_dma); + usb_free_urb(dev->urb); + kfree(dev); + } + printk(KERN_INFO "input: appletouch disconnected\n"); +} + +static int atp_suspend(struct usb_interface *iface, pm_message_t message) +{ + struct atp *dev = usb_get_intfdata(iface); + usb_kill_urb(dev->urb); + dev->valid = 0; + return 0; +} + +static int atp_resume(struct usb_interface *iface) +{ + struct atp *dev = usb_get_intfdata(iface); + if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + + return 0; +} + +static struct usb_driver atp_driver = { + .name = "appletouch", + .probe = atp_probe, + .disconnect = atp_disconnect, + .suspend = atp_suspend, + .resume = atp_resume, + .id_table = atp_table, +}; + +static int __init atp_init(void) +{ + return usb_register(&atp_driver); +} + +static void __exit atp_exit(void) +{ + usb_deregister(&atp_driver); +} + +module_init(atp_init); +module_exit(atp_exit); diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c new file mode 100644 index 00000000000..43ab6566fb6 --- /dev/null +++ b/drivers/input/mouse/atarimouse.c @@ -0,0 +1,160 @@ +/* + * Atari mouse driver for Linux/m68k + * + * Copyright (c) 2005 Michael Schmitz + * + * Based on: + * Amiga mouse driver for Linux/m68k + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * + */ +/* + * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c + * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard + * interrupt is shared with the MIDI ACIA so MIDI data also get handled there). + * This driver only deals with handing key events off to the input layer. + * + * Largely based on the old: + * + * Atari Mouse Driver for Linux + * by Robert de Vries (robert@and.nl) 19Jul93 + * + * 16 Nov 1994 Andreas Schwab + * Compatibility with busmouse + * Support for three button mouse (shamelessly stolen from MiNT) + * third button wired to one of the joystick directions on joystick 1 + * + * 1996/02/11 Andreas Schwab + * Module support + * Allow multiple open's + * + * Converted to use new generic busmouse code. 5 Apr 1998 + * Russell King <rmk@arm.uk.linux.org> + */ + + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> + +#include <asm/irq.h> +#include <asm/setup.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/atarihw.h> +#include <asm/atarikb.h> +#include <asm/atariints.h> + +MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>"); +MODULE_DESCRIPTION("Atari mouse driver"); +MODULE_LICENSE("GPL"); + +static int mouse_threshold[2] = {2,2}; + +#ifdef __MODULE__ +MODULE_PARM(mouse_threshold, "2i"); +#endif +#ifdef FIXED_ATARI_JOYSTICK +extern int atari_mouse_buttons; +#endif +static int atamouse_used = 0; + +static struct input_dev *atamouse_dev; + +static void atamouse_interrupt(char *buf) +{ + int buttons, dx, dy; + +/* ikbd_mouse_disable(); */ + + buttons = (buf[0] & 1) | ((buf[0] & 2) << 1); +#ifdef FIXED_ATARI_JOYSTICK + buttons |= atari_mouse_buttons & 2; + atari_mouse_buttons = buttons; +#endif +/* ikbd_mouse_rel_pos(); */ + + /* only relative events get here */ + dx = buf[1]; + dy = -buf[2]; + + input_report_rel(atamouse_dev, REL_X, dx); + input_report_rel(atamouse_dev, REL_Y, dy); + + input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x1); + input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2); + input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x4); + + input_sync(atamouse_dev); + + return; +} + +static int atamouse_open(struct input_dev *dev) +{ + if (atamouse_used++) + return 0; + +#ifdef FIXED_ATARI_JOYSTICK + atari_mouse_buttons = 0; +#endif + ikbd_mouse_y0_top(); + ikbd_mouse_thresh(mouse_threshold[0], mouse_threshold[1]); + ikbd_mouse_rel_pos(); + atari_input_mouse_interrupt_hook = atamouse_interrupt; + return 0; +} + +static void atamouse_close(struct input_dev *dev) +{ + if (!--atamouse_used) { + ikbd_mouse_disable(); + atari_mouse_interrupt_hook = NULL; + } +} + +static int __init atamouse_init(void) +{ + if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP)) + return -ENODEV; + + if (!(atamouse_dev = input_allocate_device())) + return -ENOMEM; + + if (!(atari_keyb_init())) + return -ENODEV; + + atamouse_dev->name = "Atari mouse"; + atamouse_dev->phys = "atamouse/input0"; + atamouse_dev->id.bustype = BUS_ATARI; + atamouse_dev->id.vendor = 0x0001; + atamouse_dev->id.product = 0x0002; + atamouse_dev->id.version = 0x0100; + + atamouse_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + atamouse_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); + atamouse_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + atamouse_dev->open = atamouse_open; + atamouse_dev->close = atamouse_close; + + input_register_device(atamouse_dev); + + printk(KERN_INFO "input: %s at keyboard ACIA\n", atamouse_dev->name); + return 0; +} + +static void __exit atamouse_exit(void) +{ + input_unregister_device(atamouse_dev); +} + +module_init(atamouse_init); +module_exit(atamouse_exit); diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c index bfb174fe323..449bf4dcbbc 100644 --- a/drivers/input/mouse/hil_ptr.c +++ b/drivers/input/mouse/hil_ptr.c @@ -88,10 +88,12 @@ static void hil_ptr_process_record(struct hil_ptr *ptr) idx = ptr->idx4/4; p = data[idx - 1]; - if ((p & ~HIL_CMDCT_POL) == - (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report; - if ((p & ~HIL_CMDCT_RPL) == - (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report; + if ((p & ~HIL_CMDCT_POL) == + (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) + goto report; + if ((p & ~HIL_CMDCT_RPL) == + (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) + goto report; /* Not a poll response. See if we are loading config records. */ switch (p & HIL_PKT_DATA_MASK) { @@ -101,27 +103,32 @@ static void hil_ptr_process_record(struct hil_ptr *ptr) for (; i < HIL_PTR_MAX_LENGTH; i++) ptr->idd[i] = 0; break; + case HIL_CMD_RSC: for (i = 0; i < idx; i++) ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK; for (; i < HIL_PTR_MAX_LENGTH; i++) ptr->rsc[i] = 0; break; + case HIL_CMD_EXD: for (i = 0; i < idx; i++) ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK; for (; i < HIL_PTR_MAX_LENGTH; i++) ptr->exd[i] = 0; break; + case HIL_CMD_RNM: for (i = 0; i < idx; i++) ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK; for (; i < HIL_PTR_MAX_LENGTH + 1; i++) - ptr->rnm[i] = '\0'; + ptr->rnm[i] = 0; break; + default: /* These occur when device isn't present */ - if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break; + if (p == (HIL_ERR_INT | HIL_PKT_CMD)) + break; /* Anything else we'd like to know about. */ printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p); break; @@ -130,7 +137,8 @@ static void hil_ptr_process_record(struct hil_ptr *ptr) report: if ((p & HIL_CMDCT_POL) != idx - 1) { - printk(KERN_WARNING PREFIX "Malformed poll packet %x (idx = %i)\n", p, idx); + printk(KERN_WARNING PREFIX + "Malformed poll packet %x (idx = %i)\n", p, idx); goto out; } @@ -139,7 +147,7 @@ static void hil_ptr_process_record(struct hil_ptr *ptr) laxis += i; ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */ - absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; + absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; for (cnt = 1; i < laxis; i++) { unsigned int lo,hi,val; @@ -157,7 +165,8 @@ static void hil_ptr_process_record(struct hil_ptr *ptr) input_report_abs(dev, ABS_X + i, val); } else { val = (int) (((int8_t)lo) | ((int8_t)hi<<8)); - if (i%3) val *= -1; + if (i%3) + val *= -1; input_report_rel(dev, REL_X + i, val); } } @@ -168,10 +177,11 @@ static void hil_ptr_process_record(struct hil_ptr *ptr) btn = ptr->data[cnt++]; up = btn & 1; btn &= 0xfe; - if (btn == 0x8e) { + if (btn == 0x8e) continue; /* TODO: proximity == touch? */ - } - else if ((btn > 0x8c) || (btn < 0x80)) continue; + else + if ((btn > 0x8c) || (btn < 0x80)) + continue; btn = (btn - 0x80) >> 1; btn = ptr->btnmap[btn]; input_report_key(dev, btn, !up); @@ -182,14 +192,14 @@ static void hil_ptr_process_record(struct hil_ptr *ptr) up(&ptr->sem); } -static void hil_ptr_process_err(struct hil_ptr *ptr) { +static void hil_ptr_process_err(struct hil_ptr *ptr) +{ printk(KERN_WARNING PREFIX "errored HIL packet\n"); ptr->idx4 = 0; up(&ptr->sem); - return; } -static irqreturn_t hil_ptr_interrupt(struct serio *serio, +static irqreturn_t hil_ptr_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { struct hil_ptr *ptr; @@ -197,29 +207,29 @@ static irqreturn_t hil_ptr_interrupt(struct serio *serio, int idx; ptr = serio_get_drvdata(serio); - if (ptr == NULL) { - BUG(); - return IRQ_HANDLED; - } + BUG_ON(ptr == NULL); if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) { hil_ptr_process_err(ptr); return IRQ_HANDLED; } idx = ptr->idx4/4; - if (!(ptr->idx4 % 4)) ptr->data[idx] = 0; + if (!(ptr->idx4 % 4)) + ptr->data[idx] = 0; packet = ptr->data[idx]; packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8); ptr->data[idx] = packet; /* Records of N 4-byte hil_packets must terminate with a command. */ - if ((++(ptr->idx4)) % 4) return IRQ_HANDLED; + if ((++(ptr->idx4)) % 4) + return IRQ_HANDLED; if ((packet & 0xffff0000) != HIL_ERR_INT) { hil_ptr_process_err(ptr); return IRQ_HANDLED; } - if (packet & HIL_PKT_CMD) + if (packet & HIL_PKT_CMD) hil_ptr_process_record(ptr); + return IRQ_HANDLED; } @@ -228,10 +238,7 @@ static void hil_ptr_disconnect(struct serio *serio) struct hil_ptr *ptr; ptr = serio_get_drvdata(serio); - if (ptr == NULL) { - BUG(); - return; - } + BUG_ON(ptr == NULL); serio_close(serio); input_unregister_device(ptr->dev); @@ -241,7 +248,7 @@ static void hil_ptr_disconnect(struct serio *serio) static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) { struct hil_ptr *ptr; - char *txt; + const char *txt; unsigned int i, naxsets, btntype; uint8_t did, *idd; @@ -252,42 +259,40 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) if (!ptr->dev) goto bail0; - ptr->dev->private = ptr; - if (serio_open(serio, driver)) goto bail1; serio_set_drvdata(serio, ptr); ptr->serio = serio; - init_MUTEX_LOCKED(&(ptr->sem)); + init_MUTEX_LOCKED(&ptr->sem); /* Get device info. MLC driver supplies devid/status/etc. */ serio->write(serio, 0); serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_IDD); - down(&(ptr->sem)); + down(&ptr->sem); serio->write(serio, 0); serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_RSC); - down(&(ptr->sem)); + down(&ptr->sem); serio->write(serio, 0); serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_RNM); - down(&(ptr->sem)); + down(&ptr->sem); serio->write(serio, 0); serio->write(serio, 0); serio->write(serio, HIL_PKT_CMD >> 8); serio->write(serio, HIL_CMD_EXD); - down(&(ptr->sem)); + down(&ptr->sem); - up(&(ptr->sem)); + up(&ptr->sem); did = ptr->idd[0]; idd = ptr->idd + 1; @@ -301,12 +306,12 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) ptr->dev->evbit[0] = BIT(EV_ABS); txt = "absolute"; } - if (!ptr->dev->evbit[0]) { + if (!ptr->dev->evbit[0]) goto bail2; - } ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd); - if (ptr->nbtn) ptr->dev->evbit[0] |= BIT(EV_KEY); + if (ptr->nbtn) + ptr->dev->evbit[0] |= BIT(EV_KEY); naxsets = HIL_IDD_NUM_AXSETS(*idd); ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd); @@ -315,7 +320,7 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) did, txt); printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n", ptr->nbtn, naxsets, ptr->naxes); - + btntype = BTN_MISC; if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET) #ifdef TABLET_SIMULATES_MOUSE @@ -325,7 +330,7 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) #endif if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN) btntype = BTN_TOUCH; - + if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE) btntype = BTN_MOUSE; @@ -341,12 +346,10 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) } if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { - for (i = 0; i < ptr->naxes; i++) { + for (i = 0; i < ptr->naxes; i++) set_bit(REL_X + i, ptr->dev->relbit); - } - for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) { + for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) set_bit(REL_X + i, ptr->dev->relbit); - } } else { for (i = 0; i < ptr->naxes; i++) { set_bit(ABS_X + i, ptr->dev->absbit); @@ -375,7 +378,7 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) ptr->dev->id.vendor = PCI_VENDOR_ID_HP; ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */ ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */ - ptr->dev->cdev.dev = &serio->dev; + ptr->dev->dev.parent = &serio->dev; input_register_device(ptr->dev); printk(KERN_INFO "input: %s (%s), ID: %d\n", @@ -419,11 +422,11 @@ static int __init hil_ptr_init(void) { return serio_register_driver(&hil_ptr_serio_driver); } - + static void __exit hil_ptr_exit(void) { serio_unregister_driver(&hil_ptr_serio_driver); } - + module_init(hil_ptr_init); module_exit(hil_ptr_exit); diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 29542f0631c..1740cadd959 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -20,6 +20,27 @@ #include "psmouse.h" #include "lifebook.h" +struct lifebook_data { + struct input_dev *dev2; /* Relative device */ + char phys[32]; +}; + +static const char *desired_serio_phys; + +static int lifebook_set_serio_phys(struct dmi_system_id *d) +{ + desired_serio_phys = d->driver_data; + return 0; +} + +static unsigned char lifebook_use_6byte_proto; + +static int lifebook_set_6byte_proto(struct dmi_system_id *d) +{ + lifebook_use_6byte_proto = 1; + return 0; +} + static struct dmi_system_id lifebook_dmi_table[] = { { .ident = "FLORA-ie 55mi", @@ -56,6 +77,24 @@ static struct dmi_system_id lifebook_dmi_table[] = { .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"), }, + .callback = lifebook_set_serio_phys, + .driver_data = "isa0060/serio3", + }, + { + .ident = "Panasonic CF-28", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"), + }, + .callback = lifebook_set_6byte_proto, + }, + { + .ident = "Panasonic CF-29", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), + }, + .callback = lifebook_set_6byte_proto, }, { .ident = "Lifebook B142", @@ -68,30 +107,70 @@ static struct dmi_system_id lifebook_dmi_table[] = { static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) { + struct lifebook_data *priv = psmouse->private; + struct input_dev *dev1 = psmouse->dev; + struct input_dev *dev2 = priv->dev2; unsigned char *packet = psmouse->packet; - struct input_dev *dev = psmouse->dev; + int relative_packet = packet[0] & 0x08; - if (psmouse->pktcnt != 3) - return PSMOUSE_GOOD_DATA; + if (relative_packet || !lifebook_use_6byte_proto) { + if (psmouse->pktcnt != 3) + return PSMOUSE_GOOD_DATA; + } else { + switch (psmouse->pktcnt) { + case 1: + return (packet[0] & 0xf8) == 0x00 ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + case 2: + return PSMOUSE_GOOD_DATA; + case 3: + return ((packet[2] & 0x30) << 2) == (packet[2] & 0xc0) ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + case 4: + return (packet[3] & 0xf8) == 0xc0 ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + case 5: + return (packet[4] & 0xc0) == (packet[2] & 0xc0) ? + PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; + case 6: + if (((packet[5] & 0x30) << 2) != (packet[5] & 0xc0)) + return PSMOUSE_BAD_DATA; + if ((packet[5] & 0xc0) != (packet[1] & 0xc0)) + return PSMOUSE_BAD_DATA; + break; /* report data */ + } + } - /* calculate X and Y */ - if ((packet[0] & 0x08) == 0x00) { - input_report_abs(dev, ABS_X, + if (relative_packet) { + if (!dev2) + printk(KERN_WARNING "lifebook.c: got relative packet " + "but no relative device set up\n"); + } else if (lifebook_use_6byte_proto) { + input_report_abs(dev1, ABS_X, + ((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f)); + input_report_abs(dev1, ABS_Y, + 4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f))); + } else { + input_report_abs(dev1, ABS_X, (packet[1] | ((packet[0] & 0x30) << 4))); - input_report_abs(dev, ABS_Y, + input_report_abs(dev1, ABS_Y, 1024 - (packet[2] | ((packet[0] & 0xC0) << 2))); - } else { - input_report_rel(dev, REL_X, - ((packet[0] & 0x10) ? packet[1] - 256 : packet[1])); - input_report_rel(dev, REL_Y, - -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2])); } - input_report_key(dev, BTN_LEFT, packet[0] & 0x01); - input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); - input_report_key(dev, BTN_TOUCH, packet[0] & 0x04); + input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04); + input_sync(dev1); - input_sync(dev); + if (dev2) { + if (relative_packet) { + input_report_rel(dev2, REL_X, + ((packet[0] & 0x10) ? packet[1] - 256 : packet[1])); + input_report_rel(dev2, REL_Y, + -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2])); + } + input_report_key(dev2, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02); + input_sync(dev2); + } return PSMOUSE_FULL_PACKET; } @@ -109,12 +188,20 @@ static int lifebook_absolute_mode(struct psmouse *psmouse) you leave this call out the touchsreen will never send absolute coordinates */ - param = 0x07; + param = lifebook_use_6byte_proto ? 0x08 : 0x07; ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); return 0; } +static void lifebook_relative_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param = 0x06; + + ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); +} + static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) { static const unsigned char params[] = { 0, 1, 2, 2, 3 }; @@ -131,6 +218,8 @@ static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolu static void lifebook_disconnect(struct psmouse *psmouse) { psmouse_reset(psmouse); + kfree(psmouse->private); + psmouse->private = NULL; } int lifebook_detect(struct psmouse *psmouse, int set_properties) @@ -138,6 +227,10 @@ int lifebook_detect(struct psmouse *psmouse, int set_properties) if (!dmi_check_system(lifebook_dmi_table)) return -1; + if (desired_serio_phys && + strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) + return -1; + if (set_properties) { psmouse->vendor = "Fujitsu"; psmouse->name = "Lifebook TouchScreen"; @@ -146,24 +239,78 @@ int lifebook_detect(struct psmouse *psmouse, int set_properties) return 0; } +static int lifebook_create_relative_device(struct psmouse *psmouse) +{ + struct input_dev *dev2; + struct lifebook_data *priv; + int error = -ENOMEM; + + priv = kzalloc(sizeof(struct lifebook_data), GFP_KERNEL); + dev2 = input_allocate_device(); + if (!priv || !dev2) + goto err_out; + + priv->dev2 = dev2; + snprintf(priv->phys, sizeof(priv->phys), + "%s/input1", psmouse->ps2dev.serio->phys); + + dev2->phys = priv->phys; + dev2->name = "PS/2 Touchpad"; + dev2->id.bustype = BUS_I8042; + dev2->id.vendor = 0x0002; + dev2->id.product = PSMOUSE_LIFEBOOK; + dev2->id.version = 0x0000; + dev2->dev.parent = &psmouse->ps2dev.serio->dev; + + dev2->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + dev2->relbit[LONG(REL_X)] = BIT(REL_X) | BIT(REL_Y); + dev2->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); + + error = input_register_device(priv->dev2); + if (error) + goto err_out; + + psmouse->private = priv; + return 0; + + err_out: + input_free_device(dev2); + kfree(priv); + return error; +} + int lifebook_init(struct psmouse *psmouse) { - struct input_dev *input_dev = psmouse->dev; + struct input_dev *dev1 = psmouse->dev; + int max_coord = lifebook_use_6byte_proto ? 1024 : 4096; if (lifebook_absolute_mode(psmouse)) return -1; - input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL); - input_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); - input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); - input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); - input_set_abs_params(input_dev, ABS_X, 0, 1024, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, 1024, 0, 0); + dev1->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); + dev1->relbit[0] = 0; + dev1->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0); + + if (!desired_serio_phys) { + if (lifebook_create_relative_device(psmouse)) { + lifebook_relative_mode(psmouse); + return -1; + } + } psmouse->protocol_handler = lifebook_process_byte; psmouse->set_resolution = lifebook_set_resolution; psmouse->disconnect = lifebook_disconnect; psmouse->reconnect = lifebook_absolute_mode; + + psmouse->model = lifebook_use_6byte_proto ? 6 : 3; + + /* + * Use packet size = 3 even when using 6-byte protocol because + * that's what POLL will return on Lifebooks (according to spec). + */ psmouse->pktsize = 3; return 0; diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h index be1c0943825..c1647cf036c 100644 --- a/drivers/input/mouse/lifebook.h +++ b/drivers/input/mouse/lifebook.h @@ -11,7 +11,18 @@ #ifndef _LIFEBOOK_H #define _LIFEBOOK_H +#ifdef CONFIG_MOUSE_PS2_LIFEBOOK int lifebook_detect(struct psmouse *psmouse, int set_properties); int lifebook_init(struct psmouse *psmouse); +#else +inline int lifebook_detect(struct psmouse *psmouse, int set_properties) +{ + return -ENOSYS; +} +inline int lifebook_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif #endif diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index d3ddea26b8c..9df74b72e6c 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -200,6 +200,7 @@ static void ps2pp_disconnect(struct psmouse *psmouse) static const struct ps2pp_info *get_model_info(unsigned char model) { static const struct ps2pp_info ps2pp_list[] = { + { 1, 0, 0 }, /* Simple 2-button mouse */ { 12, 0, PS2PP_SIDE_BTN}, { 13, 0, 0 }, { 15, PS2PP_KIND_MX, /* MX1000 */ @@ -338,12 +339,12 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties) param[1] = 0; ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); - if (!param[1]) - return -1; - model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78); buttons = param[1]; + if (!model || !buttons) + return -1; + if ((model_info = get_model_info(model)) != NULL) { /* diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h index 64a8ec52ea6..6e5712525fd 100644 --- a/drivers/input/mouse/logips2pp.h +++ b/drivers/input/mouse/logips2pp.h @@ -11,6 +11,13 @@ #ifndef _LOGIPS2PP_H #define _LOGIPS2PP_H +#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP int ps2pp_init(struct psmouse *psmouse, int set_properties); +#else +inline int ps2pp_init(struct psmouse *psmouse, int set_properties) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_LOGIPS2PP */ #endif diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 0fe5869d7d4..f15f695777f 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -28,6 +28,7 @@ #include "alps.h" #include "lifebook.h" #include "trackpoint.h" +#include "touchkit_ps2.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -569,7 +570,9 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_THINKPS; /* - * Try Synaptics TouchPad + * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol + * support is disabled in config - we need to know if it is synaptics so we + * can reset it properly after probing for intellimouse. */ if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) { synaptics_hardware = 1; @@ -605,14 +608,20 @@ static int psmouse_extensions(struct psmouse *psmouse, } } - if (max_proto > PSMOUSE_IMEX && genius_detect(psmouse, set_properties) == 0) - return PSMOUSE_GENPS; + if (max_proto > PSMOUSE_IMEX) { + + if (genius_detect(psmouse, set_properties) == 0) + return PSMOUSE_GENPS; - if (max_proto > PSMOUSE_IMEX && ps2pp_init(psmouse, set_properties) == 0) - return PSMOUSE_PS2PP; + if (ps2pp_init(psmouse, set_properties) == 0) + return PSMOUSE_PS2PP; - if (max_proto > PSMOUSE_IMEX && trackpoint_detect(psmouse, set_properties) == 0) - return PSMOUSE_TRACKPOINT; + if (trackpoint_detect(psmouse, set_properties) == 0) + return PSMOUSE_TRACKPOINT; + + if (touchkit_ps2_detect(psmouse, set_properties) == 0) + return PSMOUSE_TOUCHKIT_PS2; + } /* * Reset to defaults in case the device got confused by extended @@ -654,12 +663,14 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = 1, .detect = ps2bare_detect, }, +#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP { .type = PSMOUSE_PS2PP, .name = "PS2++", .alias = "logitech", .detect = ps2pp_init, }, +#endif { .type = PSMOUSE_THINKPS, .name = "ThinkPS/2", @@ -686,6 +697,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .maxproto = 1, .detect = im_explorer_detect, }, +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS { .type = PSMOUSE_SYNAPTICS, .name = "SynPS/2", @@ -693,6 +705,8 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = synaptics_detect, .init = synaptics_init, }, +#endif +#ifdef CONFIG_MOUSE_PS2_ALPS { .type = PSMOUSE_ALPS, .name = "AlpsPS/2", @@ -700,18 +714,31 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = alps_detect, .init = alps_init, }, +#endif +#ifdef CONFIG_MOUSE_PS2_LIFEBOOK { .type = PSMOUSE_LIFEBOOK, .name = "LBPS/2", .alias = "lifebook", .init = lifebook_init, }, +#endif +#ifdef CONFIG_MOUSE_PS2_TRACKPOINT { .type = PSMOUSE_TRACKPOINT, .name = "TPPS/2", .alias = "trackpoint", .detect = trackpoint_detect, }, +#endif +#ifdef CONFIG_MOUSE_PS2_TOUCHKIT + { + .type = PSMOUSE_TOUCHKIT_PS2, + .name = "touchkitPS/2", + .alias = "touchkit", + .detect = touchkit_ps2_detect, + }, +#endif { .type = PSMOUSE_AUTO, .name = "auto", @@ -823,12 +850,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) static void psmouse_initialize(struct psmouse *psmouse) { /* - * We set the mouse into streaming mode. - */ - - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM); - -/* * We set the mouse report rate, resolution and scaling. */ @@ -1062,8 +1083,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse { struct input_dev *input_dev = psmouse->dev; - input_dev->private = psmouse; - input_dev->cdev.dev = &psmouse->ps2dev.serio->dev; + input_dev->dev.parent = &psmouse->ps2dev.serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index cf1de95b6f2..3964e8acbc5 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -87,6 +87,7 @@ enum psmouse_type { PSMOUSE_ALPS, PSMOUSE_LIFEBOOK, PSMOUSE_TRACKPOINT, + PSMOUSE_TOUCHKIT_PS2, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c index a85d74710b4..77b8ee2b965 100644 --- a/drivers/input/mouse/sermouse.c +++ b/drivers/input/mouse/sermouse.c @@ -69,7 +69,8 @@ static void sermouse_process_msc(struct sermouse *sermouse, signed char data) switch (sermouse->count) { case 0: - if ((data & 0xf8) != 0x80) return; + if ((data & 0xf8) != 0x80) + return; input_report_key(dev, BTN_LEFT, !(data & 4)); input_report_key(dev, BTN_RIGHT, !(data & 1)); input_report_key(dev, BTN_MIDDLE, !(data & 2)); @@ -107,7 +108,10 @@ static void sermouse_process_ms(struct sermouse *sermouse, signed char data) struct input_dev *dev = sermouse->dev; signed char *buf = sermouse->buf; - if (data & 0x40) sermouse->count = 0; + if (data & 0x40) + sermouse->count = 0; + else if (sermouse->count == 0) + return; switch (sermouse->count) { @@ -169,7 +173,8 @@ static void sermouse_process_ms(struct sermouse *sermouse, signed char data) case 5: case 7: /* Ignore anything besides MZ++ */ - if (sermouse->type != SERIO_MZPP) break; + if (sermouse->type != SERIO_MZPP) + break; switch (buf[1]) { @@ -206,13 +211,16 @@ static irqreturn_t sermouse_interrupt(struct serio *serio, { struct sermouse *sermouse = serio_get_drvdata(serio); - if (time_after(jiffies, sermouse->last + HZ/10)) sermouse->count = 0; + if (time_after(jiffies, sermouse->last + HZ/10)) + sermouse->count = 0; + sermouse->last = jiffies; if (sermouse->type > SERIO_SUN) sermouse_process_ms(sermouse, data); else sermouse_process_msc(sermouse, data); + return IRQ_HANDLED; } @@ -258,12 +266,11 @@ static int sermouse_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = sermouse->type; input_dev->id.product = c; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); - input_dev->private = sermouse; if (c & 0x01) set_bit(BTN_MIDDLE, input_dev->keybit); if (c & 0x02) set_bit(BTN_SIDE, input_dev->keybit); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f0f9413d762..666ad3a53fd 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -40,33 +40,70 @@ #define YMIN_NOMINAL 1408 #define YMAX_NOMINAL 4448 + /***************************************************************************** - * Synaptics communications functions + * Stuff we need even when we do not want native Synaptics support ****************************************************************************/ /* - * Send a command to the synpatics touchpad by special commands + * Set the synaptics touchpad mode byte by special commands */ -static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) +static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode) { - if (psmouse_sliced_command(psmouse, c)) + unsigned char param[1]; + + if (psmouse_sliced_command(psmouse, mode)) return -1; - if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) + param[0] = SYN_PS_SET_MODE2; + if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE)) return -1; return 0; } +int synaptics_detect(struct psmouse *psmouse, int set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[4]; + + param[0] = 0; + + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); + + if (param[1] != 0x47) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "Synaptics"; + psmouse->name = "TouchPad"; + } + + return 0; +} + +void synaptics_reset(struct psmouse *psmouse) +{ + /* reset touchpad back to relative mode, gestures enabled */ + synaptics_mode_cmd(psmouse, 0); +} + +#ifdef CONFIG_MOUSE_PS2_SYNAPTICS + +/***************************************************************************** + * Synaptics communications functions + ****************************************************************************/ + /* - * Set the synaptics touchpad mode byte by special commands + * Send a command to the synpatics touchpad by special commands */ -static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode) +static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) { - unsigned char param[1]; - - if (psmouse_sliced_command(psmouse, mode)) + if (psmouse_sliced_command(psmouse, c)) return -1; - param[0] = SYN_PS_SET_MODE2; - if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE)) + if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) return -1; return 0; } @@ -148,7 +185,7 @@ static int synaptics_query_hardware(struct psmouse *psmouse) int retries = 0; while ((retries++ < 3) && psmouse_reset(psmouse)) - printk(KERN_ERR "synaptics reset failed\n"); + /* empty */; if (synaptics_identify(psmouse)) return -1; @@ -529,12 +566,6 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) clear_bit(REL_Y, dev->relbit); } -void synaptics_reset(struct psmouse *psmouse) -{ - /* reset touchpad back to relative mode, gestures enabled */ - synaptics_mode_cmd(psmouse, 0); -} - static void synaptics_disconnect(struct psmouse *psmouse) { synaptics_reset(psmouse); @@ -569,30 +600,6 @@ static int synaptics_reconnect(struct psmouse *psmouse) return 0; } -int synaptics_detect(struct psmouse *psmouse, int set_properties) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[4]; - - param[0] = 0; - - ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); - ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); - ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); - ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); - - if (param[1] != 0x47) - return -1; - - if (set_properties) { - psmouse->vendor = "Synaptics"; - psmouse->name = "TouchPad"; - } - - return 0; -} - #if defined(__i386__) #include <linux/dmi.h> static struct dmi_system_id toshiba_dmi_table[] = { @@ -648,6 +655,16 @@ int synaptics_init(struct psmouse *psmouse) set_input_params(psmouse->dev, priv); + /* + * Encode touchpad model so that it can be used to set + * input device->id.version and be visible to userspace. + * Because version is __u16 we have to drop something. + * Hardware info bits seem to be good candidates as they + * are documented to be for Synaptics corp. internal use. + */ + psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) | + (priv->model_id & 0x000000ff); + psmouse->protocol_handler = synaptics_process_byte; psmouse->set_rate = synaptics_set_rate; psmouse->disconnect = synaptics_disconnect; @@ -680,4 +697,12 @@ int synaptics_init(struct psmouse *psmouse) return -1; } +#else /* CONFIG_MOUSE_PS2_SYNAPTICS */ + +int synaptics_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} + +#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 68fff1dcd7d..02aa4cf7bc7 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -9,10 +9,6 @@ #ifndef _SYNAPTICS_H #define _SYNAPTICS_H -extern int synaptics_detect(struct psmouse *psmouse, int set_properties); -extern int synaptics_init(struct psmouse *psmouse); -extern void synaptics_reset(struct psmouse *psmouse); - /* synaptics queries */ #define SYN_QUE_IDENTIFY 0x00 #define SYN_QUE_MODES 0x01 @@ -62,9 +58,9 @@ extern void synaptics_reset(struct psmouse *psmouse); #define SYN_MODE_WMODE(m) ((m) & (1 << 0)) /* synaptics identify query bits */ -#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f) -#define SYN_ID_MAJOR(i) ((i) & 0x0f) -#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff) +#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f) +#define SYN_ID_MAJOR(i) ((i) & 0x0f) +#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff) #define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47) /* synaptics special commands */ @@ -98,8 +94,8 @@ struct synaptics_hw_state { struct synaptics_data { /* Data read from the touchpad */ unsigned long int model_id; /* Model-ID */ - unsigned long int capabilities; /* Capabilities */ - unsigned long int ext_cap; /* Extended Capabilities */ + unsigned long int capabilities; /* Capabilities */ + unsigned long int ext_cap; /* Extended Capabilities */ unsigned long int identity; /* Identification */ unsigned char pkt_type; /* packet type - old, new, etc */ @@ -107,4 +103,8 @@ struct synaptics_data { int scroll; }; +int synaptics_detect(struct psmouse *psmouse, int set_properties); +int synaptics_init(struct psmouse *psmouse); +void synaptics_reset(struct psmouse *psmouse); + #endif /* _SYNAPTICS_H */ diff --git a/drivers/input/mouse/touchkit_ps2.c b/drivers/input/mouse/touchkit_ps2.c new file mode 100644 index 00000000000..7b977fd2357 --- /dev/null +++ b/drivers/input/mouse/touchkit_ps2.c @@ -0,0 +1,100 @@ +/* ---------------------------------------------------------------------------- + * touchkit_ps2.c -- Driver for eGalax TouchKit PS/2 Touchscreens + * + * Copyright (C) 2005 by Stefan Lucke + * Copyright (C) 2004 by Daniel Ritz + * Copyright (C) by Todd E. Johnson (mtouchusb.c) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based upon touchkitusb.c + * + * Vendor documentation is available in support section of: + * http://www.egalax.com.tw/ + */ + +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/libps2.h> + +#include "psmouse.h" +#include "touchkit_ps2.h" + +#define TOUCHKIT_MAX_XC 0x07ff +#define TOUCHKIT_MAX_YC 0x07ff + +#define TOUCHKIT_CMD 0x0a +#define TOUCHKIT_CMD_LENGTH 1 + +#define TOUCHKIT_CMD_ACTIVE 'A' +#define TOUCHKIT_CMD_FIRMWARE_VERSION 'D' +#define TOUCHKIT_CMD_CONTROLLER_TYPE 'E' + +#define TOUCHKIT_SEND_PARMS(s, r, c) ((s) << 12 | (r) << 8 | (c)) + +#define TOUCHKIT_GET_TOUCHED(packet) (((packet)[0]) & 0x01) +#define TOUCHKIT_GET_X(packet) (((packet)[1] << 7) | (packet)[2]) +#define TOUCHKIT_GET_Y(packet) (((packet)[3] << 7) | (packet)[4]) + +static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + + if (psmouse->pktcnt != 5) + return PSMOUSE_GOOD_DATA; + + input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet)); + input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet)); + input_report_key(dev, BTN_TOUCH, TOUCHKIT_GET_TOUCHED(packet)); + input_sync(dev); + + return PSMOUSE_FULL_PACKET; +} + +int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties) +{ + struct input_dev *dev = psmouse->dev; + unsigned char param[3]; + int command; + + param[0] = TOUCHKIT_CMD_LENGTH; + param[1] = TOUCHKIT_CMD_ACTIVE; + command = TOUCHKIT_SEND_PARMS(2, 3, TOUCHKIT_CMD); + + if (ps2_command(&psmouse->ps2dev, param, command)) + return -ENODEV; + + if (param[0] != TOUCHKIT_CMD || param[1] != 0x01 || + param[2] != TOUCHKIT_CMD_ACTIVE) + return -ENODEV; + + if (set_properties) { + dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + set_bit(BTN_TOUCH, dev->keybit); + input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0); + + psmouse->vendor = "eGalax"; + psmouse->name = "Touchscreen"; + psmouse->protocol_handler = touchkit_ps2_process_byte; + psmouse->pktsize = 5; + } + + return 0; +} diff --git a/drivers/input/mouse/touchkit_ps2.h b/drivers/input/mouse/touchkit_ps2.h new file mode 100644 index 00000000000..61e9dfd8419 --- /dev/null +++ b/drivers/input/mouse/touchkit_ps2.h @@ -0,0 +1,24 @@ +/* ---------------------------------------------------------------------------- + * touchkit_ps2.h -- Driver for eGalax TouchKit PS/2 Touchscreens + * + * Copyright (C) 2005 by Stefan Lucke + * Copyright (c) 2005 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef _TOUCHKIT_PS2_H +#define _TOUCHKIT_PS2_H + +#ifdef CONFIG_MOUSE_PS2_TOUCHKIT +int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties); +#else +inline int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_TOUCHKIT */ + +#endif diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index 050298b1a09..c10a6e7d010 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -142,6 +142,13 @@ struct trackpoint_data unsigned char ext_dev; }; -extern int trackpoint_detect(struct psmouse *psmouse, int set_properties); +#ifdef CONFIG_MOUSE_PS2_TRACKPOINT +int trackpoint_detect(struct psmouse *psmouse, int set_properties); +#else +inline int trackpoint_detect(struct psmouse *psmouse, int set_properties) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_TRACKPOINT */ #endif /* _TRACKPOINT_H */ diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index c3d64fcc858..4a321576f34 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -508,8 +508,7 @@ vsxxxaa_connect (struct serio *serio, struct serio_driver *drv) input_dev->name = mouse->name; input_dev->phys = mouse->phys; input_dev->id.bustype = BUS_RS232; - input_dev->cdev.dev = &serio->dev; - input_dev->private = mouse; + input_dev->dev.parent = &serio->dev; set_bit (EV_KEY, input_dev->evbit); /* We have buttons */ set_bit (EV_REL, input_dev->evbit); diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 664bcc8116f..8675f950939 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -19,7 +19,6 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/input.h> -#include <linux/smp_lock.h> #include <linux/random.h> #include <linux/major.h> #include <linux/device.h> @@ -63,9 +62,12 @@ struct mousedev { int minor; char name[16]; wait_queue_head_t wait; - struct list_head list; + struct list_head client_list; struct input_handle handle; + struct list_head mixdev_node; + int mixdev_open; + struct mousedev_hw_data packet; unsigned int pkt_count; int old_x[4], old_y[4]; @@ -85,7 +87,7 @@ struct mousedev_motion { }; #define PACKET_QUEUE_LEN 16 -struct mousedev_list { +struct mousedev_client { struct fasync_struct *fasync; struct mousedev *mousedev; struct list_head node; @@ -111,6 +113,7 @@ static struct input_handler mousedev_handler; static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; static struct mousedev mousedev_mix; +static LIST_HEAD(mousedev_mix_list); #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) #define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03]) @@ -120,32 +123,33 @@ static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mous int size, tmp; enum { FRACTION_DENOM = 128 }; - if (mousedev->touch) { - size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; - if (size == 0) - size = 256 * 2; - - switch (code) { - case ABS_X: - fx(0) = value; - if (mousedev->pkt_count >= 2) { - tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size; - tmp += mousedev->frac_dx; - mousedev->packet.dx = tmp / FRACTION_DENOM; - mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM; - } - break; + switch (code) { + case ABS_X: + fx(0) = value; + if (mousedev->touch && mousedev->pkt_count >= 2) { + size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; + if (size == 0) + size = 256 * 2; + tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size; + tmp += mousedev->frac_dx; + mousedev->packet.dx = tmp / FRACTION_DENOM; + mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM; + } + break; - case ABS_Y: - fy(0) = value; - if (mousedev->pkt_count >= 2) { - tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size; - tmp += mousedev->frac_dy; - mousedev->packet.dy = tmp / FRACTION_DENOM; - mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM; - } - break; - } + case ABS_Y: + fy(0) = value; + if (mousedev->touch && mousedev->pkt_count >= 2) { + /* use X size to keep the same scale */ + size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; + if (size == 0) + size = 256 * 2; + tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size; + tmp += mousedev->frac_dy; + mousedev->packet.dy = tmp / FRACTION_DENOM; + mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM; + } + break; } } @@ -223,47 +227,47 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet) { - struct mousedev_list *list; + struct mousedev_client *client; struct mousedev_motion *p; unsigned long flags; int wake_readers = 0; - list_for_each_entry(list, &mousedev->list, node) { - spin_lock_irqsave(&list->packet_lock, flags); + list_for_each_entry(client, &mousedev->client_list, node) { + spin_lock_irqsave(&client->packet_lock, flags); - p = &list->packets[list->head]; - if (list->ready && p->buttons != mousedev->packet.buttons) { - unsigned int new_head = (list->head + 1) % PACKET_QUEUE_LEN; - if (new_head != list->tail) { - p = &list->packets[list->head = new_head]; + p = &client->packets[client->head]; + if (client->ready && p->buttons != mousedev->packet.buttons) { + unsigned int new_head = (client->head + 1) % PACKET_QUEUE_LEN; + if (new_head != client->tail) { + p = &client->packets[client->head = new_head]; memset(p, 0, sizeof(struct mousedev_motion)); } } if (packet->abs_event) { - p->dx += packet->x - list->pos_x; - p->dy += packet->y - list->pos_y; - list->pos_x = packet->x; - list->pos_y = packet->y; + p->dx += packet->x - client->pos_x; + p->dy += packet->y - client->pos_y; + client->pos_x = packet->x; + client->pos_y = packet->y; } - list->pos_x += packet->dx; - list->pos_x = list->pos_x < 0 ? 0 : (list->pos_x >= xres ? xres : list->pos_x); - list->pos_y += packet->dy; - list->pos_y = list->pos_y < 0 ? 0 : (list->pos_y >= yres ? yres : list->pos_y); + client->pos_x += packet->dx; + client->pos_x = client->pos_x < 0 ? 0 : (client->pos_x >= xres ? xres : client->pos_x); + client->pos_y += packet->dy; + client->pos_y = client->pos_y < 0 ? 0 : (client->pos_y >= yres ? yres : client->pos_y); p->dx += packet->dx; p->dy += packet->dy; p->dz += packet->dz; p->buttons = mousedev->packet.buttons; - if (p->dx || p->dy || p->dz || p->buttons != list->last_buttons) - list->ready = 1; + if (p->dx || p->dy || p->dz || p->buttons != client->last_buttons) + client->ready = 1; - spin_unlock_irqrestore(&list->packet_lock, flags); + spin_unlock_irqrestore(&client->packet_lock, flags); - if (list->ready) { - kill_fasync(&list->fasync, SIGIO, POLL_IN); + if (client->ready) { + kill_fasync(&client->fasync, SIGIO, POLL_IN); wake_readers = 1; } } @@ -351,9 +355,9 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig static int mousedev_fasync(int fd, struct file *file, int on) { int retval; - struct mousedev_list *list = file->private_data; + struct mousedev_client *client = file->private_data; - retval = fasync_helper(fd, file, on, &list->fasync); + retval = fasync_helper(fd, file, on, &client->fasync); return retval < 0 ? retval : 0; } @@ -364,50 +368,95 @@ static void mousedev_free(struct mousedev *mousedev) kfree(mousedev); } -static void mixdev_release(void) +static int mixdev_add_device(struct mousedev *mousedev) { - struct input_handle *handle; + int error; - list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { - struct mousedev *mousedev = handle->private; + if (mousedev_mix.open) { + error = input_open_device(&mousedev->handle); + if (error) + return error; - if (!mousedev->open) { - if (mousedev->exist) - input_close_device(&mousedev->handle); - else - mousedev_free(mousedev); + mousedev->open++; + mousedev->mixdev_open++; + } + + list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); + + return 0; +} + +static void mixdev_remove_device(struct mousedev *mousedev) +{ + if (mousedev->mixdev_open) { + mousedev->mixdev_open = 0; + if (!--mousedev->open && mousedev->exist) + input_close_device(&mousedev->handle); + } + + list_del_init(&mousedev->mixdev_node); +} + +static void mixdev_open_devices(void) +{ + struct mousedev *mousedev; + + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { + if (mousedev->exist && !mousedev->open) { + if (input_open_device(&mousedev->handle)) + continue; + + mousedev->open++; + mousedev->mixdev_open++; + } + } +} + +static void mixdev_close_devices(void) +{ + struct mousedev *mousedev, *next; + + list_for_each_entry_safe(mousedev, next, &mousedev_mix_list, mixdev_node) { + if (mousedev->mixdev_open) { + mousedev->mixdev_open = 0; + if (!--mousedev->open) { + if (mousedev->exist) + input_close_device(&mousedev->handle); + else + mousedev_free(mousedev); + } } } } -static int mousedev_release(struct inode * inode, struct file * file) +static int mousedev_release(struct inode *inode, struct file *file) { - struct mousedev_list *list = file->private_data; + struct mousedev_client *client = file->private_data; + struct mousedev *mousedev = client->mousedev; mousedev_fasync(-1, file, 0); - list_del(&list->node); + list_del(&client->node); + kfree(client); - if (!--list->mousedev->open) { - if (list->mousedev->minor == MOUSEDEV_MIX) - mixdev_release(); - else if (!mousedev_mix.open) { - if (list->mousedev->exist) - input_close_device(&list->mousedev->handle); - else - mousedev_free(list->mousedev); - } + if (!--mousedev->open) { + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_close_devices(); + else if (mousedev->exist) + input_close_device(&mousedev->handle); + else + mousedev_free(mousedev); } - kfree(list); return 0; } -static int mousedev_open(struct inode * inode, struct file * file) + +static int mousedev_open(struct inode *inode, struct file *file) { - struct mousedev_list *list; - struct input_handle *handle; + struct mousedev_client *client; struct mousedev *mousedev; + int error; int i; #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX @@ -417,31 +466,37 @@ static int mousedev_open(struct inode * inode, struct file * file) #endif i = iminor(inode) - MOUSEDEV_MINOR_BASE; - if (i >= MOUSEDEV_MINORS || !mousedev_table[i]) + if (i >= MOUSEDEV_MINORS) + return -ENODEV; + + mousedev = mousedev_table[i]; + if (!mousedev) return -ENODEV; - if (!(list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL))) + client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); + if (!client) return -ENOMEM; - spin_lock_init(&list->packet_lock); - list->pos_x = xres / 2; - list->pos_y = yres / 2; - list->mousedev = mousedev_table[i]; - list_add_tail(&list->node, &mousedev_table[i]->list); - file->private_data = list; - - if (!list->mousedev->open++) { - if (list->mousedev->minor == MOUSEDEV_MIX) { - list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { - mousedev = handle->private; - if (!mousedev->open && mousedev->exist) - input_open_device(handle); + spin_lock_init(&client->packet_lock); + client->pos_x = xres / 2; + client->pos_y = yres / 2; + client->mousedev = mousedev; + list_add_tail(&client->node, &mousedev->client_list); + + if (!mousedev->open++) { + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_open_devices(); + else if (mousedev->exist) { + error = input_open_device(&mousedev->handle); + if (error) { + list_del(&client->node); + kfree(client); + return error; } - } else - if (!mousedev_mix.open && list->mousedev->exist) - input_open_device(&list->mousedev->handle); + } } + file->private_data = client; return 0; } @@ -450,13 +505,13 @@ static inline int mousedev_limit_delta(int delta, int limit) return delta > limit ? limit : (delta < -limit ? -limit : delta); } -static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data) +static void mousedev_packet(struct mousedev_client *client, signed char *ps2_data) { struct mousedev_motion *p; unsigned long flags; - spin_lock_irqsave(&list->packet_lock, flags); - p = &list->packets[list->tail]; + spin_lock_irqsave(&client->packet_lock, flags); + p = &client->packets[client->tail]; ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07); ps2_data[1] = mousedev_limit_delta(p->dx, 127); @@ -464,44 +519,44 @@ static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data) p->dx -= ps2_data[1]; p->dy -= ps2_data[2]; - switch (list->mode) { + switch (client->mode) { case MOUSEDEV_EMUL_EXPS: ps2_data[3] = mousedev_limit_delta(p->dz, 7); p->dz -= ps2_data[3]; ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1); - list->bufsiz = 4; + client->bufsiz = 4; break; case MOUSEDEV_EMUL_IMPS: ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); ps2_data[3] = mousedev_limit_delta(p->dz, 127); p->dz -= ps2_data[3]; - list->bufsiz = 4; + client->bufsiz = 4; break; case MOUSEDEV_EMUL_PS2: default: ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); p->dz = 0; - list->bufsiz = 3; + client->bufsiz = 3; break; } if (!p->dx && !p->dy && !p->dz) { - if (list->tail == list->head) { - list->ready = 0; - list->last_buttons = p->buttons; + if (client->tail == client->head) { + client->ready = 0; + client->last_buttons = p->buttons; } else - list->tail = (list->tail + 1) % PACKET_QUEUE_LEN; + client->tail = (client->tail + 1) % PACKET_QUEUE_LEN; } - spin_unlock_irqrestore(&list->packet_lock, flags); + spin_unlock_irqrestore(&client->packet_lock, flags); } -static ssize_t mousedev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) +static ssize_t mousedev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - struct mousedev_list *list = file->private_data; + struct mousedev_client *client = file->private_data; unsigned char c; unsigned int i; @@ -510,95 +565,95 @@ static ssize_t mousedev_write(struct file * file, const char __user * buffer, si if (get_user(c, buffer + i)) return -EFAULT; - if (c == mousedev_imex_seq[list->imexseq]) { - if (++list->imexseq == MOUSEDEV_SEQ_LEN) { - list->imexseq = 0; - list->mode = MOUSEDEV_EMUL_EXPS; + if (c == mousedev_imex_seq[client->imexseq]) { + if (++client->imexseq == MOUSEDEV_SEQ_LEN) { + client->imexseq = 0; + client->mode = MOUSEDEV_EMUL_EXPS; } } else - list->imexseq = 0; + client->imexseq = 0; - if (c == mousedev_imps_seq[list->impsseq]) { - if (++list->impsseq == MOUSEDEV_SEQ_LEN) { - list->impsseq = 0; - list->mode = MOUSEDEV_EMUL_IMPS; + if (c == mousedev_imps_seq[client->impsseq]) { + if (++client->impsseq == MOUSEDEV_SEQ_LEN) { + client->impsseq = 0; + client->mode = MOUSEDEV_EMUL_IMPS; } } else - list->impsseq = 0; + client->impsseq = 0; - list->ps2[0] = 0xfa; + client->ps2[0] = 0xfa; switch (c) { case 0xeb: /* Poll */ - mousedev_packet(list, &list->ps2[1]); - list->bufsiz++; /* account for leading ACK */ + mousedev_packet(client, &client->ps2[1]); + client->bufsiz++; /* account for leading ACK */ break; case 0xf2: /* Get ID */ - switch (list->mode) { - case MOUSEDEV_EMUL_PS2: list->ps2[1] = 0; break; - case MOUSEDEV_EMUL_IMPS: list->ps2[1] = 3; break; - case MOUSEDEV_EMUL_EXPS: list->ps2[1] = 4; break; + switch (client->mode) { + case MOUSEDEV_EMUL_PS2: client->ps2[1] = 0; break; + case MOUSEDEV_EMUL_IMPS: client->ps2[1] = 3; break; + case MOUSEDEV_EMUL_EXPS: client->ps2[1] = 4; break; } - list->bufsiz = 2; + client->bufsiz = 2; break; case 0xe9: /* Get info */ - list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200; - list->bufsiz = 4; + client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200; + client->bufsiz = 4; break; case 0xff: /* Reset */ - list->impsseq = list->imexseq = 0; - list->mode = MOUSEDEV_EMUL_PS2; - list->ps2[1] = 0xaa; list->ps2[2] = 0x00; - list->bufsiz = 3; + client->impsseq = client->imexseq = 0; + client->mode = MOUSEDEV_EMUL_PS2; + client->ps2[1] = 0xaa; client->ps2[2] = 0x00; + client->bufsiz = 3; break; default: - list->bufsiz = 1; + client->bufsiz = 1; break; } - list->buffer = list->bufsiz; + client->buffer = client->bufsiz; } - kill_fasync(&list->fasync, SIGIO, POLL_IN); + kill_fasync(&client->fasync, SIGIO, POLL_IN); - wake_up_interruptible(&list->mousedev->wait); + wake_up_interruptible(&client->mousedev->wait); return count; } -static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) +static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct mousedev_list *list = file->private_data; + struct mousedev_client *client = file->private_data; int retval = 0; - if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK)) + if (!client->ready && !client->buffer && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->mousedev->wait, - !list->mousedev->exist || list->ready || list->buffer); + retval = wait_event_interruptible(client->mousedev->wait, + !client->mousedev->exist || client->ready || client->buffer); if (retval) return retval; - if (!list->mousedev->exist) + if (!client->mousedev->exist) return -ENODEV; - if (!list->buffer && list->ready) { - mousedev_packet(list, list->ps2); - list->buffer = list->bufsiz; + if (!client->buffer && client->ready) { + mousedev_packet(client, client->ps2); + client->buffer = client->bufsiz; } - if (count > list->buffer) - count = list->buffer; + if (count > client->buffer) + count = client->buffer; - list->buffer -= count; + client->buffer -= count; - if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer - count, count)) + if (copy_to_user(buffer, client->ps2 + client->bufsiz - client->buffer - count, count)) return -EFAULT; return count; @@ -607,11 +662,12 @@ static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t co /* No kernel lock - fine */ static unsigned int mousedev_poll(struct file *file, poll_table *wait) { - struct mousedev_list *list = file->private_data; + struct mousedev_client *client = file->private_data; + struct mousedev *mousedev = client->mousedev; - poll_wait(file, &list->mousedev->wait, wait); - return ((list->ready || list->buffer) ? (POLLIN | POLLRDNORM) : 0) | - (list->mousedev->exist ? 0 : (POLLHUP | POLLERR)); + poll_wait(file, &mousedev->wait, wait); + return ((client->ready || client->buffer) ? (POLLIN | POLLRDNORM) : 0) | + (mousedev->exist ? 0 : (POLLHUP | POLLERR)); } static const struct file_operations mousedev_fops = { @@ -624,23 +680,27 @@ static const struct file_operations mousedev_fops = { .fasync = mousedev_fasync, }; -static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static int mousedev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct mousedev *mousedev; struct class_device *cdev; - int minor = 0; + dev_t devt; + int minor; + int error; for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); if (minor == MOUSEDEV_MINORS) { printk(KERN_ERR "mousedev: no more free mousedev devices\n"); - return NULL; + return -ENFILE; } - if (!(mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL))) - return NULL; + mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); + if (!mousedev) + return -ENOMEM; - INIT_LIST_HEAD(&mousedev->list); + INIT_LIST_HEAD(&mousedev->client_list); + INIT_LIST_HEAD(&mousedev->mixdev_node); init_waitqueue_head(&mousedev->wait); mousedev->minor = minor; @@ -651,42 +711,66 @@ static struct input_handle *mousedev_connect(struct input_handler *handler, stru mousedev->handle.private = mousedev; sprintf(mousedev->name, "mouse%d", minor); - if (mousedev_mix.open) - input_open_device(&mousedev->handle); - mousedev_table[minor] = mousedev; - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor), - dev->cdev.dev, mousedev->name); + devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor), + + cdev = class_device_create(&input_class, &dev->cdev, devt, + dev->cdev.dev, mousedev->name); + if (IS_ERR(cdev)) { + error = PTR_ERR(cdev); + goto err_free_mousedev; + } /* temporary symlink to keep userspace happy */ - sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, - mousedev->name); + error = sysfs_create_link(&input_class.subsys.kobj, + &cdev->kobj, mousedev->name); + if (error) + goto err_cdev_destroy; + + error = input_register_handle(&mousedev->handle); + if (error) + goto err_remove_link; + + error = mixdev_add_device(mousedev); + if (error) + goto err_unregister_handle; + + return 0; - return &mousedev->handle; + err_unregister_handle: + input_unregister_handle(&mousedev->handle); + err_remove_link: + sysfs_remove_link(&input_class.subsys.kobj, mousedev->name); + err_cdev_destroy: + class_device_destroy(&input_class, devt); + err_free_mousedev: + mousedev_table[minor] = NULL; + kfree(mousedev); + return error; } static void mousedev_disconnect(struct input_handle *handle) { struct mousedev *mousedev = handle->private; - struct mousedev_list *list; + struct mousedev_client *client; - sysfs_remove_link(&input_class.subsys.kset.kobj, mousedev->name); + input_unregister_handle(handle); + + sysfs_remove_link(&input_class.subsys.kobj, mousedev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + mousedev->minor)); mousedev->exist = 0; + mixdev_remove_device(mousedev); + if (mousedev->open) { input_close_device(handle); wake_up_interruptible(&mousedev->wait); - list_for_each_entry(list, &mousedev->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_HUP); - } else { - if (mousedev_mix.open) - input_close_device(handle); + list_for_each_entry(client, &mousedev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + } else mousedev_free(mousedev); - } } static const struct input_device_id mousedev_ids[] = { @@ -714,7 +798,7 @@ static const struct input_device_id mousedev_ids[] = { .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) }, }, /* A touchpad */ - { }, /* Terminating entry */ + { }, /* Terminating entry */ }; MODULE_DEVICE_TABLE(input, mousedev_ids); @@ -746,7 +830,7 @@ static int __init mousedev_init(void) return error; memset(&mousedev_mix, 0, sizeof(struct mousedev)); - INIT_LIST_HEAD(&mousedev_mix.list); + INIT_LIST_HEAD(&mousedev_mix.client_list); init_waitqueue_head(&mousedev_mix.wait); mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; mousedev_mix.exist = 1; diff --git a/drivers/input/power.c b/drivers/input/power.c deleted file mode 100644 index ee82464a2fa..00000000000 --- a/drivers/input/power.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * $Id: power.c,v 1.10 2001/09/25 09:17:15 vojtech Exp $ - * - * Copyright (c) 2001 "Crazy" James Simmons - * - * Input driver Power Management. - * - * Sponsored by Transvirtual Technology. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to <jsimmons@transvirtual.com>. - */ - -#include <linux/module.h> -#include <linux/input.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/tty.h> -#include <linux/delay.h> -#include <linux/pm.h> - -static struct input_handler power_handler; - -/* - * Power management can't be done in a interrupt context. So we have to - * use keventd. - */ -static int suspend_button_pushed = 0; -static void suspend_button_task_handler(void *data) -{ - udelay(200); /* debounce */ - suspend_button_pushed = 0; -} - -static DECLARE_WORK(suspend_button_task, suspend_button_task_handler, NULL); - -static void power_event(struct input_handle *handle, unsigned int type, - unsigned int code, int down) -{ - struct input_dev *dev = handle->dev; - - printk("Entering power_event\n"); - - if (type == EV_PWR) { - switch (code) { - case KEY_SUSPEND: - printk("Powering down entire device\n"); - - if (!suspend_button_pushed) { - suspend_button_pushed = 1; - schedule_work(&suspend_button_task); - } - break; - case KEY_POWER: - /* Hum power down the machine. */ - break; - default: - return; - } - } - - if (type == EV_KEY) { - switch (code) { - case KEY_SUSPEND: - printk("Powering down input device\n"); - /* This is risky. See pm.h for details. */ - if (dev->state != PM_RESUME) - dev->state = PM_RESUME; - else - dev->state = PM_SUSPEND; - pm_send(dev->pm_dev, dev->state, dev); - break; - case KEY_POWER: - /* Turn the input device off completely ? */ - break; - default: - return; - } - } - return; -} - -static struct input_handle *power_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) -{ - struct input_handle *handle; - - if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL))) - return NULL; - - handle->dev = dev; - handle->handler = handler; - - input_open_device(handle); - - printk(KERN_INFO "power.c: Adding power management to input layer\n"); - return handle; -} - -static void power_disconnect(struct input_handle *handle) -{ - input_close_device(handle); - kfree(handle); -} - -static const struct input_device_id power_ids[] = { - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT(EV_KEY) }, - .keybit = { [LONG(KEY_SUSPEND)] = BIT(KEY_SUSPEND) } - }, - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT(EV_KEY) }, - .keybit = { [LONG(KEY_POWER)] = BIT(KEY_POWER) } - }, - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT, - .evbit = { BIT(EV_PWR) }, - }, - { }, /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(input, power_ids); - -static struct input_handler power_handler = { - .event = power_event, - .connect = power_connect, - .disconnect = power_disconnect, - .name = "power", - .id_table = power_ids, -}; - -static int __init power_init(void) -{ - return input_register_handler(&power_handler); -} - -static void __exit power_exit(void) -{ - input_unregister_handler(&power_handler); -} - -module_init(power_init); -module_exit(power_exit); - -MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>"); -MODULE_DESCRIPTION("Input Power Management driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c index 4fa93ff3091..93a1a6ba216 100644 --- a/drivers/input/serio/hil_mlc.c +++ b/drivers/input/serio/hil_mlc.c @@ -32,11 +32,11 @@ * * Driver theory of operation: * - * Some access methods and an ISR is defined by the sub-driver - * (e.g. hp_sdc_mlc.c). These methods are expected to provide a - * few bits of logic in addition to raw access to the HIL MLC, - * specifically, the ISR, which is entirely registered by the - * sub-driver and invoked directly, must check for record + * Some access methods and an ISR is defined by the sub-driver + * (e.g. hp_sdc_mlc.c). These methods are expected to provide a + * few bits of logic in addition to raw access to the HIL MLC, + * specifically, the ISR, which is entirely registered by the + * sub-driver and invoked directly, must check for record * termination or packet match, at which point a semaphore must * be cleared and then the hil_mlcs_tasklet must be scheduled. * @@ -47,7 +47,7 @@ * itself if output is pending. (This rescheduling should be replaced * at some point with a sub-driver-specific mechanism.) * - * A timer task prods the tasklet once per second to prevent + * A timer task prods the tasklet once per second to prevent * hangups when attached devices do not return expected data * and to initiate probes of the loop for new devices. */ @@ -83,69 +83,85 @@ DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0); /********************** Device info/instance management **********************/ -static void hil_mlc_clear_di_map (hil_mlc *mlc, int val) { +static void hil_mlc_clear_di_map(hil_mlc *mlc, int val) +{ int j; - for (j = val; j < 7 ; j++) { + + for (j = val; j < 7 ; j++) mlc->di_map[j] = -1; - } } -static void hil_mlc_clear_di_scratch (hil_mlc *mlc) { - memset(&(mlc->di_scratch), 0, sizeof(mlc->di_scratch)); +static void hil_mlc_clear_di_scratch(hil_mlc *mlc) +{ + memset(&mlc->di_scratch, 0, sizeof(mlc->di_scratch)); } -static void hil_mlc_copy_di_scratch (hil_mlc *mlc, int idx) { - memcpy(&(mlc->di[idx]), &(mlc->di_scratch), sizeof(mlc->di_scratch)); +static void hil_mlc_copy_di_scratch(hil_mlc *mlc, int idx) +{ + memcpy(&mlc->di[idx], &mlc->di_scratch, sizeof(mlc->di_scratch)); } -static int hil_mlc_match_di_scratch (hil_mlc *mlc) { +static int hil_mlc_match_di_scratch(hil_mlc *mlc) +{ int idx; for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { - int j, found; + int j, found = 0; /* In-use slots are not eligible. */ - found = 0; - for (j = 0; j < 7 ; j++) { - if (mlc->di_map[j] == idx) found++; - } - if (found) continue; - if (!memcmp(mlc->di + idx, - &(mlc->di_scratch), - sizeof(mlc->di_scratch))) break; + for (j = 0; j < 7 ; j++) + if (mlc->di_map[j] == idx) + found++; + + if (found) + continue; + + if (!memcmp(mlc->di + idx, &mlc->di_scratch, + sizeof(mlc->di_scratch))) + break; } - return((idx >= HIL_MLC_DEVMEM) ? -1 : idx); + return idx >= HIL_MLC_DEVMEM ? -1 : idx; } -static int hil_mlc_find_free_di(hil_mlc *mlc) { +static int hil_mlc_find_free_di(hil_mlc *mlc) +{ int idx; - /* TODO: Pick all-zero slots first, failing that, - * randomize the slot picked among those eligible. + + /* TODO: Pick all-zero slots first, failing that, + * randomize the slot picked among those eligible. */ for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { - int j, found; - found = 0; - for (j = 0; j < 7 ; j++) { - if (mlc->di_map[j] == idx) found++; - } - if (!found) break; + int j, found = 0; + + for (j = 0; j < 7 ; j++) + if (mlc->di_map[j] == idx) + found++; + + if (!found) + break; } - return(idx); /* Note: It is guaranteed at least one above will match */ + + return idx; /* Note: It is guaranteed at least one above will match */ } -static inline void hil_mlc_clean_serio_map(hil_mlc *mlc) { +static inline void hil_mlc_clean_serio_map(hil_mlc *mlc) +{ int idx; + for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) { - int j, found; - found = 0; - for (j = 0; j < 7 ; j++) { - if (mlc->di_map[j] == idx) found++; - } - if (!found) mlc->serio_map[idx].di_revmap = -1; + int j, found = 0; + + for (j = 0; j < 7 ; j++) + if (mlc->di_map[j] == idx) + found++; + + if (!found) + mlc->serio_map[idx].di_revmap = -1; } } -static void hil_mlc_send_polls(hil_mlc *mlc) { +static void hil_mlc_send_polls(hil_mlc *mlc) +{ int did, i, cnt; struct serio *serio; struct serio_driver *drv; @@ -157,26 +173,31 @@ static void hil_mlc_send_polls(hil_mlc *mlc) { while (mlc->icount < 15 - i) { hil_packet p; + p = mlc->ipacket[i]; if (did != (p & HIL_PKT_ADDR_MASK) >> 8) { - if (drv == NULL || drv->interrupt == NULL) goto skip; + if (drv && drv->interrupt) { + drv->interrupt(serio, 0, 0); + drv->interrupt(serio, HIL_ERR_INT >> 16, 0); + drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); + drv->interrupt(serio, HIL_CMD_POL + cnt, 0); + } - drv->interrupt(serio, 0, 0); - drv->interrupt(serio, HIL_ERR_INT >> 16, 0); - drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); - drv->interrupt(serio, HIL_CMD_POL + cnt, 0); - skip: did = (p & HIL_PKT_ADDR_MASK) >> 8; serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL; drv = (serio != NULL) ? serio->drv : NULL; cnt = 0; } - cnt++; i++; - if (drv == NULL || drv->interrupt == NULL) continue; - drv->interrupt(serio, (p >> 24), 0); - drv->interrupt(serio, (p >> 16) & 0xff, 0); - drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0); - drv->interrupt(serio, p & 0xff, 0); + + cnt++; + i++; + + if (drv && drv->interrupt) { + drv->interrupt(serio, (p >> 24), 0); + drv->interrupt(serio, (p >> 16) & 0xff, 0); + drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0); + drv->interrupt(serio, p & 0xff, 0); + } } } @@ -215,12 +236,16 @@ static void hil_mlc_send_polls(hil_mlc *mlc) { #define HILSEN_DOZE (HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK) #define HILSEN_SLEEP (HILSEN_SAME | HILSEN_BREAK) -static int hilse_match(hil_mlc *mlc, int unused) { +static int hilse_match(hil_mlc *mlc, int unused) +{ int rc; + rc = hil_mlc_match_di_scratch(mlc); if (rc == -1) { rc = hil_mlc_find_free_di(mlc); - if (rc == -1) goto err; + if (rc == -1) + goto err; + #ifdef HIL_MLC_DEBUG printk(KERN_DEBUG PREFIX "new in slot %i\n", rc); #endif @@ -231,6 +256,7 @@ static int hilse_match(hil_mlc *mlc, int unused) { serio_rescan(mlc->serio[rc]); return -1; } + mlc->di_map[mlc->ddi] = rc; #ifdef HIL_MLC_DEBUG printk(KERN_DEBUG PREFIX "same in slot %i\n", rc); @@ -238,152 +264,177 @@ static int hilse_match(hil_mlc *mlc, int unused) { mlc->serio_map[rc].di_revmap = mlc->ddi; hil_mlc_clean_serio_map(mlc); return 0; + err: printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n"); return 1; } /* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */ -static int hilse_init_lcv(hil_mlc *mlc, int unused) { +static int hilse_init_lcv(hil_mlc *mlc, int unused) +{ struct timeval tv; do_gettimeofday(&tv); - if(mlc->lcv == 0) goto restart; /* First init, no need to dally */ - if(tv.tv_sec - mlc->lcv_tv.tv_sec < 5) return -1; - restart: + if (mlc->lcv && (tv.tv_sec - mlc->lcv_tv.tv_sec) < 5) + return -1; + mlc->lcv_tv = tv; mlc->lcv = 0; + return 0; } -static int hilse_inc_lcv(hil_mlc *mlc, int lim) { - if (mlc->lcv++ >= lim) return -1; - return 0; +static int hilse_inc_lcv(hil_mlc *mlc, int lim) +{ + return mlc->lcv++ >= lim ? -1 : 0; } #if 0 -static int hilse_set_lcv(hil_mlc *mlc, int val) { +static int hilse_set_lcv(hil_mlc *mlc, int val) +{ mlc->lcv = val; + return 0; } #endif /* Management of the discovered device index (zero based, -1 means no devs) */ -static int hilse_set_ddi(hil_mlc *mlc, int val) { +static int hilse_set_ddi(hil_mlc *mlc, int val) +{ mlc->ddi = val; hil_mlc_clear_di_map(mlc, val + 1); + return 0; } -static int hilse_dec_ddi(hil_mlc *mlc, int unused) { +static int hilse_dec_ddi(hil_mlc *mlc, int unused) +{ mlc->ddi--; - if (mlc->ddi <= -1) { + if (mlc->ddi <= -1) { mlc->ddi = -1; hil_mlc_clear_di_map(mlc, 0); return -1; } hil_mlc_clear_di_map(mlc, mlc->ddi + 1); + return 0; } -static int hilse_inc_ddi(hil_mlc *mlc, int unused) { - if (mlc->ddi >= 6) { - BUG(); - return -1; - } +static int hilse_inc_ddi(hil_mlc *mlc, int unused) +{ + BUG_ON(mlc->ddi >= 6); mlc->ddi++; + return 0; } -static int hilse_take_idd(hil_mlc *mlc, int unused) { +static int hilse_take_idd(hil_mlc *mlc, int unused) +{ int i; - /* Help the state engine: - * Is this a real IDD response or just an echo? + /* Help the state engine: + * Is this a real IDD response or just an echo? * - * Real IDD response does not start with a command. + * Real IDD response does not start with a command. */ - if (mlc->ipacket[0] & HIL_PKT_CMD) goto bail; + if (mlc->ipacket[0] & HIL_PKT_CMD) + goto bail; + /* Should have the command echoed further down. */ for (i = 1; i < 16; i++) { - if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) == + if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) == (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) && - (mlc->ipacket[i] & HIL_PKT_CMD) && + (mlc->ipacket[i] & HIL_PKT_CMD) && ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD)) break; } - if (i > 15) goto bail; + if (i > 15) + goto bail; + /* And the rest of the packets should still be clear. */ - while (++i < 16) { - if (mlc->ipacket[i]) break; - } - if (i < 16) goto bail; - for (i = 0; i < 16; i++) { - mlc->di_scratch.idd[i] = + while (++i < 16) + if (mlc->ipacket[i]) + break; + + if (i < 16) + goto bail; + + for (i = 0; i < 16; i++) + mlc->di_scratch.idd[i] = mlc->ipacket[i] & HIL_PKT_DATA_MASK; - } + /* Next step is to see if RSC supported */ - if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC) + if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC) return HILSEN_NEXT; - if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) + + if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) return HILSEN_DOWN | 4; + return 0; + bail: mlc->ddi--; + return -1; /* This should send us off to ACF */ } -static int hilse_take_rsc(hil_mlc *mlc, int unused) { +static int hilse_take_rsc(hil_mlc *mlc, int unused) +{ int i; - for (i = 0; i < 16; i++) { - mlc->di_scratch.rsc[i] = + for (i = 0; i < 16; i++) + mlc->di_scratch.rsc[i] = mlc->ipacket[i] & HIL_PKT_DATA_MASK; - } + /* Next step is to see if EXD supported (IDD has already been read) */ - if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) + if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) return HILSEN_NEXT; + return 0; } -static int hilse_take_exd(hil_mlc *mlc, int unused) { +static int hilse_take_exd(hil_mlc *mlc, int unused) +{ int i; - for (i = 0; i < 16; i++) { - mlc->di_scratch.exd[i] = + for (i = 0; i < 16; i++) + mlc->di_scratch.exd[i] = mlc->ipacket[i] & HIL_PKT_DATA_MASK; - } + /* Next step is to see if RNM supported. */ - if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM) + if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM) return HILSEN_NEXT; + return 0; } -static int hilse_take_rnm(hil_mlc *mlc, int unused) { +static int hilse_take_rnm(hil_mlc *mlc, int unused) +{ int i; - for (i = 0; i < 16; i++) { - mlc->di_scratch.rnm[i] = + for (i = 0; i < 16; i++) + mlc->di_scratch.rnm[i] = mlc->ipacket[i] & HIL_PKT_DATA_MASK; - } - do { - char nam[17]; - snprintf(nam, 16, "%s", mlc->di_scratch.rnm); - nam[16] = '\0'; - printk(KERN_INFO PREFIX "Device name gotten: %s\n", nam); - } while (0); + + printk(KERN_INFO PREFIX "Device name gotten: %16s\n", + mlc->di_scratch.rnm); + return 0; } -static int hilse_operate(hil_mlc *mlc, int repoll) { +static int hilse_operate(hil_mlc *mlc, int repoll) +{ - if (mlc->opercnt == 0) hil_mlcs_probe = 0; + if (mlc->opercnt == 0) + hil_mlcs_probe = 0; mlc->opercnt = 1; hil_mlc_send_polls(mlc); - if (!hil_mlcs_probe) return 0; + if (!hil_mlcs_probe) + return 0; hil_mlcs_probe = 0; mlc->opercnt = 0; return 1; @@ -408,7 +459,7 @@ static int hilse_operate(hil_mlc *mlc, int repoll) { #define OUT_LAST(pack) \ { HILSE_OUT_LAST, { .packet = pack }, 0, 0, 0, 0 }, -struct hilse_node hil_mlc_se[HILSEN_END] = { +const struct hilse_node hil_mlc_se[HILSEN_END] = { /* 0 HILSEN_START */ FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) @@ -428,7 +479,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = { EXPECT(HIL_ERR_INT | TEST_PACKET(0xa), 2000, HILSEN_NEXT, HILSEN_RESTART, HILSEN_RESTART) OUT(HIL_CTRL_ONLY | 0) /* Disable test mode */ - + /* 9 HILSEN_DHR */ FUNC(hilse_init_lcv, 0, HILSEN_NEXT, HILSEN_SLEEP, 0) @@ -439,7 +490,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = { IN(300000, HILSEN_DHR2, HILSEN_DHR2, HILSEN_NEXT) /* 14 HILSEN_IFC */ - OUT(HIL_PKT_CMD | HIL_CMD_IFC) + OUT(HIL_PKT_CMD | HIL_CMD_IFC) EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT, 20000, HILSEN_DISC, HILSEN_DHR2, HILSEN_NEXT ) @@ -455,7 +506,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = { /* 18 HILSEN_HEAL */ OUT_LAST(HIL_CMD_ELB) - EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT, + EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT, 20000, HILSEN_REPOLL, HILSEN_DSR, HILSEN_NEXT) FUNC(hilse_dec_ddi, 0, HILSEN_HEAL, HILSEN_NEXT, 0) @@ -503,7 +554,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = { /* 44 HILSEN_PROBE */ OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT) - IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) + IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB) IN(10000, HILSEN_DISC, HILSEN_DSR, HILSEN_NEXT) OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1) @@ -514,7 +565,7 @@ struct hilse_node hil_mlc_se[HILSEN_END] = { /* 52 HILSEN_DSR */ FUNC(hilse_set_ddi, -1, HILSEN_NEXT, 0, 0) OUT(HIL_PKT_CMD | HIL_CMD_DSR) - IN(20000, HILSEN_DHR, HILSEN_DHR, HILSEN_IFC) + IN(20000, HILSEN_DHR, HILSEN_DHR, HILSEN_IFC) /* 55 HILSEN_REPOLL */ OUT(HIL_PKT_CMD | HIL_CMD_RPL) @@ -523,14 +574,15 @@ struct hilse_node hil_mlc_se[HILSEN_END] = { FUNC(hilse_operate, 1, HILSEN_OPERATE, HILSEN_IFC, HILSEN_PROBE) /* 58 HILSEN_IFCACF */ - OUT(HIL_PKT_CMD | HIL_CMD_IFC) + OUT(HIL_PKT_CMD | HIL_CMD_IFC) EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT, 20000, HILSEN_ACF2, HILSEN_DHR2, HILSEN_HEAL) /* 60 HILSEN_END */ }; -static inline void hilse_setup_input(hil_mlc *mlc, struct hilse_node *node) { +static inline void hilse_setup_input(hil_mlc *mlc, const struct hilse_node *node) +{ switch (node->act) { case HILSE_EXPECT_DISC: @@ -555,29 +607,27 @@ static inline void hilse_setup_input(hil_mlc *mlc, struct hilse_node *node) { do_gettimeofday(&(mlc->instart)); mlc->icount = 15; memset(mlc->ipacket, 0, 16 * sizeof(hil_packet)); - BUG_ON(down_trylock(&(mlc->isem))); - - return; + BUG_ON(down_trylock(&mlc->isem)); } #ifdef HIL_MLC_DEBUG -static int doze = 0; +static int doze; static int seidx; /* For debug */ -static int kick = 1; #endif -static int hilse_donode (hil_mlc *mlc) { - struct hilse_node *node; +static int hilse_donode(hil_mlc *mlc) +{ + const struct hilse_node *node; int nextidx = 0; int sched_long = 0; unsigned long flags; #ifdef HIL_MLC_DEBUG - if (mlc->seidx && (mlc->seidx != seidx) && mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) { - printk(KERN_DEBUG PREFIX "z%i \n%s {%i}", doze, kick ? "K" : "", mlc->seidx); + if (mlc->seidx && mlc->seidx != seidx && + mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) { + printk(KERN_DEBUG PREFIX "z%i \n {%i}", doze, mlc->seidx); doze = 0; } - kick = 0; seidx = mlc->seidx; #endif @@ -588,52 +638,61 @@ static int hilse_donode (hil_mlc *mlc) { hil_packet pack; case HILSE_FUNC: - if (node->object.func == NULL) break; + BUG_ON(node->object.func == NULL); rc = node->object.func(mlc, node->arg); - nextidx = (rc > 0) ? node->ugly : + nextidx = (rc > 0) ? node->ugly : ((rc < 0) ? node->bad : node->good); - if (nextidx == HILSEN_FOLLOW) nextidx = rc; + if (nextidx == HILSEN_FOLLOW) + nextidx = rc; break; + case HILSE_EXPECT_LAST: case HILSE_EXPECT_DISC: case HILSE_EXPECT: case HILSE_IN: /* Already set up from previous HILSE_OUT_* */ - write_lock_irqsave(&(mlc->lock), flags); + write_lock_irqsave(&mlc->lock, flags); rc = mlc->in(mlc, node->arg); if (rc == 2) { nextidx = HILSEN_DOZE; sched_long = 1; - write_unlock_irqrestore(&(mlc->lock), flags); + write_unlock_irqrestore(&mlc->lock, flags); break; } - if (rc == 1) nextidx = node->ugly; - else if (rc == 0) nextidx = node->good; - else nextidx = node->bad; + if (rc == 1) + nextidx = node->ugly; + else if (rc == 0) + nextidx = node->good; + else + nextidx = node->bad; mlc->istarted = 0; - write_unlock_irqrestore(&(mlc->lock), flags); + write_unlock_irqrestore(&mlc->lock, flags); break; + case HILSE_OUT_LAST: - write_lock_irqsave(&(mlc->lock), flags); + write_lock_irqsave(&mlc->lock, flags); pack = node->object.packet; pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT); goto out; + case HILSE_OUT_DISC: - write_lock_irqsave(&(mlc->lock), flags); + write_lock_irqsave(&mlc->lock, flags); pack = node->object.packet; pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT); goto out; + case HILSE_OUT: - write_lock_irqsave(&(mlc->lock), flags); + write_lock_irqsave(&mlc->lock, flags); pack = node->object.packet; out: - if (mlc->istarted) goto out2; + if (mlc->istarted) + goto out2; /* Prepare to receive input */ if ((node + 1)->act & HILSE_IN) hilse_setup_input(mlc, node + 1); out2: - write_unlock_irqrestore(&(mlc->lock), flags); + write_unlock_irqrestore(&mlc->lock, flags); if (down_trylock(&mlc->osem)) { nextidx = HILSEN_DOZE; @@ -641,60 +700,71 @@ static int hilse_donode (hil_mlc *mlc) { } up(&mlc->osem); - write_lock_irqsave(&(mlc->lock), flags); - if (!(mlc->ostarted)) { + write_lock_irqsave(&mlc->lock, flags); + if (!mlc->ostarted) { mlc->ostarted = 1; mlc->opacket = pack; mlc->out(mlc); nextidx = HILSEN_DOZE; - write_unlock_irqrestore(&(mlc->lock), flags); + write_unlock_irqrestore(&mlc->lock, flags); break; } mlc->ostarted = 0; do_gettimeofday(&(mlc->instart)); - write_unlock_irqrestore(&(mlc->lock), flags); + write_unlock_irqrestore(&mlc->lock, flags); nextidx = HILSEN_NEXT; break; + case HILSE_CTS: + write_lock_irqsave(&mlc->lock, flags); nextidx = mlc->cts(mlc) ? node->bad : node->good; + write_unlock_irqrestore(&mlc->lock, flags); break; + default: BUG(); - nextidx = 0; - break; } #ifdef HIL_MLC_DEBUG - if (nextidx == HILSEN_DOZE) doze++; + if (nextidx == HILSEN_DOZE) + doze++; #endif while (nextidx & HILSEN_SCHED) { struct timeval tv; - if (!sched_long) goto sched; + if (!sched_long) + goto sched; do_gettimeofday(&tv); - tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec); + tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec); tv.tv_usec -= mlc->instart.tv_usec; if (tv.tv_usec >= mlc->intimeout) goto sched; - tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / 1000000; + tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / USEC_PER_SEC; if (!tv.tv_usec) goto sched; mod_timer(&hil_mlcs_kicker, jiffies + tv.tv_usec); break; sched: tasklet_schedule(&hil_mlcs_tasklet); break; - } - if (nextidx & HILSEN_DOWN) mlc->seidx += nextidx & HILSEN_MASK; - else if (nextidx & HILSEN_UP) mlc->seidx -= nextidx & HILSEN_MASK; - else mlc->seidx = nextidx & HILSEN_MASK; + } + + if (nextidx & HILSEN_DOWN) + mlc->seidx += nextidx & HILSEN_MASK; + else if (nextidx & HILSEN_UP) + mlc->seidx -= nextidx & HILSEN_MASK; + else + mlc->seidx = nextidx & HILSEN_MASK; + + if (nextidx & HILSEN_BREAK) + return 1; - if (nextidx & HILSEN_BREAK) return 1; return 0; } /******************** tasklet context functions **************************/ -static void hil_mlcs_process(unsigned long unused) { +static void hil_mlcs_process(unsigned long unused) +{ struct list_head *tmp; read_lock(&hil_mlcs_lock); @@ -702,19 +772,20 @@ static void hil_mlcs_process(unsigned long unused) { struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list); while (hilse_donode(mlc) == 0) { #ifdef HIL_MLC_DEBUG - if (mlc->seidx != 41 && - mlc->seidx != 42 && - mlc->seidx != 43) - printk(KERN_DEBUG PREFIX " + "); + if (mlc->seidx != 41 && + mlc->seidx != 42 && + mlc->seidx != 43) + printk(KERN_DEBUG PREFIX " + "); #endif - }; + } } read_unlock(&hil_mlcs_lock); } /************************* Keepalive timer task *********************/ -void hil_mlcs_timer (unsigned long data) { +void hil_mlcs_timer(unsigned long data) +{ hil_mlcs_probe = 1; tasklet_schedule(&hil_mlcs_tasklet); /* Re-insert the periodic task. */ @@ -724,28 +795,25 @@ void hil_mlcs_timer (unsigned long data) { /******************** user/kernel context functions **********************/ -static int hil_mlc_serio_write(struct serio *serio, unsigned char c) { +static int hil_mlc_serio_write(struct serio *serio, unsigned char c) +{ struct hil_mlc_serio_map *map; struct hil_mlc *mlc; struct serio_driver *drv; uint8_t *idx, *last; map = serio->port_data; - if (map == NULL) { - BUG(); - return -EIO; - } + BUG_ON(map == NULL); + mlc = map->mlc; - if (mlc == NULL) { - BUG(); - return -EIO; - } - mlc->serio_opacket[map->didx] |= + BUG_ON(mlc == NULL); + + mlc->serio_opacket[map->didx] |= ((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx])); if (mlc->serio_oidx[map->didx] >= 3) { /* for now only commands */ - if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD)) + if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD)) return -EIO; switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) { case HIL_CMD_IDD: @@ -771,12 +839,11 @@ static int hil_mlc_serio_write(struct serio *serio, unsigned char c) { return -EIO; emu: drv = serio->drv; - if (drv == NULL) { - BUG(); - return -EIO; - } + BUG_ON(drv == NULL); + last = idx + 15; - while ((last != idx) && (*last == 0)) last--; + while ((last != idx) && (*last == 0)) + last--; while (idx != last) { drv->interrupt(serio, 0, 0); @@ -789,14 +856,15 @@ static int hil_mlc_serio_write(struct serio *serio, unsigned char c) { drv->interrupt(serio, HIL_ERR_INT >> 16, 0); drv->interrupt(serio, HIL_PKT_CMD >> 8, 0); drv->interrupt(serio, *idx, 0); - + mlc->serio_oidx[map->didx] = 0; mlc->serio_opacket[map->didx] = 0; return 0; } -static int hil_mlc_serio_open(struct serio *serio) { +static int hil_mlc_serio_open(struct serio *serio) +{ struct hil_mlc_serio_map *map; struct hil_mlc *mlc; @@ -804,67 +872,57 @@ static int hil_mlc_serio_open(struct serio *serio) { return -EBUSY; map = serio->port_data; - if (map == NULL) { - BUG(); - return -ENODEV; - } + BUG_ON(map == NULL); + mlc = map->mlc; - if (mlc == NULL) { - BUG(); - return -ENODEV; - } + BUG_ON(mlc == NULL); return 0; } -static void hil_mlc_serio_close(struct serio *serio) { +static void hil_mlc_serio_close(struct serio *serio) +{ struct hil_mlc_serio_map *map; struct hil_mlc *mlc; map = serio->port_data; - if (map == NULL) { - BUG(); - return; - } + BUG_ON(map == NULL); + mlc = map->mlc; - if (mlc == NULL) { - BUG(); - return; - } + BUG_ON(mlc == NULL); serio_set_drvdata(serio, NULL); serio->drv = NULL; /* TODO wake up interruptable */ } -static struct serio_device_id hil_mlc_serio_id = { +static const struct serio_device_id hil_mlc_serio_id = { .type = SERIO_HIL_MLC, .proto = SERIO_HIL, .extra = SERIO_ANY, .id = SERIO_ANY, }; -int hil_mlc_register(hil_mlc *mlc) { +int hil_mlc_register(hil_mlc *mlc) +{ int i; - unsigned long flags; + unsigned long flags; - if (mlc == NULL) { - return -EINVAL; - } + BUG_ON(mlc == NULL); mlc->istarted = 0; - mlc->ostarted = 0; + mlc->ostarted = 0; - rwlock_init(&mlc->lock); - init_MUTEX(&(mlc->osem)); + rwlock_init(&mlc->lock); + init_MUTEX(&mlc->osem); - init_MUTEX(&(mlc->isem)); - mlc->icount = -1; - mlc->imatch = 0; + init_MUTEX(&mlc->isem); + mlc->icount = -1; + mlc->imatch = 0; mlc->opercnt = 0; - init_MUTEX_LOCKED(&(mlc->csem)); + init_MUTEX_LOCKED(&(mlc->csem)); hil_mlc_clear_di_scratch(mlc); hil_mlc_clear_di_map(mlc, 0); @@ -873,6 +931,8 @@ int hil_mlc_register(hil_mlc *mlc) { hil_mlc_copy_di_scratch(mlc, i); mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL); mlc->serio[i] = mlc_serio; + snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i); + snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i); mlc_serio->id = hil_mlc_serio_id; mlc_serio->write = hil_mlc_serio_write; mlc_serio->open = hil_mlc_serio_open; @@ -897,19 +957,18 @@ int hil_mlc_register(hil_mlc *mlc) { return 0; } -int hil_mlc_unregister(hil_mlc *mlc) { +int hil_mlc_unregister(hil_mlc *mlc) +{ struct list_head *tmp; - unsigned long flags; + unsigned long flags; int i; - if (mlc == NULL) - return -EINVAL; + BUG_ON(mlc == NULL); write_lock_irqsave(&hil_mlcs_lock, flags); - list_for_each(tmp, &hil_mlcs) { + list_for_each(tmp, &hil_mlcs) if (list_entry(tmp, hil_mlc, list) == mlc) goto found; - } /* not found in list */ write_unlock_irqrestore(&hil_mlcs_lock, flags); @@ -918,7 +977,7 @@ int hil_mlc_unregister(hil_mlc *mlc) { found: list_del(tmp); - write_unlock_irqrestore(&hil_mlcs_lock, flags); + write_unlock_irqrestore(&hil_mlcs_lock, flags); for (i = 0; i < HIL_MLC_DEVMEM; i++) { serio_unregister_port(mlc->serio[i]); @@ -942,7 +1001,7 @@ static int __init hil_mlc_init(void) return 0; } - + static void __exit hil_mlc_exit(void) { del_timer(&hil_mlcs_kicker); @@ -950,6 +1009,6 @@ static void __exit hil_mlc_exit(void) tasklet_disable(&hil_mlcs_tasklet); tasklet_kill(&hil_mlcs_tasklet); } - + module_init(hil_mlc_init); module_exit(hil_mlc_exit); diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index b57370dc4e3..6af199805ff 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -34,27 +34,27 @@ * * Driver theory of operation: * - * hp_sdc_put does all writing to the SDC. ISR can run on a different - * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time + * hp_sdc_put does all writing to the SDC. ISR can run on a different + * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time * (it cannot really benefit from SMP anyway.) A tasket fit this perfectly. * - * All data coming back from the SDC is sent via interrupt and can be read - * fully in the ISR, so there are no latency/throughput problems there. - * The problem is with output, due to the slow clock speed of the SDC - * compared to the CPU. This should not be too horrible most of the time, - * but if used with HIL devices that support the multibyte transfer command, - * keeping outbound throughput flowing at the 6500KBps that the HIL is + * All data coming back from the SDC is sent via interrupt and can be read + * fully in the ISR, so there are no latency/throughput problems there. + * The problem is with output, due to the slow clock speed of the SDC + * compared to the CPU. This should not be too horrible most of the time, + * but if used with HIL devices that support the multibyte transfer command, + * keeping outbound throughput flowing at the 6500KBps that the HIL is * capable of is more than can be done at HZ=100. * - * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf - * is set to 0 when the IBF flag in the status register has cleared. ISR - * may do this, and may also access the parts of queued transactions related - * to reading data back from the SDC, but otherwise will not touch the + * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf + * is set to 0 when the IBF flag in the status register has cleared. ISR + * may do this, and may also access the parts of queued transactions related + * to reading data back from the SDC, but otherwise will not touch the * hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1. * * The i8042 write index and the values in the 4-byte input buffer * starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively, - * to minimize the amount of IO needed to the SDC. However these values + * to minimize the amount of IO needed to the SDC. However these values * do not need to be locked since they are only ever accessed by hp_sdc_put. * * A timer task schedules the tasklet once per second just to make @@ -100,39 +100,46 @@ EXPORT_SYMBOL(hp_sdc_release_timer_irq); EXPORT_SYMBOL(hp_sdc_release_hil_irq); EXPORT_SYMBOL(hp_sdc_release_cooked_irq); +EXPORT_SYMBOL(__hp_sdc_enqueue_transaction); EXPORT_SYMBOL(hp_sdc_enqueue_transaction); EXPORT_SYMBOL(hp_sdc_dequeue_transaction); static hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */ /*************** primitives for use in any context *********************/ -static inline uint8_t hp_sdc_status_in8 (void) { +static inline uint8_t hp_sdc_status_in8(void) +{ uint8_t status; unsigned long flags; write_lock_irqsave(&hp_sdc.ibf_lock, flags); status = sdc_readb(hp_sdc.status_io); - if (!(status & HP_SDC_STATUS_IBF)) hp_sdc.ibf = 0; + if (!(status & HP_SDC_STATUS_IBF)) + hp_sdc.ibf = 0; write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); return status; } -static inline uint8_t hp_sdc_data_in8 (void) { - return sdc_readb(hp_sdc.data_io); +static inline uint8_t hp_sdc_data_in8(void) +{ + return sdc_readb(hp_sdc.data_io); } -static inline void hp_sdc_status_out8 (uint8_t val) { +static inline void hp_sdc_status_out8(uint8_t val) +{ unsigned long flags; write_lock_irqsave(&hp_sdc.ibf_lock, flags); hp_sdc.ibf = 1; - if ((val & 0xf0) == 0xe0) hp_sdc.wi = 0xff; + if ((val & 0xf0) == 0xe0) + hp_sdc.wi = 0xff; sdc_writeb(val, hp_sdc.status_io); write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); } -static inline void hp_sdc_data_out8 (uint8_t val) { +static inline void hp_sdc_data_out8(uint8_t val) +{ unsigned long flags; write_lock_irqsave(&hp_sdc.ibf_lock, flags); @@ -141,11 +148,12 @@ static inline void hp_sdc_data_out8 (uint8_t val) { write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); } -/* Care must be taken to only invoke hp_sdc_spin_ibf when - * absolutely needed, or in rarely invoked subroutines. - * Not only does it waste CPU cycles, it also wastes bus cycles. +/* Care must be taken to only invoke hp_sdc_spin_ibf when + * absolutely needed, or in rarely invoked subroutines. + * Not only does it waste CPU cycles, it also wastes bus cycles. */ -static inline void hp_sdc_spin_ibf(void) { +static inline void hp_sdc_spin_ibf(void) +{ unsigned long flags; rwlock_t *lock; @@ -158,19 +166,21 @@ static inline void hp_sdc_spin_ibf(void) { } read_unlock(lock); write_lock(lock); - while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) {}; + while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) + { } hp_sdc.ibf = 0; write_unlock_irqrestore(lock, flags); } /************************ Interrupt context functions ************************/ -static void hp_sdc_take (int irq, void *dev_id, uint8_t status, uint8_t data) { +static void hp_sdc_take(int irq, void *dev_id, uint8_t status, uint8_t data) +{ hp_sdc_transaction *curr; read_lock(&hp_sdc.rtq_lock); if (hp_sdc.rcurr < 0) { - read_unlock(&hp_sdc.rtq_lock); + read_unlock(&hp_sdc.rtq_lock); return; } curr = hp_sdc.tq[hp_sdc.rcurr]; @@ -183,25 +193,27 @@ static void hp_sdc_take (int irq, void *dev_id, uint8_t status, uint8_t data) { if (hp_sdc.rqty <= 0) { /* All data has been gathered. */ - if(curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) { - if (curr->act.semaphore) up(curr->act.semaphore); - } - if(curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) { + if (curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) + if (curr->act.semaphore) + up(curr->act.semaphore); + + if (curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) if (curr->act.irqhook) curr->act.irqhook(irq, dev_id, status, data); - } + curr->actidx = curr->idx; curr->idx++; /* Return control of this transaction */ write_lock(&hp_sdc.rtq_lock); - hp_sdc.rcurr = -1; + hp_sdc.rcurr = -1; hp_sdc.rqty = 0; write_unlock(&hp_sdc.rtq_lock); tasklet_schedule(&hp_sdc.task); } } -static irqreturn_t hp_sdc_isr(int irq, void *dev_id) { +static irqreturn_t hp_sdc_isr(int irq, void *dev_id) +{ uint8_t status, data; status = hp_sdc_status_in8(); @@ -209,67 +221,74 @@ static irqreturn_t hp_sdc_isr(int irq, void *dev_id) { data = hp_sdc_data_in8(); /* For now we are ignoring these until we get the SDC to behave. */ - if (((status & 0xf1) == 0x51) && data == 0x82) { - return IRQ_HANDLED; - } + if (((status & 0xf1) == 0x51) && data == 0x82) + return IRQ_HANDLED; - switch(status & HP_SDC_STATUS_IRQMASK) { - case 0: /* This case is not documented. */ + switch (status & HP_SDC_STATUS_IRQMASK) { + case 0: /* This case is not documented. */ break; - case HP_SDC_STATUS_USERTIMER: - case HP_SDC_STATUS_PERIODIC: - case HP_SDC_STATUS_TIMER: + + case HP_SDC_STATUS_USERTIMER: + case HP_SDC_STATUS_PERIODIC: + case HP_SDC_STATUS_TIMER: read_lock(&hp_sdc.hook_lock); - if (hp_sdc.timer != NULL) + if (hp_sdc.timer != NULL) hp_sdc.timer(irq, dev_id, status, data); read_unlock(&hp_sdc.hook_lock); break; - case HP_SDC_STATUS_REG: + + case HP_SDC_STATUS_REG: hp_sdc_take(irq, dev_id, status, data); break; - case HP_SDC_STATUS_HILCMD: - case HP_SDC_STATUS_HILDATA: + + case HP_SDC_STATUS_HILCMD: + case HP_SDC_STATUS_HILDATA: read_lock(&hp_sdc.hook_lock); if (hp_sdc.hil != NULL) hp_sdc.hil(irq, dev_id, status, data); read_unlock(&hp_sdc.hook_lock); break; - case HP_SDC_STATUS_PUP: + + case HP_SDC_STATUS_PUP: read_lock(&hp_sdc.hook_lock); if (hp_sdc.pup != NULL) hp_sdc.pup(irq, dev_id, status, data); - else printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n"); + else + printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n"); read_unlock(&hp_sdc.hook_lock); break; - default: + + default: read_lock(&hp_sdc.hook_lock); if (hp_sdc.cooked != NULL) hp_sdc.cooked(irq, dev_id, status, data); read_unlock(&hp_sdc.hook_lock); break; } + return IRQ_HANDLED; } -static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id) { +static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id) +{ int status; - + status = hp_sdc_status_in8(); printk(KERN_WARNING PREFIX "NMI !\n"); -#if 0 +#if 0 if (status & HP_SDC_NMISTATUS_FHS) { read_lock(&hp_sdc.hook_lock); - if (hp_sdc.timer != NULL) + if (hp_sdc.timer != NULL) hp_sdc.timer(irq, dev_id, status, 0); read_unlock(&hp_sdc.hook_lock); - } - else { + } else { /* TODO: pass this on to the HIL handler, or do SAK here? */ printk(KERN_WARNING PREFIX "HIL NMI\n"); } #endif + return IRQ_HANDLED; } @@ -278,13 +297,17 @@ static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id) { unsigned long hp_sdc_put(void); -static void hp_sdc_tasklet(unsigned long foo) { - +static void hp_sdc_tasklet(unsigned long foo) +{ write_lock_irq(&hp_sdc.rtq_lock); + if (hp_sdc.rcurr >= 0) { struct timeval tv; + do_gettimeofday(&tv); - if (tv.tv_sec > hp_sdc.rtv.tv_sec) tv.tv_usec += 1000000; + if (tv.tv_sec > hp_sdc.rtv.tv_sec) + tv.tv_usec += USEC_PER_SEC; + if (tv.tv_usec - hp_sdc.rtv.tv_usec > HP_SDC_MAX_REG_DELAY) { hp_sdc_transaction *curr; uint8_t tmp; @@ -300,27 +323,29 @@ static void hp_sdc_tasklet(unsigned long foo) { hp_sdc.rqty = 0; tmp = curr->seq[curr->actidx]; curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD; - if(tmp & HP_SDC_ACT_SEMAPHORE) { - if (curr->act.semaphore) + if (tmp & HP_SDC_ACT_SEMAPHORE) + if (curr->act.semaphore) up(curr->act.semaphore); - } - if(tmp & HP_SDC_ACT_CALLBACK) { + + if (tmp & HP_SDC_ACT_CALLBACK) { /* Note this means that irqhooks may be called * in tasklet/bh context. */ - if (curr->act.irqhook) + if (curr->act.irqhook) curr->act.irqhook(0, NULL, 0, 0); } + curr->actidx = curr->idx; curr->idx++; - hp_sdc.rcurr = -1; + hp_sdc.rcurr = -1; } } write_unlock_irq(&hp_sdc.rtq_lock); hp_sdc_put(); } -unsigned long hp_sdc_put(void) { +unsigned long hp_sdc_put(void) +{ hp_sdc_transaction *curr; uint8_t act; int idx, curridx; @@ -333,19 +358,24 @@ unsigned long hp_sdc_put(void) { requires output, so we skip to the administrativa. */ if (hp_sdc.ibf) { hp_sdc_status_in8(); - if (hp_sdc.ibf) goto finish; + if (hp_sdc.ibf) + goto finish; } anew: /* See if we are in the middle of a sequence. */ - if (hp_sdc.wcurr < 0) hp_sdc.wcurr = 0; + if (hp_sdc.wcurr < 0) + hp_sdc.wcurr = 0; read_lock_irq(&hp_sdc.rtq_lock); - if (hp_sdc.rcurr == hp_sdc.wcurr) hp_sdc.wcurr++; + if (hp_sdc.rcurr == hp_sdc.wcurr) + hp_sdc.wcurr++; read_unlock_irq(&hp_sdc.rtq_lock); - if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0; + if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) + hp_sdc.wcurr = 0; curridx = hp_sdc.wcurr; - if (hp_sdc.tq[curridx] != NULL) goto start; + if (hp_sdc.tq[curridx] != NULL) + goto start; while (++curridx != hp_sdc.wcurr) { if (curridx >= HP_SDC_QUEUE_LEN) { @@ -358,7 +388,8 @@ unsigned long hp_sdc_put(void) { continue; } read_unlock_irq(&hp_sdc.rtq_lock); - if (hp_sdc.tq[curridx] != NULL) break; /* Found one. */ + if (hp_sdc.tq[curridx] != NULL) + break; /* Found one. */ } if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */ curridx = -1; @@ -374,7 +405,8 @@ unsigned long hp_sdc_put(void) { goto finish; } - if (hp_sdc.wcurr == -1) goto done; + if (hp_sdc.wcurr == -1) + goto done; curr = hp_sdc.tq[curridx]; idx = curr->actidx; @@ -383,20 +415,23 @@ unsigned long hp_sdc_put(void) { hp_sdc.tq[curridx] = NULL; /* Interleave outbound data between the transactions. */ hp_sdc.wcurr++; - if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0; - goto finish; + if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) + hp_sdc.wcurr = 0; + goto finish; } act = curr->seq[idx]; idx++; if (curr->idx >= curr->endidx) { - if (act & HP_SDC_ACT_DEALLOC) kfree(curr); + if (act & HP_SDC_ACT_DEALLOC) + kfree(curr); hp_sdc.tq[curridx] = NULL; /* Interleave outbound data between the transactions. */ hp_sdc.wcurr++; - if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0; - goto finish; + if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) + hp_sdc.wcurr = 0; + goto finish; } while (act & HP_SDC_ACT_PRECMD) { @@ -409,9 +444,10 @@ unsigned long hp_sdc_put(void) { curr->idx++; /* act finished? */ if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD) - goto actdone; + goto actdone; /* skip quantity field if data-out sequence follows. */ - if (act & HP_SDC_ACT_DATAOUT) curr->idx++; + if (act & HP_SDC_ACT_DATAOUT) + curr->idx++; goto finish; } if (act & HP_SDC_ACT_DATAOUT) { @@ -423,15 +459,15 @@ unsigned long hp_sdc_put(void) { hp_sdc_data_out8(curr->seq[curr->idx]); curr->idx++; /* act finished? */ - if ((curr->idx - idx >= qty) && - ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT)) + if (curr->idx - idx >= qty && + (act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT) goto actdone; goto finish; } idx += qty; act &= ~HP_SDC_ACT_DATAOUT; - } - else while (act & HP_SDC_ACT_DATAREG) { + } else + while (act & HP_SDC_ACT_DATAREG) { int mask; uint8_t w7[4]; @@ -445,26 +481,30 @@ unsigned long hp_sdc_put(void) { act &= ~HP_SDC_ACT_DATAREG; break; } - + w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0]; w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1]; w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2]; w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3]; - + if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 || - w7[hp_sdc.wi-0x70] == hp_sdc.r7[hp_sdc.wi-0x70]) { + w7[hp_sdc.wi - 0x70] == hp_sdc.r7[hp_sdc.wi - 0x70]) { int i = 0; - /* Need to point the write index register */ - while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++; + /* Need to point the write index register */ + while (i < 4 && w7[i] == hp_sdc.r7[i]) + i++; + if (i < 4) { hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i); hp_sdc.wi = 0x70 + i; goto finish; } + idx++; if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG) goto actdone; + curr->idx = idx; act &= ~HP_SDC_ACT_DATAREG; break; @@ -476,12 +516,13 @@ unsigned long hp_sdc_put(void) { { int i = 0; - while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++; + while ((i < 4) && w7[i] == hp_sdc.r7[i]) + i++; if (i >= 4) { curr->idx = idx + 1; - if ((act & HP_SDC_ACT_DURING) == + if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG) - goto actdone; + goto actdone; } } goto finish; @@ -497,7 +538,7 @@ unsigned long hp_sdc_put(void) { if (act & HP_SDC_ACT_POSTCMD) { - uint8_t postcmd; + uint8_t postcmd; /* curr->idx should == idx at this point. */ postcmd = curr->seq[idx]; @@ -505,12 +546,12 @@ unsigned long hp_sdc_put(void) { if (act & HP_SDC_ACT_DATAIN) { /* Start a new read */ - hp_sdc.rqty = curr->seq[curr->idx]; + hp_sdc.rqty = curr->seq[curr->idx]; do_gettimeofday(&hp_sdc.rtv); curr->idx++; /* Still need to lock here in case of spurious irq. */ write_lock_irq(&hp_sdc.rtq_lock); - hp_sdc.rcurr = curridx; + hp_sdc.rcurr = curridx; write_unlock_irq(&hp_sdc.rtq_lock); hp_sdc_status_out8(postcmd); goto finish; @@ -519,75 +560,86 @@ unsigned long hp_sdc_put(void) { goto actdone; } -actdone: - if (act & HP_SDC_ACT_SEMAPHORE) { + actdone: + if (act & HP_SDC_ACT_SEMAPHORE) up(curr->act.semaphore); - } - else if (act & HP_SDC_ACT_CALLBACK) { + else if (act & HP_SDC_ACT_CALLBACK) curr->act.irqhook(0,NULL,0,0); - } + if (curr->idx >= curr->endidx) { /* This transaction is over. */ - if (act & HP_SDC_ACT_DEALLOC) kfree(curr); + if (act & HP_SDC_ACT_DEALLOC) + kfree(curr); hp_sdc.tq[curridx] = NULL; - } - else { + } else { curr->actidx = idx + 1; curr->idx = idx + 2; } /* Interleave outbound data between the transactions. */ hp_sdc.wcurr++; - if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0; + if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) + hp_sdc.wcurr = 0; finish: - /* If by some quirk IBF has cleared and our ISR has run to + /* If by some quirk IBF has cleared and our ISR has run to see that that has happened, do it all again. */ - if (!hp_sdc.ibf && limit++ < 20) goto anew; + if (!hp_sdc.ibf && limit++ < 20) + goto anew; done: - if (hp_sdc.wcurr >= 0) tasklet_schedule(&hp_sdc.task); + if (hp_sdc.wcurr >= 0) + tasklet_schedule(&hp_sdc.task); write_unlock(&hp_sdc.lock); + return 0; } /******* Functions called in either user or kernel context ****/ -int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) { - unsigned long flags; +int __hp_sdc_enqueue_transaction(hp_sdc_transaction *this) +{ int i; if (this == NULL) { - tasklet_schedule(&hp_sdc.task); + BUG(); return -EINVAL; - }; - - write_lock_irqsave(&hp_sdc.lock, flags); + } /* Can't have same transaction on queue twice */ - for (i=0; i < HP_SDC_QUEUE_LEN; i++) - if (hp_sdc.tq[i] == this) goto fail; + for (i = 0; i < HP_SDC_QUEUE_LEN; i++) + if (hp_sdc.tq[i] == this) + goto fail; this->actidx = 0; this->idx = 1; /* Search for empty slot */ - for (i=0; i < HP_SDC_QUEUE_LEN; i++) { + for (i = 0; i < HP_SDC_QUEUE_LEN; i++) if (hp_sdc.tq[i] == NULL) { hp_sdc.tq[i] = this; - write_unlock_irqrestore(&hp_sdc.lock, flags); tasklet_schedule(&hp_sdc.task); return 0; } - } - write_unlock_irqrestore(&hp_sdc.lock, flags); + printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); return -EBUSY; fail: - write_unlock_irqrestore(&hp_sdc.lock,flags); printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); return -EINVAL; } -int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) { +int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) { + unsigned long flags; + int ret; + + write_lock_irqsave(&hp_sdc.lock, flags); + ret = __hp_sdc_enqueue_transaction(this); + write_unlock_irqrestore(&hp_sdc.lock,flags); + + return ret; +} + +int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) +{ unsigned long flags; int i; @@ -595,8 +647,9 @@ int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) { /* TODO: don't remove it if it's not done. */ - for (i=0; i < HP_SDC_QUEUE_LEN; i++) - if (hp_sdc.tq[i] == this) hp_sdc.tq[i] = NULL; + for (i = 0; i < HP_SDC_QUEUE_LEN; i++) + if (hp_sdc.tq[i] == this) + hp_sdc.tq[i] = NULL; write_unlock_irqrestore(&hp_sdc.lock, flags); return 0; @@ -605,11 +658,11 @@ int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) { /********************** User context functions **************************/ -int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) { - - if (callback == NULL || hp_sdc.dev == NULL) { +int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) +{ + if (callback == NULL || hp_sdc.dev == NULL) return -EINVAL; - } + write_lock_irq(&hp_sdc.hook_lock); if (hp_sdc.timer != NULL) { write_unlock_irq(&hp_sdc.hook_lock); @@ -629,11 +682,11 @@ int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) { return 0; } -int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) { - - if (callback == NULL || hp_sdc.dev == NULL) { +int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) +{ + if (callback == NULL || hp_sdc.dev == NULL) return -EINVAL; - } + write_lock_irq(&hp_sdc.hook_lock); if (hp_sdc.hil != NULL) { write_unlock_irq(&hp_sdc.hook_lock); @@ -650,11 +703,11 @@ int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) { return 0; } -int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) { - - if (callback == NULL || hp_sdc.dev == NULL) { +int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) +{ + if (callback == NULL || hp_sdc.dev == NULL) return -EINVAL; - } + write_lock_irq(&hp_sdc.hook_lock); if (hp_sdc.cooked != NULL) { write_unlock_irq(&hp_sdc.hook_lock); @@ -672,9 +725,8 @@ int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) { return 0; } -int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) { - - +int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) +{ write_lock_irq(&hp_sdc.hook_lock); if ((callback != hp_sdc.timer) || (hp_sdc.timer == NULL)) { @@ -694,8 +746,8 @@ int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) { return 0; } -int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) { - +int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) +{ write_lock_irq(&hp_sdc.hook_lock); if ((callback != hp_sdc.hil) || (hp_sdc.hil == NULL)) { @@ -715,8 +767,8 @@ int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) { return 0; } -int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) { - +int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) +{ write_lock_irq(&hp_sdc.hook_lock); if ((callback != hp_sdc.cooked) || (hp_sdc.cooked == NULL)) { @@ -738,7 +790,8 @@ int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) { /************************* Keepalive timer task *********************/ -void hp_sdc_kicker (unsigned long data) { +void hp_sdc_kicker (unsigned long data) +{ tasklet_schedule(&hp_sdc.task); /* Re-insert the periodic task. */ mod_timer(&hp_sdc.kicker, jiffies + HZ); @@ -748,12 +801,12 @@ void hp_sdc_kicker (unsigned long data) { #if defined(__hppa__) -static struct parisc_device_id hp_sdc_tbl[] = { +static const struct parisc_device_id hp_sdc_tbl[] = { { - .hw_type = HPHW_FIO, + .hw_type = HPHW_FIO, .hversion_rev = HVERSION_REV_ANY_ID, .hversion = HVERSION_ANY_ID, - .sversion = 0x73, + .sversion = 0x73, }, { 0, } }; @@ -772,16 +825,15 @@ static struct parisc_driver hp_sdc_driver = { static int __init hp_sdc_init(void) { - int i; char *errstr; hp_sdc_transaction t_sync; uint8_t ts_sync[6]; struct semaphore s_sync; - rwlock_init(&hp_sdc.lock); - rwlock_init(&hp_sdc.ibf_lock); - rwlock_init(&hp_sdc.rtq_lock); - rwlock_init(&hp_sdc.hook_lock); + rwlock_init(&hp_sdc.lock); + rwlock_init(&hp_sdc.ibf_lock); + rwlock_init(&hp_sdc.rtq_lock); + rwlock_init(&hp_sdc.hook_lock); hp_sdc.timer = NULL; hp_sdc.hil = NULL; @@ -796,7 +848,8 @@ static int __init hp_sdc_init(void) hp_sdc.r7[3] = 0xff; hp_sdc.ibf = 1; - for (i = 0; i < HP_SDC_QUEUE_LEN; i++) hp_sdc.tq[i] = NULL; + memset(&hp_sdc.tq, 0, sizeof(hp_sdc.tq)); + hp_sdc.wcurr = -1; hp_sdc.rcurr = -1; hp_sdc.rqty = 0; @@ -804,27 +857,32 @@ static int __init hp_sdc_init(void) hp_sdc.dev_err = -ENODEV; errstr = "IO not found for"; - if (!hp_sdc.base_io) goto err0; + if (!hp_sdc.base_io) + goto err0; errstr = "IRQ not found for"; - if (!hp_sdc.irq) goto err0; + if (!hp_sdc.irq) + goto err0; hp_sdc.dev_err = -EBUSY; #if defined(__hppa__) errstr = "IO not available for"; - if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) goto err0; -#endif + if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) + goto err0; +#endif errstr = "IRQ not available for"; - if(request_irq(hp_sdc.irq, &hp_sdc_isr, 0, "HP SDC", - (void *) hp_sdc.base_io)) goto err1; + if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM, + "HP SDC", &hp_sdc)) + goto err1; errstr = "NMI not available for"; - if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, 0, "HP SDC NMI", - (void *) hp_sdc.base_io)) goto err2; + if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, IRQF_SHARED, + "HP SDC NMI", &hp_sdc)) + goto err2; - printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n", + printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n", (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); hp_sdc_status_in8(); @@ -854,13 +912,14 @@ static int __init hp_sdc_init(void) hp_sdc.dev_err = 0; return 0; err2: - free_irq(hp_sdc.irq, NULL); + free_irq(hp_sdc.irq, &hp_sdc); err1: release_region(hp_sdc.data_io, 2); err0: - printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n", + printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n", errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); hp_sdc.dev = NULL; + return hp_sdc.dev_err; } @@ -868,8 +927,10 @@ static int __init hp_sdc_init(void) static int __init hp_sdc_init_hppa(struct parisc_device *d) { - if (!d) return 1; - if (hp_sdc.dev != NULL) return 1; /* We only expect one SDC */ + if (!d) + return 1; + if (hp_sdc.dev != NULL) + return 1; /* We only expect one SDC */ hp_sdc.dev = d; hp_sdc.irq = d->irq; @@ -898,18 +959,16 @@ static void hp_sdc_exit(void) /* Wait until we know this has been processed by the i8042 */ hp_sdc_spin_ibf(); - free_irq(hp_sdc.nmi, NULL); - free_irq(hp_sdc.irq, NULL); + free_irq(hp_sdc.nmi, &hp_sdc); + free_irq(hp_sdc.irq, &hp_sdc); write_unlock_irq(&hp_sdc.lock); del_timer(&hp_sdc.kicker); tasklet_kill(&hp_sdc.task); -/* release_region(hp_sdc.data_io, 2); */ - #if defined(__hppa__) - if (unregister_parisc_driver(&hp_sdc_driver)) + if (unregister_parisc_driver(&hp_sdc_driver)) printk(KERN_WARNING PREFIX "Error unregistering HP SDC"); #endif } @@ -923,7 +982,7 @@ static int __init hp_sdc_register(void) mm_segment_t fs; unsigned char i; #endif - + hp_sdc.dev = NULL; hp_sdc.dev_err = 0; #if defined(__hppa__) @@ -960,8 +1019,8 @@ static int __init hp_sdc_register(void) tq_init.seq = tq_init_seq; tq_init.act.semaphore = &tq_init_sem; - tq_init_seq[0] = - HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; + tq_init_seq[0] = + HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; tq_init_seq[1] = HP_SDC_CMD_READ_KCC; tq_init_seq[2] = 1; tq_init_seq[3] = 0; @@ -979,13 +1038,13 @@ static int __init hp_sdc_register(void) } hp_sdc.r11 = tq_init_seq[4]; if (hp_sdc.r11 & HP_SDC_CFG_NEW) { - char *str; + const char *str; printk(KERN_INFO PREFIX "New style SDC\n"); tq_init_seq[1] = HP_SDC_CMD_READ_XTD; tq_init.actidx = 0; tq_init.idx = 1; down(&tq_init_sem); - hp_sdc_enqueue_transaction(&tq_init); + hp_sdc_enqueue_transaction(&tq_init); down(&tq_init_sem); up(&tq_init_sem); if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { @@ -995,15 +1054,13 @@ static int __init hp_sdc_register(void) hp_sdc.r7e = tq_init_seq[4]; HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str) printk(KERN_INFO PREFIX "Revision: %s\n", str); - if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) { + if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) printk(KERN_INFO PREFIX "TI SN76494 beeper present\n"); - } - if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) { + if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n"); - } printk(KERN_INFO PREFIX "Spunking the self test register to force PUP " "on next firmware reset.\n"); - tq_init_seq[0] = HP_SDC_ACT_PRECMD | + tq_init_seq[0] = HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE; tq_init_seq[1] = HP_SDC_CMD_SET_STR; tq_init_seq[2] = 1; @@ -1012,14 +1069,12 @@ static int __init hp_sdc_register(void) tq_init.idx = 1; tq_init.endidx = 4; down(&tq_init_sem); - hp_sdc_enqueue_transaction(&tq_init); + hp_sdc_enqueue_transaction(&tq_init); down(&tq_init_sem); up(&tq_init_sem); - } - else { - printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n", + } else + printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n", (hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087"); - } return 0; } @@ -1027,13 +1082,13 @@ static int __init hp_sdc_register(void) module_init(hp_sdc_register); module_exit(hp_sdc_exit); -/* Timing notes: These measurements taken on my 64MHz 7100-LC (715/64) +/* Timing notes: These measurements taken on my 64MHz 7100-LC (715/64) * cycles cycles-adj time * between two consecutive mfctl(16)'s: 4 n/a 63ns * hp_sdc_spin_ibf when idle: 119 115 1.7us * gsc_writeb status register: 83 79 1.2us * IBF to clear after sending SET_IM: 6204 6006 93us - * IBF to clear after sending LOAD_RT: 4467 4352 68us + * IBF to clear after sending LOAD_RT: 4467 4352 68us * IBF to clear after sending two LOAD_RTs: 18974 18859 295us * READ_T1, read status/data, IRQ, call handler: 35564 n/a 556us * cmd to ~IBF READ_T1 2nd time right after: 5158403 n/a 81ms diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index aa4a8a4ccfd..c45ea74d53e 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -58,12 +58,13 @@ struct hp_sdc_mlc_priv_s { } hp_sdc_mlc_priv; /************************* Interrupt context ******************************/ -static void hp_sdc_mlc_isr (int irq, void *dev_id, - uint8_t status, uint8_t data) { - int idx; +static void hp_sdc_mlc_isr (int irq, void *dev_id, + uint8_t status, uint8_t data) +{ + int idx; hil_mlc *mlc = &hp_sdc_mlc; - write_lock(&(mlc->lock)); + write_lock(&mlc->lock); if (mlc->icount < 0) { printk(KERN_WARNING PREFIX "HIL Overflow!\n"); up(&mlc->isem); @@ -73,239 +74,232 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id, if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) { mlc->ipacket[idx] |= data | HIL_ERR_INT; mlc->icount--; - if (hp_sdc_mlc_priv.got5x) goto check; - if (!idx) goto check; - if ((mlc->ipacket[idx-1] & HIL_PKT_ADDR_MASK) != + if (hp_sdc_mlc_priv.got5x || !idx) + goto check; + if ((mlc->ipacket[idx - 1] & HIL_PKT_ADDR_MASK) != (mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) { mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK; - mlc->ipacket[idx] |= (mlc->ipacket[idx-1] - & HIL_PKT_ADDR_MASK); + mlc->ipacket[idx] |= (mlc->ipacket[idx - 1] + & HIL_PKT_ADDR_MASK); } goto check; } /* We know status is 5X */ - if (data & HP_SDC_HIL_ISERR) goto err; - mlc->ipacket[idx] = + if (data & HP_SDC_HIL_ISERR) + goto err; + mlc->ipacket[idx] = (data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT; hp_sdc_mlc_priv.got5x = 1; goto out; check: hp_sdc_mlc_priv.got5x = 0; - if (mlc->imatch == 0) goto done; - if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) - && (mlc->ipacket[idx] == (mlc->imatch | idx))) goto done; - if (mlc->ipacket[idx] == mlc->imatch) goto done; + if (mlc->imatch == 0) + goto done; + if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) + && (mlc->ipacket[idx] == (mlc->imatch | idx))) + goto done; + if (mlc->ipacket[idx] == mlc->imatch) + goto done; goto out; - err: + err: printk(KERN_DEBUG PREFIX "err code %x\n", data); + switch (data) { case HP_SDC_HIL_RC_DONE: printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n"); break; + case HP_SDC_HIL_ERR: - mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR | - HIL_ERR_FERR | HIL_ERR_FOF; + mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR | + HIL_ERR_FERR | HIL_ERR_FOF; break; + case HP_SDC_HIL_TO: mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR; break; + case HP_SDC_HIL_RC: printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n"); break; + default: printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data); break; } + /* No more data will be coming due to an error. */ done: tasklet_schedule(mlc->tasklet); - up(&(mlc->isem)); + up(&mlc->isem); out: - write_unlock(&(mlc->lock)); + write_unlock(&mlc->lock); } /******************** Tasklet or userspace context functions ****************/ -static int hp_sdc_mlc_in (hil_mlc *mlc, suseconds_t timeout) { - unsigned long flags; +static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) +{ struct hp_sdc_mlc_priv_s *priv; int rc = 2; priv = mlc->priv; - write_lock_irqsave(&(mlc->lock), flags); - /* Try to down the semaphore */ - if (down_trylock(&(mlc->isem))) { + if (down_trylock(&mlc->isem)) { struct timeval tv; if (priv->emtestmode) { - mlc->ipacket[0] = - HIL_ERR_INT | (mlc->opacket & - (HIL_PKT_CMD | - HIL_PKT_ADDR_MASK | + mlc->ipacket[0] = + HIL_ERR_INT | (mlc->opacket & + (HIL_PKT_CMD | + HIL_PKT_ADDR_MASK | HIL_PKT_DATA_MASK)); mlc->icount = 14; /* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */ goto wasup; } do_gettimeofday(&tv); - tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec); + tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec); if (tv.tv_usec - mlc->instart.tv_usec > mlc->intimeout) { - /* printk("!%i %i", - tv.tv_usec - mlc->instart.tv_usec, - mlc->intimeout); - */ + /* printk("!%i %i", + tv.tv_usec - mlc->instart.tv_usec, + mlc->intimeout); + */ rc = 1; - up(&(mlc->isem)); + up(&mlc->isem); } goto done; } wasup: - up(&(mlc->isem)); + up(&mlc->isem); rc = 0; - goto done; done: - write_unlock_irqrestore(&(mlc->lock), flags); return rc; } -static int hp_sdc_mlc_cts (hil_mlc *mlc) { +static int hp_sdc_mlc_cts(hil_mlc *mlc) +{ struct hp_sdc_mlc_priv_s *priv; - unsigned long flags; - priv = mlc->priv; - - write_lock_irqsave(&(mlc->lock), flags); + priv = mlc->priv; /* Try to down the semaphores -- they should be up. */ - if (down_trylock(&(mlc->isem))) { - BUG(); - goto busy; - } - if (down_trylock(&(mlc->osem))) { - BUG(); - up(&(mlc->isem)); - goto busy; - } - up(&(mlc->isem)); - up(&(mlc->osem)); + BUG_ON(down_trylock(&mlc->isem)); + BUG_ON(down_trylock(&mlc->osem)); + + up(&mlc->isem); + up(&mlc->osem); - if (down_trylock(&(mlc->csem))) { - if (priv->trans.act.semaphore != &(mlc->csem)) goto poll; - goto busy; + if (down_trylock(&mlc->csem)) { + if (priv->trans.act.semaphore != &mlc->csem) + goto poll; + else + goto busy; } - if (!(priv->tseq[4] & HP_SDC_USE_LOOP)) goto done; + + if (!(priv->tseq[4] & HP_SDC_USE_LOOP)) + goto done; poll: - priv->trans.act.semaphore = &(mlc->csem); + priv->trans.act.semaphore = &mlc->csem; priv->trans.actidx = 0; priv->trans.idx = 1; priv->trans.endidx = 5; - priv->tseq[0] = + priv->tseq[0] = HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; priv->tseq[1] = HP_SDC_CMD_READ_USE; priv->tseq[2] = 1; priv->tseq[3] = 0; priv->tseq[4] = 0; - hp_sdc_enqueue_transaction(&(priv->trans)); + __hp_sdc_enqueue_transaction(&priv->trans); busy: - write_unlock_irqrestore(&(mlc->lock), flags); return 1; done: - priv->trans.act.semaphore = &(mlc->osem); - up(&(mlc->csem)); - write_unlock_irqrestore(&(mlc->lock), flags); + priv->trans.act.semaphore = &mlc->osem; + up(&mlc->csem); return 0; } -static void hp_sdc_mlc_out (hil_mlc *mlc) { +static void hp_sdc_mlc_out(hil_mlc *mlc) +{ struct hp_sdc_mlc_priv_s *priv; - unsigned long flags; priv = mlc->priv; - write_lock_irqsave(&(mlc->lock), flags); - /* Try to down the semaphore -- it should be up. */ - if (down_trylock(&(mlc->osem))) { - BUG(); - goto done; - } + BUG_ON(down_trylock(&mlc->osem)); - if (mlc->opacket & HIL_DO_ALTER_CTRL) goto do_control; + if (mlc->opacket & HIL_DO_ALTER_CTRL) + goto do_control; do_data: if (priv->emtestmode) { - up(&(mlc->osem)); - goto done; + up(&mlc->osem); + return; } /* Shouldn't be sending commands when loop may be busy */ - if (down_trylock(&(mlc->csem))) { - BUG(); - goto done; - } - up(&(mlc->csem)); + BUG_ON(down_trylock(&mlc->csem)); + up(&mlc->csem); priv->trans.actidx = 0; priv->trans.idx = 1; - priv->trans.act.semaphore = &(mlc->osem); + priv->trans.act.semaphore = &mlc->osem; priv->trans.endidx = 6; - priv->tseq[0] = + priv->tseq[0] = HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE; priv->tseq[1] = 0x7; - priv->tseq[2] = - (mlc->opacket & + priv->tseq[2] = + (mlc->opacket & (HIL_PKT_ADDR_MASK | HIL_PKT_CMD)) >> HIL_PKT_ADDR_SHIFT; - priv->tseq[3] = - (mlc->opacket & HIL_PKT_DATA_MASK) + priv->tseq[3] = + (mlc->opacket & HIL_PKT_DATA_MASK) >> HIL_PKT_DATA_SHIFT; priv->tseq[4] = 0; /* No timeout */ - if (priv->tseq[3] == HIL_CMD_DHR) priv->tseq[4] = 1; + if (priv->tseq[3] == HIL_CMD_DHR) + priv->tseq[4] = 1; priv->tseq[5] = HP_SDC_CMD_DO_HIL; goto enqueue; do_control: priv->emtestmode = mlc->opacket & HIL_CTRL_TEST; - + /* we cannot emulate this, it should not be used. */ BUG_ON((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE); - - if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY) goto control_only; - if (mlc->opacket & HIL_CTRL_APE) { - BUG(); /* Should not send command/data after engaging APE */ - goto done; - } - /* Disengaging APE this way would not be valid either since + + if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY) + goto control_only; + + /* Should not send command/data after engaging APE */ + BUG_ON(mlc->opacket & HIL_CTRL_APE); + + /* Disengaging APE this way would not be valid either since * the loop must be allowed to idle. * - * So, it works out that we really never actually send control - * and data when using SDC, we just send the data. + * So, it works out that we really never actually send control + * and data when using SDC, we just send the data. */ goto do_data; control_only: priv->trans.actidx = 0; priv->trans.idx = 1; - priv->trans.act.semaphore = &(mlc->osem); + priv->trans.act.semaphore = &mlc->osem; priv->trans.endidx = 4; - priv->tseq[0] = + priv->tseq[0] = HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE; priv->tseq[1] = HP_SDC_CMD_SET_LPC; priv->tseq[2] = 1; - // priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC; + /* priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC; */ priv->tseq[3] = 0; if (mlc->opacket & HIL_CTRL_APE) { priv->tseq[3] |= HP_SDC_LPC_APE_IPF; - down_trylock(&(mlc->csem)); - } + down_trylock(&mlc->csem); + } enqueue: - hp_sdc_enqueue_transaction(&(priv->trans)); - done: - write_unlock_irqrestore(&(mlc->lock), flags); + hp_sdc_enqueue_transaction(&priv->trans); } static int __init hp_sdc_mlc_init(void) @@ -316,18 +310,18 @@ static int __init hp_sdc_mlc_init(void) hp_sdc_mlc_priv.emtestmode = 0; hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq; - hp_sdc_mlc_priv.trans.act.semaphore = &(mlc->osem); + hp_sdc_mlc_priv.trans.act.semaphore = &mlc->osem; hp_sdc_mlc_priv.got5x = 0; - mlc->cts = &hp_sdc_mlc_cts; - mlc->in = &hp_sdc_mlc_in; - mlc->out = &hp_sdc_mlc_out; + mlc->cts = &hp_sdc_mlc_cts; + mlc->in = &hp_sdc_mlc_in; + mlc->out = &hp_sdc_mlc_out; + mlc->priv = &hp_sdc_mlc_priv; if (hil_mlc_register(mlc)) { printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n"); goto err0; } - mlc->priv = &hp_sdc_mlc_priv; if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) { printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n"); @@ -335,10 +329,9 @@ static int __init hp_sdc_mlc_init(void) } return 0; err1: - if (hil_mlc_unregister(mlc)) { + if (hil_mlc_unregister(mlc)) printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" "This is bad. Could cause an oops.\n"); - } err0: return -EBUSY; } @@ -346,14 +339,14 @@ static int __init hp_sdc_mlc_init(void) static void __exit hp_sdc_mlc_exit(void) { hil_mlc *mlc = &hp_sdc_mlc; - if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr)) { + + if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr)) printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n" "This is bad. Could cause an oops.\n"); - } - if (hil_mlc_unregister(mlc)) { + + if (hil_mlc_unregister(mlc)) printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" "This is bad. Could cause an oops.\n"); - } } module_init(hp_sdc_mlc_init); diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index d36bd5475b6..6858bc58f0f 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -160,6 +160,28 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, }, { + /* + * No data is coming from the touchscreen unless KBC + * is in legacy mode. + */ + .ident = "Panasonic CF-29", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), + }, + }, + { + /* + * Errors on MUX ports are reported without raising AUXDATA + * causing "spurious NAK" messages. + */ + .ident = "HP Pavilion DV4017EA", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), + }, + }, + { .ident = "Toshiba P10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), @@ -280,6 +302,8 @@ static struct pnp_driver i8042_pnp_kbd_driver = { }; static struct pnp_device_id pnp_aux_devids[] = { + { .id = "FJC6000", .driver_data = 0 }, + { .id = "FJC6001", .driver_data = 0 }, { .id = "PNP0f03", .driver_data = 0 }, { .id = "PNP0f0b", .driver_data = 0 }, { .id = "PNP0f0e", .driver_data = 0 }, diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index db9cca3b65e..3888dc307e0 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -526,6 +526,33 @@ static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * i8042_toggle_aux - enables or disables AUX port on i8042 via command and + * verifies success by readinng CTR. Used when testing for presence of AUX + * port. + */ +static int __devinit i8042_toggle_aux(int on) +{ + unsigned char param; + int i; + + if (i8042_command(¶m, + on ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE)) + return -1; + + /* some chips need some time to set the I8042_CTR_AUXDIS bit */ + for (i = 0; i < 100; i++) { + udelay(50); + + if (i8042_command(¶m, I8042_CMD_CTL_RCTR)) + return -1; + + if (!(param & I8042_CTR_AUXDIS) == on) + return 0; + } + + return -1; +} /* * i8042_check_aux() applies as much paranoia as it can at detecting @@ -580,16 +607,12 @@ static int __devinit i8042_check_aux(void) * Bit assignment test - filters out PS/2 i8042's in AT mode */ - if (i8042_command(¶m, I8042_CMD_AUX_DISABLE)) - return -1; - if (i8042_command(¶m, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS)) { + if (i8042_toggle_aux(0)) { printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n"); } - if (i8042_command(¶m, I8042_CMD_AUX_ENABLE)) - return -1; - if (i8042_command(¶m, I8042_CMD_CTL_RCTR) || (param & I8042_CTR_AUXDIS)) + if (i8042_toggle_aux(1)) return -1; /* @@ -768,6 +791,13 @@ static void i8042_controller_reset(void) i8042_flush(); /* + * Disable both KBD and AUX interfaces so they don't get in the way + */ + + i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; + i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT); + +/* * Disable MUX mode if present. */ diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig new file mode 100644 index 00000000000..12dfb0eb326 --- /dev/null +++ b/drivers/input/tablet/Kconfig @@ -0,0 +1,74 @@ +# +# Tablet driver configuration +# +menuconfig INPUT_TABLET + bool "Tablets" + help + Say Y here, and a list of supported tablets will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_TABLET + +config TABLET_USB_ACECAD + tristate "Acecad Flair tablet support (USB)" + select USB + help + Say Y here if you want to use the USB version of the Acecad Flair + tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called acecad. + +config TABLET_USB_AIPTEK + tristate "Aiptek 6000U/8000U tablet support (USB)" + select USB + help + Say Y here if you want to use the USB version of the Aiptek 6000U + or Aiptek 8000U tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called aiptek. + +config TABLET_USB_GTCO + tristate "GTCO CalComp/InterWrite USB Support" + depends on USB && INPUT + help + Say Y here if you want to use the USB version of the GTCO + CalComp/InterWrite Tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called gtco. + +config TABLET_USB_KBTAB + tristate "KB Gear JamStudio tablet support (USB)" + select USB + help + Say Y here if you want to use the USB version of the KB Gear + JamStudio tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called kbtab. + +config TABLET_USB_WACOM + tristate "Wacom Intuos/Graphire tablet support (USB)" + select USB + help + Say Y here if you want to use the USB version of the Wacom Intuos + or Graphire tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called wacom. + +endif diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile new file mode 100644 index 00000000000..ce8b9a9cfa4 --- /dev/null +++ b/drivers/input/tablet/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the tablet drivers +# + +# Multipart objects. +wacom-objs := wacom_wac.o wacom_sys.o + +obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o +obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o +obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o +obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o +obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o diff --git a/drivers/input/tablet/acecad.c b/drivers/input/tablet/acecad.c new file mode 100644 index 00000000000..dd2310458c4 --- /dev/null +++ b/drivers/input/tablet/acecad.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr> + * Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr> + * + * USB Acecad "Acecad Flair" tablet support + * + * Changelog: + * v3.2 - Added sysfs support + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb/input.h> + +/* + * Version Information + */ +#define DRIVER_VERSION "v3.2" +#define DRIVER_DESC "USB Acecad Flair tablet driver" +#define DRIVER_LICENSE "GPL" +#define DRIVER_AUTHOR "Edouard TISSERANT <edouard.tisserant@wanadoo.fr>" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_FLAIR 0x0004 +#define USB_DEVICE_ID_302 0x0008 + +struct usb_acecad { + char name[128]; + char phys[64]; + struct usb_device *usbdev; + struct input_dev *input; + struct urb *irq; + + unsigned char *data; + dma_addr_t data_dma; +}; + +static void usb_acecad_irq(struct urb *urb) +{ + struct usb_acecad *acecad = urb->context; + unsigned char *data = acecad->data; + struct input_dev *dev = acecad->input; + int prox, status; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto resubmit; + } + + prox = (data[0] & 0x04) >> 2; + input_report_key(dev, BTN_TOOL_PEN, prox); + + if (prox) { + int x = data[1] | (data[2] << 8); + int y = data[3] | (data[4] << 8); + /* Pressure should compute the same way for flair and 302 */ + int pressure = data[5] | (data[6] << 8); + int touch = data[0] & 0x01; + int stylus = (data[0] & 0x10) >> 4; + int stylus2 = (data[0] & 0x20) >> 5; + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_abs(dev, ABS_PRESSURE, pressure); + input_report_key(dev, BTN_TOUCH, touch); + input_report_key(dev, BTN_STYLUS, stylus); + input_report_key(dev, BTN_STYLUS2, stylus2); + } + + /* event termination */ + input_sync(dev); + +resubmit: + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) + err("can't resubmit intr, %s-%s/input0, status %d", + acecad->usbdev->bus->bus_name, acecad->usbdev->devpath, status); +} + +static int usb_acecad_open(struct input_dev *dev) +{ + struct usb_acecad *acecad = input_get_drvdata(dev); + + acecad->irq->dev = acecad->usbdev; + if (usb_submit_urb(acecad->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usb_acecad_close(struct input_dev *dev) +{ + struct usb_acecad *acecad = input_get_drvdata(dev); + + usb_kill_urb(acecad->irq); +} + +static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface = intf->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + struct usb_acecad *acecad; + struct input_dev *input_dev; + int pipe, maxp; + int err = -ENOMEM; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!acecad || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + acecad->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &acecad->data_dma); + if (!acecad->data) { + err= -ENOMEM; + goto fail1; + } + + acecad->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!acecad->irq) { + err = -ENOMEM; + goto fail2; + } + + acecad->usbdev = dev; + acecad->input = input_dev; + + if (dev->manufacturer) + strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(acecad->name, " ", sizeof(acecad->name)); + strlcat(acecad->name, dev->product, sizeof(acecad->name)); + } + + usb_make_path(dev, acecad->phys, sizeof(acecad->phys)); + strlcat(acecad->phys, "/input0", sizeof(acecad->phys)); + + input_dev->name = acecad->name; + input_dev->phys = acecad->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, acecad); + + input_dev->open = usb_acecad_open; + input_dev->close = usb_acecad_close; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + input_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + input_dev->keybit[LONG(BTN_DIGI)] = BIT(BTN_TOOL_PEN) |BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2); + + switch (id->driver_info) { + case 0: + input_dev->absmax[ABS_X] = 5000; + input_dev->absmax[ABS_Y] = 3750; + input_dev->absmax[ABS_PRESSURE] = 512; + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad Flair Tablet %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + break; + case 1: + input_dev->absmax[ABS_X] = 3000; + input_dev->absmax[ABS_Y] = 2250; + input_dev->absmax[ABS_PRESSURE] = 1024; + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad 302 Tablet %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + break; + } + + input_dev->absfuzz[ABS_X] = 4; + input_dev->absfuzz[ABS_Y] = 4; + + usb_fill_int_urb(acecad->irq, dev, pipe, + acecad->data, maxp > 8 ? 8 : maxp, + usb_acecad_irq, acecad, endpoint->bInterval); + acecad->irq->transfer_dma = acecad->data_dma; + acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + err = input_register_device(acecad->input); + if (err) + goto fail2; + + usb_set_intfdata(intf, acecad); + + return 0; + + fail2: usb_buffer_free(dev, 8, acecad->data, acecad->data_dma); + fail1: input_free_device(input_dev); + kfree(acecad); + return err; +} + +static void usb_acecad_disconnect(struct usb_interface *intf) +{ + struct usb_acecad *acecad = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + if (acecad) { + usb_kill_urb(acecad->irq); + input_unregister_device(acecad->input); + usb_free_urb(acecad->irq); + usb_buffer_free(interface_to_usbdev(intf), 10, acecad->data, acecad->data_dma); + kfree(acecad); + } +} + +static struct usb_device_id usb_acecad_id_table [] = { + { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 }, + { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 }, + { } +}; + +MODULE_DEVICE_TABLE(usb, usb_acecad_id_table); + +static struct usb_driver usb_acecad_driver = { + .name = "usb_acecad", + .probe = usb_acecad_probe, + .disconnect = usb_acecad_disconnect, + .id_table = usb_acecad_id_table, +}; + +static int __init usb_acecad_init(void) +{ + int result = usb_register(&usb_acecad_driver); + if (result == 0) + info(DRIVER_VERSION ":" DRIVER_DESC); + return result; +} + +static void __exit usb_acecad_exit(void) +{ + usb_deregister(&usb_acecad_driver); +} + +module_init(usb_acecad_init); +module_exit(usb_acecad_exit); diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c new file mode 100644 index 00000000000..cc0a498763d --- /dev/null +++ b/drivers/input/tablet/aiptek.c @@ -0,0 +1,2236 @@ +/* + * Native support for the Aiptek HyperPen USB Tablets + * (4000U/5000U/6000U/8000U/12000U) + * + * Copyright (c) 2001 Chris Atenasio <chris@crud.net> + * Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net> + * + * based on wacom.c by + * Vojtech Pavlik <vojtech@suse.cz> + * Andreas Bach Aaen <abach@stofanet.dk> + * Clifford Wolf <clifford@clifford.at> + * Sam Mosel <sam.mosel@computer.org> + * James E. Blair <corvus@gnu.org> + * Daniel Egger <egger@suse.de> + * + * Many thanks to Oliver Kuechemann for his support. + * + * ChangeLog: + * v0.1 - Initial release + * v0.2 - Hack to get around fake event 28's. (Bryan W. Headley) + * v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002) + * Released to Linux 2.4.19 and 2.5.x + * v0.4 - Rewrote substantial portions of the code to deal with + * corrected control sequences, timing, dynamic configuration, + * support of 6000U - 12000U, procfs, and macro key support + * (Jan-1-2003 - Feb-5-2003, Bryan W. Headley) + * v1.0 - Added support for diagnostic messages, count of messages + * received from URB - Mar-8-2003, Bryan W. Headley + * v1.1 - added support for tablet resolution, changed DV and proximity + * some corrections - Jun-22-2003, martin schneebacher + * - Added support for the sysfs interface, deprecating the + * procfs interface for 2.5.x kernel. Also added support for + * Wheel command. Bryan W. Headley July-15-2003. + * v1.2 - Reworked jitter timer as a kernel thread. + * Bryan W. Headley November-28-2003/Jan-10-2004. + * v1.3 - Repaired issue of kernel thread going nuts on single-processor + * machines, introduced programmableDelay as a command line + * parameter. Feb 7 2004, Bryan W. Headley. + * v1.4 - Re-wire jitter so it does not require a thread. Courtesy of + * Rene van Paassen. Added reporting of physical pointer device + * (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know + * for reports 1, 6.) + * what physical device reports for reports 1, 6.) Also enabled + * MOUSE and LENS tool button modes. Renamed "rubber" to "eraser". + * Feb 20, 2004, Bryan W. Headley. + * v1.5 - Added previousJitterable, so we don't do jitter delay when the + * user is holding a button down for periods of time. + * + * NOTE: + * This kernel driver is augmented by the "Aiptek" XFree86 input + * driver for your X server, as well as the Gaiptek GUI Front-end + * "Tablet Manager". + * These three products are highly interactive with one another, + * so therefore it's easier to document them all as one subsystem. + * Please visit the project's "home page", located at, + * http://aiptektablet.sourceforge.net. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb/input.h> +#include <asm/uaccess.h> +#include <asm/unaligned.h> + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.5 (May-15-2004)" +#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio" +#define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)" + +/* + * Aiptek status packet: + * + * (returned as Report 1 - relative coordinates from mouse and stylus) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 0 0 1 + * byte1 0 0 0 0 0 BS2 BS Tip + * byte2 X7 X6 X5 X4 X3 X2 X1 X0 + * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * + * (returned as Report 2 - absolute coordinates from the stylus) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 0 1 0 + * byte1 X7 X6 X5 X4 X3 X2 X1 X0 + * byte2 X15 X14 X13 X12 X11 X10 X9 X8 + * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 + * byte5 * * * BS2 BS1 Tip IR DV + * byte6 P7 P6 P5 P4 P3 P2 P1 P0 + * byte7 P15 P14 P13 P12 P11 P10 P9 P8 + * + * (returned as Report 3 - absolute coordinates from the mouse) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 0 1 0 + * byte1 X7 X6 X5 X4 X3 X2 X1 X0 + * byte2 X15 X14 X13 X12 X11 X10 X9 X8 + * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * byte4 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 + * byte5 * * * BS2 BS1 Tip IR DV + * byte6 P7 P6 P5 P4 P3 P2 P1 P0 + * byte7 P15 P14 P13 P12 P11 P10 P9 P8 + * + * (returned as Report 4 - macrokeys from the stylus) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 1 0 0 + * byte1 0 0 0 BS2 BS Tip IR DV + * byte2 0 0 0 0 0 0 1 0 + * byte3 0 0 0 K4 K3 K2 K1 K0 + * byte4 P7 P6 P5 P4 P3 P2 P1 P0 + * byte5 P15 P14 P13 P12 P11 P10 P9 P8 + * + * (returned as Report 5 - macrokeys from the mouse) + * + * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 + * byte0 0 0 0 0 0 1 0 0 + * byte1 0 0 0 BS2 BS Tip IR DV + * byte2 0 0 0 0 0 0 1 0 + * byte3 0 0 0 K4 K3 K2 K1 K0 + * byte4 P7 P6 P5 P4 P3 P2 P1 P0 + * byte5 P15 P14 P13 P12 P11 P10 P9 P8 + * + * IR: In Range = Proximity on + * DV = Data Valid + * BS = Barrel Switch (as in, macro keys) + * BS2 also referred to as Tablet Pick + * + * Command Summary: + * + * Use report_type CONTROL (3) + * Use report_id 2 + * + * Command/Data Description Return Bytes Return Value + * 0x10/0x00 SwitchToMouse 0 + * 0x10/0x01 SwitchToTablet 0 + * 0x18/0x04 SetResolution 0 + * 0x12/0xFF AutoGainOn 0 + * 0x17/0x00 FilterOn 0 + * 0x01/0x00 GetXExtension 2 MaxX + * 0x01/0x01 GetYExtension 2 MaxY + * 0x02/0x00 GetModelCode 2 ModelCode = LOBYTE + * 0x03/0x00 GetODMCode 2 ODMCode + * 0x08/0x00 GetPressureLevels 2 =512 + * 0x04/0x00 GetFirmwareVersion 2 Firmware Version + * 0x11/0x02 EnableMacroKeys 0 + * + * To initialize the tablet: + * + * (1) Send Resolution500LPI (Command) + * (2) Query for Model code (Option Report) + * (3) Query for ODM code (Option Report) + * (4) Query for firmware (Option Report) + * (5) Query for GetXExtension (Option Report) + * (6) Query for GetYExtension (Option Report) + * (7) Query for GetPressureLevels (Option Report) + * (8) SwitchToTablet for Absolute coordinates, or + * SwitchToMouse for Relative coordinates (Command) + * (9) EnableMacroKeys (Command) + * (10) FilterOn (Command) + * (11) AutoGainOn (Command) + * + * (Step 9 can be omitted, but you'll then have no function keys.) + */ + +#define USB_VENDOR_ID_AIPTEK 0x08ca +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_SET_REPORT 0x09 + + /* PointerMode codes + */ +#define AIPTEK_POINTER_ONLY_MOUSE_MODE 0 +#define AIPTEK_POINTER_ONLY_STYLUS_MODE 1 +#define AIPTEK_POINTER_EITHER_MODE 2 + +#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a) \ + (a == AIPTEK_POINTER_ONLY_MOUSE_MODE || \ + a == AIPTEK_POINTER_EITHER_MODE) +#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a) \ + (a == AIPTEK_POINTER_ONLY_STYLUS_MODE || \ + a == AIPTEK_POINTER_EITHER_MODE) + + /* CoordinateMode code + */ +#define AIPTEK_COORDINATE_RELATIVE_MODE 0 +#define AIPTEK_COORDINATE_ABSOLUTE_MODE 1 + + /* XTilt and YTilt values + */ +#define AIPTEK_TILT_MIN (-128) +#define AIPTEK_TILT_MAX 127 +#define AIPTEK_TILT_DISABLE (-10101) + + /* Wheel values + */ +#define AIPTEK_WHEEL_MIN 0 +#define AIPTEK_WHEEL_MAX 1024 +#define AIPTEK_WHEEL_DISABLE (-10101) + + /* ToolCode values, which BTW are 0x140 .. 0x14f + * We have things set up such that if TOOL_BUTTON_FIRED_BIT is + * not set, we'll send one instance of AIPTEK_TOOL_BUTTON_xxx. + * + * Whenever the user resets the value, TOOL_BUTTON_FIRED_BIT will + * get reset. + */ +#define TOOL_BUTTON(x) ((x) & 0x14f) +#define TOOL_BUTTON_FIRED(x) ((x) & 0x200) +#define TOOL_BUTTON_FIRED_BIT 0x200 + /* toolMode codes + */ +#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN +#define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN +#define AIPTEK_TOOL_BUTTON_PENCIL_MODE BTN_TOOL_PENCIL +#define AIPTEK_TOOL_BUTTON_BRUSH_MODE BTN_TOOL_BRUSH +#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE BTN_TOOL_AIRBRUSH +#define AIPTEK_TOOL_BUTTON_ERASER_MODE BTN_TOOL_RUBBER +#define AIPTEK_TOOL_BUTTON_MOUSE_MODE BTN_TOOL_MOUSE +#define AIPTEK_TOOL_BUTTON_LENS_MODE BTN_TOOL_LENS + + /* Diagnostic message codes + */ +#define AIPTEK_DIAGNOSTIC_NA 0 +#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE 1 +#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE 2 +#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED 3 + + /* Time to wait (in ms) to help mask hand jittering + * when pressing the stylus buttons. + */ +#define AIPTEK_JITTER_DELAY_DEFAULT 50 + + /* Time to wait (in ms) in-between sending the tablet + * a command and beginning the process of reading the return + * sequence from the tablet. + */ +#define AIPTEK_PROGRAMMABLE_DELAY_25 25 +#define AIPTEK_PROGRAMMABLE_DELAY_50 50 +#define AIPTEK_PROGRAMMABLE_DELAY_100 100 +#define AIPTEK_PROGRAMMABLE_DELAY_200 200 +#define AIPTEK_PROGRAMMABLE_DELAY_300 300 +#define AIPTEK_PROGRAMMABLE_DELAY_400 400 +#define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT AIPTEK_PROGRAMMABLE_DELAY_400 + + /* Mouse button programming + */ +#define AIPTEK_MOUSE_LEFT_BUTTON 0x01 +#define AIPTEK_MOUSE_RIGHT_BUTTON 0x02 +#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x04 + + /* Stylus button programming + */ +#define AIPTEK_STYLUS_LOWER_BUTTON 0x08 +#define AIPTEK_STYLUS_UPPER_BUTTON 0x10 + + /* Length of incoming packet from the tablet + */ +#define AIPTEK_PACKET_LENGTH 8 + + /* We report in EV_MISC both the proximity and + * whether the report came from the stylus, tablet mouse + * or "unknown" -- Unknown when the tablet is in relative + * mode, because we only get report 1's. + */ +#define AIPTEK_REPORT_TOOL_UNKNOWN 0x10 +#define AIPTEK_REPORT_TOOL_STYLUS 0x20 +#define AIPTEK_REPORT_TOOL_MOUSE 0x40 + +static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT; +static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT; + +struct aiptek_features { + int odmCode; /* Tablet manufacturer code */ + int modelCode; /* Tablet model code (not unique) */ + int firmwareCode; /* prom/eeprom version */ + char usbPath[64 + 1]; /* device's physical usb path */ + char inputPath[64 + 1]; /* input device path */ +}; + +struct aiptek_settings { + int pointerMode; /* stylus-, mouse-only or either */ + int coordinateMode; /* absolute/relative coords */ + int toolMode; /* pen, pencil, brush, etc. tool */ + int xTilt; /* synthetic xTilt amount */ + int yTilt; /* synthetic yTilt amount */ + int wheel; /* synthetic wheel amount */ + int stylusButtonUpper; /* stylus upper btn delivers... */ + int stylusButtonLower; /* stylus lower btn delivers... */ + int mouseButtonLeft; /* mouse left btn delivers... */ + int mouseButtonMiddle; /* mouse middle btn delivers... */ + int mouseButtonRight; /* mouse right btn delivers... */ + int programmableDelay; /* delay for tablet programming */ + int jitterDelay; /* delay for hand jittering */ +}; + +struct aiptek { + struct input_dev *inputdev; /* input device struct */ + struct usb_device *usbdev; /* usb device struct */ + struct urb *urb; /* urb for incoming reports */ + dma_addr_t data_dma; /* our dma stuffage */ + struct aiptek_features features; /* tablet's array of features */ + struct aiptek_settings curSetting; /* tablet's current programmable */ + struct aiptek_settings newSetting; /* ... and new param settings */ + unsigned int ifnum; /* interface number for IO */ + int diagnostic; /* tablet diagnostic codes */ + unsigned long eventCount; /* event count */ + int inDelay; /* jitter: in jitter delay? */ + unsigned long endDelay; /* jitter: time when delay ends */ + int previousJitterable; /* jitterable prev value */ + unsigned char *data; /* incoming packet data */ +}; + +/* + * Permit easy lookup of keyboard events to send, versus + * the bitmap which comes from the tablet. This hides the + * issue that the F_keys are not sequentially numbered. + */ +static const int macroKeyEvents[] = { + KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, + KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, + KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, + KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, + KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0 +}; + +/*********************************************************************** + * Relative reports deliver values in 2's complement format to + * deal with negative offsets. + */ +static int aiptek_convert_from_2s_complement(unsigned char c) +{ + int ret; + unsigned char b = c; + int negate = 0; + + if ((b & 0x80) != 0) { + b = ~b; + b--; + negate = 1; + } + ret = b; + ret = (negate == 1) ? -ret : ret; + return ret; +} + +/*********************************************************************** + * aiptek_irq can receive one of six potential reports. + * The documentation for each is in the body of the function. + * + * The tablet reports on several attributes per invocation of + * aiptek_irq. Because the Linux Input Event system allows the + * transmission of ONE attribute per input_report_xxx() call, + * collation has to be done on the other end to reconstitute + * a complete tablet report. Further, the number of Input Event reports + * submitted varies, depending on what USB report type, and circumstance. + * To deal with this, EV_MSC is used to indicate an 'end-of-report' + * message. This has been an undocumented convention understood by the kernel + * tablet driver and clients such as gpm and XFree86's tablet drivers. + * + * Of the information received from the tablet, the one piece I + * cannot transmit is the proximity bit (without resorting to an EV_MSC + * convention above.) I therefore have taken over REL_MISC and ABS_MISC + * (for relative and absolute reports, respectively) for communicating + * Proximity. Why two events? I thought it interesting to know if the + * Proximity event occurred while the tablet was in absolute or relative + * mode. + * + * Other tablets use the notion of a certain minimum stylus pressure + * to infer proximity. While that could have been done, that is yet + * another 'by convention' behavior, the documentation for which + * would be spread between two (or more) pieces of software. + * + * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and + * replaced with the input_sync() method (which emits EV_SYN.) + */ + +static void aiptek_irq(struct urb *urb) +{ + struct aiptek *aiptek = urb->context; + unsigned char *data = aiptek->data; + struct input_dev *inputdev = aiptek->inputdev; + int jitterable = 0; + int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck; + + switch (urb->status) { + case 0: + /* Success */ + break; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* This urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + /* See if we are in a delay loop -- throw out report if true. + */ + if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) { + goto exit; + } + + aiptek->inDelay = 0; + aiptek->eventCount++; + + /* Report 1 delivers relative coordinates with either a stylus + * or the mouse. You do not know, however, which input + * tool generated the event. + */ + if (data[0] == 1) { + if (aiptek->curSetting.coordinateMode == + AIPTEK_COORDINATE_ABSOLUTE_MODE) { + aiptek->diagnostic = + AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE; + } else { + x = aiptek_convert_from_2s_complement(data[2]); + y = aiptek_convert_from_2s_complement(data[3]); + + /* jitterable keeps track of whether any button has been pressed. + * We're also using it to remap the physical mouse button mask + * to pseudo-settings. (We don't specifically care about it's + * value after moving/transposing mouse button bitmasks, except + * that a non-zero value indicates that one or more + * mouse button was pressed.) + */ + jitterable = data[5] & 0x07; + + left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; + right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; + middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; + + input_report_key(inputdev, BTN_LEFT, left); + input_report_key(inputdev, BTN_MIDDLE, middle); + input_report_key(inputdev, BTN_RIGHT, right); + input_report_rel(inputdev, REL_X, x); + input_report_rel(inputdev, REL_Y, y); + input_report_rel(inputdev, REL_MISC, 1 | AIPTEK_REPORT_TOOL_UNKNOWN); + + /* Wheel support is in the form of a single-event + * firing. + */ + if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) { + input_report_rel(inputdev, REL_WHEEL, + aiptek->curSetting.wheel); + aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; + } + input_sync(inputdev); + } + } + /* Report 2 is delivered only by the stylus, and delivers + * absolute coordinates. + */ + else if (data[0] == 2) { + if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; + } else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE + (aiptek->curSetting.pointerMode)) { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; + } else { + x = le16_to_cpu(get_unaligned((__le16 *) (data + 1))); + y = le16_to_cpu(get_unaligned((__le16 *) (data + 3))); + z = le16_to_cpu(get_unaligned((__le16 *) (data + 6))); + + p = (data[5] & 0x01) != 0 ? 1 : 0; + dv = (data[5] & 0x02) != 0 ? 1 : 0; + tip = (data[5] & 0x04) != 0 ? 1 : 0; + + /* Use jitterable to re-arrange button masks + */ + jitterable = data[5] & 0x18; + + bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; + pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; + + /* dv indicates 'data valid' (e.g., the tablet is in sync + * and has delivered a "correct" report) We will ignore + * all 'bad' reports... + */ + if (dv != 0) { + /* If we've not already sent a tool_button_?? code, do + * so now. Then set FIRED_BIT so it won't be resent unless + * the user forces FIRED_BIT off. + */ + if (TOOL_BUTTON_FIRED + (aiptek->curSetting.toolMode) == 0) { + input_report_key(inputdev, + TOOL_BUTTON(aiptek->curSetting.toolMode), + 1); + aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + } + + if (p != 0) { + input_report_abs(inputdev, ABS_X, x); + input_report_abs(inputdev, ABS_Y, y); + input_report_abs(inputdev, ABS_PRESSURE, z); + + input_report_key(inputdev, BTN_TOUCH, tip); + input_report_key(inputdev, BTN_STYLUS, bs); + input_report_key(inputdev, BTN_STYLUS2, pck); + + if (aiptek->curSetting.xTilt != + AIPTEK_TILT_DISABLE) { + input_report_abs(inputdev, + ABS_TILT_X, + aiptek->curSetting.xTilt); + } + if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) { + input_report_abs(inputdev, + ABS_TILT_Y, + aiptek->curSetting.yTilt); + } + + /* Wheel support is in the form of a single-event + * firing. + */ + if (aiptek->curSetting.wheel != + AIPTEK_WHEEL_DISABLE) { + input_report_abs(inputdev, + ABS_WHEEL, + aiptek->curSetting.wheel); + aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; + } + } + input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS); + input_sync(inputdev); + } + } + } + /* Report 3's come from the mouse in absolute mode. + */ + else if (data[0] == 3) { + if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE; + } else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE + (aiptek->curSetting.pointerMode)) { + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED; + } else { + x = le16_to_cpu(get_unaligned((__le16 *) (data + 1))); + y = le16_to_cpu(get_unaligned((__le16 *) (data + 3))); + + jitterable = data[5] & 0x1c; + + p = (data[5] & 0x01) != 0 ? 1 : 0; + dv = (data[5] & 0x02) != 0 ? 1 : 0; + left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; + right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; + middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; + + if (dv != 0) { + /* If we've not already sent a tool_button_?? code, do + * so now. Then set FIRED_BIT so it won't be resent unless + * the user forces FIRED_BIT off. + */ + if (TOOL_BUTTON_FIRED + (aiptek->curSetting.toolMode) == 0) { + input_report_key(inputdev, + TOOL_BUTTON(aiptek->curSetting.toolMode), + 1); + aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + } + + if (p != 0) { + input_report_abs(inputdev, ABS_X, x); + input_report_abs(inputdev, ABS_Y, y); + + input_report_key(inputdev, BTN_LEFT, left); + input_report_key(inputdev, BTN_MIDDLE, middle); + input_report_key(inputdev, BTN_RIGHT, right); + + /* Wheel support is in the form of a single-event + * firing. + */ + if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) { + input_report_abs(inputdev, + ABS_WHEEL, + aiptek->curSetting.wheel); + aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; + } + } + input_report_rel(inputdev, REL_MISC, p | AIPTEK_REPORT_TOOL_MOUSE); + input_sync(inputdev); + } + } + } + /* Report 4s come from the macro keys when pressed by stylus + */ + else if (data[0] == 4) { + jitterable = data[1] & 0x18; + + p = (data[1] & 0x01) != 0 ? 1 : 0; + dv = (data[1] & 0x02) != 0 ? 1 : 0; + tip = (data[1] & 0x04) != 0 ? 1 : 0; + bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; + pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; + + macro = data[3]; + z = le16_to_cpu(get_unaligned((__le16 *) (data + 4))); + + if (dv != 0) { + /* If we've not already sent a tool_button_?? code, do + * so now. Then set FIRED_BIT so it won't be resent unless + * the user forces FIRED_BIT off. + */ + if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) { + input_report_key(inputdev, + TOOL_BUTTON(aiptek->curSetting.toolMode), + 1); + aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + } + + if (p != 0) { + input_report_key(inputdev, BTN_TOUCH, tip); + input_report_key(inputdev, BTN_STYLUS, bs); + input_report_key(inputdev, BTN_STYLUS2, pck); + input_report_abs(inputdev, ABS_PRESSURE, z); + } + + /* For safety, we're sending key 'break' codes for the + * neighboring macro keys. + */ + if (macro > 0) { + input_report_key(inputdev, + macroKeyEvents[macro - 1], 0); + } + if (macro < 25) { + input_report_key(inputdev, + macroKeyEvents[macro + 1], 0); + } + input_report_key(inputdev, macroKeyEvents[macro], p); + input_report_abs(inputdev, ABS_MISC, + p | AIPTEK_REPORT_TOOL_STYLUS); + input_sync(inputdev); + } + } + /* Report 5s come from the macro keys when pressed by mouse + */ + else if (data[0] == 5) { + jitterable = data[1] & 0x1c; + + p = (data[1] & 0x01) != 0 ? 1 : 0; + dv = (data[1] & 0x02) != 0 ? 1 : 0; + left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; + right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; + middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; + macro = data[3]; + + if (dv != 0) { + /* If we've not already sent a tool_button_?? code, do + * so now. Then set FIRED_BIT so it won't be resent unless + * the user forces FIRED_BIT off. + */ + if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) { + input_report_key(inputdev, + TOOL_BUTTON(aiptek->curSetting.toolMode), + 1); + aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + } + + if (p != 0) { + input_report_key(inputdev, BTN_LEFT, left); + input_report_key(inputdev, BTN_MIDDLE, middle); + input_report_key(inputdev, BTN_RIGHT, right); + } + + /* For safety, we're sending key 'break' codes for the + * neighboring macro keys. + */ + if (macro > 0) { + input_report_key(inputdev, + macroKeyEvents[macro - 1], 0); + } + if (macro < 25) { + input_report_key(inputdev, + macroKeyEvents[macro + 1], 0); + } + + input_report_key(inputdev, macroKeyEvents[macro], 1); + input_report_rel(inputdev, ABS_MISC, + p | AIPTEK_REPORT_TOOL_MOUSE); + input_sync(inputdev); + } + } + /* We have no idea which tool can generate a report 6. Theoretically, + * neither need to, having been given reports 4 & 5 for such use. + * However, report 6 is the 'official-looking' report for macroKeys; + * reports 4 & 5 supposively are used to support unnamed, unknown + * hat switches (which just so happen to be the macroKeys.) + */ + else if (data[0] == 6) { + macro = le16_to_cpu(get_unaligned((__le16 *) (data + 1))); + if (macro > 0) { + input_report_key(inputdev, macroKeyEvents[macro - 1], + 0); + } + if (macro < 25) { + input_report_key(inputdev, macroKeyEvents[macro + 1], + 0); + } + + /* If we've not already sent a tool_button_?? code, do + * so now. Then set FIRED_BIT so it won't be resent unless + * the user forces FIRED_BIT off. + */ + if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) { + input_report_key(inputdev, + TOOL_BUTTON(aiptek->curSetting. + toolMode), 1); + aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + } + + input_report_key(inputdev, macroKeyEvents[macro], 1); + input_report_abs(inputdev, ABS_MISC, + 1 | AIPTEK_REPORT_TOOL_UNKNOWN); + input_sync(inputdev); + } else { + dbg("Unknown report %d", data[0]); + } + + /* Jitter may occur when the user presses a button on the stlyus + * or the mouse. What we do to prevent that is wait 'x' milliseconds + * following a 'jitterable' event, which should give the hand some time + * stabilize itself. + * + * We just introduced aiptek->previousJitterable to carry forth the + * notion that jitter occurs when the button state changes from on to off: + * a person drawing, holding a button down is not subject to jittering. + * With that in mind, changing from upper button depressed to lower button + * WILL transition through a jitter delay. + */ + + if (aiptek->previousJitterable != jitterable && + aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) { + aiptek->endDelay = jiffies + + ((aiptek->curSetting.jitterDelay * HZ) / 1000); + aiptek->inDelay = 1; + } + aiptek->previousJitterable = jitterable; + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval != 0) { + err("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); + } +} + +/*********************************************************************** + * These are the USB id's known so far. We do not identify them to + * specific Aiptek model numbers, because there has been overlaps, + * use, and reuse of id's in existing models. Certain models have + * been known to use more than one ID, indicative perhaps of + * manufacturing revisions. In any event, we consider these + * IDs to not be model-specific nor unique. + */ +static const struct usb_device_id aiptek_ids[] = { + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)}, + {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, aiptek_ids); + +/*********************************************************************** + * Open an instance of the tablet driver. + */ +static int aiptek_open(struct input_dev *inputdev) +{ + struct aiptek *aiptek = input_get_drvdata(inputdev); + + aiptek->urb->dev = aiptek->usbdev; + if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) + return -EIO; + + return 0; +} + +/*********************************************************************** + * Close an instance of the tablet driver. + */ +static void aiptek_close(struct input_dev *inputdev) +{ + struct aiptek *aiptek = input_get_drvdata(inputdev); + + usb_kill_urb(aiptek->urb); +} + +/*********************************************************************** + * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, + * where they were known as usb_set_report and usb_get_report. + */ +static int +aiptek_set_report(struct aiptek *aiptek, + unsigned char report_type, + unsigned char report_id, void *buffer, int size) +{ + return usb_control_msg(aiptek->usbdev, + usb_sndctrlpipe(aiptek->usbdev, 0), + USB_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_OUT, (report_type << 8) + report_id, + aiptek->ifnum, buffer, size, 5000); +} + +static int +aiptek_get_report(struct aiptek *aiptek, + unsigned char report_type, + unsigned char report_id, void *buffer, int size) +{ + return usb_control_msg(aiptek->usbdev, + usb_rcvctrlpipe(aiptek->usbdev, 0), + USB_REQ_GET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_IN, (report_type << 8) + report_id, + aiptek->ifnum, buffer, size, 5000); +} + +/*********************************************************************** + * Send a command to the tablet. + */ +static int +aiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data) +{ + const int sizeof_buf = 3 * sizeof(u8); + int ret; + u8 *buf; + + buf = kmalloc(sizeof_buf, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = 2; + buf[1] = command; + buf[2] = data; + + if ((ret = + aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) { + dbg("aiptek_program: failed, tried to send: 0x%02x 0x%02x", + command, data); + } + kfree(buf); + return ret < 0 ? ret : 0; +} + +/*********************************************************************** + * Retrieve information from the tablet. Querying info is defined as first + * sending the {command,data} sequence as a command, followed by a wait + * (aka, "programmaticDelay") and then a "read" request. + */ +static int +aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data) +{ + const int sizeof_buf = 3 * sizeof(u8); + int ret; + u8 *buf; + + buf = kmalloc(sizeof_buf, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = 2; + buf[1] = command; + buf[2] = data; + + if (aiptek_command(aiptek, command, data) != 0) { + kfree(buf); + return -EIO; + } + msleep(aiptek->curSetting.programmableDelay); + + if ((ret = + aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) { + dbg("aiptek_query failed: returned 0x%02x 0x%02x 0x%02x", + buf[0], buf[1], buf[2]); + ret = -EIO; + } else { + ret = le16_to_cpu(get_unaligned((__le16 *) (buf + 1))); + } + kfree(buf); + return ret; +} + +/*********************************************************************** + * Program the tablet into either absolute or relative mode. + * We also get information about the tablet's size. + */ +static int aiptek_program_tablet(struct aiptek *aiptek) +{ + int ret; + /* Execute Resolution500LPI */ + if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0) + return ret; + + /* Query getModelCode */ + if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0) + return ret; + aiptek->features.modelCode = ret & 0xff; + + /* Query getODMCode */ + if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0) + return ret; + aiptek->features.odmCode = ret; + + /* Query getFirmwareCode */ + if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0) + return ret; + aiptek->features.firmwareCode = ret; + + /* Query getXextension */ + if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0) + return ret; + aiptek->inputdev->absmin[ABS_X] = 0; + aiptek->inputdev->absmax[ABS_X] = ret - 1; + + /* Query getYextension */ + if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0) + return ret; + aiptek->inputdev->absmin[ABS_Y] = 0; + aiptek->inputdev->absmax[ABS_Y] = ret - 1; + + /* Query getPressureLevels */ + if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0) + return ret; + aiptek->inputdev->absmin[ABS_PRESSURE] = 0; + aiptek->inputdev->absmax[ABS_PRESSURE] = ret - 1; + + /* Depending on whether we are in absolute or relative mode, we will + * do a switchToTablet(absolute) or switchToMouse(relative) command. + */ + if (aiptek->curSetting.coordinateMode == + AIPTEK_COORDINATE_ABSOLUTE_MODE) { + /* Execute switchToTablet */ + if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) { + return ret; + } + } else { + /* Execute switchToMouse */ + if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) { + return ret; + } + } + + /* Enable the macro keys */ + if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0) + return ret; +#if 0 + /* Execute FilterOn */ + if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0) + return ret; +#endif + + /* Execute AutoGainOn */ + if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0) + return ret; + + /* Reset the eventCount, so we track events from last (re)programming + */ + aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA; + aiptek->eventCount = 0; + + return 0; +} + +/*********************************************************************** + * Sysfs functions. Sysfs prefers that individually-tunable parameters + * exist in their separate pseudo-files. Summary data that is immutable + * may exist in a singular file so long as you don't define a writeable + * interface. + */ + +/*********************************************************************** + * support the 'size' file -- display support + */ +static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "%dx%d\n", + aiptek->inputdev->absmax[ABS_X] + 1, + aiptek->inputdev->absmax[ABS_Y] + 1); +} + +/* These structs define the sysfs files, param #1 is the name of the + * file, param 2 is the file permissions, param 3 & 4 are to the + * output generator and input parser routines. Absence of a routine is + * permitted -- it only means can't either 'cat' the file, or send data + * to it. + */ +static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL); + +/*********************************************************************** + * support routines for the 'product_id' file + */ +static ssize_t show_tabletProductId(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", + aiptek->inputdev->id.product); +} + +static DEVICE_ATTR(product_id, S_IRUGO, show_tabletProductId, NULL); + +/*********************************************************************** + * support routines for the 'vendor_id' file + */ +static ssize_t show_tabletVendorId(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->inputdev->id.vendor); +} + +static DEVICE_ATTR(vendor_id, S_IRUGO, show_tabletVendorId, NULL); + +/*********************************************************************** + * support routines for the 'vendor' file + */ +static ssize_t show_tabletManufacturer(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int retval; + + if (aiptek == NULL) + return 0; + + retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->manufacturer); + return retval; +} + +static DEVICE_ATTR(vendor, S_IRUGO, show_tabletManufacturer, NULL); + +/*********************************************************************** + * support routines for the 'product' file + */ +static ssize_t show_tabletProduct(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int retval; + + if (aiptek == NULL) + return 0; + + retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->product); + return retval; +} + +static DEVICE_ATTR(product, S_IRUGO, show_tabletProduct, NULL); + +/*********************************************************************** + * support routines for the 'pointer_mode' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *s; + + if (aiptek == NULL) + return 0; + + switch (aiptek->curSetting.pointerMode) { + case AIPTEK_POINTER_ONLY_STYLUS_MODE: + s = "stylus"; + break; + + case AIPTEK_POINTER_ONLY_MOUSE_MODE: + s = "mouse"; + break; + + case AIPTEK_POINTER_EITHER_MODE: + s = "either"; + break; + + default: + s = "unknown"; + break; + } + return snprintf(buf, PAGE_SIZE, "%s\n", s); +} + +static ssize_t +store_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "stylus") == 0) { + aiptek->newSetting.pointerMode = + AIPTEK_POINTER_ONLY_STYLUS_MODE; + } else if (strcmp(buf, "mouse") == 0) { + aiptek->newSetting.pointerMode = AIPTEK_POINTER_ONLY_MOUSE_MODE; + } else if (strcmp(buf, "either") == 0) { + aiptek->newSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE; + } + return count; +} + +static DEVICE_ATTR(pointer_mode, + S_IRUGO | S_IWUGO, + show_tabletPointerMode, store_tabletPointerMode); + +/*********************************************************************** + * support routines for the 'coordinate_mode' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *s; + + if (aiptek == NULL) + return 0; + + switch (aiptek->curSetting.coordinateMode) { + case AIPTEK_COORDINATE_ABSOLUTE_MODE: + s = "absolute"; + break; + + case AIPTEK_COORDINATE_RELATIVE_MODE: + s = "relative"; + break; + + default: + s = "unknown"; + break; + } + return snprintf(buf, PAGE_SIZE, "%s\n", s); +} + +static ssize_t +store_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "absolute") == 0) { + aiptek->newSetting.pointerMode = + AIPTEK_COORDINATE_ABSOLUTE_MODE; + } else if (strcmp(buf, "relative") == 0) { + aiptek->newSetting.pointerMode = + AIPTEK_COORDINATE_RELATIVE_MODE; + } + return count; +} + +static DEVICE_ATTR(coordinate_mode, + S_IRUGO | S_IWUGO, + show_tabletCoordinateMode, store_tabletCoordinateMode); + +/*********************************************************************** + * support routines for the 'tool_mode' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *s; + + if (aiptek == NULL) + return 0; + + switch (TOOL_BUTTON(aiptek->curSetting.toolMode)) { + case AIPTEK_TOOL_BUTTON_MOUSE_MODE: + s = "mouse"; + break; + + case AIPTEK_TOOL_BUTTON_ERASER_MODE: + s = "eraser"; + break; + + case AIPTEK_TOOL_BUTTON_PENCIL_MODE: + s = "pencil"; + break; + + case AIPTEK_TOOL_BUTTON_PEN_MODE: + s = "pen"; + break; + + case AIPTEK_TOOL_BUTTON_BRUSH_MODE: + s = "brush"; + break; + + case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE: + s = "airbrush"; + break; + + case AIPTEK_TOOL_BUTTON_LENS_MODE: + s = "lens"; + break; + + default: + s = "unknown"; + break; + } + return snprintf(buf, PAGE_SIZE, "%s\n", s); +} + +static ssize_t +store_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "mouse") == 0) { + aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_MOUSE_MODE; + } else if (strcmp(buf, "eraser") == 0) { + aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_ERASER_MODE; + } else if (strcmp(buf, "pencil") == 0) { + aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PENCIL_MODE; + } else if (strcmp(buf, "pen") == 0) { + aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE; + } else if (strcmp(buf, "brush") == 0) { + aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_BRUSH_MODE; + } else if (strcmp(buf, "airbrush") == 0) { + aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE; + } else if (strcmp(buf, "lens") == 0) { + aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_LENS_MODE; + } + + return count; +} + +static DEVICE_ATTR(tool_mode, + S_IRUGO | S_IWUGO, + show_tabletToolMode, store_tabletToolMode); + +/*********************************************************************** + * support routines for the 'xtilt' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) { + return snprintf(buf, PAGE_SIZE, "disable\n"); + } else { + return snprintf(buf, PAGE_SIZE, "%d\n", + aiptek->curSetting.xTilt); + } +} + +static ssize_t +store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int x; + + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "disable") == 0) { + aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE; + } else { + x = (int)simple_strtol(buf, NULL, 10); + if (x >= AIPTEK_TILT_MIN && x <= AIPTEK_TILT_MAX) { + aiptek->newSetting.xTilt = x; + } + } + return count; +} + +static DEVICE_ATTR(xtilt, + S_IRUGO | S_IWUGO, show_tabletXtilt, store_tabletXtilt); + +/*********************************************************************** + * support routines for the 'ytilt' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) { + return snprintf(buf, PAGE_SIZE, "disable\n"); + } else { + return snprintf(buf, PAGE_SIZE, "%d\n", + aiptek->curSetting.yTilt); + } +} + +static ssize_t +store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + int y; + + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "disable") == 0) { + aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE; + } else { + y = (int)simple_strtol(buf, NULL, 10); + if (y >= AIPTEK_TILT_MIN && y <= AIPTEK_TILT_MAX) { + aiptek->newSetting.yTilt = y; + } + } + return count; +} + +static DEVICE_ATTR(ytilt, + S_IRUGO | S_IWUGO, show_tabletYtilt, store_tabletYtilt); + +/*********************************************************************** + * support routines for the 'jitter' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay); +} + +static ssize_t +store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + aiptek->newSetting.jitterDelay = (int)simple_strtol(buf, NULL, 10); + return count; +} + +static DEVICE_ATTR(jitter, + S_IRUGO | S_IWUGO, + show_tabletJitterDelay, store_tabletJitterDelay); + +/*********************************************************************** + * support routines for the 'delay' file. Note that this file + * both displays current setting and allows reprogramming. + */ +static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "%d\n", + aiptek->curSetting.programmableDelay); +} + +static ssize_t +store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, NULL, 10); + return count; +} + +static DEVICE_ATTR(delay, + S_IRUGO | S_IWUGO, + show_tabletProgrammableDelay, store_tabletProgrammableDelay); + +/*********************************************************************** + * support routines for the 'input_path' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletInputDevice(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "/dev/input/%s\n", + aiptek->features.inputPath); +} + +static DEVICE_ATTR(input_path, S_IRUGO, show_tabletInputDevice, NULL); + +/*********************************************************************** + * support routines for the 'event_count' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount); +} + +static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL); + +/*********************************************************************** + * support routines for the 'diagnostic' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *retMsg; + + if (aiptek == NULL) + return 0; + + switch (aiptek->diagnostic) { + case AIPTEK_DIAGNOSTIC_NA: + retMsg = "no errors\n"; + break; + + case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE: + retMsg = "Error: receiving relative reports\n"; + break; + + case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE: + retMsg = "Error: receiving absolute reports\n"; + break; + + case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED: + if (aiptek->curSetting.pointerMode == + AIPTEK_POINTER_ONLY_MOUSE_MODE) { + retMsg = "Error: receiving stylus reports\n"; + } else { + retMsg = "Error: receiving mouse reports\n"; + } + break; + + default: + return 0; + } + return snprintf(buf, PAGE_SIZE, retMsg); +} + +static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL); + +/*********************************************************************** + * support routines for the 'stylus_upper' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *s; + + if (aiptek == NULL) + return 0; + + switch (aiptek->curSetting.stylusButtonUpper) { + case AIPTEK_STYLUS_UPPER_BUTTON: + s = "upper"; + break; + + case AIPTEK_STYLUS_LOWER_BUTTON: + s = "lower"; + break; + + default: + s = "unknown"; + break; + } + return snprintf(buf, PAGE_SIZE, "%s\n", s); +} + +static ssize_t +store_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "upper") == 0) { + aiptek->newSetting.stylusButtonUpper = + AIPTEK_STYLUS_UPPER_BUTTON; + } else if (strcmp(buf, "lower") == 0) { + aiptek->newSetting.stylusButtonUpper = + AIPTEK_STYLUS_LOWER_BUTTON; + } + return count; +} + +static DEVICE_ATTR(stylus_upper, + S_IRUGO | S_IWUGO, + show_tabletStylusUpper, store_tabletStylusUpper); + +/*********************************************************************** + * support routines for the 'stylus_lower' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *s; + + if (aiptek == NULL) + return 0; + + switch (aiptek->curSetting.stylusButtonLower) { + case AIPTEK_STYLUS_UPPER_BUTTON: + s = "upper"; + break; + + case AIPTEK_STYLUS_LOWER_BUTTON: + s = "lower"; + break; + + default: + s = "unknown"; + break; + } + return snprintf(buf, PAGE_SIZE, "%s\n", s); +} + +static ssize_t +store_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "upper") == 0) { + aiptek->newSetting.stylusButtonLower = + AIPTEK_STYLUS_UPPER_BUTTON; + } else if (strcmp(buf, "lower") == 0) { + aiptek->newSetting.stylusButtonLower = + AIPTEK_STYLUS_LOWER_BUTTON; + } + return count; +} + +static DEVICE_ATTR(stylus_lower, + S_IRUGO | S_IWUGO, + show_tabletStylusLower, store_tabletStylusLower); + +/*********************************************************************** + * support routines for the 'mouse_left' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *s; + + if (aiptek == NULL) + return 0; + + switch (aiptek->curSetting.mouseButtonLeft) { + case AIPTEK_MOUSE_LEFT_BUTTON: + s = "left"; + break; + + case AIPTEK_MOUSE_MIDDLE_BUTTON: + s = "middle"; + break; + + case AIPTEK_MOUSE_RIGHT_BUTTON: + s = "right"; + break; + + default: + s = "unknown"; + break; + } + return snprintf(buf, PAGE_SIZE, "%s\n", s); +} + +static ssize_t +store_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "left") == 0) { + aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON; + } else if (strcmp(buf, "middle") == 0) { + aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_MIDDLE_BUTTON; + } else if (strcmp(buf, "right") == 0) { + aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_RIGHT_BUTTON; + } + return count; +} + +static DEVICE_ATTR(mouse_left, + S_IRUGO | S_IWUGO, + show_tabletMouseLeft, store_tabletMouseLeft); + +/*********************************************************************** + * support routines for the 'mouse_middle' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *s; + + if (aiptek == NULL) + return 0; + + switch (aiptek->curSetting.mouseButtonMiddle) { + case AIPTEK_MOUSE_LEFT_BUTTON: + s = "left"; + break; + + case AIPTEK_MOUSE_MIDDLE_BUTTON: + s = "middle"; + break; + + case AIPTEK_MOUSE_RIGHT_BUTTON: + s = "right"; + break; + + default: + s = "unknown"; + break; + } + return snprintf(buf, PAGE_SIZE, "%s\n", s); +} + +static ssize_t +store_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "left") == 0) { + aiptek->newSetting.mouseButtonMiddle = AIPTEK_MOUSE_LEFT_BUTTON; + } else if (strcmp(buf, "middle") == 0) { + aiptek->newSetting.mouseButtonMiddle = + AIPTEK_MOUSE_MIDDLE_BUTTON; + } else if (strcmp(buf, "right") == 0) { + aiptek->newSetting.mouseButtonMiddle = + AIPTEK_MOUSE_RIGHT_BUTTON; + } + return count; +} + +static DEVICE_ATTR(mouse_middle, + S_IRUGO | S_IWUGO, + show_tabletMouseMiddle, store_tabletMouseMiddle); + +/*********************************************************************** + * support routines for the 'mouse_right' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + char *s; + + if (aiptek == NULL) + return 0; + + switch (aiptek->curSetting.mouseButtonRight) { + case AIPTEK_MOUSE_LEFT_BUTTON: + s = "left"; + break; + + case AIPTEK_MOUSE_MIDDLE_BUTTON: + s = "middle"; + break; + + case AIPTEK_MOUSE_RIGHT_BUTTON: + s = "right"; + break; + + default: + s = "unknown"; + break; + } + return snprintf(buf, PAGE_SIZE, "%s\n", s); +} + +static ssize_t +store_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + if (strcmp(buf, "left") == 0) { + aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_LEFT_BUTTON; + } else if (strcmp(buf, "middle") == 0) { + aiptek->newSetting.mouseButtonRight = + AIPTEK_MOUSE_MIDDLE_BUTTON; + } else if (strcmp(buf, "right") == 0) { + aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON; + } + return count; +} + +static DEVICE_ATTR(mouse_right, + S_IRUGO | S_IWUGO, + show_tabletMouseRight, store_tabletMouseRight); + +/*********************************************************************** + * support routines for the 'wheel' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) { + return snprintf(buf, PAGE_SIZE, "disable\n"); + } else { + return snprintf(buf, PAGE_SIZE, "%d\n", + aiptek->curSetting.wheel); + } +} + +static ssize_t +store_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10); + return count; +} + +static DEVICE_ATTR(wheel, + S_IRUGO | S_IWUGO, show_tabletWheel, store_tabletWheel); + +/*********************************************************************** + * support routines for the 'execute' file. Note that this file + * both displays current setting and allows for setting changing. + */ +static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + /* There is nothing useful to display, so a one-line manual + * is in order... + */ + return snprintf(buf, PAGE_SIZE, + "Write anything to this file to program your tablet.\n"); +} + +static ssize_t +store_tabletExecute(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + /* We do not care what you write to this file. Merely the action + * of writing to this file triggers a tablet reprogramming. + */ + memcpy(&aiptek->curSetting, &aiptek->newSetting, + sizeof(struct aiptek_settings)); + + if (aiptek_program_tablet(aiptek) < 0) + return -EIO; + + return count; +} + +static DEVICE_ATTR(execute, + S_IRUGO | S_IWUGO, show_tabletExecute, store_tabletExecute); + +/*********************************************************************** + * support routines for the 'odm_code' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode); +} + +static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL); + +/*********************************************************************** + * support routines for the 'model_code' file. Note that this file + * only displays current setting. + */ +static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode); +} + +static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL); + +/*********************************************************************** + * support routines for the 'firmware_code' file. Note that this file + * only displays current setting. + */ +static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aiptek *aiptek = dev_get_drvdata(dev); + + if (aiptek == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "%04x\n", + aiptek->features.firmwareCode); +} + +static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL); + +/*********************************************************************** + * This routine removes all existing sysfs files managed by this device + * driver. + */ +static void aiptek_delete_files(struct device *dev) +{ + device_remove_file(dev, &dev_attr_size); + device_remove_file(dev, &dev_attr_product_id); + device_remove_file(dev, &dev_attr_vendor_id); + device_remove_file(dev, &dev_attr_vendor); + device_remove_file(dev, &dev_attr_product); + device_remove_file(dev, &dev_attr_pointer_mode); + device_remove_file(dev, &dev_attr_coordinate_mode); + device_remove_file(dev, &dev_attr_tool_mode); + device_remove_file(dev, &dev_attr_xtilt); + device_remove_file(dev, &dev_attr_ytilt); + device_remove_file(dev, &dev_attr_jitter); + device_remove_file(dev, &dev_attr_delay); + device_remove_file(dev, &dev_attr_input_path); + device_remove_file(dev, &dev_attr_event_count); + device_remove_file(dev, &dev_attr_diagnostic); + device_remove_file(dev, &dev_attr_odm_code); + device_remove_file(dev, &dev_attr_model_code); + device_remove_file(dev, &dev_attr_firmware_code); + device_remove_file(dev, &dev_attr_stylus_lower); + device_remove_file(dev, &dev_attr_stylus_upper); + device_remove_file(dev, &dev_attr_mouse_left); + device_remove_file(dev, &dev_attr_mouse_middle); + device_remove_file(dev, &dev_attr_mouse_right); + device_remove_file(dev, &dev_attr_wheel); + device_remove_file(dev, &dev_attr_execute); +} + +/*********************************************************************** + * This routine creates the sysfs files managed by this device + * driver. + */ +static int aiptek_add_files(struct device *dev) +{ + int ret; + + if ((ret = device_create_file(dev, &dev_attr_size)) || + (ret = device_create_file(dev, &dev_attr_product_id)) || + (ret = device_create_file(dev, &dev_attr_vendor_id)) || + (ret = device_create_file(dev, &dev_attr_vendor)) || + (ret = device_create_file(dev, &dev_attr_product)) || + (ret = device_create_file(dev, &dev_attr_pointer_mode)) || + (ret = device_create_file(dev, &dev_attr_coordinate_mode)) || + (ret = device_create_file(dev, &dev_attr_tool_mode)) || + (ret = device_create_file(dev, &dev_attr_xtilt)) || + (ret = device_create_file(dev, &dev_attr_ytilt)) || + (ret = device_create_file(dev, &dev_attr_jitter)) || + (ret = device_create_file(dev, &dev_attr_delay)) || + (ret = device_create_file(dev, &dev_attr_input_path)) || + (ret = device_create_file(dev, &dev_attr_event_count)) || + (ret = device_create_file(dev, &dev_attr_diagnostic)) || + (ret = device_create_file(dev, &dev_attr_odm_code)) || + (ret = device_create_file(dev, &dev_attr_model_code)) || + (ret = device_create_file(dev, &dev_attr_firmware_code)) || + (ret = device_create_file(dev, &dev_attr_stylus_lower)) || + (ret = device_create_file(dev, &dev_attr_stylus_upper)) || + (ret = device_create_file(dev, &dev_attr_mouse_left)) || + (ret = device_create_file(dev, &dev_attr_mouse_middle)) || + (ret = device_create_file(dev, &dev_attr_mouse_right)) || + (ret = device_create_file(dev, &dev_attr_wheel)) || + (ret = device_create_file(dev, &dev_attr_execute))) { + err("aiptek: killing own sysfs device files\n"); + aiptek_delete_files(dev); + } + return ret; +} + +/*********************************************************************** + * This routine is called when a tablet has been identified. It basically + * sets up the tablet and the driver's internal structures. + */ +static int +aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct aiptek *aiptek; + struct input_dev *inputdev; + struct input_handle *inputhandle; + struct list_head *node, *next; + int i; + int speeds[] = { 0, + AIPTEK_PROGRAMMABLE_DELAY_50, + AIPTEK_PROGRAMMABLE_DELAY_400, + AIPTEK_PROGRAMMABLE_DELAY_25, + AIPTEK_PROGRAMMABLE_DELAY_100, + AIPTEK_PROGRAMMABLE_DELAY_200, + AIPTEK_PROGRAMMABLE_DELAY_300 + }; + int err = -ENOMEM; + + /* programmableDelay is where the command-line specified + * delay is kept. We make it the first element of speeds[], + * so therefore, your override speed is tried first, then the + * remainder. Note that the default value of 400ms will be tried + * if you do not specify any command line parameter. + */ + speeds[0] = programmableDelay; + + aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL); + inputdev = input_allocate_device(); + if (!aiptek || !inputdev) + goto fail1; + + aiptek->data = usb_buffer_alloc(usbdev, AIPTEK_PACKET_LENGTH, + GFP_ATOMIC, &aiptek->data_dma); + if (!aiptek->data) + goto fail1; + + aiptek->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!aiptek->urb) + goto fail2; + + aiptek->inputdev = inputdev; + aiptek->usbdev = usbdev; + aiptek->ifnum = intf->altsetting[0].desc.bInterfaceNumber; + aiptek->inDelay = 0; + aiptek->endDelay = 0; + aiptek->previousJitterable = 0; + + /* Set up the curSettings struct. Said struct contains the current + * programmable parameters. The newSetting struct contains changes + * the user makes to the settings via the sysfs interface. Those + * changes are not "committed" to curSettings until the user + * writes to the sysfs/.../execute file. + */ + aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE; + aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE; + aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE; + aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE; + aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE; + aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON; + aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON; + aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON; + aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON; + aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON; + aiptek->curSetting.jitterDelay = jitterDelay; + aiptek->curSetting.programmableDelay = programmableDelay; + + /* Both structs should have equivalent settings + */ + aiptek->newSetting = aiptek->curSetting; + + /* Determine the usb devices' physical path. + * Asketh not why we always pretend we're using "../input0", + * but I suspect this will have to be refactored one + * day if a single USB device can be a keyboard & a mouse + * & a tablet, and the inputX number actually will tell + * us something... + */ + usb_make_path(usbdev, aiptek->features.usbPath, + sizeof(aiptek->features.usbPath)); + strlcat(aiptek->features.usbPath, "/input0", + sizeof(aiptek->features.usbPath)); + + /* Set up client data, pointers to open and close routines + * for the input device. + */ + inputdev->name = "Aiptek"; + inputdev->phys = aiptek->features.usbPath; + usb_to_input_id(usbdev, &inputdev->id); + inputdev->dev.parent = &intf->dev; + + input_set_drvdata(inputdev, aiptek); + + inputdev->open = aiptek_open; + inputdev->close = aiptek_close; + + /* Now program the capacities of the tablet, in terms of being + * an input device. + */ + inputdev->evbit[0] |= BIT(EV_KEY) + | BIT(EV_ABS) + | BIT(EV_REL) + | BIT(EV_MSC); + + inputdev->absbit[0] |= BIT(ABS_MISC); + + inputdev->relbit[0] |= + (BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) | BIT(REL_MISC)); + + inputdev->keybit[LONG(BTN_LEFT)] |= + (BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE)); + + inputdev->keybit[LONG(BTN_DIGI)] |= + (BIT(BTN_TOOL_PEN) | + BIT(BTN_TOOL_RUBBER) | + BIT(BTN_TOOL_PENCIL) | + BIT(BTN_TOOL_AIRBRUSH) | + BIT(BTN_TOOL_BRUSH) | + BIT(BTN_TOOL_MOUSE) | + BIT(BTN_TOOL_LENS) | + BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2)); + + inputdev->mscbit[0] = BIT(MSC_SERIAL); + + /* Programming the tablet macro keys needs to be done with a for loop + * as the keycodes are discontiguous. + */ + for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i) + set_bit(macroKeyEvents[i], inputdev->keybit); + + /* + * Program the input device coordinate capacities. We do not yet + * know what maximum X, Y, and Z values are, so we're putting fake + * values in. Later, we'll ask the tablet to put in the correct + * values. + */ + input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0); + input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0); + input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0); + input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); + input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0); + input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0); + + endpoint = &intf->altsetting[0].endpoint[0].desc; + + /* Go set up our URB, which is called when the tablet receives + * input. + */ + usb_fill_int_urb(aiptek->urb, + aiptek->usbdev, + usb_rcvintpipe(aiptek->usbdev, + endpoint->bEndpointAddress), + aiptek->data, 8, aiptek_irq, aiptek, + endpoint->bInterval); + + aiptek->urb->transfer_dma = aiptek->data_dma; + aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* Program the tablet. This sets the tablet up in the mode + * specified in newSetting, and also queries the tablet's + * physical capacities. + * + * Sanity check: if a tablet doesn't like the slow programmatic + * delay, we often get sizes of 0x0. Let's use that as an indicator + * to try faster delays, up to 25 ms. If that logic fails, well, you'll + * have to explain to us how your tablet thinks it's 0x0, and yet that's + * not an error :-) + */ + + for (i = 0; i < ARRAY_SIZE(speeds); ++i) { + aiptek->curSetting.programmableDelay = speeds[i]; + (void)aiptek_program_tablet(aiptek); + if (aiptek->inputdev->absmax[ABS_X] > 0) { + info("input: Aiptek using %d ms programming speed\n", + aiptek->curSetting.programmableDelay); + break; + } + } + + /* Register the tablet as an Input Device + */ + err = input_register_device(aiptek->inputdev); + if (err) + goto fail2; + + /* We now will look for the evdev device which is mapped to + * the tablet. The partial name is kept in the link list of + * input_handles associated with this input device. + * What identifies an evdev input_handler is that it begins + * with 'event', continues with a digit, and that in turn + * is mapped to input/eventN. + */ + list_for_each_safe(node, next, &inputdev->h_list) { + inputhandle = to_handle(node); + if (strncmp(inputhandle->name, "event", 5) == 0) { + strcpy(aiptek->features.inputPath, inputhandle->name); + break; + } + } + + /* Associate this driver's struct with the usb interface. + */ + usb_set_intfdata(intf, aiptek); + + /* Set up the sysfs files + */ + aiptek_add_files(&intf->dev); + + /* Make sure the evdev module is loaded. Assuming evdev IS a module :-) + */ + if (request_module("evdev") != 0) + info("aiptek: error loading 'evdev' module"); + + return 0; + + fail2: usb_buffer_free(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data, + aiptek->data_dma); + fail1: input_free_device(inputdev); + kfree(aiptek); + return err; +} + +/*********************************************************************** + * Deal with tablet disconnecting from the system. + */ +static void aiptek_disconnect(struct usb_interface *intf) +{ + struct aiptek *aiptek = usb_get_intfdata(intf); + + /* Disassociate driver's struct with usb interface + */ + usb_set_intfdata(intf, NULL); + if (aiptek != NULL) { + /* Free & unhook everything from the system. + */ + usb_kill_urb(aiptek->urb); + input_unregister_device(aiptek->inputdev); + aiptek_delete_files(&intf->dev); + usb_free_urb(aiptek->urb); + usb_buffer_free(interface_to_usbdev(intf), + AIPTEK_PACKET_LENGTH, + aiptek->data, aiptek->data_dma); + kfree(aiptek); + } +} + +static struct usb_driver aiptek_driver = { + .name = "aiptek", + .probe = aiptek_probe, + .disconnect = aiptek_disconnect, + .id_table = aiptek_ids, +}; + +static int __init aiptek_init(void) +{ + int result = usb_register(&aiptek_driver); + if (result == 0) { + info(DRIVER_VERSION ": " DRIVER_AUTHOR); + info(DRIVER_DESC); + } + return result; +} + +static void __exit aiptek_exit(void) +{ + usb_deregister(&aiptek_driver); +} + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +module_param(programmableDelay, int, 0); +MODULE_PARM_DESC(programmableDelay, "delay used during tablet programming"); +module_param(jitterDelay, int, 0); +MODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay"); + +module_init(aiptek_init); +module_exit(aiptek_exit); diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c new file mode 100644 index 00000000000..b2ca10f2fe0 --- /dev/null +++ b/drivers/input/tablet/gtco.c @@ -0,0 +1,1055 @@ +/* -*- linux-c -*- + +GTCO digitizer USB driver + +Use the err(), dbg() and info() macros from usb.h for system logging + +TO CHECK: Is pressure done right on report 5? + +Copyright (C) 2006 GTCO CalComp + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of GTCO-CalComp not be used in advertising +or publicity pertaining to distribution of the software without specific, +written prior permission. GTCO-CalComp makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +GTCO-CALCOMP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL GTCO-CALCOMP BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +GTCO CalComp, Inc. +7125 Riverwood Drive +Columbia, MD 21046 + +Jeremy Roberson jroberson@gtcocalcomp.com +Scott Hill shill@gtcocalcomp.com +*/ + + + +/*#define DEBUG*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/usb.h> +#include <asm/uaccess.h> +#include <asm/unaligned.h> +#include <asm/byteorder.h> + + +#include <linux/version.h> +#include <linux/usb/input.h> + +/* Version with a Major number of 2 is for kernel inclusion only. */ +#define GTCO_VERSION "2.00.0006" + + +/* MACROS */ + +#define VENDOR_ID_GTCO 0x078C +#define PID_400 0x400 +#define PID_401 0x401 +#define PID_1000 0x1000 +#define PID_1001 0x1001 +#define PID_1002 0x1002 + +/* Max size of a single report */ +#define REPORT_MAX_SIZE 10 + + +/* Bitmask whether pen is in range */ +#define MASK_INRANGE 0x20 +#define MASK_BUTTON 0x01F + +#define PATHLENGTH 64 + +/* DATA STRUCTURES */ + +/* Device table */ +static struct usb_device_id gtco_usbid_table [] = { + { USB_DEVICE(VENDOR_ID_GTCO, PID_400) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_401) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1000) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1001) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1002) }, + { } +}; +MODULE_DEVICE_TABLE (usb, gtco_usbid_table); + + +/* Structure to hold all of our device specific stuff */ +struct gtco { + + struct input_dev *inputdevice; /* input device struct pointer */ + struct usb_device *usbdev; /* the usb device for this device */ + struct urb *urbinfo; /* urb for incoming reports */ + dma_addr_t buf_dma; /* dma addr of the data buffer*/ + unsigned char * buffer; /* databuffer for reports */ + + char usbpath[PATHLENGTH]; + int openCount; + + /* Information pulled from Report Descriptor */ + u32 usage; + u32 min_X; + u32 max_X; + u32 min_Y; + u32 max_Y; + s8 mintilt_X; + s8 maxtilt_X; + s8 mintilt_Y; + s8 maxtilt_Y; + u32 maxpressure; + u32 minpressure; +}; + + + +/* Code for parsing the HID REPORT DESCRIPTOR */ + +/* From HID1.11 spec */ +struct hid_descriptor +{ + struct usb_descriptor_header header; + __le16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + u8 bDescriptorType; + __le16 wDescriptorLength; +} __attribute__ ((packed)); + + +#define HID_DESCRIPTOR_SIZE 9 +#define HID_DEVICE_TYPE 33 +#define REPORT_DEVICE_TYPE 34 + + +#define PREF_TAG(x) ((x)>>4) +#define PREF_TYPE(x) ((x>>2)&0x03) +#define PREF_SIZE(x) ((x)&0x03) + +#define TYPE_MAIN 0 +#define TYPE_GLOBAL 1 +#define TYPE_LOCAL 2 +#define TYPE_RESERVED 3 + +#define TAG_MAIN_INPUT 0x8 +#define TAG_MAIN_OUTPUT 0x9 +#define TAG_MAIN_FEATURE 0xB +#define TAG_MAIN_COL_START 0xA +#define TAG_MAIN_COL_END 0xC + +#define TAG_GLOB_USAGE 0 +#define TAG_GLOB_LOG_MIN 1 +#define TAG_GLOB_LOG_MAX 2 +#define TAG_GLOB_PHYS_MIN 3 +#define TAG_GLOB_PHYS_MAX 4 +#define TAG_GLOB_UNIT_EXP 5 +#define TAG_GLOB_UNIT 6 +#define TAG_GLOB_REPORT_SZ 7 +#define TAG_GLOB_REPORT_ID 8 +#define TAG_GLOB_REPORT_CNT 9 +#define TAG_GLOB_PUSH 10 +#define TAG_GLOB_POP 11 + +#define TAG_GLOB_MAX 12 + +#define DIGITIZER_USAGE_TIP_PRESSURE 0x30 +#define DIGITIZER_USAGE_TILT_X 0x3D +#define DIGITIZER_USAGE_TILT_Y 0x3E + + +/* + * This is an abbreviated parser for the HID Report Descriptor. We + * know what devices we are talking to, so this is by no means meant + * to be generic. We can make some safe assumptions: + * + * - We know there are no LONG tags, all short + * - We know that we have no MAIN Feature and MAIN Output items + * - We know what the IRQ reports are supposed to look like. + * + * The main purpose of this is to use the HID report desc to figure + * out the mins and maxs of the fields in the IRQ reports. The IRQ + * reports for 400/401 change slightly if the max X is bigger than 64K. + * + */ +static void parse_hid_report_descriptor(struct gtco *device, char * report, + int length) +{ + int x, i = 0; + + /* Tag primitive vars */ + __u8 prefix; + __u8 size; + __u8 tag; + __u8 type; + __u8 data = 0; + __u16 data16 = 0; + __u32 data32 = 0; + + /* For parsing logic */ + int inputnum = 0; + __u32 usage = 0; + + /* Global Values, indexed by TAG */ + __u32 globalval[TAG_GLOB_MAX]; + __u32 oldval[TAG_GLOB_MAX]; + + /* Debug stuff */ + char maintype = 'x'; + char globtype[12]; + int indent = 0; + char indentstr[10] = ""; + + + dbg("======>>>>>>PARSE<<<<<<======"); + + /* Walk this report and pull out the info we need */ + while (i < length) { + prefix = report[i]; + + /* Skip over prefix */ + i++; + + /* Determine data size and save the data in the proper variable */ + size = PREF_SIZE(prefix); + switch (size) { + case 1: + data = report[i]; + break; + case 2: + data16 = le16_to_cpu(get_unaligned((__le16 *)&report[i])); + break; + case 3: + size = 4; + data32 = le32_to_cpu(get_unaligned((__le32 *)&report[i])); + break; + } + + /* Skip size of data */ + i += size; + + /* What we do depends on the tag type */ + tag = PREF_TAG(prefix); + type = PREF_TYPE(prefix); + switch (type) { + case TYPE_MAIN: + strcpy(globtype, ""); + switch (tag) { + + case TAG_MAIN_INPUT: + /* + * The INPUT MAIN tag signifies this is + * information from a report. We need to + * figure out what it is and store the + * min/max values + */ + + maintype = 'I'; + if (data == 2) + strcpy(globtype, "Variable"); + else if (data == 3) + strcpy(globtype, "Var|Const"); + + dbg("::::: Saving Report: %d input #%d Max: 0x%X(%d) Min:0x%X(%d) of %d bits", + globalval[TAG_GLOB_REPORT_ID], inputnum, + globalval[TAG_GLOB_LOG_MAX], globalval[TAG_GLOB_LOG_MAX], + globalval[TAG_GLOB_LOG_MIN], globalval[TAG_GLOB_LOG_MIN], + globalval[TAG_GLOB_REPORT_SZ] * globalval[TAG_GLOB_REPORT_CNT]); + + + /* + We can assume that the first two input items + are always the X and Y coordinates. After + that, we look for everything else by + local usage value + */ + switch (inputnum) { + case 0: /* X coord */ + dbg("GER: X Usage: 0x%x", usage); + if (device->max_X == 0) { + device->max_X = globalval[TAG_GLOB_LOG_MAX]; + device->min_X = globalval[TAG_GLOB_LOG_MIN]; + } + break; + + case 1: /* Y coord */ + dbg("GER: Y Usage: 0x%x", usage); + if (device->max_Y == 0) { + device->max_Y = globalval[TAG_GLOB_LOG_MAX]; + device->min_Y = globalval[TAG_GLOB_LOG_MIN]; + } + break; + + default: + /* Tilt X */ + if (usage == DIGITIZER_USAGE_TILT_X) { + if (device->maxtilt_X == 0) { + device->maxtilt_X = globalval[TAG_GLOB_LOG_MAX]; + device->mintilt_X = globalval[TAG_GLOB_LOG_MIN]; + } + } + + /* Tilt Y */ + if (usage == DIGITIZER_USAGE_TILT_Y) { + if (device->maxtilt_Y == 0) { + device->maxtilt_Y = globalval[TAG_GLOB_LOG_MAX]; + device->mintilt_Y = globalval[TAG_GLOB_LOG_MIN]; + } + } + + /* Pressure */ + if (usage == DIGITIZER_USAGE_TIP_PRESSURE) { + if (device->maxpressure == 0) { + device->maxpressure = globalval[TAG_GLOB_LOG_MAX]; + device->minpressure = globalval[TAG_GLOB_LOG_MIN]; + } + } + + break; + } + + inputnum++; + break; + + case TAG_MAIN_OUTPUT: + maintype = 'O'; + break; + + case TAG_MAIN_FEATURE: + maintype = 'F'; + break; + + case TAG_MAIN_COL_START: + maintype = 'S'; + + if (data == 0) { + dbg("======>>>>>> Physical"); + strcpy(globtype, "Physical"); + } else + dbg("======>>>>>>"); + + /* Indent the debug output */ + indent++; + for (x = 0; x < indent; x++) + indentstr[x] = '-'; + indentstr[x] = 0; + + /* Save global tags */ + for (x = 0; x < TAG_GLOB_MAX; x++) + oldval[x] = globalval[x]; + + break; + + case TAG_MAIN_COL_END: + dbg("<<<<<<======"); + maintype = 'E'; + indent--; + for (x = 0; x < indent; x++) + indentstr[x] = '-'; + indentstr[x] = 0; + + /* Copy global tags back */ + for (x = 0; x < TAG_GLOB_MAX; x++) + globalval[x] = oldval[x]; + + break; + } + + switch (size) { + case 1: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr, tag, maintype, size, globtype, data); + break; + + case 2: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr, tag, maintype, size, globtype, data16); + break; + + case 4: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr, tag, maintype, size, globtype, data32); + break; + } + break; + + case TYPE_GLOBAL: + switch (tag) { + case TAG_GLOB_USAGE: + /* + * First time we hit the global usage tag, + * it should tell us the type of device + */ + if (device->usage == 0) + device->usage = data; + + strcpy(globtype, "USAGE"); + break; + + case TAG_GLOB_LOG_MIN: + strcpy(globtype, "LOG_MIN"); + break; + + case TAG_GLOB_LOG_MAX: + strcpy(globtype, "LOG_MAX"); + break; + + case TAG_GLOB_PHYS_MIN: + strcpy(globtype, "PHYS_MIN"); + break; + + case TAG_GLOB_PHYS_MAX: + strcpy(globtype, "PHYS_MAX"); + break; + + case TAG_GLOB_UNIT_EXP: + strcpy(globtype, "EXP"); + break; + + case TAG_GLOB_UNIT: + strcpy(globtype, "UNIT"); + break; + + case TAG_GLOB_REPORT_SZ: + strcpy(globtype, "REPORT_SZ"); + break; + + case TAG_GLOB_REPORT_ID: + strcpy(globtype, "REPORT_ID"); + /* New report, restart numbering */ + inputnum = 0; + break; + + case TAG_GLOB_REPORT_CNT: + strcpy(globtype, "REPORT_CNT"); + break; + + case TAG_GLOB_PUSH: + strcpy(globtype, "PUSH"); + break; + + case TAG_GLOB_POP: + strcpy(globtype, "POP"); + break; + } + + /* Check to make sure we have a good tag number + so we don't overflow array */ + if (tag < TAG_GLOB_MAX) { + switch (size) { + case 1: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x", + indentstr, globtype, tag, size, data); + globalval[tag] = data; + break; + + case 2: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x", + indentstr, globtype, tag, size, data16); + globalval[tag] = data16; + break; + + case 4: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x", + indentstr, globtype, tag, size, data32); + globalval[tag] = data32; + break; + } + } else { + dbg("%sGLOBALTAG: ILLEGAL TAG:%d SIZE: %d ", + indentstr, tag, size); + } + break; + + case TYPE_LOCAL: + switch (tag) { + case TAG_GLOB_USAGE: + strcpy(globtype, "USAGE"); + /* Always 1 byte */ + usage = data; + break; + + case TAG_GLOB_LOG_MIN: + strcpy(globtype, "MIN"); + break; + + case TAG_GLOB_LOG_MAX: + strcpy(globtype, "MAX"); + break; + + default: + strcpy(globtype, "UNKNOWN"); + break; + } + + switch (size) { + case 1: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr, tag, globtype, size, data); + break; + + case 2: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr, tag, globtype, size, data16); + break; + + case 4: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr, tag, globtype, size, data32); + break; + } + + break; + } + } +} + +/* INPUT DRIVER Routines */ + +/* + * Called when opening the input device. This will submit the URB to + * the usb system so we start getting reports + */ +static int gtco_input_open(struct input_dev *inputdev) +{ + struct gtco *device = input_get_drvdata(inputdev); + + device->urbinfo->dev = device->usbdev; + if (usb_submit_urb(device->urbinfo, GFP_KERNEL)) + return -EIO; + + return 0; +} + +/* + * Called when closing the input device. This will unlink the URB + */ +static void gtco_input_close(struct input_dev *inputdev) +{ + struct gtco *device = input_get_drvdata(inputdev); + + usb_kill_urb(device->urbinfo); +} + + +/* + * Setup input device capabilities. Tell the input system what this + * device is capable of generating. + * + * This information is based on what is read from the HID report and + * placed in the struct gtco structure + * + */ +static void gtco_setup_caps(struct input_dev *inputdev) +{ + struct gtco *device = input_get_drvdata(inputdev); + + /* Which events */ + inputdev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC); + + /* Misc event menu block */ + inputdev->mscbit[0] = BIT(MSC_SCAN)|BIT(MSC_SERIAL)|BIT(MSC_RAW) ; + + /* Absolute values based on HID report info */ + input_set_abs_params(inputdev, ABS_X, device->min_X, device->max_X, + 0, 0); + input_set_abs_params(inputdev, ABS_Y, device->min_Y, device->max_Y, + 0, 0); + + /* Proximity */ + input_set_abs_params(inputdev, ABS_DISTANCE, 0, 1, 0, 0); + + /* Tilt & pressure */ + input_set_abs_params(inputdev, ABS_TILT_X, device->mintilt_X, + device->maxtilt_X, 0, 0); + input_set_abs_params(inputdev, ABS_TILT_Y, device->mintilt_Y, + device->maxtilt_Y, 0, 0); + input_set_abs_params(inputdev, ABS_PRESSURE, device->minpressure, + device->maxpressure, 0, 0); + + /* Transducer */ + input_set_abs_params(inputdev, ABS_MISC, 0, 0xFF, 0, 0); +} + +/* USB Routines */ + +/* + * URB callback routine. Called when we get IRQ reports from the + * digitizer. + * + * This bridges the USB and input device worlds. It generates events + * on the input device based on the USB reports. + */ +static void gtco_urb_callback(struct urb *urbinfo) +{ + struct gtco *device = urbinfo->context; + struct input_dev *inputdev; + int rc; + u32 val = 0; + s8 valsigned = 0; + char le_buffer[2]; + + inputdev = device->inputdevice; + + /* Was callback OK? */ + if (urbinfo->status == -ECONNRESET || + urbinfo->status == -ENOENT || + urbinfo->status == -ESHUTDOWN) { + + /* Shutdown is occurring. Return and don't queue up any more */ + return; + } + + if (urbinfo->status != 0) { + /* + * Some unknown error. Hopefully temporary. Just go and + * requeue an URB + */ + goto resubmit; + } + + /* + * Good URB, now process + */ + + /* PID dependent when we interpret the report */ + if (inputdev->id.product == PID_1000 || + inputdev->id.product == PID_1001 || + inputdev->id.product == PID_1002) { + + /* + * Switch on the report ID + * Conveniently, the reports have more information, the higher + * the report number. We can just fall through the case + * statements if we start with the highest number report + */ + switch (device->buffer[0]) { + case 5: + /* Pressure is 9 bits */ + val = ((u16)(device->buffer[8]) << 1); + val |= (u16)(device->buffer[7] >> 7); + input_report_abs(inputdev, ABS_PRESSURE, + device->buffer[8]); + + /* Mask out the Y tilt value used for pressure */ + device->buffer[7] = (u8)((device->buffer[7]) & 0x7F); + + /* Fall thru */ + case 4: + /* Tilt */ + + /* Sign extend these 7 bit numbers. */ + if (device->buffer[6] & 0x40) + device->buffer[6] |= 0x80; + + if (device->buffer[7] & 0x40) + device->buffer[7] |= 0x80; + + + valsigned = (device->buffer[6]); + input_report_abs(inputdev, ABS_TILT_X, (s32)valsigned); + + valsigned = (device->buffer[7]); + input_report_abs(inputdev, ABS_TILT_Y, (s32)valsigned); + + /* Fall thru */ + case 2: + case 3: + /* Convert buttons, only 5 bits possible */ + val = (device->buffer[5]) & MASK_BUTTON; + + /* We don't apply any meaning to the bitmask, + just report */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); + + /* Fall thru */ + case 1: + /* All reports have X and Y coords in the same place */ + val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[1])); + input_report_abs(inputdev, ABS_X, val); + + val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[3])); + input_report_abs(inputdev, ABS_Y, val); + + /* Ditto for proximity bit */ + val = device->buffer[5] & MASK_INRANGE ? 1 : 0; + input_report_abs(inputdev, ABS_DISTANCE, val); + + /* Report 1 is an exception to how we handle buttons */ + /* Buttons are an index, not a bitmask */ + if (device->buffer[0] == 1) { + + /* + * Convert buttons, 5 bit index + * Report value of index set as one, + * the rest as 0 + */ + val = device->buffer[5] & MASK_BUTTON; + dbg("======>>>>>>REPORT 1: val 0x%X(%d)", + val, val); + + /* + * We don't apply any meaning to the button + * index, just report it + */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); + } + break; + + case 7: + /* Menu blocks */ + input_event(inputdev, EV_MSC, MSC_SCAN, + device->buffer[1]); + break; + } + } + + /* Other pid class */ + if (inputdev->id.product == PID_400 || + inputdev->id.product == PID_401) { + + /* Report 2 */ + if (device->buffer[0] == 2) { + /* Menu blocks */ + input_event(inputdev, EV_MSC, MSC_SCAN, device->buffer[1]); + } + + /* Report 1 */ + if (device->buffer[0] == 1) { + char buttonbyte; + + /* IF X max > 64K, we still a bit from the y report */ + if (device->max_X > 0x10000) { + + val = (u16)(((u16)(device->buffer[2] << 8)) | (u8)device->buffer[1]); + val |= (u32)(((u8)device->buffer[3] & 0x1) << 16); + + input_report_abs(inputdev, ABS_X, val); + + le_buffer[0] = (u8)((u8)(device->buffer[3]) >> 1); + le_buffer[0] |= (u8)((device->buffer[3] & 0x1) << 7); + + le_buffer[1] = (u8)(device->buffer[4] >> 1); + le_buffer[1] |= (u8)((device->buffer[5] & 0x1) << 7); + + val = le16_to_cpu(get_unaligned((__le16 *)le_buffer)); + input_report_abs(inputdev, ABS_Y, val); + + /* + * Shift the button byte right by one to + * make it look like the standard report + */ + buttonbyte = device->buffer[5] >> 1; + } else { + + val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[1])); + input_report_abs(inputdev, ABS_X, val); + + val = le16_to_cpu(get_unaligned((__le16 *)&device->buffer[3])); + input_report_abs(inputdev, ABS_Y, val); + + buttonbyte = device->buffer[5]; + } + + /* BUTTONS and PROXIMITY */ + val = buttonbyte & MASK_INRANGE ? 1 : 0; + input_report_abs(inputdev, ABS_DISTANCE, val); + + /* Convert buttons, only 4 bits possible */ + val = buttonbyte & 0x0F; +#ifdef USE_BUTTONS + for (i = 0; i < 5; i++) + input_report_key(inputdev, BTN_DIGI + i, val & (1 << i)); +#else + /* We don't apply any meaning to the bitmask, just report */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); +#endif + + /* TRANSDUCER */ + input_report_abs(inputdev, ABS_MISC, device->buffer[6]); + } + } + + /* Everybody gets report ID's */ + input_event(inputdev, EV_MSC, MSC_RAW, device->buffer[0]); + + /* Sync it up */ + input_sync(inputdev); + + resubmit: + rc = usb_submit_urb(urbinfo, GFP_ATOMIC); + if (rc != 0) + err("usb_submit_urb failed rc=0x%x", rc); +} + +/* + * The probe routine. This is called when the kernel find the matching USB + * vendor/product. We do the following: + * + * - Allocate mem for a local structure to manage the device + * - Request a HID Report Descriptor from the device and parse it to + * find out the device parameters + * - Create an input device and assign it attributes + * - Allocate an URB so the device can talk to us when the input + * queue is open + */ +static int gtco_probe(struct usb_interface *usbinterface, + const struct usb_device_id *id) +{ + + struct gtco *gtco; + struct input_dev *input_dev; + struct hid_descriptor *hid_desc; + char *report = NULL; + int result = 0, retry; + int error; + struct usb_endpoint_descriptor *endpoint; + + /* Allocate memory for device structure */ + gtco = kzalloc(sizeof(struct gtco), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!gtco || !input_dev) { + err("No more memory"); + error = -ENOMEM; + goto err_free_devs; + } + + /* Set pointer to the input device */ + gtco->inputdevice = input_dev; + + /* Save interface information */ + gtco->usbdev = usb_get_dev(interface_to_usbdev(usbinterface)); + + /* Allocate some data for incoming reports */ + gtco->buffer = usb_buffer_alloc(gtco->usbdev, REPORT_MAX_SIZE, + GFP_KERNEL, >co->buf_dma); + if (!gtco->buffer) { + err("No more memory for us buffers"); + error = -ENOMEM; + goto err_free_devs; + } + + /* Allocate URB for reports */ + gtco->urbinfo = usb_alloc_urb(0, GFP_KERNEL); + if (!gtco->urbinfo) { + err("Failed to allocate URB"); + return -ENOMEM; + goto err_free_buf; + } + + /* + * The endpoint is always altsetting 0, we know this since we know + * this device only has one interrupt endpoint + */ + endpoint = &usbinterface->altsetting[0].endpoint[0].desc; + + /* Some debug */ + dbg("gtco # interfaces: %d", usbinterface->num_altsetting); + dbg("num endpoints: %d", usbinterface->cur_altsetting->desc.bNumEndpoints); + dbg("interface class: %d", usbinterface->cur_altsetting->desc.bInterfaceClass); + dbg("endpoint: attribute:0x%x type:0x%x", endpoint->bmAttributes, endpoint->bDescriptorType); + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + dbg("endpoint: we have interrupt endpoint\n"); + + dbg("endpoint extra len:%d ", usbinterface->altsetting[0].extralen); + + /* + * Find the HID descriptor so we can find out the size of the + * HID report descriptor + */ + if (usb_get_extra_descriptor(usbinterface->cur_altsetting, + HID_DEVICE_TYPE, &hid_desc) != 0){ + err("Can't retrieve exta USB descriptor to get hid report descriptor length"); + error = -EIO; + goto err_free_urb; + } + + dbg("Extra descriptor success: type:%d len:%d", + hid_desc->bDescriptorType, hid_desc->wDescriptorLength); + + report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL); + if (!report) { + err("No more memory for report"); + error = -ENOMEM; + goto err_free_urb; + } + + /* Couple of tries to get reply */ + for (retry = 0; retry < 3; retry++) { + result = usb_control_msg(gtco->usbdev, + usb_rcvctrlpipe(gtco->usbdev, 0), + USB_REQ_GET_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_DIR_IN, + REPORT_DEVICE_TYPE << 8, + 0, /* interface */ + report, + hid_desc->wDescriptorLength, + 5000); /* 5 secs */ + + if (result == hid_desc->wDescriptorLength) + break; + } + + /* If we didn't get the report, fail */ + dbg("usb_control_msg result: :%d", result); + if (result != hid_desc->wDescriptorLength) { + err("Failed to get HID Report Descriptor of size: %d", + hid_desc->wDescriptorLength); + error = -EIO; + goto err_free_urb; + } + + /* Now we parse the report */ + parse_hid_report_descriptor(gtco, report, result); + + /* Now we delete it */ + kfree(report); + + /* Create a device file node */ + usb_make_path(gtco->usbdev, gtco->usbpath, sizeof(gtco->usbpath)); + strlcat(gtco->usbpath, "/input0", sizeof(gtco->usbpath)); + + /* Set Input device functions */ + input_dev->open = gtco_input_open; + input_dev->close = gtco_input_close; + + /* Set input device information */ + input_dev->name = "GTCO_CalComp"; + input_dev->phys = gtco->usbpath; + + input_set_drvdata(input_dev, gtco); + + /* Now set up all the input device capabilities */ + gtco_setup_caps(input_dev); + + /* Set input device required ID information */ + usb_to_input_id(gtco->usbdev, &input_dev->id); + input_dev->dev.parent = &usbinterface->dev; + + /* Setup the URB, it will be posted later on open of input device */ + endpoint = &usbinterface->altsetting[0].endpoint[0].desc; + + usb_fill_int_urb(gtco->urbinfo, + gtco->usbdev, + usb_rcvintpipe(gtco->usbdev, + endpoint->bEndpointAddress), + gtco->buffer, + REPORT_MAX_SIZE, + gtco_urb_callback, + gtco, + endpoint->bInterval); + + gtco->urbinfo->transfer_dma = gtco->buf_dma; + gtco->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* Save gtco pointer in USB interface gtco */ + usb_set_intfdata(usbinterface, gtco); + + /* All done, now register the input device */ + error = input_register_device(input_dev); + if (error) + goto err_free_urb; + + return 0; + + err_free_urb: + usb_free_urb(gtco->urbinfo); + err_free_buf: + usb_buffer_free(gtco->usbdev, REPORT_MAX_SIZE, + gtco->buffer, gtco->buf_dma); + err_free_devs: + kfree(report); + input_free_device(input_dev); + kfree(gtco); + return error; +} + +/* + * This function is a standard USB function called when the USB device + * is disconnected. We will get rid of the URV, de-register the input + * device, and free up allocated memory + */ +static void gtco_disconnect(struct usb_interface *interface) +{ + /* Grab private device ptr */ + struct gtco *gtco = usb_get_intfdata(interface); + + /* Now reverse all the registration stuff */ + if (gtco) { + input_unregister_device(gtco->inputdevice); + usb_kill_urb(gtco->urbinfo); + usb_free_urb(gtco->urbinfo); + usb_buffer_free(gtco->usbdev, REPORT_MAX_SIZE, + gtco->buffer, gtco->buf_dma); + kfree(gtco); + } + + info("gtco driver disconnected"); +} + +/* STANDARD MODULE LOAD ROUTINES */ + +static struct usb_driver gtco_driverinfo_table = { + .name = "gtco", + .id_table = gtco_usbid_table, + .probe = gtco_probe, + .disconnect = gtco_disconnect, +}; + +/* + * Register this module with the USB subsystem + */ +static int __init gtco_init(void) +{ + int error; + + error = usb_register(>co_driverinfo_table); + if (error) { + err("usb_register() failed rc=0x%x", error); + return error; + } + + printk("GTCO usb driver version: %s", GTCO_VERSION); + return 0; +} + +/* + * Deregister this module with the USB subsystem + */ +static void __exit gtco_exit(void) +{ + usb_deregister(>co_driverinfo_table); +} + +module_init(gtco_init); +module_exit(gtco_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c new file mode 100644 index 00000000000..91e6d00d4a4 --- /dev/null +++ b/drivers/input/tablet/kbtab.c @@ -0,0 +1,226 @@ +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb/input.h> +#include <asm/unaligned.h> + +/* + * Version Information + * v0.0.1 - Original, extremely basic version, 2.4.xx only + * v0.0.2 - Updated, works with 2.5.62 and 2.4.20; + * - added pressure-threshold modules param code from + * Alex Perry <alex.perry@ieee.org> + */ + +#define DRIVER_VERSION "v0.0.2" +#define DRIVER_AUTHOR "Josh Myer <josh@joshisanerd.com>" +#define DRIVER_DESC "USB KB Gear JamStudio Tablet driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_KBGEAR 0x084e + +static int kb_pressure_click = 0x10; +module_param(kb_pressure_click, int, 0); +MODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks"); + +struct kbtab { + unsigned char *data; + dma_addr_t data_dma; + struct input_dev *dev; + struct usb_device *usbdev; + struct urb *irq; + int x, y; + int button; + int pressure; + __u32 serial[2]; + char phys[32]; +}; + +static void kbtab_irq(struct urb *urb) +{ + struct kbtab *kbtab = urb->context; + unsigned char *data = kbtab->data; + struct input_dev *dev = kbtab->dev; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + kbtab->x = le16_to_cpu(get_unaligned((__le16 *) &data[1])); + kbtab->y = le16_to_cpu(get_unaligned((__le16 *) &data[3])); + + kbtab->pressure = (data[5]); + + input_report_key(dev, BTN_TOOL_PEN, 1); + + input_report_abs(dev, ABS_X, kbtab->x); + input_report_abs(dev, ABS_Y, kbtab->y); + + /*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/ + input_report_key(dev, BTN_RIGHT, data[0] & 0x02); + + if (-1 == kb_pressure_click) { + input_report_abs(dev, ABS_PRESSURE, kbtab->pressure); + } else { + input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0); + }; + + input_sync(dev); + + exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + +static struct usb_device_id kbtab_ids[] = { + { USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 }, + { } +}; + +MODULE_DEVICE_TABLE(usb, kbtab_ids); + +static int kbtab_open(struct input_dev *dev) +{ + struct kbtab *kbtab = input_get_drvdata(dev); + + kbtab->irq->dev = kbtab->usbdev; + if (usb_submit_urb(kbtab->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void kbtab_close(struct input_dev *dev) +{ + struct kbtab *kbtab = input_get_drvdata(dev); + + usb_kill_urb(kbtab->irq); +} + +static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct kbtab *kbtab; + struct input_dev *input_dev; + int error = -ENOMEM; + + kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbtab || !input_dev) + goto fail1; + + kbtab->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &kbtab->data_dma); + if (!kbtab->data) + goto fail1; + + kbtab->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!kbtab->irq) + goto fail2; + + kbtab->usbdev = dev; + kbtab->dev = input_dev; + + usb_make_path(dev, kbtab->phys, sizeof(kbtab->phys)); + strlcat(kbtab->phys, "/input0", sizeof(kbtab->phys)); + + input_dev->name = "KB Gear Tablet"; + input_dev->phys = kbtab->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, kbtab); + + input_dev->open = kbtab_open; + input_dev->close = kbtab_close; + + input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC); + input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH); + input_dev->mscbit[0] |= BIT(MSC_SERIAL); + input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0); + + endpoint = &intf->cur_altsetting->endpoint[0].desc; + + usb_fill_int_urb(kbtab->irq, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + kbtab->data, 8, + kbtab_irq, kbtab, endpoint->bInterval); + kbtab->irq->transfer_dma = kbtab->data_dma; + kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(kbtab->dev); + if (error) + goto fail3; + + usb_set_intfdata(intf, kbtab); + + return 0; + + fail3: usb_free_urb(kbtab->irq); + fail2: usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma); + fail1: input_free_device(input_dev); + kfree(kbtab); + return error; +} + +static void kbtab_disconnect(struct usb_interface *intf) +{ + struct kbtab *kbtab = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + if (kbtab) { + usb_kill_urb(kbtab->irq); + input_unregister_device(kbtab->dev); + usb_free_urb(kbtab->irq); + usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma); + kfree(kbtab); + } +} + +static struct usb_driver kbtab_driver = { + .name = "kbtab", + .probe = kbtab_probe, + .disconnect = kbtab_disconnect, + .id_table = kbtab_ids, +}; + +static int __init kbtab_init(void) +{ + int retval; + retval = usb_register(&kbtab_driver); + if (retval) + goto out; + info(DRIVER_VERSION ":" DRIVER_DESC); +out: + return retval; +} + +static void __exit kbtab_exit(void) +{ + usb_deregister(&kbtab_driver); +} + +module_init(kbtab_init); +module_exit(kbtab_exit); diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h new file mode 100644 index 00000000000..ef01a807ec0 --- /dev/null +++ b/drivers/input/tablet/wacom.h @@ -0,0 +1,131 @@ +/* + * drivers/input/tablet/wacom.h + * + * USB Wacom Graphire and Wacom Intuos tablet support + * + * Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz> + * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk> + * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at> + * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org> + * Copyright (c) 2000 James E. Blair <corvus@gnu.org> + * Copyright (c) 2000 Daniel Egger <egger@suse.de> + * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com> + * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be> + * Copyright (c) 2002-2006 Ping Cheng <pingc@wacom.com> + * + * ChangeLog: + * v0.1 (vp) - Initial release + * v0.2 (aba) - Support for all buttons / combinations + * v0.3 (vp) - Support for Intuos added + * v0.4 (sm) - Support for more Intuos models, menustrip + * relative mode, proximity. + * v0.5 (vp) - Big cleanup, nifty features removed, + * they belong in userspace + * v1.8 (vp) - Submit URB only when operating, moved to CVS, + * use input_report_key instead of report_btn and + * other cleanups + * v1.11 (vp) - Add URB ->dev setting for new kernels + * v1.11 (jb) - Add support for the 4D Mouse & Lens + * v1.12 (de) - Add support for two more inking pen IDs + * v1.14 (vp) - Use new USB device id probing scheme. + * Fix Wacom Graphire mouse wheel + * v1.18 (vp) - Fix mouse wheel direction + * Make mouse relative + * v1.20 (fl) - Report tool id for Intuos devices + * - Multi tools support + * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...) + * - Add PL models support + * - Fix Wacom Graphire mouse wheel again + * v1.21 (vp) - Removed protocol descriptions + * - Added MISC_SERIAL for tool serial numbers + * (gb) - Identify version on module load. + * v1.21.1 (fl) - added Graphire2 support + * v1.21.2 (fl) - added Intuos2 support + * - added all the PL ids + * v1.21.3 (fl) - added another eraser id from Neil Okamoto + * - added smooth filter for Graphire from Peri Hankey + * - added PenPartner support from Olaf van Es + * - new tool ids from Ole Martin Bjoerndalen + * v1.29 (pc) - Add support for more tablets + * - Fix pressure reporting + * v1.30 (vp) - Merge 2.4 and 2.5 drivers + * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse + * - Cleanups here and there + * v1.30.1 (pi) - Added Graphire3 support + * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ... + * v1.43 (pc) - Added support for Cintiq 21UX + * - Fixed a Graphire bug + * - Merged wacom_intuos3_irq into wacom_intuos_irq + * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc. + * - Report Device IDs + * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19 + * - Minor data report fix + * v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c, + * - where wacom_sys.c deals with system specific code, + * - and wacom_wac.c deals with Wacom specific code + * - Support Intuos3 4x6 + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef WACOM_H +#define WACOM_H +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb/input.h> +#include <asm/unaligned.h> + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.46" +#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" +#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_WACOM 0x056a + +struct wacom { + dma_addr_t data_dma; + struct input_dev *dev; + struct usb_device *usbdev; + struct urb *irq; + struct wacom_wac * wacom_wac; + char phys[32]; +}; + +struct wacom_combo { + struct wacom * wacom; + struct urb * urb; +}; + +extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo); +extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data); +extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data); +extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data); +extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value); +extern void wacom_input_sync(void *wcombo); +extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern __u16 wacom_le16_to_cpu(unsigned char *data); +extern __u16 wacom_be16_to_cpu(unsigned char *data); +extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id); +extern const struct usb_device_id * get_device_table(void); + +#endif diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c new file mode 100644 index 00000000000..83bddef6606 --- /dev/null +++ b/drivers/input/tablet/wacom_sys.c @@ -0,0 +1,318 @@ +/* + * drivers/input/tablet/wacom_sys.c + * + * USB Wacom Graphire and Wacom Intuos tablet support - system specific code + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "wacom.h" +#include "wacom_wac.h" + +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_SET_REPORT 0x09 + +static int usb_get_report(struct usb_interface *intf, unsigned char type, + unsigned char id, void *buf, int size) +{ + return usb_control_msg(interface_to_usbdev(intf), + usb_rcvctrlpipe(interface_to_usbdev(intf), 0), + USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber, + buf, size, 100); +} + +static int usb_set_report(struct usb_interface *intf, unsigned char type, + unsigned char id, void *buf, int size) +{ + return usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber, + buf, size, 1000); +} + +static struct input_dev * get_input_dev(struct wacom_combo *wcombo) +{ + return wcombo->wacom->dev; +} + +static void wacom_sys_irq(struct urb *urb) +{ + struct wacom *wacom = urb->context; + struct wacom_combo wcombo; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + wcombo.wacom = wacom; + wcombo.urb = urb; + + if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo)) + input_sync(get_input_dev(&wcombo)); + + exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + +void wacom_report_key(void *wcombo, unsigned int key_type, int key_data) +{ + input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data); + return; +} + +void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data) +{ + input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data); + return; +} + +void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data) +{ + input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data); + return; +} + +void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value) +{ + input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value); + return; +} + +__u16 wacom_be16_to_cpu(unsigned char *data) +{ + __u16 value; + value = be16_to_cpu(*(__be16 *) data); + return value; +} + +__u16 wacom_le16_to_cpu(unsigned char *data) +{ + __u16 value; + value = le16_to_cpu(*(__le16 *) data); + return value; +} + +void wacom_input_sync(void *wcombo) +{ + input_sync(get_input_dev((struct wacom_combo *)wcombo)); + return; +} + +static int wacom_open(struct input_dev *dev) +{ + struct wacom *wacom = input_get_drvdata(dev); + + wacom->irq->dev = wacom->usbdev; + if (usb_submit_urb(wacom->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void wacom_close(struct input_dev *dev) +{ + struct wacom *wacom = input_get_drvdata(dev); + + usb_kill_urb(wacom->irq); +} + +void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->evbit[0] |= BIT(EV_MSC); + input_dev->mscbit[0] |= BIT(MSC_SERIAL); + input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER); + input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_4); +} + +void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->evbit[0] |= BIT(EV_REL); + input_dev->relbit[0] |= BIT(REL_WHEEL); + input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0); +} + +void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER); + input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3); + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); +} + +void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7); + input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); +} + +void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL); + input_dev->mscbit[0] |= BIT(MSC_SERIAL); + input_dev->relbit[0] |= BIT(REL_WHEEL); + input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA); + input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH) + | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0); + input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); + input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); + input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); +} + +void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER); +} + +void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER); +} + +static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct wacom *wacom; + struct wacom_wac *wacom_wac; + struct input_dev *input_dev; + int error = -ENOMEM; + char rep_data[2], limit = 0; + + wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); + wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!wacom || !input_dev || !wacom_wac) + goto fail1; + + wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma); + if (!wacom_wac->data) + goto fail1; + + wacom->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!wacom->irq) + goto fail2; + + wacom->usbdev = dev; + wacom->dev = input_dev; + usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); + strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); + + wacom_wac->features = get_wacom_feature(id); + BUG_ON(wacom_wac->features->pktlen > 10); + + input_dev->name = wacom_wac->features->name; + wacom->wacom_wac = wacom_wac; + usb_to_input_id(dev, &input_dev->id); + + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, wacom); + + input_dev->open = wacom_open; + input_dev->close = wacom_close; + + input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS); + input_set_abs_params(input_dev, ABS_X, 0, wacom_wac->features->x_max, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, wacom_wac->features->y_max, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_wac->features->pressure_max, 0, 0); + input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC); + + wacom_init_input_dev(input_dev, wacom_wac); + + endpoint = &intf->cur_altsetting->endpoint[0].desc; + + usb_fill_int_urb(wacom->irq, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + wacom_wac->data, wacom_wac->features->pktlen, + wacom_sys_irq, wacom, endpoint->bInterval); + wacom->irq->transfer_dma = wacom->data_dma; + wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(wacom->dev); + if (error) + goto fail3; + + /* Ask the tablet to report tablet data. Repeat until it succeeds */ + do { + rep_data[0] = 2; + rep_data[1] = 2; + usb_set_report(intf, 3, 2, rep_data, 2); + usb_get_report(intf, 3, 2, rep_data, 2); + } while (rep_data[1] != 2 && limit++ < 5); + + usb_set_intfdata(intf, wacom); + return 0; + + fail3: usb_free_urb(wacom->irq); + fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma); + fail1: input_free_device(input_dev); + kfree(wacom); + kfree(wacom_wac); + return error; +} + +static void wacom_disconnect(struct usb_interface *intf) +{ + struct wacom *wacom = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (wacom) { + usb_kill_urb(wacom->irq); + input_unregister_device(wacom->dev); + usb_free_urb(wacom->irq); + usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma); + kfree(wacom->wacom_wac); + kfree(wacom); + } +} + +static struct usb_driver wacom_driver = { + .name = "wacom", + .probe = wacom_probe, + .disconnect = wacom_disconnect, +}; + +static int __init wacom_init(void) +{ + int result; + wacom_driver.id_table = get_device_table(); + result = usb_register(&wacom_driver); + if (result == 0) + info(DRIVER_VERSION ":" DRIVER_DESC); + return result; +} + +static void __exit wacom_exit(void) +{ + usb_deregister(&wacom_driver); +} + +module_init(wacom_init); +module_exit(wacom_exit); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c new file mode 100644 index 00000000000..7661f03a2db --- /dev/null +++ b/drivers/input/tablet/wacom_wac.c @@ -0,0 +1,675 @@ +/* + * drivers/input/tablet/wacom_wac.c + * + * USB Wacom Graphire and Wacom Intuos tablet support - Wacom specific code + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "wacom.h" +#include "wacom_wac.h" + +static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo) +{ + unsigned char *data = wacom->data; + + switch (data[0]) { + case 1: + if (data[5] & 0x80) { + wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID; + wacom_report_key(wcombo, wacom->tool[0], 1); + wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */ + wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); + wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); + wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127); + wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127)); + wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40)); + } else { + wacom_report_key(wcombo, wacom->tool[0], 0); + wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */ + wacom_report_abs(wcombo, ABS_PRESSURE, -1); + wacom_report_key(wcombo, BTN_TOUCH, 0); + } + break; + case 2: + wacom_report_key(wcombo, BTN_TOOL_PEN, 1); + wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */ + wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); + wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); + wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127); + wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); + wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40)); + break; + default: + printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]); + return 0; + } + return 1; +} + +static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo) +{ + unsigned char *data = wacom->data; + int prox, id, pressure; + + if (data[0] != 2) { + dbg("wacom_pl_irq: received unknown report #%d", data[0]); + return 0; + } + + prox = data[1] & 0x40; + + id = ERASER_DEVICE_ID; + if (prox) { + + pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); + if (wacom->features->pressure_max > 255) + pressure = (pressure << 1) | ((data[4] >> 6) & 1); + pressure += (wacom->features->pressure_max + 1) / 2; + + /* + * if going from out of proximity into proximity select between the eraser + * and the pen based on the state of the stylus2 button, choose eraser if + * pressed else choose pen. if not a proximity change from out to in, send + * an out of proximity for previous tool then a in for new tool. + */ + if (!wacom->tool[0]) { + /* Eraser bit set for DTF */ + if (data[1] & 0x10) + wacom->tool[1] = BTN_TOOL_RUBBER; + else + /* Going into proximity select tool */ + wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + } else { + /* was entered with stylus2 pressed */ + if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) { + /* report out proximity for previous tool */ + wacom_report_key(wcombo, wacom->tool[1], 0); + wacom_input_sync(wcombo); + wacom->tool[1] = BTN_TOOL_PEN; + return 0; + } + } + if (wacom->tool[1] != BTN_TOOL_RUBBER) { + /* Unknown tool selected default to pen tool */ + wacom->tool[1] = BTN_TOOL_PEN; + id = STYLUS_DEVICE_ID; + } + wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */ + wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */ + wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); + wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); + wacom_report_abs(wcombo, ABS_PRESSURE, pressure); + + wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08); + wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10); + /* Only allow the stylus2 button to be reported for the pen tool. */ + wacom_report_key(wcombo, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); + } else { + /* report proximity-out of a (valid) tool */ + if (wacom->tool[1] != BTN_TOOL_RUBBER) { + /* Unknown tool selected default to pen tool */ + wacom->tool[1] = BTN_TOOL_PEN; + } + wacom_report_key(wcombo, wacom->tool[1], prox); + } + + wacom->tool[0] = prox; /* Save proximity state */ + return 1; +} + +static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo) +{ + unsigned char *data = wacom->data; + int id; + + if (data[0] != 2) { + printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]); + return 0; + } + + if (data[1] & 0x04) { + wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20); + wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08); + id = ERASER_DEVICE_ID; + } else { + wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20); + wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01); + id = STYLUS_DEVICE_ID; + } + wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */ + wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2])); + wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4])); + wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6])); + wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); + wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10); + return 1; +} + +static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) +{ + unsigned char *data = wacom->data; + int x, y, id, rw; + + if (data[0] != 2) { + dbg("wacom_graphire_irq: received unknown report #%d", data[0]); + return 0; + } + + id = STYLUS_DEVICE_ID; + if (data[1] & 0x80) { /* in prox */ + + switch ((data[1] >> 5) & 3) { + + case 0: /* Pen */ + wacom->tool[0] = BTN_TOOL_PEN; + break; + + case 1: /* Rubber */ + wacom->tool[0] = BTN_TOOL_RUBBER; + id = ERASER_DEVICE_ID; + break; + + case 2: /* Mouse with wheel */ + wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04); + if (wacom->features->type == WACOM_G4) { + rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03); + wacom_report_rel(wcombo, REL_WHEEL, -rw); + } else + wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]); + /* fall through */ + + case 3: /* Mouse without wheel */ + wacom->tool[0] = BTN_TOOL_MOUSE; + id = CURSOR_DEVICE_ID; + wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01); + wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02); + if (wacom->features->type == WACOM_G4) + wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f); + else + wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f); + break; + } + x = wacom_le16_to_cpu(&data[2]); + y = wacom_le16_to_cpu(&data[4]); + wacom_report_abs(wcombo, ABS_X, x); + wacom_report_abs(wcombo, ABS_Y, y); + if (wacom->tool[0] != BTN_TOOL_MOUSE) { + wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8)); + wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01); + wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); + wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04); + } + wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */ + wacom_report_key(wcombo, wacom->tool[0], 1); + } else if (!(data[1] & 0x90)) { + wacom_report_abs(wcombo, ABS_X, 0); + wacom_report_abs(wcombo, ABS_Y, 0); + if (wacom->tool[0] == BTN_TOOL_MOUSE) { + wacom_report_key(wcombo, BTN_LEFT, 0); + wacom_report_key(wcombo, BTN_RIGHT, 0); + wacom_report_abs(wcombo, ABS_DISTANCE, 0); + } else { + wacom_report_abs(wcombo, ABS_PRESSURE, 0); + wacom_report_key(wcombo, BTN_TOUCH, 0); + wacom_report_key(wcombo, BTN_STYLUS, 0); + wacom_report_key(wcombo, BTN_STYLUS2, 0); + } + wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ + wacom_report_key(wcombo, wacom->tool[0], 0); + } + + /* send pad data */ + if (wacom->features->type == WACOM_G4) { + if (data[7] & 0xf8) { + wacom_input_sync(wcombo); /* sync last event */ + wacom->id[1] = 1; + wacom->serial[1] = (data[7] & 0xf8); + wacom_report_key(wcombo, BTN_0, (data[7] & 0x40)); + wacom_report_key(wcombo, BTN_4, (data[7] & 0x80)); + rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3); + wacom_report_rel(wcombo, REL_WHEEL, rw); + wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0); + wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); + } else if (wacom->id[1]) { + wacom_input_sync(wcombo); /* sync last event */ + wacom->id[1] = 0; + wacom_report_key(wcombo, BTN_0, (data[7] & 0x40)); + wacom_report_key(wcombo, BTN_4, (data[7] & 0x80)); + wacom_report_key(wcombo, BTN_TOOL_FINGER, 0); + wacom_report_abs(wcombo, ABS_MISC, 0); + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); + } + } + return 1; +} + +static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) +{ + unsigned char *data = wacom->data; + int idx; + + /* tool number */ + idx = data[1] & 0x01; + + /* Enter report */ + if ((data[1] & 0xfc) == 0xc0) { + /* serial number of the tool */ + wacom->serial[idx] = ((data[3] & 0x0f) << 28) + + (data[4] << 20) + (data[5] << 12) + + (data[6] << 4) + (data[7] >> 4); + + wacom->id[idx] = (data[2] << 4) | (data[3] >> 4); + switch (wacom->id[idx]) { + case 0x812: /* Inking pen */ + case 0x801: /* Intuos3 Inking pen */ + case 0x012: + wacom->tool[idx] = BTN_TOOL_PENCIL; + break; + case 0x822: /* Pen */ + case 0x842: + case 0x852: + case 0x823: /* Intuos3 Grip Pen */ + case 0x813: /* Intuos3 Classic Pen */ + case 0x885: /* Intuos3 Marker Pen */ + case 0x022: + wacom->tool[idx] = BTN_TOOL_PEN; + break; + case 0x832: /* Stroke pen */ + case 0x032: + wacom->tool[idx] = BTN_TOOL_BRUSH; + break; + case 0x007: /* Mouse 4D and 2D */ + case 0x09c: + case 0x094: + case 0x017: /* Intuos3 2D Mouse */ + wacom->tool[idx] = BTN_TOOL_MOUSE; + break; + case 0x096: /* Lens cursor */ + case 0x097: /* Intuos3 Lens cursor */ + wacom->tool[idx] = BTN_TOOL_LENS; + break; + case 0x82a: /* Eraser */ + case 0x85a: + case 0x91a: + case 0xd1a: + case 0x0fa: + case 0x82b: /* Intuos3 Grip Pen Eraser */ + case 0x81b: /* Intuos3 Classic Pen Eraser */ + case 0x91b: /* Intuos3 Airbrush Eraser */ + wacom->tool[idx] = BTN_TOOL_RUBBER; + break; + case 0xd12: + case 0x912: + case 0x112: + case 0x913: /* Intuos3 Airbrush */ + wacom->tool[idx] = BTN_TOOL_AIRBRUSH; + break; + default: /* Unknown tool */ + wacom->tool[idx] = BTN_TOOL_PEN; + } + return 1; + } + + /* Exit report */ + if ((data[1] & 0xfe) == 0x80) { + wacom_report_abs(wcombo, ABS_X, 0); + wacom_report_abs(wcombo, ABS_Y, 0); + wacom_report_abs(wcombo, ABS_DISTANCE, 0); + if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { + wacom_report_key(wcombo, BTN_LEFT, 0); + wacom_report_key(wcombo, BTN_MIDDLE, 0); + wacom_report_key(wcombo, BTN_RIGHT, 0); + wacom_report_key(wcombo, BTN_SIDE, 0); + wacom_report_key(wcombo, BTN_EXTRA, 0); + wacom_report_abs(wcombo, ABS_THROTTLE, 0); + wacom_report_abs(wcombo, ABS_RZ, 0); + } else { + wacom_report_abs(wcombo, ABS_PRESSURE, 0); + wacom_report_abs(wcombo, ABS_TILT_X, 0); + wacom_report_abs(wcombo, ABS_TILT_Y, 0); + wacom_report_key(wcombo, BTN_STYLUS, 0); + wacom_report_key(wcombo, BTN_STYLUS2, 0); + wacom_report_key(wcombo, BTN_TOUCH, 0); + wacom_report_abs(wcombo, ABS_WHEEL, 0); + } + wacom_report_key(wcombo, wacom->tool[idx], 0); + wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + return 2; + } + return 0; +} + +static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo) +{ + unsigned char *data = wacom->data; + unsigned int t; + + /* general pen packet */ + if ((data[1] & 0xb8) == 0xa0) { + t = (data[6] << 2) | ((data[7] >> 6) & 3); + wacom_report_abs(wcombo, ABS_PRESSURE, t); + wacom_report_abs(wcombo, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f); + wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2); + wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4); + wacom_report_key(wcombo, BTN_TOUCH, t > 10); + } + + /* airbrush second packet */ + if ((data[1] & 0xbc) == 0xb4) { + wacom_report_abs(wcombo, ABS_WHEEL, + (data[6] << 2) | ((data[7] >> 6) & 3)); + wacom_report_abs(wcombo, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f); + } + return; +} + +static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) +{ + unsigned char *data = wacom->data; + unsigned int t; + int idx, result; + + if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) { + dbg("wacom_intuos_irq: received unknown report #%d", data[0]); + return 0; + } + + /* tool number */ + idx = data[1] & 0x01; + + /* pad packets. Works as a second tool and is always in prox */ + if (data[0] == 12) { + /* initiate the pad as a device */ + if (wacom->tool[1] != BTN_TOOL_FINGER) + wacom->tool[1] = BTN_TOOL_FINGER; + + wacom_report_key(wcombo, BTN_0, (data[5] & 0x01)); + wacom_report_key(wcombo, BTN_1, (data[5] & 0x02)); + wacom_report_key(wcombo, BTN_2, (data[5] & 0x04)); + wacom_report_key(wcombo, BTN_3, (data[5] & 0x08)); + wacom_report_key(wcombo, BTN_4, (data[6] & 0x01)); + wacom_report_key(wcombo, BTN_5, (data[6] & 0x02)); + wacom_report_key(wcombo, BTN_6, (data[6] & 0x04)); + wacom_report_key(wcombo, BTN_7, (data[6] & 0x08)); + wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); + wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); + + if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) | + data[2] | (data[3] & 0x1f) | data[4]) + wacom_report_key(wcombo, wacom->tool[1], 1); + else + wacom_report_key(wcombo, wacom->tool[1], 0); + wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff); + return 1; + } + + /* process in/out prox events */ + result = wacom_intuos_inout(wacom, wcombo); + if (result) + return result-1; + + /* Only large I3 and I1 & I2 support Lense Cursor */ + if((wacom->tool[idx] == BTN_TOOL_LENS) + && ((wacom->features->type == INTUOS3) + || (wacom->features->type == INTUOS3S))) + return 0; + + /* Cintiq doesn't send data when RDY bit isn't set */ + if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40)) + return 0; + + if (wacom->features->type >= INTUOS3S) { + wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); + wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); + wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + } else { + wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2])); + wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4])); + wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); + } + + /* process general packets */ + wacom_intuos_general(wacom, wcombo); + + /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */ + if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) { + + if (data[1] & 0x02) { + /* Rotation packet */ + if (wacom->features->type >= INTUOS3S) { + /* I3 marker pen rotation reported as wheel + * due to valuator limitation + */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : + ((t-1) / 2 + 450)) : (450 - t / 2) ; + wacom_report_abs(wcombo, ABS_WHEEL, t); + } else { + /* 4D mouse rotation packet */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ? + ((t - 1) / 2) : -t / 2); + } + + } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3S) { + /* 4D mouse packet */ + wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01); + wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02); + wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04); + + wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x20); + wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x10); + t = (data[6] << 2) | ((data[7] >> 6) & 3); + wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); + + } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { + /* 2D mouse packet */ + wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04); + wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08); + wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x10); + wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01) + - ((data[8] & 0x02) >> 1)); + + /* I3 2D mouse side buttons */ + if (wacom->features->type >= INTUOS3S && wacom->features->type <= INTUOS3L) { + wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40); + wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20); + } + + } else if (wacom->features->type < INTUOS3S || wacom->features->type == INTUOS3L) { + /* Lens cursor packets */ + wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01); + wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02); + wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04); + wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x10); + wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x08); + } + } + + wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */ + wacom_report_key(wcombo, wacom->tool[idx], 1); + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + return 1; +} + +int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo) +{ + switch (wacom_wac->features->type) { + case PENPARTNER: + return (wacom_penpartner_irq(wacom_wac, wcombo)); + break; + case PL: + return (wacom_pl_irq(wacom_wac, wcombo)); + break; + case WACOM_G4: + case GRAPHIRE: + return (wacom_graphire_irq(wacom_wac, wcombo)); + break; + case PTU: + return (wacom_ptu_irq(wacom_wac, wcombo)); + break; + case INTUOS: + case INTUOS3S: + case INTUOS3: + case INTUOS3L: + case CINTIQ: + return (wacom_intuos_irq(wacom_wac, wcombo)); + break; + default: + return 0; + } + return 0; +} + +void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + switch (wacom_wac->features->type) { + case WACOM_G4: + input_dev_g4(input_dev, wacom_wac); + /* fall through */ + case GRAPHIRE: + input_dev_g(input_dev, wacom_wac); + break; + case INTUOS3: + case INTUOS3L: + case CINTIQ: + input_dev_i3(input_dev, wacom_wac); + /* fall through */ + case INTUOS3S: + input_dev_i3s(input_dev, wacom_wac); + case INTUOS: + input_dev_i(input_dev, wacom_wac); + break; + case PL: + case PTU: + input_dev_pl(input_dev, wacom_wac); + break; + case PENPARTNER: + input_dev_pt(input_dev, wacom_wac); + break; + } + return; +} + +static struct wacom_features wacom_features[] = { + { "Wacom Penpartner", 7, 5040, 3780, 255, 0, PENPARTNER }, + { "Wacom Graphire", 8, 10206, 7422, 511, 63, GRAPHIRE }, + { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 63, GRAPHIRE }, + { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 63, GRAPHIRE }, + { "Wacom Graphire3", 8, 10208, 7424, 511, 63, GRAPHIRE }, + { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 63, GRAPHIRE }, + { "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 63, WACOM_G4 }, + { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 63, WACOM_G4 }, + { "Wacom Volito", 8, 5104, 3712, 511, 63, GRAPHIRE }, + { "Wacom PenStation2", 8, 3250, 2320, 255, 63, GRAPHIRE }, + { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE }, + { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE }, + { "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE }, + { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS }, + { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, + { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS }, + { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 31, INTUOS }, + { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 31, INTUOS }, + { "Wacom PL400", 8, 5408, 4056, 255, 0, PL }, + { "Wacom PL500", 8, 6144, 4608, 255, 0, PL }, + { "Wacom PL600", 8, 6126, 4604, 255, 0, PL }, + { "Wacom PL600SX", 8, 6260, 5016, 255, 0, PL }, + { "Wacom PL550", 8, 6144, 4608, 511, 0, PL }, + { "Wacom PL800", 8, 7220, 5780, 511, 0, PL }, + { "Wacom PL700", 8, 6758, 5406, 511, 0, PL }, + { "Wacom PL510", 8, 6282, 4762, 511, 0, PL }, + { "Wacom DTU710", 8, 34080, 27660, 511, 0, PL }, + { "Wacom DTF521", 8, 6282, 4762, 511, 0, PL }, + { "Wacom DTF720", 8, 6858, 5506, 511, 0, PL }, + { "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU }, + { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS }, + { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, + { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 31, INTUOS }, + { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS }, + { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS }, + { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S }, + { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 }, + { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 63, INTUOS3 }, + { "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 63, INTUOS3L }, + { "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 63, INTUOS3L }, + { "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 }, + { "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S }, + { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ }, + { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, + { } +}; + +static struct usb_device_id wacom_ids[] = { + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC4) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB7) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) }, + { } +}; + +const struct usb_device_id * get_device_table(void) { + const struct usb_device_id * id_table = wacom_ids; + return id_table; +} + +struct wacom_features * get_wacom_feature(const struct usb_device_id * id) { + int index = id - wacom_ids; + struct wacom_features *wf = &wacom_features[index]; + return wf; +} + +MODULE_DEVICE_TABLE(usb, wacom_ids); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h new file mode 100644 index 00000000000..a5e12e8756d --- /dev/null +++ b/drivers/input/tablet/wacom_wac.h @@ -0,0 +1,49 @@ +/* + * drivers/input/tablet/wacom_wac.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef WACOM_WAC_H +#define WACOM_WAC_H + +#define STYLUS_DEVICE_ID 0x02 +#define CURSOR_DEVICE_ID 0x06 +#define ERASER_DEVICE_ID 0x0A +#define PAD_DEVICE_ID 0x0F + +enum { + PENPARTNER = 0, + GRAPHIRE, + WACOM_G4, + PTU, + PL, + INTUOS, + INTUOS3S, + INTUOS3, + INTUOS3L, + CINTIQ, + MAX_TYPE +}; + +struct wacom_features { + char *name; + int pktlen; + int x_max; + int y_max; + int pressure_max; + int distance_max; + int type; +}; + +struct wacom_wac { + unsigned char *data; + int tool[2]; + int id[2]; + __u32 serial[2]; + struct wacom_features *features; +}; + +#endif diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 971618059a6..5e640aeb03c 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1,5 +1,5 @@ # -# Mouse driver configuration +# Touchscreen driver configuration # menuconfig INPUT_TOUCHSCREEN bool "Touchscreens" @@ -44,9 +44,9 @@ config TOUCHSCREEN_BITSY config TOUCHSCREEN_CORGI tristate "SharpSL (Corgi and Spitz series) touchscreen driver" depends on PXA_SHARPSL - default y + default y help - Say Y here to enable the driver for the touchscreen on the + Say Y here to enable the driver for the touchscreen on the Sharp SL-C7xx and SL-Cxx00 series of PDAs. If unsure, say N. @@ -164,4 +164,58 @@ config TOUCHSCREEN_UCB1400 To compile this driver as a module, choose M here: the module will be called ucb1400_ts. +config TOUCHSCREEN_USB_COMPOSITE + tristate "USB Touchscreen Driver" + select USB + help + USB Touchscreen driver for: + - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700) + - PanJit TouchSet USB + - 3M MicroTouch USB (EX II series) + - ITM + - some other eTurboTouch + - Gunze AHL61 + - DMC TSC-10/25 + + Have a look at <http://linux.chapter7.ch/touchkit/> for + a usage description and the required user-space stuff. + + To compile this driver as a module, choose M here: the + module will be called usbtouchscreen. + +config TOUCHSCREEN_USB_EGALAX + default y + bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_PANJIT + default y + bool "PanJit device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_3M + default y + bool "3M/Microtouch EX II series device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ITM + default y + bool "ITM device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ETURBO + default y + bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_GUNZE + default y + bool "Gunze AHL61 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_DMC_TSC10 + default y + bool "DMC TSC-10/25 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 30e6e2217a1..2f86d6ad06d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -1,17 +1,18 @@ # -# Makefile for the mouse drivers. +# Makefile for the touchscreen drivers. # # Each configuration option enables a list of files. obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o -obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o -obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o -obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o -obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o -obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o -obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o -obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o +obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o +obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o +obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o +obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 0a26e066354..693e3b2a65a 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -39,7 +39,8 @@ /* * This code has been heavily tested on a Nokia 770, and lightly * tested on other ads7846 devices (OSK/Mistral, Lubbock). - * Support for ads7843 and ads7845 has only been stubbed in. + * Support for ads7843 tested on Atmel at91sam926x-EK. + * Support for ads7845 has only been stubbed in. * * IRQ handling needs a workaround because of a shortcoming in handling * edge triggered IRQs on some platforms like the OMAP1/2. These @@ -246,18 +247,16 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) /* REVISIT: take a few more samples, and compare ... */ - /* maybe off internal vREF */ - if (use_internal) { - req->ref_off = REF_OFF; - req->xfer[4].tx_buf = &req->ref_off; - req->xfer[4].len = 1; - spi_message_add_tail(&req->xfer[4], &req->msg); - - req->xfer[5].rx_buf = &req->scratch; - req->xfer[5].len = 2; - CS_CHANGE(req->xfer[5]); - spi_message_add_tail(&req->xfer[5], &req->msg); - } + /* converter in low power mode & enable PENIRQ */ + req->ref_off = PWRDOWN; + req->xfer[4].tx_buf = &req->ref_off; + req->xfer[4].len = 1; + spi_message_add_tail(&req->xfer[4], &req->msg); + + req->xfer[5].rx_buf = &req->scratch; + req->xfer[5].len = 2; + CS_CHANGE(req->xfer[5]); + spi_message_add_tail(&req->xfer[5], &req->msg); ts->irq_disabled = 1; disable_irq(spi->irq); @@ -536,6 +535,9 @@ static void ads7846_rx(void *ads) } else Rt = 0; + if (ts->model == 7843) + Rt = ts->pressure_max / 2; + /* Sample found inconsistent by debouncing or pressure is beyond * the maximum. Don't report it to user space, repeat at least * once more the measurement @@ -897,7 +899,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) input_dev->name = "ADS784x Touchscreen"; input_dev->phys = ts->phys; - input_dev->cdev.dev = &spi->dev; + input_dev->dev.parent = &spi->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c index e2945582828..e6a31d11878 100644 --- a/drivers/input/touchscreen/corgi_ts.c +++ b/drivers/input/touchscreen/corgi_ts.c @@ -300,8 +300,7 @@ static int __init corgits_probe(struct platform_device *pdev) input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0002; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &pdev->dev; - input_dev->private = corgi_ts; + input_dev->dev.parent = &pdev->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index 9d61cd133d0..557d781719f 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -312,14 +312,13 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) init_completion(&elo->cmd_done); snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys); - input_dev->private = elo; input_dev->name = "Elo Serial TouchScreen"; input_dev->phys = elo->phys; input_dev->id.bustype = BUS_RS232; input_dev->id.vendor = SERIO_ELO; input_dev->id.product = elo->id; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c index 9157eb148e8..39d602600d7 100644 --- a/drivers/input/touchscreen/gunze.c +++ b/drivers/input/touchscreen/gunze.c @@ -130,13 +130,13 @@ static int gunze_connect(struct serio *serio, struct serio_driver *drv) gunze->dev = input_dev; snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys); - input_dev->private = gunze; input_dev->name = "Gunze AHL-51S TouchScreen"; input_dev->phys = gunze->phys; input_dev->id.bustype = BUS_RS232; input_dev->id.vendor = SERIO_GUNZE; input_dev->id.product = 0x0051; input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0); diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index c4116d4f64e..09ed7803cb8 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -147,7 +147,7 @@ enum flite_pwr { unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr) { unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness; - struct h3600_dev *ts = dev->private; + struct h3600_dev *ts = input_get_drvdata(dev); /* Must be in this order */ ts->serio->write(ts->serio, 1); @@ -260,7 +260,7 @@ static int h3600ts_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { #if 0 - struct h3600_dev *ts = dev->private; + struct h3600_dev *ts = input_get_drvdata(dev); switch (type) { case EV_LED: { @@ -367,8 +367,9 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) input_dev->id.vendor = SERIO_H3600; input_dev->id.product = 0x0666; /* FIXME !!! We can ask the hardware */ input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; - input_dev->private = ts; + input_dev->dev.parent = &serio->dev; + + input_set_drvdata(input_dev, ts); input_dev->event = h3600ts_event; diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c index 24908747274..61c15024c2a 100644 --- a/drivers/input/touchscreen/hp680_ts_input.c +++ b/drivers/input/touchscreen/hp680_ts_input.c @@ -21,7 +21,7 @@ static void do_softint(void *data); static struct input_dev *hp680_ts_dev; -static DECLARE_WORK(work, do_softint, 0); +static DECLARE_WORK(work, do_softint); static void do_softint(void *data) { diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c index c3c2d735d0e..4ec3b1f940c 100644 --- a/drivers/input/touchscreen/mtouch.c +++ b/drivers/input/touchscreen/mtouch.c @@ -144,13 +144,13 @@ static int mtouch_connect(struct serio *serio, struct serio_driver *drv) mtouch->dev = input_dev; snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys); - input_dev->private = mtouch; input_dev->name = "MicroTouch Serial TouchScreen"; input_dev->phys = mtouch->phys; input_dev->id.bustype = BUS_RS232; input_dev->id.vendor = SERIO_MICROTOUCH; input_dev->id.product = 0; input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0); diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c index bd2767991ae..f2c0d3c7149 100644 --- a/drivers/input/touchscreen/penmount.c +++ b/drivers/input/touchscreen/penmount.c @@ -105,14 +105,13 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv) pm->dev = input_dev; snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys); - input_dev->private = pm; input_dev->name = "Penmount Serial TouchScreen"; input_dev->phys = pm->phys; input_dev->id.bustype = BUS_RS232; input_dev->id.vendor = SERIO_PENMOUNT; input_dev->id.product = 0; input_dev->id.version = 0x0100; - input_dev->cdev.dev = &serio->dev; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c index 35ba46c6ad2..3def7bb1df4 100644 --- a/drivers/input/touchscreen/touchright.c +++ b/drivers/input/touchscreen/touchright.c @@ -118,13 +118,13 @@ static int tr_connect(struct serio *serio, struct serio_driver *drv) tr->dev = input_dev; snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys); - input_dev->private = tr; input_dev->name = "Touchright Serial TouchScreen"; input_dev->phys = tr->phys; input_dev->id.bustype = BUS_RS232; input_dev->id.vendor = SERIO_TOUCHRIGHT; input_dev->id.product = 0; input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0); diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c index 4dc073dacab..ac4bdcf1866 100644 --- a/drivers/input/touchscreen/touchwin.c +++ b/drivers/input/touchscreen/touchwin.c @@ -125,13 +125,13 @@ static int tw_connect(struct serio *serio, struct serio_driver *drv) tw->dev = input_dev; snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys); - input_dev->private = tw; input_dev->name = "Touchwindow Serial TouchScreen"; input_dev->phys = tw->phys; input_dev->id.bustype = BUS_RS232; input_dev->id.vendor = SERIO_TOUCHWIN; input_dev->id.product = 0; input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0); diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index e8606c48c9c..6582816a047 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -97,6 +97,8 @@ struct ucb1400 { }; static int adcsync; +static int ts_delay = 55; /* us */ +static int ts_delay_pressure; /* us */ static inline u16 ucb1400_reg_read(struct ucb1400 *ucb, u16 reg) { @@ -159,6 +161,7 @@ static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb) UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + udelay(ts_delay_pressure); return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY); } @@ -180,7 +183,7 @@ static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb) UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); - udelay(55); + udelay(ts_delay); return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY); } @@ -203,7 +206,7 @@ static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400 *ucb) UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); - udelay(55); + udelay(ts_delay); return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPX); } @@ -369,7 +372,7 @@ static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid) static int ucb1400_ts_open(struct input_dev *idev) { - struct ucb1400 *ucb = idev->private; + struct ucb1400 *ucb = input_get_drvdata(idev); int ret = 0; BUG_ON(ucb->ts_task); @@ -385,7 +388,7 @@ static int ucb1400_ts_open(struct input_dev *idev) static void ucb1400_ts_close(struct input_dev *idev) { - struct ucb1400 *ucb = idev->private; + struct ucb1400 *ucb = input_get_drvdata(idev); if (ucb->ts_task) kthread_stop(ucb->ts_task); @@ -507,8 +510,9 @@ static int ucb1400_ts_probe(struct device *dev) } printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq); - idev->private = ucb; - idev->cdev.dev = dev; + input_set_drvdata(idev, ucb); + + idev->dev.parent = dev; idev->name = "UCB1400 touchscreen interface"; idev->id.vendor = ucb1400_reg_read(ucb, AC97_VENDOR_ID1); idev->id.product = id; @@ -571,7 +575,15 @@ static void __exit ucb1400_ts_exit(void) driver_unregister(&ucb1400_ts_driver); } -module_param(adcsync, int, 0444); +module_param(adcsync, bool, 0444); +MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin."); + +module_param(ts_delay, int, 0444); +MODULE_PARM_DESC(ts_delay, "Delay between panel setup and position read. Default = 55us."); + +module_param(ts_delay_pressure, int, 0444); +MODULE_PARM_DESC(ts_delay_pressure, + "delay between panel setup and pressure read. Default = 0us."); module_init(ucb1400_ts_init); module_exit(ucb1400_ts_exit); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c new file mode 100644 index 00000000000..8e18e6c6477 --- /dev/null +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -0,0 +1,839 @@ +/****************************************************************************** + * usbtouchscreen.c + * Driver for USB Touchscreens, supporting those devices: + * - eGalax Touchkit + * includes eTurboTouch CT-410/510/700 + * - 3M/Microtouch EX II series + * - ITM + * - PanJit TouchSet + * - eTurboTouch + * - Gunze AHL61 + * - DMC TSC-10/25 + * + * Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch> + * Copyright (C) by Todd E. Johnson (mtouchusb.c) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Driver is based on touchkitusb.c + * - ITM parts are from itmtouch.c + * - 3M parts are from mtouchusb.c + * - PanJit parts are from an unmerged driver by Lanslott Gish + * - DMC TSC 10/25 are from Holger Schurig, with ideas from an unmerged + * driver from Marius Vollmer + * + *****************************************************************************/ + +//#define DEBUG + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/usb/input.h> + + +#define DRIVER_VERSION "v0.5" +#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>" +#define DRIVER_DESC "USB Touchscreen Driver" + +static int swap_xy; +module_param(swap_xy, bool, 0644); +MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped."); + +/* device specifc data/functions */ +struct usbtouch_usb; +struct usbtouch_device_info { + int min_xc, max_xc; + int min_yc, max_yc; + int min_press, max_press; + int rept_size; + int flags; + + void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len); + int (*get_pkt_len) (unsigned char *pkt, int len); + int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt); + int (*init) (struct usbtouch_usb *usbtouch); +}; + +#define USBTOUCH_FLG_BUFFER 0x01 + + +/* a usbtouch device */ +struct usbtouch_usb { + unsigned char *data; + dma_addr_t data_dma; + unsigned char *buffer; + int buf_len; + struct urb *irq; + struct usb_device *udev; + struct input_dev *input; + struct usbtouch_device_info *type; + char name[128]; + char phys[64]; + + int x, y; + int touch, press; +}; + + +#if defined(CONFIG_USB_TOUCHSCREEN_EGALAX) || defined(CONFIG_USB_TOUCHSCREEN_ETURBO) +#define MULTI_PACKET +#endif + +#ifdef MULTI_PACKET +static void usbtouch_process_multi(struct usbtouch_usb *usbtouch, + unsigned char *pkt, int len); +#endif + +/* device types */ +enum { + DEVTPYE_DUMMY = -1, + DEVTYPE_EGALAX, + DEVTYPE_PANJIT, + DEVTYPE_3M, + DEVTYPE_ITM, + DEVTYPE_ETURBO, + DEVTYPE_GUNZE, + DEVTYPE_DMC_TSC10, +}; + +static struct usb_device_id usbtouch_devices[] = { +#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX + {USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX}, + {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX}, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT + {USB_DEVICE(0x134c, 0x0001), .driver_info = DEVTYPE_PANJIT}, + {USB_DEVICE(0x134c, 0x0002), .driver_info = DEVTYPE_PANJIT}, + {USB_DEVICE(0x134c, 0x0003), .driver_info = DEVTYPE_PANJIT}, + {USB_DEVICE(0x134c, 0x0004), .driver_info = DEVTYPE_PANJIT}, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_3M + {USB_DEVICE(0x0596, 0x0001), .driver_info = DEVTYPE_3M}, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_ITM + {USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM}, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO + {USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO}, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE + {USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE}, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10 + {USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10}, +#endif + + {} +}; + + +/***************************************************************************** + * eGalax part + */ + +#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX + +#define EGALAX_PKT_TYPE_MASK 0xFE +#define EGALAX_PKT_TYPE_REPT 0x80 +#define EGALAX_PKT_TYPE_DIAG 0x0A + +static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT) + return 0; + + dev->x = ((pkt[3] & 0x0F) << 7) | (pkt[4] & 0x7F); + dev->y = ((pkt[1] & 0x0F) << 7) | (pkt[2] & 0x7F); + dev->touch = pkt[0] & 0x01; + + return 1; +} + +static int egalax_get_pkt_len(unsigned char *buf, int len) +{ + switch (buf[0] & EGALAX_PKT_TYPE_MASK) { + case EGALAX_PKT_TYPE_REPT: + return 5; + + case EGALAX_PKT_TYPE_DIAG: + if (len < 2) + return -1; + + return buf[1] + 2; + } + + return 0; +} +#endif + + +/***************************************************************************** + * PanJit Part + */ +#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT +static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1]; + dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3]; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + + +/***************************************************************************** + * 3M/Microtouch Part + */ +#ifdef CONFIG_USB_TOUCHSCREEN_3M + +#define MTOUCHUSB_ASYNC_REPORT 1 +#define MTOUCHUSB_RESET 7 +#define MTOUCHUSB_REQ_CTRLLR_ID 10 + +static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = (pkt[8] << 8) | pkt[7]; + dev->y = (pkt[10] << 8) | pkt[9]; + dev->touch = (pkt[2] & 0x40) ? 1 : 0; + + return 1; +} + +static int mtouch_init(struct usbtouch_usb *usbtouch) +{ + int ret, i; + + ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0), + MTOUCHUSB_RESET, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", + __FUNCTION__, ret); + if (ret < 0) + return ret; + msleep(150); + + for (i = 0; i < 3; i++) { + ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0), + MTOUCHUSB_ASYNC_REPORT, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", + __FUNCTION__, ret); + if (ret >= 0) + break; + if (ret != -EPIPE) + return ret; + } + + return 0; +} +#endif + + +/***************************************************************************** + * ITM Part + */ +#ifdef CONFIG_USB_TOUCHSCREEN_ITM +static int itm_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + int touch; + /* + * ITM devices report invalid x/y data if not touched. + * if the screen was touched before but is not touched any more + * report touch as 0 with the last valid x/y data once. then stop + * reporting data until touched again. + */ + dev->press = ((pkt[2] & 0x01) << 7) | (pkt[5] & 0x7F); + + touch = ~pkt[7] & 0x20; + if (!touch) { + if (dev->touch) { + dev->touch = 0; + return 1; + } + + return 0; + } + + dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[3] & 0x7F); + dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[4] & 0x7F); + dev->touch = touch; + + return 1; +} +#endif + + +/***************************************************************************** + * eTurboTouch part + */ +#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO +static int eturbo_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + unsigned int shift; + + /* packets should start with sync */ + if (!(pkt[0] & 0x80)) + return 0; + + shift = (6 - (pkt[0] & 0x03)); + dev->x = ((pkt[3] << 7) | pkt[4]) >> shift; + dev->y = ((pkt[1] << 7) | pkt[2]) >> shift; + dev->touch = (pkt[0] & 0x10) ? 1 : 0; + + return 1; +} + +static int eturbo_get_pkt_len(unsigned char *buf, int len) +{ + if (buf[0] & 0x80) + return 5; + if (buf[0] == 0x01) + return 3; + return 0; +} +#endif + + +/***************************************************************************** + * Gunze part + */ +#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE +static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80)) + return 0; + + dev->x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F); + dev->y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F); + dev->touch = pkt[0] & 0x20; + + return 1; +} +#endif + +/***************************************************************************** + * DMC TSC-10/25 Part + * + * Documentation about the controller and it's protocol can be found at + * http://www.dmccoltd.com/files/controler/tsc10usb_pi_e.pdf + * http://www.dmccoltd.com/files/controler/tsc25_usb_e.pdf + */ +#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10 + +/* supported data rates. currently using 130 */ +#define TSC10_RATE_POINT 0x50 +#define TSC10_RATE_30 0x40 +#define TSC10_RATE_50 0x41 +#define TSC10_RATE_80 0x42 +#define TSC10_RATE_100 0x43 +#define TSC10_RATE_130 0x44 +#define TSC10_RATE_150 0x45 + +/* commands */ +#define TSC10_CMD_RESET 0x55 +#define TSC10_CMD_RATE 0x05 +#define TSC10_CMD_DATA1 0x01 + +static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) +{ + struct usb_device *dev = usbtouch->udev; + int ret; + unsigned char buf[2]; + + /* reset */ + buf[0] = buf[1] = 0xFF; + ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0), + TSC10_CMD_RESET, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, buf, 2, USB_CTRL_SET_TIMEOUT); + if (ret < 0) + return ret; + if (buf[0] != 0x06 || buf[1] != 0x00) + return -ENODEV; + + /* set coordinate output rate */ + buf[0] = buf[1] = 0xFF; + ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0), + TSC10_CMD_RATE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT); + if (ret < 0) + return ret; + if (buf[0] != 0x06 || buf[1] != 0x00) + return -ENODEV; + + /* start sending data */ + ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0), + TSC10_CMD_DATA1, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (ret < 0) + return ret; + + return 0; +} + + +static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[2] & 0x03) << 8) | pkt[1]; + dev->y = ((pkt[4] & 0x03) << 8) | pkt[3]; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + + +/***************************************************************************** + * the different device descriptors + */ +static struct usbtouch_device_info usbtouch_dev_info[] = { +#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX + [DEVTYPE_EGALAX] = { + .min_xc = 0x0, + .max_xc = 0x07ff, + .min_yc = 0x0, + .max_yc = 0x07ff, + .rept_size = 16, + .flags = USBTOUCH_FLG_BUFFER, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = egalax_get_pkt_len, + .read_data = egalax_read_data, + }, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT + [DEVTYPE_PANJIT] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 8, + .read_data = panjit_read_data, + }, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_3M + [DEVTYPE_3M] = { + .min_xc = 0x0, + .max_xc = 0x4000, + .min_yc = 0x0, + .max_yc = 0x4000, + .rept_size = 11, + .read_data = mtouch_read_data, + .init = mtouch_init, + }, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_ITM + [DEVTYPE_ITM] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .max_press = 0xff, + .rept_size = 8, + .read_data = itm_read_data, + }, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO + [DEVTYPE_ETURBO] = { + .min_xc = 0x0, + .max_xc = 0x07ff, + .min_yc = 0x0, + .max_yc = 0x07ff, + .rept_size = 8, + .flags = USBTOUCH_FLG_BUFFER, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = eturbo_get_pkt_len, + .read_data = eturbo_read_data, + }, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE + [DEVTYPE_GUNZE] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 4, + .read_data = gunze_read_data, + }, +#endif + +#ifdef CONFIG_USB_TOUCHSCREEN_DMC_TSC10 + [DEVTYPE_DMC_TSC10] = { + .min_xc = 0x0, + .max_xc = 0x03ff, + .min_yc = 0x0, + .max_yc = 0x03ff, + .rept_size = 5, + .init = dmc_tsc10_init, + .read_data = dmc_tsc10_read_data, + }, +#endif +}; + + +/***************************************************************************** + * Generic Part + */ +static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch, + unsigned char *pkt, int len) +{ + struct usbtouch_device_info *type = usbtouch->type; + + if (!type->read_data(usbtouch, pkt)) + return; + + input_report_key(usbtouch->input, BTN_TOUCH, usbtouch->touch); + + if (swap_xy) { + input_report_abs(usbtouch->input, ABS_X, usbtouch->y); + input_report_abs(usbtouch->input, ABS_Y, usbtouch->x); + } else { + input_report_abs(usbtouch->input, ABS_X, usbtouch->x); + input_report_abs(usbtouch->input, ABS_Y, usbtouch->y); + } + if (type->max_press) + input_report_abs(usbtouch->input, ABS_PRESSURE, usbtouch->press); + input_sync(usbtouch->input); +} + + +#ifdef MULTI_PACKET +static void usbtouch_process_multi(struct usbtouch_usb *usbtouch, + unsigned char *pkt, int len) +{ + unsigned char *buffer; + int pkt_len, pos, buf_len, tmp; + + /* process buffer */ + if (unlikely(usbtouch->buf_len)) { + /* try to get size */ + pkt_len = usbtouch->type->get_pkt_len( + usbtouch->buffer, usbtouch->buf_len); + + /* drop? */ + if (unlikely(!pkt_len)) + goto out_flush_buf; + + /* need to append -pkt_len bytes before able to get size */ + if (unlikely(pkt_len < 0)) { + int append = -pkt_len; + if (unlikely(append > len)) + append = len; + if (usbtouch->buf_len + append >= usbtouch->type->rept_size) + goto out_flush_buf; + memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append); + usbtouch->buf_len += append; + + pkt_len = usbtouch->type->get_pkt_len( + usbtouch->buffer, usbtouch->buf_len); + if (pkt_len < 0) + return; + } + + /* append */ + tmp = pkt_len - usbtouch->buf_len; + if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size) + goto out_flush_buf; + memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp); + usbtouch_process_pkt(usbtouch, usbtouch->buffer, pkt_len); + + buffer = pkt + tmp; + buf_len = len - tmp; + } else { + buffer = pkt; + buf_len = len; + } + + /* loop over the received packet, process */ + pos = 0; + while (pos < buf_len) { + /* get packet len */ + pkt_len = usbtouch->type->get_pkt_len(buffer + pos, len); + + /* unknown packet: drop everything */ + if (unlikely(!pkt_len)) + goto out_flush_buf; + + /* full packet: process */ + if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) { + usbtouch_process_pkt(usbtouch, buffer + pos, pkt_len); + } else { + /* incomplete packet: save in buffer */ + memcpy(usbtouch->buffer, buffer + pos, buf_len - pos); + usbtouch->buf_len = buf_len - pos; + return; + } + pos += pkt_len; + } + +out_flush_buf: + usbtouch->buf_len = 0; + return; +} +#endif + + +static void usbtouch_irq(struct urb *urb) +{ + struct usbtouch_usb *usbtouch = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIME: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length); + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); +} + +static int usbtouch_open(struct input_dev *input) +{ + struct usbtouch_usb *usbtouch = input_get_drvdata(input); + + usbtouch->irq->dev = usbtouch->udev; + + if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void usbtouch_close(struct input_dev *input) +{ + struct usbtouch_usb *usbtouch = input_get_drvdata(input); + + usb_kill_urb(usbtouch->irq); +} + + +static void usbtouch_free_buffers(struct usb_device *udev, + struct usbtouch_usb *usbtouch) +{ + usb_buffer_free(udev, usbtouch->type->rept_size, + usbtouch->data, usbtouch->data_dma); + kfree(usbtouch->buffer); +} + + +static int usbtouch_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usbtouch_usb *usbtouch; + struct input_dev *input_dev; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev(intf); + struct usbtouch_device_info *type; + int err = -ENOMEM; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + + usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!usbtouch || !input_dev) + goto out_free; + + type = &usbtouch_dev_info[id->driver_info]; + usbtouch->type = type; + if (!type->process_pkt) + type->process_pkt = usbtouch_process_pkt; + + usbtouch->data = usb_buffer_alloc(udev, type->rept_size, + GFP_KERNEL, &usbtouch->data_dma); + if (!usbtouch->data) + goto out_free; + + if (type->flags & USBTOUCH_FLG_BUFFER) { + usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL); + if (!usbtouch->buffer) + goto out_free_buffers; + } + + usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!usbtouch->irq) { + dbg("%s - usb_alloc_urb failed: usbtouch->irq", __FUNCTION__); + goto out_free_buffers; + } + + usbtouch->udev = udev; + usbtouch->input = input_dev; + + if (udev->manufacturer) + strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name)); + + if (udev->product) { + if (udev->manufacturer) + strlcat(usbtouch->name, " ", sizeof(usbtouch->name)); + strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name)); + } + + if (!strlen(usbtouch->name)) + snprintf(usbtouch->name, sizeof(usbtouch->name), + "USB Touchscreen %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys)); + strlcpy(usbtouch->phys, "/input0", sizeof(usbtouch->phys)); + + input_dev->name = usbtouch->name; + input_dev->phys = usbtouch->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, usbtouch); + + input_dev->open = usbtouch_open; + input_dev->close = usbtouch_close; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0); + input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0); + if (type->max_press) + input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press, + type->max_press, 0, 0); + + usb_fill_int_urb(usbtouch->irq, usbtouch->udev, + usb_rcvintpipe(usbtouch->udev, endpoint->bEndpointAddress), + usbtouch->data, type->rept_size, + usbtouch_irq, usbtouch, endpoint->bInterval); + + usbtouch->irq->dev = usbtouch->udev; + usbtouch->irq->transfer_dma = usbtouch->data_dma; + usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* device specific init */ + if (type->init) { + err = type->init(usbtouch); + if (err) { + dbg("%s - type->init() failed, err: %d", __FUNCTION__, err); + goto out_free_buffers; + } + } + + err = input_register_device(usbtouch->input); + if (err) { + dbg("%s - input_register_device failed, err: %d", __FUNCTION__, err); + goto out_free_buffers; + } + + usb_set_intfdata(intf, usbtouch); + + return 0; + +out_free_buffers: + usbtouch_free_buffers(udev, usbtouch); +out_free: + input_free_device(input_dev); + kfree(usbtouch); + return err; +} + +static void usbtouch_disconnect(struct usb_interface *intf) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + + dbg("%s - called", __FUNCTION__); + + if (!usbtouch) + return; + + dbg("%s - usbtouch is initialized, cleaning up", __FUNCTION__); + usb_set_intfdata(intf, NULL); + usb_kill_urb(usbtouch->irq); + input_unregister_device(usbtouch->input); + usb_free_urb(usbtouch->irq); + usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch); + kfree(usbtouch); +} + +MODULE_DEVICE_TABLE(usb, usbtouch_devices); + +static struct usb_driver usbtouch_driver = { + .name = "usbtouchscreen", + .probe = usbtouch_probe, + .disconnect = usbtouch_disconnect, + .id_table = usbtouch_devices, +}; + +static int __init usbtouch_init(void) +{ + return usb_register(&usbtouch_driver); +} + +static void __exit usbtouch_cleanup(void) +{ + usb_deregister(&usbtouch_driver); +} + +module_init(usbtouch_init); +module_exit(usbtouch_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("touchkitusb"); +MODULE_ALIAS("itmtouch"); +MODULE_ALIAS("mtouchusb"); diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index 0300dca8591..8238b13874c 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c @@ -48,7 +48,6 @@ #include <linux/init.h> #include <linux/input.h> #include <linux/major.h> -#include <linux/smp_lock.h> #include <linux/random.h> #include <linux/time.h> #include <linux/device.h> @@ -111,13 +110,13 @@ struct tsdev { int minor; char name[8]; wait_queue_head_t wait; - struct list_head list; + struct list_head client_list; struct input_handle handle; int x, y, pressure; struct ts_calibration cal; }; -struct tsdev_list { +struct tsdev_client { struct fasync_struct *fasync; struct list_head node; struct tsdev *tsdev; @@ -139,38 +138,49 @@ static struct tsdev *tsdev_table[TSDEV_MINORS/2]; static int tsdev_fasync(int fd, struct file *file, int on) { - struct tsdev_list *list = file->private_data; + struct tsdev_client *client = file->private_data; int retval; - retval = fasync_helper(fd, file, on, &list->fasync); + retval = fasync_helper(fd, file, on, &client->fasync); return retval < 0 ? retval : 0; } static int tsdev_open(struct inode *inode, struct file *file) { int i = iminor(inode) - TSDEV_MINOR_BASE; - struct tsdev_list *list; + struct tsdev_client *client; + struct tsdev *tsdev; + int error; printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled " "for removal.\nSee Documentation/feature-removal-schedule.txt " "for details.\n"); - if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK]) + if (i >= TSDEV_MINORS) + return -ENODEV; + + tsdev = tsdev_table[i & TSDEV_MINOR_MASK]; + if (!tsdev || !tsdev->exist) return -ENODEV; - if (!(list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL))) + client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL); + if (!client) return -ENOMEM; - list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0; + client->tsdev = tsdev; + client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0; + list_add_tail(&client->node, &tsdev->client_list); - i &= TSDEV_MINOR_MASK; - list->tsdev = tsdev_table[i]; - list_add_tail(&list->node, &tsdev_table[i]->list); - file->private_data = list; + if (!tsdev->open++ && tsdev->exist) { + error = input_open_device(&tsdev->handle); + if (error) { + list_del(&client->node); + kfree(client); + return error; + } + } - if (!list->tsdev->open++) - if (list->tsdev->exist) - input_open_device(&list->tsdev->handle); + file->private_data = client; return 0; } @@ -182,45 +192,48 @@ static void tsdev_free(struct tsdev *tsdev) static int tsdev_release(struct inode *inode, struct file *file) { - struct tsdev_list *list = file->private_data; + struct tsdev_client *client = file->private_data; + struct tsdev *tsdev = client->tsdev; tsdev_fasync(-1, file, 0); - list_del(&list->node); - if (!--list->tsdev->open) { - if (list->tsdev->exist) - input_close_device(&list->tsdev->handle); + list_del(&client->node); + kfree(client); + + if (!--tsdev->open) { + if (tsdev->exist) + input_close_device(&tsdev->handle); else - tsdev_free(list->tsdev); + tsdev_free(tsdev); } - kfree(list); + return 0; } static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, - loff_t * ppos) + loff_t *ppos) { - struct tsdev_list *list = file->private_data; + struct tsdev_client *client = file->private_data; + struct tsdev *tsdev = client->tsdev; int retval = 0; - if (list->head == list->tail && list->tsdev->exist && (file->f_flags & O_NONBLOCK)) + if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->tsdev->wait, - list->head != list->tail || !list->tsdev->exist); - + retval = wait_event_interruptible(tsdev->wait, + client->head != client->tail || !tsdev->exist); if (retval) return retval; - if (!list->tsdev->exist) + if (!tsdev->exist) return -ENODEV; - while (list->head != list->tail && + while (client->head != client->tail && retval + sizeof (struct ts_event) <= count) { - if (copy_to_user (buffer + retval, list->event + list->tail, + if (copy_to_user (buffer + retval, client->event + client->tail, sizeof (struct ts_event))) return -EFAULT; - list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1); + client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1); retval += sizeof (struct ts_event); } @@ -228,32 +241,33 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, } /* No kernel lock - fine */ -static unsigned int tsdev_poll(struct file *file, poll_table * wait) +static unsigned int tsdev_poll(struct file *file, poll_table *wait) { - struct tsdev_list *list = file->private_data; + struct tsdev_client *client = file->private_data; + struct tsdev *tsdev = client->tsdev; - poll_wait(file, &list->tsdev->wait, wait); - return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) | - (list->tsdev->exist ? 0 : (POLLHUP | POLLERR)); + poll_wait(file, &tsdev->wait, wait); + return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) | + (tsdev->exist ? 0 : (POLLHUP | POLLERR)); } static int tsdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct tsdev_list *list = file->private_data; - struct tsdev *tsdev = list->tsdev; + struct tsdev_client *client = file->private_data; + struct tsdev *tsdev = client->tsdev; int retval = 0; switch (cmd) { case TS_GET_CAL: - if (copy_to_user ((void __user *)arg, &tsdev->cal, - sizeof (struct ts_calibration))) + if (copy_to_user((void __user *)arg, &tsdev->cal, + sizeof (struct ts_calibration))) retval = -EFAULT; break; case TS_SET_CAL: - if (copy_from_user (&tsdev->cal, (void __user *)arg, - sizeof (struct ts_calibration))) + if (copy_from_user(&tsdev->cal, (void __user *)arg, + sizeof (struct ts_calibration))) retval = -EFAULT; break; @@ -279,7 +293,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct tsdev *tsdev = handle->private; - struct tsdev_list *list; + struct tsdev_client *client; struct timeval time; switch (type) { @@ -343,18 +357,18 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, if (type != EV_SYN || code != SYN_REPORT) return; - list_for_each_entry(list, &tsdev->list, node) { + list_for_each_entry(client, &tsdev->client_list, node) { int x, y, tmp; do_gettimeofday(&time); - list->event[list->head].millisecs = time.tv_usec / 100; - list->event[list->head].pressure = tsdev->pressure; + client->event[client->head].millisecs = time.tv_usec / 100; + client->event[client->head].pressure = tsdev->pressure; x = tsdev->x; y = tsdev->y; /* Calibration */ - if (!list->raw) { + if (!client->raw) { x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; if (tsdev->cal.xyswap) { @@ -362,33 +376,35 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, } } - list->event[list->head].x = x; - list->event[list->head].y = y; - list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1); - kill_fasync(&list->fasync, SIGIO, POLL_IN); + client->event[client->head].x = x; + client->event[client->head].y = y; + client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1); + kill_fasync(&client->fasync, SIGIO, POLL_IN); } wake_up_interruptible(&tsdev->wait); } -static struct input_handle *tsdev_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) +static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct tsdev *tsdev; struct class_device *cdev; + dev_t devt; int minor, delta; + int error; for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++); if (minor >= TSDEV_MINORS / 2) { printk(KERN_ERR "tsdev: You have way too many touchscreens\n"); - return NULL; + return -ENFILE; } - if (!(tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL))) - return NULL; + tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL); + if (!tsdev) + return -ENOMEM; - INIT_LIST_HEAD(&tsdev->list); + INIT_LIST_HEAD(&tsdev->client_list); init_waitqueue_head(&tsdev->wait); sprintf(tsdev->name, "ts%d", minor); @@ -415,23 +431,45 @@ static struct input_handle *tsdev_connect(struct input_handler *handler, tsdev_table[minor] = tsdev; - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), - dev->cdev.dev, tsdev->name); + devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), + + cdev = class_device_create(&input_class, &dev->cdev, devt, + dev->cdev.dev, tsdev->name); + if (IS_ERR(cdev)) { + error = PTR_ERR(cdev); + goto err_free_tsdev; + } /* temporary symlink to keep userspace happy */ - sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, - tsdev->name); + error = sysfs_create_link(&input_class.subsys.kobj, + &cdev->kobj, tsdev->name); + if (error) + goto err_cdev_destroy; + + error = input_register_handle(&tsdev->handle); + if (error) + goto err_remove_link; - return &tsdev->handle; + return 0; + + err_remove_link: + sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); + err_cdev_destroy: + class_device_destroy(&input_class, devt); + err_free_tsdev: + tsdev_table[minor] = NULL; + kfree(tsdev); + return error; } static void tsdev_disconnect(struct input_handle *handle) { struct tsdev *tsdev = handle->private; - struct tsdev_list *list; + struct tsdev_client *client; + + input_unregister_handle(handle); - sysfs_remove_link(&input_class.subsys.kset.kobj, tsdev->name); + sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + tsdev->minor)); tsdev->exist = 0; @@ -439,8 +477,8 @@ static void tsdev_disconnect(struct input_handle *handle) if (tsdev->open) { input_close_device(handle); wake_up_interruptible(&tsdev->wait); - list_for_each_entry(list, &tsdev->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_HUP); + list_for_each_entry(client, &tsdev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); } else tsdev_free(tsdev); } |