提交 9fa40a11 编写于 作者: L Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid

Pull HID updates from Jiri Kosina:

 1) Patchset from Henrik Rydberg which substantially reduces irqsoff
    latency for all input devices.  In addition to that, Henrik reworked
    multitouch handling in order to reduce runtime memory consumption.

    This patchset touches code in Input subsystem as well.  All the
    changes have been Acked by Dmitry, and we agreed to do it this way
    due to inter-dependencies between the patchset and subsequent
    changes in HID subsystem.

 2) Rework, clenaups and a lot of fixes to picolcd driver by Bruno
    Prémont.

 3) Core report descriptor handling fix which fixes resume issue on some
    devices, by Kevin Daughtridge

 4) hidraw fixes by Alexey Khoroshilov and Ratan Nalumasu

 5) wiimote driver now supports balance board, by David Herrmann.

 6) Other smaller fixes and new device id additions all over the place.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (79 commits)
  HID: hidraw: don't deallocate memory when it is in use
  HID: picoLCD: optimize for inactive debugfs
  HID: multitouch: add support for GeneralTouch multi-touchscreen
  HID: Add support for Sony PS3 BD Remote Control
  HID: keep dev_rdesc unmodified and use it for comparisons
  HID: lg4ff: Minor coding style fixes in lg4ff and hid-lg
  HID: hid-lg4ff: Set absolute axes parametes on DFP
  HID: hid-lg4ff: Adjust X axis input value accordingly to selected range.
  HID: hid-lg4ff: Minor code cleanup to improve readability
  HID: ntrig: change default value of logical/physical width/height to 1
  HID: picoLCD: bounds check in dump_buff_as_hex()
  Input: bcm5974 - Convert to MT-B
  Input: bcm5974 - Drop the logical dimensions
  Input: bcm5974 - Preparatory renames
  Input: bcm5974 - only setup button urb for TYPE1 devices
  HID: hid-multitouch: Add Flatfrog support
  HID: hid-multitouch: Fix contact count on 3M panels
  HID: hid-multitouch: Remove the redundant touch state
  HID: hid-multitouch: Simplify setup and frame synchronization
  HID: Allow more fields in the hid report
  ...
