Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: - an update to Elan touchpad SMBus driver to fetch device parameters (size, resolution) while it is still in PS/2 mode, before switching over to SMBus, as in that mode some devices return garbage dimensions - update to iforce joystick driver - miscellaneous driver fixes * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (48 commits) Input: gpio_keys_polled - allow specifying name of input device Input: edt-ft5x06 - simplify event reporting code Input: max77650-onkey - add MODULE_ALIAS() Input: atmel_mxt_ts - fix leak in mxt_update_cfg() Input: synaptics - enable SMBUS on T480 thinkpad trackpad Input: atmel_mxt_ts - fix -Wunused-const-variable Input: joydev - extend absolute mouse detection HID: quirks: Refactor ELAN 400 and 401 handling Input: elan_i2c - export the device id whitelist Input: edt-ft5x06 - use get_unaligned_be16() Input: iforce - add the Saitek R440 Force Wheel Input: iforce - use unaligned accessors, where appropriate Input: iforce - drop couple of temps from transport code Input: iforce - drop bus type from iforce structure Input: iforce - use DMA-safe buffores for USB transfers Input: iforce - allow callers supply data buffer when fetching device IDs Input: iforce - only call iforce_process_packet() if initialized Input: iforce - signal command completion from transport code Input: iforce - do not combine arguments for iforce_process_packet() Input: iforce - factor out hat handling when parsing packets ...
This commit is contained in:
commit
073c916bc0
@ -13,9 +13,20 @@ Optional properties:
|
||||
pinctrl binding [1]).
|
||||
- vcc-supply: a phandle for the regulator supplying 3.3V power.
|
||||
- elan,trackpoint: touchpad can support a trackpoint (boolean)
|
||||
- elan,clickpad: touchpad is a clickpad (the entire surface is a button)
|
||||
- elan,middle-button: touchpad has a physical middle button
|
||||
- elan,x_traces: number of antennas on the x axis
|
||||
- elan,y_traces: number of antennas on the y axis
|
||||
- some generic touchscreen properties [2]:
|
||||
* touchscreen-size-x
|
||||
* touchscreen-size-y
|
||||
* touchscreen-x-mm
|
||||
* touchscreen-y-mm
|
||||
|
||||
|
||||
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
|
||||
[2]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
|
||||
|
||||
Example:
|
||||
&i2c1 {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/input/elan-i2c-ids.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
@ -916,6 +917,8 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
|
||||
|
||||
bool hid_ignore(struct hid_device *hdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (hdev->quirks & HID_QUIRK_NO_IGNORE)
|
||||
return false;
|
||||
if (hdev->quirks & HID_QUIRK_IGNORE)
|
||||
@ -980,17 +983,14 @@ bool hid_ignore(struct hid_device *hdev)
|
||||
break;
|
||||
case USB_VENDOR_ID_ELAN:
|
||||
/*
|
||||
* Many Elan devices have a product id of 0x0401 and are handled
|
||||
* by the elan_i2c input driver. But the ACPI HID ELAN0800 dev
|
||||
* is not (and cannot be) handled by that driver ->
|
||||
* Ignore all 0x0401 devs except for the ELAN0800 dev.
|
||||
* Blacklist of everything that gets handled by the elan_i2c
|
||||
* input driver. This avoids disabling valid touchpads and
|
||||
* other ELAN devices.
|
||||
*/
|
||||
if (hdev->product == 0x0401 &&
|
||||
strncmp(hdev->name, "ELAN0800", 8) != 0)
|
||||
return true;
|
||||
/* Same with product id 0x0400 */
|
||||
if (hdev->product == 0x0400 &&
|
||||
strncmp(hdev->name, "QTEC0001", 8) != 0)
|
||||
if ((hdev->product == 0x0401 || hdev->product == 0x0400))
|
||||
for (i = 0; strlen(elan_acpi_id[i].id); ++i)
|
||||
if (!strncmp(hdev->name, elan_acpi_id[i].id,
|
||||
strlen(elan_acpi_id[i].id)))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
@ -808,6 +808,7 @@ static bool joydev_dev_is_blacklisted(struct input_dev *dev)
|
||||
static bool joydev_dev_is_absolute_mouse(struct input_dev *dev)
|
||||
{
|
||||
DECLARE_BITMAP(jd_scratch, KEY_CNT);
|
||||
bool ev_match = false;
|
||||
|
||||
BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT);
|
||||
|
||||
@ -826,17 +827,36 @@ static bool joydev_dev_is_absolute_mouse(struct input_dev *dev)
|
||||
* considered to be an absolute mouse if the following is
|
||||
* true:
|
||||
*
|
||||
* 1) Event types are exactly EV_ABS, EV_KEY and EV_SYN.
|
||||
* 1) Event types are exactly
|
||||
* EV_ABS, EV_KEY and EV_SYN
|
||||
* or
|
||||
* EV_ABS, EV_KEY, EV_SYN and EV_MSC
|
||||
* or
|
||||
* EV_ABS, EV_KEY, EV_SYN, EV_MSC and EV_REL.
|
||||
* 2) Absolute events are exactly ABS_X and ABS_Y.
|
||||
* 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE.
|
||||
* 4) Device is not on "Amiga" bus.
|
||||
*/
|
||||
|
||||
bitmap_zero(jd_scratch, EV_CNT);
|
||||
/* VMware VMMouse, HP ILO2 */
|
||||
__set_bit(EV_ABS, jd_scratch);
|
||||
__set_bit(EV_KEY, jd_scratch);
|
||||
__set_bit(EV_SYN, jd_scratch);
|
||||
if (!bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
|
||||
if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
|
||||
ev_match = true;
|
||||
|
||||
/* HP ILO2, AMI BMC firmware */
|
||||
__set_bit(EV_MSC, jd_scratch);
|
||||
if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
|
||||
ev_match = true;
|
||||
|
||||
/* VMware Virtual USB Mouse, QEMU USB Tablet, ATEN BMC firmware */
|
||||
__set_bit(EV_REL, jd_scratch);
|
||||
if (bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
|
||||
ev_match = true;
|
||||
|
||||
if (!ev_match)
|
||||
return false;
|
||||
|
||||
bitmap_zero(jd_scratch, ABS_CNT);
|
||||
|
@ -14,15 +14,15 @@ config JOYSTICK_IFORCE
|
||||
module will be called iforce.
|
||||
|
||||
config JOYSTICK_IFORCE_USB
|
||||
bool "I-Force USB joysticks and wheels"
|
||||
depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB
|
||||
tristate "I-Force USB joysticks and wheels"
|
||||
depends on JOYSTICK_IFORCE && USB
|
||||
help
|
||||
Say Y here if you have an I-Force joystick or steering wheel
|
||||
connected to your USB port.
|
||||
|
||||
config JOYSTICK_IFORCE_232
|
||||
bool "I-Force Serial joysticks and wheels"
|
||||
depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO
|
||||
tristate "I-Force Serial joysticks and wheels"
|
||||
depends on JOYSTICK_IFORCE && SERIO
|
||||
help
|
||||
Say Y here if you have an I-Force joystick or steering wheel
|
||||
connected to your serial (COM) port.
|
||||
|
@ -6,7 +6,6 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
|
||||
|
||||
iforce-y := iforce-ff.o iforce-main.o iforce-packets.o
|
||||
iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o
|
||||
iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o
|
||||
obj-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o
|
||||
obj-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o
|
||||
|
@ -9,10 +9,11 @@
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include "iforce.h"
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
|
||||
MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
|
||||
MODULE_DESCRIPTION("Core I-Force joysticks and wheels driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static signed short btn_joystick[] =
|
||||
@ -55,6 +56,7 @@ static struct iforce_device iforce_device[] = {
|
||||
{ 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
|
||||
{ 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //?
|
||||
{ 0x061c, 0xc084, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce },
|
||||
{ 0x06a3, 0xff04, "Saitek R440 Force Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
|
||||
{ 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //?
|
||||
{ 0x06f8, 0x0001, "Guillemot Jet Leader Force Feedback", btn_joystick, abs_joystick_rudder, ff_iforce },
|
||||
{ 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
|
||||
@ -120,7 +122,6 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect,
|
||||
* Upload the effect
|
||||
*/
|
||||
switch (effect->type) {
|
||||
|
||||
case FF_PERIODIC:
|
||||
ret = iforce_upload_periodic(iforce, effect, old);
|
||||
break;
|
||||
@ -173,15 +174,7 @@ static int iforce_open(struct input_dev *dev)
|
||||
{
|
||||
struct iforce *iforce = input_get_drvdata(dev);
|
||||
|
||||
switch (iforce->bus) {
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
case IFORCE_USB:
|
||||
iforce->irq->dev = iforce->usbdev;
|
||||
if (usb_submit_urb(iforce->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
iforce->xport_ops->start_io(iforce);
|
||||
|
||||
if (test_bit(EV_FF, dev->evbit)) {
|
||||
/* Enable force feedback */
|
||||
@ -214,27 +207,17 @@ static void iforce_close(struct input_dev *dev)
|
||||
!test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags));
|
||||
}
|
||||
|
||||
switch (iforce->bus) {
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
case IFORCE_USB:
|
||||
usb_kill_urb(iforce->irq);
|
||||
usb_kill_urb(iforce->out);
|
||||
usb_kill_urb(iforce->ctrl);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_232
|
||||
case IFORCE_232:
|
||||
//TODO: Wait for the last packets to be sent
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
iforce->xport_ops->stop_io(iforce);
|
||||
}
|
||||
|
||||
int iforce_init_device(struct iforce *iforce)
|
||||
int iforce_init_device(struct device *parent, u16 bustype,
|
||||
struct iforce *iforce)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
struct ff_device *ff;
|
||||
unsigned char c[] = "CEOV";
|
||||
u8 c[] = "CEOV";
|
||||
u8 buf[IFORCE_MAX_LENGTH];
|
||||
size_t len;
|
||||
int i, error;
|
||||
int ff_effects = 0;
|
||||
|
||||
@ -252,20 +235,8 @@ int iforce_init_device(struct iforce *iforce)
|
||||
* Input device fields.
|
||||
*/
|
||||
|
||||
switch (iforce->bus) {
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
case IFORCE_USB:
|
||||
input_dev->id.bustype = BUS_USB;
|
||||
input_dev->dev.parent = &iforce->usbdev->dev;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_232
|
||||
case IFORCE_232:
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->dev.parent = &iforce->serio->dev;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
input_dev->id.bustype = bustype;
|
||||
input_dev->dev.parent = parent;
|
||||
|
||||
input_set_drvdata(input_dev, iforce);
|
||||
|
||||
@ -290,7 +261,7 @@ int iforce_init_device(struct iforce *iforce)
|
||||
*/
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
if (!iforce_get_id_packet(iforce, "O"))
|
||||
if (!iforce_get_id_packet(iforce, 'O', buf, &len))
|
||||
break;
|
||||
|
||||
if (i == 20) { /* 5 seconds */
|
||||
@ -304,23 +275,23 @@ int iforce_init_device(struct iforce *iforce)
|
||||
* Get device info.
|
||||
*/
|
||||
|
||||
if (!iforce_get_id_packet(iforce, "M"))
|
||||
input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1];
|
||||
if (!iforce_get_id_packet(iforce, 'M', buf, &len) || len < 3)
|
||||
input_dev->id.vendor = get_unaligned_le16(buf + 1);
|
||||
else
|
||||
dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n");
|
||||
|
||||
if (!iforce_get_id_packet(iforce, "P"))
|
||||
input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1];
|
||||
if (!iforce_get_id_packet(iforce, 'P', buf, &len) || len < 3)
|
||||
input_dev->id.product = get_unaligned_le16(buf + 1);
|
||||
else
|
||||
dev_warn(&iforce->dev->dev, "Device does not respond to id packet P\n");
|
||||
|
||||
if (!iforce_get_id_packet(iforce, "B"))
|
||||
iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
|
||||
if (!iforce_get_id_packet(iforce, 'B', buf, &len) || len < 3)
|
||||
iforce->device_memory.end = get_unaligned_le16(buf + 1);
|
||||
else
|
||||
dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n");
|
||||
|
||||
if (!iforce_get_id_packet(iforce, "N"))
|
||||
ff_effects = iforce->edata[1];
|
||||
if (!iforce_get_id_packet(iforce, 'N', buf, &len) || len < 2)
|
||||
ff_effects = buf[1];
|
||||
else
|
||||
dev_warn(&iforce->dev->dev, "Device does not respond to id packet N\n");
|
||||
|
||||
@ -336,8 +307,9 @@ int iforce_init_device(struct iforce *iforce)
|
||||
*/
|
||||
|
||||
for (i = 0; c[i]; i++)
|
||||
if (!iforce_get_id_packet(iforce, c + i))
|
||||
iforce_dump_packet(iforce, "info", iforce->ecmd, iforce->edata);
|
||||
if (!iforce_get_id_packet(iforce, c[i], buf, &len))
|
||||
iforce_dump_packet(iforce, "info",
|
||||
(FF_CMD_QUERY & 0xff00) | len, buf);
|
||||
|
||||
/*
|
||||
* Disable spring, enable force feedback.
|
||||
@ -371,11 +343,9 @@ int iforce_init_device(struct iforce *iforce)
|
||||
signed short t = iforce->type->abs[i];
|
||||
|
||||
switch (t) {
|
||||
|
||||
case ABS_X:
|
||||
case ABS_Y:
|
||||
case ABS_WHEEL:
|
||||
|
||||
input_set_abs_params(input_dev, t, -1920, 1920, 16, 128);
|
||||
set_bit(t, input_dev->ffbit);
|
||||
break;
|
||||
@ -383,12 +353,10 @@ int iforce_init_device(struct iforce *iforce)
|
||||
case ABS_THROTTLE:
|
||||
case ABS_GAS:
|
||||
case ABS_BRAKE:
|
||||
|
||||
input_set_abs_params(input_dev, t, 0, 255, 0, 0);
|
||||
break;
|
||||
|
||||
case ABS_RUDDER:
|
||||
|
||||
input_set_abs_params(input_dev, t, -128, 127, 0, 0);
|
||||
break;
|
||||
|
||||
@ -396,7 +364,6 @@ int iforce_init_device(struct iforce *iforce)
|
||||
case ABS_HAT0Y:
|
||||
case ABS_HAT1X:
|
||||
case ABS_HAT1Y:
|
||||
|
||||
input_set_abs_params(input_dev, t, -1, 1, 0, 0);
|
||||
break;
|
||||
}
|
||||
@ -431,35 +398,4 @@ int iforce_init_device(struct iforce *iforce)
|
||||
fail: input_free_device(input_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __init iforce_init(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
err = usb_register(&iforce_usb_driver);
|
||||
if (err)
|
||||
return err;
|
||||
#endif
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_232
|
||||
err = serio_register_driver(&iforce_serio_drv);
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
if (err)
|
||||
usb_deregister(&iforce_usb_driver);
|
||||
#endif
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit iforce_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
usb_deregister(&iforce_usb_driver);
|
||||
#endif
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_232
|
||||
serio_unregister_driver(&iforce_serio_drv);
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(iforce_init);
|
||||
module_exit(iforce_exit);
|
||||
EXPORT_SYMBOL(iforce_init_device);
|
||||
|
@ -9,6 +9,7 @@
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include "iforce.h"
|
||||
|
||||
static struct {
|
||||
@ -79,27 +80,12 @@ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
|
||||
/*
|
||||
* If necessary, start the transmission
|
||||
*/
|
||||
switch (iforce->bus) {
|
||||
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_232
|
||||
case IFORCE_232:
|
||||
if (empty)
|
||||
iforce_serial_xmit(iforce);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
case IFORCE_USB:
|
||||
iforce->xport_ops->xmit(iforce);
|
||||
|
||||
if (iforce->usbdev && empty &&
|
||||
!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
|
||||
|
||||
iforce_usb_xmit(iforce);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iforce_send_packet);
|
||||
|
||||
/* Start or stop an effect */
|
||||
int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
|
||||
@ -133,70 +119,74 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
|
||||
static void iforce_report_hats_buttons(struct iforce *iforce, u8 *data)
|
||||
{
|
||||
struct input_dev *dev = iforce->dev;
|
||||
int i;
|
||||
static int being_used = 0;
|
||||
|
||||
if (being_used)
|
||||
dev_warn(&iforce->dev->dev,
|
||||
"re-entrant call to iforce_process %d\n", being_used);
|
||||
being_used++;
|
||||
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_232
|
||||
if (HI(iforce->expect_packet) == HI(cmd)) {
|
||||
iforce->expect_packet = 0;
|
||||
iforce->ecmd = cmd;
|
||||
memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
|
||||
}
|
||||
#endif
|
||||
wake_up(&iforce->wait);
|
||||
|
||||
if (!iforce->type) {
|
||||
being_used--;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (HI(cmd)) {
|
||||
|
||||
case 0x01: /* joystick position data */
|
||||
case 0x03: /* wheel position data */
|
||||
if (HI(cmd) == 1) {
|
||||
input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
|
||||
input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
|
||||
input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
|
||||
if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
|
||||
input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
|
||||
} else {
|
||||
input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
|
||||
input_report_abs(dev, ABS_GAS, 255 - data[2]);
|
||||
input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
|
||||
input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
|
||||
|
||||
for (i = 0; iforce->type->btn[i] >= 0; i++)
|
||||
input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
|
||||
input_report_key(dev, iforce->type->btn[i],
|
||||
data[(i >> 3) + 5] & (1 << (i & 7)));
|
||||
|
||||
/* If there are untouched bits left, interpret them as the second hat */
|
||||
if (i <= 8) {
|
||||
int btns = data[6];
|
||||
u8 btns = data[6];
|
||||
|
||||
if (test_bit(ABS_HAT1X, dev->absbit)) {
|
||||
if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1);
|
||||
else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1);
|
||||
else input_report_abs(dev, ABS_HAT1X, 0);
|
||||
if (btns & BIT(3))
|
||||
input_report_abs(dev, ABS_HAT1X, -1);
|
||||
else if (btns & BIT(1))
|
||||
input_report_abs(dev, ABS_HAT1X, 1);
|
||||
else
|
||||
input_report_abs(dev, ABS_HAT1X, 0);
|
||||
}
|
||||
|
||||
if (test_bit(ABS_HAT1Y, dev->absbit)) {
|
||||
if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1);
|
||||
else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1);
|
||||
else input_report_abs(dev, ABS_HAT1Y, 0);
|
||||
if (btns & BIT(0))
|
||||
input_report_abs(dev, ABS_HAT1Y, -1);
|
||||
else if (btns & BIT(2))
|
||||
input_report_abs(dev, ABS_HAT1Y, 1);
|
||||
else
|
||||
input_report_abs(dev, ABS_HAT1Y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void iforce_process_packet(struct iforce *iforce,
|
||||
u8 packet_id, u8 *data, size_t len)
|
||||
{
|
||||
struct input_dev *dev = iforce->dev;
|
||||
int i, j;
|
||||
|
||||
switch (packet_id) {
|
||||
|
||||
case 0x01: /* joystick position data */
|
||||
input_report_abs(dev, ABS_X,
|
||||
(__s16) get_unaligned_le16(data));
|
||||
input_report_abs(dev, ABS_Y,
|
||||
(__s16) get_unaligned_le16(data + 2));
|
||||
input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
|
||||
|
||||
if (len >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
|
||||
input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
|
||||
|
||||
iforce_report_hats_buttons(iforce, data);
|
||||
|
||||
input_sync(dev);
|
||||
break;
|
||||
|
||||
case 0x03: /* wheel position data */
|
||||
input_report_abs(dev, ABS_WHEEL,
|
||||
(__s16) get_unaligned_le16(data));
|
||||
input_report_abs(dev, ABS_GAS, 255 - data[2]);
|
||||
input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
|
||||
|
||||
iforce_report_hats_buttons(iforce, data);
|
||||
|
||||
input_sync(dev);
|
||||
break;
|
||||
|
||||
case 0x02: /* status report */
|
||||
@ -214,76 +204,11 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
|
||||
/* Report stop event */
|
||||
input_report_ff_status(dev, i, FF_STATUS_STOPPED);
|
||||
}
|
||||
if (LO(cmd) > 3) {
|
||||
int j;
|
||||
for (j = 3; j < LO(cmd); j += 2)
|
||||
mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
|
||||
}
|
||||
|
||||
for (j = 3; j < len; j += 2)
|
||||
mark_core_as_ready(iforce, get_unaligned_le16(data + j));
|
||||
|
||||
break;
|
||||
}
|
||||
being_used--;
|
||||
}
|
||||
|
||||
int iforce_get_id_packet(struct iforce *iforce, char *packet)
|
||||
{
|
||||
switch (iforce->bus) {
|
||||
|
||||
case IFORCE_USB: {
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
int status;
|
||||
|
||||
iforce->cr.bRequest = packet[0];
|
||||
iforce->ctrl->dev = iforce->usbdev;
|
||||
|
||||
status = usb_submit_urb(iforce->ctrl, GFP_KERNEL);
|
||||
if (status) {
|
||||
dev_err(&iforce->intf->dev,
|
||||
"usb_submit_urb failed %d\n", status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wait_event_interruptible_timeout(iforce->wait,
|
||||
iforce->ctrl->status != -EINPROGRESS, HZ);
|
||||
|
||||
if (iforce->ctrl->status) {
|
||||
dev_dbg(&iforce->intf->dev,
|
||||
"iforce->ctrl->status = %d\n",
|
||||
iforce->ctrl->status);
|
||||
usb_unlink_urb(iforce->ctrl);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
printk(KERN_DEBUG "iforce_get_id_packet: iforce->bus = USB!\n");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case IFORCE_232:
|
||||
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_232
|
||||
iforce->expect_packet = FF_CMD_QUERY;
|
||||
iforce_send_packet(iforce, FF_CMD_QUERY, packet);
|
||||
|
||||
wait_event_interruptible_timeout(iforce->wait,
|
||||
!iforce->expect_packet, HZ);
|
||||
|
||||
if (iforce->expect_packet) {
|
||||
iforce->expect_packet = 0;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
dev_err(&iforce->dev->dev,
|
||||
"iforce_get_id_packet: iforce->bus = SERIO!\n");
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&iforce->dev->dev,
|
||||
"iforce_get_id_packet: iforce->bus = %d\n",
|
||||
iforce->bus);
|
||||
break;
|
||||
}
|
||||
|
||||
return -(iforce->edata[0] != packet[0]);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(iforce_process_packet);
|
||||
|
@ -9,10 +9,26 @@
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/serio.h>
|
||||
#include "iforce.h"
|
||||
|
||||
void iforce_serial_xmit(struct iforce *iforce)
|
||||
struct iforce_serio {
|
||||
struct iforce iforce;
|
||||
|
||||
struct serio *serio;
|
||||
int idx, pkt, len, id;
|
||||
u8 csum;
|
||||
u8 expect_packet;
|
||||
u8 cmd_response[IFORCE_MAX_LENGTH];
|
||||
u8 cmd_response_len;
|
||||
u8 data_in[IFORCE_MAX_LENGTH];
|
||||
};
|
||||
|
||||
static void iforce_serio_xmit(struct iforce *iforce)
|
||||
{
|
||||
struct iforce_serio *iforce_serio = container_of(iforce,
|
||||
struct iforce_serio,
|
||||
iforce);
|
||||
unsigned char cs;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
@ -33,19 +49,20 @@ again:
|
||||
|
||||
cs = 0x2b;
|
||||
|
||||
serio_write(iforce->serio, 0x2b);
|
||||
serio_write(iforce_serio->serio, 0x2b);
|
||||
|
||||
serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
|
||||
serio_write(iforce_serio->serio, iforce->xmit.buf[iforce->xmit.tail]);
|
||||
cs ^= iforce->xmit.buf[iforce->xmit.tail];
|
||||
XMIT_INC(iforce->xmit.tail, 1);
|
||||
|
||||
for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) {
|
||||
serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
|
||||
serio_write(iforce_serio->serio,
|
||||
iforce->xmit.buf[iforce->xmit.tail]);
|
||||
cs ^= iforce->xmit.buf[iforce->xmit.tail];
|
||||
XMIT_INC(iforce->xmit.tail, 1);
|
||||
}
|
||||
|
||||
serio_write(iforce->serio, cs);
|
||||
serio_write(iforce_serio->serio, cs);
|
||||
|
||||
if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
|
||||
goto again;
|
||||
@ -55,54 +72,118 @@ again:
|
||||
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
|
||||
}
|
||||
|
||||
static int iforce_serio_get_id(struct iforce *iforce, u8 id,
|
||||
u8 *response_data, size_t *response_len)
|
||||
{
|
||||
struct iforce_serio *iforce_serio = container_of(iforce,
|
||||
struct iforce_serio,
|
||||
iforce);
|
||||
|
||||
iforce_serio->expect_packet = HI(FF_CMD_QUERY);
|
||||
iforce_serio->cmd_response_len = 0;
|
||||
|
||||
iforce_send_packet(iforce, FF_CMD_QUERY, &id);
|
||||
|
||||
wait_event_interruptible_timeout(iforce->wait,
|
||||
!iforce_serio->expect_packet, HZ);
|
||||
|
||||
if (iforce_serio->expect_packet) {
|
||||
iforce_serio->expect_packet = 0;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (iforce_serio->cmd_response[0] != id)
|
||||
return -EIO;
|
||||
|
||||
memcpy(response_data, iforce_serio->cmd_response,
|
||||
iforce_serio->cmd_response_len);
|
||||
*response_len = iforce_serio->cmd_response_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iforce_serio_start_io(struct iforce *iforce)
|
||||
{
|
||||
/* No special handling required */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iforce_serio_stop_io(struct iforce *iforce)
|
||||
{
|
||||
//TODO: Wait for the last packets to be sent
|
||||
}
|
||||
|
||||
static const struct iforce_xport_ops iforce_serio_xport_ops = {
|
||||
.xmit = iforce_serio_xmit,
|
||||
.get_id = iforce_serio_get_id,
|
||||
.start_io = iforce_serio_start_io,
|
||||
.stop_io = iforce_serio_stop_io,
|
||||
};
|
||||
|
||||
static void iforce_serio_write_wakeup(struct serio *serio)
|
||||
{
|
||||
struct iforce *iforce = serio_get_drvdata(serio);
|
||||
|
||||
iforce_serial_xmit(iforce);
|
||||
iforce_serio_xmit(iforce);
|
||||
}
|
||||
|
||||
static irqreturn_t iforce_serio_irq(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct iforce *iforce = serio_get_drvdata(serio);
|
||||
struct iforce_serio *iforce_serio = serio_get_drvdata(serio);
|
||||
struct iforce *iforce = &iforce_serio->iforce;
|
||||
|
||||
if (!iforce->pkt) {
|
||||
if (!iforce_serio->pkt) {
|
||||
if (data == 0x2b)
|
||||
iforce->pkt = 1;
|
||||
iforce_serio->pkt = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!iforce->id) {
|
||||
if (!iforce_serio->id) {
|
||||
if (data > 3 && data != 0xff)
|
||||
iforce->pkt = 0;
|
||||
iforce_serio->pkt = 0;
|
||||
else
|
||||
iforce->id = data;
|
||||
iforce_serio->id = data;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!iforce->len) {
|
||||
if (!iforce_serio->len) {
|
||||
if (data > IFORCE_MAX_LENGTH) {
|
||||
iforce->pkt = 0;
|
||||
iforce->id = 0;
|
||||
iforce_serio->pkt = 0;
|
||||
iforce_serio->id = 0;
|
||||
} else {
|
||||
iforce->len = data;
|
||||
iforce_serio->len = data;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (iforce->idx < iforce->len) {
|
||||
iforce->csum += iforce->data[iforce->idx++] = data;
|
||||
if (iforce_serio->idx < iforce_serio->len) {
|
||||
iforce_serio->data_in[iforce_serio->idx++] = data;
|
||||
iforce_serio->csum += data;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (iforce->idx == iforce->len) {
|
||||
iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data);
|
||||
iforce->pkt = 0;
|
||||
iforce->id = 0;
|
||||
iforce->len = 0;
|
||||
iforce->idx = 0;
|
||||
iforce->csum = 0;
|
||||
if (iforce_serio->idx == iforce_serio->len) {
|
||||
/* Handle command completion */
|
||||
if (iforce_serio->expect_packet == iforce_serio->id) {
|
||||
iforce_serio->expect_packet = 0;
|
||||
memcpy(iforce_serio->cmd_response,
|
||||
iforce_serio->data_in, IFORCE_MAX_LENGTH);
|
||||
iforce_serio->cmd_response_len = iforce_serio->len;
|
||||
|
||||
/* Signal that command is done */
|
||||
wake_up(&iforce->wait);
|
||||
} else if (likely(iforce->type)) {
|
||||
iforce_process_packet(iforce, iforce_serio->id,
|
||||
iforce_serio->data_in,
|
||||
iforce_serio->len);
|
||||
}
|
||||
|
||||
iforce_serio->pkt = 0;
|
||||
iforce_serio->id = 0;
|
||||
iforce_serio->len = 0;
|
||||
iforce_serio->idx = 0;
|
||||
iforce_serio->csum = 0;
|
||||
}
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
@ -110,23 +191,23 @@ out:
|
||||
|
||||
static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct iforce *iforce;
|
||||
struct iforce_serio *iforce_serio;
|
||||
int err;
|
||||
|
||||
iforce = kzalloc(sizeof(struct iforce), GFP_KERNEL);
|
||||
if (!iforce)
|
||||
iforce_serio = kzalloc(sizeof(*iforce_serio), GFP_KERNEL);
|
||||
if (!iforce_serio)
|
||||
return -ENOMEM;
|
||||
|
||||
iforce->bus = IFORCE_232;
|
||||
iforce->serio = serio;
|
||||
iforce_serio->iforce.xport_ops = &iforce_serio_xport_ops;
|
||||
|
||||
serio_set_drvdata(serio, iforce);
|
||||
iforce_serio->serio = serio;
|
||||
serio_set_drvdata(serio, iforce_serio);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail1;
|
||||
|
||||
err = iforce_init_device(iforce);
|
||||
err = iforce_init_device(&serio->dev, BUS_RS232, &iforce_serio->iforce);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
@ -134,18 +215,18 @@ static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
|
||||
|
||||
fail2: serio_close(serio);
|
||||
fail1: serio_set_drvdata(serio, NULL);
|
||||
kfree(iforce);
|
||||
kfree(iforce_serio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void iforce_serio_disconnect(struct serio *serio)
|
||||
{
|
||||
struct iforce *iforce = serio_get_drvdata(serio);
|
||||
struct iforce_serio *iforce_serio = serio_get_drvdata(serio);
|
||||
|
||||
input_unregister_device(iforce->dev);
|
||||
input_unregister_device(iforce_serio->iforce.dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
kfree(iforce);
|
||||
kfree(iforce_serio);
|
||||
}
|
||||
|
||||
static const struct serio_device_id iforce_serio_ids[] = {
|
||||
@ -171,3 +252,9 @@ struct serio_driver iforce_serio_drv = {
|
||||
.connect = iforce_serio_connect,
|
||||
.disconnect = iforce_serio_disconnect,
|
||||
};
|
||||
|
||||
module_serio_driver(iforce_serio_drv);
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
|
||||
MODULE_DESCRIPTION("RS232 I-Force joysticks and wheels driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -9,10 +9,24 @@
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include "iforce.h"
|
||||
|
||||
void iforce_usb_xmit(struct iforce *iforce)
|
||||
struct iforce_usb {
|
||||
struct iforce iforce;
|
||||
|
||||
struct usb_device *usbdev;
|
||||
struct usb_interface *intf;
|
||||
struct urb *irq, *out;
|
||||
|
||||
u8 data_in[IFORCE_MAX_LENGTH] ____cacheline_aligned;
|
||||
u8 data_out[IFORCE_MAX_LENGTH] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static void __iforce_usb_xmit(struct iforce *iforce)
|
||||
{
|
||||
struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
|
||||
iforce);
|
||||
int n, c;
|
||||
unsigned long flags;
|
||||
|
||||
@ -24,31 +38,32 @@ void iforce_usb_xmit(struct iforce *iforce)
|
||||
return;
|
||||
}
|
||||
|
||||
((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
|
||||
((char *)iforce_usb->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
|
||||
XMIT_INC(iforce->xmit.tail, 1);
|
||||
n = iforce->xmit.buf[iforce->xmit.tail];
|
||||
XMIT_INC(iforce->xmit.tail, 1);
|
||||
|
||||
iforce->out->transfer_buffer_length = n + 1;
|
||||
iforce->out->dev = iforce->usbdev;
|
||||
iforce_usb->out->transfer_buffer_length = n + 1;
|
||||
iforce_usb->out->dev = iforce_usb->usbdev;
|
||||
|
||||
/* Copy rest of data then */
|
||||
c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
|
||||
if (n < c) c=n;
|
||||
|
||||
memcpy(iforce->out->transfer_buffer + 1,
|
||||
memcpy(iforce_usb->out->transfer_buffer + 1,
|
||||
&iforce->xmit.buf[iforce->xmit.tail],
|
||||
c);
|
||||
if (n != c) {
|
||||
memcpy(iforce->out->transfer_buffer + 1 + c,
|
||||
memcpy(iforce_usb->out->transfer_buffer + 1 + c,
|
||||
&iforce->xmit.buf[0],
|
||||
n-c);
|
||||
}
|
||||
XMIT_INC(iforce->xmit.tail, n);
|
||||
|
||||
if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
|
||||
if ( (n=usb_submit_urb(iforce_usb->out, GFP_ATOMIC)) ) {
|
||||
clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
|
||||
dev_warn(&iforce->intf->dev, "usb_submit_urb failed %d\n", n);
|
||||
dev_warn(&iforce_usb->intf->dev,
|
||||
"usb_submit_urb failed %d\n", n);
|
||||
}
|
||||
|
||||
/* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
|
||||
@ -57,10 +72,77 @@ void iforce_usb_xmit(struct iforce *iforce)
|
||||
spin_unlock_irqrestore(&iforce->xmit_lock, flags);
|
||||
}
|
||||
|
||||
static void iforce_usb_xmit(struct iforce *iforce)
|
||||
{
|
||||
if (!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags))
|
||||
__iforce_usb_xmit(iforce);
|
||||
}
|
||||
|
||||
static int iforce_usb_get_id(struct iforce *iforce, u8 id,
|
||||
u8 *response_data, size_t *response_len)
|
||||
{
|
||||
struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
|
||||
iforce);
|
||||
u8 *buf;
|
||||
int status;
|
||||
|
||||
buf = kmalloc(IFORCE_MAX_LENGTH, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
status = usb_control_msg(iforce_usb->usbdev,
|
||||
usb_rcvctrlpipe(iforce_usb->usbdev, 0),
|
||||
id,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN |
|
||||
USB_RECIP_INTERFACE,
|
||||
0, 0, buf, IFORCE_MAX_LENGTH, HZ);
|
||||
if (status < 0) {
|
||||
dev_err(&iforce_usb->intf->dev,
|
||||
"usb_submit_urb failed: %d\n", status);
|
||||
} else if (buf[0] != id) {
|
||||
status = -EIO;
|
||||
} else {
|
||||
memcpy(response_data, buf, status);
|
||||
*response_len = status;
|
||||
status = 0;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int iforce_usb_start_io(struct iforce *iforce)
|
||||
{
|
||||
struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
|
||||
iforce);
|
||||
|
||||
if (usb_submit_urb(iforce_usb->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iforce_usb_stop_io(struct iforce *iforce)
|
||||
{
|
||||
struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
|
||||
iforce);
|
||||
|
||||
usb_kill_urb(iforce_usb->irq);
|
||||
usb_kill_urb(iforce_usb->out);
|
||||
}
|
||||
|
||||
static const struct iforce_xport_ops iforce_usb_xport_ops = {
|
||||
.xmit = iforce_usb_xmit,
|
||||
.get_id = iforce_usb_get_id,
|
||||
.start_io = iforce_usb_start_io,
|
||||
.stop_io = iforce_usb_stop_io,
|
||||
};
|
||||
|
||||
static void iforce_usb_irq(struct urb *urb)
|
||||
{
|
||||
struct iforce *iforce = urb->context;
|
||||
struct device *dev = &iforce->intf->dev;
|
||||
struct iforce_usb *iforce_usb = urb->context;
|
||||
struct iforce *iforce = &iforce_usb->iforce;
|
||||
struct device *dev = &iforce_usb->intf->dev;
|
||||
int status;
|
||||
|
||||
switch (urb->status) {
|
||||
@ -80,11 +162,11 @@ static void iforce_usb_irq(struct urb *urb)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
iforce_process_packet(iforce,
|
||||
(iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
|
||||
iforce_process_packet(iforce, iforce_usb->data_in[0],
|
||||
iforce_usb->data_in + 1, urb->actual_length - 1);
|
||||
|
||||
exit:
|
||||
status = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
status = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (status)
|
||||
dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
|
||||
__func__, status);
|
||||
@ -92,35 +174,28 @@ exit:
|
||||
|
||||
static void iforce_usb_out(struct urb *urb)
|
||||
{
|
||||
struct iforce *iforce = urb->context;
|
||||
struct iforce_usb *iforce_usb = urb->context;
|
||||
struct iforce *iforce = &iforce_usb->iforce;
|
||||
|
||||
if (urb->status) {
|
||||
clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
|
||||
dev_dbg(&iforce->intf->dev, "urb->status %d, exiting\n",
|
||||
dev_dbg(&iforce_usb->intf->dev, "urb->status %d, exiting\n",
|
||||
urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
iforce_usb_xmit(iforce);
|
||||
__iforce_usb_xmit(iforce);
|
||||
|
||||
wake_up(&iforce->wait);
|
||||
}
|
||||
|
||||
static void iforce_usb_ctrl(struct urb *urb)
|
||||
{
|
||||
struct iforce *iforce = urb->context;
|
||||
if (urb->status) return;
|
||||
iforce->ecmd = 0xff00 | urb->actual_length;
|
||||
wake_up(&iforce->wait);
|
||||
}
|
||||
|
||||
static int iforce_usb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *epirq, *epout;
|
||||
struct iforce *iforce;
|
||||
struct iforce_usb *iforce_usb;
|
||||
int err = -ENOMEM;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
@ -131,48 +206,45 @@ static int iforce_usb_probe(struct usb_interface *intf,
|
||||
epirq = &interface->endpoint[0].desc;
|
||||
epout = &interface->endpoint[1].desc;
|
||||
|
||||
if (!(iforce = kzalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
|
||||
iforce_usb = kzalloc(sizeof(*iforce_usb), GFP_KERNEL);
|
||||
if (!iforce_usb)
|
||||
goto fail;
|
||||
|
||||
if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
iforce_usb->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!iforce_usb->irq)
|
||||
goto fail;
|
||||
|
||||
if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
iforce_usb->out = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!iforce_usb->out)
|
||||
goto fail;
|
||||
|
||||
if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
goto fail;
|
||||
iforce_usb->iforce.xport_ops = &iforce_usb_xport_ops;
|
||||
|
||||
iforce->bus = IFORCE_USB;
|
||||
iforce->usbdev = dev;
|
||||
iforce->intf = intf;
|
||||
iforce_usb->usbdev = dev;
|
||||
iforce_usb->intf = intf;
|
||||
|
||||
iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
|
||||
iforce->cr.wIndex = 0;
|
||||
iforce->cr.wLength = cpu_to_le16(16);
|
||||
usb_fill_int_urb(iforce_usb->irq, dev,
|
||||
usb_rcvintpipe(dev, epirq->bEndpointAddress),
|
||||
iforce_usb->data_in, sizeof(iforce_usb->data_in),
|
||||
iforce_usb_irq, iforce_usb, epirq->bInterval);
|
||||
|
||||
usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
|
||||
iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
|
||||
usb_fill_int_urb(iforce_usb->out, dev,
|
||||
usb_sndintpipe(dev, epout->bEndpointAddress),
|
||||
iforce_usb->data_out, sizeof(iforce_usb->data_out),
|
||||
iforce_usb_out, iforce_usb, epout->bInterval);
|
||||
|
||||
usb_fill_int_urb(iforce->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress),
|
||||
iforce + 1, 32, iforce_usb_out, iforce, epout->bInterval);
|
||||
|
||||
usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
|
||||
(void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
|
||||
|
||||
err = iforce_init_device(iforce);
|
||||
err = iforce_init_device(&intf->dev, BUS_USB, &iforce_usb->iforce);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
usb_set_intfdata(intf, iforce);
|
||||
usb_set_intfdata(intf, iforce_usb);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (iforce) {
|
||||
usb_free_urb(iforce->irq);
|
||||
usb_free_urb(iforce->out);
|
||||
usb_free_urb(iforce->ctrl);
|
||||
kfree(iforce);
|
||||
if (iforce_usb) {
|
||||
usb_free_urb(iforce_usb->irq);
|
||||
usb_free_urb(iforce_usb->out);
|
||||
kfree(iforce_usb);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -180,17 +252,16 @@ fail:
|
||||
|
||||
static void iforce_usb_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct iforce *iforce = usb_get_intfdata(intf);
|
||||
struct iforce_usb *iforce_usb = usb_get_intfdata(intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
input_unregister_device(iforce->dev);
|
||||
input_unregister_device(iforce_usb->iforce.dev);
|
||||
|
||||
usb_free_urb(iforce->irq);
|
||||
usb_free_urb(iforce->out);
|
||||
usb_free_urb(iforce->ctrl);
|
||||
usb_free_urb(iforce_usb->irq);
|
||||
usb_free_urb(iforce_usb->out);
|
||||
|
||||
kfree(iforce);
|
||||
kfree(iforce_usb);
|
||||
}
|
||||
|
||||
static const struct usb_device_id iforce_usb_ids[] = {
|
||||
@ -202,6 +273,7 @@ static const struct usb_device_id iforce_usb_ids[] = {
|
||||
{ USB_DEVICE(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */
|
||||
{ USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */
|
||||
{ USB_DEVICE(0x061c, 0xc084) }, /* ACT LABS Force RS */
|
||||
{ USB_DEVICE(0x06a3, 0xff04) }, /* Saitek R440 Force Wheel */
|
||||
{ USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */
|
||||
{ USB_DEVICE(0x06f8, 0x0003) }, /* Guillemot Jet Leader Force Feedback */
|
||||
{ USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */
|
||||
@ -217,3 +289,9 @@ struct usb_driver iforce_usb_driver = {
|
||||
.disconnect = iforce_usb_disconnect,
|
||||
.id_table = iforce_usb_ids,
|
||||
};
|
||||
|
||||
module_usb_driver(iforce_usb_driver);
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
|
||||
MODULE_DESCRIPTION("USB I-Force joysticks and wheels driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -14,8 +14,6 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
@ -28,10 +26,6 @@
|
||||
|
||||
#define IFORCE_MAX_LENGTH 16
|
||||
|
||||
/* iforce::bus */
|
||||
#define IFORCE_232 1
|
||||
#define IFORCE_USB 2
|
||||
|
||||
#define IFORCE_EFFECTS_MAX 32
|
||||
|
||||
/* Each force feedback effect is made of one core effect, which can be
|
||||
@ -81,27 +75,21 @@ struct iforce_device {
|
||||
signed short *ff;
|
||||
};
|
||||
|
||||
struct iforce;
|
||||
|
||||
struct iforce_xport_ops {
|
||||
void (*xmit)(struct iforce *iforce);
|
||||
int (*get_id)(struct iforce *iforce, u8 id,
|
||||
u8 *response_data, size_t *response_len);
|
||||
int (*start_io)(struct iforce *iforce);
|
||||
void (*stop_io)(struct iforce *iforce);
|
||||
};
|
||||
|
||||
struct iforce {
|
||||
struct input_dev *dev; /* Input device interface */
|
||||
struct iforce_device *type;
|
||||
int bus;
|
||||
const struct iforce_xport_ops *xport_ops;
|
||||
|
||||
unsigned char data[IFORCE_MAX_LENGTH];
|
||||
unsigned char edata[IFORCE_MAX_LENGTH];
|
||||
u16 ecmd;
|
||||
u16 expect_packet;
|
||||
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_232
|
||||
struct serio *serio; /* RS232 transfer */
|
||||
int idx, pkt, len, id;
|
||||
unsigned char csum;
|
||||
#endif
|
||||
#ifdef CONFIG_JOYSTICK_IFORCE_USB
|
||||
struct usb_device *usbdev; /* USB transfer */
|
||||
struct usb_interface *intf;
|
||||
struct urb *irq, *out, *ctrl;
|
||||
struct usb_ctrlrequest cr;
|
||||
#endif
|
||||
spinlock_t xmit_lock;
|
||||
/* Buffer used for asynchronous sending of bytes to the device */
|
||||
struct circ_buf xmit;
|
||||
@ -127,23 +115,24 @@ struct iforce {
|
||||
/* Encode a time value */
|
||||
#define TIME_SCALE(a) (a)
|
||||
|
||||
static inline int iforce_get_id_packet(struct iforce *iforce, u8 id,
|
||||
u8 *response_data, size_t *response_len)
|
||||
{
|
||||
return iforce->xport_ops->get_id(iforce, id,
|
||||
response_data, response_len);
|
||||
}
|
||||
|
||||
/* Public functions */
|
||||
/* iforce-serio.c */
|
||||
void iforce_serial_xmit(struct iforce *iforce);
|
||||
|
||||
/* iforce-usb.c */
|
||||
void iforce_usb_xmit(struct iforce *iforce);
|
||||
|
||||
/* iforce-main.c */
|
||||
int iforce_init_device(struct iforce *iforce);
|
||||
int iforce_init_device(struct device *parent, u16 bustype,
|
||||
struct iforce *iforce);
|
||||
|
||||
/* iforce-packets.c */
|
||||
int iforce_control_playback(struct iforce*, u16 id, unsigned int);
|
||||
void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data);
|
||||
void iforce_process_packet(struct iforce *iforce,
|
||||
u8 packet_id, u8 *data, size_t len);
|
||||
int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
|
||||
void iforce_dump_packet(struct iforce *iforce, char *msg, u16 cmd, unsigned char *data);
|
||||
int iforce_get_id_packet(struct iforce *iforce, char *packet);
|
||||
|
||||
/* iforce-ff.c */
|
||||
int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *);
|
||||
|
@ -771,7 +771,6 @@ static int gpio_keys_probe(struct platform_device *pdev)
|
||||
struct fwnode_handle *child = NULL;
|
||||
struct gpio_keys_drvdata *ddata;
|
||||
struct input_dev *input;
|
||||
size_t size;
|
||||
int i, error;
|
||||
int wakeup = 0;
|
||||
|
||||
@ -781,9 +780,8 @@ static int gpio_keys_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
|
||||
size = sizeof(struct gpio_keys_drvdata) +
|
||||
pdata->nbuttons * sizeof(struct gpio_button_data);
|
||||
ddata = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
ddata = devm_kzalloc(dev, struct_size(ddata, data, pdata->nbuttons),
|
||||
GFP_KERNEL);
|
||||
if (!ddata) {
|
||||
dev_err(dev, "failed to allocate state\n");
|
||||
return -ENOMEM;
|
||||
|
@ -165,6 +165,8 @@ gpio_keys_polled_get_devtree_pdata(struct device *dev)
|
||||
pdata->rep = device_property_present(dev, "autorepeat");
|
||||
device_property_read_u32(dev, "poll-interval", &pdata->poll_interval);
|
||||
|
||||
device_property_read_string(dev, "label", &pdata->name);
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
if (fwnode_property_read_u32(child, "linux,code",
|
||||
&button->code)) {
|
||||
@ -232,7 +234,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
|
||||
struct gpio_keys_polled_dev *bdev;
|
||||
struct input_polled_dev *poll_dev;
|
||||
struct input_dev *input;
|
||||
size_t size;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
@ -247,9 +248,8 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = sizeof(struct gpio_keys_polled_dev) +
|
||||
pdata->nbuttons * sizeof(struct gpio_keys_button_data);
|
||||
bdev = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
bdev = devm_kzalloc(dev, struct_size(bdev, data, pdata->nbuttons),
|
||||
GFP_KERNEL);
|
||||
if (!bdev) {
|
||||
dev_err(dev, "no memory for private data\n");
|
||||
return -ENOMEM;
|
||||
@ -269,7 +269,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
|
||||
|
||||
input = poll_dev->input;
|
||||
|
||||
input->name = pdev->name;
|
||||
input->name = pdata->name ?: pdev->name;
|
||||
input->phys = DRV_NAME"/input0";
|
||||
|
||||
input->id.bustype = BUS_HOST;
|
||||
|
@ -422,7 +422,6 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
||||
dev_get_platdata(&pdev->dev);
|
||||
struct imx_keypad *keypad;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
int irq, error, i, row, col;
|
||||
|
||||
if (!keymap_data && !pdev->dev.of_node) {
|
||||
@ -455,8 +454,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
||||
timer_setup(&keypad->check_matrix_timer,
|
||||
imx_keypad_check_for_events, 0);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
keypad->mmio_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(keypad->mmio_base))
|
||||
return PTR_ERR(keypad->mmio_base);
|
||||
|
||||
|
@ -266,7 +266,7 @@ static int tca8418_keypad_probe(struct i2c_client *client,
|
||||
struct tca8418_keypad *keypad_data;
|
||||
struct input_dev *input;
|
||||
u32 rows = 0, cols = 0;
|
||||
int error, row_shift, max_keys;
|
||||
int error, row_shift;
|
||||
u8 reg;
|
||||
|
||||
/* Check i2c driver capabilities */
|
||||
@ -291,7 +291,6 @@ static int tca8418_keypad_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
row_shift = get_count_order(cols);
|
||||
max_keys = rows << row_shift;
|
||||
|
||||
/* Allocate memory for keypad_data and input device */
|
||||
keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL);
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/da9063/core.h>
|
||||
#include <linux/mfd/da9063/pdata.h>
|
||||
#include <linux/mfd/da9063/registers.h>
|
||||
#include <linux/mfd/da9062/core.h>
|
||||
#include <linux/mfd/da9062/registers.h>
|
||||
@ -192,8 +191,6 @@ static void da9063_cancel_poll(void *data)
|
||||
|
||||
static int da9063_onkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct da9063_pdata *pdata = dev_get_platdata(da9063->dev);
|
||||
struct da9063_onkey *onkey;
|
||||
const struct of_device_id *match;
|
||||
int irq;
|
||||
@ -220,11 +217,7 @@ static int da9063_onkey_probe(struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (pdata)
|
||||
onkey->key_power = pdata->key_power;
|
||||
else
|
||||
onkey->key_power =
|
||||
!of_property_read_bool(pdev->dev.of_node,
|
||||
onkey->key_power = !of_property_read_bool(pdev->dev.of_node,
|
||||
"dlg,disable-key-power");
|
||||
|
||||
onkey->input = devm_input_allocate_device(&pdev->dev);
|
||||
|
@ -119,3 +119,4 @@ module_platform_driver(max77650_onkey_driver);
|
||||
MODULE_DESCRIPTION("MAXIM 77650/77651 ONKEY driver");
|
||||
MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:max77650-onkey");
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/input/elan-i2c-ids.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
@ -96,6 +97,7 @@ struct elan_tp_data {
|
||||
u8 max_baseline;
|
||||
bool baseline_ready;
|
||||
u8 clickpad;
|
||||
bool middle_button;
|
||||
};
|
||||
|
||||
static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
|
||||
@ -363,27 +365,62 @@ static unsigned int elan_convert_resolution(u8 val)
|
||||
|
||||
static int elan_query_device_parameters(struct elan_tp_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned int x_traces, y_traces;
|
||||
u32 x_mm, y_mm;
|
||||
u8 hw_x_res, hw_y_res;
|
||||
int error;
|
||||
|
||||
error = data->ops->get_max(data->client, &data->max_x, &data->max_y);
|
||||
if (device_property_read_u32(&client->dev,
|
||||
"touchscreen-size-x", &data->max_x) ||
|
||||
device_property_read_u32(&client->dev,
|
||||
"touchscreen-size-y", &data->max_y)) {
|
||||
error = data->ops->get_max(data->client,
|
||||
&data->max_x,
|
||||
&data->max_y);
|
||||
if (error)
|
||||
return error;
|
||||
} else {
|
||||
/* size is the maximum + 1 */
|
||||
--data->max_x;
|
||||
--data->max_y;
|
||||
}
|
||||
|
||||
error = data->ops->get_num_traces(data->client, &x_traces, &y_traces);
|
||||
if (device_property_read_u32(&client->dev,
|
||||
"elan,x_traces",
|
||||
&x_traces) ||
|
||||
device_property_read_u32(&client->dev,
|
||||
"elan,y_traces",
|
||||
&y_traces)) {
|
||||
error = data->ops->get_num_traces(data->client,
|
||||
&x_traces, &y_traces);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
}
|
||||
data->width_x = data->max_x / x_traces;
|
||||
data->width_y = data->max_y / y_traces;
|
||||
|
||||
error = data->ops->get_resolution(data->client, &hw_x_res, &hw_y_res);
|
||||
if (device_property_read_u32(&client->dev,
|
||||
"touchscreen-x-mm", &x_mm) ||
|
||||
device_property_read_u32(&client->dev,
|
||||
"touchscreen-y-mm", &y_mm)) {
|
||||
error = data->ops->get_resolution(data->client,
|
||||
&hw_x_res, &hw_y_res);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
data->x_res = elan_convert_resolution(hw_x_res);
|
||||
data->y_res = elan_convert_resolution(hw_y_res);
|
||||
} else {
|
||||
data->x_res = (data->max_x + 1) / x_mm;
|
||||
data->y_res = (data->max_y + 1) / y_mm;
|
||||
}
|
||||
|
||||
if (device_property_read_bool(&client->dev, "elan,clickpad"))
|
||||
data->clickpad = 1;
|
||||
|
||||
if (device_property_read_bool(&client->dev, "elan,middle-button"))
|
||||
data->middle_button = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -923,8 +960,9 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
|
||||
finger_data += ETP_FINGER_DATA_LEN;
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_LEFT, tp_info & 0x01);
|
||||
input_report_key(input, BTN_RIGHT, tp_info & 0x02);
|
||||
input_report_key(input, BTN_LEFT, tp_info & BIT(0));
|
||||
input_report_key(input, BTN_MIDDLE, tp_info & BIT(2));
|
||||
input_report_key(input, BTN_RIGHT, tp_info & BIT(1));
|
||||
input_report_abs(input, ABS_DISTANCE, hover_event != 0);
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
input_sync(input);
|
||||
@ -1058,10 +1096,13 @@ static int elan_setup_input_device(struct elan_tp_data *data)
|
||||
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
||||
if (data->clickpad)
|
||||
if (data->clickpad) {
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
else
|
||||
} else {
|
||||
__set_bit(BTN_RIGHT, input->keybit);
|
||||
if (data->middle_button)
|
||||
__set_bit(BTN_MIDDLE, input->keybit);
|
||||
}
|
||||
__set_bit(BTN_LEFT, input->keybit);
|
||||
|
||||
/* Set up ST parameters */
|
||||
@ -1332,55 +1373,6 @@ static const struct i2c_device_id elan_id[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, elan_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id elan_acpi_id[] = {
|
||||
{ "ELAN0000", 0 },
|
||||
{ "ELAN0100", 0 },
|
||||
{ "ELAN0600", 0 },
|
||||
{ "ELAN0601", 0 },
|
||||
{ "ELAN0602", 0 },
|
||||
{ "ELAN0603", 0 },
|
||||
{ "ELAN0604", 0 },
|
||||
{ "ELAN0605", 0 },
|
||||
{ "ELAN0606", 0 },
|
||||
{ "ELAN0607", 0 },
|
||||
{ "ELAN0608", 0 },
|
||||
{ "ELAN0609", 0 },
|
||||
{ "ELAN060B", 0 },
|
||||
{ "ELAN060C", 0 },
|
||||
{ "ELAN060F", 0 },
|
||||
{ "ELAN0610", 0 },
|
||||
{ "ELAN0611", 0 },
|
||||
{ "ELAN0612", 0 },
|
||||
{ "ELAN0615", 0 },
|
||||
{ "ELAN0616", 0 },
|
||||
{ "ELAN0617", 0 },
|
||||
{ "ELAN0618", 0 },
|
||||
{ "ELAN0619", 0 },
|
||||
{ "ELAN061A", 0 },
|
||||
{ "ELAN061B", 0 },
|
||||
{ "ELAN061C", 0 },
|
||||
{ "ELAN061D", 0 },
|
||||
{ "ELAN061E", 0 },
|
||||
{ "ELAN061F", 0 },
|
||||
{ "ELAN0620", 0 },
|
||||
{ "ELAN0621", 0 },
|
||||
{ "ELAN0622", 0 },
|
||||
{ "ELAN0623", 0 },
|
||||
{ "ELAN0624", 0 },
|
||||
{ "ELAN0625", 0 },
|
||||
{ "ELAN0626", 0 },
|
||||
{ "ELAN0627", 0 },
|
||||
{ "ELAN0628", 0 },
|
||||
{ "ELAN0629", 0 },
|
||||
{ "ELAN062A", 0 },
|
||||
{ "ELAN062B", 0 },
|
||||
{ "ELAN062C", 0 },
|
||||
{ "ELAN062D", 0 },
|
||||
{ "ELAN0631", 0 },
|
||||
{ "ELAN0632", 0 },
|
||||
{ "ELAN1000", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, elan_acpi_id);
|
||||
#endif
|
||||
|
||||
|
@ -226,6 +226,52 @@ static void elantech_packet_dump(struct psmouse *psmouse)
|
||||
psmouse->pktsize, psmouse->packet);
|
||||
}
|
||||
|
||||
/*
|
||||
* Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
|
||||
* fw_version for this is based on the following fw_version & caps table:
|
||||
*
|
||||
* Laptop-model: fw_version: caps: buttons:
|
||||
* Acer S3 0x461f00 10, 13, 0e clickpad
|
||||
* Acer S7-392 0x581f01 50, 17, 0d clickpad
|
||||
* Acer V5-131 0x461f02 01, 16, 0c clickpad
|
||||
* Acer V5-551 0x461f00 ? clickpad
|
||||
* Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons
|
||||
* Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons
|
||||
* Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons
|
||||
* Asus TP500LN 0x381f17 10, 14, 0e clickpad
|
||||
* Asus X750JN 0x381f17 10, 14, 0e clickpad
|
||||
* Asus UX31 0x361f00 20, 15, 0e clickpad
|
||||
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
|
||||
* Avatar AVIU-145A2 0x361f00 ? clickpad
|
||||
* Fujitsu CELSIUS H760 0x570f02 40, 14, 0c 3 hw buttons (**)
|
||||
* Fujitsu CELSIUS H780 0x5d0f02 41, 16, 0d 3 hw buttons (**)
|
||||
* Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons
|
||||
* Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons
|
||||
* Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons
|
||||
* Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons
|
||||
* Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons
|
||||
* Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons
|
||||
* Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**)
|
||||
* Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons
|
||||
* Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*)
|
||||
* Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*)
|
||||
* Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons
|
||||
* Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad
|
||||
* Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad
|
||||
* Samsung NP900X3E-A02 0x575f03 ? clickpad
|
||||
* Samsung NP-QX410 0x851b00 19, 14, 0c clickpad
|
||||
* Samsung RC512 0x450f00 08, 15, 0c 2 hw buttons
|
||||
* Samsung RF710 0x450f00 ? 2 hw buttons
|
||||
* System76 Pangolin 0x250f01 ? 2 hw buttons
|
||||
* (*) + 3 trackpoint buttons
|
||||
* (**) + 0 trackpoint buttons
|
||||
* Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
|
||||
*/
|
||||
static inline int elantech_is_buttonpad(struct elantech_device_info *info)
|
||||
{
|
||||
return info->fw_version & 0x001000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interpret complete data packets and report absolute mode input events for
|
||||
* hardware version 1. (4 byte packets)
|
||||
@ -523,7 +569,7 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
|
||||
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
|
||||
|
||||
/* For clickpads map both buttons to BTN_LEFT */
|
||||
if (etd->info.fw_version & 0x001000)
|
||||
if (elantech_is_buttonpad(&etd->info))
|
||||
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
|
||||
else
|
||||
psmouse_report_standard_buttons(dev, packet[0]);
|
||||
@ -541,7 +587,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
|
||||
unsigned char *packet = psmouse->packet;
|
||||
|
||||
/* For clickpads map both buttons to BTN_LEFT */
|
||||
if (etd->info.fw_version & 0x001000)
|
||||
if (elantech_is_buttonpad(&etd->info))
|
||||
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
|
||||
else
|
||||
psmouse_report_standard_buttons(dev, packet[0]);
|
||||
@ -991,88 +1037,6 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int elantech_set_range(struct psmouse *psmouse,
|
||||
unsigned int *x_min, unsigned int *y_min,
|
||||
unsigned int *x_max, unsigned int *y_max,
|
||||
unsigned int *width)
|
||||
{
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
struct elantech_device_info *info = &etd->info;
|
||||
unsigned char param[3];
|
||||
unsigned char traces;
|
||||
|
||||
switch (info->hw_version) {
|
||||
case 1:
|
||||
*x_min = ETP_XMIN_V1;
|
||||
*y_min = ETP_YMIN_V1;
|
||||
*x_max = ETP_XMAX_V1;
|
||||
*y_max = ETP_YMAX_V1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (info->fw_version == 0x020800 ||
|
||||
info->fw_version == 0x020b00 ||
|
||||
info->fw_version == 0x020030) {
|
||||
*x_min = ETP_XMIN_V2;
|
||||
*y_min = ETP_YMIN_V2;
|
||||
*x_max = ETP_XMAX_V2;
|
||||
*y_max = ETP_YMAX_V2;
|
||||
} else {
|
||||
int i;
|
||||
int fixed_dpi;
|
||||
|
||||
i = (info->fw_version > 0x020800 &&
|
||||
info->fw_version < 0x020900) ? 1 : 2;
|
||||
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -1;
|
||||
|
||||
fixed_dpi = param[1] & 0x10;
|
||||
|
||||
if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
|
||||
if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
|
||||
return -1;
|
||||
|
||||
*x_max = (info->capabilities[1] - i) * param[1] / 2;
|
||||
*y_max = (info->capabilities[2] - i) * param[2] / 2;
|
||||
} else if (info->fw_version == 0x040216) {
|
||||
*x_max = 819;
|
||||
*y_max = 405;
|
||||
} else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
|
||||
*x_max = 900;
|
||||
*y_max = 500;
|
||||
} else {
|
||||
*x_max = (info->capabilities[1] - i) * 64;
|
||||
*y_max = (info->capabilities[2] - i) * 64;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -1;
|
||||
|
||||
*x_max = (0x0f & param[0]) << 8 | param[1];
|
||||
*y_max = (0xf0 & param[0]) << 4 | param[2];
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -1;
|
||||
|
||||
*x_max = (0x0f & param[0]) << 8 | param[1];
|
||||
*y_max = (0xf0 & param[0]) << 4 | param[2];
|
||||
traces = info->capabilities[1];
|
||||
if ((traces < 2) || (traces > *x_max))
|
||||
return -1;
|
||||
|
||||
*width = *x_max / (traces - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* (value from firmware) * 10 + 790 = dpi
|
||||
* we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
|
||||
@ -1099,53 +1063,12 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
|
||||
* fw_version for this is based on the following fw_version & caps table:
|
||||
*
|
||||
* Laptop-model: fw_version: caps: buttons:
|
||||
* Acer S3 0x461f00 10, 13, 0e clickpad
|
||||
* Acer S7-392 0x581f01 50, 17, 0d clickpad
|
||||
* Acer V5-131 0x461f02 01, 16, 0c clickpad
|
||||
* Acer V5-551 0x461f00 ? clickpad
|
||||
* Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons
|
||||
* Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons
|
||||
* Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons
|
||||
* Asus TP500LN 0x381f17 10, 14, 0e clickpad
|
||||
* Asus X750JN 0x381f17 10, 14, 0e clickpad
|
||||
* Asus UX31 0x361f00 20, 15, 0e clickpad
|
||||
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
|
||||
* Avatar AVIU-145A2 0x361f00 ? clickpad
|
||||
* Fujitsu CELSIUS H760 0x570f02 40, 14, 0c 3 hw buttons (**)
|
||||
* Fujitsu CELSIUS H780 0x5d0f02 41, 16, 0d 3 hw buttons (**)
|
||||
* Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons
|
||||
* Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons
|
||||
* Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons
|
||||
* Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons
|
||||
* Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons
|
||||
* Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons
|
||||
* Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**)
|
||||
* Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons
|
||||
* Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*)
|
||||
* Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*)
|
||||
* Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons
|
||||
* Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad
|
||||
* Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad
|
||||
* Samsung NP900X3E-A02 0x575f03 ? clickpad
|
||||
* Samsung NP-QX410 0x851b00 19, 14, 0c clickpad
|
||||
* Samsung RC512 0x450f00 08, 15, 0c 2 hw buttons
|
||||
* Samsung RF710 0x450f00 ? 2 hw buttons
|
||||
* System76 Pangolin 0x250f01 ? 2 hw buttons
|
||||
* (*) + 3 trackpoint buttons
|
||||
* (**) + 0 trackpoint buttons
|
||||
* Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
|
||||
*/
|
||||
static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
|
||||
if (etd->info.fw_version & 0x001000) {
|
||||
if (elantech_is_buttonpad(&etd->info)) {
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
|
||||
__clear_bit(BTN_RIGHT, dev->keybit);
|
||||
}
|
||||
@ -1181,16 +1104,6 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const char * const middle_button_pnp_ids[] = {
|
||||
"LEN2131", /* ThinkPad P52 w/ NFC */
|
||||
"LEN2132", /* ThinkPad P52 */
|
||||
"LEN2133", /* ThinkPad P72 w/ NFC */
|
||||
"LEN2134", /* ThinkPad P72 */
|
||||
"LEN0407",
|
||||
"LEN0408",
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* Set the appropriate event bits for the input subsystem
|
||||
*/
|
||||
@ -1199,10 +1112,9 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
struct elantech_device_info *info = &etd->info;
|
||||
unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
|
||||
|
||||
if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
|
||||
return -1;
|
||||
unsigned int x_min = info->x_min, y_min = info->y_min,
|
||||
x_max = info->x_max, y_max = info->y_max,
|
||||
width = info->width;
|
||||
|
||||
__set_bit(INPUT_PROP_POINTER, dev->propbit);
|
||||
__set_bit(EV_KEY, dev->evbit);
|
||||
@ -1210,8 +1122,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
||||
__clear_bit(EV_REL, dev->evbit);
|
||||
|
||||
__set_bit(BTN_LEFT, dev->keybit);
|
||||
if (dmi_check_system(elantech_dmi_has_middle_button) ||
|
||||
psmouse_matches_pnp_id(psmouse, middle_button_pnp_ids))
|
||||
if (info->has_middle_button)
|
||||
__set_bit(BTN_MIDDLE, dev->keybit);
|
||||
__set_bit(BTN_RIGHT, dev->keybit);
|
||||
|
||||
@ -1686,6 +1597,7 @@ static int elantech_query_info(struct psmouse *psmouse,
|
||||
struct elantech_device_info *info)
|
||||
{
|
||||
unsigned char param[3];
|
||||
unsigned char traces;
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
@ -1754,6 +1666,90 @@ static int elantech_query_info(struct psmouse *psmouse,
|
||||
}
|
||||
}
|
||||
|
||||
/* query range information */
|
||||
switch (info->hw_version) {
|
||||
case 1:
|
||||
info->x_min = ETP_XMIN_V1;
|
||||
info->y_min = ETP_YMIN_V1;
|
||||
info->x_max = ETP_XMAX_V1;
|
||||
info->y_max = ETP_YMAX_V1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (info->fw_version == 0x020800 ||
|
||||
info->fw_version == 0x020b00 ||
|
||||
info->fw_version == 0x020030) {
|
||||
info->x_min = ETP_XMIN_V2;
|
||||
info->y_min = ETP_YMIN_V2;
|
||||
info->x_max = ETP_XMAX_V2;
|
||||
info->y_max = ETP_YMAX_V2;
|
||||
} else {
|
||||
int i;
|
||||
int fixed_dpi;
|
||||
|
||||
i = (info->fw_version > 0x020800 &&
|
||||
info->fw_version < 0x020900) ? 1 : 2;
|
||||
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -EINVAL;
|
||||
|
||||
fixed_dpi = param[1] & 0x10;
|
||||
|
||||
if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
|
||||
if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
|
||||
return -EINVAL;
|
||||
|
||||
info->x_max = (info->capabilities[1] - i) * param[1] / 2;
|
||||
info->y_max = (info->capabilities[2] - i) * param[2] / 2;
|
||||
} else if (info->fw_version == 0x040216) {
|
||||
info->x_max = 819;
|
||||
info->y_max = 405;
|
||||
} else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
|
||||
info->x_max = 900;
|
||||
info->y_max = 500;
|
||||
} else {
|
||||
info->x_max = (info->capabilities[1] - i) * 64;
|
||||
info->y_max = (info->capabilities[2] - i) * 64;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -EINVAL;
|
||||
|
||||
info->x_max = (0x0f & param[0]) << 8 | param[1];
|
||||
info->y_max = (0xf0 & param[0]) << 4 | param[2];
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
|
||||
return -EINVAL;
|
||||
|
||||
info->x_max = (0x0f & param[0]) << 8 | param[1];
|
||||
info->y_max = (0xf0 & param[0]) << 4 | param[2];
|
||||
traces = info->capabilities[1];
|
||||
if ((traces < 2) || (traces > info->x_max))
|
||||
return -EINVAL;
|
||||
|
||||
info->width = info->x_max / (traces - 1);
|
||||
|
||||
/* column number of traces */
|
||||
info->x_traces = traces;
|
||||
|
||||
/* row number of traces */
|
||||
traces = info->capabilities[2];
|
||||
if ((traces >= 2) && (traces <= info->y_max))
|
||||
info->y_traces = traces;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* check for the middle button: DMI matching or new v4 firmwares */
|
||||
info->has_middle_button = dmi_check_system(elantech_dmi_has_middle_button) ||
|
||||
(ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) &&
|
||||
!elantech_is_buttonpad(info));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1780,10 +1776,6 @@ static const char * const i2c_blacklist_pnp_ids[] = {
|
||||
* These are known to not be working properly as bits are missing
|
||||
* in elan_i2c.
|
||||
*/
|
||||
"LEN2131", /* ThinkPad P52 w/ NFC */
|
||||
"LEN2132", /* ThinkPad P52 */
|
||||
"LEN2133", /* ThinkPad P72 w/ NFC */
|
||||
"LEN2134", /* ThinkPad P72 */
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -1791,17 +1783,45 @@ static int elantech_create_smbus(struct psmouse *psmouse,
|
||||
struct elantech_device_info *info,
|
||||
bool leave_breadcrumbs)
|
||||
{
|
||||
const struct property_entry i2c_properties[] = {
|
||||
PROPERTY_ENTRY_BOOL("elan,trackpoint"),
|
||||
{ },
|
||||
};
|
||||
struct property_entry i2c_props[11] = {};
|
||||
struct i2c_board_info smbus_board = {
|
||||
I2C_BOARD_INFO("elan_i2c", 0x15),
|
||||
.flags = I2C_CLIENT_HOST_NOTIFY,
|
||||
};
|
||||
unsigned int idx = 0;
|
||||
|
||||
smbus_board.properties = i2c_props;
|
||||
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x",
|
||||
info->x_max + 1);
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y",
|
||||
info->y_max + 1);
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-x",
|
||||
info->x_min);
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-y",
|
||||
info->y_min);
|
||||
if (info->x_res)
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-x-mm",
|
||||
(info->x_max + 1) / info->x_res);
|
||||
if (info->y_res)
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-y-mm",
|
||||
(info->y_max + 1) / info->y_res);
|
||||
|
||||
if (info->has_trackpoint)
|
||||
smbus_board.properties = i2c_properties;
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,trackpoint");
|
||||
|
||||
if (info->has_middle_button)
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,middle-button");
|
||||
|
||||
if (info->x_traces)
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,x_traces",
|
||||
info->x_traces);
|
||||
if (info->y_traces)
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,y_traces",
|
||||
info->y_traces);
|
||||
|
||||
if (elantech_is_buttonpad(info))
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad");
|
||||
|
||||
return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
|
||||
leave_breadcrumbs);
|
||||
|
@ -141,8 +141,15 @@ struct elantech_device_info {
|
||||
unsigned char debug;
|
||||
unsigned char hw_version;
|
||||
unsigned int fw_version;
|
||||
unsigned int x_min;
|
||||
unsigned int y_min;
|
||||
unsigned int x_max;
|
||||
unsigned int y_max;
|
||||
unsigned int x_res;
|
||||
unsigned int y_res;
|
||||
unsigned int x_traces;
|
||||
unsigned int y_traces;
|
||||
unsigned int width;
|
||||
unsigned int bus;
|
||||
bool paritycheck;
|
||||
bool jumpy_cursor;
|
||||
@ -150,6 +157,7 @@ struct elantech_device_info {
|
||||
bool crc_enabled;
|
||||
bool set_hw_resolution;
|
||||
bool has_trackpoint;
|
||||
bool has_middle_button;
|
||||
int (*send_cmd)(struct psmouse *psmouse, unsigned char c,
|
||||
unsigned char *param);
|
||||
};
|
||||
|
@ -173,6 +173,7 @@ static const char * const smbus_pnp_ids[] = {
|
||||
"LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
|
||||
"LEN0073", /* X1 Carbon G5 (Elantech) */
|
||||
"LEN0092", /* X1 Carbon 6 */
|
||||
"LEN0093", /* T480 */
|
||||
"LEN0096", /* X280 */
|
||||
"LEN0097", /* X280 -> ALPS trackpoint */
|
||||
"LEN200f", /* T450s */
|
||||
|
@ -70,7 +70,6 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
|
||||
int pitch_y = 0;
|
||||
int rx_receivers = 0;
|
||||
int tx_receivers = 0;
|
||||
int sensor_flags = 0;
|
||||
|
||||
item = rmi_get_register_desc_item(&f12->control_reg_desc, 8);
|
||||
if (!item) {
|
||||
@ -126,10 +125,9 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
if (rmi_register_desc_has_subpacket(item, 4)) {
|
||||
sensor_flags = buf[offset];
|
||||
/* Skip over sensor flags */
|
||||
if (rmi_register_desc_has_subpacket(item, 4))
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
sensor->x_mm = (pitch_x * rx_receivers) >> 12;
|
||||
sensor->y_mm = (pitch_y * tx_receivers) >> 12;
|
||||
|
@ -256,16 +256,6 @@ enum v4l_dbg_inputs {
|
||||
MXT_V4L_INPUT_MAX,
|
||||
};
|
||||
|
||||
static const struct v4l2_file_operations mxt_video_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = v4l2_fh_open,
|
||||
.release = vb2_fop_release,
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
.read = vb2_fop_read,
|
||||
.mmap = vb2_fop_mmap,
|
||||
.poll = vb2_fop_poll,
|
||||
};
|
||||
|
||||
enum mxt_suspend_mode {
|
||||
MXT_SUSPEND_DEEP_SLEEP = 0,
|
||||
MXT_SUSPEND_T9_CTRL = 1,
|
||||
@ -1521,7 +1511,8 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw)
|
||||
} else if (config_crc == data->config_crc) {
|
||||
dev_dbg(dev, "Config CRC 0x%06X: OK\n",
|
||||
data->config_crc);
|
||||
return 0;
|
||||
ret = 0;
|
||||
goto release_raw;
|
||||
} else {
|
||||
dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
|
||||
data->config_crc, config_crc);
|
||||
@ -2218,6 +2209,16 @@ recheck:
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
|
||||
static const struct v4l2_file_operations mxt_video_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = v4l2_fh_open,
|
||||
.release = vb2_fop_release,
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
.read = vb2_fop_read,
|
||||
.mmap = vb2_fop_mmap,
|
||||
.poll = vb2_fop_poll,
|
||||
};
|
||||
|
||||
static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
|
||||
unsigned int y)
|
||||
{
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define WORK_REGISTER_THRESHOLD 0x00
|
||||
#define WORK_REGISTER_REPORT_RATE 0x08
|
||||
@ -228,7 +229,6 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
||||
|
||||
for (i = 0; i < tsdata->max_support_points; i++) {
|
||||
u8 *buf = &rdbuf[i * tplen + offset];
|
||||
bool down;
|
||||
|
||||
type = buf[0] >> 6;
|
||||
/* ignore Reserved events */
|
||||
@ -239,23 +239,19 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
||||
if (tsdata->version == EDT_M06 && type == TOUCH_EVENT_DOWN)
|
||||
continue;
|
||||
|
||||
x = ((buf[0] << 8) | buf[1]) & 0x0fff;
|
||||
y = ((buf[2] << 8) | buf[3]) & 0x0fff;
|
||||
x = get_unaligned_be16(buf) & 0x0fff;
|
||||
y = get_unaligned_be16(buf + 2) & 0x0fff;
|
||||
/* The FT5x26 send the y coordinate first */
|
||||
if (tsdata->version == EV_FT)
|
||||
swap(x, y);
|
||||
|
||||
id = (buf[2] >> 4) & 0x0f;
|
||||
down = type != TOUCH_EVENT_UP;
|
||||
|
||||
input_mt_slot(tsdata->input, id);
|
||||
input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down);
|
||||
|
||||
if (!down)
|
||||
continue;
|
||||
|
||||
touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y,
|
||||
true);
|
||||
if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER,
|
||||
type != TOUCH_EVENT_UP))
|
||||
touchscreen_report_pos(tsdata->input, &tsdata->prop,
|
||||
x, y, true);
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(tsdata->input, true);
|
||||
|
@ -28,6 +28,7 @@ struct eeti_ts {
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *attn_gpio;
|
||||
struct touchscreen_properties props;
|
||||
struct mutex mutex;
|
||||
bool running;
|
||||
};
|
||||
|
||||
@ -62,42 +63,80 @@ static void eeti_ts_report_event(struct eeti_ts *eeti, u8 *buf)
|
||||
input_sync(eeti->input);
|
||||
}
|
||||
|
||||
static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
|
||||
static int eeti_ts_read(struct eeti_ts *eeti)
|
||||
{
|
||||
struct eeti_ts *eeti = dev_id;
|
||||
int len;
|
||||
int error;
|
||||
int len, error;
|
||||
char buf[6];
|
||||
|
||||
do {
|
||||
len = i2c_master_recv(eeti->client, buf, sizeof(buf));
|
||||
if (len != sizeof(buf)) {
|
||||
error = len < 0 ? len : -EIO;
|
||||
dev_err(&eeti->client->dev,
|
||||
"failed to read touchscreen data: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Motion packet */
|
||||
if (buf[0] & 0x80)
|
||||
eeti_ts_report_event(eeti, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct eeti_ts *eeti = dev_id;
|
||||
int error;
|
||||
|
||||
mutex_lock(&eeti->mutex);
|
||||
|
||||
do {
|
||||
/*
|
||||
* If we have attention GPIO, trust it. Otherwise we'll read
|
||||
* once and exit. We assume that in this case we are using
|
||||
* level triggered interrupt and it will get raised again
|
||||
* if/when there is more data.
|
||||
*/
|
||||
if (eeti->attn_gpio &&
|
||||
!gpiod_get_value_cansleep(eeti->attn_gpio)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf[0] & 0x80) {
|
||||
/* Motion packet */
|
||||
eeti_ts_report_event(eeti, buf);
|
||||
}
|
||||
} while (eeti->running &&
|
||||
eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio));
|
||||
error = eeti_ts_read(eeti);
|
||||
if (error)
|
||||
break;
|
||||
|
||||
} while (eeti->running && eeti->attn_gpio);
|
||||
|
||||
mutex_unlock(&eeti->mutex);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void eeti_ts_start(struct eeti_ts *eeti)
|
||||
{
|
||||
mutex_lock(&eeti->mutex);
|
||||
|
||||
eeti->running = true;
|
||||
wmb();
|
||||
enable_irq(eeti->client->irq);
|
||||
|
||||
/*
|
||||
* Kick the controller in case we are using edge interrupt and
|
||||
* we missed our edge while interrupt was disabled. We expect
|
||||
* the attention GPIO to be wired in this case.
|
||||
*/
|
||||
if (eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio))
|
||||
eeti_ts_read(eeti);
|
||||
|
||||
mutex_unlock(&eeti->mutex);
|
||||
}
|
||||
|
||||
static void eeti_ts_stop(struct eeti_ts *eeti)
|
||||
{
|
||||
/*
|
||||
* Not locking here, just setting a flag and expect that the
|
||||
* interrupt thread will notice the flag eventually.
|
||||
*/
|
||||
eeti->running = false;
|
||||
wmb();
|
||||
disable_irq(eeti->client->irq);
|
||||
@ -140,6 +179,8 @@ static int eeti_ts_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&eeti->mutex);
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input) {
|
||||
dev_err(dev, "Failed to allocate input device.\n");
|
||||
|
@ -364,8 +364,6 @@ static int imx6ul_tsc_probe(struct platform_device *pdev)
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct imx6ul_tsc *tsc;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *tsc_mem;
|
||||
struct resource *adc_mem;
|
||||
int err;
|
||||
int tsc_irq;
|
||||
int adc_irq;
|
||||
@ -403,16 +401,14 @@ static int imx6ul_tsc_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
tsc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
tsc->tsc_regs = devm_ioremap_resource(&pdev->dev, tsc_mem);
|
||||
tsc->tsc_regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(tsc->tsc_regs)) {
|
||||
err = PTR_ERR(tsc->tsc_regs);
|
||||
dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
adc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
tsc->adc_regs = devm_ioremap_resource(&pdev->dev, adc_mem);
|
||||
tsc->adc_regs = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(tsc->adc_regs)) {
|
||||
err = PTR_ERR(tsc->adc_regs);
|
||||
dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err);
|
||||
|
@ -1056,8 +1056,6 @@ static int iqs5xx_probe(struct i2c_client *client,
|
||||
if (!iqs5xx)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&client->dev, iqs5xx);
|
||||
|
||||
i2c_set_clientdata(client, iqs5xx);
|
||||
iqs5xx->client = client;
|
||||
|
||||
|
76
include/linux/input/elan-i2c-ids.h
Normal file
76
include/linux/input/elan-i2c-ids.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Elan I2C/SMBus Touchpad device whitelist
|
||||
*
|
||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||
*
|
||||
* Author: æ維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||
* Author: KT Liao <kt.liao@emc.com.tw>
|
||||
* Version: 1.6.3
|
||||
*
|
||||
* Based on cyapa driver:
|
||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||
* copyright (c) 2011-2012 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Trademarks are the property of their respective owners.
|
||||
*/
|
||||
|
||||
#ifndef __ELAN_I2C_IDS_H
|
||||
#define __ELAN_I2C_IDS_H
|
||||
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
static const struct acpi_device_id elan_acpi_id[] = {
|
||||
{ "ELAN0000", 0 },
|
||||
{ "ELAN0100", 0 },
|
||||
{ "ELAN0600", 0 },
|
||||
{ "ELAN0601", 0 },
|
||||
{ "ELAN0602", 0 },
|
||||
{ "ELAN0603", 0 },
|
||||
{ "ELAN0604", 0 },
|
||||
{ "ELAN0605", 0 },
|
||||
{ "ELAN0606", 0 },
|
||||
{ "ELAN0607", 0 },
|
||||
{ "ELAN0608", 0 },
|
||||
{ "ELAN0609", 0 },
|
||||
{ "ELAN060B", 0 },
|
||||
{ "ELAN060C", 0 },
|
||||
{ "ELAN060F", 0 },
|
||||
{ "ELAN0610", 0 },
|
||||
{ "ELAN0611", 0 },
|
||||
{ "ELAN0612", 0 },
|
||||
{ "ELAN0615", 0 },
|
||||
{ "ELAN0616", 0 },
|
||||
{ "ELAN0617", 0 },
|
||||
{ "ELAN0618", 0 },
|
||||
{ "ELAN0619", 0 },
|
||||
{ "ELAN061A", 0 },
|
||||
{ "ELAN061B", 0 },
|
||||
{ "ELAN061C", 0 },
|
||||
{ "ELAN061D", 0 },
|
||||
{ "ELAN061E", 0 },
|
||||
{ "ELAN061F", 0 },
|
||||
{ "ELAN0620", 0 },
|
||||
{ "ELAN0621", 0 },
|
||||
{ "ELAN0622", 0 },
|
||||
{ "ELAN0623", 0 },
|
||||
{ "ELAN0624", 0 },
|
||||
{ "ELAN0625", 0 },
|
||||
{ "ELAN0626", 0 },
|
||||
{ "ELAN0627", 0 },
|
||||
{ "ELAN0628", 0 },
|
||||
{ "ELAN0629", 0 },
|
||||
{ "ELAN062A", 0 },
|
||||
{ "ELAN062B", 0 },
|
||||
{ "ELAN062C", 0 },
|
||||
{ "ELAN062D", 0 },
|
||||
{ "ELAN0631", 0 },
|
||||
{ "ELAN0632", 0 },
|
||||
{ "ELAN1000", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* __ELAN_I2C_IDS_H */
|
Loading…
Reference in New Issue
Block a user