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: (29 commits) Input: i8042 - add Dell Vostro 1510 to nomux list Input: gtco - use USB endpoint API Input: add support for Maple controller as a joystick Input: atkbd - broaden the Dell DMI signatures Input: HIL drivers - add MODULE_ALIAS() Input: map_to_7segment.h - convert to __inline__ for userspace Input: add support for enhanced rotary controller on pxa930 and pxa935 Input: add support for trackball on pxa930 and pxa935 Input: add da9034 touchscreen support Input: ads7846 - strict_strtoul takes unsigned long Input: make some variables and functions static Input: add tsc2007 based touchscreen driver Input: psmouse - add module parameters to control OLPC touchpad delays Input: i8042 - add Gigabyte M912 netbook to noloop exception table Input: atkbd - Samsung NC10 key repeat fix Input: atkbd - add keyboard quirk for HP Pavilion ZV6100 laptop Input: libps2 - handle 0xfc responses from devices Input: add support for Wacom W8001 penabled serial touchscreen Input: synaptics - report multi-taps only if supported by the device Input: add joystick driver for Walkera WK-0701 RC transmitter ...
This commit is contained in:
commit
db30c70575
54 changed files with 3104 additions and 521 deletions
109
Documentation/input/walkera0701.txt
Normal file
109
Documentation/input/walkera0701.txt
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
|
||||||
|
Walkera WK-0701 transmitter is supplied with a ready to fly Walkera
|
||||||
|
helicopters such as HM36, HM37, HM60. The walkera0701 module enables to use
|
||||||
|
this transmitter as joystick
|
||||||
|
|
||||||
|
Devel homepage and download:
|
||||||
|
http://zub.fei.tuke.sk/walkera-wk0701/
|
||||||
|
|
||||||
|
or use cogito:
|
||||||
|
cg-clone http://zub.fei.tuke.sk/GIT/walkera0701-joystick
|
||||||
|
|
||||||
|
|
||||||
|
Connecting to PC:
|
||||||
|
|
||||||
|
At back side of transmitter S-video connector can be found. Modulation
|
||||||
|
pulses from processor to HF part can be found at pin 2 of this connector,
|
||||||
|
pin 3 is GND. Between pin 3 and CPU 5k6 resistor can be found. To get
|
||||||
|
modulation pulses to PC, signal pulses must be amplified.
|
||||||
|
|
||||||
|
Cable: (walkera TX to parport)
|
||||||
|
|
||||||
|
Walkera WK-0701 TX S-VIDEO connector:
|
||||||
|
(back side of TX)
|
||||||
|
__ __ S-video: canon25
|
||||||
|
/ |_| \ pin 2 (signal) NPN parport
|
||||||
|
/ O 4 3 O \ pin 3 (GND) LED ________________ 10 ACK
|
||||||
|
( O 2 1 O ) | C
|
||||||
|
\ ___ / 2 ________________________|\|_____|/
|
||||||
|
| [___] | |/| B |\
|
||||||
|
------- 3 __________________________________|________________ 25 GND
|
||||||
|
E
|
||||||
|
|
||||||
|
|
||||||
|
I use green LED and BC109 NPN transistor.
|
||||||
|
|
||||||
|
Software:
|
||||||
|
|
||||||
|
Build kernel with walkera0701 module. Module walkera0701 need exclusive
|
||||||
|
access to parport, modules like lp must be unloaded before loading
|
||||||
|
walkera0701 module, check dmesg for error messages. Connect TX to PC by
|
||||||
|
cable and run jstest /dev/input/js0 to see values from TX. If no value can
|
||||||
|
be changed by TX "joystick", check output from /proc/interrupts. Value for
|
||||||
|
(usually irq7) parport must increase if TX is on.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Technical details:
|
||||||
|
|
||||||
|
Driver use interrupt from parport ACK input bit to measure pulse length
|
||||||
|
using hrtimers.
|
||||||
|
|
||||||
|
Frame format:
|
||||||
|
Based on walkera WK-0701 PCM Format description by Shaul Eizikovich.
|
||||||
|
(downloaded from http://www.smartpropoplus.com/Docs/Walkera_Wk-0701_PCM.pdf)
|
||||||
|
|
||||||
|
Signal pulses:
|
||||||
|
(ANALOG)
|
||||||
|
SYNC BIN OCT
|
||||||
|
+---------+ +------+
|
||||||
|
| | | |
|
||||||
|
--+ +------+ +---
|
||||||
|
|
||||||
|
Frame:
|
||||||
|
SYNC , BIN1, OCT1, BIN2, OCT2 ... BIN24, OCT24, BIN25, next frame SYNC ..
|
||||||
|
|
||||||
|
pulse length:
|
||||||
|
Binary values: Analog octal values:
|
||||||
|
|
||||||
|
288 uS Binary 0 318 uS 000
|
||||||
|
438 uS Binary 1 398 uS 001
|
||||||
|
478 uS 010
|
||||||
|
558 uS 011
|
||||||
|
638 uS 100
|
||||||
|
1306 uS SYNC 718 uS 101
|
||||||
|
798 uS 110
|
||||||
|
878 uS 111
|
||||||
|
|
||||||
|
24 bin+oct values + 1 bin value = 24*4+1 bits = 97 bits
|
||||||
|
|
||||||
|
(Warning, pulses on ACK ar inverted by transistor, irq is rised up on sync
|
||||||
|
to bin change or octal value to bin change).
|
||||||
|
|
||||||
|
Binary data representations:
|
||||||
|
|
||||||
|
One binary and octal value can be grouped to nibble. 24 nibbles + one binary
|
||||||
|
values can be sampled between sync pulses.
|
||||||
|
|
||||||
|
Values for first four channels (analog joystick values) can be found in
|
||||||
|
first 10 nibbles. Analog value is represented by one sign bit and 9 bit
|
||||||
|
absolute binary value. (10 bits per channel). Next nibble is checksum for
|
||||||
|
first ten nibbles.
|
||||||
|
|
||||||
|
Next nibbles 12 .. 21 represents four channels (not all channels can be
|
||||||
|
directly controlled from TX). Binary representations ar the same as in first
|
||||||
|
four channels. In nibbles 22 and 23 is a special magic number. Nibble 24 is
|
||||||
|
checksum for nibbles 12..23.
|
||||||
|
|
||||||
|
After last octal value for nibble 24 and next sync pulse one additional
|
||||||
|
binary value can be sampled. This bit and magic number is not used in
|
||||||
|
software driver. Some details about this magic numbers can be found in
|
||||||
|
Walkera_Wk-0701_PCM.pdf.
|
||||||
|
|
||||||
|
Checksum calculation:
|
||||||
|
|
||||||
|
Summary of octal values in nibbles must be same as octal value in checksum
|
||||||
|
nibble (only first 3 bits are used). Binary value for checksum nibble is
|
||||||
|
calculated by sum of binary values in checked nibbles + sum of octal values
|
||||||
|
in checked nibbles divided by 8. Only bit 0 of this sum is used.
|
||||||
|
|
20
arch/arm/mach-pxa/include/mach/pxa930_rotary.h
Normal file
20
arch/arm/mach-pxa/include/mach/pxa930_rotary.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef __ASM_ARCH_PXA930_ROTARY_H
|
||||||
|
#define __ASM_ARCH_PXA930_ROTARY_H
|
||||||
|
|
||||||
|
/* NOTE:
|
||||||
|
*
|
||||||
|
* rotary can be either interpreted as a ralative input event (e.g.
|
||||||
|
* REL_WHEEL or REL_HWHEEL) or a specific key event (e.g. UP/DOWN
|
||||||
|
* or LEFT/RIGHT), depending on if up_key & down_key are assigned
|
||||||
|
* or rel_code is assigned a non-zero value. When all are non-zero,
|
||||||
|
* up_key and down_key will be preferred.
|
||||||
|
*/
|
||||||
|
struct pxa930_rotary_platform_data {
|
||||||
|
int up_key;
|
||||||
|
int down_key;
|
||||||
|
int rel_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
void __init pxa930_set_rotarykey_info(struct pxa930_rotary_platform_data *info);
|
||||||
|
|
||||||
|
#endif /* __ASM_ARCH_PXA930_ROTARY_H */
|
10
arch/arm/mach-pxa/include/mach/pxa930_trkball.h
Normal file
10
arch/arm/mach-pxa/include/mach/pxa930_trkball.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef __ASM_ARCH_PXA930_TRKBALL_H
|
||||||
|
#define __ASM_ARCH_PXA930_TRKBALL_H
|
||||||
|
|
||||||
|
struct pxa930_trkball_platform_data {
|
||||||
|
int x_filter;
|
||||||
|
int y_filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __ASM_ARCH_PXA930_TRKBALL_H */
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# Each configuration option enables a list of files.
|
# Each configuration option enables a list of files.
|
||||||
|
|
||||||
obj-$(CONFIG_INPUT) += input-core.o
|
obj-$(CONFIG_INPUT) += input-core.o
|
||||||
input-core-objs := input.o ff-core.o
|
input-core-objs := input.o input-compat.o ff-core.o
|
||||||
|
|
||||||
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
|
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
|
||||||
obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
|
obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
|
||||||
|
|
|
@ -39,7 +39,7 @@ MODULE_LICENSE("GPL");
|
||||||
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
|
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
|
printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
|
||||||
handle->dev->dev.bus_id, type, code, value);
|
dev_name(&handle->dev->dev), type, code, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
|
static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
|
@ -65,7 +65,7 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
goto err_unregister_handle;
|
goto err_unregister_handle;
|
||||||
|
|
||||||
printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n",
|
printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n",
|
||||||
dev->dev.bus_id,
|
dev_name(&dev->dev),
|
||||||
dev->name ?: "unknown",
|
dev->name ?: "unknown",
|
||||||
dev->phys ?: "unknown");
|
dev->phys ?: "unknown");
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
static void evbug_disconnect(struct input_handle *handle)
|
static void evbug_disconnect(struct input_handle *handle)
|
||||||
{
|
{
|
||||||
printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n",
|
printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n",
|
||||||
handle->dev->dev.bus_id);
|
dev_name(&handle->dev->dev));
|
||||||
|
|
||||||
input_close_device(handle);
|
input_close_device(handle);
|
||||||
input_unregister_handle(handle);
|
input_unregister_handle(handle);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/major.h>
|
#include <linux/major.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/compat.h>
|
#include "input-compat.h"
|
||||||
|
|
||||||
struct evdev {
|
struct evdev {
|
||||||
int exist;
|
int exist;
|
||||||
|
@ -290,187 +290,6 @@ static int evdev_open(struct inode *inode, struct file *file)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
|
|
||||||
struct input_event_compat {
|
|
||||||
struct compat_timeval time;
|
|
||||||
__u16 type;
|
|
||||||
__u16 code;
|
|
||||||
__s32 value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ff_periodic_effect_compat {
|
|
||||||
__u16 waveform;
|
|
||||||
__u16 period;
|
|
||||||
__s16 magnitude;
|
|
||||||
__s16 offset;
|
|
||||||
__u16 phase;
|
|
||||||
|
|
||||||
struct ff_envelope envelope;
|
|
||||||
|
|
||||||
__u32 custom_len;
|
|
||||||
compat_uptr_t custom_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ff_effect_compat {
|
|
||||||
__u16 type;
|
|
||||||
__s16 id;
|
|
||||||
__u16 direction;
|
|
||||||
struct ff_trigger trigger;
|
|
||||||
struct ff_replay replay;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct ff_constant_effect constant;
|
|
||||||
struct ff_ramp_effect ramp;
|
|
||||||
struct ff_periodic_effect_compat periodic;
|
|
||||||
struct ff_condition_effect condition[2]; /* One for each axis */
|
|
||||||
struct ff_rumble_effect rumble;
|
|
||||||
} u;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Note to the author of this code: did it ever occur to
|
|
||||||
you why the ifdefs are needed? Think about it again. -AK */
|
|
||||||
#ifdef CONFIG_X86_64
|
|
||||||
# define COMPAT_TEST is_compat_task()
|
|
||||||
#elif defined(CONFIG_IA64)
|
|
||||||
# define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
|
|
||||||
#elif defined(CONFIG_S390)
|
|
||||||
# define COMPAT_TEST test_thread_flag(TIF_31BIT)
|
|
||||||
#elif defined(CONFIG_MIPS)
|
|
||||||
# define COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR)
|
|
||||||
#else
|
|
||||||
# define COMPAT_TEST test_thread_flag(TIF_32BIT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline size_t evdev_event_size(void)
|
|
||||||
{
|
|
||||||
return COMPAT_TEST ?
|
|
||||||
sizeof(struct input_event_compat) : sizeof(struct input_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int evdev_event_from_user(const char __user *buffer,
|
|
||||||
struct input_event *event)
|
|
||||||
{
|
|
||||||
if (COMPAT_TEST) {
|
|
||||||
struct input_event_compat compat_event;
|
|
||||||
|
|
||||||
if (copy_from_user(&compat_event, buffer,
|
|
||||||
sizeof(struct input_event_compat)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
event->time.tv_sec = compat_event.time.tv_sec;
|
|
||||||
event->time.tv_usec = compat_event.time.tv_usec;
|
|
||||||
event->type = compat_event.type;
|
|
||||||
event->code = compat_event.code;
|
|
||||||
event->value = compat_event.value;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (copy_from_user(event, buffer, sizeof(struct input_event)))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int evdev_event_to_user(char __user *buffer,
|
|
||||||
const struct input_event *event)
|
|
||||||
{
|
|
||||||
if (COMPAT_TEST) {
|
|
||||||
struct input_event_compat compat_event;
|
|
||||||
|
|
||||||
compat_event.time.tv_sec = event->time.tv_sec;
|
|
||||||
compat_event.time.tv_usec = event->time.tv_usec;
|
|
||||||
compat_event.type = event->type;
|
|
||||||
compat_event.code = event->code;
|
|
||||||
compat_event.value = event->value;
|
|
||||||
|
|
||||||
if (copy_to_user(buffer, &compat_event,
|
|
||||||
sizeof(struct input_event_compat)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (copy_to_user(buffer, event, sizeof(struct input_event)))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int evdev_ff_effect_from_user(const char __user *buffer, size_t size,
|
|
||||||
struct ff_effect *effect)
|
|
||||||
{
|
|
||||||
if (COMPAT_TEST) {
|
|
||||||
struct ff_effect_compat *compat_effect;
|
|
||||||
|
|
||||||
if (size != sizeof(struct ff_effect_compat))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* It so happens that the pointer which needs to be changed
|
|
||||||
* is the last field in the structure, so we can copy the
|
|
||||||
* whole thing and replace just the pointer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
compat_effect = (struct ff_effect_compat *)effect;
|
|
||||||
|
|
||||||
if (copy_from_user(compat_effect, buffer,
|
|
||||||
sizeof(struct ff_effect_compat)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (compat_effect->type == FF_PERIODIC &&
|
|
||||||
compat_effect->u.periodic.waveform == FF_CUSTOM)
|
|
||||||
effect->u.periodic.custom_data =
|
|
||||||
compat_ptr(compat_effect->u.periodic.custom_data);
|
|
||||||
} else {
|
|
||||||
if (size != sizeof(struct ff_effect))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static inline size_t evdev_event_size(void)
|
|
||||||
{
|
|
||||||
return sizeof(struct input_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int evdev_event_from_user(const char __user *buffer,
|
|
||||||
struct input_event *event)
|
|
||||||
{
|
|
||||||
if (copy_from_user(event, buffer, sizeof(struct input_event)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int evdev_event_to_user(char __user *buffer,
|
|
||||||
const struct input_event *event)
|
|
||||||
{
|
|
||||||
if (copy_to_user(buffer, event, sizeof(struct input_event)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int evdev_ff_effect_from_user(const char __user *buffer, size_t size,
|
|
||||||
struct ff_effect *effect)
|
|
||||||
{
|
|
||||||
if (size != sizeof(struct ff_effect))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CONFIG_COMPAT */
|
|
||||||
|
|
||||||
static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
||||||
size_t count, loff_t *ppos)
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
|
@ -490,14 +309,14 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
||||||
|
|
||||||
while (retval < count) {
|
while (retval < count) {
|
||||||
|
|
||||||
if (evdev_event_from_user(buffer + retval, &event)) {
|
if (input_event_from_user(buffer + retval, &event)) {
|
||||||
retval = -EFAULT;
|
retval = -EFAULT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_inject_event(&evdev->handle,
|
input_inject_event(&evdev->handle,
|
||||||
event.type, event.code, event.value);
|
event.type, event.code, event.value);
|
||||||
retval += evdev_event_size();
|
retval += input_event_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -531,7 +350,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
|
||||||
struct input_event event;
|
struct input_event event;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (count < evdev_event_size())
|
if (count < input_event_size())
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (client->head == client->tail && evdev->exist &&
|
if (client->head == client->tail && evdev->exist &&
|
||||||
|
@ -546,13 +365,13 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
|
||||||
if (!evdev->exist)
|
if (!evdev->exist)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
while (retval + evdev_event_size() <= count &&
|
while (retval + input_event_size() <= count &&
|
||||||
evdev_fetch_next_event(client, &event)) {
|
evdev_fetch_next_event(client, &event)) {
|
||||||
|
|
||||||
if (evdev_event_to_user(buffer + retval, &event))
|
if (input_event_to_user(buffer + retval, &event))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
retval += evdev_event_size();
|
retval += input_event_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -823,7 +642,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||||
|
|
||||||
if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) {
|
if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) {
|
||||||
|
|
||||||
if (evdev_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
|
if (input_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
error = input_ff_upload(dev, &effect, file);
|
error = input_ff_upload(dev, &effect, file);
|
||||||
|
@ -1000,7 +819,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
evdev->handle.handler = handler;
|
evdev->handle.handler = handler;
|
||||||
evdev->handle.private = evdev;
|
evdev->handle.private = evdev;
|
||||||
|
|
||||||
strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
|
dev_set_name(&evdev->dev, evdev->name);
|
||||||
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
|
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
|
||||||
evdev->dev.class = &input_class;
|
evdev->dev.class = &input_class;
|
||||||
evdev->dev.parent = &dev->dev;
|
evdev->dev.parent = &dev->dev;
|
||||||
|
|
|
@ -530,8 +530,7 @@ static void gameport_init_port(struct gameport *gameport)
|
||||||
|
|
||||||
mutex_init(&gameport->drv_mutex);
|
mutex_init(&gameport->drv_mutex);
|
||||||
device_initialize(&gameport->dev);
|
device_initialize(&gameport->dev);
|
||||||
snprintf(gameport->dev.bus_id, sizeof(gameport->dev.bus_id),
|
dev_set_name(&gameport->dev, "gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1);
|
||||||
"gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1);
|
|
||||||
gameport->dev.bus = &gameport_bus;
|
gameport->dev.bus = &gameport_bus;
|
||||||
gameport->dev.release = gameport_release_port;
|
gameport->dev.release = gameport_release_port;
|
||||||
if (gameport->parent)
|
if (gameport->parent)
|
||||||
|
|
|
@ -226,7 +226,7 @@ static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
|
||||||
ns558->gameport = port;
|
ns558->gameport = port;
|
||||||
|
|
||||||
gameport_set_name(port, "NS558 PnP Gameport");
|
gameport_set_name(port, "NS558 PnP Gameport");
|
||||||
gameport_set_phys(port, "pnp%s/gameport0", dev->dev.bus_id);
|
gameport_set_phys(port, "pnp%s/gameport0", dev_name(&dev->dev));
|
||||||
port->dev.parent = &dev->dev;
|
port->dev.parent = &dev->dev;
|
||||||
port->io = ioport;
|
port->io = ioport;
|
||||||
|
|
||||||
|
|
135
drivers/input/input-compat.c
Normal file
135
drivers/input/input-compat.c
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* 32bit compatibility wrappers for the input subsystem.
|
||||||
|
*
|
||||||
|
* Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
#include "input-compat.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
|
||||||
|
int input_event_from_user(const char __user *buffer,
|
||||||
|
struct input_event *event)
|
||||||
|
{
|
||||||
|
if (INPUT_COMPAT_TEST) {
|
||||||
|
struct input_event_compat compat_event;
|
||||||
|
|
||||||
|
if (copy_from_user(&compat_event, buffer,
|
||||||
|
sizeof(struct input_event_compat)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
event->time.tv_sec = compat_event.time.tv_sec;
|
||||||
|
event->time.tv_usec = compat_event.time.tv_usec;
|
||||||
|
event->type = compat_event.type;
|
||||||
|
event->code = compat_event.code;
|
||||||
|
event->value = compat_event.value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (copy_from_user(event, buffer, sizeof(struct input_event)))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_event_to_user(char __user *buffer,
|
||||||
|
const struct input_event *event)
|
||||||
|
{
|
||||||
|
if (INPUT_COMPAT_TEST) {
|
||||||
|
struct input_event_compat compat_event;
|
||||||
|
|
||||||
|
compat_event.time.tv_sec = event->time.tv_sec;
|
||||||
|
compat_event.time.tv_usec = event->time.tv_usec;
|
||||||
|
compat_event.type = event->type;
|
||||||
|
compat_event.code = event->code;
|
||||||
|
compat_event.value = event->value;
|
||||||
|
|
||||||
|
if (copy_to_user(buffer, &compat_event,
|
||||||
|
sizeof(struct input_event_compat)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (copy_to_user(buffer, event, sizeof(struct input_event)))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_ff_effect_from_user(const char __user *buffer, size_t size,
|
||||||
|
struct ff_effect *effect)
|
||||||
|
{
|
||||||
|
if (INPUT_COMPAT_TEST) {
|
||||||
|
struct ff_effect_compat *compat_effect;
|
||||||
|
|
||||||
|
if (size != sizeof(struct ff_effect_compat))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It so happens that the pointer which needs to be changed
|
||||||
|
* is the last field in the structure, so we can retrieve the
|
||||||
|
* whole thing and replace just the pointer.
|
||||||
|
*/
|
||||||
|
compat_effect = (struct ff_effect_compat *)effect;
|
||||||
|
|
||||||
|
if (copy_from_user(compat_effect, buffer,
|
||||||
|
sizeof(struct ff_effect_compat)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (compat_effect->type == FF_PERIODIC &&
|
||||||
|
compat_effect->u.periodic.waveform == FF_CUSTOM)
|
||||||
|
effect->u.periodic.custom_data =
|
||||||
|
compat_ptr(compat_effect->u.periodic.custom_data);
|
||||||
|
} else {
|
||||||
|
if (size != sizeof(struct ff_effect))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int input_event_from_user(const char __user *buffer,
|
||||||
|
struct input_event *event)
|
||||||
|
{
|
||||||
|
if (copy_from_user(event, buffer, sizeof(struct input_event)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_event_to_user(char __user *buffer,
|
||||||
|
const struct input_event *event)
|
||||||
|
{
|
||||||
|
if (copy_to_user(buffer, event, sizeof(struct input_event)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_ff_effect_from_user(const char __user *buffer, size_t size,
|
||||||
|
struct ff_effect *effect)
|
||||||
|
{
|
||||||
|
if (size != sizeof(struct ff_effect))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_COMPAT */
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(input_event_from_user);
|
||||||
|
EXPORT_SYMBOL_GPL(input_event_to_user);
|
||||||
|
EXPORT_SYMBOL_GPL(input_ff_effect_from_user);
|
94
drivers/input/input-compat.h
Normal file
94
drivers/input/input-compat.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#ifndef _INPUT_COMPAT_H
|
||||||
|
#define _INPUT_COMPAT_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 32bit compatibility wrappers for the input subsystem.
|
||||||
|
*
|
||||||
|
* Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/compat.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
|
||||||
|
/* Note to the author of this code: did it ever occur to
|
||||||
|
you why the ifdefs are needed? Think about it again. -AK */
|
||||||
|
#ifdef CONFIG_X86_64
|
||||||
|
# define INPUT_COMPAT_TEST is_compat_task()
|
||||||
|
#elif defined(CONFIG_IA64)
|
||||||
|
# define INPUT_COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
|
||||||
|
#elif defined(CONFIG_S390)
|
||||||
|
# define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT)
|
||||||
|
#elif defined(CONFIG_MIPS)
|
||||||
|
# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR)
|
||||||
|
#else
|
||||||
|
# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct input_event_compat {
|
||||||
|
struct compat_timeval time;
|
||||||
|
__u16 type;
|
||||||
|
__u16 code;
|
||||||
|
__s32 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ff_periodic_effect_compat {
|
||||||
|
__u16 waveform;
|
||||||
|
__u16 period;
|
||||||
|
__s16 magnitude;
|
||||||
|
__s16 offset;
|
||||||
|
__u16 phase;
|
||||||
|
|
||||||
|
struct ff_envelope envelope;
|
||||||
|
|
||||||
|
__u32 custom_len;
|
||||||
|
compat_uptr_t custom_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ff_effect_compat {
|
||||||
|
__u16 type;
|
||||||
|
__s16 id;
|
||||||
|
__u16 direction;
|
||||||
|
struct ff_trigger trigger;
|
||||||
|
struct ff_replay replay;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct ff_constant_effect constant;
|
||||||
|
struct ff_ramp_effect ramp;
|
||||||
|
struct ff_periodic_effect_compat periodic;
|
||||||
|
struct ff_condition_effect condition[2]; /* One for each axis */
|
||||||
|
struct ff_rumble_effect rumble;
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline size_t input_event_size(void)
|
||||||
|
{
|
||||||
|
return INPUT_COMPAT_TEST ?
|
||||||
|
sizeof(struct input_event_compat) : sizeof(struct input_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static inline size_t input_event_size(void)
|
||||||
|
{
|
||||||
|
return sizeof(struct input_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_COMPAT */
|
||||||
|
|
||||||
|
int input_event_from_user(const char __user *buffer,
|
||||||
|
struct input_event *event);
|
||||||
|
|
||||||
|
int input_event_to_user(char __user *buffer,
|
||||||
|
const struct input_event *event);
|
||||||
|
|
||||||
|
int input_ff_effect_from_user(const char __user *buffer, size_t size,
|
||||||
|
struct ff_effect *effect);
|
||||||
|
|
||||||
|
#endif /* _INPUT_COMPAT_H */
|
|
@ -1389,8 +1389,8 @@ int input_register_device(struct input_dev *dev)
|
||||||
if (!dev->setkeycode)
|
if (!dev->setkeycode)
|
||||||
dev->setkeycode = input_default_setkeycode;
|
dev->setkeycode = input_default_setkeycode;
|
||||||
|
|
||||||
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
|
dev_set_name(&dev->dev, "input%ld",
|
||||||
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
|
(unsigned long) atomic_inc_return(&input_no) - 1);
|
||||||
|
|
||||||
error = device_add(&dev->dev);
|
error = device_add(&dev->dev);
|
||||||
if (error)
|
if (error)
|
||||||
|
|
|
@ -800,7 +800,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcpy(joydev->dev.bus_id, joydev->name, sizeof(joydev->dev.bus_id));
|
dev_set_name(&joydev->dev, joydev->name);
|
||||||
joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
|
joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
|
||||||
joydev->dev.class = &input_class;
|
joydev->dev.class = &input_class;
|
||||||
joydev->dev.parent = &dev->dev;
|
joydev->dev.parent = &dev->dev;
|
||||||
|
|
|
@ -294,4 +294,28 @@ config JOYSTICK_XPAD_LEDS
|
||||||
This option enables support for the LED which surrounds the Big X on
|
This option enables support for the LED which surrounds the Big X on
|
||||||
XBox 360 controller.
|
XBox 360 controller.
|
||||||
|
|
||||||
|
config JOYSTICK_WALKERA0701
|
||||||
|
tristate "Walkera WK-0701 RC transmitter"
|
||||||
|
depends on HIGH_RES_TIMERS && PARPORT
|
||||||
|
help
|
||||||
|
Say Y or M here if you have a Walkera WK-0701 transmitter which is
|
||||||
|
supplied with a ready to fly Walkera helicopters such as HM36,
|
||||||
|
HM37, HM60 and want to use it via parport as a joystick. More
|
||||||
|
information is available: <file:Documentation/input/walkera0701.txt>
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called walkera0701.
|
||||||
|
|
||||||
|
config JOYSTICK_MAPLE
|
||||||
|
tristate "Dreamcast control pad"
|
||||||
|
depends on MAPLE
|
||||||
|
help
|
||||||
|
Say Y here if you have a SEGA Dreamcast and want to use your
|
||||||
|
controller as a joystick.
|
||||||
|
|
||||||
|
Most Dreamcast users will say Y.
|
||||||
|
|
||||||
|
To compile this as a module choose M here: the module will be called
|
||||||
|
maplecontrol.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -19,6 +19,7 @@ obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/
|
||||||
obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
|
obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
|
||||||
obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
|
obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
|
||||||
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
|
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
|
||||||
|
obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
|
||||||
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
|
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
|
||||||
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
|
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
|
||||||
obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
|
obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
|
||||||
|
@ -29,4 +30,5 @@ obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o
|
||||||
obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
|
obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
|
||||||
obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
|
obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
|
||||||
obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
|
obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
|
||||||
|
obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
|
||||||
|
|
||||||
|
|
193
drivers/input/joystick/maplecontrol.c
Normal file
193
drivers/input/joystick/maplecontrol.c
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* SEGA Dreamcast controller driver
|
||||||
|
* Based on drivers/usb/iforce.c
|
||||||
|
*
|
||||||
|
* Copyright Yaegashi Takeshi, 2001
|
||||||
|
* Adrian McMenamin, 2008
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/timer.h>
|
||||||
|
#include <linux/maple.h>
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
|
||||||
|
MODULE_DESCRIPTION("SEGA Dreamcast controller driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
struct dc_pad {
|
||||||
|
struct input_dev *dev;
|
||||||
|
struct maple_device *mdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dc_pad_callback(struct mapleq *mq)
|
||||||
|
{
|
||||||
|
unsigned short buttons;
|
||||||
|
struct maple_device *mapledev = mq->dev;
|
||||||
|
struct dc_pad *pad = maple_get_drvdata(mapledev);
|
||||||
|
struct input_dev *dev = pad->dev;
|
||||||
|
unsigned char *res = mq->recvbuf;
|
||||||
|
|
||||||
|
buttons = ~le16_to_cpup((__le16 *)(res + 8));
|
||||||
|
|
||||||
|
input_report_abs(dev, ABS_HAT0Y,
|
||||||
|
(buttons & 0x0010 ? -1 : 0) + (buttons & 0x0020 ? 1 : 0));
|
||||||
|
input_report_abs(dev, ABS_HAT0X,
|
||||||
|
(buttons & 0x0040 ? -1 : 0) + (buttons & 0x0080 ? 1 : 0));
|
||||||
|
input_report_abs(dev, ABS_HAT1Y,
|
||||||
|
(buttons & 0x1000 ? -1 : 0) + (buttons & 0x2000 ? 1 : 0));
|
||||||
|
input_report_abs(dev, ABS_HAT1X,
|
||||||
|
(buttons & 0x4000 ? -1 : 0) + (buttons & 0x8000 ? 1 : 0));
|
||||||
|
|
||||||
|
input_report_key(dev, BTN_C, buttons & 0x0001);
|
||||||
|
input_report_key(dev, BTN_B, buttons & 0x0002);
|
||||||
|
input_report_key(dev, BTN_A, buttons & 0x0004);
|
||||||
|
input_report_key(dev, BTN_START, buttons & 0x0008);
|
||||||
|
input_report_key(dev, BTN_Z, buttons & 0x0100);
|
||||||
|
input_report_key(dev, BTN_Y, buttons & 0x0200);
|
||||||
|
input_report_key(dev, BTN_X, buttons & 0x0400);
|
||||||
|
input_report_key(dev, BTN_SELECT, buttons & 0x0800);
|
||||||
|
|
||||||
|
input_report_abs(dev, ABS_GAS, res[10]);
|
||||||
|
input_report_abs(dev, ABS_BRAKE, res[11]);
|
||||||
|
input_report_abs(dev, ABS_X, res[12]);
|
||||||
|
input_report_abs(dev, ABS_Y, res[13]);
|
||||||
|
input_report_abs(dev, ABS_RX, res[14]);
|
||||||
|
input_report_abs(dev, ABS_RY, res[15]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dc_pad_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct dc_pad *pad = dev->dev.platform_data;
|
||||||
|
|
||||||
|
maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20,
|
||||||
|
MAPLE_FUNC_CONTROLLER);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dc_pad_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct dc_pad *pad = dev->dev.platform_data;
|
||||||
|
|
||||||
|
maple_getcond_callback(pad->mdev, dc_pad_callback, 0,
|
||||||
|
MAPLE_FUNC_CONTROLLER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allow the controller to be used */
|
||||||
|
static int __devinit probe_maple_controller(struct device *dev)
|
||||||
|
{
|
||||||
|
static const short btn_bit[32] = {
|
||||||
|
BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1,
|
||||||
|
BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const short abs_bit[32] = {
|
||||||
|
-1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X,
|
||||||
|
-1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X,
|
||||||
|
ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct maple_device *mdev = to_maple_dev(dev);
|
||||||
|
struct maple_driver *mdrv = to_maple_driver(dev->driver);
|
||||||
|
int i, error;
|
||||||
|
struct dc_pad *pad;
|
||||||
|
struct input_dev *idev;
|
||||||
|
unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]);
|
||||||
|
|
||||||
|
pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
|
||||||
|
idev = input_allocate_device();
|
||||||
|
if (!pad || !idev) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
pad->dev = idev;
|
||||||
|
pad->mdev = mdev;
|
||||||
|
|
||||||
|
idev->open = dc_pad_open;
|
||||||
|
idev->close = dc_pad_close;
|
||||||
|
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
if (data & (1 << i)) {
|
||||||
|
if (btn_bit[i] >= 0)
|
||||||
|
__set_bit(btn_bit[i], idev->keybit);
|
||||||
|
else if (abs_bit[i] >= 0)
|
||||||
|
__set_bit(abs_bit[i], idev->absbit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idev->keybit[BIT_WORD(BTN_JOYSTICK)])
|
||||||
|
idev->evbit[0] |= BIT_MASK(EV_KEY);
|
||||||
|
|
||||||
|
if (idev->absbit[0])
|
||||||
|
idev->evbit[0] |= BIT_MASK(EV_ABS);
|
||||||
|
|
||||||
|
for (i = ABS_X; i <= ABS_BRAKE; i++)
|
||||||
|
input_set_abs_params(idev, i, 0, 255, 0, 0);
|
||||||
|
|
||||||
|
for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++)
|
||||||
|
input_set_abs_params(idev, i, 1, -1, 0, 0);
|
||||||
|
|
||||||
|
idev->dev.platform_data = pad;
|
||||||
|
idev->dev.parent = &mdev->dev;
|
||||||
|
idev->name = mdev->product_name;
|
||||||
|
idev->id.bustype = BUS_HOST;
|
||||||
|
input_set_drvdata(idev, pad);
|
||||||
|
|
||||||
|
error = input_register_device(idev);
|
||||||
|
if (error)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
mdev->driver = mdrv;
|
||||||
|
maple_set_drvdata(mdev, pad);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
input_free_device(idev);
|
||||||
|
kfree(pad);
|
||||||
|
maple_set_drvdata(mdev, NULL);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit remove_maple_controller(struct device *dev)
|
||||||
|
{
|
||||||
|
struct maple_device *mdev = to_maple_dev(dev);
|
||||||
|
struct dc_pad *pad = maple_get_drvdata(mdev);
|
||||||
|
|
||||||
|
mdev->callback = NULL;
|
||||||
|
input_unregister_device(pad->dev);
|
||||||
|
maple_set_drvdata(mdev, NULL);
|
||||||
|
kfree(pad);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct maple_driver dc_pad_driver = {
|
||||||
|
.function = MAPLE_FUNC_CONTROLLER,
|
||||||
|
.drv = {
|
||||||
|
.name = "Dreamcast_controller",
|
||||||
|
.probe = probe_maple_controller,
|
||||||
|
.remove = __devexit_p(remove_maple_controller),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init dc_pad_init(void)
|
||||||
|
{
|
||||||
|
return maple_driver_register(&dc_pad_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit dc_pad_exit(void)
|
||||||
|
{
|
||||||
|
maple_driver_unregister(&dc_pad_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(dc_pad_init);
|
||||||
|
module_exit(dc_pad_exit);
|
292
drivers/input/joystick/walkera0701.c
Normal file
292
drivers/input/joystick/walkera0701.c
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
/*
|
||||||
|
* Parallel port to Walkera WK-0701 TX joystick
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Peter Popovec
|
||||||
|
*
|
||||||
|
* More about driver: <file:Documentation/input/walkera0701.txt>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* #define WK0701_DEBUG */
|
||||||
|
|
||||||
|
#define RESERVE 20000
|
||||||
|
#define SYNC_PULSE 1306000
|
||||||
|
#define BIN0_PULSE 288000
|
||||||
|
#define BIN1_PULSE 438000
|
||||||
|
|
||||||
|
#define ANALOG_MIN_PULSE 318000
|
||||||
|
#define ANALOG_MAX_PULSE 878000
|
||||||
|
#define ANALOG_DELTA 80000
|
||||||
|
|
||||||
|
#define BIN_SAMPLE ((BIN0_PULSE + BIN1_PULSE) / 2)
|
||||||
|
|
||||||
|
#define NO_SYNC 25
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/parport.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/hrtimer.h>
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Peter Popovec <popovec@fei.tuke.sk>");
|
||||||
|
MODULE_DESCRIPTION("Walkera WK-0701 TX as joystick");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
static unsigned int walkera0701_pp_no;
|
||||||
|
module_param_named(port, walkera0701_pp_no, int, 0);
|
||||||
|
MODULE_PARM_DESC(port,
|
||||||
|
"Parallel port adapter for Walkera WK-0701 TX (default is 0)");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For now, only one device is supported, if somebody need more devices, code
|
||||||
|
* can be expanded, one struct walkera_dev per device must be allocated and
|
||||||
|
* set up by walkera0701_connect (release of device by walkera0701_disconnect)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct walkera_dev {
|
||||||
|
unsigned char buf[25];
|
||||||
|
u64 irq_time, irq_lasttime;
|
||||||
|
int counter;
|
||||||
|
int ack;
|
||||||
|
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
struct hrtimer timer;
|
||||||
|
|
||||||
|
struct parport *parport;
|
||||||
|
struct pardevice *pardevice;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct walkera_dev w_dev;
|
||||||
|
|
||||||
|
static inline void walkera0701_parse_frame(struct walkera_dev *w)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int val1, val2, val3, val4, val5, val6, val7, val8;
|
||||||
|
int crc1, crc2;
|
||||||
|
|
||||||
|
for (crc1 = crc2 = i = 0; i < 10; i++) {
|
||||||
|
crc1 += w->buf[i] & 7;
|
||||||
|
crc2 += (w->buf[i] & 8) >> 3;
|
||||||
|
}
|
||||||
|
if ((w->buf[10] & 7) != (crc1 & 7))
|
||||||
|
return;
|
||||||
|
if (((w->buf[10] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
|
||||||
|
return;
|
||||||
|
for (crc1 = crc2 = 0, i = 11; i < 23; i++) {
|
||||||
|
crc1 += w->buf[i] & 7;
|
||||||
|
crc2 += (w->buf[i] & 8) >> 3;
|
||||||
|
}
|
||||||
|
if ((w->buf[23] & 7) != (crc1 & 7))
|
||||||
|
return;
|
||||||
|
if (((w->buf[23] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
|
||||||
|
return;
|
||||||
|
val1 = ((w->buf[0] & 7) * 256 + w->buf[1] * 16 + w->buf[2]) >> 2;
|
||||||
|
val1 *= ((w->buf[0] >> 2) & 2) - 1; /* sign */
|
||||||
|
val2 = (w->buf[2] & 1) << 8 | (w->buf[3] << 4) | w->buf[4];
|
||||||
|
val2 *= (w->buf[2] & 2) - 1; /* sign */
|
||||||
|
val3 = ((w->buf[5] & 7) * 256 + w->buf[6] * 16 + w->buf[7]) >> 2;
|
||||||
|
val3 *= ((w->buf[5] >> 2) & 2) - 1; /* sign */
|
||||||
|
val4 = (w->buf[7] & 1) << 8 | (w->buf[8] << 4) | w->buf[9];
|
||||||
|
val4 *= (w->buf[7] & 2) - 1; /* sign */
|
||||||
|
val5 = ((w->buf[11] & 7) * 256 + w->buf[12] * 16 + w->buf[13]) >> 2;
|
||||||
|
val5 *= ((w->buf[11] >> 2) & 2) - 1; /* sign */
|
||||||
|
val6 = (w->buf[13] & 1) << 8 | (w->buf[14] << 4) | w->buf[15];
|
||||||
|
val6 *= (w->buf[13] & 2) - 1; /* sign */
|
||||||
|
val7 = ((w->buf[16] & 7) * 256 + w->buf[17] * 16 + w->buf[18]) >> 2;
|
||||||
|
val7 *= ((w->buf[16] >> 2) & 2) - 1; /*sign */
|
||||||
|
val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
|
||||||
|
val8 *= (w->buf[18] & 2) - 1; /*sign */
|
||||||
|
|
||||||
|
#ifdef WK0701_DEBUG
|
||||||
|
{
|
||||||
|
int magic, magic_bit;
|
||||||
|
magic = (w->buf[21] << 4) | w->buf[22];
|
||||||
|
magic_bit = (w->buf[24] & 8) >> 3;
|
||||||
|
printk(KERN_DEBUG
|
||||||
|
"walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
|
||||||
|
val1, val2, val3, val4, val5, val6, val7, val8, magic,
|
||||||
|
magic_bit);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
input_report_abs(w->input_dev, ABS_X, val2);
|
||||||
|
input_report_abs(w->input_dev, ABS_Y, val1);
|
||||||
|
input_report_abs(w->input_dev, ABS_Z, val6);
|
||||||
|
input_report_abs(w->input_dev, ABS_THROTTLE, val3);
|
||||||
|
input_report_abs(w->input_dev, ABS_RUDDER, val4);
|
||||||
|
input_report_abs(w->input_dev, ABS_MISC, val7);
|
||||||
|
input_report_key(w->input_dev, BTN_GEAR_DOWN, val5 > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int read_ack(struct pardevice *p)
|
||||||
|
{
|
||||||
|
return parport_read_status(p->port) & 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* falling edge, prepare to BIN value calculation */
|
||||||
|
static void walkera0701_irq_handler(void *handler_data)
|
||||||
|
{
|
||||||
|
u64 pulse_time;
|
||||||
|
struct walkera_dev *w = handler_data;
|
||||||
|
|
||||||
|
w->irq_time = ktime_to_ns(ktime_get());
|
||||||
|
pulse_time = w->irq_time - w->irq_lasttime;
|
||||||
|
w->irq_lasttime = w->irq_time;
|
||||||
|
|
||||||
|
/* cancel timer, if in handler or active do resync */
|
||||||
|
if (unlikely(0 != hrtimer_try_to_cancel(&w->timer))) {
|
||||||
|
w->counter = NO_SYNC;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w->counter < NO_SYNC) {
|
||||||
|
if (w->ack) {
|
||||||
|
pulse_time -= BIN1_PULSE;
|
||||||
|
w->buf[w->counter] = 8;
|
||||||
|
} else {
|
||||||
|
pulse_time -= BIN0_PULSE;
|
||||||
|
w->buf[w->counter] = 0;
|
||||||
|
}
|
||||||
|
if (w->counter == 24) { /* full frame */
|
||||||
|
walkera0701_parse_frame(w);
|
||||||
|
w->counter = NO_SYNC;
|
||||||
|
if (abs(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */
|
||||||
|
w->counter = 0;
|
||||||
|
} else {
|
||||||
|
if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE)
|
||||||
|
&& (pulse_time < (ANALOG_MAX_PULSE + RESERVE)))) {
|
||||||
|
pulse_time -= (ANALOG_MIN_PULSE - RESERVE);
|
||||||
|
pulse_time = (u32) pulse_time / ANALOG_DELTA; /* overtiping is safe, pulsetime < s32.. */
|
||||||
|
w->buf[w->counter++] |= (pulse_time & 7);
|
||||||
|
} else
|
||||||
|
w->counter = NO_SYNC;
|
||||||
|
}
|
||||||
|
} else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) <
|
||||||
|
RESERVE + BIN1_PULSE - BIN0_PULSE) /* frame sync .. */
|
||||||
|
w->counter = 0;
|
||||||
|
|
||||||
|
hrtimer_start(&w->timer, ktime_set(0, BIN_SAMPLE), HRTIMER_MODE_REL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum hrtimer_restart timer_handler(struct hrtimer
|
||||||
|
*handle)
|
||||||
|
{
|
||||||
|
struct walkera_dev *w;
|
||||||
|
|
||||||
|
w = container_of(handle, struct walkera_dev, timer);
|
||||||
|
w->ack = read_ack(w->pardevice);
|
||||||
|
|
||||||
|
return HRTIMER_NORESTART;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int walkera0701_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct walkera_dev *w = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
parport_enable_irq(w->parport);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void walkera0701_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct walkera_dev *w = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
parport_disable_irq(w->parport);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int walkera0701_connect(struct walkera_dev *w, int parport)
|
||||||
|
{
|
||||||
|
int err = -ENODEV;
|
||||||
|
|
||||||
|
w->parport = parport_find_number(parport);
|
||||||
|
if (w->parport == NULL)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (w->parport->irq == -1) {
|
||||||
|
printk(KERN_ERR "walkera0701: parport without interrupt\n");
|
||||||
|
goto init_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = -EBUSY;
|
||||||
|
w->pardevice = parport_register_device(w->parport, "walkera0701",
|
||||||
|
NULL, NULL, walkera0701_irq_handler,
|
||||||
|
PARPORT_DEV_EXCL, w);
|
||||||
|
if (!w->pardevice)
|
||||||
|
goto init_err;
|
||||||
|
|
||||||
|
if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT))
|
||||||
|
goto init_err1;
|
||||||
|
|
||||||
|
if (parport_claim(w->pardevice))
|
||||||
|
goto init_err1;
|
||||||
|
|
||||||
|
w->input_dev = input_allocate_device();
|
||||||
|
if (!w->input_dev)
|
||||||
|
goto init_err2;
|
||||||
|
|
||||||
|
input_set_drvdata(w->input_dev, w);
|
||||||
|
w->input_dev->name = "Walkera WK-0701 TX";
|
||||||
|
w->input_dev->phys = w->parport->name;
|
||||||
|
w->input_dev->id.bustype = BUS_PARPORT;
|
||||||
|
|
||||||
|
/* TODO what id vendor/product/version ? */
|
||||||
|
w->input_dev->id.vendor = 0x0001;
|
||||||
|
w->input_dev->id.product = 0x0001;
|
||||||
|
w->input_dev->id.version = 0x0100;
|
||||||
|
w->input_dev->open = walkera0701_open;
|
||||||
|
w->input_dev->close = walkera0701_close;
|
||||||
|
|
||||||
|
w->input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY);
|
||||||
|
w->input_dev->keybit[BIT_WORD(BTN_GEAR_DOWN)] = BIT_MASK(BTN_GEAR_DOWN);
|
||||||
|
|
||||||
|
input_set_abs_params(w->input_dev, ABS_X, -512, 512, 0, 0);
|
||||||
|
input_set_abs_params(w->input_dev, ABS_Y, -512, 512, 0, 0);
|
||||||
|
input_set_abs_params(w->input_dev, ABS_Z, -512, 512, 0, 0);
|
||||||
|
input_set_abs_params(w->input_dev, ABS_THROTTLE, -512, 512, 0, 0);
|
||||||
|
input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
|
||||||
|
input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
|
||||||
|
|
||||||
|
err = input_register_device(w->input_dev);
|
||||||
|
if (err)
|
||||||
|
goto init_err3;
|
||||||
|
|
||||||
|
hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||||
|
w->timer.function = timer_handler;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
init_err3:
|
||||||
|
input_free_device(w->input_dev);
|
||||||
|
init_err2:
|
||||||
|
parport_release(w->pardevice);
|
||||||
|
init_err1:
|
||||||
|
parport_unregister_device(w->pardevice);
|
||||||
|
init_err:
|
||||||
|
parport_put_port(w->parport);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void walkera0701_disconnect(struct walkera_dev *w)
|
||||||
|
{
|
||||||
|
hrtimer_cancel(&w->timer);
|
||||||
|
input_unregister_device(w->input_dev);
|
||||||
|
parport_release(w->pardevice);
|
||||||
|
parport_unregister_device(w->pardevice);
|
||||||
|
parport_put_port(w->parport);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init walkera0701_init(void)
|
||||||
|
{
|
||||||
|
return walkera0701_connect(&w_dev, walkera0701_pp_no);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit walkera0701_exit(void)
|
||||||
|
{
|
||||||
|
walkera0701_disconnect(&w_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(walkera0701_init);
|
||||||
|
module_exit(walkera0701_exit);
|
|
@ -268,6 +268,15 @@ config KEYBOARD_PXA27x
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called pxa27x_keypad.
|
module will be called pxa27x_keypad.
|
||||||
|
|
||||||
|
config KEYBOARD_PXA930_ROTARY
|
||||||
|
tristate "PXA930/PXA935 Enhanced Rotary Controller Support"
|
||||||
|
depends on CPU_PXA930 || CPU_PXA935
|
||||||
|
help
|
||||||
|
Enable support for PXA930/PXA935 Enhanced Rotary Controller.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called pxa930_rotary.
|
||||||
|
|
||||||
config KEYBOARD_AAED2000
|
config KEYBOARD_AAED2000
|
||||||
tristate "AAED-2000 keyboard"
|
tristate "AAED-2000 keyboard"
|
||||||
depends on MACH_AAED2000
|
depends on MACH_AAED2000
|
||||||
|
|
|
@ -20,6 +20,7 @@ obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
|
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
||||||
|
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
||||||
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
|
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
|
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
|
||||||
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
|
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
|
||||||
|
|
|
@ -883,6 +883,39 @@ static void atkbd_inventec_keymap_fixup(struct atkbd *atkbd)
|
||||||
atkbd->force_release_mask);
|
atkbd->force_release_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform fixup for HP Pavilion ZV6100 laptop that doesn't generate release
|
||||||
|
* for its volume buttons
|
||||||
|
*/
|
||||||
|
static void atkbd_hp_zv6100_keymap_fixup(struct atkbd *atkbd)
|
||||||
|
{
|
||||||
|
const unsigned int forced_release_keys[] = {
|
||||||
|
0xae, 0xb0,
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (atkbd->set == 2)
|
||||||
|
for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
|
||||||
|
__set_bit(forced_release_keys[i],
|
||||||
|
atkbd->force_release_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Samsung NC10 with Fn+F? key release not working
|
||||||
|
*/
|
||||||
|
static void atkbd_samsung_keymap_fixup(struct atkbd *atkbd)
|
||||||
|
{
|
||||||
|
const unsigned int forced_release_keys[] = {
|
||||||
|
0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9,
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (atkbd->set == 2)
|
||||||
|
for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
|
||||||
|
__set_bit(forced_release_keys[i],
|
||||||
|
atkbd->force_release_mask);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* atkbd_set_keycode_table() initializes keyboard's keycode table
|
* atkbd_set_keycode_table() initializes keyboard's keycode table
|
||||||
* according to the selected scancode set
|
* according to the selected scancode set
|
||||||
|
@ -1475,6 +1508,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
||||||
.callback = atkbd_setup_fixup,
|
.callback = atkbd_setup_fixup,
|
||||||
.driver_data = atkbd_dell_laptop_keymap_fixup,
|
.driver_data = atkbd_dell_laptop_keymap_fixup,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.ident = "Dell Laptop",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
|
||||||
|
DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
|
||||||
|
},
|
||||||
|
.callback = atkbd_setup_fixup,
|
||||||
|
.driver_data = atkbd_dell_laptop_keymap_fixup,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.ident = "HP 2133",
|
.ident = "HP 2133",
|
||||||
.matches = {
|
.matches = {
|
||||||
|
@ -1484,6 +1526,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
||||||
.callback = atkbd_setup_fixup,
|
.callback = atkbd_setup_fixup,
|
||||||
.driver_data = atkbd_hp_keymap_fixup,
|
.driver_data = atkbd_hp_keymap_fixup,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.ident = "HP Pavilion ZV6100",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"),
|
||||||
|
},
|
||||||
|
.callback = atkbd_setup_fixup,
|
||||||
|
.driver_data = atkbd_hp_zv6100_keymap_fixup,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.ident = "Inventec Symphony",
|
.ident = "Inventec Symphony",
|
||||||
.matches = {
|
.matches = {
|
||||||
|
@ -1493,6 +1544,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
||||||
.callback = atkbd_setup_fixup,
|
.callback = atkbd_setup_fixup,
|
||||||
.driver_data = atkbd_inventec_keymap_fixup,
|
.driver_data = atkbd_inventec_keymap_fixup,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.ident = "Samsung NC10",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
|
||||||
|
},
|
||||||
|
.callback = atkbd_setup_fixup,
|
||||||
|
.driver_data = atkbd_samsung_keymap_fixup,
|
||||||
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,10 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
||||||
input->id.product = 0x0001;
|
input->id.product = 0x0001;
|
||||||
input->id.version = 0x0100;
|
input->id.version = 0x0100;
|
||||||
|
|
||||||
|
/* Enable auto repeat feature of Linux input subsystem */
|
||||||
|
if (pdata->rep)
|
||||||
|
__set_bit(EV_REP, input->evbit);
|
||||||
|
|
||||||
ddata->input = input;
|
ddata->input = input;
|
||||||
|
|
||||||
for (i = 0; i < pdata->nbuttons; i++) {
|
for (i = 0; i < pdata->nbuttons; i++) {
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
|
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
|
||||||
MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
|
MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
|
||||||
MODULE_LICENSE("Dual BSD/GPL");
|
MODULE_LICENSE("Dual BSD/GPL");
|
||||||
|
MODULE_ALIAS("serio:ty03pr25id00ex*");
|
||||||
|
|
||||||
#define HIL_KBD_MAX_LENGTH 16
|
#define HIL_KBD_MAX_LENGTH 16
|
||||||
|
|
||||||
|
|
|
@ -122,14 +122,10 @@ static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)
|
||||||
|
|
||||||
/* read the keypad status */
|
/* read the keypad status */
|
||||||
if (cpu_is_omap24xx()) {
|
if (cpu_is_omap24xx()) {
|
||||||
int i;
|
|
||||||
for (i = 0; i < omap_kp->rows; i++)
|
|
||||||
disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
|
|
||||||
|
|
||||||
/* read the keypad status */
|
/* read the keypad status */
|
||||||
for (col = 0; col < omap_kp->cols; col++) {
|
for (col = 0; col < omap_kp->cols; col++) {
|
||||||
set_col_gpio_val(omap_kp, ~(1 << col));
|
set_col_gpio_val(omap_kp, ~(1 << col));
|
||||||
state[col] = ~(get_row_gpio_val(omap_kp)) & 0x3f;
|
state[col] = ~(get_row_gpio_val(omap_kp)) & 0xff;
|
||||||
}
|
}
|
||||||
set_col_gpio_val(omap_kp, 0);
|
set_col_gpio_val(omap_kp, 0);
|
||||||
|
|
||||||
|
|
212
drivers/input/keyboard/pxa930_rotary.c
Normal file
212
drivers/input/keyboard/pxa930_rotary.c
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Driver for the enhanced rotary controller on pxa930 and pxa935
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
#include <mach/pxa930_rotary.h>
|
||||||
|
|
||||||
|
#define SBCR (0x04)
|
||||||
|
#define ERCR (0x0c)
|
||||||
|
|
||||||
|
#define SBCR_ERSB (1 << 5)
|
||||||
|
|
||||||
|
struct pxa930_rotary {
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
void __iomem *mmio_base;
|
||||||
|
int last_ercr;
|
||||||
|
|
||||||
|
struct pxa930_rotary_platform_data *pdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void clear_sbcr(struct pxa930_rotary *r)
|
||||||
|
{
|
||||||
|
uint32_t sbcr = __raw_readl(r->mmio_base + SBCR);
|
||||||
|
|
||||||
|
__raw_writel(sbcr | SBCR_ERSB, r->mmio_base + SBCR);
|
||||||
|
__raw_writel(sbcr & ~SBCR_ERSB, r->mmio_base + SBCR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t rotary_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct pxa930_rotary *r = dev_id;
|
||||||
|
struct pxa930_rotary_platform_data *pdata = r->pdata;
|
||||||
|
int ercr, delta, key;
|
||||||
|
|
||||||
|
ercr = __raw_readl(r->mmio_base + ERCR) & 0xf;
|
||||||
|
clear_sbcr(r);
|
||||||
|
|
||||||
|
delta = ercr - r->last_ercr;
|
||||||
|
if (delta == 0)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
r->last_ercr = ercr;
|
||||||
|
|
||||||
|
if (pdata->up_key && pdata->down_key) {
|
||||||
|
key = (delta > 0) ? pdata->up_key : pdata->down_key;
|
||||||
|
input_report_key(r->input_dev, key, 1);
|
||||||
|
input_sync(r->input_dev);
|
||||||
|
input_report_key(r->input_dev, key, 0);
|
||||||
|
} else
|
||||||
|
input_report_rel(r->input_dev, pdata->rel_code, delta);
|
||||||
|
|
||||||
|
input_sync(r->input_dev);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pxa930_rotary_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct pxa930_rotary *r = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
clear_sbcr(r);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxa930_rotary_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct pxa930_rotary *r = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
clear_sbcr(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit pxa930_rotary_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct pxa930_rotary_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
struct pxa930_rotary *r;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
struct resource *res;
|
||||||
|
int irq;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "no irq for rotary controller\n");
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res) {
|
||||||
|
dev_err(&pdev->dev, "no I/O memory defined\n");
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pdata) {
|
||||||
|
dev_err(&pdev->dev, "no platform data defined\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = kzalloc(sizeof(struct pxa930_rotary), GFP_KERNEL);
|
||||||
|
if (!r)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r->mmio_base = ioremap_nocache(res->start, resource_size(res));
|
||||||
|
if (r->mmio_base == NULL) {
|
||||||
|
dev_err(&pdev->dev, "failed to remap IO memory\n");
|
||||||
|
err = -ENXIO;
|
||||||
|
goto failed_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->pdata = pdata;
|
||||||
|
platform_set_drvdata(pdev, r);
|
||||||
|
|
||||||
|
/* allocate and register the input device */
|
||||||
|
input_dev = input_allocate_device();
|
||||||
|
if (!input_dev) {
|
||||||
|
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto failed_free_io;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_dev->name = pdev->name;
|
||||||
|
input_dev->id.bustype = BUS_HOST;
|
||||||
|
input_dev->open = pxa930_rotary_open;
|
||||||
|
input_dev->close = pxa930_rotary_close;
|
||||||
|
input_dev->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
if (pdata->up_key && pdata->down_key) {
|
||||||
|
__set_bit(pdata->up_key, input_dev->keybit);
|
||||||
|
__set_bit(pdata->down_key, input_dev->keybit);
|
||||||
|
__set_bit(EV_KEY, input_dev->evbit);
|
||||||
|
} else {
|
||||||
|
__set_bit(pdata->rel_code, input_dev->relbit);
|
||||||
|
__set_bit(EV_REL, input_dev->evbit);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->input_dev = input_dev;
|
||||||
|
input_set_drvdata(input_dev, r);
|
||||||
|
|
||||||
|
err = request_irq(irq, rotary_irq, IRQF_DISABLED,
|
||||||
|
"enhanced rotary", r);
|
||||||
|
if (err) {
|
||||||
|
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||||
|
goto failed_free_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = input_register_device(input_dev);
|
||||||
|
if (err) {
|
||||||
|
dev_err(&pdev->dev, "failed to register input device\n");
|
||||||
|
goto failed_free_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
failed_free_irq:
|
||||||
|
free_irq(irq, r);
|
||||||
|
failed_free_input:
|
||||||
|
input_free_device(input_dev);
|
||||||
|
failed_free_io:
|
||||||
|
iounmap(r->mmio_base);
|
||||||
|
failed_free:
|
||||||
|
kfree(r);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit pxa930_rotary_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct pxa930_rotary *r = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
free_irq(platform_get_irq(pdev, 0), r);
|
||||||
|
input_unregister_device(r->input_dev);
|
||||||
|
iounmap(r->mmio_base);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
kfree(r);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver pxa930_rotary_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "pxa930-rotary",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = pxa930_rotary_probe,
|
||||||
|
.remove = __devexit_p(pxa930_rotary_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init pxa930_rotary_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&pxa930_rotary_driver);
|
||||||
|
}
|
||||||
|
module_init(pxa930_rotary_init);
|
||||||
|
|
||||||
|
static void __exit pxa930_rotary_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&pxa930_rotary_driver);
|
||||||
|
}
|
||||||
|
module_exit(pxa930_rotary_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller");
|
||||||
|
MODULE_AUTHOR("Yao Yong <yaoyong@marvell.com>");
|
|
@ -52,13 +52,13 @@ static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int c
|
||||||
spin_lock_irqsave(&i8253_lock, flags);
|
spin_lock_irqsave(&i8253_lock, flags);
|
||||||
|
|
||||||
if (count) {
|
if (count) {
|
||||||
/* enable counter 2 */
|
|
||||||
outb_p(inb_p(0x61) | 3, 0x61);
|
|
||||||
/* set command for counter 2, 2 byte write */
|
/* set command for counter 2, 2 byte write */
|
||||||
outb_p(0xB6, 0x43);
|
outb_p(0xB6, 0x43);
|
||||||
/* select desired HZ */
|
/* select desired HZ */
|
||||||
outb_p(count & 0xff, 0x42);
|
outb_p(count & 0xff, 0x42);
|
||||||
outb((count >> 8) & 0xff, 0x42);
|
outb((count >> 8) & 0xff, 0x42);
|
||||||
|
/* enable counter 2 */
|
||||||
|
outb_p(inb_p(0x61) | 3, 0x61);
|
||||||
} else {
|
} else {
|
||||||
/* disable counter 2 */
|
/* disable counter 2 */
|
||||||
outb(inb_p(0x61) & 0xFC, 0x61);
|
outb(inb_p(0x61) & 0xFC, 0x61);
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/miscdevice.h>
|
#include <linux/miscdevice.h>
|
||||||
#include <linux/uinput.h>
|
#include <linux/uinput.h>
|
||||||
|
#include "../input-compat.h"
|
||||||
|
|
||||||
static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
|
static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
|
@ -78,6 +79,7 @@ static struct uinput_request* uinput_request_find(struct uinput_device *udev, in
|
||||||
/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
|
/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
|
||||||
if (id >= UINPUT_NUM_REQUESTS || id < 0)
|
if (id >= UINPUT_NUM_REQUESTS || id < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return udev->requests[id];
|
return udev->requests[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +129,17 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff
|
||||||
struct uinput_request request;
|
struct uinput_request request;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* uinput driver does not currently support periodic effects with
|
||||||
|
* custom waveform since it does not have a way to pass buffer of
|
||||||
|
* samples (custom_data) to userspace. If ever there is a device
|
||||||
|
* supporting custom waveforms we would need to define an additional
|
||||||
|
* ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
|
||||||
|
*/
|
||||||
|
if (effect->type == FF_PERIODIC &&
|
||||||
|
effect->u.periodic.waveform == FF_CUSTOM)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
request.id = -1;
|
request.id = -1;
|
||||||
init_completion(&request.done);
|
init_completion(&request.done);
|
||||||
request.code = UI_FF_UPLOAD;
|
request.code = UI_FF_UPLOAD;
|
||||||
|
@ -353,15 +366,15 @@ static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char
|
||||||
{
|
{
|
||||||
struct input_event ev;
|
struct input_event ev;
|
||||||
|
|
||||||
if (count != sizeof(struct input_event))
|
if (count < input_event_size())
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
|
if (input_event_from_user(buffer, &ev))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
input_event(udev->dev, ev.type, ev.code, ev.value);
|
input_event(udev->dev, ev.type, ev.code, ev.value);
|
||||||
|
|
||||||
return sizeof(struct input_event);
|
return input_event_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
||||||
|
@ -407,13 +420,13 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (udev->head != udev->tail && retval + sizeof(struct input_event) <= count) {
|
while (udev->head != udev->tail && retval + input_event_size() <= count) {
|
||||||
if (copy_to_user(buffer + retval, &udev->buff[udev->tail], sizeof(struct input_event))) {
|
if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) {
|
||||||
retval = -EFAULT;
|
retval = -EFAULT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
|
udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
|
||||||
retval += sizeof(struct input_event);
|
retval += input_event_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -444,6 +457,93 @@ static int uinput_release(struct inode *inode, struct file *file)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
struct uinput_ff_upload_compat {
|
||||||
|
int request_id;
|
||||||
|
int retval;
|
||||||
|
struct ff_effect_compat effect;
|
||||||
|
struct ff_effect_compat old;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int uinput_ff_upload_to_user(char __user *buffer,
|
||||||
|
const struct uinput_ff_upload *ff_up)
|
||||||
|
{
|
||||||
|
if (INPUT_COMPAT_TEST) {
|
||||||
|
struct uinput_ff_upload_compat ff_up_compat;
|
||||||
|
|
||||||
|
ff_up_compat.request_id = ff_up->request_id;
|
||||||
|
ff_up_compat.retval = ff_up->retval;
|
||||||
|
/*
|
||||||
|
* It so happens that the pointer that gives us the trouble
|
||||||
|
* is the last field in the structure. Since we don't support
|
||||||
|
* custom waveforms in uinput anyway we can just copy the whole
|
||||||
|
* thing (to the compat size) and ignore the pointer.
|
||||||
|
*/
|
||||||
|
memcpy(&ff_up_compat.effect, &ff_up->effect,
|
||||||
|
sizeof(struct ff_effect_compat));
|
||||||
|
memcpy(&ff_up_compat.old, &ff_up->old,
|
||||||
|
sizeof(struct ff_effect_compat));
|
||||||
|
|
||||||
|
if (copy_to_user(buffer, &ff_up_compat,
|
||||||
|
sizeof(struct uinput_ff_upload_compat)))
|
||||||
|
return -EFAULT;
|
||||||
|
} else {
|
||||||
|
if (copy_to_user(buffer, ff_up,
|
||||||
|
sizeof(struct uinput_ff_upload)))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uinput_ff_upload_from_user(const char __user *buffer,
|
||||||
|
struct uinput_ff_upload *ff_up)
|
||||||
|
{
|
||||||
|
if (INPUT_COMPAT_TEST) {
|
||||||
|
struct uinput_ff_upload_compat ff_up_compat;
|
||||||
|
|
||||||
|
if (copy_from_user(&ff_up_compat, buffer,
|
||||||
|
sizeof(struct uinput_ff_upload_compat)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
ff_up->request_id = ff_up_compat.request_id;
|
||||||
|
ff_up->retval = ff_up_compat.retval;
|
||||||
|
memcpy(&ff_up->effect, &ff_up_compat.effect,
|
||||||
|
sizeof(struct ff_effect_compat));
|
||||||
|
memcpy(&ff_up->old, &ff_up_compat.old,
|
||||||
|
sizeof(struct ff_effect_compat));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (copy_from_user(ff_up, buffer,
|
||||||
|
sizeof(struct uinput_ff_upload)))
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static int uinput_ff_upload_to_user(char __user *buffer,
|
||||||
|
const struct uinput_ff_upload *ff_up)
|
||||||
|
{
|
||||||
|
if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uinput_ff_upload_from_user(const char __user *buffer,
|
||||||
|
struct uinput_ff_upload *ff_up)
|
||||||
|
{
|
||||||
|
if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#define uinput_set_bit(_arg, _bit, _max) \
|
#define uinput_set_bit(_arg, _bit, _max) \
|
||||||
({ \
|
({ \
|
||||||
int __ret = 0; \
|
int __ret = 0; \
|
||||||
|
@ -455,19 +555,17 @@ static int uinput_release(struct inode *inode, struct file *file)
|
||||||
__ret; \
|
__ret; \
|
||||||
})
|
})
|
||||||
|
|
||||||
static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
|
||||||
|
unsigned long arg, void __user *p)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
struct uinput_device *udev;
|
struct uinput_device *udev = file->private_data;
|
||||||
void __user *p = (void __user *)arg;
|
|
||||||
struct uinput_ff_upload ff_up;
|
struct uinput_ff_upload ff_up;
|
||||||
struct uinput_ff_erase ff_erase;
|
struct uinput_ff_erase ff_erase;
|
||||||
struct uinput_request *req;
|
struct uinput_request *req;
|
||||||
int length;
|
int length;
|
||||||
char *phys;
|
char *phys;
|
||||||
|
|
||||||
udev = file->private_data;
|
|
||||||
|
|
||||||
retval = mutex_lock_interruptible(&udev->mutex);
|
retval = mutex_lock_interruptible(&udev->mutex);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -549,26 +647,24 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UI_BEGIN_FF_UPLOAD:
|
case UI_BEGIN_FF_UPLOAD:
|
||||||
if (copy_from_user(&ff_up, p, sizeof(ff_up))) {
|
retval = uinput_ff_upload_from_user(p, &ff_up);
|
||||||
retval = -EFAULT;
|
if (retval)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
req = uinput_request_find(udev, ff_up.request_id);
|
req = uinput_request_find(udev, ff_up.request_id);
|
||||||
if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) {
|
if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) {
|
||||||
retval = -EINVAL;
|
retval = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ff_up.retval = 0;
|
ff_up.retval = 0;
|
||||||
memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect));
|
ff_up.effect = *req->u.upload.effect;
|
||||||
if (req->u.upload.old)
|
if (req->u.upload.old)
|
||||||
memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect));
|
ff_up.old = *req->u.upload.old;
|
||||||
else
|
else
|
||||||
memset(&ff_up.old, 0, sizeof(struct ff_effect));
|
memset(&ff_up.old, 0, sizeof(struct ff_effect));
|
||||||
|
|
||||||
if (copy_to_user(p, &ff_up, sizeof(ff_up))) {
|
retval = uinput_ff_upload_to_user(p, &ff_up);
|
||||||
retval = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UI_BEGIN_FF_ERASE:
|
case UI_BEGIN_FF_ERASE:
|
||||||
|
@ -576,29 +672,34 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
retval = -EFAULT;
|
retval = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
req = uinput_request_find(udev, ff_erase.request_id);
|
req = uinput_request_find(udev, ff_erase.request_id);
|
||||||
if (!(req && req->code == UI_FF_ERASE)) {
|
if (!req || req->code != UI_FF_ERASE) {
|
||||||
retval = -EINVAL;
|
retval = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ff_erase.retval = 0;
|
ff_erase.retval = 0;
|
||||||
ff_erase.effect_id = req->u.effect_id;
|
ff_erase.effect_id = req->u.effect_id;
|
||||||
if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
|
if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
|
||||||
retval = -EFAULT;
|
retval = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UI_END_FF_UPLOAD:
|
case UI_END_FF_UPLOAD:
|
||||||
if (copy_from_user(&ff_up, p, sizeof(ff_up))) {
|
retval = uinput_ff_upload_from_user(p, &ff_up);
|
||||||
retval = -EFAULT;
|
if (retval)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
req = uinput_request_find(udev, ff_up.request_id);
|
req = uinput_request_find(udev, ff_up.request_id);
|
||||||
if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) {
|
if (!req || req->code != UI_FF_UPLOAD ||
|
||||||
|
!req->u.upload.effect) {
|
||||||
retval = -EINVAL;
|
retval = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
req->retval = ff_up.retval;
|
req->retval = ff_up.retval;
|
||||||
uinput_request_done(udev, req);
|
uinput_request_done(udev, req);
|
||||||
break;
|
break;
|
||||||
|
@ -608,11 +709,13 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
retval = -EFAULT;
|
retval = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
req = uinput_request_find(udev, ff_erase.request_id);
|
req = uinput_request_find(udev, ff_erase.request_id);
|
||||||
if (!(req && req->code == UI_FF_ERASE)) {
|
if (!req || req->code != UI_FF_ERASE) {
|
||||||
retval = -EINVAL;
|
retval = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
req->retval = ff_erase.retval;
|
req->retval = ff_erase.retval;
|
||||||
uinput_request_done(udev, req);
|
uinput_request_done(udev, req);
|
||||||
break;
|
break;
|
||||||
|
@ -626,6 +729,18 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct file_operations uinput_fops = {
|
static const struct file_operations uinput_fops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.open = uinput_open,
|
.open = uinput_open,
|
||||||
|
@ -634,6 +749,9 @@ static const struct file_operations uinput_fops = {
|
||||||
.write = uinput_write,
|
.write = uinput_write,
|
||||||
.poll = uinput_poll,
|
.poll = uinput_poll,
|
||||||
.unlocked_ioctl = uinput_ioctl,
|
.unlocked_ioctl = uinput_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl = uinput_compat_ioctl,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct miscdevice uinput_misc = {
|
static struct miscdevice uinput_misc = {
|
||||||
|
|
|
@ -286,4 +286,10 @@ config MOUSE_GPIO
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called gpio_mouse.
|
module will be called gpio_mouse.
|
||||||
|
|
||||||
|
config MOUSE_PXA930_TRKBALL
|
||||||
|
tristate "PXA930 Trackball mouse"
|
||||||
|
depends on CPU_PXA930 || CPU_PXA935
|
||||||
|
help
|
||||||
|
Say Y here to support PXA930 Trackball mouse.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -4,19 +4,20 @@
|
||||||
|
|
||||||
# Each configuration option enables a list of files.
|
# Each configuration option enables a list of files.
|
||||||
|
|
||||||
obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
|
obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
|
||||||
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
|
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
|
||||||
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
||||||
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
|
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
|
||||||
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
|
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
|
||||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||||
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
||||||
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
|
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
|
||||||
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
|
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
|
||||||
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
|
obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
|
||||||
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
|
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
|
||||||
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
|
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
|
||||||
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
|
||||||
|
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
||||||
|
|
||||||
psmouse-objs := psmouse-base.o synaptics.o
|
psmouse-objs := psmouse-base.o synaptics.o
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
|
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
|
||||||
* Copyright (C) 2005-2008 Johannes Berg (johannes@sipsolutions.net)
|
* Copyright (C) 2005-2008 Johannes Berg (johannes@sipsolutions.net)
|
||||||
* Copyright (C) 2005 Stelian Pop (stelian@popies.net)
|
* Copyright (C) 2005-2008 Stelian Pop (stelian@popies.net)
|
||||||
* Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
|
* Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
|
||||||
* Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
|
* Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
|
||||||
* Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
|
* Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
|
||||||
|
@ -35,16 +35,74 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/usb/input.h>
|
#include <linux/usb/input.h>
|
||||||
|
|
||||||
/* Type of touchpad */
|
/*
|
||||||
enum atp_touchpad_type {
|
* Note: We try to keep the touchpad aspect ratio while still doing only
|
||||||
ATP_FOUNTAIN,
|
* simple arithmetics:
|
||||||
ATP_GEYSER1,
|
* 0 <= x <= (xsensors - 1) * xfact
|
||||||
ATP_GEYSER2,
|
* 0 <= y <= (ysensors - 1) * yfact
|
||||||
ATP_GEYSER3,
|
*/
|
||||||
ATP_GEYSER4
|
struct atp_info {
|
||||||
|
int xsensors; /* number of X sensors */
|
||||||
|
int xsensors_17; /* 17" models have more sensors */
|
||||||
|
int ysensors; /* number of Y sensors */
|
||||||
|
int xfact; /* X multiplication factor */
|
||||||
|
int yfact; /* Y multiplication factor */
|
||||||
|
int datalen; /* size of USB transfers */
|
||||||
|
void (*callback)(struct urb *); /* callback function */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ATP_DEVICE(prod, type) \
|
static void atp_complete_geyser_1_2(struct urb *urb);
|
||||||
|
static void atp_complete_geyser_3_4(struct urb *urb);
|
||||||
|
|
||||||
|
static const struct atp_info fountain_info = {
|
||||||
|
.xsensors = 16,
|
||||||
|
.xsensors_17 = 26,
|
||||||
|
.ysensors = 16,
|
||||||
|
.xfact = 64,
|
||||||
|
.yfact = 43,
|
||||||
|
.datalen = 81,
|
||||||
|
.callback = atp_complete_geyser_1_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct atp_info geyser1_info = {
|
||||||
|
.xsensors = 16,
|
||||||
|
.xsensors_17 = 26,
|
||||||
|
.ysensors = 16,
|
||||||
|
.xfact = 64,
|
||||||
|
.yfact = 43,
|
||||||
|
.datalen = 81,
|
||||||
|
.callback = atp_complete_geyser_1_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct atp_info geyser2_info = {
|
||||||
|
.xsensors = 15,
|
||||||
|
.xsensors_17 = 20,
|
||||||
|
.ysensors = 9,
|
||||||
|
.xfact = 64,
|
||||||
|
.yfact = 43,
|
||||||
|
.datalen = 64,
|
||||||
|
.callback = atp_complete_geyser_1_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct atp_info geyser3_info = {
|
||||||
|
.xsensors = 20,
|
||||||
|
.ysensors = 10,
|
||||||
|
.xfact = 64,
|
||||||
|
.yfact = 64,
|
||||||
|
.datalen = 64,
|
||||||
|
.callback = atp_complete_geyser_3_4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct atp_info geyser4_info = {
|
||||||
|
.xsensors = 20,
|
||||||
|
.ysensors = 10,
|
||||||
|
.xfact = 64,
|
||||||
|
.yfact = 64,
|
||||||
|
.datalen = 64,
|
||||||
|
.callback = atp_complete_geyser_3_4,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ATP_DEVICE(prod, info) \
|
||||||
{ \
|
{ \
|
||||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
|
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
|
||||||
USB_DEVICE_ID_MATCH_INT_CLASS | \
|
USB_DEVICE_ID_MATCH_INT_CLASS | \
|
||||||
|
@ -53,7 +111,7 @@ enum atp_touchpad_type {
|
||||||
.idProduct = (prod), \
|
.idProduct = (prod), \
|
||||||
.bInterfaceClass = 0x03, \
|
.bInterfaceClass = 0x03, \
|
||||||
.bInterfaceProtocol = 0x02, \
|
.bInterfaceProtocol = 0x02, \
|
||||||
.driver_info = ATP_ ## type, \
|
.driver_info = (unsigned long) &info, \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -62,43 +120,39 @@ enum atp_touchpad_type {
|
||||||
* According to Info.plist Geyser IV is the same as Geyser III.)
|
* According to Info.plist Geyser IV is the same as Geyser III.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static struct usb_device_id atp_table [] = {
|
static struct usb_device_id atp_table[] = {
|
||||||
/* PowerBooks Feb 2005, iBooks G4 */
|
/* PowerBooks Feb 2005, iBooks G4 */
|
||||||
ATP_DEVICE(0x020e, FOUNTAIN), /* FOUNTAIN ANSI */
|
ATP_DEVICE(0x020e, fountain_info), /* FOUNTAIN ANSI */
|
||||||
ATP_DEVICE(0x020f, FOUNTAIN), /* FOUNTAIN ISO */
|
ATP_DEVICE(0x020f, fountain_info), /* FOUNTAIN ISO */
|
||||||
ATP_DEVICE(0x030a, FOUNTAIN), /* FOUNTAIN TP ONLY */
|
ATP_DEVICE(0x030a, fountain_info), /* FOUNTAIN TP ONLY */
|
||||||
ATP_DEVICE(0x030b, GEYSER1), /* GEYSER 1 TP ONLY */
|
ATP_DEVICE(0x030b, geyser1_info), /* GEYSER 1 TP ONLY */
|
||||||
|
|
||||||
/* PowerBooks Oct 2005 */
|
/* PowerBooks Oct 2005 */
|
||||||
ATP_DEVICE(0x0214, GEYSER2), /* GEYSER 2 ANSI */
|
ATP_DEVICE(0x0214, geyser2_info), /* GEYSER 2 ANSI */
|
||||||
ATP_DEVICE(0x0215, GEYSER2), /* GEYSER 2 ISO */
|
ATP_DEVICE(0x0215, geyser2_info), /* GEYSER 2 ISO */
|
||||||
ATP_DEVICE(0x0216, GEYSER2), /* GEYSER 2 JIS */
|
ATP_DEVICE(0x0216, geyser2_info), /* GEYSER 2 JIS */
|
||||||
|
|
||||||
/* Core Duo MacBook & MacBook Pro */
|
/* Core Duo MacBook & MacBook Pro */
|
||||||
ATP_DEVICE(0x0217, GEYSER3), /* GEYSER 3 ANSI */
|
ATP_DEVICE(0x0217, geyser3_info), /* GEYSER 3 ANSI */
|
||||||
ATP_DEVICE(0x0218, GEYSER3), /* GEYSER 3 ISO */
|
ATP_DEVICE(0x0218, geyser3_info), /* GEYSER 3 ISO */
|
||||||
ATP_DEVICE(0x0219, GEYSER3), /* GEYSER 3 JIS */
|
ATP_DEVICE(0x0219, geyser3_info), /* GEYSER 3 JIS */
|
||||||
|
|
||||||
/* Core2 Duo MacBook & MacBook Pro */
|
/* Core2 Duo MacBook & MacBook Pro */
|
||||||
ATP_DEVICE(0x021a, GEYSER4), /* GEYSER 4 ANSI */
|
ATP_DEVICE(0x021a, geyser4_info), /* GEYSER 4 ANSI */
|
||||||
ATP_DEVICE(0x021b, GEYSER4), /* GEYSER 4 ISO */
|
ATP_DEVICE(0x021b, geyser4_info), /* GEYSER 4 ISO */
|
||||||
ATP_DEVICE(0x021c, GEYSER4), /* GEYSER 4 JIS */
|
ATP_DEVICE(0x021c, geyser4_info), /* GEYSER 4 JIS */
|
||||||
|
|
||||||
/* Core2 Duo MacBook3,1 */
|
/* Core2 Duo MacBook3,1 */
|
||||||
ATP_DEVICE(0x0229, GEYSER4), /* GEYSER 4 HF ANSI */
|
ATP_DEVICE(0x0229, geyser4_info), /* GEYSER 4 HF ANSI */
|
||||||
ATP_DEVICE(0x022a, GEYSER4), /* GEYSER 4 HF ISO */
|
ATP_DEVICE(0x022a, geyser4_info), /* GEYSER 4 HF ISO */
|
||||||
ATP_DEVICE(0x022b, GEYSER4), /* GEYSER 4 HF JIS */
|
ATP_DEVICE(0x022b, geyser4_info), /* GEYSER 4 HF JIS */
|
||||||
|
|
||||||
/* Terminating entry */
|
/* Terminating entry */
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(usb, atp_table);
|
MODULE_DEVICE_TABLE(usb, atp_table);
|
||||||
|
|
||||||
/*
|
/* maximum number of sensors */
|
||||||
* number of sensors. Note that only 16 instead of 26 X (horizontal)
|
|
||||||
* sensors exist on 12" and 15" PowerBooks. All models have 16 Y
|
|
||||||
* (vertical) sensors.
|
|
||||||
*/
|
|
||||||
#define ATP_XSENSORS 26
|
#define ATP_XSENSORS 26
|
||||||
#define ATP_YSENSORS 16
|
#define ATP_YSENSORS 16
|
||||||
|
|
||||||
|
@ -107,21 +161,6 @@ MODULE_DEVICE_TABLE(usb, atp_table);
|
||||||
|
|
||||||
/* maximum pressure this driver will report */
|
/* maximum pressure this driver will report */
|
||||||
#define ATP_PRESSURE 300
|
#define ATP_PRESSURE 300
|
||||||
/*
|
|
||||||
* multiplication factor for the X and Y coordinates.
|
|
||||||
* We try to keep the touchpad aspect ratio while still doing only simple
|
|
||||||
* arithmetics.
|
|
||||||
* The factors below give coordinates like:
|
|
||||||
*
|
|
||||||
* 0 <= x < 960 on 12" and 15" Powerbooks
|
|
||||||
* 0 <= x < 1600 on 17" Powerbooks and 17" MacBook Pro
|
|
||||||
* 0 <= x < 1216 on MacBooks and 15" MacBook Pro
|
|
||||||
*
|
|
||||||
* 0 <= y < 646 on all Powerbooks
|
|
||||||
* 0 <= y < 774 on all MacBooks
|
|
||||||
*/
|
|
||||||
#define ATP_XFACT 64
|
|
||||||
#define ATP_YFACT 43
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
|
* Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
|
||||||
|
@ -159,7 +198,7 @@ struct atp {
|
||||||
struct urb *urb; /* usb request block */
|
struct urb *urb; /* usb request block */
|
||||||
u8 *data; /* transferred data */
|
u8 *data; /* transferred data */
|
||||||
struct input_dev *input; /* input dev */
|
struct input_dev *input; /* input dev */
|
||||||
enum atp_touchpad_type type; /* type of touchpad */
|
const struct atp_info *info; /* touchpad model */
|
||||||
bool open;
|
bool open;
|
||||||
bool valid; /* are the samples valid? */
|
bool valid; /* are the samples valid? */
|
||||||
bool size_detect_done;
|
bool size_detect_done;
|
||||||
|
@ -169,7 +208,6 @@ struct atp {
|
||||||
signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
|
signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
|
||||||
signed char xy_old[ATP_XSENSORS + ATP_YSENSORS];
|
signed char xy_old[ATP_XSENSORS + ATP_YSENSORS];
|
||||||
int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
|
int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
|
||||||
int datalen; /* size of USB transfer */
|
|
||||||
int idlecount; /* number of empty packets */
|
int idlecount; /* number of empty packets */
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
};
|
};
|
||||||
|
@ -359,7 +397,7 @@ static int atp_status_check(struct urb *urb)
|
||||||
if (!dev->overflow_warned) {
|
if (!dev->overflow_warned) {
|
||||||
printk(KERN_WARNING "appletouch: OVERFLOW with data "
|
printk(KERN_WARNING "appletouch: OVERFLOW with data "
|
||||||
"length %d, actual length is %d\n",
|
"length %d, actual length is %d\n",
|
||||||
dev->datalen, dev->urb->actual_length);
|
dev->info->datalen, dev->urb->actual_length);
|
||||||
dev->overflow_warned = true;
|
dev->overflow_warned = true;
|
||||||
}
|
}
|
||||||
case -ECONNRESET:
|
case -ECONNRESET:
|
||||||
|
@ -377,7 +415,7 @@ static int atp_status_check(struct urb *urb)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* drop incomplete datasets */
|
/* drop incomplete datasets */
|
||||||
if (dev->urb->actual_length != dev->datalen) {
|
if (dev->urb->actual_length != dev->info->datalen) {
|
||||||
dprintk("appletouch: incomplete data package"
|
dprintk("appletouch: incomplete data package"
|
||||||
" (first byte: %d, length: %d).\n",
|
" (first byte: %d, length: %d).\n",
|
||||||
dev->data[0], dev->urb->actual_length);
|
dev->data[0], dev->urb->actual_length);
|
||||||
|
@ -387,6 +425,25 @@ static int atp_status_check(struct urb *urb)
|
||||||
return ATP_URB_STATUS_SUCCESS;
|
return ATP_URB_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void atp_detect_size(struct atp *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* 17" Powerbooks have extra X sensors */
|
||||||
|
for (i = dev->info->xsensors; i < ATP_XSENSORS; i++) {
|
||||||
|
if (dev->xy_cur[i]) {
|
||||||
|
|
||||||
|
printk(KERN_INFO "appletouch: 17\" model detected.\n");
|
||||||
|
|
||||||
|
input_set_abs_params(dev->input, ABS_X, 0,
|
||||||
|
(dev->info->xsensors_17 - 1) *
|
||||||
|
dev->info->xfact - 1,
|
||||||
|
ATP_FUZZ, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* USB interrupt callback functions
|
* USB interrupt callback functions
|
||||||
*/
|
*/
|
||||||
|
@ -407,7 +464,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
/* reorder the sensors values */
|
/* reorder the sensors values */
|
||||||
if (dev->type == ATP_GEYSER2) {
|
if (dev->info == &geyser2_info) {
|
||||||
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
|
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -437,8 +494,8 @@ static void atp_complete_geyser_1_2(struct urb *urb)
|
||||||
dev->xy_cur[i + 24] = dev->data[5 * i + 44];
|
dev->xy_cur[i + 24] = dev->data[5 * i + 44];
|
||||||
|
|
||||||
/* Y values */
|
/* Y values */
|
||||||
dev->xy_cur[i + 26] = dev->data[5 * i + 1];
|
dev->xy_cur[ATP_XSENSORS + i] = dev->data[5 * i + 1];
|
||||||
dev->xy_cur[i + 34] = dev->data[5 * i + 3];
|
dev->xy_cur[ATP_XSENSORS + i + 8] = dev->data[5 * i + 3];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,32 +510,8 @@ static void atp_complete_geyser_1_2(struct urb *urb)
|
||||||
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
|
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
|
||||||
|
|
||||||
/* Perform size detection, if not done already */
|
/* Perform size detection, if not done already */
|
||||||
if (!dev->size_detect_done) {
|
if (unlikely(!dev->size_detect_done)) {
|
||||||
|
atp_detect_size(dev);
|
||||||
/* 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;
|
dev->size_detect_done = 1;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
@ -499,10 +532,10 @@ static void atp_complete_geyser_1_2(struct urb *urb)
|
||||||
dbg_dump("accumulator", dev->xy_acc);
|
dbg_dump("accumulator", dev->xy_acc);
|
||||||
|
|
||||||
x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
|
x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
|
||||||
ATP_XFACT, &x_z, &x_f);
|
dev->info->xfact, &x_z, &x_f);
|
||||||
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
|
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
|
||||||
ATP_YFACT, &y_z, &y_f);
|
dev->info->yfact, &y_z, &y_f);
|
||||||
key = dev->data[dev->datalen - 1] & ATP_STATUS_BUTTON;
|
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
|
||||||
|
|
||||||
if (x && y) {
|
if (x && y) {
|
||||||
if (dev->x_old != -1) {
|
if (dev->x_old != -1) {
|
||||||
|
@ -583,7 +616,7 @@ static void atp_complete_geyser_3_4(struct urb *urb)
|
||||||
dbg_dump("sample", dev->xy_cur);
|
dbg_dump("sample", dev->xy_cur);
|
||||||
|
|
||||||
/* Just update the base values (i.e. touchpad in untouched state) */
|
/* Just update the base values (i.e. touchpad in untouched state) */
|
||||||
if (dev->data[dev->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
|
if (dev->data[dev->info->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
|
||||||
|
|
||||||
dprintk(KERN_DEBUG "appletouch: updated base values\n");
|
dprintk(KERN_DEBUG "appletouch: updated base values\n");
|
||||||
|
|
||||||
|
@ -610,10 +643,10 @@ static void atp_complete_geyser_3_4(struct urb *urb)
|
||||||
dbg_dump("accumulator", dev->xy_acc);
|
dbg_dump("accumulator", dev->xy_acc);
|
||||||
|
|
||||||
x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
|
x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
|
||||||
ATP_XFACT, &x_z, &x_f);
|
dev->info->xfact, &x_z, &x_f);
|
||||||
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
|
y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
|
||||||
ATP_YFACT, &y_z, &y_f);
|
dev->info->yfact, &y_z, &y_f);
|
||||||
key = dev->data[dev->datalen - 1] & ATP_STATUS_BUTTON;
|
key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
|
||||||
|
|
||||||
if (x && y) {
|
if (x && y) {
|
||||||
if (dev->x_old != -1) {
|
if (dev->x_old != -1) {
|
||||||
|
@ -705,7 +738,7 @@ static int atp_handle_geyser(struct atp *dev)
|
||||||
{
|
{
|
||||||
struct usb_device *udev = dev->udev;
|
struct usb_device *udev = dev->udev;
|
||||||
|
|
||||||
if (dev->type != ATP_FOUNTAIN) {
|
if (dev->info != &fountain_info) {
|
||||||
/* switch to raw sensor mode */
|
/* switch to raw sensor mode */
|
||||||
if (atp_geyser_init(udev))
|
if (atp_geyser_init(udev))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -726,6 +759,7 @@ static int atp_probe(struct usb_interface *iface,
|
||||||
struct usb_endpoint_descriptor *endpoint;
|
struct usb_endpoint_descriptor *endpoint;
|
||||||
int int_in_endpointAddr = 0;
|
int int_in_endpointAddr = 0;
|
||||||
int i, error = -ENOMEM;
|
int i, error = -ENOMEM;
|
||||||
|
const struct atp_info *info = (const struct atp_info *)id->driver_info;
|
||||||
|
|
||||||
/* set up the endpoint information */
|
/* set up the endpoint information */
|
||||||
/* use only the first interrupt-in endpoint */
|
/* use only the first interrupt-in endpoint */
|
||||||
|
@ -753,35 +787,22 @@ static int atp_probe(struct usb_interface *iface,
|
||||||
|
|
||||||
dev->udev = udev;
|
dev->udev = udev;
|
||||||
dev->input = input_dev;
|
dev->input = input_dev;
|
||||||
dev->type = id->driver_info;
|
dev->info = info;
|
||||||
dev->overflow_warned = false;
|
dev->overflow_warned = false;
|
||||||
if (dev->type == ATP_FOUNTAIN || dev->type == ATP_GEYSER1)
|
|
||||||
dev->datalen = 81;
|
|
||||||
else
|
|
||||||
dev->datalen = 64;
|
|
||||||
|
|
||||||
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
|
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||||
if (!dev->urb)
|
if (!dev->urb)
|
||||||
goto err_free_devs;
|
goto err_free_devs;
|
||||||
|
|
||||||
dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
|
dev->data = usb_buffer_alloc(dev->udev, dev->info->datalen, GFP_KERNEL,
|
||||||
&dev->urb->transfer_dma);
|
&dev->urb->transfer_dma);
|
||||||
if (!dev->data)
|
if (!dev->data)
|
||||||
goto err_free_urb;
|
goto err_free_urb;
|
||||||
|
|
||||||
/* Select the USB complete (callback) function */
|
usb_fill_int_urb(dev->urb, udev,
|
||||||
if (dev->type == ATP_FOUNTAIN ||
|
usb_rcvintpipe(udev, int_in_endpointAddr),
|
||||||
dev->type == ATP_GEYSER1 ||
|
dev->data, dev->info->datalen,
|
||||||
dev->type == ATP_GEYSER2)
|
dev->info->callback, dev, 1);
|
||||||
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);
|
error = atp_handle_geyser(dev);
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -802,35 +823,12 @@ static int atp_probe(struct usb_interface *iface,
|
||||||
|
|
||||||
set_bit(EV_ABS, input_dev->evbit);
|
set_bit(EV_ABS, input_dev->evbit);
|
||||||
|
|
||||||
if (dev->type == ATP_GEYSER3 || dev->type == ATP_GEYSER4) {
|
input_set_abs_params(input_dev, ABS_X, 0,
|
||||||
/*
|
(dev->info->xsensors - 1) * dev->info->xfact - 1,
|
||||||
* MacBook have 20 X sensors, 10 Y sensors
|
ATP_FUZZ, 0);
|
||||||
*/
|
input_set_abs_params(input_dev, ABS_Y, 0,
|
||||||
input_set_abs_params(input_dev, ABS_X, 0,
|
(dev->info->ysensors - 1) * dev->info->yfact - 1,
|
||||||
((20 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
|
ATP_FUZZ, 0);
|
||||||
input_set_abs_params(input_dev, ABS_Y, 0,
|
|
||||||
((10 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
|
|
||||||
} else if (dev->type == ATP_GEYSER2) {
|
|
||||||
/*
|
|
||||||
* Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected
|
|
||||||
* later.
|
|
||||||
*/
|
|
||||||
input_set_abs_params(input_dev, ABS_X, 0,
|
|
||||||
((15 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
|
|
||||||
input_set_abs_params(input_dev, ABS_Y, 0,
|
|
||||||
((9 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* 12" and 15" Powerbooks only have 16 x sensors,
|
|
||||||
* 17" models are detected later.
|
|
||||||
*/
|
|
||||||
input_set_abs_params(input_dev, ABS_X, 0,
|
|
||||||
(16 - 1) * ATP_XFACT - 1,
|
|
||||||
ATP_FUZZ, 0);
|
|
||||||
input_set_abs_params(input_dev, ABS_Y, 0,
|
|
||||||
(ATP_YSENSORS - 1) * ATP_YFACT - 1,
|
|
||||||
ATP_FUZZ, 0);
|
|
||||||
}
|
|
||||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
|
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
|
||||||
|
|
||||||
set_bit(EV_KEY, input_dev->evbit);
|
set_bit(EV_KEY, input_dev->evbit);
|
||||||
|
@ -852,7 +850,7 @@ static int atp_probe(struct usb_interface *iface,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_buffer:
|
err_free_buffer:
|
||||||
usb_buffer_free(dev->udev, dev->datalen,
|
usb_buffer_free(dev->udev, dev->info->datalen,
|
||||||
dev->data, dev->urb->transfer_dma);
|
dev->data, dev->urb->transfer_dma);
|
||||||
err_free_urb:
|
err_free_urb:
|
||||||
usb_free_urb(dev->urb);
|
usb_free_urb(dev->urb);
|
||||||
|
@ -871,7 +869,7 @@ static void atp_disconnect(struct usb_interface *iface)
|
||||||
if (dev) {
|
if (dev) {
|
||||||
usb_kill_urb(dev->urb);
|
usb_kill_urb(dev->urb);
|
||||||
input_unregister_device(dev->input);
|
input_unregister_device(dev->input);
|
||||||
usb_buffer_free(dev->udev, dev->datalen,
|
usb_buffer_free(dev->udev, dev->info->datalen,
|
||||||
dev->data, dev->urb->transfer_dma);
|
dev->data, dev->urb->transfer_dma);
|
||||||
usb_free_urb(dev->urb);
|
usb_free_urb(dev->urb);
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
|
|
|
@ -173,7 +173,7 @@ static int __devexit gpio_mouse_remove(struct platform_device *pdev)
|
||||||
/* work with hotplug and coldplug */
|
/* work with hotplug and coldplug */
|
||||||
MODULE_ALIAS("platform:gpio_mouse");
|
MODULE_ALIAS("platform:gpio_mouse");
|
||||||
|
|
||||||
struct platform_driver gpio_mouse_device_driver = {
|
static struct platform_driver gpio_mouse_device_driver = {
|
||||||
.remove = __devexit_p(gpio_mouse_remove),
|
.remove = __devexit_p(gpio_mouse_remove),
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gpio_mouse",
|
.name = "gpio_mouse",
|
||||||
|
|
|
@ -48,6 +48,30 @@ module_param(recalib_delta, int, 0644);
|
||||||
MODULE_PARM_DESC(recalib_delta,
|
MODULE_PARM_DESC(recalib_delta,
|
||||||
"packets containing a delta this large will cause a recalibration.");
|
"packets containing a delta this large will cause a recalibration.");
|
||||||
|
|
||||||
|
static int jumpy_delay = 1000;
|
||||||
|
module_param(jumpy_delay, int, 0644);
|
||||||
|
MODULE_PARM_DESC(jumpy_delay,
|
||||||
|
"delay (ms) before recal after jumpiness detected");
|
||||||
|
|
||||||
|
static int spew_delay = 1000;
|
||||||
|
module_param(spew_delay, int, 0644);
|
||||||
|
MODULE_PARM_DESC(spew_delay,
|
||||||
|
"delay (ms) before recal after packet spew detected");
|
||||||
|
|
||||||
|
static int recal_guard_time = 2000;
|
||||||
|
module_param(recal_guard_time, int, 0644);
|
||||||
|
MODULE_PARM_DESC(recal_guard_time,
|
||||||
|
"interval (ms) during which recal will be restarted if packet received");
|
||||||
|
|
||||||
|
static int post_interrupt_delay = 1000;
|
||||||
|
module_param(post_interrupt_delay, int, 0644);
|
||||||
|
MODULE_PARM_DESC(post_interrupt_delay,
|
||||||
|
"delay (ms) before recal after recal interrupt detected");
|
||||||
|
|
||||||
|
static int autorecal = 1;
|
||||||
|
module_param(autorecal, int, 0644);
|
||||||
|
MODULE_PARM_DESC(autorecal, "enable recalibration in the driver");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When the touchpad gets ultra-sensitive, one can keep their finger 1/2"
|
* 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
|
* above the pad and still have it send packets. This causes a jump cursor
|
||||||
|
@ -66,7 +90,7 @@ static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y)
|
||||||
/* My car gets forty rods to the hogshead and that's the
|
/* My car gets forty rods to the hogshead and that's the
|
||||||
* way I likes it! */
|
* way I likes it! */
|
||||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||||
msecs_to_jiffies(1000));
|
msecs_to_jiffies(jumpy_delay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +127,7 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
|
||||||
hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n",
|
hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n",
|
||||||
priv->x_tally, priv->y_tally);
|
priv->x_tally, priv->y_tally);
|
||||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||||
msecs_to_jiffies(1000));
|
msecs_to_jiffies(spew_delay));
|
||||||
}
|
}
|
||||||
/* reset every 100 packets */
|
/* reset every 100 packets */
|
||||||
priv->count = 0;
|
priv->count = 0;
|
||||||
|
@ -181,7 +205,7 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
|
||||||
"packet inside calibration window, "
|
"packet inside calibration window, "
|
||||||
"queueing another recalibration\n");
|
"queueing another recalibration\n");
|
||||||
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
psmouse_queue_work(psmouse, &priv->recalib_wq,
|
||||||
msecs_to_jiffies(1000));
|
msecs_to_jiffies(post_interrupt_delay));
|
||||||
}
|
}
|
||||||
priv->recalib_window = 0;
|
priv->recalib_window = 0;
|
||||||
}
|
}
|
||||||
|
@ -231,7 +255,7 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse)
|
||||||
* If someone's finger *was* on the touchpad, it's probably
|
* If someone's finger *was* on the touchpad, it's probably
|
||||||
* miscalibrated. So, we should schedule another recalibration
|
* miscalibrated. So, we should schedule another recalibration
|
||||||
*/
|
*/
|
||||||
priv->recalib_window = jiffies + msecs_to_jiffies(2000);
|
priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
|
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
|
||||||
MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
|
MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
|
||||||
MODULE_LICENSE("Dual BSD/GPL");
|
MODULE_LICENSE("Dual BSD/GPL");
|
||||||
|
MODULE_ALIAS("serio:ty03pr25id0Fex*");
|
||||||
|
|
||||||
#define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */
|
#define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */
|
||||||
#undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */
|
#undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */
|
||||||
|
|
269
drivers/input/mouse/pxa930_trkball.c
Normal file
269
drivers/input/mouse/pxa930_trkball.c
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* PXA930 track ball mouse driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007 Marvell International Ltd.
|
||||||
|
* 2008-02-28: Yong Yao <yaoyong@marvell.com>
|
||||||
|
* initial version
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
#include <mach/hardware.h>
|
||||||
|
#include <mach/pxa930_trkball.h>
|
||||||
|
|
||||||
|
/* Trackball Controller Register Definitions */
|
||||||
|
#define TBCR (0x000C)
|
||||||
|
#define TBCNTR (0x0010)
|
||||||
|
#define TBSBC (0x0014)
|
||||||
|
|
||||||
|
#define TBCR_TBRST (1 << 1)
|
||||||
|
#define TBCR_TBSB (1 << 10)
|
||||||
|
|
||||||
|
#define TBCR_Y_FLT(n) (((n) & 0xf) << 6)
|
||||||
|
#define TBCR_X_FLT(n) (((n) & 0xf) << 2)
|
||||||
|
|
||||||
|
#define TBCNTR_YM(n) (((n) >> 24) & 0xff)
|
||||||
|
#define TBCNTR_YP(n) (((n) >> 16) & 0xff)
|
||||||
|
#define TBCNTR_XM(n) (((n) >> 8) & 0xff)
|
||||||
|
#define TBCNTR_XP(n) ((n) & 0xff)
|
||||||
|
|
||||||
|
#define TBSBC_TBSBC (0x1)
|
||||||
|
|
||||||
|
struct pxa930_trkball {
|
||||||
|
struct pxa930_trkball_platform_data *pdata;
|
||||||
|
|
||||||
|
/* Memory Mapped Register */
|
||||||
|
struct resource *mem;
|
||||||
|
void __iomem *mmio_base;
|
||||||
|
|
||||||
|
struct input_dev *input;
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t pxa930_trkball_interrupt(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct pxa930_trkball *trkball = dev_id;
|
||||||
|
struct input_dev *input = trkball->input;
|
||||||
|
int tbcntr, x, y;
|
||||||
|
|
||||||
|
/* According to the spec software must read TBCNTR twice:
|
||||||
|
* if the read value is the same, the reading is valid
|
||||||
|
*/
|
||||||
|
tbcntr = __raw_readl(trkball->mmio_base + TBCNTR);
|
||||||
|
|
||||||
|
if (tbcntr == __raw_readl(trkball->mmio_base + TBCNTR)) {
|
||||||
|
x = (TBCNTR_XP(tbcntr) - TBCNTR_XM(tbcntr)) / 2;
|
||||||
|
y = (TBCNTR_YP(tbcntr) - TBCNTR_YM(tbcntr)) / 2;
|
||||||
|
|
||||||
|
input_report_rel(input, REL_X, x);
|
||||||
|
input_report_rel(input, REL_Y, y);
|
||||||
|
input_sync(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
__raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
|
||||||
|
__raw_writel(0, trkball->mmio_base + TBSBC);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For TBCR, we need to wait for a while to make sure it has been modified. */
|
||||||
|
static int write_tbcr(struct pxa930_trkball *trkball, int v)
|
||||||
|
{
|
||||||
|
int i = 100;
|
||||||
|
|
||||||
|
__raw_writel(v, trkball->mmio_base + TBCR);
|
||||||
|
|
||||||
|
while (i--) {
|
||||||
|
if (__raw_readl(trkball->mmio_base + TBCR) == v)
|
||||||
|
break;
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
pr_err("%s: timed out writing TBCR(%x)!\n", __func__, v);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxa930_trkball_config(struct pxa930_trkball *trkball)
|
||||||
|
{
|
||||||
|
uint32_t tbcr;
|
||||||
|
|
||||||
|
/* According to spec, need to write the filters of x,y to 0xf first! */
|
||||||
|
tbcr = __raw_readl(trkball->mmio_base + TBCR);
|
||||||
|
write_tbcr(trkball, tbcr | TBCR_X_FLT(0xf) | TBCR_Y_FLT(0xf));
|
||||||
|
write_tbcr(trkball, TBCR_X_FLT(trkball->pdata->x_filter) |
|
||||||
|
TBCR_Y_FLT(trkball->pdata->y_filter));
|
||||||
|
|
||||||
|
/* According to spec, set TBCR_TBRST first, before clearing it! */
|
||||||
|
tbcr = __raw_readl(trkball->mmio_base + TBCR);
|
||||||
|
write_tbcr(trkball, tbcr | TBCR_TBRST);
|
||||||
|
write_tbcr(trkball, tbcr & ~TBCR_TBRST);
|
||||||
|
|
||||||
|
__raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
|
||||||
|
__raw_writel(0, trkball->mmio_base + TBSBC);
|
||||||
|
|
||||||
|
pr_debug("%s: final TBCR=%x!\n", __func__,
|
||||||
|
__raw_readl(trkball->mmio_base + TBCR));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pxa930_trkball_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct pxa930_trkball *trkball = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
pxa930_trkball_config(trkball);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxa930_trkball_disable(struct pxa930_trkball *trkball)
|
||||||
|
{
|
||||||
|
uint32_t tbcr = __raw_readl(trkball->mmio_base + TBCR);
|
||||||
|
|
||||||
|
/* Held in reset, gate the 32-KHz input clock off */
|
||||||
|
write_tbcr(trkball, tbcr | TBCR_TBRST);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pxa930_trkball_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct pxa930_trkball *trkball = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
pxa930_trkball_disable(trkball);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit pxa930_trkball_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct pxa930_trkball *trkball;
|
||||||
|
struct input_dev *input;
|
||||||
|
struct resource *res;
|
||||||
|
int irq, error;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "failed to get trkball irq\n");
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res) {
|
||||||
|
dev_err(&pdev->dev, "failed to get register memory\n");
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
trkball = kzalloc(sizeof(struct pxa930_trkball), GFP_KERNEL);
|
||||||
|
if (!trkball)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
trkball->pdata = pdev->dev.platform_data;
|
||||||
|
if (!trkball->pdata) {
|
||||||
|
dev_err(&pdev->dev, "no platform data defined\n");
|
||||||
|
error = -EINVAL;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
trkball->mmio_base = ioremap_nocache(res->start, resource_size(res));
|
||||||
|
if (!trkball->mmio_base) {
|
||||||
|
dev_err(&pdev->dev, "failed to ioremap registers\n");
|
||||||
|
error = -ENXIO;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* held the module in reset, will be enabled in open() */
|
||||||
|
pxa930_trkball_disable(trkball);
|
||||||
|
|
||||||
|
error = request_irq(irq, pxa930_trkball_interrupt, IRQF_DISABLED,
|
||||||
|
pdev->name, trkball);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
|
||||||
|
goto failed_free_io;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, trkball);
|
||||||
|
|
||||||
|
input = input_allocate_device();
|
||||||
|
if (!input) {
|
||||||
|
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto failed_free_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
input->name = pdev->name;
|
||||||
|
input->id.bustype = BUS_HOST;
|
||||||
|
input->open = pxa930_trkball_open;
|
||||||
|
input->close = pxa930_trkball_close;
|
||||||
|
input->dev.parent = &pdev->dev;
|
||||||
|
input_set_drvdata(input, trkball);
|
||||||
|
|
||||||
|
trkball->input = input;
|
||||||
|
|
||||||
|
input_set_capability(input, EV_REL, REL_X);
|
||||||
|
input_set_capability(input, EV_REL, REL_Y);
|
||||||
|
|
||||||
|
error = input_register_device(input);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&pdev->dev, "unable to register input device\n");
|
||||||
|
goto failed_free_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
failed_free_input:
|
||||||
|
input_free_device(input);
|
||||||
|
failed_free_irq:
|
||||||
|
free_irq(irq, trkball);
|
||||||
|
failed_free_io:
|
||||||
|
iounmap(trkball->mmio_base);
|
||||||
|
failed:
|
||||||
|
kfree(trkball);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit pxa930_trkball_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct pxa930_trkball *trkball = platform_get_drvdata(pdev);
|
||||||
|
int irq = platform_get_irq(pdev, 0);
|
||||||
|
|
||||||
|
input_unregister_device(trkball->input);
|
||||||
|
free_irq(irq, trkball);
|
||||||
|
iounmap(trkball->mmio_base);
|
||||||
|
kfree(trkball);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver pxa930_trkball_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "pxa930-trkball",
|
||||||
|
},
|
||||||
|
.probe = pxa930_trkball_probe,
|
||||||
|
.remove = __devexit_p(pxa930_trkball_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init pxa930_trkball_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&pxa930_trkball_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit pxa930_trkball_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&pxa930_trkball_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(pxa930_trkball_init);
|
||||||
|
module_exit(pxa930_trkball_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Yong Yao <yaoyong@marvell.com>");
|
||||||
|
MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -445,12 +445,14 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||||
|
|
||||||
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
|
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
|
||||||
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
|
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
|
||||||
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
|
|
||||||
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
|
|
||||||
|
|
||||||
input_report_key(dev, BTN_LEFT, hw.left);
|
input_report_key(dev, BTN_LEFT, hw.left);
|
||||||
input_report_key(dev, BTN_RIGHT, hw.right);
|
input_report_key(dev, BTN_RIGHT, hw.right);
|
||||||
|
|
||||||
|
if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
|
||||||
|
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
|
||||||
|
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
|
||||||
|
}
|
||||||
|
|
||||||
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
|
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
|
||||||
input_report_key(dev, BTN_MIDDLE, hw.middle);
|
input_report_key(dev, BTN_MIDDLE, hw.middle);
|
||||||
|
|
||||||
|
@ -543,12 +545,14 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
|
||||||
set_bit(EV_KEY, dev->evbit);
|
set_bit(EV_KEY, dev->evbit);
|
||||||
set_bit(BTN_TOUCH, dev->keybit);
|
set_bit(BTN_TOUCH, dev->keybit);
|
||||||
set_bit(BTN_TOOL_FINGER, dev->keybit);
|
set_bit(BTN_TOOL_FINGER, dev->keybit);
|
||||||
set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
|
|
||||||
set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
|
|
||||||
|
|
||||||
set_bit(BTN_LEFT, dev->keybit);
|
set_bit(BTN_LEFT, dev->keybit);
|
||||||
set_bit(BTN_RIGHT, dev->keybit);
|
set_bit(BTN_RIGHT, dev->keybit);
|
||||||
|
|
||||||
|
if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
|
||||||
|
set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
|
||||||
|
set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
|
||||||
|
}
|
||||||
|
|
||||||
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
|
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
|
||||||
set_bit(BTN_MIDDLE, dev->keybit);
|
set_bit(BTN_MIDDLE, dev->keybit);
|
||||||
|
|
||||||
|
|
|
@ -878,8 +878,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
|
||||||
mousedev->handle.handler = handler;
|
mousedev->handle.handler = handler;
|
||||||
mousedev->handle.private = mousedev;
|
mousedev->handle.private = mousedev;
|
||||||
|
|
||||||
strlcpy(mousedev->dev.bus_id, mousedev->name,
|
dev_set_name(&mousedev->dev, mousedev->name);
|
||||||
sizeof(mousedev->dev.bus_id));
|
|
||||||
mousedev->dev.class = &input_class;
|
mousedev->dev.class = &input_class;
|
||||||
if (dev)
|
if (dev)
|
||||||
mousedev->dev.parent = &dev->dev;
|
mousedev->dev.parent = &dev->dev;
|
||||||
|
|
|
@ -934,6 +934,7 @@ int hil_mlc_register(hil_mlc *mlc)
|
||||||
snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i);
|
snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i);
|
||||||
snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i);
|
snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i);
|
||||||
mlc_serio->id = hil_mlc_serio_id;
|
mlc_serio->id = hil_mlc_serio_id;
|
||||||
|
mlc_serio->id.id = i; /* HIL port no. */
|
||||||
mlc_serio->write = hil_mlc_serio_write;
|
mlc_serio->write = hil_mlc_serio_write;
|
||||||
mlc_serio->open = hil_mlc_serio_open;
|
mlc_serio->open = hil_mlc_serio_open;
|
||||||
mlc_serio->close = hil_mlc_serio_close;
|
mlc_serio->close = hil_mlc_serio_close;
|
||||||
|
|
|
@ -143,6 +143,14 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
|
||||||
DMI_MATCH(DMI_PRODUCT_VERSION, "M606"),
|
DMI_MATCH(DMI_PRODUCT_VERSION, "M606"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.ident = "Gigabyte M912",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "M912"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_VERSION, "01"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -351,6 +359,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"),
|
DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.ident = "Dell Vostro 1510",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -262,9 +262,17 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PS2_RET_NAK:
|
case PS2_RET_NAK:
|
||||||
ps2dev->nak = 1;
|
ps2dev->flags |= PS2_FLAG_NAK;
|
||||||
|
ps2dev->nak = PS2_RET_NAK;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PS2_RET_ERR:
|
||||||
|
if (ps2dev->flags & PS2_FLAG_NAK) {
|
||||||
|
ps2dev->flags &= ~PS2_FLAG_NAK;
|
||||||
|
ps2dev->nak = PS2_RET_ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Workaround for mice which don't ACK the Get ID command.
|
* Workaround for mice which don't ACK the Get ID command.
|
||||||
* These are valid mouse IDs that we recognize.
|
* These are valid mouse IDs that we recognize.
|
||||||
|
@ -282,8 +290,11 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!ps2dev->nak && ps2dev->cmdcnt)
|
if (!ps2dev->nak) {
|
||||||
ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
|
ps2dev->flags &= ~PS2_FLAG_NAK;
|
||||||
|
if (ps2dev->cmdcnt)
|
||||||
|
ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
|
||||||
|
}
|
||||||
|
|
||||||
ps2dev->flags &= ~PS2_FLAG_ACK;
|
ps2dev->flags &= ~PS2_FLAG_ACK;
|
||||||
wake_up(&ps2dev->wait);
|
wake_up(&ps2dev->wait);
|
||||||
|
@ -329,6 +340,7 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev)
|
||||||
if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
|
if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
|
||||||
wake_up(&ps2dev->wait);
|
wake_up(&ps2dev->wait);
|
||||||
|
|
||||||
ps2dev->flags = 0;
|
/* reset all flags except last nack */
|
||||||
|
ps2dev->flags &= PS2_FLAG_NAK;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ps2_cmd_aborted);
|
EXPORT_SYMBOL(ps2_cmd_aborted);
|
||||||
|
|
|
@ -153,7 +153,7 @@ static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_i
|
||||||
serio->open = pcips2_open;
|
serio->open = pcips2_open;
|
||||||
serio->close = pcips2_close;
|
serio->close = pcips2_close;
|
||||||
strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
|
strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
|
||||||
strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys));
|
strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
|
||||||
serio->port_data = ps2if;
|
serio->port_data = ps2if;
|
||||||
serio->dev.parent = &dev->dev;
|
serio->dev.parent = &dev->dev;
|
||||||
ps2if->io = serio;
|
ps2if->io = serio;
|
||||||
|
|
|
@ -546,8 +546,8 @@ static void serio_init_port(struct serio *serio)
|
||||||
spin_lock_init(&serio->lock);
|
spin_lock_init(&serio->lock);
|
||||||
mutex_init(&serio->drv_mutex);
|
mutex_init(&serio->drv_mutex);
|
||||||
device_initialize(&serio->dev);
|
device_initialize(&serio->dev);
|
||||||
snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id),
|
dev_set_name(&serio->dev, "serio%ld",
|
||||||
"serio%ld", (long)atomic_inc_return(&serio_no) - 1);
|
(long)atomic_inc_return(&serio_no) - 1);
|
||||||
serio->dev.bus = &serio_bus;
|
serio->dev.bus = &serio_bus;
|
||||||
serio->dev.release = serio_release_port;
|
serio->dev.release = serio_release_port;
|
||||||
if (serio->parent) {
|
if (serio->parent) {
|
||||||
|
|
|
@ -58,23 +58,20 @@
|
||||||
|
|
||||||
/* Mask for all the Receive Interrupts */
|
/* Mask for all the Receive Interrupts */
|
||||||
#define XPS2_IPIXR_RX_ALL (XPS2_IPIXR_RX_OVF | XPS2_IPIXR_RX_ERR | \
|
#define XPS2_IPIXR_RX_ALL (XPS2_IPIXR_RX_OVF | XPS2_IPIXR_RX_ERR | \
|
||||||
XPS2_IPIXR_RX_FULL)
|
XPS2_IPIXR_RX_FULL)
|
||||||
|
|
||||||
/* Mask for all the Interrupts */
|
/* Mask for all the Interrupts */
|
||||||
#define XPS2_IPIXR_ALL (XPS2_IPIXR_TX_ALL | XPS2_IPIXR_RX_ALL | \
|
#define XPS2_IPIXR_ALL (XPS2_IPIXR_TX_ALL | XPS2_IPIXR_RX_ALL | \
|
||||||
XPS2_IPIXR_WDT_TOUT)
|
XPS2_IPIXR_WDT_TOUT)
|
||||||
|
|
||||||
/* Global Interrupt Enable mask */
|
/* Global Interrupt Enable mask */
|
||||||
#define XPS2_GIER_GIE_MASK 0x80000000
|
#define XPS2_GIER_GIE_MASK 0x80000000
|
||||||
|
|
||||||
struct xps2data {
|
struct xps2data {
|
||||||
int irq;
|
int irq;
|
||||||
u32 phys_addr;
|
|
||||||
u32 remap_size;
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
u8 rxb; /* Rx buffer */
|
|
||||||
void __iomem *base_address; /* virt. address of control registers */
|
void __iomem *base_address; /* virt. address of control registers */
|
||||||
unsigned int dfl;
|
unsigned int flags;
|
||||||
struct serio serio; /* serio */
|
struct serio serio; /* serio */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,8 +79,13 @@ struct xps2data {
|
||||||
/* XPS PS/2 data transmission calls */
|
/* XPS PS/2 data transmission calls */
|
||||||
/************************************/
|
/************************************/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* xps2_recv() will attempt to receive a byte of data from the PS/2 port.
|
* xps2_recv() - attempts to receive a byte from the PS/2 port.
|
||||||
|
* @drvdata: pointer to ps2 device private data structure
|
||||||
|
* @byte: address where the read data will be copied
|
||||||
|
*
|
||||||
|
* If there is any data available in the PS/2 receiver, this functions reads
|
||||||
|
* the data, otherwise it returns error.
|
||||||
*/
|
*/
|
||||||
static int xps2_recv(struct xps2data *drvdata, u8 *byte)
|
static int xps2_recv(struct xps2data *drvdata, u8 *byte)
|
||||||
{
|
{
|
||||||
|
@ -116,33 +118,27 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id)
|
||||||
|
|
||||||
/* Check which interrupt is active */
|
/* Check which interrupt is active */
|
||||||
if (intr_sr & XPS2_IPIXR_RX_OVF)
|
if (intr_sr & XPS2_IPIXR_RX_OVF)
|
||||||
printk(KERN_WARNING "%s: receive overrun error\n",
|
dev_warn(drvdata->serio.dev.parent, "receive overrun error\n");
|
||||||
drvdata->serio.name);
|
|
||||||
|
|
||||||
if (intr_sr & XPS2_IPIXR_RX_ERR)
|
if (intr_sr & XPS2_IPIXR_RX_ERR)
|
||||||
drvdata->dfl |= SERIO_PARITY;
|
drvdata->flags |= SERIO_PARITY;
|
||||||
|
|
||||||
if (intr_sr & (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_WDT_TOUT))
|
if (intr_sr & (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_WDT_TOUT))
|
||||||
drvdata->dfl |= SERIO_TIMEOUT;
|
drvdata->flags |= SERIO_TIMEOUT;
|
||||||
|
|
||||||
if (intr_sr & XPS2_IPIXR_RX_FULL) {
|
if (intr_sr & XPS2_IPIXR_RX_FULL) {
|
||||||
status = xps2_recv(drvdata, &drvdata->rxb);
|
status = xps2_recv(drvdata, &c);
|
||||||
|
|
||||||
/* Error, if a byte is not received */
|
/* Error, if a byte is not received */
|
||||||
if (status) {
|
if (status) {
|
||||||
printk(KERN_ERR
|
dev_err(drvdata->serio.dev.parent,
|
||||||
"%s: wrong rcvd byte count (%d)\n",
|
"wrong rcvd byte count (%d)\n", status);
|
||||||
drvdata->serio.name, status);
|
|
||||||
} else {
|
} else {
|
||||||
c = drvdata->rxb;
|
serio_interrupt(&drvdata->serio, c, drvdata->flags);
|
||||||
serio_interrupt(&drvdata->serio, c, drvdata->dfl);
|
drvdata->flags = 0;
|
||||||
drvdata->dfl = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intr_sr & XPS2_IPIXR_TX_ACK)
|
|
||||||
drvdata->dfl = 0;
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,8 +146,15 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id)
|
||||||
/* serio callbacks */
|
/* serio callbacks */
|
||||||
/*******************/
|
/*******************/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* sxps2_write() sends a byte out through the PS/2 interface.
|
* sxps2_write() - sends a byte out through the PS/2 port.
|
||||||
|
* @pserio: pointer to the serio structure of the PS/2 port
|
||||||
|
* @c: data that needs to be written to the PS/2 port
|
||||||
|
*
|
||||||
|
* This function checks if the PS/2 transmitter is empty and sends a byte.
|
||||||
|
* Otherwise it returns error. Transmission fails only when nothing is connected
|
||||||
|
* to the PS/2 port. Thats why, we do not try to resend the data in case of a
|
||||||
|
* failure.
|
||||||
*/
|
*/
|
||||||
static int sxps2_write(struct serio *pserio, unsigned char c)
|
static int sxps2_write(struct serio *pserio, unsigned char c)
|
||||||
{
|
{
|
||||||
|
@ -174,33 +177,39 @@ static int sxps2_write(struct serio *pserio, unsigned char c)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* sxps2_open() is called when a port is open by the higher layer.
|
* sxps2_open() - called when a port is opened by the higher layer.
|
||||||
|
* @pserio: pointer to the serio structure of the PS/2 device
|
||||||
|
*
|
||||||
|
* This function requests irq and enables interrupts for the PS/2 device.
|
||||||
*/
|
*/
|
||||||
static int sxps2_open(struct serio *pserio)
|
static int sxps2_open(struct serio *pserio)
|
||||||
{
|
{
|
||||||
struct xps2data *drvdata = pserio->port_data;
|
struct xps2data *drvdata = pserio->port_data;
|
||||||
int retval;
|
int error;
|
||||||
|
u8 c;
|
||||||
|
|
||||||
retval = request_irq(drvdata->irq, &xps2_interrupt, 0,
|
error = request_irq(drvdata->irq, &xps2_interrupt, 0,
|
||||||
DRIVER_NAME, drvdata);
|
DRIVER_NAME, drvdata);
|
||||||
if (retval) {
|
if (error) {
|
||||||
printk(KERN_ERR
|
dev_err(drvdata->serio.dev.parent,
|
||||||
"%s: Couldn't allocate interrupt %d\n",
|
"Couldn't allocate interrupt %d\n", drvdata->irq);
|
||||||
drvdata->serio.name, drvdata->irq);
|
return error;
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start reception by enabling the interrupts */
|
/* start reception by enabling the interrupts */
|
||||||
out_be32(drvdata->base_address + XPS2_GIER_OFFSET, XPS2_GIER_GIE_MASK);
|
out_be32(drvdata->base_address + XPS2_GIER_OFFSET, XPS2_GIER_GIE_MASK);
|
||||||
out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, XPS2_IPIXR_RX_ALL);
|
out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, XPS2_IPIXR_RX_ALL);
|
||||||
(void)xps2_recv(drvdata, &drvdata->rxb);
|
(void)xps2_recv(drvdata, &c);
|
||||||
|
|
||||||
return 0; /* success */
|
return 0; /* success */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* sxps2_close() frees the interrupt.
|
* sxps2_close() - frees the interrupt.
|
||||||
|
* @pserio: pointer to the serio structure of the PS/2 device
|
||||||
|
*
|
||||||
|
* This function frees the irq and disables interrupts for the PS/2 device.
|
||||||
*/
|
*/
|
||||||
static void sxps2_close(struct serio *pserio)
|
static void sxps2_close(struct serio *pserio)
|
||||||
{
|
{
|
||||||
|
@ -212,24 +221,41 @@ static void sxps2_close(struct serio *pserio)
|
||||||
free_irq(drvdata->irq, drvdata);
|
free_irq(drvdata->irq, drvdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************/
|
/**
|
||||||
/* Device setup code */
|
* xps2_of_probe - probe method for the PS/2 device.
|
||||||
/*********************/
|
* @of_dev: pointer to OF device structure
|
||||||
|
* @match: pointer to the stucture used for matching a device
|
||||||
static int xps2_setup(struct device *dev, struct resource *regs_res,
|
*
|
||||||
struct resource *irq_res)
|
* This function probes the PS/2 device in the device tree.
|
||||||
|
* It initializes the driver data structure and the hardware.
|
||||||
|
* It returns 0, if the driver is bound to the PS/2 device, or a negative
|
||||||
|
* value if there is an error.
|
||||||
|
*/
|
||||||
|
static int __devinit xps2_of_probe(struct of_device *ofdev,
|
||||||
|
const struct of_device_id *match)
|
||||||
{
|
{
|
||||||
|
struct resource r_irq; /* Interrupt resources */
|
||||||
|
struct resource r_mem; /* IO mem resources */
|
||||||
struct xps2data *drvdata;
|
struct xps2data *drvdata;
|
||||||
struct serio *serio;
|
struct serio *serio;
|
||||||
unsigned long remap_size;
|
struct device *dev = &ofdev->dev;
|
||||||
int retval;
|
resource_size_t remap_size, phys_addr;
|
||||||
|
int error;
|
||||||
|
|
||||||
if (!dev)
|
dev_info(dev, "Device Tree Probing \'%s\'\n",
|
||||||
return -EINVAL;
|
ofdev->node->name);
|
||||||
|
|
||||||
if (!regs_res || !irq_res) {
|
/* Get iospace for the device */
|
||||||
dev_err(dev, "IO resource(s) not found\n");
|
error = of_address_to_resource(ofdev->node, 0, &r_mem);
|
||||||
return -EINVAL;
|
if (error) {
|
||||||
|
dev_err(dev, "invalid address\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get IRQ for the device */
|
||||||
|
if (of_irq_to_resource(ofdev->node, 0, &r_irq) == NO_IRQ) {
|
||||||
|
dev_err(dev, "no IRQ found\n");
|
||||||
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL);
|
drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL);
|
||||||
|
@ -241,24 +267,23 @@ static int xps2_setup(struct device *dev, struct resource *regs_res,
|
||||||
dev_set_drvdata(dev, drvdata);
|
dev_set_drvdata(dev, drvdata);
|
||||||
|
|
||||||
spin_lock_init(&drvdata->lock);
|
spin_lock_init(&drvdata->lock);
|
||||||
drvdata->irq = irq_res->start;
|
drvdata->irq = r_irq.start;
|
||||||
|
|
||||||
remap_size = regs_res->end - regs_res->start + 1;
|
phys_addr = r_mem.start;
|
||||||
if (!request_mem_region(regs_res->start, remap_size, DRIVER_NAME)) {
|
remap_size = r_mem.end - r_mem.start + 1;
|
||||||
dev_err(dev, "Couldn't lock memory region at 0x%08X\n",
|
if (!request_mem_region(phys_addr, remap_size, DRIVER_NAME)) {
|
||||||
(unsigned int)regs_res->start);
|
dev_err(dev, "Couldn't lock memory region at 0x%08llX\n",
|
||||||
retval = -EBUSY;
|
(unsigned long long)phys_addr);
|
||||||
|
error = -EBUSY;
|
||||||
goto failed1;
|
goto failed1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in configuration data and add them to the list */
|
/* Fill in configuration data and add them to the list */
|
||||||
drvdata->phys_addr = regs_res->start;
|
drvdata->base_address = ioremap(phys_addr, remap_size);
|
||||||
drvdata->remap_size = remap_size;
|
|
||||||
drvdata->base_address = ioremap(regs_res->start, remap_size);
|
|
||||||
if (drvdata->base_address == NULL) {
|
if (drvdata->base_address == NULL) {
|
||||||
dev_err(dev, "Couldn't ioremap memory at 0x%08X\n",
|
dev_err(dev, "Couldn't ioremap memory at 0x%08llX\n",
|
||||||
(unsigned int)regs_res->start);
|
(unsigned long long)phys_addr);
|
||||||
retval = -EFAULT;
|
error = -EFAULT;
|
||||||
goto failed2;
|
goto failed2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,8 +294,9 @@ static int xps2_setup(struct device *dev, struct resource *regs_res,
|
||||||
* we have the PS2 in a good state */
|
* we have the PS2 in a good state */
|
||||||
out_be32(drvdata->base_address + XPS2_SRST_OFFSET, XPS2_SRST_RESET);
|
out_be32(drvdata->base_address + XPS2_SRST_OFFSET, XPS2_SRST_RESET);
|
||||||
|
|
||||||
dev_info(dev, "Xilinx PS2 at 0x%08X mapped to 0x%p, irq=%d\n",
|
dev_info(dev, "Xilinx PS2 at 0x%08llX mapped to 0x%p, irq=%d\n",
|
||||||
drvdata->phys_addr, drvdata->base_address, drvdata->irq);
|
(unsigned long long)phys_addr, drvdata->base_address,
|
||||||
|
drvdata->irq);
|
||||||
|
|
||||||
serio = &drvdata->serio;
|
serio = &drvdata->serio;
|
||||||
serio->id.type = SERIO_8042;
|
serio->id.type = SERIO_8042;
|
||||||
|
@ -280,71 +306,51 @@ static int xps2_setup(struct device *dev, struct resource *regs_res,
|
||||||
serio->port_data = drvdata;
|
serio->port_data = drvdata;
|
||||||
serio->dev.parent = dev;
|
serio->dev.parent = dev;
|
||||||
snprintf(serio->name, sizeof(serio->name),
|
snprintf(serio->name, sizeof(serio->name),
|
||||||
"Xilinx XPS PS/2 at %08X", drvdata->phys_addr);
|
"Xilinx XPS PS/2 at %08llX", (unsigned long long)phys_addr);
|
||||||
snprintf(serio->phys, sizeof(serio->phys),
|
snprintf(serio->phys, sizeof(serio->phys),
|
||||||
"xilinxps2/serio at %08X", drvdata->phys_addr);
|
"xilinxps2/serio at %08llX", (unsigned long long)phys_addr);
|
||||||
|
|
||||||
serio_register_port(serio);
|
serio_register_port(serio);
|
||||||
|
|
||||||
return 0; /* success */
|
return 0; /* success */
|
||||||
|
|
||||||
failed2:
|
failed2:
|
||||||
release_mem_region(regs_res->start, remap_size);
|
release_mem_region(phys_addr, remap_size);
|
||||||
failed1:
|
failed1:
|
||||||
kfree(drvdata);
|
kfree(drvdata);
|
||||||
dev_set_drvdata(dev, NULL);
|
dev_set_drvdata(dev, NULL);
|
||||||
|
|
||||||
return retval;
|
return error;
|
||||||
}
|
|
||||||
|
|
||||||
/***************************/
|
|
||||||
/* OF Platform Bus Support */
|
|
||||||
/***************************/
|
|
||||||
|
|
||||||
static int __devinit xps2_of_probe(struct of_device *ofdev, const struct
|
|
||||||
of_device_id * match)
|
|
||||||
{
|
|
||||||
struct resource r_irq; /* Interrupt resources */
|
|
||||||
struct resource r_mem; /* IO mem resources */
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
printk(KERN_INFO "Device Tree Probing \'%s\'\n",
|
|
||||||
ofdev->node->name);
|
|
||||||
|
|
||||||
/* Get iospace for the device */
|
|
||||||
rc = of_address_to_resource(ofdev->node, 0, &r_mem);
|
|
||||||
if (rc) {
|
|
||||||
dev_err(&ofdev->dev, "invalid address\n");
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get IRQ for the device */
|
|
||||||
rc = of_irq_to_resource(ofdev->node, 0, &r_irq);
|
|
||||||
if (rc == NO_IRQ) {
|
|
||||||
dev_err(&ofdev->dev, "no IRQ found\n");
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return xps2_setup(&ofdev->dev, &r_mem, &r_irq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xps2_of_remove - unbinds the driver from the PS/2 device.
|
||||||
|
* @of_dev: pointer to OF device structure
|
||||||
|
*
|
||||||
|
* This function is called if a device is physically removed from the system or
|
||||||
|
* if the driver module is being unloaded. It frees any resources allocated to
|
||||||
|
* the device.
|
||||||
|
*/
|
||||||
static int __devexit xps2_of_remove(struct of_device *of_dev)
|
static int __devexit xps2_of_remove(struct of_device *of_dev)
|
||||||
{
|
{
|
||||||
struct device *dev = &of_dev->dev;
|
struct device *dev = &of_dev->dev;
|
||||||
struct xps2data *drvdata;
|
struct xps2data *drvdata = dev_get_drvdata(dev);
|
||||||
|
struct resource r_mem; /* IO mem resources */
|
||||||
if (!dev)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
drvdata = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
serio_unregister_port(&drvdata->serio);
|
serio_unregister_port(&drvdata->serio);
|
||||||
iounmap(drvdata->base_address);
|
iounmap(drvdata->base_address);
|
||||||
release_mem_region(drvdata->phys_addr, drvdata->remap_size);
|
|
||||||
|
/* Get iospace of the device */
|
||||||
|
if (of_address_to_resource(of_dev->node, 0, &r_mem))
|
||||||
|
dev_err(dev, "invalid address\n");
|
||||||
|
else
|
||||||
|
release_mem_region(r_mem.start, r_mem.end - r_mem.start + 1);
|
||||||
|
|
||||||
kfree(drvdata);
|
kfree(drvdata);
|
||||||
|
|
||||||
dev_set_drvdata(dev, NULL);
|
dev_set_drvdata(dev, NULL);
|
||||||
|
|
||||||
return 0; /* success */
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Match table for of_platform binding */
|
/* Match table for of_platform binding */
|
||||||
|
|
|
@ -877,7 +877,7 @@ static int gtco_probe(struct usb_interface *usbinterface,
|
||||||
dbg("num endpoints: %d", usbinterface->cur_altsetting->desc.bNumEndpoints);
|
dbg("num endpoints: %d", usbinterface->cur_altsetting->desc.bNumEndpoints);
|
||||||
dbg("interface class: %d", usbinterface->cur_altsetting->desc.bInterfaceClass);
|
dbg("interface class: %d", usbinterface->cur_altsetting->desc.bInterfaceClass);
|
||||||
dbg("endpoint: attribute:0x%x type:0x%x", endpoint->bmAttributes, endpoint->bDescriptorType);
|
dbg("endpoint: attribute:0x%x type:0x%x", endpoint->bmAttributes, endpoint->bDescriptorType);
|
||||||
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
|
if (usb_endpoint_xfer_int(endpoint))
|
||||||
dbg("endpoint: we have interrupt endpoint\n");
|
dbg("endpoint: we have interrupt endpoint\n");
|
||||||
|
|
||||||
dbg("endpoint extra len:%d ", usbinterface->altsetting[0].extralen);
|
dbg("endpoint extra len:%d ", usbinterface->altsetting[0].extralen);
|
||||||
|
|
|
@ -535,7 +535,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
|
static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
|
||||||
{
|
{
|
||||||
char *data = wacom->data;
|
char *data = wacom->data;
|
||||||
int prox = 0, pressure;
|
int prox = 0, pressure;
|
||||||
|
|
|
@ -58,6 +58,14 @@ config TOUCHSCREEN_CORGI
|
||||||
NOTE: this driver is deprecated, try enable SPI and generic
|
NOTE: this driver is deprecated, try enable SPI and generic
|
||||||
ADS7846-based touchscreen driver.
|
ADS7846-based touchscreen driver.
|
||||||
|
|
||||||
|
config TOUCHSCREEN_DA9034
|
||||||
|
tristate "Touchscreen support for Dialog Semiconductor DA9034"
|
||||||
|
depends on PMIC_DA903X
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Say Y here to enable the support for the touchscreen found
|
||||||
|
on Dialog Semiconductor DA9034 PMIC.
|
||||||
|
|
||||||
config TOUCHSCREEN_FUJITSU
|
config TOUCHSCREEN_FUJITSU
|
||||||
tristate "Fujitsu serial touchscreen"
|
tristate "Fujitsu serial touchscreen"
|
||||||
select SERIO
|
select SERIO
|
||||||
|
@ -95,6 +103,19 @@ config TOUCHSCREEN_ELO
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called elo.
|
module will be called elo.
|
||||||
|
|
||||||
|
config TOUCHSCREEN_WACOM_W8001
|
||||||
|
tristate "Wacom W8001 penabled serial touchscreen"
|
||||||
|
select SERIO
|
||||||
|
help
|
||||||
|
Say Y here if you have an Wacom W8001 penabled serial touchscreen
|
||||||
|
connected to your system.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called wacom_w8001.
|
||||||
|
|
||||||
|
|
||||||
config TOUCHSCREEN_MTOUCH
|
config TOUCHSCREEN_MTOUCH
|
||||||
tristate "MicroTouch serial touchscreens"
|
tristate "MicroTouch serial touchscreens"
|
||||||
select SERIO
|
select SERIO
|
||||||
|
@ -376,4 +397,15 @@ config TOUCHSCREEN_TOUCHIT213
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called touchit213.
|
module will be called touchit213.
|
||||||
|
|
||||||
|
config TOUCHSCREEN_TSC2007
|
||||||
|
tristate "TSC2007 based touchscreens"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Say Y here if you have a TSC2007 based touchscreen.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called tsc2007.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -25,8 +25,11 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
|
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
|
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
|
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
|
||||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
|
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
|
||||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
|
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
|
||||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
|
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
|
||||||
|
|
|
@ -472,7 +472,7 @@ static ssize_t ads7846_disable_store(struct device *dev,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct ads7846 *ts = dev_get_drvdata(dev);
|
struct ads7846 *ts = dev_get_drvdata(dev);
|
||||||
long i;
|
unsigned long i;
|
||||||
|
|
||||||
if (strict_strtoul(buf, 10, &i))
|
if (strict_strtoul(buf, 10, &i))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -559,7 +559,7 @@ static void ads7846_rx(void *ads)
|
||||||
if (packet->tc.ignore || Rt > ts->pressure_max) {
|
if (packet->tc.ignore || Rt > ts->pressure_max) {
|
||||||
#ifdef VERBOSE
|
#ifdef VERBOSE
|
||||||
pr_debug("%s: ignored %d pressure %d\n",
|
pr_debug("%s: ignored %d pressure %d\n",
|
||||||
ts->spi->dev.bus_id, packet->tc.ignore, Rt);
|
dev_name(&ts->spi->dev), packet->tc.ignore, Rt);
|
||||||
#endif
|
#endif
|
||||||
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
|
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
|
||||||
HRTIMER_MODE_REL);
|
HRTIMER_MODE_REL);
|
||||||
|
@ -947,7 +947,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
||||||
ts->penirq_recheck_delay_usecs =
|
ts->penirq_recheck_delay_usecs =
|
||||||
pdata->penirq_recheck_delay_usecs;
|
pdata->penirq_recheck_delay_usecs;
|
||||||
|
|
||||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id);
|
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
|
||||||
|
|
||||||
input_dev->name = "ADS784x Touchscreen";
|
input_dev->name = "ADS784x Touchscreen";
|
||||||
input_dev->phys = ts->phys;
|
input_dev->phys = ts->phys;
|
||||||
|
|
389
drivers/input/touchscreen/da9034-ts.c
Normal file
389
drivers/input/touchscreen/da9034-ts.c
Normal file
|
@ -0,0 +1,389 @@
|
||||||
|
/*
|
||||||
|
* Touchscreen driver for Dialog Semiconductor DA9034
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006-2008 Marvell International Ltd.
|
||||||
|
* Fengwei Yin <fengwei.yin@marvell.com>
|
||||||
|
* Eric Miao <eric.miao@marvell.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/mfd/da903x.h>
|
||||||
|
|
||||||
|
#define DA9034_MANUAL_CTRL 0x50
|
||||||
|
#define DA9034_LDO_ADC_EN (1 << 4)
|
||||||
|
|
||||||
|
#define DA9034_AUTO_CTRL1 0x51
|
||||||
|
|
||||||
|
#define DA9034_AUTO_CTRL2 0x52
|
||||||
|
#define DA9034_AUTO_TSI_EN (1 << 3)
|
||||||
|
#define DA9034_PEN_DETECT (1 << 4)
|
||||||
|
|
||||||
|
#define DA9034_TSI_CTRL1 0x53
|
||||||
|
#define DA9034_TSI_CTRL2 0x54
|
||||||
|
#define DA9034_TSI_X_MSB 0x6c
|
||||||
|
#define DA9034_TSI_Y_MSB 0x6d
|
||||||
|
#define DA9034_TSI_XY_LSB 0x6e
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STATE_IDLE, /* wait for pendown */
|
||||||
|
STATE_BUSY, /* TSI busy sampling */
|
||||||
|
STATE_STOP, /* sample available */
|
||||||
|
STATE_WAIT, /* Wait to start next sample */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EVENT_PEN_DOWN,
|
||||||
|
EVENT_PEN_UP,
|
||||||
|
EVENT_TSI_READY,
|
||||||
|
EVENT_TIMEDOUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct da9034_touch {
|
||||||
|
struct device *da9034_dev;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
|
||||||
|
struct delayed_work tsi_work;
|
||||||
|
struct notifier_block notifier;
|
||||||
|
|
||||||
|
int state;
|
||||||
|
|
||||||
|
int interval_ms;
|
||||||
|
int x_inverted;
|
||||||
|
int y_inverted;
|
||||||
|
|
||||||
|
int last_x;
|
||||||
|
int last_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int is_pen_down(struct da9034_touch *touch)
|
||||||
|
{
|
||||||
|
return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int detect_pen_down(struct da9034_touch *touch, int on)
|
||||||
|
{
|
||||||
|
if (on)
|
||||||
|
return da903x_set_bits(touch->da9034_dev,
|
||||||
|
DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
|
||||||
|
else
|
||||||
|
return da903x_clr_bits(touch->da9034_dev,
|
||||||
|
DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_tsi(struct da9034_touch *touch)
|
||||||
|
{
|
||||||
|
uint8_t _x, _y, _v;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3);
|
||||||
|
touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int start_tsi(struct da9034_touch *touch)
|
||||||
|
{
|
||||||
|
return da903x_set_bits(touch->da9034_dev,
|
||||||
|
DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int stop_tsi(struct da9034_touch *touch)
|
||||||
|
{
|
||||||
|
return da903x_clr_bits(touch->da9034_dev,
|
||||||
|
DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void report_pen_down(struct da9034_touch *touch)
|
||||||
|
{
|
||||||
|
int x = touch->last_x;
|
||||||
|
int y = touch->last_y;
|
||||||
|
|
||||||
|
x &= 0xfff;
|
||||||
|
if (touch->x_inverted)
|
||||||
|
x = 1024 - x;
|
||||||
|
y &= 0xfff;
|
||||||
|
if (touch->y_inverted)
|
||||||
|
y = 1024 - y;
|
||||||
|
|
||||||
|
input_report_abs(touch->input_dev, ABS_X, x);
|
||||||
|
input_report_abs(touch->input_dev, ABS_Y, y);
|
||||||
|
input_report_key(touch->input_dev, BTN_TOUCH, 1);
|
||||||
|
|
||||||
|
input_sync(touch->input_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void report_pen_up(struct da9034_touch *touch)
|
||||||
|
{
|
||||||
|
input_report_key(touch->input_dev, BTN_TOUCH, 0);
|
||||||
|
input_sync(touch->input_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void da9034_event_handler(struct da9034_touch *touch, int event)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch (touch->state) {
|
||||||
|
case STATE_IDLE:
|
||||||
|
if (event != EVENT_PEN_DOWN)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Enable auto measurement of the TSI, this will
|
||||||
|
* automatically disable pen down detection
|
||||||
|
*/
|
||||||
|
err = start_tsi(touch);
|
||||||
|
if (err)
|
||||||
|
goto err_reset;
|
||||||
|
|
||||||
|
touch->state = STATE_BUSY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_BUSY:
|
||||||
|
if (event != EVENT_TSI_READY)
|
||||||
|
break;
|
||||||
|
|
||||||
|
err = read_tsi(touch);
|
||||||
|
if (err)
|
||||||
|
goto err_reset;
|
||||||
|
|
||||||
|
/* Disable auto measurement of the TSI, so that
|
||||||
|
* pen down status will be available
|
||||||
|
*/
|
||||||
|
err = stop_tsi(touch);
|
||||||
|
if (err)
|
||||||
|
goto err_reset;
|
||||||
|
|
||||||
|
touch->state = STATE_STOP;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_STOP:
|
||||||
|
if (event == EVENT_PEN_DOWN) {
|
||||||
|
report_pen_down(touch);
|
||||||
|
schedule_delayed_work(&touch->tsi_work,
|
||||||
|
msecs_to_jiffies(touch->interval_ms));
|
||||||
|
touch->state = STATE_WAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event == EVENT_PEN_UP) {
|
||||||
|
report_pen_up(touch);
|
||||||
|
touch->state = STATE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_sync(touch->input_dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_WAIT:
|
||||||
|
if (event != EVENT_TIMEDOUT)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (is_pen_down(touch)) {
|
||||||
|
start_tsi(touch);
|
||||||
|
touch->state = STATE_BUSY;
|
||||||
|
} else
|
||||||
|
touch->state = STATE_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
err_reset:
|
||||||
|
touch->state = STATE_IDLE;
|
||||||
|
stop_tsi(touch);
|
||||||
|
detect_pen_down(touch, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void da9034_tsi_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct da9034_touch *touch =
|
||||||
|
container_of(work, struct da9034_touch, tsi_work.work);
|
||||||
|
|
||||||
|
da9034_event_handler(touch, EVENT_TIMEDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9034_touch_notifier(struct notifier_block *nb,
|
||||||
|
unsigned long event, void *data)
|
||||||
|
{
|
||||||
|
struct da9034_touch *touch =
|
||||||
|
container_of(nb, struct da9034_touch, notifier);
|
||||||
|
|
||||||
|
if (event & DA9034_EVENT_PEN_DOWN) {
|
||||||
|
if (is_pen_down(touch))
|
||||||
|
da9034_event_handler(touch, EVENT_PEN_DOWN);
|
||||||
|
else
|
||||||
|
da9034_event_handler(touch, EVENT_PEN_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event & DA9034_EVENT_TSI_READY)
|
||||||
|
da9034_event_handler(touch, EVENT_TSI_READY);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int da9034_touch_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct da9034_touch *touch = input_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier,
|
||||||
|
DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
|
||||||
|
if (ret)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
/* Enable ADC LDO */
|
||||||
|
ret = da903x_set_bits(touch->da9034_dev,
|
||||||
|
DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
|
||||||
|
ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
touch->state = STATE_IDLE;
|
||||||
|
detect_pen_down(touch, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void da9034_touch_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct da9034_touch *touch = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
da903x_unregister_notifier(touch->da9034_dev, &touch->notifier,
|
||||||
|
DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(&touch->tsi_work);
|
||||||
|
|
||||||
|
touch->state = STATE_IDLE;
|
||||||
|
stop_tsi(touch);
|
||||||
|
detect_pen_down(touch, 0);
|
||||||
|
|
||||||
|
/* Disable ADC LDO */
|
||||||
|
da903x_clr_bits(touch->da9034_dev,
|
||||||
|
DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int __devinit da9034_touch_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct da9034_touch_pdata *pdata = pdev->dev.platform_data;
|
||||||
|
struct da9034_touch *touch;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
touch = kzalloc(sizeof(struct da9034_touch), GFP_KERNEL);
|
||||||
|
if (touch == NULL) {
|
||||||
|
dev_err(&pdev->dev, "failed to allocate driver data\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
touch->da9034_dev = pdev->dev.parent;
|
||||||
|
|
||||||
|
if (pdata) {
|
||||||
|
touch->interval_ms = pdata->interval_ms;
|
||||||
|
touch->x_inverted = pdata->x_inverted;
|
||||||
|
touch->y_inverted = pdata->y_inverted;
|
||||||
|
} else
|
||||||
|
/* fallback into default */
|
||||||
|
touch->interval_ms = 10;
|
||||||
|
|
||||||
|
INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);
|
||||||
|
touch->notifier.notifier_call = da9034_touch_notifier;
|
||||||
|
|
||||||
|
input_dev = input_allocate_device();
|
||||||
|
if (!input_dev) {
|
||||||
|
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_free_touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_dev->name = pdev->name;
|
||||||
|
input_dev->open = da9034_touch_open;
|
||||||
|
input_dev->close = da9034_touch_close;
|
||||||
|
input_dev->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
__set_bit(EV_ABS, input_dev->evbit);
|
||||||
|
__set_bit(ABS_X, input_dev->absbit);
|
||||||
|
__set_bit(ABS_Y, input_dev->absbit);
|
||||||
|
input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
|
||||||
|
input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
|
||||||
|
|
||||||
|
__set_bit(EV_KEY, input_dev->evbit);
|
||||||
|
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||||
|
|
||||||
|
touch->input_dev = input_dev;
|
||||||
|
input_set_drvdata(input_dev, touch);
|
||||||
|
|
||||||
|
ret = input_register_device(input_dev);
|
||||||
|
if (ret)
|
||||||
|
goto err_free_input;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, touch);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free_input:
|
||||||
|
input_free_device(input_dev);
|
||||||
|
err_free_touch:
|
||||||
|
kfree(touch);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit da9034_touch_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct da9034_touch *touch = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
input_unregister_device(touch->input_dev);
|
||||||
|
kfree(touch);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver da9034_touch_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "da9034-touch",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = da9034_touch_probe,
|
||||||
|
.remove = __devexit_p(da9034_touch_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init da9034_touch_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&da9034_touch_driver);
|
||||||
|
}
|
||||||
|
module_init(da9034_touch_init);
|
||||||
|
|
||||||
|
static void __exit da9034_touch_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&da9034_touch_driver);
|
||||||
|
}
|
||||||
|
module_exit(da9034_touch_exit);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
|
||||||
|
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:da9034-touch");
|
381
drivers/input/touchscreen/tsc2007.c
Normal file
381
drivers/input/touchscreen/tsc2007.c
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
/*
|
||||||
|
* drivers/input/touchscreen/tsc2007.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 MtekVision Co., Ltd.
|
||||||
|
* Kwangwoo Lee <kwlee@mtekvision.com>
|
||||||
|
*
|
||||||
|
* Using code from:
|
||||||
|
* - ads7846.c
|
||||||
|
* Copyright (c) 2005 David Brownell
|
||||||
|
* Copyright (c) 2006 Nokia Corporation
|
||||||
|
* - corgi_ts.c
|
||||||
|
* Copyright (C) 2004-2005 Richard Purdie
|
||||||
|
* - omap_ts.[hc], ads7846.h, ts_osk.c
|
||||||
|
* Copyright (C) 2002 MontaVista Software
|
||||||
|
* Copyright (C) 2004 Texas Instruments
|
||||||
|
* Copyright (C) 2005 Dirk Behme
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/hrtimer.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c/tsc2007.h>
|
||||||
|
|
||||||
|
#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */
|
||||||
|
#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */
|
||||||
|
|
||||||
|
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
|
||||||
|
#define TSC2007_MEASURE_AUX (0x2 << 4)
|
||||||
|
#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
|
||||||
|
#define TSC2007_ACTIVATE_XN (0x8 << 4)
|
||||||
|
#define TSC2007_ACTIVATE_YN (0x9 << 4)
|
||||||
|
#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
|
||||||
|
#define TSC2007_SETUP (0xb << 4)
|
||||||
|
#define TSC2007_MEASURE_X (0xc << 4)
|
||||||
|
#define TSC2007_MEASURE_Y (0xd << 4)
|
||||||
|
#define TSC2007_MEASURE_Z1 (0xe << 4)
|
||||||
|
#define TSC2007_MEASURE_Z2 (0xf << 4)
|
||||||
|
|
||||||
|
#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
|
||||||
|
#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
|
||||||
|
#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
|
||||||
|
#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
|
||||||
|
|
||||||
|
#define TSC2007_12BIT (0x0 << 1)
|
||||||
|
#define TSC2007_8BIT (0x1 << 1)
|
||||||
|
|
||||||
|
#define MAX_12BIT ((1 << 12) - 1)
|
||||||
|
|
||||||
|
#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
|
||||||
|
|
||||||
|
#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
|
||||||
|
#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
|
||||||
|
#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
|
||||||
|
#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
|
||||||
|
#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
|
||||||
|
|
||||||
|
struct ts_event {
|
||||||
|
u16 x;
|
||||||
|
u16 y;
|
||||||
|
u16 z1, z2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tsc2007 {
|
||||||
|
struct input_dev *input;
|
||||||
|
char phys[32];
|
||||||
|
struct hrtimer timer;
|
||||||
|
struct ts_event tc;
|
||||||
|
|
||||||
|
struct i2c_client *client;
|
||||||
|
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
|
u16 model;
|
||||||
|
u16 x_plate_ohms;
|
||||||
|
|
||||||
|
unsigned pendown;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
int (*get_pendown_state)(void);
|
||||||
|
void (*clear_penirq)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
|
||||||
|
{
|
||||||
|
s32 data;
|
||||||
|
u16 val;
|
||||||
|
|
||||||
|
data = i2c_smbus_read_word_data(tsc->client, cmd);
|
||||||
|
if (data < 0) {
|
||||||
|
dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The protocol and raw data format from i2c interface:
|
||||||
|
* S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
|
||||||
|
* Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
|
||||||
|
*/
|
||||||
|
val = swab16(data) >> 4;
|
||||||
|
|
||||||
|
dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tsc2007_send_event(void *tsc)
|
||||||
|
{
|
||||||
|
struct tsc2007 *ts = tsc;
|
||||||
|
u32 rt;
|
||||||
|
u16 x, y, z1, z2;
|
||||||
|
|
||||||
|
x = ts->tc.x;
|
||||||
|
y = ts->tc.y;
|
||||||
|
z1 = ts->tc.z1;
|
||||||
|
z2 = ts->tc.z2;
|
||||||
|
|
||||||
|
/* range filtering */
|
||||||
|
if (x == MAX_12BIT)
|
||||||
|
x = 0;
|
||||||
|
|
||||||
|
if (likely(x && z1)) {
|
||||||
|
/* compute touch pressure resistance using equation #1 */
|
||||||
|
rt = z2;
|
||||||
|
rt -= z1;
|
||||||
|
rt *= x;
|
||||||
|
rt *= ts->x_plate_ohms;
|
||||||
|
rt /= z1;
|
||||||
|
rt = (rt + 2047) >> 12;
|
||||||
|
} else
|
||||||
|
rt = 0;
|
||||||
|
|
||||||
|
/* Sample found inconsistent by debouncing or pressure is beyond
|
||||||
|
* the maximum. Don't report it to user space, repeat at least
|
||||||
|
* once more the measurement
|
||||||
|
*/
|
||||||
|
if (rt > MAX_12BIT) {
|
||||||
|
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
|
||||||
|
|
||||||
|
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
|
||||||
|
HRTIMER_MODE_REL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTE: We can't rely on the pressure to determine the pen down
|
||||||
|
* state, even this controller has a pressure sensor. The pressure
|
||||||
|
* value can fluctuate for quite a while after lifting the pen and
|
||||||
|
* in some cases may not even settle at the expected value.
|
||||||
|
*
|
||||||
|
* The only safe way to check for the pen up condition is in the
|
||||||
|
* timer by reading the pen signal state (it's a GPIO _and_ IRQ).
|
||||||
|
*/
|
||||||
|
if (rt) {
|
||||||
|
struct input_dev *input = ts->input;
|
||||||
|
|
||||||
|
if (!ts->pendown) {
|
||||||
|
dev_dbg(&ts->client->dev, "DOWN\n");
|
||||||
|
|
||||||
|
input_report_key(input, BTN_TOUCH, 1);
|
||||||
|
ts->pendown = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_report_abs(input, ABS_X, x);
|
||||||
|
input_report_abs(input, ABS_Y, y);
|
||||||
|
input_report_abs(input, ABS_PRESSURE, rt);
|
||||||
|
|
||||||
|
input_sync(input);
|
||||||
|
|
||||||
|
dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
|
||||||
|
x, y, rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
|
||||||
|
HRTIMER_MODE_REL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tsc2007_read_values(struct tsc2007 *tsc)
|
||||||
|
{
|
||||||
|
/* y- still on; turn on only y+ (and ADC) */
|
||||||
|
tsc->tc.y = tsc2007_xfer(tsc, READ_Y);
|
||||||
|
|
||||||
|
/* turn y- off, x+ on, then leave in lowpower */
|
||||||
|
tsc->tc.x = tsc2007_xfer(tsc, READ_X);
|
||||||
|
|
||||||
|
/* turn y+ off, x- on; we'll use formula #1 */
|
||||||
|
tsc->tc.z1 = tsc2007_xfer(tsc, READ_Z1);
|
||||||
|
tsc->tc.z2 = tsc2007_xfer(tsc, READ_Z2);
|
||||||
|
|
||||||
|
/* power down */
|
||||||
|
tsc2007_xfer(tsc, PWRDOWN);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
|
||||||
|
{
|
||||||
|
struct tsc2007 *ts = container_of(handle, struct tsc2007, timer);
|
||||||
|
|
||||||
|
spin_lock_irq(&ts->lock);
|
||||||
|
|
||||||
|
if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
|
||||||
|
struct input_dev *input = ts->input;
|
||||||
|
|
||||||
|
dev_dbg(&ts->client->dev, "UP\n");
|
||||||
|
|
||||||
|
input_report_key(input, BTN_TOUCH, 0);
|
||||||
|
input_report_abs(input, ABS_PRESSURE, 0);
|
||||||
|
input_sync(input);
|
||||||
|
|
||||||
|
ts->pendown = 0;
|
||||||
|
enable_irq(ts->irq);
|
||||||
|
} else {
|
||||||
|
/* pen is still down, continue with the measurement */
|
||||||
|
dev_dbg(&ts->client->dev, "pen is still down\n");
|
||||||
|
|
||||||
|
tsc2007_read_values(ts);
|
||||||
|
tsc2007_send_event(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irq(&ts->lock);
|
||||||
|
|
||||||
|
return HRTIMER_NORESTART;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t tsc2007_irq(int irq, void *handle)
|
||||||
|
{
|
||||||
|
struct tsc2007 *ts = handle;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ts->lock, flags);
|
||||||
|
|
||||||
|
if (likely(ts->get_pendown_state())) {
|
||||||
|
disable_irq(ts->irq);
|
||||||
|
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
|
||||||
|
HRTIMER_MODE_REL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts->clear_penirq)
|
||||||
|
ts->clear_penirq();
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&ts->lock, flags);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tsc2007_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct tsc2007 *ts;
|
||||||
|
struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!pdata) {
|
||||||
|
dev_err(&client->dev, "platform data is required!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i2c_check_functionality(client->adapter,
|
||||||
|
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL);
|
||||||
|
input_dev = input_allocate_device();
|
||||||
|
if (!ts || !input_dev) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto err_free_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts->client = client;
|
||||||
|
i2c_set_clientdata(client, ts);
|
||||||
|
|
||||||
|
ts->input = input_dev;
|
||||||
|
|
||||||
|
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||||
|
ts->timer.function = tsc2007_timer;
|
||||||
|
|
||||||
|
spin_lock_init(&ts->lock);
|
||||||
|
|
||||||
|
ts->model = pdata->model;
|
||||||
|
ts->x_plate_ohms = pdata->x_plate_ohms;
|
||||||
|
ts->get_pendown_state = pdata->get_pendown_state;
|
||||||
|
ts->clear_penirq = pdata->clear_penirq;
|
||||||
|
|
||||||
|
pdata->init_platform_hw();
|
||||||
|
|
||||||
|
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", client->dev.bus_id);
|
||||||
|
|
||||||
|
input_dev->name = "TSC2007 Touchscreen";
|
||||||
|
input_dev->phys = ts->phys;
|
||||||
|
input_dev->id.bustype = BUS_I2C;
|
||||||
|
|
||||||
|
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||||
|
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||||
|
|
||||||
|
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
|
||||||
|
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
|
||||||
|
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
|
||||||
|
|
||||||
|
tsc2007_read_values(ts);
|
||||||
|
|
||||||
|
ts->irq = client->irq;
|
||||||
|
|
||||||
|
err = request_irq(ts->irq, tsc2007_irq, 0,
|
||||||
|
client->dev.driver->name, ts);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(&client->dev, "irq %d busy?\n", ts->irq);
|
||||||
|
goto err_free_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = input_register_device(input_dev);
|
||||||
|
if (err)
|
||||||
|
goto err_free_irq;
|
||||||
|
|
||||||
|
dev_info(&client->dev, "registered with irq (%d)\n", ts->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free_irq:
|
||||||
|
free_irq(ts->irq, ts);
|
||||||
|
hrtimer_cancel(&ts->timer);
|
||||||
|
err_free_mem:
|
||||||
|
input_free_device(input_dev);
|
||||||
|
kfree(ts);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tsc2007_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct tsc2007 *ts = i2c_get_clientdata(client);
|
||||||
|
struct tsc2007_platform_data *pdata;
|
||||||
|
|
||||||
|
pdata = client->dev.platform_data;
|
||||||
|
pdata->exit_platform_hw();
|
||||||
|
|
||||||
|
free_irq(ts->irq, ts);
|
||||||
|
hrtimer_cancel(&ts->timer);
|
||||||
|
input_unregister_device(ts->input);
|
||||||
|
kfree(ts);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct i2c_device_id tsc2007_idtable[] = {
|
||||||
|
{ "tsc2007", 0 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
|
||||||
|
|
||||||
|
static struct i2c_driver tsc2007_driver = {
|
||||||
|
.driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "tsc2007"
|
||||||
|
},
|
||||||
|
.id_table = tsc2007_idtable,
|
||||||
|
.probe = tsc2007_probe,
|
||||||
|
.remove = tsc2007_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init tsc2007_init(void)
|
||||||
|
{
|
||||||
|
return i2c_add_driver(&tsc2007_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit tsc2007_exit(void)
|
||||||
|
{
|
||||||
|
i2c_del_driver(&tsc2007_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(tsc2007_init);
|
||||||
|
module_exit(tsc2007_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
|
||||||
|
MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -424,7 +424,7 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
|
||||||
0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
if (buf[0] != 0x06 || buf[1] != 0x00) {
|
if (buf[0] != 0x06) {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
@ -437,8 +437,7 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
|
||||||
TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
if ((buf[0] != 0x06 || buf[1] != 0x00) &&
|
if ((buf[0] != 0x06) && (buf[0] != 0x15 || buf[1] != 0x01)) {
|
||||||
(buf[0] != 0x15 || buf[1] != 0x01)) {
|
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
325
drivers/input/touchscreen/wacom_w8001.c
Normal file
325
drivers/input/touchscreen/wacom_w8001.c
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
/*
|
||||||
|
* Wacom W8001 penabled serial touchscreen driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Jaya Kumar
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file COPYING in the main directory of this archive for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* Layout based on Elo serial touchscreen driver by Vojtech Pavlik
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/serio.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
|
||||||
|
#define DRIVER_DESC "Wacom W8001 serial touchscreen driver"
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definitions & global arrays.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define W8001_MAX_LENGTH 11
|
||||||
|
#define W8001_PACKET_LEN 11
|
||||||
|
#define W8001_LEAD_MASK 0x80
|
||||||
|
#define W8001_LEAD_BYTE 0x80
|
||||||
|
#define W8001_TAB_MASK 0x40
|
||||||
|
#define W8001_TAB_BYTE 0x40
|
||||||
|
|
||||||
|
#define W8001_QUERY_PACKET 0x20
|
||||||
|
|
||||||
|
struct w8001_coord {
|
||||||
|
u8 rdy;
|
||||||
|
u8 tsw;
|
||||||
|
u8 f1;
|
||||||
|
u8 f2;
|
||||||
|
u16 x;
|
||||||
|
u16 y;
|
||||||
|
u16 pen_pressure;
|
||||||
|
u8 tilt_x;
|
||||||
|
u8 tilt_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-touchscreen data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct w8001 {
|
||||||
|
struct input_dev *dev;
|
||||||
|
struct serio *serio;
|
||||||
|
struct mutex cmd_mutex;
|
||||||
|
struct completion cmd_done;
|
||||||
|
int id;
|
||||||
|
int idx;
|
||||||
|
unsigned char expected_packet;
|
||||||
|
unsigned char data[W8001_MAX_LENGTH];
|
||||||
|
unsigned char response[W8001_PACKET_LEN];
|
||||||
|
char phys[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int parse_data(u8 *data, struct w8001_coord *coord)
|
||||||
|
{
|
||||||
|
coord->rdy = data[0] & 0x20;
|
||||||
|
coord->tsw = data[0] & 0x01;
|
||||||
|
coord->f1 = data[0] & 0x02;
|
||||||
|
coord->f2 = data[0] & 0x04;
|
||||||
|
|
||||||
|
coord->x = (data[1] & 0x7F) << 9;
|
||||||
|
coord->x |= (data[2] & 0x7F) << 2;
|
||||||
|
coord->x |= (data[6] & 0x60) >> 5;
|
||||||
|
|
||||||
|
coord->y = (data[3] & 0x7F) << 9;
|
||||||
|
coord->y |= (data[4] & 0x7F) << 2;
|
||||||
|
coord->y |= (data[6] & 0x18) >> 3;
|
||||||
|
|
||||||
|
coord->pen_pressure = data[5] & 0x7F;
|
||||||
|
coord->pen_pressure |= (data[6] & 0x07) << 7 ;
|
||||||
|
|
||||||
|
coord->tilt_x = data[7] & 0x7F;
|
||||||
|
coord->tilt_y = data[8] & 0x7F;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void w8001_process_data(struct w8001 *w8001, unsigned char data)
|
||||||
|
{
|
||||||
|
struct input_dev *dev = w8001->dev;
|
||||||
|
u8 tmp;
|
||||||
|
struct w8001_coord coord;
|
||||||
|
|
||||||
|
w8001->data[w8001->idx] = data;
|
||||||
|
switch (w8001->idx++) {
|
||||||
|
case 0:
|
||||||
|
if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) {
|
||||||
|
pr_debug("w8001: unsynchronized data: 0x%02x\n", data);
|
||||||
|
w8001->idx = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
tmp = w8001->data[0] & W8001_TAB_MASK;
|
||||||
|
if (unlikely(tmp == W8001_TAB_BYTE))
|
||||||
|
break;
|
||||||
|
w8001->idx = 0;
|
||||||
|
memset(&coord, 0, sizeof(coord));
|
||||||
|
parse_data(w8001->data, &coord);
|
||||||
|
input_report_abs(dev, ABS_X, coord.x);
|
||||||
|
input_report_abs(dev, ABS_Y, coord.y);
|
||||||
|
input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure);
|
||||||
|
input_report_key(dev, BTN_TOUCH, coord.tsw);
|
||||||
|
input_sync(dev);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
w8001->idx = 0;
|
||||||
|
memcpy(w8001->response, &w8001->data, W8001_PACKET_LEN);
|
||||||
|
w8001->expected_packet = W8001_QUERY_PACKET;
|
||||||
|
complete(&w8001->cmd_done);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||||
|
unsigned char data, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct w8001 *w8001 = serio_get_drvdata(serio);
|
||||||
|
|
||||||
|
w8001_process_data(w8001, data);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w8001_async_command(struct w8001 *w8001, unsigned char *packet,
|
||||||
|
int len)
|
||||||
|
{
|
||||||
|
int rc = -1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mutex_lock(&w8001->cmd_mutex);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (serio_write(w8001->serio, packet[i]))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&w8001->cmd_mutex);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w8001_command(struct w8001 *w8001, unsigned char *packet, int len)
|
||||||
|
{
|
||||||
|
int rc = -1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mutex_lock(&w8001->cmd_mutex);
|
||||||
|
|
||||||
|
serio_pause_rx(w8001->serio);
|
||||||
|
init_completion(&w8001->cmd_done);
|
||||||
|
serio_continue_rx(w8001->serio);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (serio_write(w8001->serio, packet[i]))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_completion_timeout(&w8001->cmd_done, HZ);
|
||||||
|
|
||||||
|
if (w8001->expected_packet == W8001_QUERY_PACKET) {
|
||||||
|
/* We are back in reporting mode, the query was ACKed */
|
||||||
|
memcpy(packet, w8001->response, W8001_PACKET_LEN);
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&w8001->cmd_mutex);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w8001_setup(struct w8001 *w8001)
|
||||||
|
{
|
||||||
|
struct w8001_coord coord;
|
||||||
|
struct input_dev *dev = w8001->dev;
|
||||||
|
unsigned char start[1] = { '1' };
|
||||||
|
unsigned char query[11] = { '*' };
|
||||||
|
|
||||||
|
if (w8001_command(w8001, query, 1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(&coord, 0, sizeof(coord));
|
||||||
|
parse_data(query, &coord);
|
||||||
|
|
||||||
|
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
|
||||||
|
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
|
||||||
|
input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
|
||||||
|
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
|
||||||
|
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
|
||||||
|
|
||||||
|
if (w8001_async_command(w8001, start, 1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* w8001_disconnect() is the opposite of w8001_connect()
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void w8001_disconnect(struct serio *serio)
|
||||||
|
{
|
||||||
|
struct w8001 *w8001 = serio_get_drvdata(serio);
|
||||||
|
|
||||||
|
input_get_device(w8001->dev);
|
||||||
|
input_unregister_device(w8001->dev);
|
||||||
|
serio_close(serio);
|
||||||
|
serio_set_drvdata(serio, NULL);
|
||||||
|
input_put_device(w8001->dev);
|
||||||
|
kfree(w8001);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* w8001_connect() is the routine that is called when someone adds a
|
||||||
|
* new serio device that supports the w8001 protocol and registers it as
|
||||||
|
* an input device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||||
|
{
|
||||||
|
struct w8001 *w8001;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
|
||||||
|
input_dev = input_allocate_device();
|
||||||
|
if (!w8001 || !input_dev) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
w8001->serio = serio;
|
||||||
|
w8001->id = serio->id.id;
|
||||||
|
w8001->dev = input_dev;
|
||||||
|
mutex_init(&w8001->cmd_mutex);
|
||||||
|
init_completion(&w8001->cmd_done);
|
||||||
|
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
|
||||||
|
|
||||||
|
input_dev->name = "Wacom W8001 Penabled Serial TouchScreen";
|
||||||
|
input_dev->phys = w8001->phys;
|
||||||
|
input_dev->id.bustype = BUS_RS232;
|
||||||
|
input_dev->id.vendor = SERIO_W8001;
|
||||||
|
input_dev->id.product = w8001->id;
|
||||||
|
input_dev->id.version = 0x0100;
|
||||||
|
input_dev->dev.parent = &serio->dev;
|
||||||
|
|
||||||
|
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||||
|
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||||
|
|
||||||
|
serio_set_drvdata(serio, w8001);
|
||||||
|
err = serio_open(serio, drv);
|
||||||
|
if (err)
|
||||||
|
goto fail2;
|
||||||
|
|
||||||
|
if (w8001_setup(w8001))
|
||||||
|
goto fail3;
|
||||||
|
|
||||||
|
err = input_register_device(w8001->dev);
|
||||||
|
if (err)
|
||||||
|
goto fail3;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail3:
|
||||||
|
serio_close(serio);
|
||||||
|
fail2:
|
||||||
|
serio_set_drvdata(serio, NULL);
|
||||||
|
fail1:
|
||||||
|
input_free_device(input_dev);
|
||||||
|
kfree(w8001);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct serio_device_id w8001_serio_ids[] = {
|
||||||
|
{
|
||||||
|
.type = SERIO_RS232,
|
||||||
|
.proto = SERIO_W8001,
|
||||||
|
.id = SERIO_ANY,
|
||||||
|
.extra = SERIO_ANY,
|
||||||
|
},
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(serio, w8001_serio_ids);
|
||||||
|
|
||||||
|
static struct serio_driver w8001_drv = {
|
||||||
|
.driver = {
|
||||||
|
.name = "w8001",
|
||||||
|
},
|
||||||
|
.description = DRIVER_DESC,
|
||||||
|
.id_table = w8001_serio_ids,
|
||||||
|
.interrupt = w8001_interrupt,
|
||||||
|
.connect = w8001_connect,
|
||||||
|
.disconnect = w8001_disconnect,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init w8001_init(void)
|
||||||
|
{
|
||||||
|
return serio_register_driver(&w8001_drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit w8001_exit(void)
|
||||||
|
{
|
||||||
|
serio_unregister_driver(&w8001_drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(w8001_init);
|
||||||
|
module_exit(w8001_exit);
|
|
@ -15,6 +15,7 @@ struct gpio_keys_button {
|
||||||
struct gpio_keys_platform_data {
|
struct gpio_keys_platform_data {
|
||||||
struct gpio_keys_button *buttons;
|
struct gpio_keys_button *buttons;
|
||||||
int nbuttons;
|
int nbuttons;
|
||||||
|
unsigned int rep:1; /* enable input subsystem auto repeat */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
17
include/linux/i2c/tsc2007.h
Normal file
17
include/linux/i2c/tsc2007.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef __LINUX_I2C_TSC2007_H
|
||||||
|
#define __LINUX_I2C_TSC2007_H
|
||||||
|
|
||||||
|
/* linux/i2c/tsc2007.h */
|
||||||
|
|
||||||
|
struct tsc2007_platform_data {
|
||||||
|
u16 model; /* 2007. */
|
||||||
|
u16 x_plate_ohms;
|
||||||
|
|
||||||
|
int (*get_pendown_state)(void);
|
||||||
|
void (*clear_penirq)(void); /* If needed, clear 2nd level
|
||||||
|
interrupt source */
|
||||||
|
int (*init_platform_hw)(void);
|
||||||
|
void (*exit_platform_hw)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -18,11 +18,13 @@
|
||||||
#define PS2_RET_ID 0x00
|
#define PS2_RET_ID 0x00
|
||||||
#define PS2_RET_ACK 0xfa
|
#define PS2_RET_ACK 0xfa
|
||||||
#define PS2_RET_NAK 0xfe
|
#define PS2_RET_NAK 0xfe
|
||||||
|
#define PS2_RET_ERR 0xfc
|
||||||
|
|
||||||
#define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */
|
#define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */
|
||||||
#define PS2_FLAG_CMD 2 /* Waiting for command to finish */
|
#define PS2_FLAG_CMD 2 /* Waiting for command to finish */
|
||||||
#define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */
|
#define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */
|
||||||
#define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */
|
#define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */
|
||||||
|
#define PS2_FLAG_NAK 16 /* Last transmission was NAKed */
|
||||||
|
|
||||||
struct ps2dev {
|
struct ps2dev {
|
||||||
struct serio *serio;
|
struct serio *serio;
|
||||||
|
|
|
@ -75,7 +75,7 @@ struct seg7_conversion_map {
|
||||||
unsigned char table[128];
|
unsigned char table[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int map_to_seg7(struct seg7_conversion_map *map, int c)
|
static __inline__ int map_to_seg7(struct seg7_conversion_map *map, int c)
|
||||||
{
|
{
|
||||||
return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL;
|
return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,5 +213,6 @@ static inline void serio_unpin_driver(struct serio *serio)
|
||||||
#define SERIO_ZHENHUA 0x36
|
#define SERIO_ZHENHUA 0x36
|
||||||
#define SERIO_INEXIO 0x37
|
#define SERIO_INEXIO 0x37
|
||||||
#define SERIO_TOUCHIT213 0x37
|
#define SERIO_TOUCHIT213 0x37
|
||||||
|
#define SERIO_W8001 0x39
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue