Commit 1dd32946 authored by Dmitry Torokhov's avatar Dmitry Torokhov

Merge branch 'next' into for-linus

parents 4a6908a3 e941da31
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.
#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 */
#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.
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_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)
{
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,
......@@ -65,7 +65,7 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
goto err_unregister_handle;
printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n",
dev->dev.bus_id,
dev_name(&dev->dev),
dev->name ?: "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)
{
printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n",
handle->dev->dev.bus_id);
dev_name(&handle->dev->dev));
input_close_device(handle);
input_unregister_handle(handle);
......
......@@ -19,7 +19,7 @@
#include <linux/input.h>
#include <linux/major.h>
#include <linux/device.h>
#include <linux/compat.h>
#include "input-compat.h"
struct evdev {
int exist;
......@@ -290,187 +290,6 @@ static int evdev_open(struct inode *inode, struct file *file)
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,
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) {
if (evdev_event_from_user(buffer + retval, &event)) {
if (input_event_from_user(buffer + retval, &event)) {
retval = -EFAULT;
goto out;
}
input_inject_event(&evdev->handle,
event.type, event.code, event.value);
retval += evdev_event_size();
retval += input_event_size();
}
out:
......@@ -531,7 +350,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
struct input_event event;
int retval;
if (count < evdev_event_size())
if (count < input_event_size())
return -EINVAL;
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)
return -ENODEV;
while (retval + evdev_event_size() <= count &&
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (evdev_event_to_user(buffer + retval, &event))
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += evdev_event_size();
retval += input_event_size();
}
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 (evdev_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
if (input_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
return -EFAULT;
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.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.class = &input_class;
evdev->dev.parent = &dev->dev;
......
......@@ -530,8 +530,7 @@ static void gameport_init_port(struct gameport *gameport)
mutex_init(&gameport->drv_mutex);
device_initialize(&gameport->dev);
snprintf(gameport->dev.bus_id, sizeof(gameport->dev.bus_id),
"gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1);
dev_set_name(&gameport->dev, "gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1);
gameport->dev.bus = &gameport_bus;
gameport->dev.release = gameport_release_port;
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;
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->io = ioport;
......
/*
* 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);
#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)
dev->setkeycode = input_default_setkeycode;
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);
error = device_add(&dev->dev);
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.class = &input_class;
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
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
......@@ -19,6 +19,7 @@ obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/
obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.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_XPAD) += xpad.o
obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
/*
* 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);
/*
* 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
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
tristate "AAED-2000 keyboard"
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_OMAP) += omap-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_GPIO) += gpio_keys.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);
}
/*
* 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
* according to the selected scancode set
......@@ -1475,6 +1508,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.callback = atkbd_setup_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",
.matches = {
......@@ -1484,6 +1526,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.callback = atkbd_setup_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",
.matches = {
......@@ -1493,6 +1544,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.callback = atkbd_setup_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.version = 0x0100;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
ddata->input = input;
for (i = 0; i < pdata->nbuttons; i++) {
......
......@@ -47,6 +47,7 @@
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("serio:ty03pr25id00ex*");
#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 */
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 */
for (col = 0; col < omap_kp->cols; 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);
......
/*
* 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);
if (count) {
/* enable counter 2 */
outb_p(inb_p(0x61) | 3, 0x61);
/* set command for counter 2, 2 byte write */
outb_p(0xB6, 0x43);
/* select desired HZ */
outb_p(count & 0xff, 0x42);
outb((count >> 8) & 0xff, 0x42);
/* enable counter 2 */
outb_p(inb_p(0x61) | 3, 0x61);
} else {
/* disable counter 2 */
outb(inb_p(0x61) & 0xFC, 0x61);
......
......@@ -37,6 +37,7 @@
#include <linux/fs.h>
#include <linux/miscdevice.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)
{
......@@ -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. */
if (id >= UINPUT_NUM_REQUESTS || id < 0)
return NULL;
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;
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;
init_completion(&request.done);
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;
if (count != sizeof(struct input_event))
if (count < input_event_size())
return -EINVAL;
if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
if (input_event_from_user(buffer, &ev))
return -EFAULT;
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)
......@@ -407,13 +420,13 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count,
goto out;
}
while (udev->head != udev->tail && retval + sizeof(struct input_event) <= count) {
if (copy_to_user(buffer + retval, &udev->buff[udev->tail], sizeof(struct input_event))) {
while (udev->head != udev->tail && retval + input_event_size() <= count) {
if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) {
retval = -EFAULT;
goto out;
}
udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
retval += sizeof(struct input_event);
retval += input_event_size();
}
out:
......@@ -444,6 +457,93 @@ static int uinput_release(struct inode *inode, struct file *file)
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) \
({ \
int __ret = 0; \
......@@ -455,19 +555,17 @@ static int uinput_release(struct inode *inode, struct file *file)
__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;
struct uinput_device *udev;
void __user *p = (void __user *)arg;
struct uinput_device *udev = file->private_data;
struct uinput_ff_upload ff_up;
struct uinput_ff_erase ff_erase;
struct uinput_request *req;
int length;
char *phys;
udev = file->private_data;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
......@@ -549,26 +647,24 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
case UI_BEGIN_FF_UPLOAD:
if (copy_from_user(&ff_up, p, sizeof(ff_up))) {
retval = -EFAULT;
retval = uinput_ff_upload_from_user(p, &ff_up);
if (retval)
break;
}
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;
break;
}
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)
memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect));
ff_up.old = *req->u.upload.old;
else
memset(&ff_up.old, 0, sizeof(struct ff_effect));
if (copy_to_user(p, &ff_up, sizeof(ff_up))) {
retval = -EFAULT;
break;
}
retval = uinput_ff_upload_to_user(p, &ff_up);
break;
case UI_BEGIN_FF_ERASE:
......@@ -576,29 +672,34 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
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;
break;
}
ff_erase.retval = 0;
ff_erase.effect_id = req->u.effect_id;
if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
retval = -EFAULT;
break;
}
break;
case UI_END_FF_UPLOAD:
if (copy_from_user(&ff_up, p, sizeof(ff_up))) {
retval = -EFAULT;
retval = uinput_ff_upload_from_user(p, &ff_up);
if (retval)
break;
}
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;
break;
}
req->retval = ff_up.retval;
uinput_request_done(udev, req);
break;
......@@ -608,11 +709,13 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
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;
break;
}
req->retval = ff_erase.retval;
uinput_request_done(udev, req);
break;
......@@ -626,6 +729,18 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
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 = {
.owner = THIS_MODULE,
.open = uinput_open,
......@@ -634,6 +749,9 @@ static const struct file_operations uinput_fops = {
.write = uinput_write,
.poll = uinput_poll,
.unlocked_ioctl = uinput_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = uinput_compat_ioctl,
#endif
};
static struct miscdevice uinput_misc = {
......
......@@ -286,4 +286,10 @@ config MOUSE_GPIO
To compile this driver as a module, choose M here: the
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
......@@ -13,6 +13,7 @@ obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
......
This diff is collapsed.
......@@ -173,7 +173,7 @@ static int __devexit gpio_mouse_remove(struct platform_device *pdev)
/* work with hotplug and coldplug */
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),
.driver = {
.name = "gpio_mouse",
......
......@@ -48,6 +48,30 @@ module_param(recalib_delta, int, 0644);
MODULE_PARM_DESC(recalib_delta,
"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"
* 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
* way I likes it! */
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",
priv->x_tally, priv->y_tally);
psmouse_queue_work(psmouse, &priv->recalib_wq,
msecs_to_jiffies(1000));
msecs_to_jiffies(spew_delay));
}
/* reset every 100 packets */
priv->count = 0;
......@@ -181,7 +205,7 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
"packet inside calibration window, "
"queueing another recalibration\n");
psmouse_queue_work(psmouse, &priv->recalib_wq,
msecs_to_jiffies(1000));
msecs_to_jiffies(post_interrupt_delay));
}
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
* 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;
}
......
......@@ -46,7 +46,7 @@
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("serio:ty03pr25id0Fex*");
#define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */
#undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */
......
/*
* 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_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_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))
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(BTN_TOUCH, 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_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))
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.private = mousedev;
strlcpy(mousedev->dev.bus_id, mousedev->name,
sizeof(mousedev->dev.bus_id));
dev_set_name(&mousedev->dev, mousedev->name);
mousedev->dev.class = &input_class;
if (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->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i);
mlc_serio->id = hil_mlc_serio_id;
mlc_serio->id.id = i; /* HIL port no. */
mlc_serio->write = hil_mlc_serio_write;
mlc_serio->open = hil_mlc_serio_open;
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"),
},
},
{
.ident = "Gigabyte M912",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
DMI_MATCH(DMI_PRODUCT_NAME, "M912"),
DMI_MATCH(DMI_PRODUCT_VERSION, "01"),
},
},
{ }
};
......
......@@ -262,8 +262,16 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
break;
case PS2_RET_NAK:
ps2dev->nak = 1;
ps2dev->flags |= PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_NAK;
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.
......@@ -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_NAK;
if (ps2dev->cmdcnt)
ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
}
ps2dev->flags &= ~PS2_FLAG_ACK;
wake_up(&ps2dev->wait);
......@@ -329,6 +340,7 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev)
if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
wake_up(&ps2dev->wait);
ps2dev->flags = 0;
/* reset all flags except last nack */
ps2dev->flags &= PS2_FLAG_NAK;
}
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->close = pcips2_close;
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->dev.parent = &dev->dev;
ps2if->io = serio;
......
......@@ -546,8 +546,8 @@ static void serio_init_port(struct serio *serio)
spin_lock_init(&serio->lock);
mutex_init(&serio->drv_mutex);
device_initialize(&serio->dev);
snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id),
"serio%ld", (long)atomic_inc_return(&serio_no) - 1);
dev_set_name(&serio->dev, "serio%ld",
(long)atomic_inc_return(&serio_no) - 1);
serio->dev.bus = &serio_bus;
serio->dev.release = serio_release_port;
if (serio->parent) {
......
This diff is collapsed.
......@@ -877,7 +877,7 @@ static int gtco_probe(struct usb_interface *usbinterface,
dbg("num endpoints: %d", usbinterface->cur_altsetting->desc.bNumEndpoints);
dbg("interface class: %d", usbinterface->cur_altsetting->desc.bInterfaceClass);
dbg("endpoint: attribute:0x%x type:0x%x", endpoint->bmAttributes, endpoint->bDescriptorType);
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
if (usb_endpoint_xfer_int(endpoint))
dbg("endpoint: we have interrupt endpoint\n");
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;
}
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;
int prox = 0, pressure;
......
......@@ -58,6 +58,14 @@ config TOUCHSCREEN_CORGI
NOTE: this driver is deprecated, try enable SPI and generic
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
tristate "Fujitsu serial touchscreen"
select SERIO
......@@ -95,6 +103,19 @@ config TOUCHSCREEN_ELO
To compile this driver as a module, choose M here: the
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
tristate "MicroTouch serial touchscreens"
select SERIO
......@@ -376,4 +397,15 @@ config TOUCHSCREEN_TOUCHIT213
To compile this driver as a module, choose M here: the
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
......@@ -25,8 +25,11 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.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_DA9034) += da9034-ts.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.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)
{
struct ads7846 *ts = dev_get_drvdata(dev);
long i;
unsigned long i;
if (strict_strtoul(buf, 10, &i))
return -EINVAL;
......@@ -559,7 +559,7 @@ static void ads7846_rx(void *ads)
if (packet->tc.ignore || Rt > ts->pressure_max) {
#ifdef VERBOSE
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
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
HRTIMER_MODE_REL);
......@@ -947,7 +947,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
ts->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->phys = ts->phys;
......
/*
* 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");
/*
* 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);
if (ret < 0)
goto err_out;
if (buf[0] != 0x06 || buf[1] != 0x00) {
if (buf[0] != 0x06) {
ret = -ENODEV;
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);
if (ret < 0)
goto err_out;
if ((buf[0] != 0x06 || buf[1] != 0x00) &&
(buf[0] != 0x15 || buf[1] != 0x01)) {
if ((buf[0] != 0x06) && (buf[0] != 0x15 || buf[1] != 0x01)) {
ret = -ENODEV;
goto err_out;
}
......
/*
* 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_button *buttons;
int nbuttons;
unsigned int rep:1; /* enable input subsystem auto repeat */
};
#endif
#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_ACK 0xfa
#define PS2_RET_NAK 0xfe
#define PS2_RET_ERR 0xfc
#define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */
#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_WAITID 8 /* Command execiting is GET ID */
#define PS2_FLAG_NAK 16 /* Last transmission was NAKed */
struct ps2dev {
struct serio *serio;
......
......@@ -75,7 +75,7 @@ struct seg7_conversion_map {
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;
}
......
......@@ -213,5 +213,6 @@ static inline void serio_unpin_driver(struct serio *serio)
#define SERIO_ZHENHUA 0x36
#define SERIO_INEXIO 0x37
#define SERIO_TOUCHIT213 0x37
#define SERIO_W8001 0x39
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment