Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (32 commits) Input: wm97xx - update email address for Liam Girdwood Input: i8042 - add Thinkpad R31 to nomux list Input: move map_to_7segment.h to include/linux Input: ads7846 - fix cache line sharing issue Input: cm109 - add missing newlines to messages Input: document i8042.debug in kernel-parameters.txt Input: keyboard - fix potential out of bound access to key_map Input: psmouse - add OLPC touchpad driver Input: psmouse - tweak PSMOUSE_DEFINE_ATTR to support raw set callbacks Input: psmouse - add psmouse_queue_work() for ps/2 extension to make use of Input: psmouse - export psmouse_set_state for ps/2 extensions to use Input: ads7846 - introduce .gpio_pendown to get pendown state Input: ALPS - add signature for DualPoint found in Dell Latitude E6500 Input: serio_raw - allow attaching to translated (SERIO_I8042XL) ports Input: cm109 - don't use obsolete logging macros Input: atkbd - expand Latitude's force release quirk to other Dells Input: bf54x-keys - add power management support Input: atmel_tsadcc - improve accuracy Input: convert drivers to use strict_strtoul() Input: appletouch - handle geyser 3/4 status bits ...
This commit is contained in:
Коммит
36ac1d2f32
|
@ -796,6 +796,7 @@ and is between 256 and 4096 characters. It is defined in the file
|
|||
Defaults to the default architecture's huge page size
|
||||
if not specified.
|
||||
|
||||
i8042.debug [HW] Toggle i8042 debug mode
|
||||
i8042.direct [HW] Put keyboard port into non-translated mode
|
||||
i8042.dumbkbd [HW] Pretend that controller can only read data from
|
||||
keyboard and cannot control its state
|
||||
|
|
|
@ -4618,7 +4618,7 @@ WM97XX TOUCHSCREEN DRIVERS
|
|||
P: Mark Brown
|
||||
M: broonie@opensource.wolfsonmicro.com
|
||||
P: Liam Girdwood
|
||||
M: liam.girdwood@wolfsonmicro.com
|
||||
M: lrg@slimlogic.co.uk
|
||||
L: linux-input@vger.kernel.org
|
||||
T: git git://opensource.wolfsonmicro.com/linux-2.6-touch
|
||||
W: http://opensource.wolfsonmicro.com/node/7
|
||||
|
|
|
@ -1249,7 +1249,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
|
|||
return;
|
||||
}
|
||||
|
||||
if (keycode > NR_KEYS)
|
||||
if (keycode >= NR_KEYS)
|
||||
if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8)
|
||||
keysym = K(KT_BRL, keycode - KEY_BRL_DOT1 + 1);
|
||||
else
|
||||
|
|
|
@ -231,6 +231,7 @@ static void gameport_find_driver(struct gameport *gameport)
|
|||
enum gameport_event_type {
|
||||
GAMEPORT_REGISTER_PORT,
|
||||
GAMEPORT_REGISTER_DRIVER,
|
||||
GAMEPORT_ATTACH_DRIVER,
|
||||
};
|
||||
|
||||
struct gameport_event {
|
||||
|
@ -245,11 +246,12 @@ static LIST_HEAD(gameport_event_list);
|
|||
static DECLARE_WAIT_QUEUE_HEAD(gameport_wait);
|
||||
static struct task_struct *gameport_task;
|
||||
|
||||
static void gameport_queue_event(void *object, struct module *owner,
|
||||
enum gameport_event_type event_type)
|
||||
static int gameport_queue_event(void *object, struct module *owner,
|
||||
enum gameport_event_type event_type)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct gameport_event *event;
|
||||
int retval = 0;
|
||||
|
||||
spin_lock_irqsave(&gameport_event_lock, flags);
|
||||
|
||||
|
@ -268,24 +270,34 @@ static void gameport_queue_event(void *object, struct module *owner,
|
|||
}
|
||||
}
|
||||
|
||||
if ((event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC))) {
|
||||
if (!try_module_get(owner)) {
|
||||
printk(KERN_WARNING "gameport: Can't get module reference, dropping event %d\n", event_type);
|
||||
kfree(event);
|
||||
goto out;
|
||||
}
|
||||
|
||||
event->type = event_type;
|
||||
event->object = object;
|
||||
event->owner = owner;
|
||||
|
||||
list_add_tail(&event->node, &gameport_event_list);
|
||||
wake_up(&gameport_wait);
|
||||
} else {
|
||||
printk(KERN_ERR "gameport: Not enough memory to queue event %d\n", event_type);
|
||||
event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
|
||||
if (!event) {
|
||||
printk(KERN_ERR
|
||||
"gameport: Not enough memory to queue event %d\n",
|
||||
event_type);
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!try_module_get(owner)) {
|
||||
printk(KERN_WARNING
|
||||
"gameport: Can't get module reference, dropping event %d\n",
|
||||
event_type);
|
||||
kfree(event);
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
event->type = event_type;
|
||||
event->object = object;
|
||||
event->owner = owner;
|
||||
|
||||
list_add_tail(&event->node, &gameport_event_list);
|
||||
wake_up(&gameport_wait);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&gameport_event_lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void gameport_free_event(struct gameport_event *event)
|
||||
|
@ -378,9 +390,10 @@ static void gameport_handle_event(void)
|
|||
}
|
||||
|
||||
/*
|
||||
* Remove all events that have been submitted for a given gameport port.
|
||||
* Remove all events that have been submitted for a given object,
|
||||
* be it a gameport port or a driver.
|
||||
*/
|
||||
static void gameport_remove_pending_events(struct gameport *gameport)
|
||||
static void gameport_remove_pending_events(void *object)
|
||||
{
|
||||
struct list_head *node, *next;
|
||||
struct gameport_event *event;
|
||||
|
@ -390,7 +403,7 @@ static void gameport_remove_pending_events(struct gameport *gameport)
|
|||
|
||||
list_for_each_safe(node, next, &gameport_event_list) {
|
||||
event = list_entry(node, struct gameport_event, node);
|
||||
if (event->object == gameport) {
|
||||
if (event->object == object) {
|
||||
list_del_init(node);
|
||||
gameport_free_event(event);
|
||||
}
|
||||
|
@ -705,10 +718,40 @@ static void gameport_add_driver(struct gameport_driver *drv)
|
|||
drv->driver.name, error);
|
||||
}
|
||||
|
||||
void __gameport_register_driver(struct gameport_driver *drv, struct module *owner)
|
||||
int __gameport_register_driver(struct gameport_driver *drv, struct module *owner,
|
||||
const char *mod_name)
|
||||
{
|
||||
int error;
|
||||
|
||||
drv->driver.bus = &gameport_bus;
|
||||
gameport_queue_event(drv, owner, GAMEPORT_REGISTER_DRIVER);
|
||||
drv->driver.owner = owner;
|
||||
drv->driver.mod_name = mod_name;
|
||||
|
||||
/*
|
||||
* Temporarily disable automatic binding because probing
|
||||
* takes long time and we are better off doing it in kgameportd
|
||||
*/
|
||||
drv->ignore = 1;
|
||||
|
||||
error = driver_register(&drv->driver);
|
||||
if (error) {
|
||||
printk(KERN_ERR
|
||||
"gameport: driver_register() failed for %s, error: %d\n",
|
||||
drv->driver.name, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset ignore flag and let kgameportd bind the driver to free ports
|
||||
*/
|
||||
drv->ignore = 0;
|
||||
error = gameport_queue_event(drv, NULL, GAMEPORT_ATTACH_DRIVER);
|
||||
if (error) {
|
||||
driver_unregister(&drv->driver);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gameport_unregister_driver(struct gameport_driver *drv)
|
||||
|
@ -716,7 +759,9 @@ void gameport_unregister_driver(struct gameport_driver *drv)
|
|||
struct gameport *gameport;
|
||||
|
||||
mutex_lock(&gameport_mutex);
|
||||
|
||||
drv->ignore = 1; /* so gameport_find_driver ignores it */
|
||||
gameport_remove_pending_events(drv);
|
||||
|
||||
start_over:
|
||||
list_for_each_entry(gameport, &gameport_list, node) {
|
||||
|
@ -729,6 +774,7 @@ start_over:
|
|||
}
|
||||
|
||||
driver_unregister(&drv->driver);
|
||||
|
||||
mutex_unlock(&gameport_mutex);
|
||||
}
|
||||
|
||||
|
|
|
@ -414,8 +414,7 @@ static struct gameport_driver a3d_drv = {
|
|||
|
||||
static int __init a3d_init(void)
|
||||
{
|
||||
gameport_register_driver(&a3d_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&a3d_drv);
|
||||
}
|
||||
|
||||
static void __exit a3d_exit(void)
|
||||
|
|
|
@ -572,8 +572,7 @@ static struct gameport_driver adi_drv = {
|
|||
|
||||
static int __init adi_init(void)
|
||||
{
|
||||
gameport_register_driver(&adi_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&adi_drv);
|
||||
}
|
||||
|
||||
static void __exit adi_exit(void)
|
||||
|
|
|
@ -761,9 +761,7 @@ static struct gameport_driver analog_drv = {
|
|||
static int __init analog_init(void)
|
||||
{
|
||||
analog_parse_options();
|
||||
gameport_register_driver(&analog_drv);
|
||||
|
||||
return 0;
|
||||
return gameport_register_driver(&analog_drv);
|
||||
}
|
||||
|
||||
static void __exit analog_exit(void)
|
||||
|
|
|
@ -263,8 +263,7 @@ static struct gameport_driver cobra_drv = {
|
|||
|
||||
static int __init cobra_init(void)
|
||||
{
|
||||
gameport_register_driver(&cobra_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&cobra_drv);
|
||||
}
|
||||
|
||||
static void __exit cobra_exit(void)
|
||||
|
|
|
@ -375,8 +375,7 @@ static struct gameport_driver gf2k_drv = {
|
|||
|
||||
static int __init gf2k_init(void)
|
||||
{
|
||||
gameport_register_driver(&gf2k_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&gf2k_drv);
|
||||
}
|
||||
|
||||
static void __exit gf2k_exit(void)
|
||||
|
|
|
@ -426,8 +426,7 @@ static struct gameport_driver grip_drv = {
|
|||
|
||||
static int __init grip_init(void)
|
||||
{
|
||||
gameport_register_driver(&grip_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&grip_drv);
|
||||
}
|
||||
|
||||
static void __exit grip_exit(void)
|
||||
|
|
|
@ -689,8 +689,7 @@ static struct gameport_driver grip_drv = {
|
|||
|
||||
static int __init grip_init(void)
|
||||
{
|
||||
gameport_register_driver(&grip_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&grip_drv);
|
||||
}
|
||||
|
||||
static void __exit grip_exit(void)
|
||||
|
|
|
@ -283,8 +283,7 @@ static struct gameport_driver guillemot_drv = {
|
|||
|
||||
static int __init guillemot_init(void)
|
||||
{
|
||||
gameport_register_driver(&guillemot_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&guillemot_drv);
|
||||
}
|
||||
|
||||
static void __exit guillemot_exit(void)
|
||||
|
|
|
@ -317,8 +317,7 @@ static struct gameport_driver interact_drv = {
|
|||
|
||||
static int __init interact_init(void)
|
||||
{
|
||||
gameport_register_driver(&interact_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&interact_drv);
|
||||
}
|
||||
|
||||
static void __exit interact_exit(void)
|
||||
|
|
|
@ -161,8 +161,7 @@ static struct gameport_driver joydump_drv = {
|
|||
|
||||
static int __init joydump_init(void)
|
||||
{
|
||||
gameport_register_driver(&joydump_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&joydump_drv);
|
||||
}
|
||||
|
||||
static void __exit joydump_exit(void)
|
||||
|
|
|
@ -818,8 +818,7 @@ static struct gameport_driver sw_drv = {
|
|||
|
||||
static int __init sw_init(void)
|
||||
{
|
||||
gameport_register_driver(&sw_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&sw_drv);
|
||||
}
|
||||
|
||||
static void __exit sw_exit(void)
|
||||
|
|
|
@ -438,8 +438,7 @@ static struct gameport_driver tmdc_drv = {
|
|||
|
||||
static int __init tmdc_init(void)
|
||||
{
|
||||
gameport_register_driver(&tmdc_drv);
|
||||
return 0;
|
||||
return gameport_register_driver(&tmdc_drv);
|
||||
}
|
||||
|
||||
static void __exit tmdc_exit(void)
|
||||
|
|
|
@ -834,10 +834,10 @@ static void atkbd_disconnect(struct serio *serio)
|
|||
}
|
||||
|
||||
/*
|
||||
* Most special keys (Fn+F?) on Dell Latitudes do not generate release
|
||||
* Most special keys (Fn+F?) on Dell laptops do not generate release
|
||||
* events so we have to do it ourselves.
|
||||
*/
|
||||
static void atkbd_latitude_keymap_fixup(struct atkbd *atkbd)
|
||||
static void atkbd_dell_laptop_keymap_fixup(struct atkbd *atkbd)
|
||||
{
|
||||
const unsigned int forced_release_keys[] = {
|
||||
0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93,
|
||||
|
@ -1207,15 +1207,13 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun
|
|||
{
|
||||
struct input_dev *old_dev, *new_dev;
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
int err;
|
||||
unsigned char old_extra, old_set;
|
||||
|
||||
if (!atkbd->write)
|
||||
return -EIO;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || value > 1)
|
||||
if (strict_strtoul(buf, 10, &value) || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (atkbd->extra != value) {
|
||||
|
@ -1264,12 +1262,10 @@ static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t cou
|
|||
{
|
||||
struct input_dev *old_dev, *new_dev;
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
int err;
|
||||
unsigned char old_scroll;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || value > 1)
|
||||
if (strict_strtoul(buf, 10, &value) || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (atkbd->scroll != value) {
|
||||
|
@ -1310,15 +1306,13 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
|
|||
{
|
||||
struct input_dev *old_dev, *new_dev;
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
int err;
|
||||
unsigned char old_set, old_extra;
|
||||
|
||||
if (!atkbd->write)
|
||||
return -EIO;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || (value != 2 && value != 3))
|
||||
if (strict_strtoul(buf, 10, &value) || (value != 2 && value != 3))
|
||||
return -EINVAL;
|
||||
|
||||
if (atkbd->set != value) {
|
||||
|
@ -1361,15 +1355,13 @@ static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t
|
|||
{
|
||||
struct input_dev *old_dev, *new_dev;
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
int err;
|
||||
unsigned char old_softrepeat, old_softraw;
|
||||
|
||||
if (!atkbd->write)
|
||||
return -EIO;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || value > 1)
|
||||
if (strict_strtoul(buf, 10, &value) || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (atkbd->softrepeat != value) {
|
||||
|
@ -1413,12 +1405,10 @@ static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t co
|
|||
{
|
||||
struct input_dev *old_dev, *new_dev;
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
int err;
|
||||
unsigned char old_softraw;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || value > 1)
|
||||
if (strict_strtoul(buf, 10, &value) || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (atkbd->softraw != value) {
|
||||
|
@ -1461,13 +1451,13 @@ static int __init atkbd_setup_fixup(const struct dmi_system_id *id)
|
|||
|
||||
static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
||||
{
|
||||
.ident = "Dell Latitude series",
|
||||
.ident = "Dell Laptop",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
|
||||
},
|
||||
.callback = atkbd_setup_fixup,
|
||||
.driver_data = atkbd_latitude_keymap_fixup,
|
||||
.driver_data = atkbd_dell_laptop_keymap_fixup,
|
||||
},
|
||||
{
|
||||
.ident = "HP 2133",
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* Modified:
|
||||
* Copyright 2007 Analog Devices Inc.
|
||||
* Copyright 2007-2008 Analog Devices Inc.
|
||||
*
|
||||
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
|
@ -81,6 +81,9 @@ struct bf54x_kpad {
|
|||
unsigned short *keycode;
|
||||
struct timer_list timer;
|
||||
unsigned int keyup_test_jiffies;
|
||||
unsigned short kpad_msel;
|
||||
unsigned short kpad_prescale;
|
||||
unsigned short kpad_ctl;
|
||||
};
|
||||
|
||||
static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
|
||||
|
@ -360,6 +363,10 @@ static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
|
|||
{
|
||||
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||
|
||||
bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();
|
||||
bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();
|
||||
bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
enable_irq_wake(bf54x_kpad->irq);
|
||||
|
||||
|
@ -370,6 +377,10 @@ static int bfin_kpad_resume(struct platform_device *pdev)
|
|||
{
|
||||
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
|
||||
|
||||
bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);
|
||||
bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);
|
||||
bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
disable_irq_wake(bf54x_kpad->irq);
|
||||
|
||||
|
|
|
@ -36,9 +36,10 @@ struct gpio_keys_drvdata {
|
|||
struct gpio_button_data data[0];
|
||||
};
|
||||
|
||||
static void gpio_keys_report_event(struct gpio_keys_button *button,
|
||||
struct input_dev *input)
|
||||
static void gpio_keys_report_event(struct gpio_button_data *bdata)
|
||||
{
|
||||
struct gpio_keys_button *button = bdata->button;
|
||||
struct input_dev *input = bdata->input;
|
||||
unsigned int type = button->type ?: EV_KEY;
|
||||
int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
|
||||
|
||||
|
@ -50,34 +51,23 @@ static void gpio_check_button(unsigned long _data)
|
|||
{
|
||||
struct gpio_button_data *data = (struct gpio_button_data *)_data;
|
||||
|
||||
gpio_keys_report_event(data->button, data->input);
|
||||
gpio_keys_report_event(data);
|
||||
}
|
||||
|
||||
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = dev_id;
|
||||
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
struct gpio_button_data *bdata = dev_id;
|
||||
struct gpio_keys_button *button = bdata->button;
|
||||
|
||||
for (i = 0; i < pdata->nbuttons; i++) {
|
||||
struct gpio_keys_button *button = &pdata->buttons[i];
|
||||
BUG_ON(irq != gpio_to_irq(button->gpio));
|
||||
|
||||
if (irq == gpio_to_irq(button->gpio)) {
|
||||
struct gpio_button_data *bdata = &ddata->data[i];
|
||||
if (button->debounce_interval)
|
||||
mod_timer(&bdata->timer,
|
||||
jiffies + msecs_to_jiffies(button->debounce_interval));
|
||||
else
|
||||
gpio_keys_report_event(bdata);
|
||||
|
||||
if (button->debounce_interval)
|
||||
mod_timer(&bdata->timer,
|
||||
jiffies +
|
||||
msecs_to_jiffies(button->debounce_interval));
|
||||
else
|
||||
gpio_keys_report_event(button, bdata->input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
||||
|
@ -151,7 +141,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
|||
IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING,
|
||||
button->desc ? button->desc : "gpio_keys",
|
||||
pdev);
|
||||
bdata);
|
||||
if (error) {
|
||||
pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
|
||||
irq, error);
|
||||
|
@ -178,7 +168,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
|||
|
||||
fail2:
|
||||
while (--i >= 0) {
|
||||
free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
|
||||
free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
|
||||
if (pdata->buttons[i].debounce_interval)
|
||||
del_timer_sync(&ddata->data[i].timer);
|
||||
gpio_free(pdata->buttons[i].gpio);
|
||||
|
@ -203,7 +193,7 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
|
|||
|
||||
for (i = 0; i < pdata->nbuttons; i++) {
|
||||
int irq = gpio_to_irq(pdata->buttons[i].gpio);
|
||||
free_irq(irq, pdev);
|
||||
free_irq(irq, &ddata->data[i]);
|
||||
if (pdata->buttons[i].debounce_interval)
|
||||
del_timer_sync(&ddata->data[i].timer);
|
||||
gpio_free(pdata->buttons[i].gpio);
|
||||
|
|
|
@ -180,6 +180,19 @@ config INPUT_YEALINK
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called yealink.
|
||||
|
||||
config INPUT_CM109
|
||||
tristate "C-Media CM109 USB I/O Controller"
|
||||
depends on EXPERIMENTAL
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select USB
|
||||
help
|
||||
Say Y here if you want to enable keyboard and buzzer functions of the
|
||||
C-Media CM109 usb phones. The audio part is enabled by the generic
|
||||
usb sound driver, so you might want to enable that as well.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called cm109.
|
||||
|
||||
config INPUT_UINPUT
|
||||
tristate "User level driver support"
|
||||
help
|
||||
|
|
|
@ -16,6 +16,7 @@ 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_INPUT_CM109) += cm109.o
|
||||
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
|
||||
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
|
||||
obj-$(CONFIG_INPUT_APANEL) += apanel.o
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* 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>
|
||||
* Copyright (C) 2005-2008 Ville Syrjala <syrjala@sci.fi>
|
||||
* Copyright (C) 2007-2008 Peter Stokes <linux@dadeos.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
|
||||
|
@ -12,7 +12,7 @@
|
|||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_DESC "ATI/Philips USB RF remote driver"
|
||||
#define DRIVER_VERSION "0.2"
|
||||
#define DRIVER_VERSION "0.3"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
|
@ -27,7 +27,7 @@ MODULE_LICENSE("GPL");
|
|||
* 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
|
||||
* 16 and then press the "PC" button again, the button will slowly flash the count of the
|
||||
* newly configured "channel".
|
||||
*/
|
||||
|
||||
|
@ -45,9 +45,25 @@ static struct usb_device_id ati_remote2_id_table[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
|
||||
|
||||
static struct {
|
||||
int hw_code;
|
||||
int key_code;
|
||||
static DEFINE_MUTEX(ati_remote2_mutex);
|
||||
|
||||
enum {
|
||||
ATI_REMOTE2_OPENED = 0x1,
|
||||
ATI_REMOTE2_SUSPENDED = 0x2,
|
||||
};
|
||||
|
||||
enum {
|
||||
ATI_REMOTE2_AUX1,
|
||||
ATI_REMOTE2_AUX2,
|
||||
ATI_REMOTE2_AUX3,
|
||||
ATI_REMOTE2_AUX4,
|
||||
ATI_REMOTE2_PC,
|
||||
ATI_REMOTE2_MODES,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
u8 hw_code;
|
||||
u16 keycode;
|
||||
} ati_remote2_key_table[] = {
|
||||
{ 0x00, KEY_0 },
|
||||
{ 0x01, KEY_1 },
|
||||
|
@ -73,6 +89,7 @@ static struct {
|
|||
{ 0x37, KEY_RECORD },
|
||||
{ 0x38, KEY_DVD },
|
||||
{ 0x39, KEY_TV },
|
||||
{ 0x3f, KEY_PROG1 }, /* AUX1-AUX4 and PC */
|
||||
{ 0x54, KEY_MENU },
|
||||
{ 0x58, KEY_UP },
|
||||
{ 0x59, KEY_DOWN },
|
||||
|
@ -91,15 +108,9 @@ static struct {
|
|||
{ 0xa9, BTN_LEFT },
|
||||
{ 0xaa, BTN_RIGHT },
|
||||
{ 0xbe, KEY_QUESTION },
|
||||
{ 0xd5, KEY_FRONT },
|
||||
{ 0xd0, KEY_EDIT },
|
||||
{ 0xd5, KEY_FRONT },
|
||||
{ 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 {
|
||||
|
@ -117,46 +128,106 @@ struct ati_remote2 {
|
|||
|
||||
char name[64];
|
||||
char phys[64];
|
||||
|
||||
/* Each mode (AUX1-AUX4 and PC) can have an independent keymap. */
|
||||
u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)];
|
||||
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
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 int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message);
|
||||
static int ati_remote2_resume(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,
|
||||
.suspend = ati_remote2_suspend,
|
||||
.resume = ati_remote2_resume,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
||||
static int ati_remote2_open(struct input_dev *idev)
|
||||
static int ati_remote2_submit_urbs(struct ati_remote2 *ar2)
|
||||
{
|
||||
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", __func__, r);
|
||||
"%s(): usb_submit_urb() = %d\n", __func__, 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", __func__, r);
|
||||
"%s(): usb_submit_urb() = %d\n", __func__, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ati_remote2_kill_urbs(struct ati_remote2 *ar2)
|
||||
{
|
||||
usb_kill_urb(ar2->urb[1]);
|
||||
usb_kill_urb(ar2->urb[0]);
|
||||
}
|
||||
|
||||
static int ati_remote2_open(struct input_dev *idev)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
int r;
|
||||
|
||||
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
|
||||
|
||||
r = usb_autopm_get_interface(ar2->intf[0]);
|
||||
if (r) {
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
mutex_lock(&ati_remote2_mutex);
|
||||
|
||||
if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) {
|
||||
r = ati_remote2_submit_urbs(ar2);
|
||||
if (r)
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
ar2->flags |= ATI_REMOTE2_OPENED;
|
||||
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
|
||||
usb_autopm_put_interface(ar2->intf[0]);
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
usb_autopm_put_interface(ar2->intf[0]);
|
||||
fail1:
|
||||
return r;
|
||||
}
|
||||
|
||||
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]);
|
||||
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
|
||||
|
||||
mutex_lock(&ati_remote2_mutex);
|
||||
|
||||
if (!(ar2->flags & ATI_REMOTE2_SUSPENDED))
|
||||
ati_remote2_kill_urbs(ar2);
|
||||
|
||||
ar2->flags &= ~ATI_REMOTE2_OPENED;
|
||||
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
}
|
||||
|
||||
static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
|
||||
|
@ -172,7 +243,7 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
|
|||
|
||||
mode = data[0] & 0x0F;
|
||||
|
||||
if (mode > 4) {
|
||||
if (mode > ATI_REMOTE2_PC) {
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"Unknown mode byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
|
@ -191,7 +262,7 @@ static int ati_remote2_lookup(unsigned int hw_code)
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
|
||||
for (i = 0; i < ARRAY_SIZE(ati_remote2_key_table); i++)
|
||||
if (ati_remote2_key_table[i].hw_code == hw_code)
|
||||
return i;
|
||||
|
||||
|
@ -211,7 +282,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
|||
|
||||
mode = data[0] & 0x0F;
|
||||
|
||||
if (mode > 4) {
|
||||
if (mode > ATI_REMOTE2_PC) {
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"Unknown mode byte (%02x %02x %02x %02x)\n",
|
||||
data[3], data[2], data[1], data[0]);
|
||||
|
@ -219,10 +290,6 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -236,8 +303,6 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
|||
|
||||
if (data[1] == 0)
|
||||
ar2->mode = mode;
|
||||
|
||||
hw_code |= mode << 8;
|
||||
}
|
||||
|
||||
if (!((1 << mode) & mode_mask))
|
||||
|
@ -260,8 +325,8 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
|||
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)
|
||||
if (ar2->keycode[mode][index] == BTN_LEFT ||
|
||||
ar2->keycode[mode][index] == BTN_RIGHT)
|
||||
return;
|
||||
|
||||
if (!time_after_eq(jiffies, ar2->jiffies))
|
||||
|
@ -276,7 +341,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
|||
return;
|
||||
}
|
||||
|
||||
input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]);
|
||||
input_event(idev, EV_KEY, ar2->keycode[mode][index], data[1]);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
|
@ -287,6 +352,7 @@ static void ati_remote2_complete_mouse(struct urb *urb)
|
|||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
usb_mark_last_busy(ar2->udev);
|
||||
ati_remote2_input_mouse(ar2);
|
||||
break;
|
||||
case -ENOENT:
|
||||
|
@ -297,6 +363,7 @@ static void ati_remote2_complete_mouse(struct urb *urb)
|
|||
"%s(): urb status = %d\n", __func__, urb->status);
|
||||
return;
|
||||
default:
|
||||
usb_mark_last_busy(ar2->udev);
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s(): urb status = %d\n", __func__, urb->status);
|
||||
}
|
||||
|
@ -314,6 +381,7 @@ static void ati_remote2_complete_key(struct urb *urb)
|
|||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
usb_mark_last_busy(ar2->udev);
|
||||
ati_remote2_input_key(ar2);
|
||||
break;
|
||||
case -ENOENT:
|
||||
|
@ -324,6 +392,7 @@ static void ati_remote2_complete_key(struct urb *urb)
|
|||
"%s(): urb status = %d\n", __func__, urb->status);
|
||||
return;
|
||||
default:
|
||||
usb_mark_last_busy(ar2->udev);
|
||||
dev_err(&ar2->intf[1]->dev,
|
||||
"%s(): urb status = %d\n", __func__, urb->status);
|
||||
}
|
||||
|
@ -334,10 +403,60 @@ static void ati_remote2_complete_key(struct urb *urb)
|
|||
"%s(): usb_submit_urb() = %d\n", __func__, r);
|
||||
}
|
||||
|
||||
static int ati_remote2_getkeycode(struct input_dev *idev,
|
||||
int scancode, int *keycode)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
int index, mode;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask))
|
||||
return -EINVAL;
|
||||
|
||||
index = ati_remote2_lookup(scancode & 0xFF);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*keycode = ar2->keycode[mode][index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keycode)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
int index, mode, old_keycode;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask))
|
||||
return -EINVAL;
|
||||
|
||||
index = ati_remote2_lookup(scancode & 0xFF);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (keycode < KEY_RESERVED || keycode > KEY_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
old_keycode = ar2->keycode[mode][index];
|
||||
ar2->keycode[mode][index] = keycode;
|
||||
set_bit(keycode, idev->keybit);
|
||||
|
||||
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
|
||||
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
|
||||
if (ar2->keycode[mode][index] == old_keycode)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
clear_bit(old_keycode, idev->keybit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ati_remote2_input_init(struct ati_remote2 *ar2)
|
||||
{
|
||||
struct input_dev *idev;
|
||||
int i, retval;
|
||||
int index, mode, retval;
|
||||
|
||||
idev = input_allocate_device();
|
||||
if (!idev)
|
||||
|
@ -350,8 +469,26 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
|
|||
idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
|
||||
BIT_MASK(BTN_RIGHT);
|
||||
idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(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);
|
||||
|
||||
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
|
||||
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
|
||||
ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode;
|
||||
set_bit(ar2->keycode[mode][index], idev->keybit);
|
||||
}
|
||||
}
|
||||
|
||||
/* AUX1-AUX4 and PC generate the same scancode. */
|
||||
index = ati_remote2_lookup(0x3f);
|
||||
ar2->keycode[ATI_REMOTE2_AUX1][index] = KEY_PROG1;
|
||||
ar2->keycode[ATI_REMOTE2_AUX2][index] = KEY_PROG2;
|
||||
ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3;
|
||||
ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4;
|
||||
ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC;
|
||||
set_bit(KEY_PROG1, idev->keybit);
|
||||
set_bit(KEY_PROG2, idev->keybit);
|
||||
set_bit(KEY_PROG3, idev->keybit);
|
||||
set_bit(KEY_PROG4, idev->keybit);
|
||||
set_bit(KEY_PC, idev->keybit);
|
||||
|
||||
idev->rep[REP_DELAY] = 250;
|
||||
idev->rep[REP_PERIOD] = 33;
|
||||
|
@ -359,6 +496,9 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
|
|||
idev->open = ati_remote2_open;
|
||||
idev->close = ati_remote2_close;
|
||||
|
||||
idev->getkeycode = ati_remote2_getkeycode;
|
||||
idev->setkeycode = ati_remote2_setkeycode;
|
||||
|
||||
idev->name = ar2->name;
|
||||
idev->phys = ar2->phys;
|
||||
|
||||
|
@ -490,6 +630,8 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
|
|||
|
||||
usb_set_intfdata(interface, ar2);
|
||||
|
||||
interface->needs_remote_wakeup = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
|
@ -522,6 +664,57 @@ static void ati_remote2_disconnect(struct usb_interface *interface)
|
|||
kfree(ar2);
|
||||
}
|
||||
|
||||
static int ati_remote2_suspend(struct usb_interface *interface,
|
||||
pm_message_t message)
|
||||
{
|
||||
struct ati_remote2 *ar2;
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return 0;
|
||||
|
||||
ar2 = usb_get_intfdata(interface);
|
||||
|
||||
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
|
||||
|
||||
mutex_lock(&ati_remote2_mutex);
|
||||
|
||||
if (ar2->flags & ATI_REMOTE2_OPENED)
|
||||
ati_remote2_kill_urbs(ar2);
|
||||
|
||||
ar2->flags |= ATI_REMOTE2_SUSPENDED;
|
||||
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ati_remote2_resume(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote2 *ar2;
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
int r = 0;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return 0;
|
||||
|
||||
ar2 = usb_get_intfdata(interface);
|
||||
|
||||
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
|
||||
|
||||
mutex_lock(&ati_remote2_mutex);
|
||||
|
||||
if (ar2->flags & ATI_REMOTE2_OPENED)
|
||||
r = ati_remote2_submit_urbs(ar2);
|
||||
|
||||
if (!r)
|
||||
ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
|
||||
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __init ati_remote2_init(void)
|
||||
{
|
||||
int r;
|
||||
|
|
|
@ -0,0 +1,882 @@
|
|||
/*
|
||||
* Driver for the VoIP USB phones with CM109 chipsets.
|
||||
*
|
||||
* Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tested devices:
|
||||
* - Komunikate KIP1000
|
||||
* - Genius G-talk
|
||||
* - Allied-Telesis Corega USBPH01
|
||||
* - ...
|
||||
*
|
||||
* This driver is based on the yealink.c driver
|
||||
*
|
||||
* Thanks to:
|
||||
* - Authors of yealink.c
|
||||
* - Thomas Reitmayr
|
||||
* - Oliver Neukum for good review comments and code
|
||||
* - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
|
||||
* - Dmitry Torokhov for valuable input and review
|
||||
*
|
||||
* Todo:
|
||||
* - Read/write EEPROM
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_VERSION "20080805"
|
||||
#define DRIVER_AUTHOR "Alfred E. Heggestad"
|
||||
#define DRIVER_DESC "CM109 phone driver"
|
||||
|
||||
static char *phone = "kip1000";
|
||||
module_param(phone, charp, S_IRUSR);
|
||||
MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01}");
|
||||
|
||||
enum {
|
||||
/* HID Registers */
|
||||
HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */
|
||||
HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */
|
||||
HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */
|
||||
HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */
|
||||
HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
|
||||
HID_OR1 = 0x01, /* GPO - General Purpose Output */
|
||||
HID_OR2 = 0x02, /* Set GPIO to input/output mode */
|
||||
HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */
|
||||
|
||||
/* HID_IR0 */
|
||||
RECORD_MUTE = 1 << 3,
|
||||
PLAYBACK_MUTE = 1 << 2,
|
||||
VOLUME_DOWN = 1 << 1,
|
||||
VOLUME_UP = 1 << 0,
|
||||
|
||||
/* HID_OR0 */
|
||||
/* bits 7-6
|
||||
0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
|
||||
and SPDIF
|
||||
1: HID_OR0-3 are used as generic HID registers
|
||||
2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
|
||||
EEPROM_DATA0-1, EEPROM_CTRL (see Note)
|
||||
3: Reserved
|
||||
*/
|
||||
HID_OR_GPO_BUZ_SPDIF = 0 << 6,
|
||||
HID_OR_GENERIC_HID_REG = 1 << 6,
|
||||
HID_OR_MAP_MCU_EEPROM = 2 << 6,
|
||||
|
||||
BUZZER_ON = 1 << 5,
|
||||
|
||||
/* up to 256 normal keys, up to 16 special keys */
|
||||
KEYMAP_SIZE = 256 + 16,
|
||||
};
|
||||
|
||||
/* CM109 protocol packet */
|
||||
struct cm109_ctl_packet {
|
||||
u8 byte[4];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
|
||||
|
||||
/* CM109 device structure */
|
||||
struct cm109_dev {
|
||||
struct input_dev *idev; /* input device */
|
||||
struct usb_device *udev; /* usb device */
|
||||
struct usb_interface *intf;
|
||||
|
||||
/* irq input channel */
|
||||
struct cm109_ctl_packet *irq_data;
|
||||
dma_addr_t irq_dma;
|
||||
struct urb *urb_irq;
|
||||
|
||||
/* control output channel */
|
||||
struct cm109_ctl_packet *ctl_data;
|
||||
dma_addr_t ctl_dma;
|
||||
struct usb_ctrlrequest *ctl_req;
|
||||
dma_addr_t ctl_req_dma;
|
||||
struct urb *urb_ctl;
|
||||
/*
|
||||
* The 3 bitfields below are protected by ctl_submit_lock.
|
||||
* They have to be separate since they are accessed from IRQ
|
||||
* context.
|
||||
*/
|
||||
unsigned irq_urb_pending:1; /* irq_urb is in flight */
|
||||
unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
|
||||
unsigned buzzer_pending:1; /* need to issue buzz command */
|
||||
spinlock_t ctl_submit_lock;
|
||||
|
||||
unsigned char buzzer_state; /* on/off */
|
||||
|
||||
/* flags */
|
||||
unsigned open:1;
|
||||
unsigned resetting:1;
|
||||
unsigned shutdown:1;
|
||||
|
||||
/* This mutex protects writes to the above flags */
|
||||
struct mutex pm_mutex;
|
||||
|
||||
unsigned short keymap[KEYMAP_SIZE];
|
||||
|
||||
char phys[64]; /* physical device path */
|
||||
int key_code; /* last reported key */
|
||||
int keybit; /* 0=new scan 1,2,4,8=scan columns */
|
||||
u8 gpi; /* Cached value of GPI (high nibble) */
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* CM109 key interface
|
||||
*****************************************************************************/
|
||||
|
||||
static unsigned short special_keymap(int code)
|
||||
{
|
||||
if (code > 0xff) {
|
||||
switch (code - 0xff) {
|
||||
case RECORD_MUTE: return KEY_MUTE;
|
||||
case PLAYBACK_MUTE: return KEY_MUTE;
|
||||
case VOLUME_DOWN: return KEY_VOLUMEDOWN;
|
||||
case VOLUME_UP: return KEY_VOLUMEUP;
|
||||
}
|
||||
}
|
||||
return KEY_RESERVED;
|
||||
}
|
||||
|
||||
/* Map device buttons to internal key events.
|
||||
*
|
||||
* 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.
|
||||
|
||||
Komunikate KIP1000 Keyboard Matrix
|
||||
|
||||
-> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
|
||||
| | | |
|
||||
<- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
|
||||
| | | |
|
||||
END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
|
||||
| | | |
|
||||
OK -- * -- 0 -- # --> GPI pin 7 (0x80)
|
||||
| | | |
|
||||
|
||||
/|\ /|\ /|\ /|\
|
||||
| | | |
|
||||
GPO
|
||||
pin: 3 2 1 0
|
||||
0x8 0x4 0x2 0x1
|
||||
|
||||
*/
|
||||
static unsigned short keymap_kip1000(int scancode)
|
||||
{
|
||||
switch (scancode) { /* phone key: */
|
||||
case 0x82: return KEY_NUMERIC_0; /* 0 */
|
||||
case 0x14: return KEY_NUMERIC_1; /* 1 */
|
||||
case 0x12: return KEY_NUMERIC_2; /* 2 */
|
||||
case 0x11: return KEY_NUMERIC_3; /* 3 */
|
||||
case 0x24: return KEY_NUMERIC_4; /* 4 */
|
||||
case 0x22: return KEY_NUMERIC_5; /* 5 */
|
||||
case 0x21: return KEY_NUMERIC_6; /* 6 */
|
||||
case 0x44: return KEY_NUMERIC_7; /* 7 */
|
||||
case 0x42: return KEY_NUMERIC_8; /* 8 */
|
||||
case 0x41: return KEY_NUMERIC_9; /* 9 */
|
||||
case 0x81: return KEY_NUMERIC_POUND; /* # */
|
||||
case 0x84: return KEY_NUMERIC_STAR; /* * */
|
||||
case 0x88: return KEY_ENTER; /* pickup */
|
||||
case 0x48: return KEY_ESC; /* hangup */
|
||||
case 0x28: return KEY_LEFT; /* IN */
|
||||
case 0x18: return KEY_RIGHT; /* OUT */
|
||||
default: return special_keymap(scancode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Contributed by Shaun Jackman <sjackman@gmail.com>
|
||||
|
||||
Genius G-Talk keyboard matrix
|
||||
0 1 2 3
|
||||
4: 0 4 8 Talk
|
||||
5: 1 5 9 End
|
||||
6: 2 6 # Up
|
||||
7: 3 7 * Down
|
||||
*/
|
||||
static unsigned short keymap_gtalk(int scancode)
|
||||
{
|
||||
switch (scancode) {
|
||||
case 0x11: return KEY_NUMERIC_0;
|
||||
case 0x21: return KEY_NUMERIC_1;
|
||||
case 0x41: return KEY_NUMERIC_2;
|
||||
case 0x81: return KEY_NUMERIC_3;
|
||||
case 0x12: return KEY_NUMERIC_4;
|
||||
case 0x22: return KEY_NUMERIC_5;
|
||||
case 0x42: return KEY_NUMERIC_6;
|
||||
case 0x82: return KEY_NUMERIC_7;
|
||||
case 0x14: return KEY_NUMERIC_8;
|
||||
case 0x24: return KEY_NUMERIC_9;
|
||||
case 0x44: return KEY_NUMERIC_POUND; /* # */
|
||||
case 0x84: return KEY_NUMERIC_STAR; /* * */
|
||||
case 0x18: return KEY_ENTER; /* Talk (green handset) */
|
||||
case 0x28: return KEY_ESC; /* End (red handset) */
|
||||
case 0x48: return KEY_UP; /* Menu up (rocker switch) */
|
||||
case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
|
||||
default: return special_keymap(scancode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Keymap for Allied-Telesis Corega USBPH01
|
||||
* http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
|
||||
*
|
||||
* Contributed by july@nat.bg
|
||||
*/
|
||||
static unsigned short keymap_usbph01(int scancode)
|
||||
{
|
||||
switch (scancode) {
|
||||
case 0x11: return KEY_NUMERIC_0; /* 0 */
|
||||
case 0x21: return KEY_NUMERIC_1; /* 1 */
|
||||
case 0x41: return KEY_NUMERIC_2; /* 2 */
|
||||
case 0x81: return KEY_NUMERIC_3; /* 3 */
|
||||
case 0x12: return KEY_NUMERIC_4; /* 4 */
|
||||
case 0x22: return KEY_NUMERIC_5; /* 5 */
|
||||
case 0x42: return KEY_NUMERIC_6; /* 6 */
|
||||
case 0x82: return KEY_NUMERIC_7; /* 7 */
|
||||
case 0x14: return KEY_NUMERIC_8; /* 8 */
|
||||
case 0x24: return KEY_NUMERIC_9; /* 9 */
|
||||
case 0x44: return KEY_NUMERIC_POUND; /* # */
|
||||
case 0x84: return KEY_NUMERIC_STAR; /* * */
|
||||
case 0x18: return KEY_ENTER; /* pickup */
|
||||
case 0x28: return KEY_ESC; /* hangup */
|
||||
case 0x48: return KEY_LEFT; /* IN */
|
||||
case 0x88: return KEY_RIGHT; /* OUT */
|
||||
default: return special_keymap(scancode);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned short (*keymap)(int) = keymap_kip1000;
|
||||
|
||||
/*
|
||||
* Completes a request by converting the data into events for the
|
||||
* input subsystem.
|
||||
*/
|
||||
static void report_key(struct cm109_dev *dev, int key)
|
||||
{
|
||||
struct input_dev *idev = dev->idev;
|
||||
|
||||
if (dev->key_code >= 0) {
|
||||
/* old key up */
|
||||
input_report_key(idev, dev->key_code, 0);
|
||||
}
|
||||
|
||||
dev->key_code = key;
|
||||
if (key >= 0) {
|
||||
/* new valid key */
|
||||
input_report_key(idev, key, 1);
|
||||
}
|
||||
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* CM109 usb communication interface
|
||||
*****************************************************************************/
|
||||
|
||||
static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (dev->buzzer_state)
|
||||
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
|
||||
else
|
||||
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
|
||||
|
||||
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
|
||||
if (error)
|
||||
err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ handler
|
||||
*/
|
||||
static void cm109_urb_irq_callback(struct urb *urb)
|
||||
{
|
||||
struct cm109_dev *dev = urb->context;
|
||||
const int status = urb->status;
|
||||
int error;
|
||||
|
||||
dev_dbg(&urb->dev->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
|
||||
dev->irq_data->byte[0],
|
||||
dev->irq_data->byte[1],
|
||||
dev->irq_data->byte[2],
|
||||
dev->irq_data->byte[3],
|
||||
dev->keybit);
|
||||
|
||||
if (status) {
|
||||
if (status == -ESHUTDOWN)
|
||||
return;
|
||||
err("%s: urb status %d", __func__, status);
|
||||
}
|
||||
|
||||
/* Special keys */
|
||||
if (dev->irq_data->byte[HID_IR0] & 0x0f) {
|
||||
const int code = (dev->irq_data->byte[HID_IR0] & 0x0f);
|
||||
report_key(dev, dev->keymap[0xff + code]);
|
||||
}
|
||||
|
||||
/* Scan key column */
|
||||
if (dev->keybit == 0xf) {
|
||||
|
||||
/* Any changes ? */
|
||||
if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
|
||||
goto out;
|
||||
|
||||
dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
|
||||
dev->keybit = 0x1;
|
||||
} else {
|
||||
report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
|
||||
|
||||
dev->keybit <<= 1;
|
||||
if (dev->keybit > 0x8)
|
||||
dev->keybit = 0xf;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
spin_lock(&dev->ctl_submit_lock);
|
||||
|
||||
dev->irq_urb_pending = 0;
|
||||
|
||||
if (likely(!dev->shutdown)) {
|
||||
|
||||
if (dev->buzzer_state)
|
||||
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
|
||||
else
|
||||
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
|
||||
|
||||
dev->ctl_data->byte[HID_OR1] = dev->keybit;
|
||||
dev->ctl_data->byte[HID_OR2] = dev->keybit;
|
||||
|
||||
dev->buzzer_pending = 0;
|
||||
dev->ctl_urb_pending = 1;
|
||||
|
||||
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
|
||||
if (error)
|
||||
err("%s: usb_submit_urb (urb_ctl) failed %d",
|
||||
__func__, error);
|
||||
}
|
||||
|
||||
spin_unlock(&dev->ctl_submit_lock);
|
||||
}
|
||||
|
||||
static void cm109_urb_ctl_callback(struct urb *urb)
|
||||
{
|
||||
struct cm109_dev *dev = urb->context;
|
||||
const int status = urb->status;
|
||||
int error;
|
||||
|
||||
dev_dbg(&urb->dev->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
|
||||
dev->ctl_data->byte[0],
|
||||
dev->ctl_data->byte[1],
|
||||
dev->ctl_data->byte[2],
|
||||
dev->ctl_data->byte[3]);
|
||||
|
||||
if (status)
|
||||
err("%s: urb status %d", __func__, status);
|
||||
|
||||
spin_lock(&dev->ctl_submit_lock);
|
||||
|
||||
dev->ctl_urb_pending = 0;
|
||||
|
||||
if (likely(!dev->shutdown)) {
|
||||
|
||||
if (dev->buzzer_pending) {
|
||||
dev->buzzer_pending = 0;
|
||||
dev->ctl_urb_pending = 1;
|
||||
cm109_submit_buzz_toggle(dev);
|
||||
} else if (likely(!dev->irq_urb_pending)) {
|
||||
/* ask for key data */
|
||||
dev->irq_urb_pending = 1;
|
||||
error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
|
||||
if (error)
|
||||
err("%s: usb_submit_urb (urb_irq) failed %d",
|
||||
__func__, error);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&dev->ctl_submit_lock);
|
||||
}
|
||||
|
||||
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->ctl_submit_lock, flags);
|
||||
|
||||
if (dev->ctl_urb_pending) {
|
||||
/* URB completion will resubmit */
|
||||
dev->buzzer_pending = 1;
|
||||
} else {
|
||||
dev->ctl_urb_pending = 1;
|
||||
cm109_submit_buzz_toggle(dev);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
|
||||
}
|
||||
|
||||
static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (on)
|
||||
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
|
||||
else
|
||||
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
|
||||
|
||||
error = usb_control_msg(dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
dev->ctl_req->bRequest,
|
||||
dev->ctl_req->bRequestType,
|
||||
le16_to_cpu(dev->ctl_req->wValue),
|
||||
le16_to_cpu(dev->ctl_req->wIndex),
|
||||
dev->ctl_data,
|
||||
USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
|
||||
if (error && error != EINTR)
|
||||
err("%s: usb_control_msg() failed %d", __func__, error);
|
||||
}
|
||||
|
||||
static void cm109_stop_traffic(struct cm109_dev *dev)
|
||||
{
|
||||
dev->shutdown = 1;
|
||||
/*
|
||||
* Make sure other CPUs see this
|
||||
*/
|
||||
smp_wmb();
|
||||
|
||||
usb_kill_urb(dev->urb_ctl);
|
||||
usb_kill_urb(dev->urb_irq);
|
||||
|
||||
cm109_toggle_buzzer_sync(dev, 0);
|
||||
|
||||
dev->shutdown = 0;
|
||||
smp_wmb();
|
||||
}
|
||||
|
||||
static void cm109_restore_state(struct cm109_dev *dev)
|
||||
{
|
||||
if (dev->open) {
|
||||
/*
|
||||
* Restore buzzer state.
|
||||
* This will also kick regular URB submission
|
||||
*/
|
||||
cm109_toggle_buzzer_async(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* input event interface
|
||||
*****************************************************************************/
|
||||
|
||||
static int cm109_input_open(struct input_dev *idev)
|
||||
{
|
||||
struct cm109_dev *dev = input_get_drvdata(idev);
|
||||
int error;
|
||||
|
||||
error = usb_autopm_get_interface(dev->intf);
|
||||
if (error < 0) {
|
||||
err("%s - cannot autoresume, result %d",
|
||||
__func__, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
|
||||
dev->buzzer_state = 0;
|
||||
dev->key_code = -1; /* no keys pressed */
|
||||
dev->keybit = 0xf;
|
||||
|
||||
/* issue INIT */
|
||||
dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
|
||||
dev->ctl_data->byte[HID_OR1] = dev->keybit;
|
||||
dev->ctl_data->byte[HID_OR2] = dev->keybit;
|
||||
dev->ctl_data->byte[HID_OR3] = 0x00;
|
||||
|
||||
error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
|
||||
if (error)
|
||||
err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
|
||||
else
|
||||
dev->open = 1;
|
||||
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
|
||||
if (error)
|
||||
usb_autopm_put_interface(dev->intf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void cm109_input_close(struct input_dev *idev)
|
||||
{
|
||||
struct cm109_dev *dev = input_get_drvdata(idev);
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
|
||||
/*
|
||||
* Once we are here event delivery is stopped so we
|
||||
* don't need to worry about someone starting buzzer
|
||||
* again
|
||||
*/
|
||||
cm109_stop_traffic(dev);
|
||||
dev->open = 0;
|
||||
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
|
||||
usb_autopm_put_interface(dev->intf);
|
||||
}
|
||||
|
||||
static int cm109_input_ev(struct input_dev *idev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
struct cm109_dev *dev = input_get_drvdata(idev);
|
||||
|
||||
dev_dbg(&dev->udev->dev,
|
||||
"input_ev: type=%u code=%u value=%d\n", type, code, value);
|
||||
|
||||
if (type != EV_SND)
|
||||
return -EINVAL;
|
||||
|
||||
switch (code) {
|
||||
case SND_TONE:
|
||||
case SND_BELL:
|
||||
dev->buzzer_state = !!value;
|
||||
if (!dev->resetting)
|
||||
cm109_toggle_buzzer_async(dev);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Linux interface and usb initialisation
|
||||
*****************************************************************************/
|
||||
|
||||
struct driver_info {
|
||||
char *name;
|
||||
};
|
||||
|
||||
static const struct driver_info info_cm109 = {
|
||||
.name = "CM109 USB driver",
|
||||
};
|
||||
|
||||
enum {
|
||||
VENDOR_ID = 0x0d8c, /* C-Media Electronics */
|
||||
PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
|
||||
};
|
||||
|
||||
/* table of devices that work with this driver */
|
||||
static const struct usb_device_id cm109_usb_table[] = {
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
|
||||
USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
.idVendor = VENDOR_ID,
|
||||
.idProduct = PRODUCT_ID_CM109,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = (kernel_ulong_t) &info_cm109
|
||||
},
|
||||
/* you can add more devices here with product ID 0x0008 - 0x000f */
|
||||
{ }
|
||||
};
|
||||
|
||||
static void cm109_usb_cleanup(struct cm109_dev *dev)
|
||||
{
|
||||
if (dev->ctl_req)
|
||||
usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)),
|
||||
dev->ctl_req, dev->ctl_req_dma);
|
||||
if (dev->ctl_data)
|
||||
usb_buffer_free(dev->udev, USB_PKT_LEN,
|
||||
dev->ctl_data, dev->ctl_dma);
|
||||
if (dev->irq_data)
|
||||
usb_buffer_free(dev->udev, USB_PKT_LEN,
|
||||
dev->irq_data, dev->irq_dma);
|
||||
|
||||
usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
|
||||
usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
static void cm109_usb_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct cm109_dev *dev = usb_get_intfdata(interface);
|
||||
|
||||
usb_set_intfdata(interface, NULL);
|
||||
input_unregister_device(dev->idev);
|
||||
cm109_usb_cleanup(dev);
|
||||
}
|
||||
|
||||
static int cm109_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 cm109_dev *dev;
|
||||
struct input_dev *input_dev = NULL;
|
||||
int ret, pipe, i;
|
||||
int error = -ENOMEM;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&dev->ctl_submit_lock);
|
||||
mutex_init(&dev->pm_mutex);
|
||||
|
||||
dev->udev = udev;
|
||||
dev->intf = intf;
|
||||
|
||||
dev->idev = input_dev = input_allocate_device();
|
||||
if (!input_dev)
|
||||
goto err_out;
|
||||
|
||||
/* allocate usb buffers */
|
||||
dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
|
||||
GFP_KERNEL, &dev->irq_dma);
|
||||
if (!dev->irq_data)
|
||||
goto err_out;
|
||||
|
||||
dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
|
||||
GFP_KERNEL, &dev->ctl_dma);
|
||||
if (!dev->ctl_data)
|
||||
goto err_out;
|
||||
|
||||
dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)),
|
||||
GFP_KERNEL, &dev->ctl_req_dma);
|
||||
if (!dev->ctl_req)
|
||||
goto err_out;
|
||||
|
||||
/* allocate urb structures */
|
||||
dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!dev->urb_irq)
|
||||
goto err_out;
|
||||
|
||||
dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!dev->urb_ctl)
|
||||
goto err_out;
|
||||
|
||||
/* 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 %d", ret, USB_PKT_LEN);
|
||||
|
||||
/* initialise irq urb */
|
||||
usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
|
||||
USB_PKT_LEN,
|
||||
cm109_urb_irq_callback, dev, endpoint->bInterval);
|
||||
dev->urb_irq->transfer_dma = dev->irq_dma;
|
||||
dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
dev->urb_irq->dev = udev;
|
||||
|
||||
/* initialise ctl urb */
|
||||
dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
|
||||
USB_DIR_OUT;
|
||||
dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
|
||||
dev->ctl_req->wValue = cpu_to_le16(0x200);
|
||||
dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
|
||||
dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
|
||||
|
||||
usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
|
||||
(void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
|
||||
cm109_urb_ctl_callback, dev);
|
||||
dev->urb_ctl->setup_dma = dev->ctl_req_dma;
|
||||
dev->urb_ctl->transfer_dma = dev->ctl_dma;
|
||||
dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
|
||||
URB_NO_TRANSFER_DMA_MAP;
|
||||
dev->urb_ctl->dev = udev;
|
||||
|
||||
/* find out the physical bus location */
|
||||
usb_make_path(udev, dev->phys, sizeof(dev->phys));
|
||||
strlcat(dev->phys, "/input0", sizeof(dev->phys));
|
||||
|
||||
/* register settings for the input device */
|
||||
input_dev->name = nfo->name;
|
||||
input_dev->phys = dev->phys;
|
||||
usb_to_input_id(udev, &input_dev->id);
|
||||
input_dev->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, dev);
|
||||
input_dev->open = cm109_input_open;
|
||||
input_dev->close = cm109_input_close;
|
||||
input_dev->event = cm109_input_ev;
|
||||
|
||||
input_dev->keycode = dev->keymap;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
|
||||
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
|
||||
|
||||
/* register available key events */
|
||||
for (i = 0; i < KEYMAP_SIZE; i++) {
|
||||
unsigned short k = keymap(i);
|
||||
dev->keymap[i] = k;
|
||||
__set_bit(k, input_dev->keybit);
|
||||
}
|
||||
__clear_bit(KEY_RESERVED, input_dev->keybit);
|
||||
|
||||
error = input_register_device(dev->idev);
|
||||
if (error)
|
||||
goto err_out;
|
||||
|
||||
usb_set_intfdata(intf, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
input_free_device(input_dev);
|
||||
cm109_usb_cleanup(dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct cm109_dev *dev = usb_get_intfdata(intf);
|
||||
|
||||
dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
cm109_stop_traffic(dev);
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm109_usb_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct cm109_dev *dev = usb_get_intfdata(intf);
|
||||
|
||||
dev_info(&intf->dev, "cm109: usb_resume\n");
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
cm109_restore_state(dev);
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm109_usb_pre_reset(struct usb_interface *intf)
|
||||
{
|
||||
struct cm109_dev *dev = usb_get_intfdata(intf);
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
|
||||
/*
|
||||
* Make sure input events don't try to toggle buzzer
|
||||
* while we are resetting
|
||||
*/
|
||||
dev->resetting = 1;
|
||||
smp_wmb();
|
||||
|
||||
cm109_stop_traffic(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm109_usb_post_reset(struct usb_interface *intf)
|
||||
{
|
||||
struct cm109_dev *dev = usb_get_intfdata(intf);
|
||||
|
||||
dev->resetting = 0;
|
||||
smp_wmb();
|
||||
|
||||
cm109_restore_state(dev);
|
||||
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_driver cm109_driver = {
|
||||
.name = "cm109",
|
||||
.probe = cm109_usb_probe,
|
||||
.disconnect = cm109_usb_disconnect,
|
||||
.suspend = cm109_usb_suspend,
|
||||
.resume = cm109_usb_resume,
|
||||
.reset_resume = cm109_usb_resume,
|
||||
.pre_reset = cm109_usb_pre_reset,
|
||||
.post_reset = cm109_usb_post_reset,
|
||||
.id_table = cm109_usb_table,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
||||
static int __init cm109_select_keymap(void)
|
||||
{
|
||||
/* Load the phone keymap */
|
||||
if (!strcasecmp(phone, "kip1000")) {
|
||||
keymap = keymap_kip1000;
|
||||
printk(KERN_INFO KBUILD_MODNAME ": "
|
||||
"Keymap for Komunikate KIP1000 phone loaded\n");
|
||||
} else if (!strcasecmp(phone, "gtalk")) {
|
||||
keymap = keymap_gtalk;
|
||||
printk(KERN_INFO KBUILD_MODNAME ": "
|
||||
"Keymap for Genius G-talk phone loaded\n");
|
||||
} else if (!strcasecmp(phone, "usbph01")) {
|
||||
keymap = keymap_usbph01;
|
||||
printk(KERN_INFO KBUILD_MODNAME ": "
|
||||
"Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
|
||||
} else {
|
||||
printk(KERN_ERR KBUILD_MODNAME ": "
|
||||
"Unsupported phone: %s\n", phone);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init cm109_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = cm109_select_keymap();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = usb_register(&cm109_driver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
printk(KERN_INFO KBUILD_MODNAME ": "
|
||||
DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cm109_exit(void)
|
||||
{
|
||||
usb_deregister(&cm109_driver);
|
||||
}
|
||||
|
||||
module_init(cm109_init);
|
||||
module_exit(cm109_exit);
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, cm109_usb_table);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
|
@ -277,6 +277,16 @@ static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = {
|
|||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = {
|
||||
{ KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */
|
||||
{ KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */
|
||||
{ KE_BLUETOOTH, 0x30 }, /* Fn+F10 */
|
||||
{ KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */
|
||||
{ KE_KEY, 0x36, {KEY_WWW} }, /* www button */
|
||||
{ KE_WIFI, 0x78 }, /* satelite dish button */
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static struct key_entry keymap_fujitsu_n3510[] __initdata = {
|
||||
{ KE_KEY, 0x11, {KEY_PROG1} },
|
||||
{ KE_KEY, 0x12, {KEY_PROG2} },
|
||||
|
@ -616,6 +626,15 @@ static struct dmi_system_id dmi_ids[] __initdata = {
|
|||
},
|
||||
.driver_data = keymap_fs_amilo_pro_v2000
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Fujitsu-Siemens Amilo Pro Edition V3505",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"),
|
||||
},
|
||||
.driver_data = keymap_fs_amilo_pro_v3505
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Fujitsu-Siemens Amilo M7400",
|
||||
|
|
|
@ -52,8 +52,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/map_to_7segment.h>
|
||||
|
||||
#include "map_to_7segment.h"
|
||||
#include "yealink.h"
|
||||
|
||||
#define DRIVER_VERSION "yld-20051230"
|
||||
|
|
|
@ -96,6 +96,16 @@ config MOUSE_PS2_TOUCHKIT
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MOUSE_PS2_OLPC
|
||||
bool "OLPC PS/2 mouse protocol extension"
|
||||
depends on MOUSE_PS2 && OLPC
|
||||
help
|
||||
Say Y here if you have an OLPC XO-1 laptop (with built-in
|
||||
PS/2 touchpad/tablet device). The manufacturer calls the
|
||||
touchpad an HGPK.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MOUSE_SERIAL
|
||||
tristate "Serial mouse"
|
||||
select SERIO
|
||||
|
|
|
@ -21,6 +21,7 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
|||
psmouse-objs := psmouse-base.o synaptics.o
|
||||
|
||||
psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
|
||||
|
|
|
@ -54,6 +54,7 @@ static const struct alps_model_info alps_model_data[] = {
|
|||
{ { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
|
||||
{ { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
|
||||
{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
|
||||
{ { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */
|
||||
{ { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 } /* Dell Vostro 1400 */
|
||||
};
|
||||
|
||||
|
|
|
@ -136,12 +136,28 @@ MODULE_DEVICE_TABLE(usb, atp_table);
|
|||
#define ATP_GEYSER_MODE_REQUEST_INDEX 0
|
||||
#define ATP_GEYSER_MODE_VENDOR_VALUE 0x04
|
||||
|
||||
/**
|
||||
* enum atp_status_bits - status bit meanings
|
||||
*
|
||||
* These constants represent the meaning of the status bits.
|
||||
* (only Geyser 3/4)
|
||||
*
|
||||
* @ATP_STATUS_BUTTON: The button was pressed
|
||||
* @ATP_STATUS_BASE_UPDATE: Update of the base values (untouched pad)
|
||||
* @ATP_STATUS_FROM_RESET: Reset previously performed
|
||||
*/
|
||||
enum atp_status_bits {
|
||||
ATP_STATUS_BUTTON = BIT(0),
|
||||
ATP_STATUS_BASE_UPDATE = BIT(2),
|
||||
ATP_STATUS_FROM_RESET = BIT(4),
|
||||
};
|
||||
|
||||
/* 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 */
|
||||
u8 *data; /* transferred data */
|
||||
struct input_dev *input; /* input dev */
|
||||
enum atp_touchpad_type type; /* type of touchpad */
|
||||
bool open;
|
||||
|
@ -251,8 +267,6 @@ static void atp_reinit(struct work_struct *work)
|
|||
int retval;
|
||||
|
||||
dprintk("appletouch: putting appletouch to sleep (reinit)\n");
|
||||
dev->idlecount = 0;
|
||||
|
||||
atp_geyser_init(udev);
|
||||
|
||||
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
|
||||
|
@ -327,11 +341,14 @@ static inline void atp_report_fingers(struct input_dev *input, int fingers)
|
|||
input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
|
||||
}
|
||||
|
||||
static void atp_complete(struct urb *urb)
|
||||
/* Check URB status and for correct length of data package */
|
||||
|
||||
#define ATP_URB_STATUS_SUCCESS 0
|
||||
#define ATP_URB_STATUS_ERROR 1
|
||||
#define ATP_URB_STATUS_ERROR_FATAL 2
|
||||
|
||||
static int atp_status_check(struct urb *urb)
|
||||
{
|
||||
int x, y, x_z, y_z, x_f, y_f;
|
||||
int retval, i, j;
|
||||
int key;
|
||||
struct atp *dev = urb->context;
|
||||
|
||||
switch (urb->status) {
|
||||
|
@ -351,11 +368,12 @@ static void atp_complete(struct urb *urb)
|
|||
/* This urb is terminated, clean up */
|
||||
dbg("atp_complete: urb shutting down with status: %d",
|
||||
urb->status);
|
||||
return;
|
||||
return ATP_URB_STATUS_ERROR_FATAL;
|
||||
|
||||
default:
|
||||
dbg("atp_complete: nonzero urb status received: %d",
|
||||
urb->status);
|
||||
goto exit;
|
||||
return ATP_URB_STATUS_ERROR;
|
||||
}
|
||||
|
||||
/* drop incomplete datasets */
|
||||
|
@ -363,30 +381,33 @@ static void atp_complete(struct urb *urb)
|
|||
dprintk("appletouch: incomplete data package"
|
||||
" (first byte: %d, length: %d).\n",
|
||||
dev->data[0], dev->urb->actual_length);
|
||||
goto exit;
|
||||
return ATP_URB_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return ATP_URB_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* USB interrupt callback functions
|
||||
*/
|
||||
|
||||
/* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */
|
||||
|
||||
static void atp_complete_geyser_1_2(struct urb *urb)
|
||||
{
|
||||
int x, y, x_z, y_z, x_f, y_f;
|
||||
int retval, i, j;
|
||||
int key;
|
||||
struct atp *dev = urb->context;
|
||||
int status = atp_status_check(urb);
|
||||
|
||||
if (status == ATP_URB_STATUS_ERROR_FATAL)
|
||||
return;
|
||||
else if (status == ATP_URB_STATUS_ERROR)
|
||||
goto exit;
|
||||
|
||||
/* reorder the sensors values */
|
||||
if (dev->type == ATP_GEYSER3 || dev->type == ATP_GEYSER4) {
|
||||
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 (dev->type == ATP_GEYSER2) {
|
||||
if (dev->type == ATP_GEYSER2) {
|
||||
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
|
||||
|
||||
/*
|
||||
|
@ -427,34 +448,40 @@ static void atp_complete(struct urb *urb)
|
|||
/* first sample */
|
||||
dev->valid = true;
|
||||
dev->x_old = dev->y_old = -1;
|
||||
|
||||
/* Store first sample */
|
||||
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
|
||||
|
||||
if (dev->size_detect_done ||
|
||||
dev->type == ATP_GEYSER3) /* No 17" Macbooks (yet) */
|
||||
/* Perform size detection, if not done already */
|
||||
if (!dev->size_detect_done) {
|
||||
|
||||
/* 17" Powerbooks have extra X sensors */
|
||||
for (i = (dev->type == ATP_GEYSER2 ? 15 : 16);
|
||||
i < ATP_XSENSORS; i++) {
|
||||
if (!dev->xy_cur[i])
|
||||
continue;
|
||||
|
||||
printk(KERN_INFO
|
||||
"appletouch: 17\" model detected.\n");
|
||||
|
||||
if (dev->type == ATP_GEYSER2)
|
||||
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,
|
||||
(26 - 1) *
|
||||
ATP_XFACT - 1,
|
||||
ATP_FUZZ, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
dev->size_detect_done = 1;
|
||||
goto exit;
|
||||
|
||||
/* 17" Powerbooks have extra X sensors */
|
||||
for (i = (dev->type == ATP_GEYSER2 ? 15 : 16);
|
||||
i < ATP_XSENSORS; i++) {
|
||||
if (!dev->xy_cur[i])
|
||||
continue;
|
||||
|
||||
printk(KERN_INFO "appletouch: 17\" model detected.\n");
|
||||
if (dev->type == ATP_GEYSER2)
|
||||
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;
|
||||
}
|
||||
|
||||
dev->size_detect_done = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
|
||||
|
@ -475,7 +502,118 @@ static void atp_complete(struct urb *urb)
|
|||
ATP_XFACT, &x_z, &x_f);
|
||||
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
|
||||
ATP_YFACT, &y_z, &y_f);
|
||||
key = dev->data[dev->datalen - 1] & 1;
|
||||
key = dev->data[dev->datalen - 1] & ATP_STATUS_BUTTON;
|
||||
|
||||
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(KERN_DEBUG "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, key);
|
||||
input_sync(dev->input);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err("atp_complete: usb_submit_urb failed with result %d",
|
||||
retval);
|
||||
}
|
||||
|
||||
/* Interrupt function for older touchpads: GEYSER3/GEYSER4 */
|
||||
|
||||
static void atp_complete_geyser_3_4(struct urb *urb)
|
||||
{
|
||||
int x, y, x_z, y_z, x_f, y_f;
|
||||
int retval, i, j;
|
||||
int key;
|
||||
struct atp *dev = urb->context;
|
||||
int status = atp_status_check(urb);
|
||||
|
||||
if (status == ATP_URB_STATUS_ERROR_FATAL)
|
||||
return;
|
||||
else if (status == ATP_URB_STATUS_ERROR)
|
||||
goto exit;
|
||||
|
||||
/* Reorder the sensors values:
|
||||
*
|
||||
* 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];
|
||||
}
|
||||
|
||||
dbg_dump("sample", dev->xy_cur);
|
||||
|
||||
/* Just update the base values (i.e. touchpad in untouched state) */
|
||||
if (dev->data[dev->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
|
||||
|
||||
dprintk(KERN_DEBUG "appletouch: updated base values\n");
|
||||
|
||||
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
|
||||
/* calculate the change */
|
||||
dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
|
||||
|
||||
/* this is a round-robin value, so couple with that */
|
||||
if (dev->xy_acc[i] > 127)
|
||||
dev->xy_acc[i] -= 256;
|
||||
|
||||
if (dev->xy_acc[i] < -127)
|
||||
dev->xy_acc[i] += 256;
|
||||
|
||||
/* prevent down drifting */
|
||||
if (dev->xy_acc[i] < 0)
|
||||
dev->xy_acc[i] = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
key = dev->data[dev->datalen - 1] & ATP_STATUS_BUTTON;
|
||||
|
||||
if (x && y) {
|
||||
if (dev->x_old != -1) {
|
||||
|
@ -514,28 +652,27 @@ static void atp_complete(struct urb *urb)
|
|||
input_sync(dev->input);
|
||||
|
||||
/*
|
||||
* Many Geysers will continue to send packets continually after
|
||||
* Geysers 3/4 will continue to send packets continually after
|
||||
* the first touch unless reinitialised. Do so if it's been
|
||||
* idle for a while in order to avoid waking the kernel up
|
||||
* several hundred times a second. Re-initialization does not
|
||||
* work on Fountain touchpads.
|
||||
* several hundred times a second.
|
||||
*/
|
||||
if (dev->type != ATP_FOUNTAIN) {
|
||||
/*
|
||||
* Button must not be pressed when entering suspend,
|
||||
* otherwise we will never release the button.
|
||||
*/
|
||||
if (!x && !y && !key) {
|
||||
dev->idlecount++;
|
||||
if (dev->idlecount == 10) {
|
||||
dev->valid = false;
|
||||
schedule_work(&dev->work);
|
||||
/* Don't resubmit urb here, wait for reinit */
|
||||
return;
|
||||
}
|
||||
} else
|
||||
|
||||
/*
|
||||
* Button must not be pressed when entering suspend,
|
||||
* otherwise we will never release the button.
|
||||
*/
|
||||
if (!x && !y && !key) {
|
||||
dev->idlecount++;
|
||||
if (dev->idlecount == 10) {
|
||||
dev->x_old = dev->y_old = -1;
|
||||
dev->idlecount = 0;
|
||||
}
|
||||
schedule_work(&dev->work);
|
||||
/* Don't resubmit urb here, wait for reinit */
|
||||
return;
|
||||
}
|
||||
} else
|
||||
dev->idlecount = 0;
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
|
||||
|
@ -632,9 +769,19 @@ static int atp_probe(struct usb_interface *iface,
|
|||
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);
|
||||
/* Select the USB complete (callback) function */
|
||||
if (dev->type == ATP_FOUNTAIN ||
|
||||
dev->type == ATP_GEYSER1 ||
|
||||
dev->type == ATP_GEYSER2)
|
||||
usb_fill_int_urb(dev->urb, udev,
|
||||
usb_rcvintpipe(udev, int_in_endpointAddr),
|
||||
dev->data, dev->datalen,
|
||||
atp_complete_geyser_1_2, dev, 1);
|
||||
else
|
||||
usb_fill_int_urb(dev->urb, udev,
|
||||
usb_rcvintpipe(udev, int_in_endpointAddr),
|
||||
dev->data, dev->datalen,
|
||||
atp_complete_geyser_3_4, dev, 1);
|
||||
|
||||
error = atp_handle_geyser(dev);
|
||||
if (error)
|
||||
|
@ -751,8 +898,6 @@ 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 = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* OLPC HGPK (XO-1) touchpad PS/2 mouse driver
|
||||
*
|
||||
* Copyright (c) 2006-2008 One Laptop Per Child
|
||||
* Authors:
|
||||
* Zephaniah E. Hull
|
||||
* Andres Salomon <dilinger@debian.org>
|
||||
*
|
||||
* This driver is partly based on the ALPS driver, which is:
|
||||
*
|
||||
* Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
|
||||
* Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
|
||||
* Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
|
||||
* Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The spec from ALPS is available from
|
||||
* <http://wiki.laptop.org/go/Touch_Pad/Tablet>. It refers to this
|
||||
* device as HGPK (Hybrid GS, PT, and Keymatrix).
|
||||
*
|
||||
* The earliest versions of the device had simultaneous reporting; that
|
||||
* was removed. After that, the device used the Advanced Mode GS/PT streaming
|
||||
* stuff. That turned out to be too buggy to support, so we've finally
|
||||
* switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad).
|
||||
*/
|
||||
|
||||
#define DEBUG
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/libps2.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/olpc.h>
|
||||
|
||||
#include "psmouse.h"
|
||||
#include "hgpk.h"
|
||||
|
||||
static int tpdebug;
|
||||
module_param(tpdebug, int, 0644);
|
||||
MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
|
||||
|
||||
static int recalib_delta = 100;
|
||||
module_param(recalib_delta, int, 0644);
|
||||
MODULE_PARM_DESC(recalib_delta,
|
||||
"packets containing a delta this large will cause a recalibration.");
|
||||
|
||||
/*
|
||||
* When the touchpad gets ultra-sensitive, one can keep their finger 1/2"
|
||||
* above the pad and still have it send packets. This causes a jump cursor
|
||||
* when one places their finger on the pad. We can probably detect the
|
||||
* jump as we see a large deltas (>= 100px). In mouse mode, I've been
|
||||
* unable to even come close to 100px deltas during normal usage, so I think
|
||||
* this threshold is safe. If a large delta occurs, trigger a recalibration.
|
||||
*/
|
||||
static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
if (abs(x) > recalib_delta || abs(y) > recalib_delta) {
|
||||
hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n",
|
||||
recalib_delta, x, y);
|
||||
/* My car gets forty rods to the hogshead and that's the
|
||||
* way I likes it! */
|
||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||
msecs_to_jiffies(1000));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have no idea why this particular hardware bug occurs. The touchpad
|
||||
* will randomly start spewing packets without anything touching the
|
||||
* pad. This wouldn't necessarily be bad, but it's indicative of a
|
||||
* severely miscalibrated pad; attempting to use the touchpad while it's
|
||||
* spewing means the cursor will jump all over the place, and act "drunk".
|
||||
*
|
||||
* The packets that are spewed tend to all have deltas between -2 and 2, and
|
||||
* the cursor will move around without really going very far. It will
|
||||
* tend to end up in the same location; if we tally up the changes over
|
||||
* 100 packets, we end up w/ a final delta of close to 0. This happens
|
||||
* pretty regularly when the touchpad is spewing, and is pretty hard to
|
||||
* manually trigger (at least for *my* fingers). So, it makes a perfect
|
||||
* scheme for detecting spews.
|
||||
*/
|
||||
static void hgpk_spewing_hack(struct psmouse *psmouse,
|
||||
int l, int r, int x, int y)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
/* ignore button press packets; many in a row could trigger
|
||||
* a false-positive! */
|
||||
if (l || r)
|
||||
return;
|
||||
|
||||
priv->x_tally += x;
|
||||
priv->y_tally += y;
|
||||
|
||||
if (++priv->count > 100) {
|
||||
if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) {
|
||||
hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n",
|
||||
priv->x_tally, priv->y_tally);
|
||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||
msecs_to_jiffies(1000));
|
||||
}
|
||||
/* reset every 100 packets */
|
||||
priv->count = 0;
|
||||
priv->x_tally = 0;
|
||||
priv->y_tally = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HGPK Mouse Mode format (standard mouse format, sans middle button)
|
||||
*
|
||||
* byte 0: y-over x-over y-neg x-neg 1 0 swr swl
|
||||
* byte 1: x7 x6 x5 x4 x3 x2 x1 x0
|
||||
* byte 2: y7 y6 y5 y4 y3 y2 y1 y0
|
||||
*
|
||||
* swr/swl are the left/right buttons.
|
||||
* x-neg/y-neg are the x and y delta negative bits
|
||||
* x-over/y-over are the x and y overflow bits
|
||||
*/
|
||||
static int hgpk_validate_byte(unsigned char *packet)
|
||||
{
|
||||
return (packet[0] & 0x0C) == 0x08;
|
||||
}
|
||||
|
||||
static void hgpk_process_packet(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
int x, y, left, right;
|
||||
|
||||
left = packet[0] & 1;
|
||||
right = (packet[0] >> 1) & 1;
|
||||
|
||||
x = packet[1] - ((packet[0] << 4) & 0x100);
|
||||
y = ((packet[0] << 3) & 0x100) - packet[2];
|
||||
|
||||
hgpk_jumpy_hack(psmouse, x, y);
|
||||
hgpk_spewing_hack(psmouse, left, right, x, y);
|
||||
|
||||
if (tpdebug)
|
||||
hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, left);
|
||||
input_report_key(dev, BTN_RIGHT, right);
|
||||
|
||||
input_report_rel(dev, REL_X, x);
|
||||
input_report_rel(dev, REL_Y, y);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
if (hgpk_validate_byte(psmouse->packet)) {
|
||||
hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n",
|
||||
__func__, psmouse->pktcnt, psmouse->packet[0],
|
||||
psmouse->packet[1], psmouse->packet[2]);
|
||||
return PSMOUSE_BAD_DATA;
|
||||
}
|
||||
|
||||
if (psmouse->pktcnt >= psmouse->pktsize) {
|
||||
hgpk_process_packet(psmouse);
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
}
|
||||
|
||||
if (priv->recalib_window) {
|
||||
if (time_before(jiffies, priv->recalib_window)) {
|
||||
/*
|
||||
* ugh, got a packet inside our recalibration
|
||||
* window, schedule another recalibration.
|
||||
*/
|
||||
hgpk_dbg(psmouse,
|
||||
"packet inside calibration window, "
|
||||
"queueing another recalibration\n");
|
||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||
msecs_to_jiffies(1000));
|
||||
}
|
||||
priv->recalib_window = 0;
|
||||
}
|
||||
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
}
|
||||
|
||||
static int hgpk_force_recalibrate(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
/* C-series touchpads added the recalibrate command */
|
||||
if (psmouse->model < HGPK_MODEL_C)
|
||||
return 0;
|
||||
|
||||
/* we don't want to race with the irq handler, nor with resyncs */
|
||||
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
|
||||
|
||||
/* start by resetting the device */
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
/* send the recalibrate request */
|
||||
if (ps2_command(ps2dev, NULL, 0xf5) ||
|
||||
ps2_command(ps2dev, NULL, 0xf5) ||
|
||||
ps2_command(ps2dev, NULL, 0xe6) ||
|
||||
ps2_command(ps2dev, NULL, 0xf5)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* according to ALPS, 150mS is required for recalibration */
|
||||
msleep(150);
|
||||
|
||||
/* XXX: If a finger is down during this delay, recalibration will
|
||||
* detect capacitance incorrectly. This is a hardware bug, and
|
||||
* we don't have a good way to deal with it. The 2s window stuff
|
||||
* (below) is our best option for now.
|
||||
*/
|
||||
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
|
||||
return -1;
|
||||
|
||||
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
|
||||
|
||||
/* After we recalibrate, we shouldn't get any packets for 2s. If
|
||||
* we do, it's likely that someone's finger was on the touchpad.
|
||||
* If someone's finger *was* on the touchpad, it's probably
|
||||
* miscalibrated. So, we should schedule another recalibration
|
||||
*/
|
||||
priv->recalib_window = jiffies + msecs_to_jiffies(2000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This kills power to the touchpad; according to ALPS, current consumption
|
||||
* goes down to 50uA after running this. To turn power back on, we drive
|
||||
* MS-DAT low.
|
||||
*/
|
||||
static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
int timeo;
|
||||
|
||||
/* Added on D-series touchpads */
|
||||
if (psmouse->model < HGPK_MODEL_D)
|
||||
return 0;
|
||||
|
||||
if (enable) {
|
||||
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
|
||||
|
||||
/*
|
||||
* Sending a byte will drive MS-DAT low; this will wake up
|
||||
* the controller. Once we get an ACK back from it, it
|
||||
* means we can continue with the touchpad re-init. ALPS
|
||||
* tells us that 1s should be long enough, so set that as
|
||||
* the upper bound.
|
||||
*/
|
||||
for (timeo = 20; timeo > 0; timeo--) {
|
||||
if (!ps2_sendbyte(&psmouse->ps2dev,
|
||||
PSMOUSE_CMD_DISABLE, 20))
|
||||
break;
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
/* should be all set, enable the touchpad */
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
|
||||
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
|
||||
|
||||
} else {
|
||||
hgpk_dbg(psmouse, "Powering off touchpad.\n");
|
||||
psmouse_set_state(psmouse, PSMOUSE_IGNORE);
|
||||
|
||||
if (ps2_command(ps2dev, NULL, 0xec) ||
|
||||
ps2_command(ps2dev, NULL, 0xec) ||
|
||||
ps2_command(ps2dev, NULL, 0xea)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* probably won't see an ACK, the touchpad will be off */
|
||||
ps2_sendbyte(&psmouse->ps2dev, 0xec, 20);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hgpk_poll(struct psmouse *psmouse)
|
||||
{
|
||||
/* We can't poll, so always return failure. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int hgpk_reconnect(struct psmouse *psmouse)
|
||||
{
|
||||
/* During suspend/resume the ps2 rails remain powered. We don't want
|
||||
* to do a reset because it's flush data out of buffers; however,
|
||||
* earlier prototypes (B1) had some brokenness that required a reset. */
|
||||
if (olpc_board_at_least(olpc_board(0xb2)))
|
||||
if (psmouse->ps2dev.serio->dev.power.power_state.event !=
|
||||
PM_EVENT_ON)
|
||||
return 0;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
return sprintf(buf, "%d\n", priv->powered);
|
||||
}
|
||||
|
||||
static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
unsigned long value;
|
||||
int err;
|
||||
|
||||
err = strict_strtoul(buf, 10, &value);
|
||||
if (err || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (value != priv->powered) {
|
||||
/*
|
||||
* hgpk_toggle_power will deal w/ state so
|
||||
* we're not racing w/ irq
|
||||
*/
|
||||
err = hgpk_toggle_power(psmouse, value);
|
||||
if (!err)
|
||||
priv->powered = value;
|
||||
}
|
||||
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,
|
||||
hgpk_show_powered, hgpk_set_powered, 0);
|
||||
|
||||
static void hgpk_disconnect(struct psmouse *psmouse)
|
||||
{
|
||||
struct hgpk_data *priv = psmouse->private;
|
||||
|
||||
device_remove_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_powered.dattr);
|
||||
psmouse_reset(psmouse);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static void hgpk_recalib_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *w = container_of(work, struct delayed_work, work);
|
||||
struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq);
|
||||
struct psmouse *psmouse = priv->psmouse;
|
||||
|
||||
hgpk_dbg(psmouse, "recalibrating touchpad..\n");
|
||||
|
||||
if (hgpk_force_recalibrate(psmouse))
|
||||
hgpk_err(psmouse, "recalibration failed!\n");
|
||||
}
|
||||
|
||||
static int hgpk_register(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
int err;
|
||||
|
||||
/* unset the things that psmouse-base sets which we don't have */
|
||||
__clear_bit(BTN_MIDDLE, dev->keybit);
|
||||
|
||||
/* set the things we do have */
|
||||
__set_bit(EV_KEY, dev->evbit);
|
||||
__set_bit(EV_REL, dev->evbit);
|
||||
|
||||
__set_bit(REL_X, dev->relbit);
|
||||
__set_bit(REL_Y, dev->relbit);
|
||||
|
||||
__set_bit(BTN_LEFT, dev->keybit);
|
||||
__set_bit(BTN_RIGHT, dev->keybit);
|
||||
|
||||
/* register handlers */
|
||||
psmouse->protocol_handler = hgpk_process_byte;
|
||||
psmouse->poll = hgpk_poll;
|
||||
psmouse->disconnect = hgpk_disconnect;
|
||||
psmouse->reconnect = hgpk_reconnect;
|
||||
psmouse->pktsize = 3;
|
||||
|
||||
/* Disable the idle resync. */
|
||||
psmouse->resync_time = 0;
|
||||
/* Reset after a lot of bad bytes. */
|
||||
psmouse->resetafter = 1024;
|
||||
|
||||
err = device_create_file(&psmouse->ps2dev.serio->dev,
|
||||
&psmouse_attr_powered.dattr);
|
||||
if (err)
|
||||
hgpk_err(psmouse, "Failed to create sysfs attribute\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int hgpk_init(struct psmouse *psmouse)
|
||||
{
|
||||
struct hgpk_data *priv;
|
||||
int err = -ENOMEM;
|
||||
|
||||
priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL);
|
||||
if (!priv)
|
||||
goto alloc_fail;
|
||||
|
||||
psmouse->private = priv;
|
||||
priv->psmouse = psmouse;
|
||||
priv->powered = 1;
|
||||
INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work);
|
||||
|
||||
err = psmouse_reset(psmouse);
|
||||
if (err)
|
||||
goto init_fail;
|
||||
|
||||
err = hgpk_register(psmouse);
|
||||
if (err)
|
||||
goto init_fail;
|
||||
|
||||
return 0;
|
||||
|
||||
init_fail:
|
||||
kfree(priv);
|
||||
alloc_fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
unsigned char param[3];
|
||||
|
||||
/* E7, E7, E7, E9 gets us a 3 byte identifier */
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
|
||||
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]);
|
||||
|
||||
/* HGPK signature: 0x67, 0x00, 0x<model> */
|
||||
if (param[0] != 0x67 || param[1] != 0x00)
|
||||
return -ENODEV;
|
||||
|
||||
hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]);
|
||||
|
||||
return param[2];
|
||||
}
|
||||
|
||||
int hgpk_detect(struct psmouse *psmouse, int set_properties)
|
||||
{
|
||||
int version;
|
||||
|
||||
version = hgpk_get_model(psmouse);
|
||||
if (version < 0)
|
||||
return version;
|
||||
|
||||
if (set_properties) {
|
||||
psmouse->vendor = "ALPS";
|
||||
psmouse->name = "HGPK";
|
||||
psmouse->model = version;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* OLPC HGPK (XO-1) touchpad PS/2 mouse driver
|
||||
*/
|
||||
|
||||
#ifndef _HGPK_H
|
||||
#define _HGPK_H
|
||||
|
||||
enum hgpk_model_t {
|
||||
HGPK_MODEL_PREA = 0x0a, /* pre-B1s */
|
||||
HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */
|
||||
HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */
|
||||
HGPK_MODEL_C = 0x3c,
|
||||
HGPK_MODEL_D = 0x50, /* C1, mass production */
|
||||
};
|
||||
|
||||
struct hgpk_data {
|
||||
struct psmouse *psmouse;
|
||||
int powered;
|
||||
int count, x_tally, y_tally; /* hardware workaround stuff */
|
||||
unsigned long recalib_window;
|
||||
struct delayed_work recalib_wq;
|
||||
};
|
||||
|
||||
#define hgpk_dbg(psmouse, format, arg...) \
|
||||
dev_dbg(&(psmouse)->ps2dev.serio->dev, format, ## arg)
|
||||
#define hgpk_err(psmouse, format, arg...) \
|
||||
dev_err(&(psmouse)->ps2dev.serio->dev, format, ## arg)
|
||||
#define hgpk_info(psmouse, format, arg...) \
|
||||
dev_info(&(psmouse)->ps2dev.serio->dev, format, ## arg)
|
||||
#define hgpk_warn(psmouse, format, arg...) \
|
||||
dev_warn(&(psmouse)->ps2dev.serio->dev, format, ## arg)
|
||||
#define hgpk_notice(psmouse, format, arg...) \
|
||||
dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg)
|
||||
|
||||
#ifdef CONFIG_MOUSE_PS2_OLPC
|
||||
int hgpk_detect(struct psmouse *psmouse, int set_properties);
|
||||
int hgpk_init(struct psmouse *psmouse);
|
||||
#else
|
||||
static inline int hgpk_detect(struct psmouse *psmouse, int set_properties)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int hgpk_init(struct psmouse *psmouse)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -157,10 +157,8 @@ static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, void *data,
|
|||
static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || value > 1)
|
||||
if (strict_strtoul(buf, 10, &value) || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
ps2pp_set_smartscroll(psmouse, value);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "synaptics.h"
|
||||
#include "logips2pp.h"
|
||||
#include "alps.h"
|
||||
#include "hgpk.h"
|
||||
#include "lifebook.h"
|
||||
#include "trackpoint.h"
|
||||
#include "touchkit_ps2.h"
|
||||
|
@ -201,6 +202,12 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
|
|||
return PSMOUSE_FULL_PACKET;
|
||||
}
|
||||
|
||||
void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
|
||||
unsigned long delay)
|
||||
{
|
||||
queue_delayed_work(kpsmoused_wq, work, delay);
|
||||
}
|
||||
|
||||
/*
|
||||
* __psmouse_set_state() sets new psmouse state and resets all flags.
|
||||
*/
|
||||
|
@ -220,7 +227,7 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta
|
|||
* is not a concern.
|
||||
*/
|
||||
|
||||
static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
|
||||
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
|
||||
{
|
||||
serio_pause_rx(psmouse->ps2dev.serio);
|
||||
__psmouse_set_state(psmouse, new_state);
|
||||
|
@ -305,7 +312,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
|
|||
psmouse->name, psmouse->phys, psmouse->pktcnt);
|
||||
psmouse->badbyte = psmouse->packet[0];
|
||||
__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
|
||||
queue_work(kpsmoused_wq, &psmouse->resync_work);
|
||||
psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -342,7 +349,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
|
|||
time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
|
||||
psmouse->badbyte = psmouse->packet[0];
|
||||
__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
|
||||
queue_work(kpsmoused_wq, &psmouse->resync_work);
|
||||
psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -630,8 +637,20 @@ static int psmouse_extensions(struct psmouse *psmouse,
|
|||
}
|
||||
}
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
/*
|
||||
* Try OLPC HGPK touchpad.
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
hgpk_detect(psmouse, set_properties) == 0) {
|
||||
if (!set_properties || hgpk_init(psmouse) == 0)
|
||||
return PSMOUSE_HGPK;
|
||||
/*
|
||||
* Init failed, try basic relative protocols
|
||||
*/
|
||||
max_proto = PSMOUSE_IMEX;
|
||||
}
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (genius_detect(psmouse, set_properties) == 0)
|
||||
return PSMOUSE_GENPS;
|
||||
|
||||
|
@ -761,6 +780,14 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
|||
.alias = "touchkit",
|
||||
.detect = touchkit_ps2_detect,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_MOUSE_PS2_OLPC
|
||||
{
|
||||
.type = PSMOUSE_HGPK,
|
||||
.name = "OLPC HGPK",
|
||||
.alias = "hgpk",
|
||||
.detect = hgpk_detect,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.type = PSMOUSE_CORTRON,
|
||||
|
@ -935,7 +962,7 @@ static int psmouse_poll(struct psmouse *psmouse)
|
|||
static void psmouse_resync(struct work_struct *work)
|
||||
{
|
||||
struct psmouse *parent = NULL, *psmouse =
|
||||
container_of(work, struct psmouse, resync_work);
|
||||
container_of(work, struct psmouse, resync_work.work);
|
||||
struct serio *serio = psmouse->ps2dev.serio;
|
||||
psmouse_ret_t rc = PSMOUSE_GOOD_DATA;
|
||||
int failed = 0, enabled = 0;
|
||||
|
@ -1194,7 +1221,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
|
|||
goto err_free;
|
||||
|
||||
ps2_init(&psmouse->ps2dev, serio);
|
||||
INIT_WORK(&psmouse->resync_work, psmouse_resync);
|
||||
INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync);
|
||||
psmouse->dev = input_dev;
|
||||
snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys);
|
||||
|
||||
|
@ -1395,25 +1422,29 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
|
|||
|
||||
psmouse = serio_get_drvdata(serio);
|
||||
|
||||
if (psmouse->state == PSMOUSE_IGNORE) {
|
||||
retval = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (attr->protect) {
|
||||
if (psmouse->state == PSMOUSE_IGNORE) {
|
||||
retval = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
|
||||
parent = serio_get_drvdata(serio->parent);
|
||||
psmouse_deactivate(parent);
|
||||
}
|
||||
if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
|
||||
parent = serio_get_drvdata(serio->parent);
|
||||
psmouse_deactivate(parent);
|
||||
}
|
||||
|
||||
psmouse_deactivate(psmouse);
|
||||
psmouse_deactivate(psmouse);
|
||||
}
|
||||
|
||||
retval = attr->set(psmouse, attr->data, buf, count);
|
||||
|
||||
if (retval != -ENODEV)
|
||||
psmouse_activate(psmouse);
|
||||
if (attr->protect) {
|
||||
if (retval != -ENODEV)
|
||||
psmouse_activate(psmouse);
|
||||
|
||||
if (parent)
|
||||
psmouse_activate(parent);
|
||||
if (parent)
|
||||
psmouse_activate(parent);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&psmouse_mutex);
|
||||
|
@ -1433,10 +1464,8 @@ static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const
|
|||
{
|
||||
unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest)
|
||||
if (strict_strtoul(buf, 10, &value))
|
||||
return -EINVAL;
|
||||
|
||||
if ((unsigned int)value != value)
|
||||
|
@ -1549,10 +1578,8 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
|
|||
static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest)
|
||||
if (strict_strtoul(buf, 10, &value))
|
||||
return -EINVAL;
|
||||
|
||||
psmouse->set_rate(psmouse, value);
|
||||
|
@ -1562,10 +1589,8 @@ static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const
|
|||
static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count)
|
||||
{
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest)
|
||||
if (strict_strtoul(buf, 10, &value))
|
||||
return -EINVAL;
|
||||
|
||||
psmouse->set_resolution(psmouse, value);
|
||||
|
|
|
@ -39,7 +39,7 @@ struct psmouse {
|
|||
void *private;
|
||||
struct input_dev *dev;
|
||||
struct ps2dev ps2dev;
|
||||
struct work_struct resync_work;
|
||||
struct delayed_work resync_work;
|
||||
char *vendor;
|
||||
char *name;
|
||||
unsigned char packet[8];
|
||||
|
@ -89,20 +89,24 @@ enum psmouse_type {
|
|||
PSMOUSE_TRACKPOINT,
|
||||
PSMOUSE_TOUCHKIT_PS2,
|
||||
PSMOUSE_CORTRON,
|
||||
PSMOUSE_HGPK,
|
||||
PSMOUSE_AUTO /* This one should always be last */
|
||||
};
|
||||
|
||||
void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
|
||||
unsigned long delay);
|
||||
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
|
||||
int psmouse_reset(struct psmouse *psmouse);
|
||||
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
|
||||
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
|
||||
|
||||
|
||||
struct psmouse_attribute {
|
||||
struct device_attribute dattr;
|
||||
void *data;
|
||||
ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf);
|
||||
ssize_t (*set)(struct psmouse *psmouse, void *data,
|
||||
const char *buf, size_t count);
|
||||
int protect;
|
||||
};
|
||||
#define to_psmouse_attr(a) container_of((a), struct psmouse_attribute, dattr)
|
||||
|
||||
|
@ -111,7 +115,7 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *at
|
|||
ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \
|
||||
#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \
|
||||
static ssize_t _show(struct psmouse *, void *data, char *); \
|
||||
static ssize_t _set(struct psmouse *, void *data, const char *, size_t); \
|
||||
static struct psmouse_attribute psmouse_attr_##_name = { \
|
||||
|
@ -126,6 +130,10 @@ static struct psmouse_attribute psmouse_attr_##_name = { \
|
|||
.data = _data, \
|
||||
.show = _show, \
|
||||
.set = _set, \
|
||||
.protect = _protect, \
|
||||
}
|
||||
|
||||
#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \
|
||||
__PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, 1)
|
||||
|
||||
#endif /* _PSMOUSE_H */
|
||||
|
|
|
@ -89,10 +89,8 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
|
|||
struct trackpoint_attr_data *attr = data;
|
||||
unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || value > 255)
|
||||
if (strict_strtoul(buf, 10, &value) || value > 255)
|
||||
return -EINVAL;
|
||||
|
||||
*field = value;
|
||||
|
@ -117,10 +115,8 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
|
|||
struct trackpoint_attr_data *attr = data;
|
||||
unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
|
||||
unsigned long value;
|
||||
char *rest;
|
||||
|
||||
value = simple_strtoul(buf, &rest, 10);
|
||||
if (*rest || value > 1)
|
||||
if (strict_strtoul(buf, 10, &value) || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->inverted)
|
||||
|
|
|
@ -322,6 +322,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "IBM 2656",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "2656"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -373,6 +373,12 @@ static struct serio_device_id serio_raw_serio_ids[] = {
|
|||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{
|
||||
.type = SERIO_8042_XL,
|
||||
.proto = SERIO_ANY,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
|
|
@ -1202,16 +1202,22 @@ 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;
|
||||
long x;
|
||||
|
||||
if (strict_strtol(buf, 10, &x)) {
|
||||
size_t len = buf[count - 1] == '\n' ? count - 1 : count;
|
||||
|
||||
if (strncmp(buf, "disable", len))
|
||||
return -EINVAL;
|
||||
|
||||
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;
|
||||
}
|
||||
if (x < AIPTEK_TILT_MIN || x > AIPTEK_TILT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
aiptek->newSetting.xTilt = x;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1238,16 +1244,22 @@ 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;
|
||||
long y;
|
||||
|
||||
if (strict_strtol(buf, 10, &y)) {
|
||||
size_t len = buf[count - 1] == '\n' ? count - 1 : count;
|
||||
|
||||
if (strncmp(buf, "disable", len))
|
||||
return -EINVAL;
|
||||
|
||||
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;
|
||||
}
|
||||
if (y < AIPTEK_TILT_MIN || y > AIPTEK_TILT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
aiptek->newSetting.yTilt = y;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1269,8 +1281,12 @@ 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);
|
||||
long j;
|
||||
|
||||
aiptek->newSetting.jitterDelay = (int)simple_strtol(buf, NULL, 10);
|
||||
if (strict_strtol(buf, 10, &j))
|
||||
return -EINVAL;
|
||||
|
||||
aiptek->newSetting.jitterDelay = (int)j;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1294,8 +1310,12 @@ 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);
|
||||
long d;
|
||||
|
||||
aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, NULL, 10);
|
||||
if (strict_strtol(buf, 10, &d))
|
||||
return -EINVAL;
|
||||
|
||||
aiptek->newSetting.programmableDelay = (int)d;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1541,8 +1561,11 @@ 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);
|
||||
long w;
|
||||
|
||||
aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10);
|
||||
if (strict_strtol(buf, 10, &w)) return -EINVAL;
|
||||
|
||||
aiptek->newSetting.wheel = (int)w;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,17 @@ struct ts_event {
|
|||
int ignore;
|
||||
};
|
||||
|
||||
/*
|
||||
* We allocate this separately to avoid cache line sharing issues when
|
||||
* driver is used with DMA-based SPI controllers (like atmel_spi) on
|
||||
* systems where main memory is not DMA-coherent (most non-x86 boards).
|
||||
*/
|
||||
struct ads7846_packet {
|
||||
u8 read_x, read_y, read_z1, read_z2, pwrdown;
|
||||
u16 dummy; /* for the pwrdown read */
|
||||
struct ts_event tc;
|
||||
};
|
||||
|
||||
struct ads7846 {
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
|
@ -86,9 +97,7 @@ struct ads7846 {
|
|||
u16 x_plate_ohms;
|
||||
u16 pressure_max;
|
||||
|
||||
u8 read_x, read_y, read_z1, read_z2, pwrdown;
|
||||
u16 dummy; /* for the pwrdown read */
|
||||
struct ts_event tc;
|
||||
struct ads7846_packet *packet;
|
||||
|
||||
struct spi_transfer xfer[18];
|
||||
struct spi_message msg[5];
|
||||
|
@ -463,10 +472,11 @@ static ssize_t ads7846_disable_store(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct ads7846 *ts = dev_get_drvdata(dev);
|
||||
char *endp;
|
||||
int i;
|
||||
long i;
|
||||
|
||||
if (strict_strtoul(buf, 10, &i))
|
||||
return -EINVAL;
|
||||
|
||||
i = simple_strtoul(buf, &endp, 10);
|
||||
spin_lock_irq(&ts->lock);
|
||||
|
||||
if (i)
|
||||
|
@ -512,16 +522,17 @@ static int get_pendown_state(struct ads7846 *ts)
|
|||
static void ads7846_rx(void *ads)
|
||||
{
|
||||
struct ads7846 *ts = ads;
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
unsigned Rt;
|
||||
u16 x, y, z1, z2;
|
||||
|
||||
/* ads7846_rx_val() did in-place conversion (including byteswap) from
|
||||
* on-the-wire format as part of debouncing to get stable readings.
|
||||
*/
|
||||
x = ts->tc.x;
|
||||
y = ts->tc.y;
|
||||
z1 = ts->tc.z1;
|
||||
z2 = ts->tc.z2;
|
||||
x = packet->tc.x;
|
||||
y = packet->tc.y;
|
||||
z1 = packet->tc.z1;
|
||||
z2 = packet->tc.z2;
|
||||
|
||||
/* range filtering */
|
||||
if (x == MAX_12BIT)
|
||||
|
@ -545,10 +556,10 @@ static void ads7846_rx(void *ads)
|
|||
* the maximum. Don't report it to user space, repeat at least
|
||||
* once more the measurement
|
||||
*/
|
||||
if (ts->tc.ignore || Rt > ts->pressure_max) {
|
||||
if (packet->tc.ignore || Rt > ts->pressure_max) {
|
||||
#ifdef VERBOSE
|
||||
pr_debug("%s: ignored %d pressure %d\n",
|
||||
ts->spi->dev.bus_id, ts->tc.ignore, Rt);
|
||||
ts->spi->dev.bus_id, packet->tc.ignore, Rt);
|
||||
#endif
|
||||
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
|
||||
HRTIMER_MODE_REL);
|
||||
|
@ -641,6 +652,7 @@ static int ads7846_no_filter(void *ads, int data_idx, int *val)
|
|||
static void ads7846_rx_val(void *ads)
|
||||
{
|
||||
struct ads7846 *ts = ads;
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
struct spi_message *m;
|
||||
struct spi_transfer *t;
|
||||
int val;
|
||||
|
@ -660,7 +672,7 @@ static void ads7846_rx_val(void *ads)
|
|||
case ADS7846_FILTER_REPEAT:
|
||||
break;
|
||||
case ADS7846_FILTER_IGNORE:
|
||||
ts->tc.ignore = 1;
|
||||
packet->tc.ignore = 1;
|
||||
/* Last message will contain ads7846_rx() as the
|
||||
* completion function.
|
||||
*/
|
||||
|
@ -668,7 +680,7 @@ static void ads7846_rx_val(void *ads)
|
|||
break;
|
||||
case ADS7846_FILTER_OK:
|
||||
*(u16 *)t->rx_buf = val;
|
||||
ts->tc.ignore = 0;
|
||||
packet->tc.ignore = 0;
|
||||
m = &ts->msg[++ts->msg_idx];
|
||||
break;
|
||||
default:
|
||||
|
@ -773,7 +785,6 @@ static void ads7846_disable(struct ads7846 *ts)
|
|||
/* we know the chip's in lowpower mode since we always
|
||||
* leave it that way after every request
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/* Must be called with ts->lock held */
|
||||
|
@ -849,6 +860,7 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)
|
|||
static int __devinit ads7846_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ads7846 *ts;
|
||||
struct ads7846_packet *packet;
|
||||
struct input_dev *input_dev;
|
||||
struct ads7846_platform_data *pdata = spi->dev.platform_data;
|
||||
struct spi_message *m;
|
||||
|
@ -884,14 +896,16 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
return err;
|
||||
|
||||
ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
|
||||
packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ts || !input_dev) {
|
||||
if (!ts || !packet || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&spi->dev, ts);
|
||||
|
||||
ts->packet = packet;
|
||||
ts->spi = spi;
|
||||
ts->input = input_dev;
|
||||
ts->vref_mv = pdata->vref_mv;
|
||||
|
@ -963,13 +977,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
spi_message_init(m);
|
||||
|
||||
/* y- still on; turn on only y+ (and ADC) */
|
||||
ts->read_y = READ_Y(vref);
|
||||
x->tx_buf = &ts->read_y;
|
||||
packet->read_y = READ_Y(vref);
|
||||
x->tx_buf = &packet->read_y;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->tc.y;
|
||||
x->rx_buf = &packet->tc.y;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
|
@ -981,12 +995,12 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
x->delay_usecs = pdata->settle_delay_usecs;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &ts->read_y;
|
||||
x->tx_buf = &packet->read_y;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->tc.y;
|
||||
x->rx_buf = &packet->tc.y;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
@ -999,13 +1013,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
|
||||
/* turn y- off, x+ on, then leave in lowpower */
|
||||
x++;
|
||||
ts->read_x = READ_X(vref);
|
||||
x->tx_buf = &ts->read_x;
|
||||
packet->read_x = READ_X(vref);
|
||||
x->tx_buf = &packet->read_x;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->tc.x;
|
||||
x->rx_buf = &packet->tc.x;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
|
@ -1014,12 +1028,12 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
x->delay_usecs = pdata->settle_delay_usecs;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &ts->read_x;
|
||||
x->tx_buf = &packet->read_x;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->tc.x;
|
||||
x->rx_buf = &packet->tc.x;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
@ -1033,13 +1047,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
spi_message_init(m);
|
||||
|
||||
x++;
|
||||
ts->read_z1 = READ_Z1(vref);
|
||||
x->tx_buf = &ts->read_z1;
|
||||
packet->read_z1 = READ_Z1(vref);
|
||||
x->tx_buf = &packet->read_z1;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->tc.z1;
|
||||
x->rx_buf = &packet->tc.z1;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
|
@ -1048,12 +1062,12 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
x->delay_usecs = pdata->settle_delay_usecs;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &ts->read_z1;
|
||||
x->tx_buf = &packet->read_z1;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->tc.z1;
|
||||
x->rx_buf = &packet->tc.z1;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
@ -1065,13 +1079,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
spi_message_init(m);
|
||||
|
||||
x++;
|
||||
ts->read_z2 = READ_Z2(vref);
|
||||
x->tx_buf = &ts->read_z2;
|
||||
packet->read_z2 = READ_Z2(vref);
|
||||
x->tx_buf = &packet->read_z2;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->tc.z2;
|
||||
x->rx_buf = &packet->tc.z2;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
|
@ -1080,12 +1094,12 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
x->delay_usecs = pdata->settle_delay_usecs;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &ts->read_z2;
|
||||
x->tx_buf = &packet->read_z2;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->tc.z2;
|
||||
x->rx_buf = &packet->tc.z2;
|
||||
x->len = 2;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
@ -1099,13 +1113,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
spi_message_init(m);
|
||||
|
||||
x++;
|
||||
ts->pwrdown = PWRDOWN;
|
||||
x->tx_buf = &ts->pwrdown;
|
||||
packet->pwrdown = PWRDOWN;
|
||||
x->tx_buf = &packet->pwrdown;
|
||||
x->len = 1;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
x++;
|
||||
x->rx_buf = &ts->dummy;
|
||||
x->rx_buf = &packet->dummy;
|
||||
x->len = 2;
|
||||
CS_CHANGE(*x);
|
||||
spi_message_add_tail(x, m);
|
||||
|
@ -1158,6 +1172,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
ts->filter_cleanup(ts->filter_data);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(packet);
|
||||
kfree(ts);
|
||||
return err;
|
||||
}
|
||||
|
@ -1183,6 +1198,7 @@ static int __devexit ads7846_remove(struct spi_device *spi)
|
|||
if (ts->filter_cleanup)
|
||||
ts->filter_cleanup(ts->filter_data);
|
||||
|
||||
kfree(ts->packet);
|
||||
kfree(ts);
|
||||
|
||||
dev_dbg(&spi->dev, "unregistered touchscreen\n");
|
||||
|
|
|
@ -91,6 +91,9 @@ struct atmel_tsadcc {
|
|||
char phys[32];
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
unsigned int prev_absx;
|
||||
unsigned int prev_absy;
|
||||
unsigned char bufferedmeasure;
|
||||
};
|
||||
|
||||
static void __iomem *tsc_base;
|
||||
|
@ -100,10 +103,9 @@ static void __iomem *tsc_base;
|
|||
|
||||
static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev)
|
||||
{
|
||||
struct input_dev *input_dev = ((struct atmel_tsadcc *)dev)->input;
|
||||
struct atmel_tsadcc *ts_dev = (struct atmel_tsadcc *)dev;
|
||||
struct input_dev *input_dev = ts_dev->input;
|
||||
|
||||
unsigned int absx;
|
||||
unsigned int absy;
|
||||
unsigned int status;
|
||||
unsigned int reg;
|
||||
|
||||
|
@ -121,6 +123,7 @@ static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev)
|
|||
atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT);
|
||||
|
||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||
ts_dev->bufferedmeasure = 0;
|
||||
input_sync(input_dev);
|
||||
|
||||
} else if (status & ATMEL_TSADCC_PENCNT) {
|
||||
|
@ -138,16 +141,23 @@ static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev)
|
|||
} else if (status & ATMEL_TSADCC_EOC(3)) {
|
||||
/* Conversion finished */
|
||||
|
||||
absx = atmel_tsadcc_read(ATMEL_TSADCC_CDR3) << 10;
|
||||
absx /= atmel_tsadcc_read(ATMEL_TSADCC_CDR2);
|
||||
if (ts_dev->bufferedmeasure) {
|
||||
/* Last measurement is always discarded, since it can
|
||||
* be erroneous.
|
||||
* Always report previous measurement */
|
||||
input_report_abs(input_dev, ABS_X, ts_dev->prev_absx);
|
||||
input_report_abs(input_dev, ABS_Y, ts_dev->prev_absy);
|
||||
input_report_key(input_dev, BTN_TOUCH, 1);
|
||||
input_sync(input_dev);
|
||||
} else
|
||||
ts_dev->bufferedmeasure = 1;
|
||||
|
||||
absy = atmel_tsadcc_read(ATMEL_TSADCC_CDR1) << 10;
|
||||
absy /= atmel_tsadcc_read(ATMEL_TSADCC_CDR0);
|
||||
/* Now make new measurement */
|
||||
ts_dev->prev_absx = atmel_tsadcc_read(ATMEL_TSADCC_CDR3) << 10;
|
||||
ts_dev->prev_absx /= atmel_tsadcc_read(ATMEL_TSADCC_CDR2);
|
||||
|
||||
input_report_abs(input_dev, ABS_X, absx);
|
||||
input_report_abs(input_dev, ABS_Y, absy);
|
||||
input_report_key(input_dev, BTN_TOUCH, 1);
|
||||
input_sync(input_dev);
|
||||
ts_dev->prev_absy = atmel_tsadcc_read(ATMEL_TSADCC_CDR1) << 10;
|
||||
ts_dev->prev_absy /= atmel_tsadcc_read(ATMEL_TSADCC_CDR0);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -223,6 +233,7 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
ts_dev->input = input_dev;
|
||||
ts_dev->bufferedmeasure = 0;
|
||||
|
||||
snprintf(ts_dev->phys, sizeof(ts_dev->phys),
|
||||
"%s/input0", pdev->dev.bus_id);
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
* Wolfson WM97xx AC97 Codecs.
|
||||
*
|
||||
* Copyright 2004, 2007 Wolfson Microelectronics PLC.
|
||||
* Author: Liam Girdwood
|
||||
* liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
|
||||
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Parts Copyright : Ian Molton <spyro@f2s.com>
|
||||
* Andrew Zabolotny <zap@homelink.ru>
|
||||
*
|
||||
|
@ -296,6 +295,6 @@ module_init(mainstone_wm97xx_init);
|
|||
module_exit(mainstone_wm97xx_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
|
||||
MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec.
|
||||
*
|
||||
* Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
|
||||
* Author: Liam Girdwood
|
||||
* liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
|
||||
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Parts Copyright : Ian Molton <spyro@f2s.com>
|
||||
* Andrew Zabolotny <zap@homelink.ru>
|
||||
* Russell King <rmk@arm.linux.org.uk>
|
||||
|
@ -347,6 +346,6 @@ struct wm97xx_codec_drv wm9705_codec = {
|
|||
EXPORT_SYMBOL_GPL(wm9705_codec);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
|
||||
MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs.
|
||||
*
|
||||
* Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
|
||||
* Author: Liam Girdwood
|
||||
* liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
|
||||
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Parts Copyright : Ian Molton <spyro@f2s.com>
|
||||
* Andrew Zabolotny <zap@homelink.ru>
|
||||
* Russell King <rmk@arm.linux.org.uk>
|
||||
|
@ -462,6 +461,6 @@ struct wm97xx_codec_drv wm9712_codec = {
|
|||
EXPORT_SYMBOL_GPL(wm9712_codec);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
|
||||
MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec.
|
||||
*
|
||||
* Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
|
||||
* Author: Liam Girdwood
|
||||
* liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
|
||||
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Parts Copyright : Ian Molton <spyro@f2s.com>
|
||||
* Andrew Zabolotny <zap@homelink.ru>
|
||||
* Russell King <rmk@arm.linux.org.uk>
|
||||
|
@ -476,6 +475,6 @@ struct wm97xx_codec_drv wm9713_codec = {
|
|||
EXPORT_SYMBOL_GPL(wm9713_codec);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
|
||||
MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
* and WM9713 AC97 Codecs.
|
||||
*
|
||||
* Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
|
||||
* Author: Liam Girdwood
|
||||
* liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
|
||||
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
|
||||
* Parts Copyright : Ian Molton <spyro@f2s.com>
|
||||
* Andrew Zabolotny <zap@homelink.ru>
|
||||
* Russell King <rmk@arm.linux.org.uk>
|
||||
|
@ -824,6 +823,6 @@ module_init(wm97xx_init);
|
|||
module_exit(wm97xx_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
|
||||
MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -107,6 +107,7 @@ header-y += keyctl.h
|
|||
header-y += limits.h
|
||||
header-y += magic.h
|
||||
header-y += major.h
|
||||
header-y += map_to_7segment.h
|
||||
header-y += matroxfb.h
|
||||
header-y += meye.h
|
||||
header-y += minix_fs.h
|
||||
|
|
|
@ -146,10 +146,11 @@ static inline void gameport_unpin_driver(struct gameport *gameport)
|
|||
mutex_unlock(&gameport->drv_mutex);
|
||||
}
|
||||
|
||||
void __gameport_register_driver(struct gameport_driver *drv, struct module *owner);
|
||||
static inline void gameport_register_driver(struct gameport_driver *drv)
|
||||
int __gameport_register_driver(struct gameport_driver *drv,
|
||||
struct module *owner, const char *mod_name);
|
||||
static inline int __must_check gameport_register_driver(struct gameport_driver *drv)
|
||||
{
|
||||
__gameport_register_driver(drv, THIS_MODULE);
|
||||
return __gameport_register_driver(drv, THIS_MODULE, KBUILD_MODNAME);
|
||||
}
|
||||
|
||||
void gameport_unregister_driver(struct gameport_driver *drv);
|
||||
|
|
|
@ -577,9 +577,22 @@ struct input_absinfo {
|
|||
#define KEY_BRL_DOT9 0x1f9
|
||||
#define KEY_BRL_DOT10 0x1fa
|
||||
|
||||
#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */
|
||||
#define KEY_NUMERIC_1 0x201 /* and other keypads */
|
||||
#define KEY_NUMERIC_2 0x202
|
||||
#define KEY_NUMERIC_3 0x203
|
||||
#define KEY_NUMERIC_4 0x204
|
||||
#define KEY_NUMERIC_5 0x205
|
||||
#define KEY_NUMERIC_6 0x206
|
||||
#define KEY_NUMERIC_7 0x207
|
||||
#define KEY_NUMERIC_8 0x208
|
||||
#define KEY_NUMERIC_9 0x209
|
||||
#define KEY_NUMERIC_STAR 0x20a
|
||||
#define KEY_NUMERIC_POUND 0x20b
|
||||
|
||||
/* We avoid low common keys in module aliases so they don't get huge. */
|
||||
#define KEY_MIN_INTERESTING KEY_MUTE
|
||||
#define KEY_MAX 0x1ff
|
||||
#define KEY_MAX 0x2ff
|
||||
#define KEY_CNT (KEY_MAX+1)
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -36,7 +34,7 @@
|
|||
* Usage:
|
||||
*
|
||||
* Register a map variable, and fill it with a character set:
|
||||
* static SEG7_DEFAULT_MAP(map_seg7);
|
||||
* static SEG7_DEFAULT_MAP(map_seg7);
|
||||
*
|
||||
*
|
||||
* Then use for conversion:
|
|
@ -284,7 +284,7 @@ struct pcmcia_device_id {
|
|||
/* Input */
|
||||
#define INPUT_DEVICE_ID_EV_MAX 0x1f
|
||||
#define INPUT_DEVICE_ID_KEY_MIN_INTERESTING 0x71
|
||||
#define INPUT_DEVICE_ID_KEY_MAX 0x1ff
|
||||
#define INPUT_DEVICE_ID_KEY_MAX 0x2ff
|
||||
#define INPUT_DEVICE_ID_REL_MAX 0x0f
|
||||
#define INPUT_DEVICE_ID_ABS_MAX 0x3f
|
||||
#define INPUT_DEVICE_ID_MSC_MAX 0x07
|
||||
|
|
Загрузка…
Ссылка в новой задаче