mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (29 commits) Input: i8042 - add Dell Vostro 1510 to nomux list Input: gtco - use USB endpoint API Input: add support for Maple controller as a joystick Input: atkbd - broaden the Dell DMI signatures Input: HIL drivers - add MODULE_ALIAS() Input: map_to_7segment.h - convert to __inline__ for userspace Input: add support for enhanced rotary controller on pxa930 and pxa935 Input: add support for trackball on pxa930 and pxa935 Input: add da9034 touchscreen support Input: ads7846 - strict_strtoul takes unsigned long Input: make some variables and functions static Input: add tsc2007 based touchscreen driver Input: psmouse - add module parameters to control OLPC touchpad delays Input: i8042 - add Gigabyte M912 netbook to noloop exception table Input: atkbd - Samsung NC10 key repeat fix Input: atkbd - add keyboard quirk for HP Pavilion ZV6100 laptop Input: libps2 - handle 0xfc responses from devices Input: add support for Wacom W8001 penabled serial touchscreen Input: synaptics - report multi-taps only if supported by the device Input: add joystick driver for Walkera WK-0701 RC transmitter ...
This commit is contained in:
109
Documentation/input/walkera0701.txt
Normal file
109
Documentation/input/walkera0701.txt
Normal file
@@ -0,0 +1,109 @@
|
||||
|
||||
Walkera WK-0701 transmitter is supplied with a ready to fly Walkera
|
||||
helicopters such as HM36, HM37, HM60. The walkera0701 module enables to use
|
||||
this transmitter as joystick
|
||||
|
||||
Devel homepage and download:
|
||||
http://zub.fei.tuke.sk/walkera-wk0701/
|
||||
|
||||
or use cogito:
|
||||
cg-clone http://zub.fei.tuke.sk/GIT/walkera0701-joystick
|
||||
|
||||
|
||||
Connecting to PC:
|
||||
|
||||
At back side of transmitter S-video connector can be found. Modulation
|
||||
pulses from processor to HF part can be found at pin 2 of this connector,
|
||||
pin 3 is GND. Between pin 3 and CPU 5k6 resistor can be found. To get
|
||||
modulation pulses to PC, signal pulses must be amplified.
|
||||
|
||||
Cable: (walkera TX to parport)
|
||||
|
||||
Walkera WK-0701 TX S-VIDEO connector:
|
||||
(back side of TX)
|
||||
__ __ S-video: canon25
|
||||
/ |_| \ pin 2 (signal) NPN parport
|
||||
/ O 4 3 O \ pin 3 (GND) LED ________________ 10 ACK
|
||||
( O 2 1 O ) | C
|
||||
\ ___ / 2 ________________________|\|_____|/
|
||||
| [___] | |/| B |\
|
||||
------- 3 __________________________________|________________ 25 GND
|
||||
E
|
||||
|
||||
|
||||
I use green LED and BC109 NPN transistor.
|
||||
|
||||
Software:
|
||||
|
||||
Build kernel with walkera0701 module. Module walkera0701 need exclusive
|
||||
access to parport, modules like lp must be unloaded before loading
|
||||
walkera0701 module, check dmesg for error messages. Connect TX to PC by
|
||||
cable and run jstest /dev/input/js0 to see values from TX. If no value can
|
||||
be changed by TX "joystick", check output from /proc/interrupts. Value for
|
||||
(usually irq7) parport must increase if TX is on.
|
||||
|
||||
|
||||
|
||||
Technical details:
|
||||
|
||||
Driver use interrupt from parport ACK input bit to measure pulse length
|
||||
using hrtimers.
|
||||
|
||||
Frame format:
|
||||
Based on walkera WK-0701 PCM Format description by Shaul Eizikovich.
|
||||
(downloaded from http://www.smartpropoplus.com/Docs/Walkera_Wk-0701_PCM.pdf)
|
||||
|
||||
Signal pulses:
|
||||
(ANALOG)
|
||||
SYNC BIN OCT
|
||||
+---------+ +------+
|
||||
| | | |
|
||||
--+ +------+ +---
|
||||
|
||||
Frame:
|
||||
SYNC , BIN1, OCT1, BIN2, OCT2 ... BIN24, OCT24, BIN25, next frame SYNC ..
|
||||
|
||||
pulse length:
|
||||
Binary values: Analog octal values:
|
||||
|
||||
288 uS Binary 0 318 uS 000
|
||||
438 uS Binary 1 398 uS 001
|
||||
478 uS 010
|
||||
558 uS 011
|
||||
638 uS 100
|
||||
1306 uS SYNC 718 uS 101
|
||||
798 uS 110
|
||||
878 uS 111
|
||||
|
||||
24 bin+oct values + 1 bin value = 24*4+1 bits = 97 bits
|
||||
|
||||
(Warning, pulses on ACK ar inverted by transistor, irq is rised up on sync
|
||||
to bin change or octal value to bin change).
|
||||
|
||||
Binary data representations:
|
||||
|
||||
One binary and octal value can be grouped to nibble. 24 nibbles + one binary
|
||||
values can be sampled between sync pulses.
|
||||
|
||||
Values for first four channels (analog joystick values) can be found in
|
||||
first 10 nibbles. Analog value is represented by one sign bit and 9 bit
|
||||
absolute binary value. (10 bits per channel). Next nibble is checksum for
|
||||
first ten nibbles.
|
||||
|
||||
Next nibbles 12 .. 21 represents four channels (not all channels can be
|
||||
directly controlled from TX). Binary representations ar the same as in first
|
||||
four channels. In nibbles 22 and 23 is a special magic number. Nibble 24 is
|
||||
checksum for nibbles 12..23.
|
||||
|
||||
After last octal value for nibble 24 and next sync pulse one additional
|
||||
binary value can be sampled. This bit and magic number is not used in
|
||||
software driver. Some details about this magic numbers can be found in
|
||||
Walkera_Wk-0701_PCM.pdf.
|
||||
|
||||
Checksum calculation:
|
||||
|
||||
Summary of octal values in nibbles must be same as octal value in checksum
|
||||
nibble (only first 3 bits are used). Binary value for checksum nibble is
|
||||
calculated by sum of binary values in checked nibbles + sum of octal values
|
||||
in checked nibbles divided by 8. Only bit 0 of this sum is used.
|
||||
|
||||
20
arch/arm/mach-pxa/include/mach/pxa930_rotary.h
Normal file
20
arch/arm/mach-pxa/include/mach/pxa930_rotary.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef __ASM_ARCH_PXA930_ROTARY_H
|
||||
#define __ASM_ARCH_PXA930_ROTARY_H
|
||||
|
||||
/* NOTE:
|
||||
*
|
||||
* rotary can be either interpreted as a ralative input event (e.g.
|
||||
* REL_WHEEL or REL_HWHEEL) or a specific key event (e.g. UP/DOWN
|
||||
* or LEFT/RIGHT), depending on if up_key & down_key are assigned
|
||||
* or rel_code is assigned a non-zero value. When all are non-zero,
|
||||
* up_key and down_key will be preferred.
|
||||
*/
|
||||
struct pxa930_rotary_platform_data {
|
||||
int up_key;
|
||||
int down_key;
|
||||
int rel_code;
|
||||
};
|
||||
|
||||
void __init pxa930_set_rotarykey_info(struct pxa930_rotary_platform_data *info);
|
||||
|
||||
#endif /* __ASM_ARCH_PXA930_ROTARY_H */
|
||||
10
arch/arm/mach-pxa/include/mach/pxa930_trkball.h
Normal file
10
arch/arm/mach-pxa/include/mach/pxa930_trkball.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __ASM_ARCH_PXA930_TRKBALL_H
|
||||
#define __ASM_ARCH_PXA930_TRKBALL_H
|
||||
|
||||
struct pxa930_trkball_platform_data {
|
||||
int x_filter;
|
||||
int y_filter;
|
||||
};
|
||||
|
||||
#endif /* __ASM_ARCH_PXA930_TRKBALL_H */
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
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;
|
||||
|
||||
|
||||
135
drivers/input/input-compat.c
Normal file
135
drivers/input/input-compat.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 32bit compatibility wrappers for the input subsystem.
|
||||
*
|
||||
* Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include "input-compat.h"
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
int input_event_from_user(const char __user *buffer,
|
||||
struct input_event *event)
|
||||
{
|
||||
if (INPUT_COMPAT_TEST) {
|
||||
struct input_event_compat compat_event;
|
||||
|
||||
if (copy_from_user(&compat_event, buffer,
|
||||
sizeof(struct input_event_compat)))
|
||||
return -EFAULT;
|
||||
|
||||
event->time.tv_sec = compat_event.time.tv_sec;
|
||||
event->time.tv_usec = compat_event.time.tv_usec;
|
||||
event->type = compat_event.type;
|
||||
event->code = compat_event.code;
|
||||
event->value = compat_event.value;
|
||||
|
||||
} else {
|
||||
if (copy_from_user(event, buffer, sizeof(struct input_event)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int input_event_to_user(char __user *buffer,
|
||||
const struct input_event *event)
|
||||
{
|
||||
if (INPUT_COMPAT_TEST) {
|
||||
struct input_event_compat compat_event;
|
||||
|
||||
compat_event.time.tv_sec = event->time.tv_sec;
|
||||
compat_event.time.tv_usec = event->time.tv_usec;
|
||||
compat_event.type = event->type;
|
||||
compat_event.code = event->code;
|
||||
compat_event.value = event->value;
|
||||
|
||||
if (copy_to_user(buffer, &compat_event,
|
||||
sizeof(struct input_event_compat)))
|
||||
return -EFAULT;
|
||||
|
||||
} else {
|
||||
if (copy_to_user(buffer, event, sizeof(struct input_event)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int input_ff_effect_from_user(const char __user *buffer, size_t size,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
if (INPUT_COMPAT_TEST) {
|
||||
struct ff_effect_compat *compat_effect;
|
||||
|
||||
if (size != sizeof(struct ff_effect_compat))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* It so happens that the pointer which needs to be changed
|
||||
* is the last field in the structure, so we can retrieve the
|
||||
* whole thing and replace just the pointer.
|
||||
*/
|
||||
compat_effect = (struct ff_effect_compat *)effect;
|
||||
|
||||
if (copy_from_user(compat_effect, buffer,
|
||||
sizeof(struct ff_effect_compat)))
|
||||
return -EFAULT;
|
||||
|
||||
if (compat_effect->type == FF_PERIODIC &&
|
||||
compat_effect->u.periodic.waveform == FF_CUSTOM)
|
||||
effect->u.periodic.custom_data =
|
||||
compat_ptr(compat_effect->u.periodic.custom_data);
|
||||
} else {
|
||||
if (size != sizeof(struct ff_effect))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int input_event_from_user(const char __user *buffer,
|
||||
struct input_event *event)
|
||||
{
|
||||
if (copy_from_user(event, buffer, sizeof(struct input_event)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int input_event_to_user(char __user *buffer,
|
||||
const struct input_event *event)
|
||||
{
|
||||
if (copy_to_user(buffer, event, sizeof(struct input_event)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int input_ff_effect_from_user(const char __user *buffer, size_t size,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
if (size != sizeof(struct ff_effect))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
EXPORT_SYMBOL_GPL(input_event_from_user);
|
||||
EXPORT_SYMBOL_GPL(input_event_to_user);
|
||||
EXPORT_SYMBOL_GPL(input_ff_effect_from_user);
|
||||
94
drivers/input/input-compat.h
Normal file
94
drivers/input/input-compat.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#ifndef _INPUT_COMPAT_H
|
||||
#define _INPUT_COMPAT_H
|
||||
|
||||
/*
|
||||
* 32bit compatibility wrappers for the input subsystem.
|
||||
*
|
||||
* Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
/* Note to the author of this code: did it ever occur to
|
||||
you why the ifdefs are needed? Think about it again. -AK */
|
||||
#ifdef CONFIG_X86_64
|
||||
# define INPUT_COMPAT_TEST is_compat_task()
|
||||
#elif defined(CONFIG_IA64)
|
||||
# define INPUT_COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
|
||||
#elif defined(CONFIG_S390)
|
||||
# define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT)
|
||||
#elif defined(CONFIG_MIPS)
|
||||
# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR)
|
||||
#else
|
||||
# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT)
|
||||
#endif
|
||||
|
||||
struct input_event_compat {
|
||||
struct compat_timeval time;
|
||||
__u16 type;
|
||||
__u16 code;
|
||||
__s32 value;
|
||||
};
|
||||
|
||||
struct ff_periodic_effect_compat {
|
||||
__u16 waveform;
|
||||
__u16 period;
|
||||
__s16 magnitude;
|
||||
__s16 offset;
|
||||
__u16 phase;
|
||||
|
||||
struct ff_envelope envelope;
|
||||
|
||||
__u32 custom_len;
|
||||
compat_uptr_t custom_data;
|
||||
};
|
||||
|
||||
struct ff_effect_compat {
|
||||
__u16 type;
|
||||
__s16 id;
|
||||
__u16 direction;
|
||||
struct ff_trigger trigger;
|
||||
struct ff_replay replay;
|
||||
|
||||
union {
|
||||
struct ff_constant_effect constant;
|
||||
struct ff_ramp_effect ramp;
|
||||
struct ff_periodic_effect_compat periodic;
|
||||
struct ff_condition_effect condition[2]; /* One for each axis */
|
||||
struct ff_rumble_effect rumble;
|
||||
} u;
|
||||
};
|
||||
|
||||
static inline size_t input_event_size(void)
|
||||
{
|
||||
return INPUT_COMPAT_TEST ?
|
||||
sizeof(struct input_event_compat) : sizeof(struct input_event);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline size_t input_event_size(void)
|
||||
{
|
||||
return sizeof(struct input_event);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
int input_event_from_user(const char __user *buffer,
|
||||
struct input_event *event);
|
||||
|
||||
int input_event_to_user(char __user *buffer,
|
||||
const struct input_event *event);
|
||||
|
||||
int input_ff_effect_from_user(const char __user *buffer, size_t size,
|
||||
struct ff_effect *effect);
|
||||
|
||||
#endif /* _INPUT_COMPAT_H */
|
||||
@@ -1389,8 +1389,8 @@ int input_register_device(struct input_dev *dev)
|
||||
if (!dev->setkeycode)
|
||||
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
|
||||
|
||||
|
||||
193
drivers/input/joystick/maplecontrol.c
Normal file
193
drivers/input/joystick/maplecontrol.c
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* SEGA Dreamcast controller driver
|
||||
* Based on drivers/usb/iforce.c
|
||||
*
|
||||
* Copyright Yaegashi Takeshi, 2001
|
||||
* Adrian McMenamin, 2008
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/maple.h>
|
||||
|
||||
MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
|
||||
MODULE_DESCRIPTION("SEGA Dreamcast controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct dc_pad {
|
||||
struct input_dev *dev;
|
||||
struct maple_device *mdev;
|
||||
};
|
||||
|
||||
static void dc_pad_callback(struct mapleq *mq)
|
||||
{
|
||||
unsigned short buttons;
|
||||
struct maple_device *mapledev = mq->dev;
|
||||
struct dc_pad *pad = maple_get_drvdata(mapledev);
|
||||
struct input_dev *dev = pad->dev;
|
||||
unsigned char *res = mq->recvbuf;
|
||||
|
||||
buttons = ~le16_to_cpup((__le16 *)(res + 8));
|
||||
|
||||
input_report_abs(dev, ABS_HAT0Y,
|
||||
(buttons & 0x0010 ? -1 : 0) + (buttons & 0x0020 ? 1 : 0));
|
||||
input_report_abs(dev, ABS_HAT0X,
|
||||
(buttons & 0x0040 ? -1 : 0) + (buttons & 0x0080 ? 1 : 0));
|
||||
input_report_abs(dev, ABS_HAT1Y,
|
||||
(buttons & 0x1000 ? -1 : 0) + (buttons & 0x2000 ? 1 : 0));
|
||||
input_report_abs(dev, ABS_HAT1X,
|
||||
(buttons & 0x4000 ? -1 : 0) + (buttons & 0x8000 ? 1 : 0));
|
||||
|
||||
input_report_key(dev, BTN_C, buttons & 0x0001);
|
||||
input_report_key(dev, BTN_B, buttons & 0x0002);
|
||||
input_report_key(dev, BTN_A, buttons & 0x0004);
|
||||
input_report_key(dev, BTN_START, buttons & 0x0008);
|
||||
input_report_key(dev, BTN_Z, buttons & 0x0100);
|
||||
input_report_key(dev, BTN_Y, buttons & 0x0200);
|
||||
input_report_key(dev, BTN_X, buttons & 0x0400);
|
||||
input_report_key(dev, BTN_SELECT, buttons & 0x0800);
|
||||
|
||||
input_report_abs(dev, ABS_GAS, res[10]);
|
||||
input_report_abs(dev, ABS_BRAKE, res[11]);
|
||||
input_report_abs(dev, ABS_X, res[12]);
|
||||
input_report_abs(dev, ABS_Y, res[13]);
|
||||
input_report_abs(dev, ABS_RX, res[14]);
|
||||
input_report_abs(dev, ABS_RY, res[15]);
|
||||
}
|
||||
|
||||
static int dc_pad_open(struct input_dev *dev)
|
||||
{
|
||||
struct dc_pad *pad = dev->dev.platform_data;
|
||||
|
||||
maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20,
|
||||
MAPLE_FUNC_CONTROLLER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dc_pad_close(struct input_dev *dev)
|
||||
{
|
||||
struct dc_pad *pad = dev->dev.platform_data;
|
||||
|
||||
maple_getcond_callback(pad->mdev, dc_pad_callback, 0,
|
||||
MAPLE_FUNC_CONTROLLER);
|
||||
}
|
||||
|
||||
/* allow the controller to be used */
|
||||
static int __devinit probe_maple_controller(struct device *dev)
|
||||
{
|
||||
static const short btn_bit[32] = {
|
||||
BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1,
|
||||
BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
static const short abs_bit[32] = {
|
||||
-1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X,
|
||||
-1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X,
|
||||
ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
struct maple_device *mdev = to_maple_dev(dev);
|
||||
struct maple_driver *mdrv = to_maple_driver(dev->driver);
|
||||
int i, error;
|
||||
struct dc_pad *pad;
|
||||
struct input_dev *idev;
|
||||
unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]);
|
||||
|
||||
pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
|
||||
idev = input_allocate_device();
|
||||
if (!pad || !idev) {
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pad->dev = idev;
|
||||
pad->mdev = mdev;
|
||||
|
||||
idev->open = dc_pad_open;
|
||||
idev->close = dc_pad_close;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (data & (1 << i)) {
|
||||
if (btn_bit[i] >= 0)
|
||||
__set_bit(btn_bit[i], idev->keybit);
|
||||
else if (abs_bit[i] >= 0)
|
||||
__set_bit(abs_bit[i], idev->absbit);
|
||||
}
|
||||
}
|
||||
|
||||
if (idev->keybit[BIT_WORD(BTN_JOYSTICK)])
|
||||
idev->evbit[0] |= BIT_MASK(EV_KEY);
|
||||
|
||||
if (idev->absbit[0])
|
||||
idev->evbit[0] |= BIT_MASK(EV_ABS);
|
||||
|
||||
for (i = ABS_X; i <= ABS_BRAKE; i++)
|
||||
input_set_abs_params(idev, i, 0, 255, 0, 0);
|
||||
|
||||
for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++)
|
||||
input_set_abs_params(idev, i, 1, -1, 0, 0);
|
||||
|
||||
idev->dev.platform_data = pad;
|
||||
idev->dev.parent = &mdev->dev;
|
||||
idev->name = mdev->product_name;
|
||||
idev->id.bustype = BUS_HOST;
|
||||
input_set_drvdata(idev, pad);
|
||||
|
||||
error = input_register_device(idev);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
mdev->driver = mdrv;
|
||||
maple_set_drvdata(mdev, pad);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
input_free_device(idev);
|
||||
kfree(pad);
|
||||
maple_set_drvdata(mdev, NULL);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit remove_maple_controller(struct device *dev)
|
||||
{
|
||||
struct maple_device *mdev = to_maple_dev(dev);
|
||||
struct dc_pad *pad = maple_get_drvdata(mdev);
|
||||
|
||||
mdev->callback = NULL;
|
||||
input_unregister_device(pad->dev);
|
||||
maple_set_drvdata(mdev, NULL);
|
||||
kfree(pad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct maple_driver dc_pad_driver = {
|
||||
.function = MAPLE_FUNC_CONTROLLER,
|
||||
.drv = {
|
||||
.name = "Dreamcast_controller",
|
||||
.probe = probe_maple_controller,
|
||||
.remove = __devexit_p(remove_maple_controller),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dc_pad_init(void)
|
||||
{
|
||||
return maple_driver_register(&dc_pad_driver);
|
||||
}
|
||||
|
||||
static void __exit dc_pad_exit(void)
|
||||
{
|
||||
maple_driver_unregister(&dc_pad_driver);
|
||||
}
|
||||
|
||||
module_init(dc_pad_init);
|
||||
module_exit(dc_pad_exit);
|
||||
292
drivers/input/joystick/walkera0701.c
Normal file
292
drivers/input/joystick/walkera0701.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Parallel port to Walkera WK-0701 TX joystick
|
||||
*
|
||||
* Copyright (c) 2008 Peter Popovec
|
||||
*
|
||||
* More about driver: <file:Documentation/input/walkera0701.txt>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* #define WK0701_DEBUG */
|
||||
|
||||
#define RESERVE 20000
|
||||
#define SYNC_PULSE 1306000
|
||||
#define BIN0_PULSE 288000
|
||||
#define BIN1_PULSE 438000
|
||||
|
||||
#define ANALOG_MIN_PULSE 318000
|
||||
#define ANALOG_MAX_PULSE 878000
|
||||
#define ANALOG_DELTA 80000
|
||||
|
||||
#define BIN_SAMPLE ((BIN0_PULSE + BIN1_PULSE) / 2)
|
||||
|
||||
#define NO_SYNC 25
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/hrtimer.h>
|
||||
|
||||
MODULE_AUTHOR("Peter Popovec <popovec@fei.tuke.sk>");
|
||||
MODULE_DESCRIPTION("Walkera WK-0701 TX as joystick");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static unsigned int walkera0701_pp_no;
|
||||
module_param_named(port, walkera0701_pp_no, int, 0);
|
||||
MODULE_PARM_DESC(port,
|
||||
"Parallel port adapter for Walkera WK-0701 TX (default is 0)");
|
||||
|
||||
/*
|
||||
* For now, only one device is supported, if somebody need more devices, code
|
||||
* can be expanded, one struct walkera_dev per device must be allocated and
|
||||
* set up by walkera0701_connect (release of device by walkera0701_disconnect)
|
||||
*/
|
||||
|
||||
struct walkera_dev {
|
||||
unsigned char buf[25];
|
||||
u64 irq_time, irq_lasttime;
|
||||
int counter;
|
||||
int ack;
|
||||
|
||||
struct input_dev *input_dev;
|
||||
struct hrtimer timer;
|
||||
|
||||
struct parport *parport;
|
||||
struct pardevice *pardevice;
|
||||
};
|
||||
|
||||
static struct walkera_dev w_dev;
|
||||
|
||||
static inline void walkera0701_parse_frame(struct walkera_dev *w)
|
||||
{
|
||||
int i;
|
||||
int val1, val2, val3, val4, val5, val6, val7, val8;
|
||||
int crc1, crc2;
|
||||
|
||||
for (crc1 = crc2 = i = 0; i < 10; i++) {
|
||||
crc1 += w->buf[i] & 7;
|
||||
crc2 += (w->buf[i] & 8) >> 3;
|
||||
}
|
||||
if ((w->buf[10] & 7) != (crc1 & 7))
|
||||
return;
|
||||
if (((w->buf[10] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
|
||||
return;
|
||||
for (crc1 = crc2 = 0, i = 11; i < 23; i++) {
|
||||
crc1 += w->buf[i] & 7;
|
||||
crc2 += (w->buf[i] & 8) >> 3;
|
||||
}
|
||||
if ((w->buf[23] & 7) != (crc1 & 7))
|
||||
return;
|
||||
if (((w->buf[23] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
|
||||
return;
|
||||
val1 = ((w->buf[0] & 7) * 256 + w->buf[1] * 16 + w->buf[2]) >> 2;
|
||||
val1 *= ((w->buf[0] >> 2) & 2) - 1; /* sign */
|
||||
val2 = (w->buf[2] & 1) << 8 | (w->buf[3] << 4) | w->buf[4];
|
||||
val2 *= (w->buf[2] & 2) - 1; /* sign */
|
||||
val3 = ((w->buf[5] & 7) * 256 + w->buf[6] * 16 + w->buf[7]) >> 2;
|
||||
val3 *= ((w->buf[5] >> 2) & 2) - 1; /* sign */
|
||||
val4 = (w->buf[7] & 1) << 8 | (w->buf[8] << 4) | w->buf[9];
|
||||
val4 *= (w->buf[7] & 2) - 1; /* sign */
|
||||
val5 = ((w->buf[11] & 7) * 256 + w->buf[12] * 16 + w->buf[13]) >> 2;
|
||||
val5 *= ((w->buf[11] >> 2) & 2) - 1; /* sign */
|
||||
val6 = (w->buf[13] & 1) << 8 | (w->buf[14] << 4) | w->buf[15];
|
||||
val6 *= (w->buf[13] & 2) - 1; /* sign */
|
||||
val7 = ((w->buf[16] & 7) * 256 + w->buf[17] * 16 + w->buf[18]) >> 2;
|
||||
val7 *= ((w->buf[16] >> 2) & 2) - 1; /*sign */
|
||||
val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
|
||||
val8 *= (w->buf[18] & 2) - 1; /*sign */
|
||||
|
||||
#ifdef WK0701_DEBUG
|
||||
{
|
||||
int magic, magic_bit;
|
||||
magic = (w->buf[21] << 4) | w->buf[22];
|
||||
magic_bit = (w->buf[24] & 8) >> 3;
|
||||
printk(KERN_DEBUG
|
||||
"walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
|
||||
val1, val2, val3, val4, val5, val6, val7, val8, magic,
|
||||
magic_bit);
|
||||
}
|
||||
#endif
|
||||
input_report_abs(w->input_dev, ABS_X, val2);
|
||||
input_report_abs(w->input_dev, ABS_Y, val1);
|
||||
input_report_abs(w->input_dev, ABS_Z, val6);
|
||||
input_report_abs(w->input_dev, ABS_THROTTLE, val3);
|
||||
input_report_abs(w->input_dev, ABS_RUDDER, val4);
|
||||
input_report_abs(w->input_dev, ABS_MISC, val7);
|
||||
input_report_key(w->input_dev, BTN_GEAR_DOWN, val5 > 0);
|
||||
}
|
||||
|
||||
static inline int read_ack(struct pardevice *p)
|
||||
{
|
||||
return parport_read_status(p->port) & 0x40;
|
||||
}
|
||||
|
||||
/* falling edge, prepare to BIN value calculation */
|
||||
static void walkera0701_irq_handler(void *handler_data)
|
||||
{
|
||||
u64 pulse_time;
|
||||
struct walkera_dev *w = handler_data;
|
||||
|
||||
w->irq_time = ktime_to_ns(ktime_get());
|
||||
pulse_time = w->irq_time - w->irq_lasttime;
|
||||
w->irq_lasttime = w->irq_time;
|
||||
|
||||
/* cancel timer, if in handler or active do resync */
|
||||
if (unlikely(0 != hrtimer_try_to_cancel(&w->timer))) {
|
||||
w->counter = NO_SYNC;
|
||||
return;
|
||||
}
|
||||
|
||||
if (w->counter < NO_SYNC) {
|
||||
if (w->ack) {
|
||||
pulse_time -= BIN1_PULSE;
|
||||
w->buf[w->counter] = 8;
|
||||
} else {
|
||||
pulse_time -= BIN0_PULSE;
|
||||
w->buf[w->counter] = 0;
|
||||
}
|
||||
if (w->counter == 24) { /* full frame */
|
||||
walkera0701_parse_frame(w);
|
||||
w->counter = NO_SYNC;
|
||||
if (abs(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */
|
||||
w->counter = 0;
|
||||
} else {
|
||||
if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE)
|
||||
&& (pulse_time < (ANALOG_MAX_PULSE + RESERVE)))) {
|
||||
pulse_time -= (ANALOG_MIN_PULSE - RESERVE);
|
||||
pulse_time = (u32) pulse_time / ANALOG_DELTA; /* overtiping is safe, pulsetime < s32.. */
|
||||
w->buf[w->counter++] |= (pulse_time & 7);
|
||||
} else
|
||||
w->counter = NO_SYNC;
|
||||
}
|
||||
} else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) <
|
||||
RESERVE + BIN1_PULSE - BIN0_PULSE) /* frame sync .. */
|
||||
w->counter = 0;
|
||||
|
||||
hrtimer_start(&w->timer, ktime_set(0, BIN_SAMPLE), HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart timer_handler(struct hrtimer
|
||||
*handle)
|
||||
{
|
||||
struct walkera_dev *w;
|
||||
|
||||
w = container_of(handle, struct walkera_dev, timer);
|
||||
w->ack = read_ack(w->pardevice);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static int walkera0701_open(struct input_dev *dev)
|
||||
{
|
||||
struct walkera_dev *w = input_get_drvdata(dev);
|
||||
|
||||
parport_enable_irq(w->parport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void walkera0701_close(struct input_dev *dev)
|
||||
{
|
||||
struct walkera_dev *w = input_get_drvdata(dev);
|
||||
|
||||
parport_disable_irq(w->parport);
|
||||
}
|
||||
|
||||
static int walkera0701_connect(struct walkera_dev *w, int parport)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
|
||||
w->parport = parport_find_number(parport);
|
||||
if (w->parport == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (w->parport->irq == -1) {
|
||||
printk(KERN_ERR "walkera0701: parport without interrupt\n");
|
||||
goto init_err;
|
||||
}
|
||||
|
||||
err = -EBUSY;
|
||||
w->pardevice = parport_register_device(w->parport, "walkera0701",
|
||||
NULL, NULL, walkera0701_irq_handler,
|
||||
PARPORT_DEV_EXCL, w);
|
||||
if (!w->pardevice)
|
||||
goto init_err;
|
||||
|
||||
if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT))
|
||||
goto init_err1;
|
||||
|
||||
if (parport_claim(w->pardevice))
|
||||
goto init_err1;
|
||||
|
||||
w->input_dev = input_allocate_device();
|
||||
if (!w->input_dev)
|
||||
goto init_err2;
|
||||
|
||||
input_set_drvdata(w->input_dev, w);
|
||||
w->input_dev->name = "Walkera WK-0701 TX";
|
||||
w->input_dev->phys = w->parport->name;
|
||||
w->input_dev->id.bustype = BUS_PARPORT;
|
||||
|
||||
/* TODO what id vendor/product/version ? */
|
||||
w->input_dev->id.vendor = 0x0001;
|
||||
w->input_dev->id.product = 0x0001;
|
||||
w->input_dev->id.version = 0x0100;
|
||||
w->input_dev->open = walkera0701_open;
|
||||
w->input_dev->close = walkera0701_close;
|
||||
|
||||
w->input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY);
|
||||
w->input_dev->keybit[BIT_WORD(BTN_GEAR_DOWN)] = BIT_MASK(BTN_GEAR_DOWN);
|
||||
|
||||
input_set_abs_params(w->input_dev, ABS_X, -512, 512, 0, 0);
|
||||
input_set_abs_params(w->input_dev, ABS_Y, -512, 512, 0, 0);
|
||||
input_set_abs_params(w->input_dev, ABS_Z, -512, 512, 0, 0);
|
||||
input_set_abs_params(w->input_dev, ABS_THROTTLE, -512, 512, 0, 0);
|
||||
input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
|
||||
input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
|
||||
|
||||
err = input_register_device(w->input_dev);
|
||||
if (err)
|
||||
goto init_err3;
|
||||
|
||||
hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
w->timer.function = timer_handler;
|
||||
return 0;
|
||||
|
||||
init_err3:
|
||||
input_free_device(w->input_dev);
|
||||
init_err2:
|
||||
parport_release(w->pardevice);
|
||||
init_err1:
|
||||
parport_unregister_device(w->pardevice);
|
||||
init_err:
|
||||
parport_put_port(w->parport);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void walkera0701_disconnect(struct walkera_dev *w)
|
||||
{
|
||||
hrtimer_cancel(&w->timer);
|
||||
input_unregister_device(w->input_dev);
|
||||
parport_release(w->pardevice);
|
||||
parport_unregister_device(w->pardevice);
|
||||
parport_put_port(w->parport);
|
||||
}
|
||||
|
||||
static int __init walkera0701_init(void)
|
||||
{
|
||||
return walkera0701_connect(&w_dev, walkera0701_pp_no);
|
||||
}
|
||||
|
||||
static void __exit walkera0701_exit(void)
|
||||
{
|
||||
walkera0701_disconnect(&w_dev);
|
||||
}
|
||||
|
||||
module_init(walkera0701_init);
|
||||
module_exit(walkera0701_exit);
|
||||
@@ -268,6 +268,15 @@ config KEYBOARD_PXA27x
|
||||
To compile this driver as a module, choose M here: the
|
||||
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++) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user