WWhat: /sys/class/hidraw/hidraw*/device/oled*_img
Date: June 2012
Contact: linux-bluetooth@vger.kernel.org
Description:
The /sys/class/hidraw/hidraw*/device/oled*_img files control
OLED mocro displays on Intuos4 Wireless tablet. Accepted image
has to contain 256 bytes (64x32 px 1 bit colour). The format
is the same as PBM image 62x32px without header (64 bits per
horizontal line, 32 lines). An example of setting OLED No. 0:
dd bs=256 count=1 if=img_file of=[path to oled0_img]/oled0_img
The attribute is read only and no local copy of the image is
stored.
What: /sys/class/hidraw/hidraw*/device/speed
Date: April 2010
Kernel Version: 2.6.35
......
......@@ -5322,6 +5322,12 @@ L: linux-mtd@lists.infradead.org
S: Maintained
F: drivers/mtd/devices/phram.c
PICOLCD HID DRIVER
M: Bruno Prémont <bonbons@linux-vserver.org>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-picolcd*
PICOXCELL SUPPORT
M: Jamie Iles <jamie@jamieiles.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
......
......@@ -307,7 +307,6 @@ config HID_LOGITECH
config HID_LOGITECH_DJ
tristate "Logitech Unifying receivers full support"
depends on HID_LOGITECH
default m
---help---
Say Y if you want support for Logitech Unifying receivers and devices.
Unifying receivers are capable of pairing up to 6 Logitech compliant
......@@ -527,6 +526,14 @@ config HID_PICOLCD_LEDS
---help---
Provide access to PicoLCD's GPO pins via leds class.
config HID_PICOLCD_CIR
bool "CIR via RC class" if EXPERT
default !EXPERT
depends on HID_PICOLCD
depends on HID_PICOLCD=RC_CORE || RC_CORE=y
---help---
Provide access to PicoLCD's CIR interface via remote control (LIRC).
config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices"
depends on USB_HID
......@@ -534,6 +541,15 @@ config HID_PRIMAX
Support for Primax devices that are not fully compliant with the
HID standard.
config HID_PS3REMOTE
tristate "Sony PS3 BD Remote Control"
depends on BT_HIDP
---help---
Support for the Sony PS3 Blue-ray Disk Remote Control and Logitech
Harmony Adapter for PS3, which connect over Bluetooth.
Support for the 6-axis controllers is provided by HID_SONY.
config HID_ROCCAT
tristate "Roccat device support"
depends on USB_HID
......@@ -561,7 +577,9 @@ config HID_SONY
tristate "Sony PS3 controller"
depends on USB_HID
---help---
Support for Sony PS3 controller.
Support for Sony PS3 6-axis controllers.
Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE.
config HID_SPEEDLINK
tristate "Speedlink VAD Cezanne mouse support"
......
......@@ -69,7 +69,28 @@ obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
hid-picolcd-y += hid-picolcd_core.o
ifdef CONFIG_HID_PICOLCD_FB
hid-picolcd-y += hid-picolcd_fb.o
endif
ifdef CONFIG_HID_PICOLCD_BACKLIGHT
hid-picolcd-y += hid-picolcd_backlight.o
endif
ifdef CONFIG_HID_PICOLCD_LCD
hid-picolcd-y += hid-picolcd_lcd.o
endif
ifdef CONFIG_HID_PICOLCD_LEDS
hid-picolcd-y += hid-picolcd_leds.o
endif
ifdef CONFIG_HID_PICOLCD_CIR
hid-picolcd-y += hid-picolcd_cir.o
endif
ifdef CONFIG_DEBUG_FS
hid-picolcd-y += hid-picolcd_debugfs.o
endif
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
*/
......
......@@ -9,7 +9,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
#include <linux/device.h>
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
......@@ -126,7 +126,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
hid_err(parser->device, "collection stack overflow\n");
return -1;
return -EINVAL;
}
if (parser->device->maxcollection == parser->device->collection_size) {
......@@ -134,7 +134,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
parser->device->collection_size * 2, GFP_KERNEL);
if (collection == NULL) {
hid_err(parser->device, "failed to reallocate collection array\n");
return -1;
return -ENOMEM;
}
memcpy(collection, parser->device->collection,
sizeof(struct hid_collection) *
......@@ -170,7 +170,7 @@ static int close_collection(struct hid_parser *parser)
{
if (!parser->collection_stack_ptr) {
hid_err(parser->device, "collection stack underflow\n");
return -1;
return -EINVAL;
}
parser->collection_stack_ptr--;
return 0;
......@@ -374,7 +374,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
parser->global.report_size = item_udata(item);
if (parser->global.report_size > 96) {
if (parser->global.report_size > 128) {
hid_err(parser->device, "invalid report_size %d\n",
parser->global.report_size);
return -1;
......@@ -757,6 +757,7 @@ int hid_open_report(struct hid_device *device)
struct hid_item item;
unsigned int size;
__u8 *start;
__u8 *buf;
__u8 *end;
int ret;
static int (*dispatch_type[])(struct hid_parser *parser,
......@@ -775,12 +776,21 @@ int hid_open_report(struct hid_device *device)
return -ENODEV;
size = device->dev_rsize;
buf = kmemdup(start, size, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
if (device->driver->report_fixup)
start = device->driver->report_fixup(device, start, &size);
start = device->driver->report_fixup(device, buf, &size);
else
start = buf;
device->rdesc = kmemdup(start, size, GFP_KERNEL);
if (device->rdesc == NULL)
start = kmemdup(start, size, GFP_KERNEL);
kfree(buf);
if (start == NULL)
return -ENOMEM;
device->rdesc = start;
device->rsize = size;
parser = vzalloc(sizeof(struct hid_parser));
......@@ -1448,7 +1458,14 @@ void hid_disconnect(struct hid_device *hdev)
}
EXPORT_SYMBOL_GPL(hid_disconnect);
/* a list of devices for which there is a specialized driver on HID bus */
/*
* A list of devices for which there is a specialized driver on HID bus.
*
* Please note that for multitouch devices (driven by hid-multitouch driver),
* there is a proper autodetection and autoloading in place (based on presence
* of HID_DG_CONTACTID), so those devices don't need to be added to this list,
* as we are doing the right thing in hid_scan_usage().
*/
static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
......@@ -1566,6 +1583,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
......@@ -1627,6 +1645,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
#if IS_ENABLED(CONFIG_HID_ROCCAT)
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
......@@ -1635,10 +1654,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
......@@ -1663,6 +1684,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
......@@ -911,15 +911,21 @@ static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
}
static int hid_debug_rdesc_show(struct seq_file *f, void *p)
{
struct hid_device *hdev = f->private;
const __u8 *rdesc = hdev->rdesc;
unsigned rsize = hdev->rsize;
int i;
if (!rdesc) {
rdesc = hdev->dev_rdesc;
rsize = hdev->dev_rsize;
}
/* dump HID report descriptor */
for (i = 0; i < hdev->rsize; i++)
seq_printf(f, "%02x ", hdev->rdesc[i]);
for (i = 0; i < rsize; i++)
seq_printf(f, "%02x ", rdesc[i]);
seq_printf(f, "\n\n");
/* dump parsed data and input mappings */
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
......@@ -4,7 +4,6 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2006-2008 Jiri Kosina
*/
......
......@@ -100,8 +100,7 @@ static void holtekff_send(struct holtekff_device *holtekff,
holtekff->field->value[i] = data[i];
}
dbg_hid("sending %02x %02x %02x %02x %02x %02x %02x\n", data[0],
data[1], data[2], data[3], data[4], data[5], data[6]);
dbg_hid("sending %*ph\n", 7, data);
usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT);
}
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
*/
/*
......@@ -269,7 +268,11 @@
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
......@@ -283,6 +286,9 @@
#define USB_VENDOR_ID_EMS 0x2006
#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
#define USB_VENDOR_ID_FLATFROG 0x25b5
#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
......@@ -296,6 +302,9 @@
#define USB_VENDOR_ID_EZKEY 0x0518
#define USB_DEVICE_ID_BTC_8193 0x0002
#define USB_VENDOR_ID_FREESCALE 0x15A2
#define USB_DEVICE_ID_FREESCALE_MX28 0x004F
#define USB_VENDOR_ID_FRUCTEL 0x25B6
#define USB_DEVICE_ID_GAMETEL_MT_MODE 0x0002
......@@ -305,6 +314,7 @@
#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
......@@ -496,6 +506,7 @@
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211
#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
......@@ -652,7 +663,6 @@
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008
#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001
#define USB_VENDOR_ID_ROCCAT 0x1e7d
#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4
......@@ -683,6 +693,7 @@
#define USB_VENDOR_ID_SONY 0x054c
#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b
#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
......@@ -758,6 +769,7 @@
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781
#define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
......
......@@ -1154,6 +1154,7 @@ static void report_features(struct hid_device *hid)
int hidinput_connect(struct hid_device *hid, unsigned int force)
{
struct hid_driver *drv = hid->driver;
struct hid_report *report;
struct hid_input *hidinput = NULL;
struct input_dev *input_dev;
......@@ -1228,6 +1229,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
* UGCI) cram a lot of unrelated inputs into the
* same interface. */
hidinput->report = report;
if (drv->input_configured)
drv->input_configured(hid, hidinput);
if (input_register_device(hidinput->input))
goto out_cleanup;
hidinput = NULL;
......@@ -1235,8 +1238,12 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
}
if (hidinput && input_register_device(hidinput->input))
goto out_cleanup;
if (hidinput) {
if (drv->input_configured)
drv->input_configured(hid, hidinput);
if (input_register_device(hidinput->input))
goto out_cleanup;
}
return 0;
......
......@@ -24,7 +24,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000)
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
return 0;
switch (usage->hid & HID_USAGE) {
......
......@@ -56,9 +56,8 @@ static int tpkbd_input_mapping(struct hid_device *hdev,
static int tpkbd_features_set(struct hid_device *hdev)
{
struct hid_report *report;
struct tpkbd_data_pointer *data_pointer;
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02;
......@@ -77,14 +76,8 @@ static ssize_t pointer_press_to_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
}
......@@ -94,16 +87,10 @@ static ssize_t pointer_press_to_select_store(struct device *dev,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
......@@ -119,14 +106,8 @@ static ssize_t pointer_dragging_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
}
......@@ -136,16 +117,10 @@ static ssize_t pointer_dragging_store(struct device *dev,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
......@@ -161,14 +136,8 @@ static ssize_t pointer_release_to_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
}
......@@ -178,16 +147,10 @@ static ssize_t pointer_release_to_select_store(struct device *dev,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
......@@ -203,14 +166,8 @@ static ssize_t pointer_select_right_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
}
......@@ -220,16 +177,10 @@ static ssize_t pointer_select_right_store(struct device *dev,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
......@@ -245,14 +196,8 @@ static ssize_t pointer_sensitivity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
data_pointer->sensitivity);
......@@ -263,16 +208,10 @@ static ssize_t pointer_sensitivity_store(struct device *dev,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
return -EINVAL;
......@@ -286,14 +225,10 @@ static ssize_t pointer_press_speed_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
data_pointer->press_speed);
......@@ -304,16 +239,10 @@ static ssize_t pointer_press_speed_store(struct device *dev,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
return -EINVAL;
......@@ -370,15 +299,11 @@ static const struct attribute_group tpkbd_attr_group_pointer = {
static enum led_brightness tpkbd_led_brightness_get(
struct led_classdev *led_cdev)
{
struct device *dev;
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
struct device *dev = led_cdev->dev->parent;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int led_nr = 0;
dev = led_cdev->dev->parent;
hdev = container_of(dev, struct hid_device, dev);
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (led_cdev == &data_pointer->led_micmute)
led_nr = 1;
......@@ -390,16 +315,12 @@ static enum led_brightness tpkbd_led_brightness_get(
static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct device *dev;
struct hid_device *hdev;
struct device *dev = led_cdev->dev->parent;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
struct hid_report *report;
struct tpkbd_data_pointer *data_pointer;
int led_nr = 0;
dev = led_cdev->dev->parent;
hdev = container_of(dev, struct hid_device, dev);
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (led_cdev == &data_pointer->led_micmute)
led_nr = 1;
......@@ -508,13 +429,11 @@ static int tpkbd_probe(struct hid_device *hdev,
static void tpkbd_remove_tp(struct hid_device *hdev)
{
struct tpkbd_data_pointer *data_pointer;
struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
sysfs_remove_group(&hdev->dev.kobj,
&tpkbd_attr_group_pointer);
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
led_classdev_unregister(&data_pointer->led_micmute);
led_classdev_unregister(&data_pointer->led_mute);
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2010 Hendrik Iben
*/
......@@ -109,7 +108,7 @@ static __u8 dfp_rdesc_fixed[] = {
static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
rdesc[84] == 0x8c && rdesc[85] == 0x02) {
......@@ -278,7 +277,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
0, 0, 0, 0, 0,183,184,185,186,187,
188,189,190,191,192,193,194, 0, 0, 0
};
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
unsigned int hid = usage->hid;
if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
......@@ -319,7 +318,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
(field->flags & HID_MAIN_ITEM_RELATIVE))
......@@ -335,13 +334,16 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
static int lg_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
input_event(field->hidinput->input, usage->type, usage->code,
-value);
return 1;
}
if (drv_data->quirks & LG_FF4) {
return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
}
return 0;
}
......@@ -358,7 +360,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
return -ENOMEM;
}
drv_data->quirks = id->driver_data;
hid_set_drvdata(hdev, (void *)drv_data);
if (drv_data->quirks & LG_NOGET)
......@@ -380,7 +382,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
/* Setup wireless link with Logitech Wii wheel */
if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
......@@ -416,7 +418,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
static void lg_remove(struct hid_device *hdev)
{
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if (drv_data->quirks & LG_FF4)
lg4ff_deinit(hdev);
......@@ -476,7 +478,7 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 },
......
......@@ -25,9 +25,13 @@ static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
#endif
#ifdef CONFIG_LOGIWHEELS_FF
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
int lg4ff_init(struct hid_device *hdev);
int lg4ff_deinit(struct hid_device *hdev);
#else
static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
#endif
......
......@@ -43,6 +43,11 @@
#define G27_REV_MAJ 0x12
#define G27_REV_MIN 0x38
#define DFP_X_MIN 0
#define DFP_X_MAX 16383
#define DFP_PEDAL_MIN 0
#define DFP_PEDAL_MAX 255
#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
......@@ -53,6 +58,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
struct lg4ff_device_entry {
__u32 product_id;
__u16 range;
__u16 min_range;
__u16 max_range;
......@@ -129,26 +135,77 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {
{G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */
};
/* Recalculates X axis value accordingly to currently selected range */
static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
{
__u16 max_range;
__s32 new_value;
if (range == 900)
return value;
else if (range == 200)
return value;
else if (range < 200)
max_range = 200;
else
max_range = 900;
new_value = 8192 + mult_frac(value - 8192, max_range, range);
if (new_value < 0)
return 0;
else if (new_value > 16383)
return 16383;
else
return new_value;
}
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
{
struct lg4ff_device_entry *entry = drv_data->device_props;
__s32 new_value = 0;
if (!entry) {
hid_err(hid, "Device properties not found");
return 0;
}
switch (entry->product_id) {
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
switch (usage->code) {
case ABS_X:
new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
input_event(field->hidinput->input, usage->type, usage->code, new_value);
return 1;
default:
return 0;
}
default:
return 0;
}
}
static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
__s32 *value = report->field[0]->value;
int x;
#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
switch (effect->type) {
case FF_CONSTANT:
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
CLAMP(x);
report->field[0]->value[0] = 0x11; /* Slot 1 */
report->field[0]->value[1] = 0x08;
report->field[0]->value[2] = x;
report->field[0]->value[3] = 0x80;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
value[0] = 0x11; /* Slot 1 */
value[1] = 0x08;
value[2] = x;
value[3] = 0x80;
value[4] = 0x00;
value[5] = 0x00;
value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
break;
......@@ -163,14 +220,15 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
__s32 *value = report->field[0]->value;
report->field[0]->value[0] = 0xfe;
report->field[0]->value[1] = 0x0d;
report->field[0]->value[2] = magnitude >> 13;
report->field[0]->value[3] = magnitude >> 13;
report->field[0]->value[4] = magnitude >> 8;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
value[0] = 0xfe;
value[1] = 0x0d;
value[2] = magnitude >> 13;
value[3] = magnitude >> 13;
value[4] = magnitude >> 8;
value[5] = 0x00;
value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
......@@ -181,16 +239,16 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
__s32 *value = report->field[0]->value;
magnitude = magnitude * 90 / 65535;
report->field[0]->value[0] = 0xfe;
report->field[0]->value[1] = 0x03;
report->field[0]->value[2] = magnitude >> 14;
report->field[0]->value[3] = magnitude >> 14;
report->field[0]->value[4] = magnitude;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
value[0] = 0xfe;
value[1] = 0x03;
value[2] = magnitude >> 14;
value[3] = magnitude >> 14;
value[4] = magnitude;
value[5] = 0x00;
value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
......@@ -200,15 +258,17 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
__s32 *value = report->field[0]->value;
dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
report->field[0]->value[0] = 0xf8;
report->field[0]->value[1] = 0x81;
report->field[0]->value[2] = range & 0x00ff;
report->field[0]->value[3] = (range & 0xff00) >> 8;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
value[0] = 0xf8;
value[1] = 0x81;
value[2] = range & 0x00ff;
value[3] = (range & 0xff00) >> 8;
value[4] = 0x00;
value[5] = 0x00;
value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
......@@ -219,16 +279,18 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
int start_left, start_right, full_range;
__s32 *value = report->field[0]->value;
dbg_hid("Driving Force Pro: setting range to %u\n", range);
/* Prepare "coarse" limit command */
report->field[0]->value[0] = 0xf8;
report->field[0]->value[1] = 0x00; /* Set later */
report->field[0]->value[2] = 0x00;
report->field[0]->value[3] = 0x00;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
value[0] = 0xf8;
value[1] = 0x00; /* Set later */
value[2] = 0x00;
value[3] = 0x00;
value[4] = 0x00;
value[5] = 0x00;
value[6] = 0x00;
if (range > 200) {
report->field[0]->value[1] = 0x03;
......@@ -240,13 +302,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
usbhid_submit_report(hid, report, USB_DIR_OUT);
/* Prepare "fine" limit command */
report->field[0]->value[0] = 0x81;
report->field[0]->value[1] = 0x0b;
report->field[0]->value[2] = 0x00;
report->field[0]->value[3] = 0x00;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
value[0] = 0x81;
value[1] = 0x0b;
value[2] = 0x00;
value[3] = 0x00;
value[4] = 0x00;
value[5] = 0x00;
value[6] = 0x00;
if (range == 200 || range == 900) { /* Do not apply any fine limit */
usbhid_submit_report(hid, report, USB_DIR_OUT);
......@@ -257,11 +319,11 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
start_left = (((full_range - range + 1) * 2047) / full_range);
start_right = 0xfff - start_left;
report->field[0]->value[2] = start_left >> 4;
report->field[0]->value[3] = start_right >> 4;
report->field[0]->value[4] = 0xff;
report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
report->field[0]->value[6] = 0xff;
value[2] = start_left >> 4;
value[3] = start_right >> 4;
value[4] = 0xff;
value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
value[6] = 0xff;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
......@@ -344,14 +406,15 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
report->field[0]->value[0] = 0xf8;
report->field[0]->value[1] = 0x12;
report->field[0]->value[2] = leds;
report->field[0]->value[3] = 0x00;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
__s32 *value = report->field[0]->value;
value[0] = 0xf8;
value[1] = 0x12;
value[2] = leds;
value[3] = 0x00;
value[4] = 0x00;
value[5] = 0x00;
value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
......@@ -360,7 +423,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
{
struct device *dev = led_cdev->dev->parent;
struct hid_device *hid = container_of(dev, struct hid_device, dev);
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
struct lg_drv_data *drv_data = hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, state = 0;
......@@ -395,7 +458,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
{
struct device *dev = led_cdev->dev->parent;
struct hid_device *hid = container_of(dev, struct hid_device, dev);
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
struct lg_drv_data *drv_data = hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, value = 0;
......@@ -501,7 +564,7 @@ int lg4ff_init(struct hid_device *hid)
/* Check if autocentering is available and
* set the centering force to zero by default */
if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
else
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
......@@ -524,6 +587,7 @@ int lg4ff_init(struct hid_device *hid)
}
drv_data->device_props = entry;
entry->product_id = lg4ff_devices[i].product_id;
entry->min_range = lg4ff_devices[i].min_range;
entry->max_range = lg4ff_devices[i].max_range;
entry->set_range = lg4ff_devices[i].set_range;
......@@ -534,6 +598,18 @@ int lg4ff_init(struct hid_device *hid)
return error;
dbg_hid("sysfs interface created\n");
/* Set default axes parameters */
switch (lg4ff_devices[i].product_id) {
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
dbg_hid("Setting axes parameters for Driving Force Pro\n");
input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0);
input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
break;
default:
break;
}
/* Set the maximum range to start with */
entry->range = entry->max_range;
if (entry->set_range != NULL)
......@@ -594,6 +670,8 @@ int lg4ff_init(struct hid_device *hid)
return 0;
}
int lg4ff_deinit(struct hid_device *hid)
{
struct lg4ff_device_entry *entry;
......
......@@ -392,7 +392,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
__set_bit(EV_ABS, input->evbit);
error = input_mt_init_slots(input, 16);
error = input_mt_init_slots(input, 16, 0);
if (error)
return error;
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
......@@ -51,12 +51,12 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
#define MT_QUIRK_NO_AREA (1 << 9)
struct mt_slot {
__s32 x, y, p, w, h;
__s32 contactid; /* the device ContactID assigned to this slot */
bool touch_state; /* is the touch valid? */
bool seen_in_this_frame;/* has this slot been updated */
};
struct mt_class {
......@@ -92,8 +92,9 @@ struct mt_device {
__u8 touches_by_report; /* how many touches are present in one report:
* 1 means we should use a serial protocol
* > 1 means hybrid (multitouch) protocol */
bool serial_maybe; /* need to check for serial protocol */
bool curvalid; /* is the current contact valid? */
struct mt_slot *slots;
unsigned mt_flags; /* flags to pass to input-mt */
};
/* classes of device behavior */
......@@ -115,6 +116,9 @@ struct mt_device {
#define MT_CLS_EGALAX_SERIAL 0x0104
#define MT_CLS_TOPSEED 0x0105
#define MT_CLS_PANASONIC 0x0106
#define MT_CLS_FLATFROG 0x0107
#define MT_CLS_GENERALTOUCH_TWOFINGERS 0x0108
#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109
#define MT_DEFAULT_MAXCONTACT 10
......@@ -134,25 +138,6 @@ static int cypress_compute_slot(struct mt_device *td)
return -1;
}
static int find_slot_from_contactid(struct mt_device *td)
{
int i;
for (i = 0; i < td->maxcontacts; ++i) {
if (td->slots[i].contactid == td->curdata.contactid &&
td->slots[i].touch_state)
return i;
}
for (i = 0; i < td->maxcontacts; ++i) {
if (!td->slots[i].seen_in_this_frame &&
!td->slots[i].touch_state)
return i;
}
/* should not occurs. If this happens that means
* that the device sent more touches that it says
* in the report descriptor. It is ignored then. */
return -1;
}
static struct mt_class mt_classes[] = {
{ .name = MT_CLS_DEFAULT,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
......@@ -190,7 +175,9 @@ static struct mt_class mt_classes[] = {
MT_QUIRK_SLOT_IS_CONTACTID,
.sn_move = 2048,
.sn_width = 128,
.sn_height = 128 },
.sn_height = 128,
.maxcontacts = 60,
},
{ .name = MT_CLS_CYPRESS,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
MT_QUIRK_CYPRESS,
......@@ -215,7 +202,24 @@ static struct mt_class mt_classes[] = {
{ .name = MT_CLS_PANASONIC,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP,
.maxcontacts = 4 },
{ .name = MT_CLS_GENERALTOUCH_TWOFINGERS,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
MT_QUIRK_VALID_IS_INRANGE |
MT_QUIRK_SLOT_IS_CONTACTNUMBER,
.maxcontacts = 2
},
{ .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
MT_QUIRK_SLOT_IS_CONTACTNUMBER,
.maxcontacts = 10
},
{ .name = MT_CLS_FLATFROG,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
MT_QUIRK_NO_AREA,
.sn_move = 2048,
.maxcontacts = 40,
},
{ }
};
......@@ -319,24 +323,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* We need to ignore fields that belong to other collections
* such as Mouse that might have the same GenericDesktop usages. */
if (field->application == HID_DG_TOUCHSCREEN)
set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
td->mt_flags |= INPUT_MT_DIRECT;
else if (field->application != HID_DG_TOUCHPAD)
return 0;
/* In case of an indirect device (touchpad), we need to add
* specific BTN_TOOL_* to be handled by the synaptics xorg
* driver.
* We also consider that touchscreens providing buttons are touchpads.
/*
* Model touchscreens providing buttons as touchpads.
*/
if (field->application == HID_DG_TOUCHPAD ||
(usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON ||
cls->is_indirect) {
set_bit(INPUT_PROP_POINTER, hi->input->propbit);
set_bit(BTN_TOOL_FINGER, hi->input->keybit);
set_bit(BTN_TOOL_DOUBLETAP, hi->input->keybit);
set_bit(BTN_TOOL_TRIPLETAP, hi->input->keybit);
set_bit(BTN_TOOL_QUADTAP, hi->input->keybit);
}
(usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
td->mt_flags |= INPUT_MT_POINTER;
/* eGalax devices provide a Digitizer.Stylus input which overrides
* the correct Digitizers.Finger X/Y ranges.
......@@ -353,8 +349,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_POSITION_X);
set_abs(hi->input, ABS_MT_POSITION_X, field,
cls->sn_move);
/* touchscreen emulation */
set_abs(hi->input, ABS_X, field, cls->sn_move);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
......@@ -363,8 +357,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_POSITION_Y);
set_abs(hi->input, ABS_MT_POSITION_Y, field,
cls->sn_move);
/* touchscreen emulation */
set_abs(hi->input, ABS_Y, field, cls->sn_move);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
......@@ -388,9 +380,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
td->last_field_index = field->index;
return 1;
case HID_DG_CONTACTID:
if (!td->maxcontacts)
td->maxcontacts = MT_DEFAULT_MAXCONTACT;
input_mt_init_slots(hi->input, td->maxcontacts);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
td->touches_by_report++;
......@@ -398,18 +387,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MAJOR);
set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
cls->sn_width);
if (!(cls->quirks & MT_QUIRK_NO_AREA))
set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
cls->sn_width);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
case HID_DG_HEIGHT:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MINOR);
set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
cls->sn_height);
input_set_abs_params(hi->input,
if (!(cls->quirks & MT_QUIRK_NO_AREA)) {
set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
cls->sn_height);
input_set_abs_params(hi->input,
ABS_MT_ORIENTATION, 0, 1, 0, 0);
}
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
......@@ -418,9 +410,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_PRESSURE);
set_abs(hi->input, ABS_MT_PRESSURE, field,
cls->sn_pressure);
/* touchscreen emulation */
set_abs(hi->input, ABS_PRESSURE, field,
cls->sn_pressure);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
......@@ -464,7 +453,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
return -1;
}
static int mt_compute_slot(struct mt_device *td)
static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
{
__s32 quirks = td->mtclass.quirks;
......@@ -480,42 +469,23 @@ static int mt_compute_slot(struct mt_device *td)
if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE)
return td->curdata.contactid - 1;
return find_slot_from_contactid(td);
return input_mt_get_slot_by_key(input, td->curdata.contactid);
}
/*
* this function is called when a whole contact has been processed,
* so that it can assign it to a slot and store the data there
*/
static void mt_complete_slot(struct mt_device *td)
static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
{
td->curdata.seen_in_this_frame = true;
if (td->curvalid) {
int slotnum = mt_compute_slot(td);
if (slotnum >= 0 && slotnum < td->maxcontacts)
td->slots[slotnum] = td->curdata;
}
td->num_received++;
}
int slotnum = mt_compute_slot(td, input);
struct mt_slot *s = &td->curdata;
if (slotnum < 0 || slotnum >= td->maxcontacts)
return;
/*
* this function is called when a whole packet has been received and processed,
* so that it can decide what to send to the input layer.
*/
static void mt_emit_event(struct mt_device *td, struct input_dev *input)
{
int i;
for (i = 0; i < td->maxcontacts; ++i) {
struct mt_slot *s = &(td->slots[i]);
if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
!s->seen_in_this_frame) {
s->touch_state = false;
}
input_mt_slot(input, i);
input_mt_slot(input, slotnum);
input_mt_report_slot_state(input, MT_TOOL_FINGER,
s->touch_state);
if (s->touch_state) {
......@@ -532,24 +502,29 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input)
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
}
s->seen_in_this_frame = false;
}
input_mt_report_pointer_emulation(input, true);
td->num_received++;
}
/*
* this function is called when a whole packet has been received and processed,
* so that it can decide what to send to the input layer.
*/
static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
{
input_mt_sync_frame(input);
input_sync(input);
td->num_received = 0;
}
static int mt_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct mt_device *td = hid_get_drvdata(hid);
__s32 quirks = td->mtclass.quirks;
if (hid->claimed & HID_CLAIMED_INPUT && td->slots) {
if (hid->claimed & HID_CLAIMED_INPUT) {
switch (usage->hid) {
case HID_DG_INRANGE:
if (quirks & MT_QUIRK_ALWAYS_VALID)
......@@ -602,11 +577,11 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
}
if (usage->hid == td->last_slot_field)
mt_complete_slot(td);
mt_complete_slot(td, field->hidinput->input);
if (field->index == td->last_field_index
&& td->num_received >= td->num_expected)
mt_emit_event(td, field->hidinput->input);
mt_sync_frame(td, field->hidinput->input);
}
......@@ -685,18 +660,45 @@ static void mt_post_parse(struct mt_device *td)
}
}
static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct mt_device *td = hid_get_drvdata(hdev);
struct mt_class *cls = &td->mtclass;
struct input_dev *input = hi->input;
/* Only initialize slots for MT input devices */
if (!test_bit(ABS_MT_POSITION_X, input->absbit))
return;
if (!td->maxcontacts)
td->maxcontacts = MT_DEFAULT_MAXCONTACT;
mt_post_parse(td);
if (td->serial_maybe)
mt_post_parse_default_settings(td);
if (cls->is_indirect)
td->mt_flags |= INPUT_MT_POINTER;
if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
td->mt_flags |= INPUT_MT_DROP_UNUSED;
input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
td->mt_flags = 0;
}
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret, i;
struct mt_device *td;
struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
if (id) {
for (i = 0; mt_classes[i].name ; i++) {
if (id->driver_data == mt_classes[i].name) {
mtclass = &(mt_classes[i]);
break;
}
for (i = 0; mt_classes[i].name ; i++) {
if (id->driver_data == mt_classes[i].name) {
mtclass = &(mt_classes[i]);
break;
}
}
......@@ -722,6 +724,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto fail;
}
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
td->serial_maybe = true;
ret = hid_parse(hdev);
if (ret != 0)
goto fail;
......@@ -730,20 +735,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
goto fail;
mt_post_parse(td);
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
mt_post_parse_default_settings(td);
td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot),
GFP_KERNEL);
if (!td->slots) {
dev_err(&hdev->dev, "cannot allocate multitouch slots\n");
hid_hw_stop(hdev);
ret = -ENOMEM;
goto fail;
}
ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
mt_set_maxcontacts(hdev);
......@@ -767,6 +758,32 @@ static int mt_reset_resume(struct hid_device *hdev)
mt_set_input_mode(hdev);
return 0;
}
static int mt_resume(struct hid_device *hdev)
{
struct usb_interface *intf;
struct usb_host_interface *interface;
struct usb_device *dev;
if (hdev->bus != BUS_USB)
return 0;
intf = to_usb_interface(hdev->dev.parent);
interface = intf->cur_altsetting;
dev = hid_to_usb_dev(hdev);
/* Some Elan legacy devices require SET_IDLE to be set on resume.
* It should be safe to send it to other devices too.
* Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
HID_REQ_SET_IDLE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, interface->desc.bInterfaceNumber,
NULL, 0, USB_CTRL_SET_TIMEOUT);
return 0;
}
#endif
static void mt_remove(struct hid_device *hdev)
......@@ -774,7 +791,6 @@ static void mt_remove(struct hid_device *hdev)
struct mt_device *td = hid_get_drvdata(hdev);
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
hid_hw_stop(hdev);
kfree(td->slots);
kfree(td);
hid_set_drvdata(hdev, NULL);
}
......@@ -883,19 +899,39 @@ static const struct hid_device_id mt_devices[] = {
{ .driver_data = MT_CLS_EGALAX_SERIAL,
MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) },
{ .driver_data = MT_CLS_EGALAX_SERIAL,
MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7) },
{ .driver_data = MT_CLS_EGALAX_SERIAL,
MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
{ .driver_data = MT_CLS_EGALAX,
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) },
{ .driver_data = MT_CLS_EGALAX,
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) },
{ .driver_data = MT_CLS_EGALAX,
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) },
/* Elo TouchSystems IntelliTouch Plus panel */
{ .driver_data = MT_CLS_DUAL_NSMU_CONTACTID,
MT_USB_DEVICE(USB_VENDOR_ID_ELO,
USB_DEVICE_ID_ELO_TS2515) },
/* Flatfrog Panels */
{ .driver_data = MT_CLS_FLATFROG,
MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG,
USB_DEVICE_ID_MULTITOUCH_3200) },
/* GeneralTouch panel */
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
{ .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS,
MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
{ .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) },
/* Gametel game controller */
{ .driver_data = MT_CLS_DEFAULT,
......@@ -1087,11 +1123,13 @@ static struct hid_driver mt_driver = {
.remove = mt_remove,
.input_mapping = mt_input_mapping,
.input_mapped = mt_input_mapped,
.input_configured = mt_input_configured,
.feature_mapping = mt_feature_mapping,
.usage_table = mt_grabbed_usages,
.event = mt_event,
#ifdef CONFIG_PM
.reset_resume = mt_reset_resume,
.resume = mt_resume,
#endif
};
......
......@@ -882,10 +882,10 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
nd->activate_slack = activate_slack;
nd->act_state = activate_slack;
nd->deactivate_slack = -deactivate_slack;
nd->sensor_logical_width = 0;
nd->sensor_logical_height = 0;
nd->sensor_physical_width = 0;
nd->sensor_physical_height = 0;
nd->sensor_logical_width = 1;
nd->sensor_logical_height = 1;
nd->sensor_physical_width = 1;
nd->sensor_physical_height = 1;
hid_set_drvdata(hdev, nd);
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#define PICOLCD_NAME "PicoLCD (graphic)"
/* Report numbers */
#define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */
#define ERR_SUCCESS 0x00
#define ERR_PARAMETER_MISSING 0x01
#define ERR_DATA_MISSING 0x02
#define ERR_BLOCK_READ_ONLY 0x03
#define ERR_BLOCK_NOT_ERASABLE 0x04
#define ERR_BLOCK_TOO_BIG 0x05
#define ERR_SECTION_OVERFLOW 0x06
#define ERR_INVALID_CMD_LEN 0x07
#define ERR_INVALID_DATA_LEN 0x08
#define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */
#define REPORT_IR_DATA 0x21 /* LCD: IN[63] */
#define REPORT_EE_DATA 0x32 /* LCD: IN[63] */
#define REPORT_MEMORY 0x41 /* LCD: IN[63] */
#define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */
#define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */
#define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */
#define REPORT_RESET 0x93 /* LCD: OUT[2] */
#define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */
#define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */
#define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */
#define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */
#define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */
#define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */
#define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */
#define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */
#define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */
#define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */
#define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */
#define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */
#define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */
#define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */
#define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */
#define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */
#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */
#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */
/* Description of in-progress IO operation, used for operations
* that trigger response from device */
struct picolcd_pending {
struct hid_report *out_report;
struct hid_report *in_report;
struct completion ready;
int raw_size;
u8 raw_data[64];
};
#define PICOLCD_KEYS 17
/* Per device data structure */
struct picolcd_data {
struct hid_device *hdev;
#ifdef CONFIG_DEBUG_FS
struct dentry *debug_reset;
struct dentry *debug_eeprom;
struct dentry *debug_flash;
struct mutex mutex_flash;
int addr_sz;
#endif
u8 version[2];
unsigned short opmode_delay;
/* input stuff */
u8 pressed_keys[2];
struct input_dev *input_keys;
#ifdef CONFIG_HID_PICOLCD_CIR
struct rc_dev *rc_dev;
#endif
unsigned short keycode[PICOLCD_KEYS];
#ifdef CONFIG_HID_PICOLCD_FB
/* Framebuffer stuff */
struct fb_info *fb_info;
#endif /* CONFIG_HID_PICOLCD_FB */
#ifdef CONFIG_HID_PICOLCD_LCD
struct lcd_device *lcd;
u8 lcd_contrast;
#endif /* CONFIG_HID_PICOLCD_LCD */
#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
struct backlight_device *backlight;
u8 lcd_brightness;
u8 lcd_power;
#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
#ifdef CONFIG_HID_PICOLCD_LEDS
/* LED stuff */
u8 led_state;
struct led_classdev *led[8];
#endif /* CONFIG_HID_PICOLCD_LEDS */
/* Housekeeping stuff */
spinlock_t lock;
struct mutex mutex;
struct picolcd_pending *pending;
int status;
#define PICOLCD_BOOTLOADER 1
#define PICOLCD_FAILED 2
#define PICOLCD_CIR_SHUN 4
};
#ifdef CONFIG_HID_PICOLCD_FB
struct picolcd_fb_data {
/* Framebuffer stuff */
spinlock_t lock;
struct picolcd_data *picolcd;
u8 update_rate;
u8 bpp;
u8 force;
u8 ready;
u8 *vbitmap; /* local copy of what was sent to PicoLCD */
u8 *bitmap; /* framebuffer */
};
#endif /* CONFIG_HID_PICOLCD_FB */
/* Find a given report */
#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir);
#ifdef CONFIG_DEBUG_FS
void picolcd_debug_out_report(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report);
#define usbhid_submit_report(a, b, c) \
do { \
picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
usbhid_submit_report(a, b, c); \
} while (0)
void picolcd_debug_raw_event(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report,
u8 *raw_data, int size);
void picolcd_init_devfs(struct picolcd_data *data,
struct hid_report *eeprom_r, struct hid_report *eeprom_w,
struct hid_report *flash_r, struct hid_report *flash_w,
struct hid_report *reset);
void picolcd_exit_devfs(struct picolcd_data *data);
#else
static inline void picolcd_debug_out_report(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report)
{
}
static inline void picolcd_debug_raw_event(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report,
u8 *raw_data, int size)
{
}
static inline void picolcd_init_devfs(struct picolcd_data *data,
struct hid_report *eeprom_r, struct hid_report *eeprom_w,
struct hid_report *flash_r, struct hid_report *flash_w,
struct hid_report *reset)
{
}
static inline void picolcd_exit_devfs(struct picolcd_data *data)
{
}
#endif /* CONFIG_DEBUG_FS */
#ifdef CONFIG_HID_PICOLCD_FB
int picolcd_fb_reset(struct picolcd_data *data, int clear);
int picolcd_init_framebuffer(struct picolcd_data *data);
void picolcd_exit_framebuffer(struct picolcd_data *data);
void picolcd_fb_refresh(struct picolcd_data *data);
#define picolcd_fbinfo(d) ((d)->fb_info)
#else
static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
{
return 0;
}
static inline int picolcd_init_framebuffer(struct picolcd_data *data)
{
return 0;
}
static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
{
}
static inline void picolcd_fb_refresh(struct picolcd_data *data)
{
}
#define picolcd_fbinfo(d) NULL
#endif /* CONFIG_HID_PICOLCD_FB */
#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
int picolcd_init_backlight(struct picolcd_data *data,
struct hid_report *report);
void picolcd_exit_backlight(struct picolcd_data *data);
int picolcd_resume_backlight(struct picolcd_data *data);
void picolcd_suspend_backlight(struct picolcd_data *data);
#else
static inline int picolcd_init_backlight(struct picolcd_data *data,
struct hid_report *report)
{
return 0;
}
static inline void picolcd_exit_backlight(struct picolcd_data *data)
{
}
static inline int picolcd_resume_backlight(struct picolcd_data *data)
{
return 0;
}
static inline void picolcd_suspend_backlight(struct picolcd_data *data)
{
}
#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
#ifdef CONFIG_HID_PICOLCD_LCD
int picolcd_init_lcd(struct picolcd_data *data,
struct hid_report *report);
void picolcd_exit_lcd(struct picolcd_data *data);
int picolcd_resume_lcd(struct picolcd_data *data);
#else
static inline int picolcd_init_lcd(struct picolcd_data *data,
struct hid_report *report)
{
return 0;
}
static inline void picolcd_exit_lcd(struct picolcd_data *data)
{
}
static inline int picolcd_resume_lcd(struct picolcd_data *data)
{
return 0;
}
#endif /* CONFIG_HID_PICOLCD_LCD */
#ifdef CONFIG_HID_PICOLCD_LEDS
int picolcd_init_leds(struct picolcd_data *data,
struct hid_report *report);
void picolcd_exit_leds(struct picolcd_data *data);
void picolcd_leds_set(struct picolcd_data *data);
#else
static inline int picolcd_init_leds(struct picolcd_data *data,
struct hid_report *report)
{
return 0;
}
static inline void picolcd_exit_leds(struct picolcd_data *data)
{
}
static inline void picolcd_leds_set(struct picolcd_data *data)
{
}
#endif /* CONFIG_HID_PICOLCD_LEDS */
#ifdef CONFIG_HID_PICOLCD_CIR
int picolcd_raw_cir(struct picolcd_data *data,
struct hid_report *report, u8 *raw_data, int size);
int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report);
void picolcd_exit_cir(struct picolcd_data *data);
#else
static inline int picolcd_raw_cir(struct picolcd_data *data,
struct hid_report *report, u8 *raw_data, int size)
{
return 1;
}
static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
{
return 0;
}
static inline void picolcd_exit_cir(struct picolcd_data *data)
{
}
#endif /* CONFIG_HID_PICOLCD_LIRC */
int picolcd_reset(struct hid_device *hdev);
struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
int report_id, const u8 *raw_data, int size);
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <linux/hid.h>
#include "usbhid/usbhid.h"
#include <linux/usb.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include "hid-picolcd.h"
static int picolcd_get_brightness(struct backlight_device *bdev)
{
struct picolcd_data *data = bl_get_data(bdev);
return data->lcd_brightness;
}
static int picolcd_set_brightness(struct backlight_device *bdev)
{
struct picolcd_data *data = bl_get_data(bdev);
struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
unsigned long flags;
if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
return -ENODEV;
data->lcd_brightness = bdev->props.brightness & 0x0ff;
data->lcd_power = bdev->props.power;
spin_lock_irqsave(&data->lock, flags);
hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
if (!(data->status & PICOLCD_FAILED))
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
{
return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
}
static const struct backlight_ops picolcd_blops = {
.update_status = picolcd_set_brightness,
.get_brightness = picolcd_get_brightness,
.check_fb = picolcd_check_bl_fb,
};
int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
{
struct device *dev = &data->hdev->dev;
struct backlight_device *bdev;
struct backlight_properties props;
if (!report)
return -ENODEV;
if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
report->field[0]->report_size != 8) {
dev_err(dev, "unsupported BRIGHTNESS report");
return -EINVAL;
}
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW;
props.max_brightness = 0xff;
bdev = backlight_device_register(dev_name(dev), dev, data,
&picolcd_blops, &props);
if (IS_ERR(bdev)) {
dev_err(dev, "failed to register backlight\n");
return PTR_ERR(bdev);
}
bdev->props.brightness = 0xff;
data->lcd_brightness = 0xff;
data->backlight = bdev;
picolcd_set_brightness(bdev);
return 0;
}
void picolcd_exit_backlight(struct picolcd_data *data)
{
struct backlight_device *bdev = data->backlight;
data->backlight = NULL;
if (bdev)
backlight_device_unregister(bdev);
}
int picolcd_resume_backlight(struct picolcd_data *data)
{
if (!data->backlight)
return 0;
return picolcd_set_brightness(data->backlight);
}
#ifdef CONFIG_PM
void picolcd_suspend_backlight(struct picolcd_data *data)
{
int bl_power = data->lcd_power;
if (!data->backlight)
return;
data->backlight->props.power = FB_BLANK_POWERDOWN;
picolcd_set_brightness(data->backlight);
data->lcd_power = data->backlight->props.power = bl_power;
}
#endif /* CONFIG_PM */
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <linux/hid.h>
#include <linux/hid-debug.h>
#include <linux/input.h>
#include "hid-ids.h"
#include "usbhid/usbhid.h"
#include <linux/usb.h>
#include <linux/fb.h>
#include <linux/vmalloc.h>
#include <linux/backlight.h>
#include <linux/lcd.h>
#include <linux/leds.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/completion.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <media/rc-core.h>
#include "hid-picolcd.h"
int picolcd_raw_cir(struct picolcd_data *data,
struct hid_report *report, u8 *raw_data, int size)
{
unsigned long flags;
int i, w, sz;
DEFINE_IR_RAW_EVENT(rawir);
/* ignore if rc_dev is NULL or status is shunned */
spin_lock_irqsave(&data->lock, flags);
if (!data->rc_dev || (data->status & PICOLCD_CIR_SHUN)) {
spin_unlock_irqrestore(&data->lock, flags);
return 1;
}
spin_unlock_irqrestore(&data->lock, flags);
/* PicoLCD USB packets contain 16-bit intervals in network order,
* with value negated for pulse. Intervals are in microseconds.
*
* Note: some userspace LIRC code for PicoLCD says negated values
* for space - is it a matter of IR chip? (pulse for my TSOP2236)
*
* In addition, the first interval seems to be around 15000 + base
* interval for non-first report of IR data - thus the quirk below
* to get RC_CODE to understand Sony and JVC remotes I have at hand
*/
sz = size > 0 ? min((int)raw_data[0], size-1) : 0;
for (i = 0; i+1 < sz; i += 2) {
init_ir_raw_event(&rawir);
w = (raw_data[i] << 8) | (raw_data[i+1]);
rawir.pulse = !!(w & 0x8000);
rawir.duration = US_TO_NS(rawir.pulse ? (65536 - w) : w);
/* Quirk!! - see above */
if (i == 0 && rawir.duration > 15000000)
rawir.duration -= 15000000;
ir_raw_event_store(data->rc_dev, &rawir);
}
ir_raw_event_handle(data->rc_dev);
return 1;
}
static int picolcd_cir_open(struct rc_dev *dev)
{
struct picolcd_data *data = dev->priv;
unsigned long flags;
spin_lock_irqsave(&data->lock, flags);
data->status &= ~PICOLCD_CIR_SHUN;
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static void picolcd_cir_close(struct rc_dev *dev)
{
struct picolcd_data *data = dev->priv;
unsigned long flags;
spin_lock_irqsave(&data->lock, flags);
data->status |= PICOLCD_CIR_SHUN;
spin_unlock_irqrestore(&data->lock, flags);
}
/* initialize CIR input device */
int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
{
struct rc_dev *rdev;
int ret = 0;
rdev = rc_allocate_device();
if (!rdev)
return -ENOMEM;
rdev->priv = data;
rdev->driver_type = RC_DRIVER_IR_RAW;
rdev->allowed_protos = RC_TYPE_ALL;
rdev->open = picolcd_cir_open;
rdev->close = picolcd_cir_close;
rdev->input_name = data->hdev->name;
rdev->input_phys = data->hdev->phys;
rdev->input_id.bustype = data->hdev->bus;
rdev->input_id.vendor = data->hdev->vendor;
rdev->input_id.product = data->hdev->product;
rdev->input_id.version = data->hdev->version;
rdev->dev.parent = &data->hdev->dev;
rdev->driver_name = PICOLCD_NAME;
rdev->map_name = RC_MAP_RC6_MCE;
rdev->timeout = MS_TO_NS(100);
rdev->rx_resolution = US_TO_NS(1);
ret = rc_register_device(rdev);
if (ret)
goto err;
data->rc_dev = rdev;
return 0;
err:
rc_free_device(rdev);
return ret;
}
void picolcd_exit_cir(struct picolcd_data *data)
{
struct rc_dev *rdev = data->rc_dev;
data->rc_dev = NULL;
rc_unregister_device(rdev);
}
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <linux/hid.h>
#include <linux/hid-debug.h>
#include <linux/input.h>
#include "hid-ids.h"
#include "usbhid/usbhid.h"
#include <linux/usb.h>
#include <linux/fb.h>
#include <linux/vmalloc.h>
#include <linux/completion.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include "hid-picolcd.h"
/* Input device
*
* The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
* and header for 4x4 key matrix. The built-in keys are part of the matrix.
*/
static const unsigned short def_keymap[PICOLCD_KEYS] = {
KEY_RESERVED, /* none */
KEY_BACK, /* col 4 + row 1 */
KEY_HOMEPAGE, /* col 3 + row 1 */
KEY_RESERVED, /* col 2 + row 1 */
KEY_RESERVED, /* col 1 + row 1 */
KEY_SCROLLUP, /* col 4 + row 2 */
KEY_OK, /* col 3 + row 2 */
KEY_SCROLLDOWN, /* col 2 + row 2 */
KEY_RESERVED, /* col 1 + row 2 */
KEY_RESERVED, /* col 4 + row 3 */
KEY_RESERVED, /* col 3 + row 3 */
KEY_RESERVED, /* col 2 + row 3 */
KEY_RESERVED, /* col 1 + row 3 */
KEY_RESERVED, /* col 4 + row 4 */
KEY_RESERVED, /* col 3 + row 4 */
KEY_RESERVED, /* col 2 + row 4 */
KEY_RESERVED, /* col 1 + row 4 */
};
/* Find a given report */
struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
{
struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
struct hid_report *report = NULL;
list_for_each_entry(report, feature_report_list, list) {
if (report->id == id)
return report;
}
hid_warn(hdev, "No report with id 0x%x found\n", id);
return NULL;
}
/* Submit a report and wait for a reply from device - if device fades away
* or does not respond in time, return NULL */
struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
int report_id, const u8 *raw_data, int size)
{
struct picolcd_data *data = hid_get_drvdata(hdev);
struct picolcd_pending *work;
struct hid_report *report = picolcd_out_report(report_id, hdev);
unsigned long flags;
int i, j, k;
if (!report || !data)
return NULL;
if (data->status & PICOLCD_FAILED)
return NULL;
work = kzalloc(sizeof(*work), GFP_KERNEL);
if (!work)
return NULL;
init_completion(&work->ready);
work->out_report = report;
work->in_report = NULL;
work->raw_size = 0;
mutex_lock(&data->mutex);
spin_lock_irqsave(&data->lock, flags);
for (i = k = 0; i < report->maxfield; i++)
for (j = 0; j < report->field[i]->report_count; j++) {
hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
k++;
}
if (data->status & PICOLCD_FAILED) {
kfree(work);
work = NULL;
} else {
data->pending = work;
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
spin_unlock_irqrestore(&data->lock, flags);
wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
spin_lock_irqsave(&data->lock, flags);
data->pending = NULL;
}
spin_unlock_irqrestore(&data->lock, flags);
mutex_unlock(&data->mutex);
return work;
}
/*
* input class device
*/
static int picolcd_raw_keypad(struct picolcd_data *data,
struct hid_report *report, u8 *raw_data, int size)
{
/*
* Keypad event
* First and second data bytes list currently pressed keys,
* 0x00 means no key and at most 2 keys may be pressed at same time
*/
int i, j;
/* determine newly pressed keys */
for (i = 0; i < size; i++) {
unsigned int key_code;
if (raw_data[i] == 0)
continue;
for (j = 0; j < sizeof(data->pressed_keys); j++)
if (data->pressed_keys[j] == raw_data[i])
goto key_already_down;
for (j = 0; j < sizeof(data->pressed_keys); j++)
if (data->pressed_keys[j] == 0) {
data->pressed_keys[j] = raw_data[i];
break;
}
input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
if (raw_data[i] < PICOLCD_KEYS)
key_code = data->keycode[raw_data[i]];
else
key_code = KEY_UNKNOWN;
if (key_code != KEY_UNKNOWN) {
dbg_hid(PICOLCD_NAME " got key press for %u:%d",
raw_data[i], key_code);
input_report_key(data->input_keys, key_code, 1);
}
input_sync(data->input_keys);
key_already_down:
continue;
}
/* determine newly released keys */
for (j = 0; j < sizeof(data->pressed_keys); j++) {
unsigned int key_code;
if (data->pressed_keys[j] == 0)
continue;
for (i = 0; i < size; i++)
if (data->pressed_keys[j] == raw_data[i])
goto key_still_down;
input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
if (data->pressed_keys[j] < PICOLCD_KEYS)
key_code = data->keycode[data->pressed_keys[j]];
else
key_code = KEY_UNKNOWN;
if (key_code != KEY_UNKNOWN) {
dbg_hid(PICOLCD_NAME " got key release for %u:%d",
data->pressed_keys[j], key_code);
input_report_key(data->input_keys, key_code, 0);
}
input_sync(data->input_keys);
data->pressed_keys[j] = 0;
key_still_down:
continue;
}
return 1;
}
static int picolcd_check_version(struct hid_device *hdev)
{
struct picolcd_data *data = hid_get_drvdata(hdev);
struct picolcd_pending *verinfo;
int ret = 0;
if (!data)
return -ENODEV;
verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
if (!verinfo) {
hid_err(hdev, "no version response from PicoLCD\n");
return -ENODEV;
}
if (verinfo->raw_size == 2) {
data->version[0] = verinfo->raw_data[1];
data->version[1] = verinfo->raw_data[0];
if (data->status & PICOLCD_BOOTLOADER) {
hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
verinfo->raw_data[1], verinfo->raw_data[0]);
} else {
hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
verinfo->raw_data[1], verinfo->raw_data[0]);
}
} else {
hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
ret = -EINVAL;
}
kfree(verinfo);
return ret;
}
/*
* Reset our device and wait for answer to VERSION request
*/
int picolcd_reset(struct hid_device *hdev)
{
struct picolcd_data *data = hid_get_drvdata(hdev);
struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
unsigned long flags;
int error;
if (!data || !report || report->maxfield != 1)
return -ENODEV;
spin_lock_irqsave(&data->lock, flags);
if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
data->status |= PICOLCD_BOOTLOADER;
/* perform the reset */
hid_set_field(report->field[0], 0, 1);
if (data->status & PICOLCD_FAILED) {
spin_unlock_irqrestore(&data->lock, flags);
return -ENODEV;
}
usbhid_submit_report(hdev, report, USB_DIR_OUT);
spin_unlock_irqrestore(&data->lock, flags);
error = picolcd_check_version(hdev);
if (error)
return error;
picolcd_resume_lcd(data);
picolcd_resume_backlight(data);
picolcd_fb_refresh(data);
picolcd_leds_set(data);
return 0;
}
/*
* The "operation_mode" sysfs attribute
*/
static ssize_t picolcd_operation_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct picolcd_data *data = dev_get_drvdata(dev);
if (data->status & PICOLCD_BOOTLOADER)
return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
else
return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
}
static ssize_t picolcd_operation_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct picolcd_data *data = dev_get_drvdata(dev);
struct hid_report *report = NULL;
size_t cnt = count;
int timeout = data->opmode_delay;
unsigned long flags;
if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
if (data->status & PICOLCD_BOOTLOADER)
report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
buf += 3;
cnt -= 3;
} else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
if (!(data->status & PICOLCD_BOOTLOADER))
report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
buf += 10;
cnt -= 10;
}
if (!report)
return -EINVAL;
while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
cnt--;
if (cnt != 0)
return -EINVAL;
spin_lock_irqsave(&data->lock, flags);
hid_set_field(report->field[0], 0, timeout & 0xff);
hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
spin_unlock_irqrestore(&data->lock, flags);
return count;
}
static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
picolcd_operation_mode_store);
/*
* The "operation_mode_delay" sysfs attribute
*/
static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct picolcd_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
}
static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct picolcd_data *data = dev_get_drvdata(dev);
unsigned u;
if (sscanf(buf, "%u", &u) != 1)
return -EINVAL;
if (u > 30000)
return -EINVAL;
else
data->opmode_delay = u;
return count;
}
static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
picolcd_operation_mode_delay_store);
/*
* Handle raw report as sent by device
*/
static int picolcd_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *raw_data, int size)
{
struct picolcd_data *data = hid_get_drvdata(hdev);
unsigned long flags;
int ret = 0;
if (!data)
return 1;
if (report->id == REPORT_KEY_STATE) {
if (data->input_keys)
ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
} else if (report->id == REPORT_IR_DATA) {
ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
} else {
spin_lock_irqsave(&data->lock, flags);
/*
* We let the caller of picolcd_send_and_wait() check if the
* report we got is one of the expected ones or not.
*/
if (data->pending) {
memcpy(data->pending->raw_data, raw_data+1, size-1);
data->pending->raw_size = size-1;
data->pending->in_report = report;
complete(&data->pending->ready);
}
spin_unlock_irqrestore(&data->lock, flags);
}
picolcd_debug_raw_event(data, hdev, report, raw_data, size);
return 1;
}
#ifdef CONFIG_PM
static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
{
if (PMSG_IS_AUTO(message))
return 0;
picolcd_suspend_backlight(hid_get_drvdata(hdev));
dbg_hid(PICOLCD_NAME " device ready for suspend\n");
return 0;
}
static int picolcd_resume(struct hid_device *hdev)
{
int ret;
ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
if (ret)
dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
return 0;
}
static int picolcd_reset_resume(struct hid_device *hdev)
{
int ret;
ret = picolcd_reset(hdev);
if (ret)
dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
if (ret)
dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
if (ret)
dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
if (ret)
dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
picolcd_leds_set(hid_get_drvdata(hdev));
return 0;
}
#endif
/* initialize keypad input device */
static int picolcd_init_keys(struct picolcd_data *data,
struct hid_report *report)
{
struct hid_device *hdev = data->hdev;
struct input_dev *idev;
int error, i;
if (!report)
return -ENODEV;
if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
report->field[0]->report_size != 8) {
hid_err(hdev, "unsupported KEY_STATE report\n");
return -EINVAL;
}
idev = input_allocate_device();
if (idev == NULL) {
hid_err(hdev, "failed to allocate input device\n");
return -ENOMEM;
}
input_set_drvdata(idev, hdev);
memcpy(data->keycode, def_keymap, sizeof(def_keymap));
idev->name = hdev->name;
idev->phys = hdev->phys;
idev->uniq = hdev->uniq;
idev->id.bustype = hdev->bus;
idev->id.vendor = hdev->vendor;
idev->id.product = hdev->product;
idev->id.version = hdev->version;
idev->dev.parent = &hdev->dev;
idev->keycode = &data->keycode;
idev->keycodemax = PICOLCD_KEYS;
idev->keycodesize = sizeof(data->keycode[0]);
input_set_capability(idev, EV_MSC, MSC_SCAN);
set_bit(EV_REP, idev->evbit);
for (i = 0; i < PICOLCD_KEYS; i++)
input_set_capability(idev, EV_KEY, data->keycode[i]);
error = input_register_device(idev);
if (error) {
hid_err(hdev, "error registering the input device\n");
input_free_device(idev);
return error;
}
data->input_keys = idev;
return 0;
}
static void picolcd_exit_keys(struct picolcd_data *data)
{
struct input_dev *idev = data->input_keys;
data->input_keys = NULL;
if (idev)
input_unregister_device(idev);
}
static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
{
int error;
/* Setup keypad input device */
error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
if (error)
goto err;
/* Setup CIR input device */
error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
if (error)
goto err;
/* Set up the framebuffer device */
error = picolcd_init_framebuffer(data);
if (error)
goto err;
/* Setup lcd class device */
error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
if (error)
goto err;
/* Setup backlight class device */
error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
if (error)
goto err;
/* Setup the LED class devices */
error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
if (error)
goto err;
picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
picolcd_out_report(REPORT_EE_WRITE, hdev),
picolcd_out_report(REPORT_READ_MEMORY, hdev),
picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
picolcd_out_report(REPORT_RESET, hdev));
return 0;
err:
picolcd_exit_leds(data);
picolcd_exit_backlight(data);
picolcd_exit_lcd(data);
picolcd_exit_framebuffer(data);
picolcd_exit_cir(data);
picolcd_exit_keys(data);
return error;
}
static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
{
picolcd_init_devfs(data, NULL, NULL,
picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
return 0;
}
static int picolcd_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
struct picolcd_data *data;
int error = -ENOMEM;
dbg_hid(PICOLCD_NAME " hardware probe...\n");
/*
* Let's allocate the picolcd data structure, set some reasonable
* defaults, and associate it with the device
*/
data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
if (data == NULL) {
hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
error = -ENOMEM;
goto err_no_cleanup;
}
spin_lock_init(&data->lock);
mutex_init(&data->mutex);
data->hdev = hdev;
data->opmode_delay = 5000;
if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
data->status |= PICOLCD_BOOTLOADER;
hid_set_drvdata(hdev, data);
/* Parse the device reports and start it up */
error = hid_parse(hdev);
if (error) {
hid_err(hdev, "device report parse failed\n");
goto err_cleanup_data;
}
error = hid_hw_start(hdev, 0);
if (error) {
hid_err(hdev, "hardware start failed\n");
goto err_cleanup_data;
}
error = hid_hw_open(hdev);
if (error) {
hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
goto err_cleanup_hid_hw;
}
error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
if (error) {
hid_err(hdev, "failed to create sysfs attributes\n");
goto err_cleanup_hid_ll;
}
error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
if (error) {
hid_err(hdev, "failed to create sysfs attributes\n");
goto err_cleanup_sysfs1;
}
if (data->status & PICOLCD_BOOTLOADER)
error = picolcd_probe_bootloader(hdev, data);
else
error = picolcd_probe_lcd(hdev, data);
if (error)
goto err_cleanup_sysfs2;
dbg_hid(PICOLCD_NAME " activated and initialized\n");
return 0;
err_cleanup_sysfs2:
device_remove_file(&hdev->dev, &dev_attr_operation_mode);
err_cleanup_sysfs1:
device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
err_cleanup_hid_ll:
hid_hw_close(hdev);
err_cleanup_hid_hw:
hid_hw_stop(hdev);
err_cleanup_data:
kfree(data);
err_no_cleanup:
hid_set_drvdata(hdev, NULL);
return error;
}
static void picolcd_remove(struct hid_device *hdev)
{
struct picolcd_data *data = hid_get_drvdata(hdev);
unsigned long flags;
dbg_hid(PICOLCD_NAME " hardware remove...\n");
spin_lock_irqsave(&data->lock, flags);
data->status |= PICOLCD_FAILED;
spin_unlock_irqrestore(&data->lock, flags);
picolcd_exit_devfs(data);
device_remove_file(&hdev->dev, &dev_attr_operation_mode);
device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
hid_hw_close(hdev);
hid_hw_stop(hdev);
/* Shortcut potential pending reply that will never arrive */
spin_lock_irqsave(&data->lock, flags);
if (data->pending)
complete(&data->pending->ready);
spin_unlock_irqrestore(&data->lock, flags);
/* Cleanup LED */
picolcd_exit_leds(data);
/* Clean up the framebuffer */
picolcd_exit_backlight(data);
picolcd_exit_lcd(data);
picolcd_exit_framebuffer(data);
/* Cleanup input */
picolcd_exit_cir(data);
picolcd_exit_keys(data);
hid_set_drvdata(hdev, NULL);
mutex_destroy(&data->mutex);
/* Finally, clean up the picolcd data itself */
kfree(data);
}
static const struct hid_device_id picolcd_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
{ }
};
MODULE_DEVICE_TABLE(hid, picolcd_devices);
static struct hid_driver picolcd_driver = {
.name = "hid-picolcd",
.id_table = picolcd_devices,
.probe = picolcd_probe,
.remove = picolcd_remove,
.raw_event = picolcd_raw_event,
#ifdef CONFIG_PM
.suspend = picolcd_suspend,
.resume = picolcd_resume,
.reset_resume = picolcd_reset_resume,
#endif
};
static int __init picolcd_init(void)
{
return hid_register_driver(&picolcd_driver);
}
static void __exit picolcd_exit(void)
{
hid_unregister_driver(&picolcd_driver);
}
module_init(picolcd_init);
module_exit(picolcd_exit);
MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
MODULE_LICENSE("GPL v2");
此差异已折叠。
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <linux/hid.h>
#include "usbhid/usbhid.h"
#include <linux/usb.h>
#include <linux/fb.h>
#include <linux/lcd.h>
#include "hid-picolcd.h"
/*
* lcd class device
*/
static int picolcd_get_contrast(struct lcd_device *ldev)
{
struct picolcd_data *data = lcd_get_data(ldev);
return data->lcd_contrast;
}
static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
{
struct picolcd_data *data = lcd_get_data(ldev);
struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
unsigned long flags;
if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
return -ENODEV;
data->lcd_contrast = contrast & 0x0ff;
spin_lock_irqsave(&data->lock, flags);
hid_set_field(report->field[0], 0, data->lcd_contrast);
if (!(data->status & PICOLCD_FAILED))
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
{
return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
}
static struct lcd_ops picolcd_lcdops = {
.get_contrast = picolcd_get_contrast,
.set_contrast = picolcd_set_contrast,
.check_fb = picolcd_check_lcd_fb,
};
int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
{
struct device *dev = &data->hdev->dev;
struct lcd_device *ldev;
if (!report)
return -ENODEV;
if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
report->field[0]->report_size != 8) {
dev_err(dev, "unsupported CONTRAST report");
return -EINVAL;
}
ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
if (IS_ERR(ldev)) {
dev_err(dev, "failed to register LCD\n");
return PTR_ERR(ldev);
}
ldev->props.max_contrast = 0x0ff;
data->lcd_contrast = 0xe5;
data->lcd = ldev;
picolcd_set_contrast(ldev, 0xe5);
return 0;
}
void picolcd_exit_lcd(struct picolcd_data *data)
{
struct lcd_device *ldev = data->lcd;
data->lcd = NULL;
if (ldev)
lcd_device_unregister(ldev);
}
int picolcd_resume_lcd(struct picolcd_data *data)
{
if (!data->lcd)
return 0;
return picolcd_set_contrast(data->lcd, data->lcd_contrast);
}
此差异已折叠。
......@@ -64,29 +64,6 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
return 0;
}
static int px_probe(struct hid_device *hid, const struct hid_device_id *id)
{
int ret;
ret = hid_parse(hid);
if (ret) {
hid_err(hid, "parse failed\n");
goto fail;
}
ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
if (ret)
hid_err(hid, "hw start failed\n");
fail:
return ret;
}
static void px_remove(struct hid_device *hid)
{
hid_hw_stop(hid);
}
static const struct hid_device_id px_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
{ }
......@@ -97,8 +74,6 @@ static struct hid_driver px_driver = {
.name = "primax",
.id_table = px_devices,
.raw_event = px_raw_event,
.probe = px_probe,
.remove = px_remove,
};
static int __init px_init(void)
......
此差异已折叠。
此差异已折叠。
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
*
......
......@@ -4,7 +4,6 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2006-2008 Jiri Kosina
*/
......
......@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -405,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
goto exit;
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
input_mt_init_slots(dev, nslot);
input_mt_init_slots(dev, nslot, 0);
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
input_set_events_per_packet(dev, 60);
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册