Commit 073c916b authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents a2d79c71 59747372
...@@ -13,9 +13,20 @@ Optional properties: ...@@ -13,9 +13,20 @@ Optional properties:
pinctrl binding [1]). pinctrl binding [1]).
- vcc-supply: a phandle for the regulator supplying 3.3V power. - vcc-supply: a phandle for the regulator supplying 3.3V power.
- elan,trackpoint: touchpad can support a trackpoint (boolean) - 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 [0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt [1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
[2]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
Example: Example:
&i2c1 { &i2c1 {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/input/elan-i2c-ids.h>
#include "hid-ids.h" #include "hid-ids.h"
...@@ -916,6 +917,8 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { ...@@ -916,6 +917,8 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
bool hid_ignore(struct hid_device *hdev) bool hid_ignore(struct hid_device *hdev)
{ {
int i;
if (hdev->quirks & HID_QUIRK_NO_IGNORE) if (hdev->quirks & HID_QUIRK_NO_IGNORE)
return false; return false;
if (hdev->quirks & HID_QUIRK_IGNORE) if (hdev->quirks & HID_QUIRK_IGNORE)
...@@ -980,18 +983,15 @@ bool hid_ignore(struct hid_device *hdev) ...@@ -980,18 +983,15 @@ bool hid_ignore(struct hid_device *hdev)
break; break;
case USB_VENDOR_ID_ELAN: case USB_VENDOR_ID_ELAN:
/* /*
* Many Elan devices have a product id of 0x0401 and are handled * Blacklist of everything that gets handled by the elan_i2c
* by the elan_i2c input driver. But the ACPI HID ELAN0800 dev * input driver. This avoids disabling valid touchpads and
* is not (and cannot be) handled by that driver -> * other ELAN devices.
* Ignore all 0x0401 devs except for the ELAN0800 dev.
*/ */
if (hdev->product == 0x0401 && if ((hdev->product == 0x0401 || hdev->product == 0x0400))
strncmp(hdev->name, "ELAN0800", 8) != 0) for (i = 0; strlen(elan_acpi_id[i].id); ++i)
return true; if (!strncmp(hdev->name, elan_acpi_id[i].id,
/* Same with product id 0x0400 */ strlen(elan_acpi_id[i].id)))
if (hdev->product == 0x0400 && return true;
strncmp(hdev->name, "QTEC0001", 8) != 0)
return true;
break; break;
} }
......
...@@ -808,6 +808,7 @@ static bool joydev_dev_is_blacklisted(struct input_dev *dev) ...@@ -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) static bool joydev_dev_is_absolute_mouse(struct input_dev *dev)
{ {
DECLARE_BITMAP(jd_scratch, KEY_CNT); DECLARE_BITMAP(jd_scratch, KEY_CNT);
bool ev_match = false;
BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT); 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) ...@@ -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 * considered to be an absolute mouse if the following is
* true: * 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. * 2) Absolute events are exactly ABS_X and ABS_Y.
* 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE. * 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE.
* 4) Device is not on "Amiga" bus. * 4) Device is not on "Amiga" bus.
*/ */
bitmap_zero(jd_scratch, EV_CNT); bitmap_zero(jd_scratch, EV_CNT);
/* VMware VMMouse, HP ILO2 */
__set_bit(EV_ABS, jd_scratch); __set_bit(EV_ABS, jd_scratch);
__set_bit(EV_KEY, jd_scratch); __set_bit(EV_KEY, jd_scratch);
__set_bit(EV_SYN, 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; return false;
bitmap_zero(jd_scratch, ABS_CNT); bitmap_zero(jd_scratch, ABS_CNT);
......
...@@ -14,15 +14,15 @@ config JOYSTICK_IFORCE ...@@ -14,15 +14,15 @@ config JOYSTICK_IFORCE
module will be called iforce. module will be called iforce.
config JOYSTICK_IFORCE_USB config JOYSTICK_IFORCE_USB
bool "I-Force USB joysticks and wheels" tristate "I-Force USB joysticks and wheels"
depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB depends on JOYSTICK_IFORCE && USB
help help
Say Y here if you have an I-Force joystick or steering wheel Say Y here if you have an I-Force joystick or steering wheel
connected to your USB port. connected to your USB port.
config JOYSTICK_IFORCE_232 config JOYSTICK_IFORCE_232
bool "I-Force Serial joysticks and wheels" tristate "I-Force Serial joysticks and wheels"
depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO depends on JOYSTICK_IFORCE && SERIO
help help
Say Y here if you have an I-Force joystick or steering wheel Say Y here if you have an I-Force joystick or steering wheel
connected to your serial (COM) port. connected to your serial (COM) port.
......
...@@ -5,8 +5,7 @@ ...@@ -5,8 +5,7 @@
# By Johann Deneux <johann.deneux@gmail.com> # By Johann Deneux <johann.deneux@gmail.com>
# #
obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
iforce-y := iforce-ff.o iforce-main.o iforce-packets.o iforce-y := iforce-ff.o iforce-main.o iforce-packets.o
iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o obj-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o
iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o obj-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o
...@@ -372,12 +372,12 @@ int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, stru ...@@ -372,12 +372,12 @@ int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, stru
} }
switch (effect->u.periodic.waveform) { switch (effect->u.periodic.waveform) {
case FF_SQUARE: wave_code = 0x20; break; case FF_SQUARE: wave_code = 0x20; break;
case FF_TRIANGLE: wave_code = 0x21; break; case FF_TRIANGLE: wave_code = 0x21; break;
case FF_SINE: wave_code = 0x22; break; case FF_SINE: wave_code = 0x22; break;
case FF_SAW_UP: wave_code = 0x23; break; case FF_SAW_UP: wave_code = 0x23; break;
case FF_SAW_DOWN: wave_code = 0x24; break; case FF_SAW_DOWN: wave_code = 0x24; break;
default: wave_code = 0x20; break; default: wave_code = 0x20; break;
} }
if (!old || need_core(old, effect)) { if (!old || need_core(old, effect)) {
...@@ -476,9 +476,9 @@ int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, str ...@@ -476,9 +476,9 @@ int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, str
int core_err = 0; int core_err = 0;
switch (effect->type) { switch (effect->type) {
case FF_SPRING: type = 0x40; break; case FF_SPRING: type = 0x40; break;
case FF_DAMPER: type = 0x41; break; case FF_DAMPER: type = 0x41; break;
default: return -1; default: return -1;
} }
if (!old || need_condition_modifier(iforce, old, effect)) { if (!old || need_condition_modifier(iforce, old, effect)) {
......
...@@ -9,10 +9,11 @@ ...@@ -9,10 +9,11 @@
/* /*
*/ */
#include <asm/unaligned.h>
#include "iforce.h" #include "iforce.h"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>"); 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"); MODULE_LICENSE("GPL");
static signed short btn_joystick[] = static signed short btn_joystick[] =
...@@ -55,6 +56,7 @@ static struct iforce_device iforce_device[] = { ...@@ -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 }, //? { 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, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //?
{ 0x061c, 0xc084, "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 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, 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 }, //? { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
...@@ -120,22 +122,21 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, ...@@ -120,22 +122,21 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect,
* Upload the effect * Upload the effect
*/ */
switch (effect->type) { switch (effect->type) {
case FF_PERIODIC:
ret = iforce_upload_periodic(iforce, effect, old);
break;
case FF_PERIODIC: case FF_CONSTANT:
ret = iforce_upload_periodic(iforce, effect, old); ret = iforce_upload_constant(iforce, effect, old);
break; break;
case FF_CONSTANT:
ret = iforce_upload_constant(iforce, effect, old);
break;
case FF_SPRING: case FF_SPRING:
case FF_DAMPER: case FF_DAMPER:
ret = iforce_upload_condition(iforce, effect, old); ret = iforce_upload_condition(iforce, effect, old);
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
if (ret == 0) { if (ret == 0) {
...@@ -173,15 +174,7 @@ static int iforce_open(struct input_dev *dev) ...@@ -173,15 +174,7 @@ static int iforce_open(struct input_dev *dev)
{ {
struct iforce *iforce = input_get_drvdata(dev); struct iforce *iforce = input_get_drvdata(dev);
switch (iforce->bus) { iforce->xport_ops->start_io(iforce);
#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
}
if (test_bit(EV_FF, dev->evbit)) { if (test_bit(EV_FF, dev->evbit)) {
/* Enable force feedback */ /* Enable force feedback */
...@@ -214,27 +207,17 @@ static void iforce_close(struct input_dev *dev) ...@@ -214,27 +207,17 @@ static void iforce_close(struct input_dev *dev)
!test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)); !test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags));
} }
switch (iforce->bus) { iforce->xport_ops->stop_io(iforce);
#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
}
} }
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 input_dev *input_dev;
struct ff_device *ff; struct ff_device *ff;
unsigned char c[] = "CEOV"; u8 c[] = "CEOV";
u8 buf[IFORCE_MAX_LENGTH];
size_t len;
int i, error; int i, error;
int ff_effects = 0; int ff_effects = 0;
...@@ -252,20 +235,8 @@ int iforce_init_device(struct iforce *iforce) ...@@ -252,20 +235,8 @@ int iforce_init_device(struct iforce *iforce)
* Input device fields. * Input device fields.
*/ */
switch (iforce->bus) { input_dev->id.bustype = bustype;
#ifdef CONFIG_JOYSTICK_IFORCE_USB input_dev->dev.parent = parent;
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_set_drvdata(input_dev, iforce); input_set_drvdata(input_dev, iforce);
...@@ -290,7 +261,7 @@ int iforce_init_device(struct iforce *iforce) ...@@ -290,7 +261,7 @@ int iforce_init_device(struct iforce *iforce)
*/ */
for (i = 0; i < 20; i++) for (i = 0; i < 20; i++)
if (!iforce_get_id_packet(iforce, "O")) if (!iforce_get_id_packet(iforce, 'O', buf, &len))
break; break;
if (i == 20) { /* 5 seconds */ if (i == 20) { /* 5 seconds */
...@@ -304,23 +275,23 @@ int iforce_init_device(struct iforce *iforce) ...@@ -304,23 +275,23 @@ int iforce_init_device(struct iforce *iforce)
* Get device info. * Get device info.
*/ */
if (!iforce_get_id_packet(iforce, "M")) if (!iforce_get_id_packet(iforce, 'M', buf, &len) || len < 3)
input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1]; input_dev->id.vendor = get_unaligned_le16(buf + 1);
else else
dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n"); dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n");
if (!iforce_get_id_packet(iforce, "P")) if (!iforce_get_id_packet(iforce, 'P', buf, &len) || len < 3)
input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1]; input_dev->id.product = get_unaligned_le16(buf + 1);
else else
dev_warn(&iforce->dev->dev, "Device does not respond to id packet P\n"); dev_warn(&iforce->dev->dev, "Device does not respond to id packet P\n");
if (!iforce_get_id_packet(iforce, "B")) if (!iforce_get_id_packet(iforce, 'B', buf, &len) || len < 3)
iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; iforce->device_memory.end = get_unaligned_le16(buf + 1);
else else
dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n"); dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n");
if (!iforce_get_id_packet(iforce, "N")) if (!iforce_get_id_packet(iforce, 'N', buf, &len) || len < 2)
ff_effects = iforce->edata[1]; ff_effects = buf[1];
else else
dev_warn(&iforce->dev->dev, "Device does not respond to id packet N\n"); 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) ...@@ -336,8 +307,9 @@ int iforce_init_device(struct iforce *iforce)
*/ */
for (i = 0; c[i]; i++) for (i = 0; c[i]; i++)
if (!iforce_get_id_packet(iforce, c + i)) if (!iforce_get_id_packet(iforce, c[i], buf, &len))
iforce_dump_packet(iforce, "info", iforce->ecmd, iforce->edata); iforce_dump_packet(iforce, "info",
(FF_CMD_QUERY & 0xff00) | len, buf);
/* /*
* Disable spring, enable force feedback. * Disable spring, enable force feedback.
...@@ -371,34 +343,29 @@ int iforce_init_device(struct iforce *iforce) ...@@ -371,34 +343,29 @@ int iforce_init_device(struct iforce *iforce)
signed short t = iforce->type->abs[i]; signed short t = iforce->type->abs[i];
switch (t) { 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;
case ABS_X: case ABS_THROTTLE:
case ABS_Y: case ABS_GAS:
case ABS_WHEEL: case ABS_BRAKE:
input_set_abs_params(input_dev, t, 0, 255, 0, 0);
input_set_abs_params(input_dev, t, -1920, 1920, 16, 128); break;
set_bit(t, input_dev->ffbit);
break;
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;
case ABS_HAT0X: case ABS_RUDDER:
case ABS_HAT0Y: input_set_abs_params(input_dev, t, -128, 127, 0, 0);
case ABS_HAT1X: break;
case ABS_HAT1Y:
input_set_abs_params(input_dev, t, -1, 1, 0, 0); case ABS_HAT0X:
break; 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) ...@@ -431,35 +398,4 @@ int iforce_init_device(struct iforce *iforce)
fail: input_free_device(input_dev); fail: input_free_device(input_dev);
return error; return error;
} }
EXPORT_SYMBOL(iforce_init_device);
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);
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
/* /*
*/ */
#include <asm/unaligned.h>
#include "iforce.h" #include "iforce.h"
static struct { static struct {
...@@ -79,27 +80,12 @@ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) ...@@ -79,27 +80,12 @@ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
/* /*
* If necessary, start the transmission * If necessary, start the transmission
*/ */
switch (iforce->bus) { if (empty)
iforce->xport_ops->xmit(iforce);
#ifdef CONFIG_JOYSTICK_IFORCE_232
case IFORCE_232:
if (empty)
iforce_serial_xmit(iforce);
break;
#endif
#ifdef CONFIG_JOYSTICK_IFORCE_USB
case IFORCE_USB:
if (iforce->usbdev && empty &&
!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
iforce_usb_xmit(iforce);
}
break;
#endif
}
return 0; return 0;
} }
EXPORT_SYMBOL(iforce_send_packet);
/* Start or stop an effect */ /* Start or stop an effect */
int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value) int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
...@@ -133,157 +119,96 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr) ...@@ -133,157 +119,96 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
return -1; 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; struct input_dev *dev = iforce->dev;
int i; int i;
static int being_used = 0;
if (being_used) input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
dev_warn(&iforce->dev->dev, input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
"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) { for (i = 0; iforce->type->btn[i] >= 0; i++)
being_used--; input_report_key(dev, iforce->type->btn[i],
return; data[(i >> 3) + 5] & (1 << (i & 7)));
}
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); /* If there are untouched bits left, interpret them as the second hat */
input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); if (i <= 8) {
u8 btns = data[6];
for (i = 0; iforce->type->btn[i] >= 0; i++)
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];
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 (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);
}
}
input_sync(dev); if (test_bit(ABS_HAT1X, dev->absbit)) {
if (btns & BIT(3))
break; input_report_abs(dev, ABS_HAT1X, -1);
else if (btns & BIT(1))
case 0x02: /* status report */ input_report_abs(dev, ABS_HAT1X, 1);
input_report_key(dev, BTN_DEAD, data[0] & 0x02); else
input_sync(dev); input_report_abs(dev, ABS_HAT1X, 0);
}
/* Check if an effect was just started or stopped */ if (test_bit(ABS_HAT1Y, dev->absbit)) {
i = data[1] & 0x7f; if (btns & BIT(0))
if (data[1] & 0x80) { input_report_abs(dev, ABS_HAT1Y, -1);
if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { else if (btns & BIT(2))
/* Report play event */ input_report_abs(dev, ABS_HAT1Y, 1);
input_report_ff_status(dev, i, FF_STATUS_PLAYING); else
} input_report_abs(dev, ABS_HAT1Y, 0);
} else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { }
/* 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));
}
break;
} }
being_used--;
} }
int iforce_get_id_packet(struct iforce *iforce, char *packet) void iforce_process_packet(struct iforce *iforce,
u8 packet_id, u8 *data, size_t len)
{ {
switch (iforce->bus) { struct input_dev *dev = iforce->dev;
int i, j;
case IFORCE_USB: { switch (packet_id) {
#ifdef CONFIG_JOYSTICK_IFORCE_USB
int status;
iforce->cr.bRequest = packet[0]; case 0x01: /* joystick position data */
iforce->ctrl->dev = iforce->usbdev; 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]);
status = usb_submit_urb(iforce->ctrl, GFP_KERNEL); if (len >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
if (status) { input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
dev_err(&iforce->intf->dev,
"usb_submit_urb failed %d\n", status);
return -1;
}
wait_event_interruptible_timeout(iforce->wait, iforce_report_hats_buttons(iforce, data);
iforce->ctrl->status != -EINPROGRESS, HZ);
if (iforce->ctrl->status) { input_sync(dev);
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; break;
case IFORCE_232: 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]);
#ifdef CONFIG_JOYSTICK_IFORCE_232 iforce_report_hats_buttons(iforce, data);
iforce->expect_packet = FF_CMD_QUERY;
iforce_send_packet(iforce, FF_CMD_QUERY, packet);
wait_event_interruptible_timeout(iforce->wait, input_sync(dev);
!iforce->expect_packet, HZ); break;
case 0x02: /* status report */
input_report_key(dev, BTN_DEAD, data[0] & 0x02);
input_sync(dev);
if (iforce->expect_packet) { /* Check if an effect was just started or stopped */
iforce->expect_packet = 0; i = data[1] & 0x7f;
return -1; if (data[1] & 0x80) {
if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
/* Report play event */
input_report_ff_status(dev, i, FF_STATUS_PLAYING);
}
} else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
/* Report stop event */
input_report_ff_status(dev, i, FF_STATUS_STOPPED);
} }
#else
dev_err(&iforce->dev->dev,
"iforce_get_id_packet: iforce->bus = SERIO!\n");
#endif
break;
default: for (j = 3; j < len; j += 2)
dev_err(&iforce->dev->dev, mark_core_as_ready(iforce, get_unaligned_le16(data + j));
"iforce_get_id_packet: iforce->bus = %d\n",
iforce->bus);
break; break;
} }
return -(iforce->edata[0] != packet[0]);
} }
EXPORT_SYMBOL(iforce_process_packet);
...@@ -9,10 +9,26 @@ ...@@ -9,10 +9,26 @@
/* /*
*/ */
#include <linux/serio.h>
#include "iforce.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; unsigned char cs;
int i; int i;
unsigned long flags; unsigned long flags;
...@@ -33,19 +49,20 @@ void iforce_serial_xmit(struct iforce *iforce) ...@@ -33,19 +49,20 @@ void iforce_serial_xmit(struct iforce *iforce)
cs = 0x2b; 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]; cs ^= iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1); XMIT_INC(iforce->xmit.tail, 1);
for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { 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]; cs ^= iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1); 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)) if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
goto again; goto again;
...@@ -55,54 +72,118 @@ void iforce_serial_xmit(struct iforce *iforce) ...@@ -55,54 +72,118 @@ void iforce_serial_xmit(struct iforce *iforce)
spin_unlock_irqrestore(&iforce->xmit_lock, flags); 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) static void iforce_serio_write_wakeup(struct serio *serio)
{ {
struct iforce *iforce = serio_get_drvdata(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, static irqreturn_t iforce_serio_irq(struct serio *serio,
unsigned char data, unsigned int flags) 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) if (data == 0x2b)
iforce->pkt = 1; iforce_serio->pkt = 1;
goto out; goto out;
} }
if (!iforce->id) { if (!iforce_serio->id) {
if (data > 3 && data != 0xff) if (data > 3 && data != 0xff)
iforce->pkt = 0; iforce_serio->pkt = 0;
else else
iforce->id = data; iforce_serio->id = data;
goto out; goto out;
} }
if (!iforce->len) { if (!iforce_serio->len) {
if (data > IFORCE_MAX_LENGTH) { if (data > IFORCE_MAX_LENGTH) {
iforce->pkt = 0; iforce_serio->pkt = 0;
iforce->id = 0; iforce_serio->id = 0;
} else { } else {
iforce->len = data; iforce_serio->len = data;
} }
goto out; goto out;
} }
if (iforce->idx < iforce->len) { if (iforce_serio->idx < iforce_serio->len) {
iforce->csum += iforce->data[iforce->idx++] = data; iforce_serio->data_in[iforce_serio->idx++] = data;
iforce_serio->csum += data;
goto out; goto out;
} }
if (iforce->idx == iforce->len) { if (iforce_serio->idx == iforce_serio->len) {
iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data); /* Handle command completion */
iforce->pkt = 0; if (iforce_serio->expect_packet == iforce_serio->id) {
iforce->id = 0; iforce_serio->expect_packet = 0;
iforce->len = 0; memcpy(iforce_serio->cmd_response,
iforce->idx = 0; iforce_serio->data_in, IFORCE_MAX_LENGTH);
iforce->csum = 0; 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: out:
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -110,23 +191,23 @@ static irqreturn_t iforce_serio_irq(struct serio *serio, ...@@ -110,23 +191,23 @@ static irqreturn_t iforce_serio_irq(struct serio *serio,
static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv) static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
{ {
struct iforce *iforce; struct iforce_serio *iforce_serio;
int err; int err;
iforce = kzalloc(sizeof(struct iforce), GFP_KERNEL); iforce_serio = kzalloc(sizeof(*iforce_serio), GFP_KERNEL);
if (!iforce) if (!iforce_serio)
return -ENOMEM; return -ENOMEM;
iforce->bus = IFORCE_232; iforce_serio->iforce.xport_ops = &iforce_serio_xport_ops;
iforce->serio = serio;
serio_set_drvdata(serio, iforce); iforce_serio->serio = serio;
serio_set_drvdata(serio, iforce_serio);
err = serio_open(serio, drv); err = serio_open(serio, drv);
if (err) if (err)
goto fail1; goto fail1;
err = iforce_init_device(iforce); err = iforce_init_device(&serio->dev, BUS_RS232, &iforce_serio->iforce);
if (err) if (err)
goto fail2; goto fail2;
...@@ -134,18 +215,18 @@ static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv) ...@@ -134,18 +215,18 @@ static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
fail2: serio_close(serio); fail2: serio_close(serio);
fail1: serio_set_drvdata(serio, NULL); fail1: serio_set_drvdata(serio, NULL);
kfree(iforce); kfree(iforce_serio);
return err; return err;
} }
static void iforce_serio_disconnect(struct serio *serio) 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_close(serio);
serio_set_drvdata(serio, NULL); serio_set_drvdata(serio, NULL);
kfree(iforce); kfree(iforce_serio);
} }
static const struct serio_device_id iforce_serio_ids[] = { static const struct serio_device_id iforce_serio_ids[] = {
...@@ -171,3 +252,9 @@ struct serio_driver iforce_serio_drv = { ...@@ -171,3 +252,9 @@ struct serio_driver iforce_serio_drv = {
.connect = iforce_serio_connect, .connect = iforce_serio_connect,
.disconnect = iforce_serio_disconnect, .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 @@ ...@@ -9,10 +9,24 @@
/* /*
*/ */
#include <linux/usb.h>
#include "iforce.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; int n, c;
unsigned long flags; unsigned long flags;
...@@ -24,31 +38,32 @@ void iforce_usb_xmit(struct iforce *iforce) ...@@ -24,31 +38,32 @@ void iforce_usb_xmit(struct iforce *iforce)
return; 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); XMIT_INC(iforce->xmit.tail, 1);
n = iforce->xmit.buf[iforce->xmit.tail]; n = iforce->xmit.buf[iforce->xmit.tail];
XMIT_INC(iforce->xmit.tail, 1); XMIT_INC(iforce->xmit.tail, 1);
iforce->out->transfer_buffer_length = n + 1; iforce_usb->out->transfer_buffer_length = n + 1;
iforce->out->dev = iforce->usbdev; iforce_usb->out->dev = iforce_usb->usbdev;
/* Copy rest of data then */ /* Copy rest of data then */
c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE); c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
if (n < c) c=n; if (n < c) c=n;
memcpy(iforce->out->transfer_buffer + 1, memcpy(iforce_usb->out->transfer_buffer + 1,
&iforce->xmit.buf[iforce->xmit.tail], &iforce->xmit.buf[iforce->xmit.tail],
c); c);
if (n != c) { if (n != c) {
memcpy(iforce->out->transfer_buffer + 1 + c, memcpy(iforce_usb->out->transfer_buffer + 1 + c,
&iforce->xmit.buf[0], &iforce->xmit.buf[0],
n-c); n-c);
} }
XMIT_INC(iforce->xmit.tail, n); 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); 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. /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
...@@ -57,10 +72,77 @@ void iforce_usb_xmit(struct iforce *iforce) ...@@ -57,10 +72,77 @@ void iforce_usb_xmit(struct iforce *iforce)
spin_unlock_irqrestore(&iforce->xmit_lock, flags); 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) static void iforce_usb_irq(struct urb *urb)
{ {
struct iforce *iforce = urb->context; struct iforce_usb *iforce_usb = urb->context;
struct device *dev = &iforce->intf->dev; struct iforce *iforce = &iforce_usb->iforce;
struct device *dev = &iforce_usb->intf->dev;
int status; int status;
switch (urb->status) { switch (urb->status) {
...@@ -80,11 +162,11 @@ static void iforce_usb_irq(struct urb *urb) ...@@ -80,11 +162,11 @@ static void iforce_usb_irq(struct urb *urb)
goto exit; goto exit;
} }
iforce_process_packet(iforce, iforce_process_packet(iforce, iforce_usb->data_in[0],
(iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1); iforce_usb->data_in + 1, urb->actual_length - 1);
exit: exit:
status = usb_submit_urb (urb, GFP_ATOMIC); status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) if (status)
dev_err(dev, "%s - usb_submit_urb failed with result %d\n", dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
__func__, status); __func__, status);
...@@ -92,35 +174,28 @@ static void iforce_usb_irq(struct urb *urb) ...@@ -92,35 +174,28 @@ static void iforce_usb_irq(struct urb *urb)
static void iforce_usb_out(struct urb *urb) 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) { if (urb->status) {
clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); 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); urb->status);
return; return;
} }
iforce_usb_xmit(iforce); __iforce_usb_xmit(iforce);
wake_up(&iforce->wait); 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, static int iforce_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_device *dev = interface_to_usbdev(intf); struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface; struct usb_host_interface *interface;
struct usb_endpoint_descriptor *epirq, *epout; struct usb_endpoint_descriptor *epirq, *epout;
struct iforce *iforce; struct iforce_usb *iforce_usb;
int err = -ENOMEM; int err = -ENOMEM;
interface = intf->cur_altsetting; interface = intf->cur_altsetting;
...@@ -131,48 +206,45 @@ static int iforce_usb_probe(struct usb_interface *intf, ...@@ -131,48 +206,45 @@ static int iforce_usb_probe(struct usb_interface *intf,
epirq = &interface->endpoint[0].desc; epirq = &interface->endpoint[0].desc;
epout = &interface->endpoint[1].desc; epout = &interface->endpoint[1].desc;
if (!(iforce = kzalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) iforce_usb = kzalloc(sizeof(*iforce_usb), GFP_KERNEL);
goto fail; if (!iforce_usb)
if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL)))
goto fail; goto fail;
if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL))) iforce_usb->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!iforce_usb->irq)
goto fail; goto fail;
if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL))) iforce_usb->out = usb_alloc_urb(0, GFP_KERNEL);
if (!iforce_usb->out)
goto fail; goto fail;
iforce->bus = IFORCE_USB; iforce_usb->iforce.xport_ops = &iforce_usb_xport_ops;
iforce->usbdev = dev;
iforce->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->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress), iforce_usb->usbdev = dev;
iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval); iforce_usb->intf = intf;
usb_fill_int_urb(iforce->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress), usb_fill_int_urb(iforce_usb->irq, dev,
iforce + 1, 32, iforce_usb_out, iforce, epout->bInterval); usb_rcvintpipe(dev, epirq->bEndpointAddress),
iforce_usb->data_in, sizeof(iforce_usb->data_in),
iforce_usb_irq, iforce_usb, epirq->bInterval);
usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0), usb_fill_int_urb(iforce_usb->out, dev,
(void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce); usb_sndintpipe(dev, epout->bEndpointAddress),
iforce_usb->data_out, sizeof(iforce_usb->data_out),
iforce_usb_out, iforce_usb, epout->bInterval);
err = iforce_init_device(iforce); err = iforce_init_device(&intf->dev, BUS_USB, &iforce_usb->iforce);
if (err) if (err)
goto fail; goto fail;
usb_set_intfdata(intf, iforce); usb_set_intfdata(intf, iforce_usb);
return 0; return 0;
fail: fail:
if (iforce) { if (iforce_usb) {
usb_free_urb(iforce->irq); usb_free_urb(iforce_usb->irq);
usb_free_urb(iforce->out); usb_free_urb(iforce_usb->out);
usb_free_urb(iforce->ctrl); kfree(iforce_usb);
kfree(iforce);
} }
return err; return err;
...@@ -180,17 +252,16 @@ static int iforce_usb_probe(struct usb_interface *intf, ...@@ -180,17 +252,16 @@ static int iforce_usb_probe(struct usb_interface *intf,
static void iforce_usb_disconnect(struct usb_interface *intf) 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); 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_usb->irq);
usb_free_urb(iforce->out); usb_free_urb(iforce_usb->out);
usb_free_urb(iforce->ctrl);
kfree(iforce); kfree(iforce_usb);
} }
static const struct usb_device_id iforce_usb_ids[] = { static const struct usb_device_id iforce_usb_ids[] = {
...@@ -202,6 +273,7 @@ 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(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */
{ USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */ { USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */
{ USB_DEVICE(0x061c, 0xc084) }, /* 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, 0x0001) }, /* Guillemot Race Leader Force Feedback */
{ USB_DEVICE(0x06f8, 0x0003) }, /* Guillemot Jet Leader Force Feedback */ { USB_DEVICE(0x06f8, 0x0003) }, /* Guillemot Jet Leader Force Feedback */
{ USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */ { USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */
...@@ -217,3 +289,9 @@ struct usb_driver iforce_usb_driver = { ...@@ -217,3 +289,9 @@ struct usb_driver iforce_usb_driver = {
.disconnect = iforce_usb_disconnect, .disconnect = iforce_usb_disconnect,
.id_table = iforce_usb_ids, .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 @@ ...@@ -14,8 +14,6 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/serio.h>
#include <linux/circ_buf.h> #include <linux/circ_buf.h>
#include <linux/mutex.h> #include <linux/mutex.h>
...@@ -28,10 +26,6 @@ ...@@ -28,10 +26,6 @@
#define IFORCE_MAX_LENGTH 16 #define IFORCE_MAX_LENGTH 16
/* iforce::bus */
#define IFORCE_232 1
#define IFORCE_USB 2
#define IFORCE_EFFECTS_MAX 32 #define IFORCE_EFFECTS_MAX 32
/* Each force feedback effect is made of one core effect, which can be /* Each force feedback effect is made of one core effect, which can be
...@@ -81,27 +75,21 @@ struct iforce_device { ...@@ -81,27 +75,21 @@ struct iforce_device {
signed short *ff; 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 iforce {
struct input_dev *dev; /* Input device interface */ struct input_dev *dev; /* Input device interface */
struct iforce_device *type; 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; spinlock_t xmit_lock;
/* Buffer used for asynchronous sending of bytes to the device */ /* Buffer used for asynchronous sending of bytes to the device */
struct circ_buf xmit; struct circ_buf xmit;
...@@ -127,23 +115,24 @@ struct iforce { ...@@ -127,23 +115,24 @@ struct iforce {
/* Encode a time value */ /* Encode a time value */
#define TIME_SCALE(a) (a) #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 */ /* 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 */ /* 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 */ /* iforce-packets.c */
int iforce_control_playback(struct iforce*, u16 id, unsigned int); 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); 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); 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 */ /* iforce-ff.c */
int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *); 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) ...@@ -771,7 +771,6 @@ static int gpio_keys_probe(struct platform_device *pdev)
struct fwnode_handle *child = NULL; struct fwnode_handle *child = NULL;
struct gpio_keys_drvdata *ddata; struct gpio_keys_drvdata *ddata;
struct input_dev *input; struct input_dev *input;
size_t size;
int i, error; int i, error;
int wakeup = 0; int wakeup = 0;
...@@ -781,9 +780,8 @@ static int gpio_keys_probe(struct platform_device *pdev) ...@@ -781,9 +780,8 @@ static int gpio_keys_probe(struct platform_device *pdev)
return PTR_ERR(pdata); return PTR_ERR(pdata);
} }
size = sizeof(struct gpio_keys_drvdata) + ddata = devm_kzalloc(dev, struct_size(ddata, data, pdata->nbuttons),
pdata->nbuttons * sizeof(struct gpio_button_data); GFP_KERNEL);
ddata = devm_kzalloc(dev, size, GFP_KERNEL);
if (!ddata) { if (!ddata) {
dev_err(dev, "failed to allocate state\n"); dev_err(dev, "failed to allocate state\n");
return -ENOMEM; return -ENOMEM;
......
...@@ -165,6 +165,8 @@ gpio_keys_polled_get_devtree_pdata(struct device *dev) ...@@ -165,6 +165,8 @@ gpio_keys_polled_get_devtree_pdata(struct device *dev)
pdata->rep = device_property_present(dev, "autorepeat"); pdata->rep = device_property_present(dev, "autorepeat");
device_property_read_u32(dev, "poll-interval", &pdata->poll_interval); 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) { device_for_each_child_node(dev, child) {
if (fwnode_property_read_u32(child, "linux,code", if (fwnode_property_read_u32(child, "linux,code",
&button->code)) { &button->code)) {
...@@ -232,7 +234,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) ...@@ -232,7 +234,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
struct gpio_keys_polled_dev *bdev; struct gpio_keys_polled_dev *bdev;
struct input_polled_dev *poll_dev; struct input_polled_dev *poll_dev;
struct input_dev *input; struct input_dev *input;
size_t size;
int error; int error;
int i; int i;
...@@ -247,9 +248,8 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) ...@@ -247,9 +248,8 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
size = sizeof(struct gpio_keys_polled_dev) + bdev = devm_kzalloc(dev, struct_size(bdev, data, pdata->nbuttons),
pdata->nbuttons * sizeof(struct gpio_keys_button_data); GFP_KERNEL);
bdev = devm_kzalloc(dev, size, GFP_KERNEL);
if (!bdev) { if (!bdev) {
dev_err(dev, "no memory for private data\n"); dev_err(dev, "no memory for private data\n");
return -ENOMEM; return -ENOMEM;
...@@ -269,7 +269,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) ...@@ -269,7 +269,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
input = poll_dev->input; input = poll_dev->input;
input->name = pdev->name; input->name = pdata->name ?: pdev->name;
input->phys = DRV_NAME"/input0"; input->phys = DRV_NAME"/input0";
input->id.bustype = BUS_HOST; input->id.bustype = BUS_HOST;
......
...@@ -422,7 +422,6 @@ static int imx_keypad_probe(struct platform_device *pdev) ...@@ -422,7 +422,6 @@ static int imx_keypad_probe(struct platform_device *pdev)
dev_get_platdata(&pdev->dev); dev_get_platdata(&pdev->dev);
struct imx_keypad *keypad; struct imx_keypad *keypad;
struct input_dev *input_dev; struct input_dev *input_dev;
struct resource *res;
int irq, error, i, row, col; int irq, error, i, row, col;
if (!keymap_data && !pdev->dev.of_node) { if (!keymap_data && !pdev->dev.of_node) {
...@@ -455,8 +454,7 @@ static int imx_keypad_probe(struct platform_device *pdev) ...@@ -455,8 +454,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
timer_setup(&keypad->check_matrix_timer, timer_setup(&keypad->check_matrix_timer,
imx_keypad_check_for_events, 0); imx_keypad_check_for_events, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); keypad->mmio_base = devm_platform_ioremap_resource(pdev, 0);
keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(keypad->mmio_base)) if (IS_ERR(keypad->mmio_base))
return PTR_ERR(keypad->mmio_base); return PTR_ERR(keypad->mmio_base);
......
...@@ -266,7 +266,7 @@ static int tca8418_keypad_probe(struct i2c_client *client, ...@@ -266,7 +266,7 @@ static int tca8418_keypad_probe(struct i2c_client *client,
struct tca8418_keypad *keypad_data; struct tca8418_keypad *keypad_data;
struct input_dev *input; struct input_dev *input;
u32 rows = 0, cols = 0; u32 rows = 0, cols = 0;
int error, row_shift, max_keys; int error, row_shift;
u8 reg; u8 reg;
/* Check i2c driver capabilities */ /* Check i2c driver capabilities */
...@@ -291,7 +291,6 @@ static int tca8418_keypad_probe(struct i2c_client *client, ...@@ -291,7 +291,6 @@ static int tca8418_keypad_probe(struct i2c_client *client,
} }
row_shift = get_count_order(cols); row_shift = get_count_order(cols);
max_keys = rows << row_shift;
/* Allocate memory for keypad_data and input device */ /* Allocate memory for keypad_data and input device */
keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL); keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL);
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/mfd/da9063/core.h> #include <linux/mfd/da9063/core.h>
#include <linux/mfd/da9063/pdata.h>
#include <linux/mfd/da9063/registers.h> #include <linux/mfd/da9063/registers.h>
#include <linux/mfd/da9062/core.h> #include <linux/mfd/da9062/core.h>
#include <linux/mfd/da9062/registers.h> #include <linux/mfd/da9062/registers.h>
...@@ -192,8 +191,6 @@ static void da9063_cancel_poll(void *data) ...@@ -192,8 +191,6 @@ static void da9063_cancel_poll(void *data)
static int da9063_onkey_probe(struct platform_device *pdev) 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; struct da9063_onkey *onkey;
const struct of_device_id *match; const struct of_device_id *match;
int irq; int irq;
...@@ -220,12 +217,8 @@ static int da9063_onkey_probe(struct platform_device *pdev) ...@@ -220,12 +217,8 @@ static int da9063_onkey_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
} }
if (pdata) onkey->key_power = !of_property_read_bool(pdev->dev.of_node,
onkey->key_power = pdata->key_power; "dlg,disable-key-power");
else
onkey->key_power =
!of_property_read_bool(pdev->dev.of_node,
"dlg,disable-key-power");
onkey->input = devm_input_allocate_device(&pdev->dev); onkey->input = devm_input_allocate_device(&pdev->dev);
if (!onkey->input) { if (!onkey->input) {
......
...@@ -119,3 +119,4 @@ module_platform_driver(max77650_onkey_driver); ...@@ -119,3 +119,4 @@ module_platform_driver(max77650_onkey_driver);
MODULE_DESCRIPTION("MAXIM 77650/77651 ONKEY driver"); MODULE_DESCRIPTION("MAXIM 77650/77651 ONKEY driver");
MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:max77650-onkey");
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/input/elan-i2c-ids.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -96,6 +97,7 @@ struct elan_tp_data { ...@@ -96,6 +97,7 @@ struct elan_tp_data {
u8 max_baseline; u8 max_baseline;
bool baseline_ready; bool baseline_ready;
u8 clickpad; u8 clickpad;
bool middle_button;
}; };
static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count, static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
...@@ -363,27 +365,62 @@ static unsigned int elan_convert_resolution(u8 val) ...@@ -363,27 +365,62 @@ static unsigned int elan_convert_resolution(u8 val)
static int elan_query_device_parameters(struct elan_tp_data *data) static int elan_query_device_parameters(struct elan_tp_data *data)
{ {
struct i2c_client *client = data->client;
unsigned int x_traces, y_traces; unsigned int x_traces, y_traces;
u32 x_mm, y_mm;
u8 hw_x_res, hw_y_res; u8 hw_x_res, hw_y_res;
int error; int error;
error = data->ops->get_max(data->client, &data->max_x, &data->max_y); if (device_property_read_u32(&client->dev,
if (error) "touchscreen-size-x", &data->max_x) ||
return error; device_property_read_u32(&client->dev,
"touchscreen-size-y", &data->max_y)) {
error = data->ops->get_num_traces(data->client, &x_traces, &y_traces); error = data->ops->get_max(data->client,
if (error) &data->max_x,
return error; &data->max_y);
if (error)
return error;
} else {
/* size is the maximum + 1 */
--data->max_x;
--data->max_y;
}
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_x = data->max_x / x_traces;
data->width_y = data->max_y / y_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,
if (error) "touchscreen-x-mm", &x_mm) ||
return error; 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;
data->x_res = elan_convert_resolution(hw_x_res); if (device_property_read_bool(&client->dev, "elan,middle-button"))
data->y_res = elan_convert_resolution(hw_y_res); data->middle_button = true;
return 0; return 0;
} }
...@@ -923,8 +960,9 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet) ...@@ -923,8 +960,9 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
finger_data += ETP_FINGER_DATA_LEN; finger_data += ETP_FINGER_DATA_LEN;
} }
input_report_key(input, BTN_LEFT, tp_info & 0x01); input_report_key(input, BTN_LEFT, tp_info & BIT(0));
input_report_key(input, BTN_RIGHT, tp_info & 0x02); 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_report_abs(input, ABS_DISTANCE, hover_event != 0);
input_mt_report_pointer_emulation(input, true); input_mt_report_pointer_emulation(input, true);
input_sync(input); input_sync(input);
...@@ -1058,10 +1096,13 @@ static int elan_setup_input_device(struct elan_tp_data *data) ...@@ -1058,10 +1096,13 @@ static int elan_setup_input_device(struct elan_tp_data *data)
__set_bit(EV_ABS, input->evbit); __set_bit(EV_ABS, input->evbit);
__set_bit(INPUT_PROP_POINTER, input->propbit); __set_bit(INPUT_PROP_POINTER, input->propbit);
if (data->clickpad) if (data->clickpad) {
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit); __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
else } else {
__set_bit(BTN_RIGHT, input->keybit); __set_bit(BTN_RIGHT, input->keybit);
if (data->middle_button)
__set_bit(BTN_MIDDLE, input->keybit);
}
__set_bit(BTN_LEFT, input->keybit); __set_bit(BTN_LEFT, input->keybit);
/* Set up ST parameters */ /* Set up ST parameters */
...@@ -1332,55 +1373,6 @@ static const struct i2c_device_id elan_id[] = { ...@@ -1332,55 +1373,6 @@ static const struct i2c_device_id elan_id[] = {
MODULE_DEVICE_TABLE(i2c, elan_id); MODULE_DEVICE_TABLE(i2c, elan_id);
#ifdef CONFIG_ACPI #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); MODULE_DEVICE_TABLE(acpi, elan_acpi_id);
#endif #endif
......
...@@ -226,6 +226,52 @@ static void elantech_packet_dump(struct psmouse *psmouse) ...@@ -226,6 +226,52 @@ static void elantech_packet_dump(struct psmouse *psmouse)
psmouse->pktsize, psmouse->packet); 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 * Interpret complete data packets and report absolute mode input events for
* hardware version 1. (4 byte packets) * hardware version 1. (4 byte packets)
...@@ -523,7 +569,7 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse, ...@@ -523,7 +569,7 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
/* For clickpads map both buttons to BTN_LEFT */ /* 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); input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
else else
psmouse_report_standard_buttons(dev, packet[0]); psmouse_report_standard_buttons(dev, packet[0]);
...@@ -541,7 +587,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse) ...@@ -541,7 +587,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
/* For clickpads map both buttons to BTN_LEFT */ /* 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); input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
else else
psmouse_report_standard_buttons(dev, packet[0]); psmouse_report_standard_buttons(dev, packet[0]);
...@@ -991,88 +1037,6 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) ...@@ -991,88 +1037,6 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
return rc; 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 * (value from firmware) * 10 + 790 = dpi
* we also have to convert dpi to dots/mm (*10/254 to avoid floating point) * 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, ...@@ -1099,53 +1063,12 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
return 0; 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) static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
{ {
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
struct elantech_data *etd = psmouse->private; 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); __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
__clear_bit(BTN_RIGHT, dev->keybit); __clear_bit(BTN_RIGHT, dev->keybit);
} }
...@@ -1181,16 +1104,6 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = { ...@@ -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 * Set the appropriate event bits for the input subsystem
*/ */
...@@ -1199,10 +1112,9 @@ static int elantech_set_input_params(struct psmouse *psmouse) ...@@ -1199,10 +1112,9 @@ static int elantech_set_input_params(struct psmouse *psmouse)
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
struct elantech_data *etd = psmouse->private; struct elantech_data *etd = psmouse->private;
struct elantech_device_info *info = &etd->info; struct elantech_device_info *info = &etd->info;
unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0; unsigned int x_min = info->x_min, y_min = info->y_min,
x_max = info->x_max, y_max = info->y_max,
if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) width = info->width;
return -1;
__set_bit(INPUT_PROP_POINTER, dev->propbit); __set_bit(INPUT_PROP_POINTER, dev->propbit);
__set_bit(EV_KEY, dev->evbit); __set_bit(EV_KEY, dev->evbit);
...@@ -1210,8 +1122,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) ...@@ -1210,8 +1122,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
__clear_bit(EV_REL, dev->evbit); __clear_bit(EV_REL, dev->evbit);
__set_bit(BTN_LEFT, dev->keybit); __set_bit(BTN_LEFT, dev->keybit);
if (dmi_check_system(elantech_dmi_has_middle_button) || if (info->has_middle_button)
psmouse_matches_pnp_id(psmouse, middle_button_pnp_ids))
__set_bit(BTN_MIDDLE, dev->keybit); __set_bit(BTN_MIDDLE, dev->keybit);
__set_bit(BTN_RIGHT, dev->keybit); __set_bit(BTN_RIGHT, dev->keybit);
...@@ -1686,6 +1597,7 @@ static int elantech_query_info(struct psmouse *psmouse, ...@@ -1686,6 +1597,7 @@ static int elantech_query_info(struct psmouse *psmouse,
struct elantech_device_info *info) struct elantech_device_info *info)
{ {
unsigned char param[3]; unsigned char param[3];
unsigned char traces;
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
...@@ -1754,6 +1666,90 @@ static int elantech_query_info(struct psmouse *psmouse, ...@@ -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; return 0;
} }
...@@ -1780,10 +1776,6 @@ static const char * const i2c_blacklist_pnp_ids[] = { ...@@ -1780,10 +1776,6 @@ static const char * const i2c_blacklist_pnp_ids[] = {
* These are known to not be working properly as bits are missing * These are known to not be working properly as bits are missing
* in elan_i2c. * in elan_i2c.
*/ */
"LEN2131", /* ThinkPad P52 w/ NFC */
"LEN2132", /* ThinkPad P52 */
"LEN2133", /* ThinkPad P72 w/ NFC */
"LEN2134", /* ThinkPad P72 */
NULL NULL
}; };
...@@ -1791,17 +1783,45 @@ static int elantech_create_smbus(struct psmouse *psmouse, ...@@ -1791,17 +1783,45 @@ static int elantech_create_smbus(struct psmouse *psmouse,
struct elantech_device_info *info, struct elantech_device_info *info,
bool leave_breadcrumbs) bool leave_breadcrumbs)
{ {
const struct property_entry i2c_properties[] = { struct property_entry i2c_props[11] = {};
PROPERTY_ENTRY_BOOL("elan,trackpoint"),
{ },
};
struct i2c_board_info smbus_board = { struct i2c_board_info smbus_board = {
I2C_BOARD_INFO("elan_i2c", 0x15), I2C_BOARD_INFO("elan_i2c", 0x15),
.flags = I2C_CLIENT_HOST_NOTIFY, .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) 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, return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
leave_breadcrumbs); leave_breadcrumbs);
......
...@@ -141,8 +141,15 @@ struct elantech_device_info { ...@@ -141,8 +141,15 @@ struct elantech_device_info {
unsigned char debug; unsigned char debug;
unsigned char hw_version; unsigned char hw_version;
unsigned int fw_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 x_res;
unsigned int y_res; unsigned int y_res;
unsigned int x_traces;
unsigned int y_traces;
unsigned int width;
unsigned int bus; unsigned int bus;
bool paritycheck; bool paritycheck;
bool jumpy_cursor; bool jumpy_cursor;
...@@ -150,6 +157,7 @@ struct elantech_device_info { ...@@ -150,6 +157,7 @@ struct elantech_device_info {
bool crc_enabled; bool crc_enabled;
bool set_hw_resolution; bool set_hw_resolution;
bool has_trackpoint; bool has_trackpoint;
bool has_middle_button;
int (*send_cmd)(struct psmouse *psmouse, unsigned char c, int (*send_cmd)(struct psmouse *psmouse, unsigned char c,
unsigned char *param); unsigned char *param);
}; };
......
...@@ -173,6 +173,7 @@ static const char * const smbus_pnp_ids[] = { ...@@ -173,6 +173,7 @@ static const char * const smbus_pnp_ids[] = {
"LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */ "LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
"LEN0073", /* X1 Carbon G5 (Elantech) */ "LEN0073", /* X1 Carbon G5 (Elantech) */
"LEN0092", /* X1 Carbon 6 */ "LEN0092", /* X1 Carbon 6 */
"LEN0093", /* T480 */
"LEN0096", /* X280 */ "LEN0096", /* X280 */
"LEN0097", /* X280 -> ALPS trackpoint */ "LEN0097", /* X280 -> ALPS trackpoint */
"LEN200f", /* T450s */ "LEN200f", /* T450s */
......
...@@ -70,7 +70,6 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) ...@@ -70,7 +70,6 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
int pitch_y = 0; int pitch_y = 0;
int rx_receivers = 0; int rx_receivers = 0;
int tx_receivers = 0; int tx_receivers = 0;
int sensor_flags = 0;
item = rmi_get_register_desc_item(&f12->control_reg_desc, 8); item = rmi_get_register_desc_item(&f12->control_reg_desc, 8);
if (!item) { if (!item) {
...@@ -126,10 +125,9 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) ...@@ -126,10 +125,9 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
offset += 2; offset += 2;
} }
if (rmi_register_desc_has_subpacket(item, 4)) { /* Skip over sensor flags */
sensor_flags = buf[offset]; if (rmi_register_desc_has_subpacket(item, 4))
offset += 1; offset += 1;
}
sensor->x_mm = (pitch_x * rx_receivers) >> 12; sensor->x_mm = (pitch_x * rx_receivers) >> 12;
sensor->y_mm = (pitch_y * tx_receivers) >> 12; sensor->y_mm = (pitch_y * tx_receivers) >> 12;
......
...@@ -256,16 +256,6 @@ enum v4l_dbg_inputs { ...@@ -256,16 +256,6 @@ enum v4l_dbg_inputs {
MXT_V4L_INPUT_MAX, 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 { enum mxt_suspend_mode {
MXT_SUSPEND_DEEP_SLEEP = 0, MXT_SUSPEND_DEEP_SLEEP = 0,
MXT_SUSPEND_T9_CTRL = 1, MXT_SUSPEND_T9_CTRL = 1,
...@@ -1521,7 +1511,8 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) ...@@ -1521,7 +1511,8 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw)
} else if (config_crc == data->config_crc) { } else if (config_crc == data->config_crc) {
dev_dbg(dev, "Config CRC 0x%06X: OK\n", dev_dbg(dev, "Config CRC 0x%06X: OK\n",
data->config_crc); data->config_crc);
return 0; ret = 0;
goto release_raw;
} else { } else {
dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
data->config_crc, config_crc); data->config_crc, config_crc);
...@@ -2218,6 +2209,16 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data) ...@@ -2218,6 +2209,16 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data)
} }
#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 #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, static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
unsigned int y) unsigned int y)
{ {
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/input/mt.h> #include <linux/input/mt.h>
#include <linux/input/touchscreen.h> #include <linux/input/touchscreen.h>
#include <asm/unaligned.h>
#define WORK_REGISTER_THRESHOLD 0x00 #define WORK_REGISTER_THRESHOLD 0x00
#define WORK_REGISTER_REPORT_RATE 0x08 #define WORK_REGISTER_REPORT_RATE 0x08
...@@ -228,7 +229,6 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) ...@@ -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++) { for (i = 0; i < tsdata->max_support_points; i++) {
u8 *buf = &rdbuf[i * tplen + offset]; u8 *buf = &rdbuf[i * tplen + offset];
bool down;
type = buf[0] >> 6; type = buf[0] >> 6;
/* ignore Reserved events */ /* ignore Reserved events */
...@@ -239,23 +239,19 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) ...@@ -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) if (tsdata->version == EDT_M06 && type == TOUCH_EVENT_DOWN)
continue; continue;
x = ((buf[0] << 8) | buf[1]) & 0x0fff; x = get_unaligned_be16(buf) & 0x0fff;
y = ((buf[2] << 8) | buf[3]) & 0x0fff; y = get_unaligned_be16(buf + 2) & 0x0fff;
/* The FT5x26 send the y coordinate first */ /* The FT5x26 send the y coordinate first */
if (tsdata->version == EV_FT) if (tsdata->version == EV_FT)
swap(x, y); swap(x, y);
id = (buf[2] >> 4) & 0x0f; id = (buf[2] >> 4) & 0x0f;
down = type != TOUCH_EVENT_UP;
input_mt_slot(tsdata->input, id); input_mt_slot(tsdata->input, id);
input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER,
type != TOUCH_EVENT_UP))
if (!down) touchscreen_report_pos(tsdata->input, &tsdata->prop,
continue; x, y, true);
touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y,
true);
} }
input_mt_report_pointer_emulation(tsdata->input, true); input_mt_report_pointer_emulation(tsdata->input, true);
......
...@@ -28,6 +28,7 @@ struct eeti_ts { ...@@ -28,6 +28,7 @@ struct eeti_ts {
struct input_dev *input; struct input_dev *input;
struct gpio_desc *attn_gpio; struct gpio_desc *attn_gpio;
struct touchscreen_properties props; struct touchscreen_properties props;
struct mutex mutex;
bool running; bool running;
}; };
...@@ -62,42 +63,80 @@ static void eeti_ts_report_event(struct eeti_ts *eeti, u8 *buf) ...@@ -62,42 +63,80 @@ static void eeti_ts_report_event(struct eeti_ts *eeti, u8 *buf)
input_sync(eeti->input); input_sync(eeti->input);
} }
static int eeti_ts_read(struct eeti_ts *eeti)
{
int len, error;
char buf[6];
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) static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
{ {
struct eeti_ts *eeti = dev_id; struct eeti_ts *eeti = dev_id;
int len;
int error; int error;
char buf[6];
mutex_lock(&eeti->mutex);
do { do {
len = i2c_master_recv(eeti->client, buf, sizeof(buf)); /*
if (len != sizeof(buf)) { * If we have attention GPIO, trust it. Otherwise we'll read
error = len < 0 ? len : -EIO; * once and exit. We assume that in this case we are using
dev_err(&eeti->client->dev, * level triggered interrupt and it will get raised again
"failed to read touchscreen data: %d\n", * if/when there is more data.
error); */
if (eeti->attn_gpio &&
!gpiod_get_value_cansleep(eeti->attn_gpio)) {
break; break;
} }
if (buf[0] & 0x80) { error = eeti_ts_read(eeti);
/* Motion packet */ if (error)
eeti_ts_report_event(eeti, buf); break;
}
} while (eeti->running && } while (eeti->running && eeti->attn_gpio);
eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio));
mutex_unlock(&eeti->mutex);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void eeti_ts_start(struct eeti_ts *eeti) static void eeti_ts_start(struct eeti_ts *eeti)
{ {
mutex_lock(&eeti->mutex);
eeti->running = true; eeti->running = true;
wmb();
enable_irq(eeti->client->irq); 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) 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; eeti->running = false;
wmb(); wmb();
disable_irq(eeti->client->irq); disable_irq(eeti->client->irq);
...@@ -140,6 +179,8 @@ static int eeti_ts_probe(struct i2c_client *client, ...@@ -140,6 +179,8 @@ static int eeti_ts_probe(struct i2c_client *client,
return -ENOMEM; return -ENOMEM;
} }
mutex_init(&eeti->mutex);
input = devm_input_allocate_device(dev); input = devm_input_allocate_device(dev);
if (!input) { if (!input) {
dev_err(dev, "Failed to allocate input device.\n"); dev_err(dev, "Failed to allocate input device.\n");
......
...@@ -364,8 +364,6 @@ static int imx6ul_tsc_probe(struct platform_device *pdev) ...@@ -364,8 +364,6 @@ static int imx6ul_tsc_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct imx6ul_tsc *tsc; struct imx6ul_tsc *tsc;
struct input_dev *input_dev; struct input_dev *input_dev;
struct resource *tsc_mem;
struct resource *adc_mem;
int err; int err;
int tsc_irq; int tsc_irq;
int adc_irq; int adc_irq;
...@@ -403,16 +401,14 @@ static int imx6ul_tsc_probe(struct platform_device *pdev) ...@@ -403,16 +401,14 @@ static int imx6ul_tsc_probe(struct platform_device *pdev)
return err; return err;
} }
tsc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); tsc->tsc_regs = devm_platform_ioremap_resource(pdev, 0);
tsc->tsc_regs = devm_ioremap_resource(&pdev->dev, tsc_mem);
if (IS_ERR(tsc->tsc_regs)) { if (IS_ERR(tsc->tsc_regs)) {
err = PTR_ERR(tsc->tsc_regs); err = PTR_ERR(tsc->tsc_regs);
dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err); dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err);
return err; return err;
} }
adc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); tsc->adc_regs = devm_platform_ioremap_resource(pdev, 1);
tsc->adc_regs = devm_ioremap_resource(&pdev->dev, adc_mem);
if (IS_ERR(tsc->adc_regs)) { if (IS_ERR(tsc->adc_regs)) {
err = PTR_ERR(tsc->adc_regs); err = PTR_ERR(tsc->adc_regs);
dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err); dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err);
......
...@@ -1056,8 +1056,6 @@ static int iqs5xx_probe(struct i2c_client *client, ...@@ -1056,8 +1056,6 @@ static int iqs5xx_probe(struct i2c_client *client,
if (!iqs5xx) if (!iqs5xx)
return -ENOMEM; return -ENOMEM;
dev_set_drvdata(&client->dev, iqs5xx);
i2c_set_clientdata(client, iqs5xx); i2c_set_clientdata(client, iqs5xx);
iqs5xx->client = client; iqs5xx->client = client;
......
/*
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment