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 ...
This commit is contained in:
commit
9fa40a1135
|
@ -1,3 +1,16 @@
|
|||
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;
|
||||
|
||||
if (device->driver->report_fixup)
|
||||
start = device->driver->report_fixup(device, start, &size);
|
||||
|
||||
device->rdesc = kmemdup(start, size, GFP_KERNEL);
|
||||
if (device->rdesc == NULL)
|
||||
buf = kmemdup(start, size, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (device->driver->report_fixup)
|
||||
start = device->driver->report_fixup(device, buf, &size);
|
||||
else
|
||||
start = buf;
|
||||
|
||||
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 @@ err_free:
|
|||
|
||||
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 @@ err_free:
|
|||
|
||||
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);
|
||||
__s32 *value = report->field[0]->value;
|
||||
|
||||
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;
|
||||
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 @@ out:
|
|||
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);
|
||||
int slotnum = mt_compute_slot(td, input);
|
||||
struct mt_slot *s = &td->curdata;
|
||||
|
||||
if (slotnum >= 0 && slotnum < td->maxcontacts)
|
||||
td->slots[slotnum] = td->curdata;
|
||||
}
|
||||
td->num_received++;
|
||||
}
|
||||
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
|
||||
*/
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,309 @@
|
|||
/***************************************************************************
|
||||
* 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);
|
|
@ -0,0 +1,122 @@
|
|||
/***************************************************************************
|
||||
* 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 */
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
/***************************************************************************
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,689 @@
|
|||
/***************************************************************************
|
||||
* 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");
|
|
@ -0,0 +1,899 @@
|
|||
/***************************************************************************
|
||||
* 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 "usbhid/usbhid.h"
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "hid-picolcd.h"
|
||||
|
||||
|
||||
static int picolcd_debug_reset_show(struct seq_file *f, void *p)
|
||||
{
|
||||
if (picolcd_fbinfo((struct picolcd_data *)f->private))
|
||||
seq_printf(f, "all fb\n");
|
||||
else
|
||||
seq_printf(f, "all\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
|
||||
{
|
||||
return single_open(f, picolcd_debug_reset_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
|
||||
char buf[32];
|
||||
size_t cnt = min(count, sizeof(buf)-1);
|
||||
if (copy_from_user(buf, user_buf, cnt))
|
||||
return -EFAULT;
|
||||
|
||||
while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
|
||||
cnt--;
|
||||
buf[cnt] = '\0';
|
||||
if (strcmp(buf, "all") == 0) {
|
||||
picolcd_reset(data->hdev);
|
||||
picolcd_fb_reset(data, 1);
|
||||
} else if (strcmp(buf, "fb") == 0) {
|
||||
picolcd_fb_reset(data, 1);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations picolcd_debug_reset_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = picolcd_debug_reset_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.write = picolcd_debug_reset_write,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* The "eeprom" file
|
||||
*/
|
||||
static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
|
||||
size_t s, loff_t *off)
|
||||
{
|
||||
struct picolcd_data *data = f->private_data;
|
||||
struct picolcd_pending *resp;
|
||||
u8 raw_data[3];
|
||||
ssize_t ret = -EIO;
|
||||
|
||||
if (s == 0)
|
||||
return -EINVAL;
|
||||
if (*off > 0x0ff)
|
||||
return 0;
|
||||
|
||||
/* prepare buffer with info about what we want to read (addr & len) */
|
||||
raw_data[0] = *off & 0xff;
|
||||
raw_data[1] = (*off >> 8) & 0xff;
|
||||
raw_data[2] = s < 20 ? s : 20;
|
||||
if (*off + raw_data[2] > 0xff)
|
||||
raw_data[2] = 0x100 - *off;
|
||||
resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
|
||||
sizeof(raw_data));
|
||||
if (!resp)
|
||||
return -EIO;
|
||||
|
||||
if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
|
||||
/* successful read :) */
|
||||
ret = resp->raw_data[2];
|
||||
if (ret > s)
|
||||
ret = s;
|
||||
if (copy_to_user(u, resp->raw_data+3, ret))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
*off += ret;
|
||||
} /* anything else is some kind of IO error */
|
||||
|
||||
kfree(resp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
|
||||
size_t s, loff_t *off)
|
||||
{
|
||||
struct picolcd_data *data = f->private_data;
|
||||
struct picolcd_pending *resp;
|
||||
ssize_t ret = -EIO;
|
||||
u8 raw_data[23];
|
||||
|
||||
if (s == 0)
|
||||
return -EINVAL;
|
||||
if (*off > 0x0ff)
|
||||
return -ENOSPC;
|
||||
|
||||
memset(raw_data, 0, sizeof(raw_data));
|
||||
raw_data[0] = *off & 0xff;
|
||||
raw_data[1] = (*off >> 8) & 0xff;
|
||||
raw_data[2] = min_t(size_t, 20, s);
|
||||
if (*off + raw_data[2] > 0xff)
|
||||
raw_data[2] = 0x100 - *off;
|
||||
|
||||
if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
|
||||
return -EFAULT;
|
||||
resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
|
||||
sizeof(raw_data));
|
||||
|
||||
if (!resp)
|
||||
return -EIO;
|
||||
|
||||
if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
|
||||
/* check if written data matches */
|
||||
if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
|
||||
*off += raw_data[2];
|
||||
ret = raw_data[2];
|
||||
}
|
||||
}
|
||||
kfree(resp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* - read/write happens in chunks of at most 20 bytes, it's up to userspace
|
||||
* to loop in order to get more data.
|
||||
* - on write errors on otherwise correct write request the bytes
|
||||
* that should have been written are in undefined state.
|
||||
*/
|
||||
static const struct file_operations picolcd_debug_eeprom_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = picolcd_debug_eeprom_read,
|
||||
.write = picolcd_debug_eeprom_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
/*
|
||||
* The "flash" file
|
||||
*/
|
||||
/* record a flash address to buf (bounds check to be done by caller) */
|
||||
static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
|
||||
{
|
||||
buf[0] = off & 0xff;
|
||||
buf[1] = (off >> 8) & 0xff;
|
||||
if (data->addr_sz == 3)
|
||||
buf[2] = (off >> 16) & 0xff;
|
||||
return data->addr_sz == 2 ? 2 : 3;
|
||||
}
|
||||
|
||||
/* read a given size of data (bounds check to be done by caller) */
|
||||
static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
|
||||
char __user *u, size_t s, loff_t *off)
|
||||
{
|
||||
struct picolcd_pending *resp;
|
||||
u8 raw_data[4];
|
||||
ssize_t ret = 0;
|
||||
int len_off, err = -EIO;
|
||||
|
||||
while (s > 0) {
|
||||
err = -EIO;
|
||||
len_off = _picolcd_flash_setaddr(data, raw_data, *off);
|
||||
raw_data[len_off] = s > 32 ? 32 : s;
|
||||
resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
|
||||
if (!resp || !resp->in_report)
|
||||
goto skip;
|
||||
if (resp->in_report->id == REPORT_MEMORY ||
|
||||
resp->in_report->id == REPORT_BL_READ_MEMORY) {
|
||||
if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
|
||||
goto skip;
|
||||
if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
|
||||
err = -EFAULT;
|
||||
goto skip;
|
||||
}
|
||||
*off += raw_data[len_off];
|
||||
s -= raw_data[len_off];
|
||||
ret += raw_data[len_off];
|
||||
err = 0;
|
||||
}
|
||||
skip:
|
||||
kfree(resp);
|
||||
if (err)
|
||||
return ret > 0 ? ret : err;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
|
||||
size_t s, loff_t *off)
|
||||
{
|
||||
struct picolcd_data *data = f->private_data;
|
||||
|
||||
if (s == 0)
|
||||
return -EINVAL;
|
||||
if (*off > 0x05fff)
|
||||
return 0;
|
||||
if (*off + s > 0x05fff)
|
||||
s = 0x06000 - *off;
|
||||
|
||||
if (data->status & PICOLCD_BOOTLOADER)
|
||||
return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
|
||||
else
|
||||
return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
|
||||
}
|
||||
|
||||
/* erase block aligned to 64bytes boundary */
|
||||
static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
|
||||
loff_t *off)
|
||||
{
|
||||
struct picolcd_pending *resp;
|
||||
u8 raw_data[3];
|
||||
int len_off;
|
||||
ssize_t ret = -EIO;
|
||||
|
||||
if (*off & 0x3f)
|
||||
return -EINVAL;
|
||||
|
||||
len_off = _picolcd_flash_setaddr(data, raw_data, *off);
|
||||
resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
|
||||
if (!resp || !resp->in_report)
|
||||
goto skip;
|
||||
if (resp->in_report->id == REPORT_MEMORY ||
|
||||
resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
|
||||
if (memcmp(raw_data, resp->raw_data, len_off) != 0)
|
||||
goto skip;
|
||||
ret = 0;
|
||||
}
|
||||
skip:
|
||||
kfree(resp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write a given size of data (bounds check to be done by caller) */
|
||||
static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
|
||||
const char __user *u, size_t s, loff_t *off)
|
||||
{
|
||||
struct picolcd_pending *resp;
|
||||
u8 raw_data[36];
|
||||
ssize_t ret = 0;
|
||||
int len_off, err = -EIO;
|
||||
|
||||
while (s > 0) {
|
||||
err = -EIO;
|
||||
len_off = _picolcd_flash_setaddr(data, raw_data, *off);
|
||||
raw_data[len_off] = s > 32 ? 32 : s;
|
||||
if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
|
||||
len_off+1+raw_data[len_off]);
|
||||
if (!resp || !resp->in_report)
|
||||
goto skip;
|
||||
if (resp->in_report->id == REPORT_MEMORY ||
|
||||
resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
|
||||
if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
|
||||
goto skip;
|
||||
*off += raw_data[len_off];
|
||||
s -= raw_data[len_off];
|
||||
ret += raw_data[len_off];
|
||||
err = 0;
|
||||
}
|
||||
skip:
|
||||
kfree(resp);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
return ret > 0 ? ret : err;
|
||||
}
|
||||
|
||||
static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
|
||||
size_t s, loff_t *off)
|
||||
{
|
||||
struct picolcd_data *data = f->private_data;
|
||||
ssize_t err, ret = 0;
|
||||
int report_erase, report_write;
|
||||
|
||||
if (s == 0)
|
||||
return -EINVAL;
|
||||
if (*off > 0x5fff)
|
||||
return -ENOSPC;
|
||||
if (s & 0x3f)
|
||||
return -EINVAL;
|
||||
if (*off & 0x3f)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->status & PICOLCD_BOOTLOADER) {
|
||||
report_erase = REPORT_BL_ERASE_MEMORY;
|
||||
report_write = REPORT_BL_WRITE_MEMORY;
|
||||
} else {
|
||||
report_erase = REPORT_ERASE_MEMORY;
|
||||
report_write = REPORT_WRITE_MEMORY;
|
||||
}
|
||||
mutex_lock(&data->mutex_flash);
|
||||
while (s > 0) {
|
||||
err = _picolcd_flash_erase64(data, report_erase, off);
|
||||
if (err)
|
||||
break;
|
||||
err = _picolcd_flash_write(data, report_write, u, 64, off);
|
||||
if (err < 0)
|
||||
break;
|
||||
ret += err;
|
||||
*off += err;
|
||||
s -= err;
|
||||
if (err != 64)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&data->mutex_flash);
|
||||
return ret > 0 ? ret : err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
* - concurrent writing is prevented by mutex and all writes must be
|
||||
* n*64 bytes and 64-byte aligned, each write being preceded by an
|
||||
* ERASE which erases a 64byte block.
|
||||
* If less than requested was written or an error is returned for an
|
||||
* otherwise correct write request the next 64-byte block which should
|
||||
* have been written is in undefined state (mostly: original, erased,
|
||||
* (half-)written with write error)
|
||||
* - reading can happen without special restriction
|
||||
*/
|
||||
static const struct file_operations picolcd_debug_flash_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = picolcd_debug_flash_read,
|
||||
.write = picolcd_debug_flash_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Helper code for HID report level dumping/debugging
|
||||
*/
|
||||
static const char * const error_codes[] = {
|
||||
"success", "parameter missing", "data_missing", "block readonly",
|
||||
"block not erasable", "block too big", "section overflow",
|
||||
"invalid command length", "invalid data length",
|
||||
};
|
||||
|
||||
static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
|
||||
const size_t data_len)
|
||||
{
|
||||
int i, j;
|
||||
for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) {
|
||||
dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
|
||||
dst[j++] = hex_asc[data[i] & 0x0f];
|
||||
dst[j++] = ' ';
|
||||
}
|
||||
dst[j] = '\0';
|
||||
if (j > 0)
|
||||
dst[j-1] = '\n';
|
||||
if (i < data_len && j > 2)
|
||||
dst[j-2] = dst[j-3] = '.';
|
||||
}
|
||||
|
||||
void picolcd_debug_out_report(struct picolcd_data *data,
|
||||
struct hid_device *hdev, struct hid_report *report)
|
||||
{
|
||||
u8 raw_data[70];
|
||||
int raw_size = (report->size >> 3) + 1;
|
||||
char *buff;
|
||||
#define BUFF_SZ 256
|
||||
|
||||
/* Avoid unnecessary overhead if debugfs is disabled */
|
||||
if (list_empty(&hdev->debug_list))
|
||||
return;
|
||||
|
||||
buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
|
||||
if (!buff)
|
||||
return;
|
||||
|
||||
snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ",
|
||||
report->id, raw_size);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_size + 5 > sizeof(raw_data)) {
|
||||
kfree(buff);
|
||||
hid_debug_event(hdev, " TOO BIG\n");
|
||||
return;
|
||||
} else {
|
||||
raw_data[0] = report->id;
|
||||
hid_output_report(report, raw_data);
|
||||
dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
|
||||
hid_debug_event(hdev, buff);
|
||||
}
|
||||
|
||||
switch (report->id) {
|
||||
case REPORT_LED_STATE:
|
||||
/* 1 data byte with GPO state */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_LED_STATE", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_BRIGHTNESS:
|
||||
/* 1 data byte with brightness */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_BRIGHTNESS", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_CONTRAST:
|
||||
/* 1 data byte with contrast */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_CONTRAST", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_RESET:
|
||||
/* 2 data bytes with reset duration in ms */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_RESET", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
|
||||
raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_LCD_CMD:
|
||||
/* 63 data bytes with LCD commands */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_LCD_CMD", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
/* TODO: format decoding */
|
||||
break;
|
||||
case REPORT_LCD_DATA:
|
||||
/* 63 data bytes with LCD data */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_LCD_CMD", report->id, raw_size-1);
|
||||
/* TODO: format decoding */
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_LCD_CMD_DATA:
|
||||
/* 63 data bytes with LCD commands and data */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_LCD_CMD", report->id, raw_size-1);
|
||||
/* TODO: format decoding */
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_EE_READ:
|
||||
/* 3 data bytes with read area description */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_EE_READ", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_EE_WRITE:
|
||||
/* 3+1..20 data bytes with write area description */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_EE_WRITE", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[3] == 0) {
|
||||
snprintf(buff, BUFF_SZ, "\tNo data\n");
|
||||
} else if (raw_data[3] + 4 <= raw_size) {
|
||||
snprintf(buff, BUFF_SZ, "\tData: ");
|
||||
hid_debug_event(hdev, buff);
|
||||
dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
|
||||
} else {
|
||||
snprintf(buff, BUFF_SZ, "\tData overflowed\n");
|
||||
}
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_ERASE_MEMORY:
|
||||
case REPORT_BL_ERASE_MEMORY:
|
||||
/* 3 data bytes with pointer inside erase block */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_ERASE_MEMORY", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
switch (data->addr_sz) {
|
||||
case 2:
|
||||
snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
break;
|
||||
case 3:
|
||||
snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
|
||||
raw_data[3], raw_data[2], raw_data[1]);
|
||||
break;
|
||||
default:
|
||||
snprintf(buff, BUFF_SZ, "\tNot supported\n");
|
||||
}
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_READ_MEMORY:
|
||||
case REPORT_BL_READ_MEMORY:
|
||||
/* 4 data bytes with read area description */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_READ_MEMORY", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
switch (data->addr_sz) {
|
||||
case 2:
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
|
||||
break;
|
||||
case 3:
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
|
||||
raw_data[3], raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
|
||||
break;
|
||||
default:
|
||||
snprintf(buff, BUFF_SZ, "\tNot supported\n");
|
||||
}
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_WRITE_MEMORY:
|
||||
case REPORT_BL_WRITE_MEMORY:
|
||||
/* 4+1..32 data bytes with write adrea description */
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_WRITE_MEMORY", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
switch (data->addr_sz) {
|
||||
case 2:
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[3] == 0) {
|
||||
snprintf(buff, BUFF_SZ, "\tNo data\n");
|
||||
} else if (raw_data[3] + 4 <= raw_size) {
|
||||
snprintf(buff, BUFF_SZ, "\tData: ");
|
||||
hid_debug_event(hdev, buff);
|
||||
dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
|
||||
} else {
|
||||
snprintf(buff, BUFF_SZ, "\tData overflowed\n");
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
|
||||
raw_data[3], raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[4] == 0) {
|
||||
snprintf(buff, BUFF_SZ, "\tNo data\n");
|
||||
} else if (raw_data[4] + 5 <= raw_size) {
|
||||
snprintf(buff, BUFF_SZ, "\tData: ");
|
||||
hid_debug_event(hdev, buff);
|
||||
dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
|
||||
} else {
|
||||
snprintf(buff, BUFF_SZ, "\tData overflowed\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
snprintf(buff, BUFF_SZ, "\tNot supported\n");
|
||||
}
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_SPLASH_RESTART:
|
||||
/* TODO */
|
||||
break;
|
||||
case REPORT_EXIT_KEYBOARD:
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
|
||||
raw_data[1] | (raw_data[2] << 8),
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_VERSION:
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_VERSION", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_DEVID:
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_DEVID", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_SPLASH_SIZE:
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_SPLASH_SIZE", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_HOOK_VERSION:
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_HOOK_VERSION", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_EXIT_FLASHER:
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"REPORT_VERSION", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
|
||||
raw_data[1] | (raw_data[2] << 8),
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
default:
|
||||
snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
|
||||
"<unknown>", report->id, raw_size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
}
|
||||
wake_up_interruptible(&hdev->debug_wait);
|
||||
kfree(buff);
|
||||
}
|
||||
|
||||
void picolcd_debug_raw_event(struct picolcd_data *data,
|
||||
struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *raw_data, int size)
|
||||
{
|
||||
char *buff;
|
||||
|
||||
#define BUFF_SZ 256
|
||||
/* Avoid unnecessary overhead if debugfs is disabled */
|
||||
if (list_empty(&hdev->debug_list))
|
||||
return;
|
||||
|
||||
buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
|
||||
if (!buff)
|
||||
return;
|
||||
|
||||
switch (report->id) {
|
||||
case REPORT_ERROR_CODE:
|
||||
/* 2 data bytes with affected report and error code */
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_ERROR_CODE", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[2] < ARRAY_SIZE(error_codes))
|
||||
snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
|
||||
raw_data[2], error_codes[raw_data[2]], raw_data[1]);
|
||||
else
|
||||
snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_KEY_STATE:
|
||||
/* 2 data bytes with key state */
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_KEY_STATE", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[1] == 0)
|
||||
snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
|
||||
else if (raw_data[2] == 0)
|
||||
snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
|
||||
raw_data[1], raw_data[1]);
|
||||
else
|
||||
snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
|
||||
raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_IR_DATA:
|
||||
/* Up to 20 byes of IR scancode data */
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_IR_DATA", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[1] == 0) {
|
||||
snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
|
||||
hid_debug_event(hdev, buff);
|
||||
} else if (raw_data[1] + 1 <= size) {
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
|
||||
raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
} else {
|
||||
snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
|
||||
raw_data[1]-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
}
|
||||
break;
|
||||
case REPORT_EE_DATA:
|
||||
/* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_EE_DATA", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[3] == 0) {
|
||||
snprintf(buff, BUFF_SZ, "\tNo data\n");
|
||||
hid_debug_event(hdev, buff);
|
||||
} else if (raw_data[3] + 4 <= size) {
|
||||
snprintf(buff, BUFF_SZ, "\tData: ");
|
||||
hid_debug_event(hdev, buff);
|
||||
dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
|
||||
hid_debug_event(hdev, buff);
|
||||
} else {
|
||||
snprintf(buff, BUFF_SZ, "\tData overflowed\n");
|
||||
hid_debug_event(hdev, buff);
|
||||
}
|
||||
break;
|
||||
case REPORT_MEMORY:
|
||||
/* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_MEMORY", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
switch (data->addr_sz) {
|
||||
case 2:
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[3] == 0) {
|
||||
snprintf(buff, BUFF_SZ, "\tNo data\n");
|
||||
} else if (raw_data[3] + 4 <= size) {
|
||||
snprintf(buff, BUFF_SZ, "\tData: ");
|
||||
hid_debug_event(hdev, buff);
|
||||
dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
|
||||
} else {
|
||||
snprintf(buff, BUFF_SZ, "\tData overflowed\n");
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
|
||||
raw_data[3], raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
|
||||
hid_debug_event(hdev, buff);
|
||||
if (raw_data[4] == 0) {
|
||||
snprintf(buff, BUFF_SZ, "\tNo data\n");
|
||||
} else if (raw_data[4] + 5 <= size) {
|
||||
snprintf(buff, BUFF_SZ, "\tData: ");
|
||||
hid_debug_event(hdev, buff);
|
||||
dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
|
||||
} else {
|
||||
snprintf(buff, BUFF_SZ, "\tData overflowed\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
snprintf(buff, BUFF_SZ, "\tNot supported\n");
|
||||
}
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_VERSION:
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_VERSION", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
|
||||
raw_data[2], raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_BL_ERASE_MEMORY:
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_BL_ERASE_MEMORY", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
/* TODO */
|
||||
break;
|
||||
case REPORT_BL_READ_MEMORY:
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_BL_READ_MEMORY", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
/* TODO */
|
||||
break;
|
||||
case REPORT_BL_WRITE_MEMORY:
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_BL_WRITE_MEMORY", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
/* TODO */
|
||||
break;
|
||||
case REPORT_DEVID:
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_DEVID", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
|
||||
raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
|
||||
raw_data[5]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_SPLASH_SIZE:
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_SPLASH_SIZE", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
|
||||
(raw_data[2] << 8) | raw_data[1]);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
|
||||
(raw_data[4] << 8) | raw_data[3]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
case REPORT_HOOK_VERSION:
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"REPORT_HOOK_VERSION", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
|
||||
raw_data[1], raw_data[2]);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
default:
|
||||
snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
|
||||
"<unknown>", report->id, size-1);
|
||||
hid_debug_event(hdev, buff);
|
||||
break;
|
||||
}
|
||||
wake_up_interruptible(&hdev->debug_wait);
|
||||
kfree(buff);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct hid_device *hdev = data->hdev;
|
||||
|
||||
mutex_init(&data->mutex_flash);
|
||||
|
||||
/* reset */
|
||||
if (reset)
|
||||
data->debug_reset = debugfs_create_file("reset", 0600,
|
||||
hdev->debug_dir, data, &picolcd_debug_reset_fops);
|
||||
|
||||
/* eeprom */
|
||||
if (eeprom_r || eeprom_w)
|
||||
data->debug_eeprom = debugfs_create_file("eeprom",
|
||||
(eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
|
||||
hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
|
||||
|
||||
/* flash */
|
||||
if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
|
||||
data->addr_sz = flash_r->field[0]->report_count - 1;
|
||||
else
|
||||
data->addr_sz = -1;
|
||||
if (data->addr_sz == 2 || data->addr_sz == 3) {
|
||||
data->debug_flash = debugfs_create_file("flash",
|
||||
(flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
|
||||
hdev->debug_dir, data, &picolcd_debug_flash_fops);
|
||||
} else if (flash_r || flash_w)
|
||||
hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
|
||||
}
|
||||
|
||||
void picolcd_exit_devfs(struct picolcd_data *data)
|
||||
{
|
||||
struct dentry *dent;
|
||||
|
||||
dent = data->debug_reset;
|
||||
data->debug_reset = NULL;
|
||||
if (dent)
|
||||
debugfs_remove(dent);
|
||||
dent = data->debug_eeprom;
|
||||
data->debug_eeprom = NULL;
|
||||
if (dent)
|
||||
debugfs_remove(dent);
|
||||
dent = data->debug_flash;
|
||||
data->debug_flash = NULL;
|
||||
if (dent)
|
||||
debugfs_remove(dent);
|
||||
mutex_destroy(&data->mutex_flash);
|
||||
}
|
||||
|
|
@ -0,0 +1,615 @@
|
|||
/***************************************************************************
|
||||
* 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/vmalloc.h>
|
||||
#include "usbhid/usbhid.h"
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "hid-picolcd.h"
|
||||
|
||||
/* Framebuffer
|
||||
*
|
||||
* The PicoLCD use a Topway LCD module of 256x64 pixel
|
||||
* This display area is tiled over 4 controllers with 8 tiles
|
||||
* each. Each tile has 8x64 pixel, each data byte representing
|
||||
* a 1-bit wide vertical line of the tile.
|
||||
*
|
||||
* The display can be updated at a tile granularity.
|
||||
*
|
||||
* Chip 1 Chip 2 Chip 3 Chip 4
|
||||
* +----------------+----------------+----------------+----------------+
|
||||
* | Tile 1 | Tile 1 | Tile 1 | Tile 1 |
|
||||
* +----------------+----------------+----------------+----------------+
|
||||
* | Tile 2 | Tile 2 | Tile 2 | Tile 2 |
|
||||
* +----------------+----------------+----------------+----------------+
|
||||
* ...
|
||||
* +----------------+----------------+----------------+----------------+
|
||||
* | Tile 8 | Tile 8 | Tile 8 | Tile 8 |
|
||||
* +----------------+----------------+----------------+----------------+
|
||||
*/
|
||||
#define PICOLCDFB_NAME "picolcdfb"
|
||||
#define PICOLCDFB_WIDTH (256)
|
||||
#define PICOLCDFB_HEIGHT (64)
|
||||
#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
|
||||
|
||||
#define PICOLCDFB_UPDATE_RATE_LIMIT 10
|
||||
#define PICOLCDFB_UPDATE_RATE_DEFAULT 2
|
||||
|
||||
/* Framebuffer visual structures */
|
||||
static const struct fb_fix_screeninfo picolcdfb_fix = {
|
||||
.id = PICOLCDFB_NAME,
|
||||
.type = FB_TYPE_PACKED_PIXELS,
|
||||
.visual = FB_VISUAL_MONO01,
|
||||
.xpanstep = 0,
|
||||
.ypanstep = 0,
|
||||
.ywrapstep = 0,
|
||||
.line_length = PICOLCDFB_WIDTH / 8,
|
||||
.accel = FB_ACCEL_NONE,
|
||||
};
|
||||
|
||||
static const struct fb_var_screeninfo picolcdfb_var = {
|
||||
.xres = PICOLCDFB_WIDTH,
|
||||
.yres = PICOLCDFB_HEIGHT,
|
||||
.xres_virtual = PICOLCDFB_WIDTH,
|
||||
.yres_virtual = PICOLCDFB_HEIGHT,
|
||||
.width = 103,
|
||||
.height = 26,
|
||||
.bits_per_pixel = 1,
|
||||
.grayscale = 1,
|
||||
.red = {
|
||||
.offset = 0,
|
||||
.length = 1,
|
||||
.msb_right = 0,
|
||||
},
|
||||
.green = {
|
||||
.offset = 0,
|
||||
.length = 1,
|
||||
.msb_right = 0,
|
||||
},
|
||||
.blue = {
|
||||
.offset = 0,
|
||||
.length = 1,
|
||||
.msb_right = 0,
|
||||
},
|
||||
.transp = {
|
||||
.offset = 0,
|
||||
.length = 0,
|
||||
.msb_right = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/* Send a given tile to PicoLCD */
|
||||
static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap,
|
||||
int chip, int tile)
|
||||
{
|
||||
struct hid_report *report1, *report2;
|
||||
unsigned long flags;
|
||||
u8 *tdata;
|
||||
int i;
|
||||
|
||||
report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev);
|
||||
if (!report1 || report1->maxfield != 1)
|
||||
return -ENODEV;
|
||||
report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev);
|
||||
if (!report2 || report2->maxfield != 1)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
if ((data->status & PICOLCD_FAILED)) {
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
return -ENODEV;
|
||||
}
|
||||
hid_set_field(report1->field[0], 0, chip << 2);
|
||||
hid_set_field(report1->field[0], 1, 0x02);
|
||||
hid_set_field(report1->field[0], 2, 0x00);
|
||||
hid_set_field(report1->field[0], 3, 0x00);
|
||||
hid_set_field(report1->field[0], 4, 0xb8 | tile);
|
||||
hid_set_field(report1->field[0], 5, 0x00);
|
||||
hid_set_field(report1->field[0], 6, 0x00);
|
||||
hid_set_field(report1->field[0], 7, 0x40);
|
||||
hid_set_field(report1->field[0], 8, 0x00);
|
||||
hid_set_field(report1->field[0], 9, 0x00);
|
||||
hid_set_field(report1->field[0], 10, 32);
|
||||
|
||||
hid_set_field(report2->field[0], 0, (chip << 2) | 0x01);
|
||||
hid_set_field(report2->field[0], 1, 0x00);
|
||||
hid_set_field(report2->field[0], 2, 0x00);
|
||||
hid_set_field(report2->field[0], 3, 32);
|
||||
|
||||
tdata = vbitmap + (tile * 4 + chip) * 64;
|
||||
for (i = 0; i < 64; i++)
|
||||
if (i < 32)
|
||||
hid_set_field(report1->field[0], 11 + i, tdata[i]);
|
||||
else
|
||||
hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
|
||||
|
||||
usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
|
||||
usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Translate a single tile*/
|
||||
static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
|
||||
int chip, int tile)
|
||||
{
|
||||
int i, b, changed = 0;
|
||||
u8 tdata[64];
|
||||
u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
|
||||
|
||||
if (bpp == 1) {
|
||||
for (b = 7; b >= 0; b--) {
|
||||
const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
|
||||
for (i = 0; i < 64; i++) {
|
||||
tdata[i] <<= 1;
|
||||
tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
|
||||
}
|
||||
}
|
||||
} else if (bpp == 8) {
|
||||
for (b = 7; b >= 0; b--) {
|
||||
const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
|
||||
for (i = 0; i < 64; i++) {
|
||||
tdata[i] <<= 1;
|
||||
tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Oops, we should never get here! */
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
if (tdata[i] != vdata[i]) {
|
||||
changed = 1;
|
||||
vdata[i] = tdata[i];
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void picolcd_fb_refresh(struct picolcd_data *data)
|
||||
{
|
||||
if (data->fb_info)
|
||||
schedule_delayed_work(&data->fb_info->deferred_work, 0);
|
||||
}
|
||||
|
||||
/* Reconfigure LCD display */
|
||||
int picolcd_fb_reset(struct picolcd_data *data, int clear)
|
||||
{
|
||||
struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
|
||||
struct picolcd_fb_data *fbdata = data->fb_info->par;
|
||||
int i, j;
|
||||
unsigned long flags;
|
||||
static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
|
||||
|
||||
if (!report || report->maxfield != 1)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < report->field[0]->maxusage; j++)
|
||||
if (j == 0)
|
||||
hid_set_field(report->field[0], j, i << 2);
|
||||
else if (j < sizeof(mapcmd))
|
||||
hid_set_field(report->field[0], j, mapcmd[j]);
|
||||
else
|
||||
hid_set_field(report->field[0], j, 0);
|
||||
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
|
||||
}
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
|
||||
if (clear) {
|
||||
memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE);
|
||||
memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp);
|
||||
}
|
||||
fbdata->force = 1;
|
||||
|
||||
/* schedule first output of framebuffer */
|
||||
if (fbdata->ready)
|
||||
schedule_delayed_work(&data->fb_info->deferred_work, 0);
|
||||
else
|
||||
fbdata->ready = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update fb_vbitmap from the screen_base and send changed tiles to device */
|
||||
static void picolcd_fb_update(struct fb_info *info)
|
||||
{
|
||||
int chip, tile, n;
|
||||
unsigned long flags;
|
||||
struct picolcd_fb_data *fbdata = info->par;
|
||||
struct picolcd_data *data;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
spin_lock_irqsave(&fbdata->lock, flags);
|
||||
if (!fbdata->ready && fbdata->picolcd)
|
||||
picolcd_fb_reset(fbdata->picolcd, 0);
|
||||
spin_unlock_irqrestore(&fbdata->lock, flags);
|
||||
|
||||
/*
|
||||
* Translate the framebuffer into the format needed by the PicoLCD.
|
||||
* See display layout above.
|
||||
* Do this one tile after the other and push those tiles that changed.
|
||||
*
|
||||
* Wait for our IO to complete as otherwise we might flood the queue!
|
||||
*/
|
||||
n = 0;
|
||||
for (chip = 0; chip < 4; chip++)
|
||||
for (tile = 0; tile < 8; tile++) {
|
||||
if (!fbdata->force && !picolcd_fb_update_tile(
|
||||
fbdata->vbitmap, fbdata->bitmap,
|
||||
fbdata->bpp, chip, tile))
|
||||
continue;
|
||||
n += 2;
|
||||
if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
|
||||
spin_lock_irqsave(&fbdata->lock, flags);
|
||||
data = fbdata->picolcd;
|
||||
spin_unlock_irqrestore(&fbdata->lock, flags);
|
||||
mutex_unlock(&info->lock);
|
||||
if (!data)
|
||||
return;
|
||||
usbhid_wait_io(data->hdev);
|
||||
mutex_lock(&info->lock);
|
||||
n = 0;
|
||||
}
|
||||
spin_lock_irqsave(&fbdata->lock, flags);
|
||||
data = fbdata->picolcd;
|
||||
spin_unlock_irqrestore(&fbdata->lock, flags);
|
||||
if (!data || picolcd_fb_send_tile(data,
|
||||
fbdata->vbitmap, chip, tile))
|
||||
goto out;
|
||||
}
|
||||
fbdata->force = false;
|
||||
if (n) {
|
||||
spin_lock_irqsave(&fbdata->lock, flags);
|
||||
data = fbdata->picolcd;
|
||||
spin_unlock_irqrestore(&fbdata->lock, flags);
|
||||
mutex_unlock(&info->lock);
|
||||
if (data)
|
||||
usbhid_wait_io(data->hdev);
|
||||
return;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
}
|
||||
|
||||
/* Stub to call the system default and update the image on the picoLCD */
|
||||
static void picolcd_fb_fillrect(struct fb_info *info,
|
||||
const struct fb_fillrect *rect)
|
||||
{
|
||||
if (!info->par)
|
||||
return;
|
||||
sys_fillrect(info, rect);
|
||||
|
||||
schedule_delayed_work(&info->deferred_work, 0);
|
||||
}
|
||||
|
||||
/* Stub to call the system default and update the image on the picoLCD */
|
||||
static void picolcd_fb_copyarea(struct fb_info *info,
|
||||
const struct fb_copyarea *area)
|
||||
{
|
||||
if (!info->par)
|
||||
return;
|
||||
sys_copyarea(info, area);
|
||||
|
||||
schedule_delayed_work(&info->deferred_work, 0);
|
||||
}
|
||||
|
||||
/* Stub to call the system default and update the image on the picoLCD */
|
||||
static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
|
||||
{
|
||||
if (!info->par)
|
||||
return;
|
||||
sys_imageblit(info, image);
|
||||
|
||||
schedule_delayed_work(&info->deferred_work, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is the slow path from userspace. they can seek and write to
|
||||
* the fb. it's inefficient to do anything less than a full screen draw
|
||||
*/
|
||||
static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t ret;
|
||||
if (!info->par)
|
||||
return -ENODEV;
|
||||
ret = fb_sys_write(info, buf, count, ppos);
|
||||
if (ret >= 0)
|
||||
schedule_delayed_work(&info->deferred_work, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int picolcd_fb_blank(int blank, struct fb_info *info)
|
||||
{
|
||||
/* We let fb notification do this for us via lcd/backlight device */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void picolcd_fb_destroy(struct fb_info *info)
|
||||
{
|
||||
struct picolcd_fb_data *fbdata = info->par;
|
||||
|
||||
/* make sure no work is deferred */
|
||||
fb_deferred_io_cleanup(info);
|
||||
|
||||
/* No thridparty should ever unregister our framebuffer! */
|
||||
WARN_ON(fbdata->picolcd != NULL);
|
||||
|
||||
vfree((u8 *)info->fix.smem_start);
|
||||
framebuffer_release(info);
|
||||
}
|
||||
|
||||
static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
{
|
||||
__u32 bpp = var->bits_per_pixel;
|
||||
__u32 activate = var->activate;
|
||||
|
||||
/* only allow 1/8 bit depth (8-bit is grayscale) */
|
||||
*var = picolcdfb_var;
|
||||
var->activate = activate;
|
||||
if (bpp >= 8) {
|
||||
var->bits_per_pixel = 8;
|
||||
var->red.length = 8;
|
||||
var->green.length = 8;
|
||||
var->blue.length = 8;
|
||||
} else {
|
||||
var->bits_per_pixel = 1;
|
||||
var->red.length = 1;
|
||||
var->green.length = 1;
|
||||
var->blue.length = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int picolcd_set_par(struct fb_info *info)
|
||||
{
|
||||
struct picolcd_fb_data *fbdata = info->par;
|
||||
u8 *tmp_fb, *o_fb;
|
||||
if (info->var.bits_per_pixel == fbdata->bpp)
|
||||
return 0;
|
||||
/* switch between 1/8 bit depths */
|
||||
if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
|
||||
return -EINVAL;
|
||||
|
||||
o_fb = fbdata->bitmap;
|
||||
tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
|
||||
if (!tmp_fb)
|
||||
return -ENOMEM;
|
||||
|
||||
/* translate FB content to new bits-per-pixel */
|
||||
if (info->var.bits_per_pixel == 1) {
|
||||
int i, b;
|
||||
for (i = 0; i < PICOLCDFB_SIZE; i++) {
|
||||
u8 p = 0;
|
||||
for (b = 0; b < 8; b++) {
|
||||
p <<= 1;
|
||||
p |= o_fb[i*8+b] ? 0x01 : 0x00;
|
||||
}
|
||||
tmp_fb[i] = p;
|
||||
}
|
||||
memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
|
||||
info->fix.visual = FB_VISUAL_MONO01;
|
||||
info->fix.line_length = PICOLCDFB_WIDTH / 8;
|
||||
} else {
|
||||
int i;
|
||||
memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
|
||||
for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
|
||||
o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
|
||||
info->fix.visual = FB_VISUAL_DIRECTCOLOR;
|
||||
info->fix.line_length = PICOLCDFB_WIDTH;
|
||||
}
|
||||
|
||||
kfree(tmp_fb);
|
||||
fbdata->bpp = info->var.bits_per_pixel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Note this can't be const because of struct fb_info definition */
|
||||
static struct fb_ops picolcdfb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_destroy = picolcd_fb_destroy,
|
||||
.fb_read = fb_sys_read,
|
||||
.fb_write = picolcd_fb_write,
|
||||
.fb_blank = picolcd_fb_blank,
|
||||
.fb_fillrect = picolcd_fb_fillrect,
|
||||
.fb_copyarea = picolcd_fb_copyarea,
|
||||
.fb_imageblit = picolcd_fb_imageblit,
|
||||
.fb_check_var = picolcd_fb_check_var,
|
||||
.fb_set_par = picolcd_set_par,
|
||||
};
|
||||
|
||||
|
||||
/* Callback from deferred IO workqueue */
|
||||
static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
|
||||
{
|
||||
picolcd_fb_update(info);
|
||||
}
|
||||
|
||||
static const struct fb_deferred_io picolcd_fb_defio = {
|
||||
.delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
|
||||
.deferred_io = picolcd_fb_deferred_io,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The "fb_update_rate" sysfs attribute
|
||||
*/
|
||||
static ssize_t picolcd_fb_update_rate_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct picolcd_data *data = dev_get_drvdata(dev);
|
||||
struct picolcd_fb_data *fbdata = data->fb_info->par;
|
||||
unsigned i, fb_update_rate = fbdata->update_rate;
|
||||
size_t ret = 0;
|
||||
|
||||
for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
|
||||
if (ret >= PAGE_SIZE)
|
||||
break;
|
||||
else if (i == fb_update_rate)
|
||||
ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
|
||||
else
|
||||
ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
|
||||
if (ret > 0)
|
||||
buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t picolcd_fb_update_rate_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct picolcd_data *data = dev_get_drvdata(dev);
|
||||
struct picolcd_fb_data *fbdata = data->fb_info->par;
|
||||
int i;
|
||||
unsigned u;
|
||||
|
||||
if (count < 1 || count > 10)
|
||||
return -EINVAL;
|
||||
|
||||
i = sscanf(buf, "%u", &u);
|
||||
if (i != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
|
||||
return -ERANGE;
|
||||
else if (u == 0)
|
||||
u = PICOLCDFB_UPDATE_RATE_DEFAULT;
|
||||
|
||||
fbdata->update_rate = u;
|
||||
data->fb_info->fbdefio->delay = HZ / fbdata->update_rate;
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
|
||||
picolcd_fb_update_rate_store);
|
||||
|
||||
/* initialize Framebuffer device */
|
||||
int picolcd_init_framebuffer(struct picolcd_data *data)
|
||||
{
|
||||
struct device *dev = &data->hdev->dev;
|
||||
struct fb_info *info = NULL;
|
||||
struct picolcd_fb_data *fbdata = NULL;
|
||||
int i, error = -ENOMEM;
|
||||
u32 *palette;
|
||||
|
||||
/* The extra memory is:
|
||||
* - 256*u32 for pseudo_palette
|
||||
* - struct fb_deferred_io
|
||||
*/
|
||||
info = framebuffer_alloc(256 * sizeof(u32) +
|
||||
sizeof(struct fb_deferred_io) +
|
||||
sizeof(struct picolcd_fb_data) +
|
||||
PICOLCDFB_SIZE, dev);
|
||||
if (info == NULL) {
|
||||
dev_err(dev, "failed to allocate a framebuffer\n");
|
||||
goto err_nomem;
|
||||
}
|
||||
|
||||
info->fbdefio = info->par;
|
||||
*info->fbdefio = picolcd_fb_defio;
|
||||
info->par += sizeof(struct fb_deferred_io);
|
||||
palette = info->par;
|
||||
info->par += 256 * sizeof(u32);
|
||||
for (i = 0; i < 256; i++)
|
||||
palette[i] = i > 0 && i < 16 ? 0xff : 0;
|
||||
info->pseudo_palette = palette;
|
||||
info->fbops = &picolcdfb_ops;
|
||||
info->var = picolcdfb_var;
|
||||
info->fix = picolcdfb_fix;
|
||||
info->fix.smem_len = PICOLCDFB_SIZE*8;
|
||||
info->flags = FBINFO_FLAG_DEFAULT;
|
||||
|
||||
fbdata = info->par;
|
||||
spin_lock_init(&fbdata->lock);
|
||||
fbdata->picolcd = data;
|
||||
fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
|
||||
fbdata->bpp = picolcdfb_var.bits_per_pixel;
|
||||
fbdata->force = 1;
|
||||
fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data);
|
||||
fbdata->bitmap = vmalloc(PICOLCDFB_SIZE*8);
|
||||
if (fbdata->bitmap == NULL) {
|
||||
dev_err(dev, "can't get a free page for framebuffer\n");
|
||||
goto err_nomem;
|
||||
}
|
||||
info->screen_base = (char __force __iomem *)fbdata->bitmap;
|
||||
info->fix.smem_start = (unsigned long)fbdata->bitmap;
|
||||
memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE);
|
||||
data->fb_info = info;
|
||||
|
||||
error = picolcd_fb_reset(data, 1);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to configure display\n");
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
error = device_create_file(dev, &dev_attr_fb_update_rate);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to create sysfs attributes\n");
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
fb_deferred_io_init(info);
|
||||
error = register_framebuffer(info);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to register framebuffer\n");
|
||||
goto err_sysfs;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
device_remove_file(dev, &dev_attr_fb_update_rate);
|
||||
fb_deferred_io_cleanup(info);
|
||||
err_cleanup:
|
||||
data->fb_info = NULL;
|
||||
|
||||
err_nomem:
|
||||
if (fbdata)
|
||||
vfree(fbdata->bitmap);
|
||||
framebuffer_release(info);
|
||||
return error;
|
||||
}
|
||||
|
||||
void picolcd_exit_framebuffer(struct picolcd_data *data)
|
||||
{
|
||||
struct fb_info *info = data->fb_info;
|
||||
struct picolcd_fb_data *fbdata = info->par;
|
||||
unsigned long flags;
|
||||
|
||||
device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
|
||||
|
||||
/* disconnect framebuffer from HID dev */
|
||||
spin_lock_irqsave(&fbdata->lock, flags);
|
||||
fbdata->picolcd = NULL;
|
||||
spin_unlock_irqrestore(&fbdata->lock, flags);
|
||||
|
||||
/* make sure there is no running update - thus that fbdata->picolcd
|
||||
* once obtained under lock is guaranteed not to get free() under
|
||||
* the feet of the deferred work */
|
||||
flush_delayed_work_sync(&info->deferred_work);
|
||||
|
||||
data->fb_info = NULL;
|
||||
unregister_framebuffer(info);
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/***************************************************************************
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/***************************************************************************
|
||||
* 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 "hid-picolcd.h"
|
||||
|
||||
|
||||
void picolcd_leds_set(struct picolcd_data *data)
|
||||
{
|
||||
struct hid_report *report;
|
||||
unsigned long flags;
|
||||
|
||||
if (!data->led[0])
|
||||
return;
|
||||
report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
|
||||
if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
hid_set_field(report->field[0], 0, data->led_state);
|
||||
if (!(data->status & PICOLCD_FAILED))
|
||||
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
}
|
||||
|
||||
static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct device *dev;
|
||||
struct hid_device *hdev;
|
||||
struct picolcd_data *data;
|
||||
int i, state = 0;
|
||||
|
||||
dev = led_cdev->dev->parent;
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
data = hid_get_drvdata(hdev);
|
||||
if (!data)
|
||||
return;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (led_cdev != data->led[i])
|
||||
continue;
|
||||
state = (data->led_state >> i) & 1;
|
||||
if (value == LED_OFF && state) {
|
||||
data->led_state &= ~(1 << i);
|
||||
picolcd_leds_set(data);
|
||||
} else if (value != LED_OFF && !state) {
|
||||
data->led_state |= 1 << i;
|
||||
picolcd_leds_set(data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct device *dev;
|
||||
struct hid_device *hdev;
|
||||
struct picolcd_data *data;
|
||||
int i, value = 0;
|
||||
|
||||
dev = led_cdev->dev->parent;
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
data = hid_get_drvdata(hdev);
|
||||
for (i = 0; i < 8; i++)
|
||||
if (led_cdev == data->led[i]) {
|
||||
value = (data->led_state >> i) & 1;
|
||||
break;
|
||||
}
|
||||
return value ? LED_FULL : LED_OFF;
|
||||
}
|
||||
|
||||
int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
|
||||
{
|
||||
struct device *dev = &data->hdev->dev;
|
||||
struct led_classdev *led;
|
||||
size_t name_sz = strlen(dev_name(dev)) + 8;
|
||||
char *name;
|
||||
int i, ret = 0;
|
||||
|
||||
if (!report)
|
||||
return -ENODEV;
|
||||
if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
|
||||
report->field[0]->report_size != 8) {
|
||||
dev_err(dev, "unsupported LED_STATE report");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
|
||||
if (!led) {
|
||||
dev_err(dev, "can't allocate memory for LED %d\n", i);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
name = (void *)(&led[1]);
|
||||
snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
|
||||
led->name = name;
|
||||
led->brightness = 0;
|
||||
led->max_brightness = 1;
|
||||
led->brightness_get = picolcd_led_get_brightness;
|
||||
led->brightness_set = picolcd_led_set_brightness;
|
||||
|
||||
data->led[i] = led;
|
||||
ret = led_classdev_register(dev, data->led[i]);
|
||||
if (ret) {
|
||||
data->led[i] = NULL;
|
||||
kfree(led);
|
||||
dev_err(dev, "can't register LED %d\n", i);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
for (i = 0; i < 8; i++)
|
||||
if (data->led[i]) {
|
||||
led = data->led[i];
|
||||
data->led[i] = NULL;
|
||||
led_classdev_unregister(led);
|
||||
kfree(led);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void picolcd_exit_leds(struct picolcd_data *data)
|
||||
{
|
||||
struct led_classdev *led;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
led = data->led[i];
|
||||
data->led[i] = NULL;
|
||||
if (!led)
|
||||
continue;
|
||||
led_classdev_unregister(led);
|
||||
kfree(led);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -105,7 +105,7 @@ static ssize_t show_channel(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
|
||||
dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel);
|
||||
|
||||
|
@ -118,7 +118,7 @@ static ssize_t store_channel(struct device *dev,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
|
||||
unsigned channel = 0;
|
||||
|
||||
|
@ -142,7 +142,7 @@ static ssize_t show_sustain(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
|
||||
dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain);
|
||||
|
||||
|
@ -155,7 +155,7 @@ static ssize_t store_sustain(struct device *dev,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
|
||||
unsigned sustain = 0;
|
||||
|
||||
|
@ -181,7 +181,7 @@ static ssize_t show_octave(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
|
||||
dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave);
|
||||
|
||||
|
@ -194,7 +194,7 @@ static ssize_t store_octave(struct device *dev,
|
|||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
|
||||
int octave = 0;
|
||||
|
||||
|
@ -759,7 +759,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
struct pcmidi_snd *pm;
|
||||
|
||||
pm = pk->pm;
|
||||
|
@ -777,7 +777,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
static int pk_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *data, int size)
|
||||
{
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
int ret = 0;
|
||||
|
||||
if (1 == pk->pm->ifnum) {
|
||||
|
@ -858,7 +858,7 @@ err_free_pk:
|
|||
|
||||
static void pk_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
|
||||
struct pk_device *pk = hid_get_drvdata(hdev);
|
||||
struct pcmidi_snd *pm;
|
||||
|
||||
pm = pk->pm;
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* HID driver for Sony PS3 BD Remote Control
|
||||
*
|
||||
* Copyright (c) 2012 David Dillow <dave@thedillows.org>
|
||||
* Based on a blend of the bluez fakehid user-space code by Marcel Holtmann
|
||||
* and other kernel HID drivers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
/* NOTE: in order for the Sony PS3 BD Remote Control to be found by
|
||||
* a Bluetooth host, the key combination Start+Enter has to be kept pressed
|
||||
* for about 7 seconds with the Bluetooth Host Controller in discovering mode.
|
||||
*
|
||||
* There will be no PIN request from the device.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static __u8 ps3remote_rdesc[] = {
|
||||
0x05, 0x01, /* GUsagePage Generic Desktop */
|
||||
0x09, 0x05, /* LUsage 0x05 [Game Pad] */
|
||||
0xA1, 0x01, /* MCollection Application (mouse, keyboard) */
|
||||
|
||||
/* Use collection 1 for joypad buttons */
|
||||
0xA1, 0x02, /* MCollection Logical (interrelated data) */
|
||||
|
||||
/* Ignore the 1st byte, maybe it is used for a controller
|
||||
* number but it's not needed for correct operation */
|
||||
0x75, 0x08, /* GReportSize 0x08 [8] */
|
||||
0x95, 0x01, /* GReportCount 0x01 [1] */
|
||||
0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
|
||||
|
||||
/* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
|
||||
* buttons multiple keypresses are allowed */
|
||||
0x05, 0x09, /* GUsagePage Button */
|
||||
0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */
|
||||
0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */
|
||||
0x14, /* GLogicalMinimum [0] */
|
||||
0x25, 0x01, /* GLogicalMaximum 0x01 [1] */
|
||||
0x75, 0x01, /* GReportSize 0x01 [1] */
|
||||
0x95, 0x18, /* GReportCount 0x18 [24] */
|
||||
0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */
|
||||
|
||||
0xC0, /* MEndCollection */
|
||||
|
||||
/* Use collection 2 for remote control buttons */
|
||||
0xA1, 0x02, /* MCollection Logical (interrelated data) */
|
||||
|
||||
/* 5th byte is used for remote control buttons */
|
||||
0x05, 0x09, /* GUsagePage Button */
|
||||
0x18, /* LUsageMinimum [No button pressed] */
|
||||
0x29, 0xFE, /* LUsageMaximum 0xFE [Button 254] */
|
||||
0x14, /* GLogicalMinimum [0] */
|
||||
0x26, 0xFE, 0x00, /* GLogicalMaximum 0x00FE [254] */
|
||||
0x75, 0x08, /* GReportSize 0x08 [8] */
|
||||
0x95, 0x01, /* GReportCount 0x01 [1] */
|
||||
0x80, /* MInput */
|
||||
|
||||
/* Ignore bytes from 6th to 11th, 6th to 10th are always constant at
|
||||
* 0xff and 11th is for press indication */
|
||||
0x75, 0x08, /* GReportSize 0x08 [8] */
|
||||
0x95, 0x06, /* GReportCount 0x06 [6] */
|
||||
0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
|
||||
|
||||
/* 12th byte is for battery strength */
|
||||
0x05, 0x06, /* GUsagePage Generic Device Controls */
|
||||
0x09, 0x20, /* LUsage 0x20 [Battery Strength] */
|
||||
0x14, /* GLogicalMinimum [0] */
|
||||
0x25, 0x05, /* GLogicalMaximum 0x05 [5] */
|
||||
0x75, 0x08, /* GReportSize 0x08 [8] */
|
||||
0x95, 0x01, /* GReportCount 0x01 [1] */
|
||||
0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */
|
||||
|
||||
0xC0, /* MEndCollection */
|
||||
|
||||
0xC0 /* MEndCollection [Game Pad] */
|
||||
};
|
||||
|
||||
static const unsigned int ps3remote_keymap_joypad_buttons[] = {
|
||||
[0x01] = KEY_SELECT,
|
||||
[0x02] = BTN_THUMBL, /* L3 */
|
||||
[0x03] = BTN_THUMBR, /* R3 */
|
||||
[0x04] = BTN_START,
|
||||
[0x05] = KEY_UP,
|
||||
[0x06] = KEY_RIGHT,
|
||||
[0x07] = KEY_DOWN,
|
||||
[0x08] = KEY_LEFT,
|
||||
[0x09] = BTN_TL2, /* L2 */
|
||||
[0x0a] = BTN_TR2, /* R2 */
|
||||
[0x0b] = BTN_TL, /* L1 */
|
||||
[0x0c] = BTN_TR, /* R1 */
|
||||
[0x0d] = KEY_OPTION, /* options/triangle */
|
||||
[0x0e] = KEY_BACK, /* back/circle */
|
||||
[0x0f] = BTN_0, /* cross */
|
||||
[0x10] = KEY_SCREEN, /* view/square */
|
||||
[0x11] = KEY_HOMEPAGE, /* PS button */
|
||||
[0x14] = KEY_ENTER,
|
||||
};
|
||||
static const unsigned int ps3remote_keymap_remote_buttons[] = {
|
||||
[0x00] = KEY_1,
|
||||
[0x01] = KEY_2,
|
||||
[0x02] = KEY_3,
|
||||
[0x03] = KEY_4,
|
||||
[0x04] = KEY_5,
|
||||
[0x05] = KEY_6,
|
||||
[0x06] = KEY_7,
|
||||
[0x07] = KEY_8,
|
||||
[0x08] = KEY_9,
|
||||
[0x09] = KEY_0,
|
||||
[0x0e] = KEY_ESC, /* return */
|
||||
[0x0f] = KEY_CLEAR,
|
||||
[0x16] = KEY_EJECTCD,
|
||||
[0x1a] = KEY_MENU, /* top menu */
|
||||
[0x28] = KEY_TIME,
|
||||
[0x30] = KEY_PREVIOUS,
|
||||
[0x31] = KEY_NEXT,
|
||||
[0x32] = KEY_PLAY,
|
||||
[0x33] = KEY_REWIND, /* scan back */
|
||||
[0x34] = KEY_FORWARD, /* scan forward */
|
||||
[0x38] = KEY_STOP,
|
||||
[0x39] = KEY_PAUSE,
|
||||
[0x40] = KEY_CONTEXT_MENU, /* pop up/menu */
|
||||
[0x60] = KEY_FRAMEBACK, /* slow/step back */
|
||||
[0x61] = KEY_FRAMEFORWARD, /* slow/step forward */
|
||||
[0x63] = KEY_SUBTITLE,
|
||||
[0x64] = KEY_AUDIO,
|
||||
[0x65] = KEY_ANGLE,
|
||||
[0x70] = KEY_INFO, /* display */
|
||||
[0x80] = KEY_BLUE,
|
||||
[0x81] = KEY_RED,
|
||||
[0x82] = KEY_GREEN,
|
||||
[0x83] = KEY_YELLOW,
|
||||
};
|
||||
|
||||
static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
*rsize = sizeof(ps3remote_rdesc);
|
||||
return ps3remote_rdesc;
|
||||
}
|
||||
|
||||
static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
unsigned int key = usage->hid & HID_USAGE;
|
||||
|
||||
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
|
||||
return -1;
|
||||
|
||||
switch (usage->collection_index) {
|
||||
case 1:
|
||||
if (key >= ARRAY_SIZE(ps3remote_keymap_joypad_buttons))
|
||||
return -1;
|
||||
|
||||
key = ps3remote_keymap_joypad_buttons[key];
|
||||
if (!key)
|
||||
return -1;
|
||||
break;
|
||||
case 2:
|
||||
if (key >= ARRAY_SIZE(ps3remote_keymap_remote_buttons))
|
||||
return -1;
|
||||
|
||||
key = ps3remote_keymap_remote_buttons[key];
|
||||
if (!key)
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct hid_device_id ps3remote_devices[] = {
|
||||
/* PS3 BD Remote Control */
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
|
||||
/* Logitech Harmony Adapter for PS3 */
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, ps3remote_devices);
|
||||
|
||||
static struct hid_driver ps3remote_driver = {
|
||||
.name = "ps3_remote",
|
||||
.id_table = ps3remote_devices,
|
||||
.report_fixup = ps3remote_fixup,
|
||||
.input_mapping = ps3remote_mapping,
|
||||
};
|
||||
|
||||
static int __init ps3remote_init(void)
|
||||
{
|
||||
return hid_register_driver(&ps3remote_driver);
|
||||
}
|
||||
|
||||
static void __exit ps3remote_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&ps3remote_driver);
|
||||
}
|
||||
|
||||
module_init(ps3remote_init);
|
||||
module_exit(ps3remote_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Dillow <dave@thedillows.org>, Antonio Ospite <ospite@studenti.unina.it>");
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
@ -466,6 +466,86 @@ static __u8 twhl850_rdesc_fixed2[] = {
|
|||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/*
|
||||
* See TWHA60 description, device and HID report descriptors at
|
||||
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60
|
||||
*/
|
||||
|
||||
/* Size of the original descriptors of TWHA60 tablet */
|
||||
#define TWHA60_RDESC_ORIG_SIZE0 254
|
||||
#define TWHA60_RDESC_ORIG_SIZE1 139
|
||||
|
||||
/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
|
||||
static __u8 twha60_rdesc_fixed0[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x09, /* Report ID (9), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x04, /* Report Count (4), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x09, 0x32, /* Usage (In Range), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0x10, 0x27, /* Physical Maximum (10000), */
|
||||
0x27, 0x3F, 0x9C,
|
||||
0x00, 0x00, /* Logical Maximum (39999), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x46, 0x6A, 0x18, /* Physical Maximum (6250), */
|
||||
0x26, 0xA7, 0x61, /* Logical Maximum (24999), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
|
||||
static __u8 twha60_rdesc_fixed1[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x06, /* Usage (Keyboard), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x05, /* Report ID (5), */
|
||||
0x05, 0x07, /* Usage Page (Keyboard), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x08, /* Report Count (8), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x95, 0x0C, /* Report Count (12), */
|
||||
0x19, 0x3A, /* Usage Minimum (KB F1), */
|
||||
0x29, 0x45, /* Usage Maximum (KB F12), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x0C, /* Report Count (12), */
|
||||
0x19, 0x68, /* Usage Minimum (KB F13), */
|
||||
0x29, 0x73, /* Usage Maximum (KB F24), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x08, /* Report Count (8), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
|
@ -525,6 +605,22 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
|
||||
switch (iface_num) {
|
||||
case 0:
|
||||
if (*rsize == TWHA60_RDESC_ORIG_SIZE0) {
|
||||
rdesc = twha60_rdesc_fixed0;
|
||||
*rsize = sizeof(twha60_rdesc_fixed0);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (*rsize == TWHA60_RDESC_ORIG_SIZE1) {
|
||||
rdesc = twha60_rdesc_fixed1;
|
||||
*rsize = sizeof(twha60_rdesc_fixed1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rdesc;
|
||||
|
@ -543,6 +639,8 @@ static const struct hid_device_id uclogic_devices[] = {
|
|||
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) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, uclogic_devices);
|
||||
|
|
|
@ -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>
|
||||
* Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
|
||||
* Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
|
||||
|
@ -33,6 +32,8 @@
|
|||
#define PAD_DEVICE_ID 0x0F
|
||||
|
||||
#define WAC_CMD_LED_CONTROL 0x20
|
||||
#define WAC_CMD_ICON_START_STOP 0x21
|
||||
#define WAC_CMD_ICON_TRANSFER 0x26
|
||||
|
||||
struct wacom_data {
|
||||
__u16 tool;
|
||||
|
@ -69,6 +70,91 @@ static enum power_supply_property wacom_ac_props[] = {
|
|||
POWER_SUPPLY_PROP_SCOPE,
|
||||
};
|
||||
|
||||
static void wacom_scramble(__u8 *image)
|
||||
{
|
||||
__u16 mask;
|
||||
__u16 s1;
|
||||
__u16 s2;
|
||||
__u16 r1 ;
|
||||
__u16 r2 ;
|
||||
__u16 r;
|
||||
__u8 buf[256];
|
||||
int i, w, x, y, z;
|
||||
|
||||
for (x = 0; x < 32; x++) {
|
||||
for (y = 0; y < 8; y++)
|
||||
buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
|
||||
}
|
||||
|
||||
/* Change 76543210 into GECA6420 as required by Intuos4 WL
|
||||
* HGFEDCBA HFDB7531
|
||||
*/
|
||||
for (x = 0; x < 4; x++) {
|
||||
for (y = 0; y < 4; y++) {
|
||||
for (z = 0; z < 8; z++) {
|
||||
mask = 0x0001;
|
||||
r1 = 0;
|
||||
r2 = 0;
|
||||
i = (x << 6) + (y << 4) + z;
|
||||
s1 = buf[i];
|
||||
s2 = buf[i+8];
|
||||
for (w = 0; w < 8; w++) {
|
||||
r1 |= (s1 & mask);
|
||||
r2 |= (s2 & mask);
|
||||
s1 <<= 1;
|
||||
s2 <<= 1;
|
||||
mask <<= 2;
|
||||
}
|
||||
r = r1 | (r2 << 1);
|
||||
i = (x << 6) + (y << 4) + (z << 1);
|
||||
image[i] = 0xFF & r;
|
||||
image[i+1] = (0xFF00 & r) >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wacom_set_image(struct hid_device *hdev, const char *image,
|
||||
__u8 icon_no)
|
||||
{
|
||||
__u8 rep_data[68];
|
||||
__u8 p[256];
|
||||
int ret, i, j;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
p[i] = image[i];
|
||||
|
||||
rep_data[0] = WAC_CMD_ICON_START_STOP;
|
||||
rep_data[1] = 0;
|
||||
ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
|
||||
HID_FEATURE_REPORT);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
rep_data[0] = WAC_CMD_ICON_TRANSFER;
|
||||
rep_data[1] = icon_no & 0x07;
|
||||
|
||||
wacom_scramble(p);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 64; j++)
|
||||
rep_data[j + 3] = p[(i << 6) + j];
|
||||
|
||||
rep_data[2] = i;
|
||||
ret = hdev->hid_output_raw_report(hdev, rep_data, 67,
|
||||
HID_FEATURE_REPORT);
|
||||
}
|
||||
|
||||
rep_data[0] = WAC_CMD_ICON_START_STOP;
|
||||
rep_data[1] = 0;
|
||||
|
||||
ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
|
||||
HID_FEATURE_REPORT);
|
||||
|
||||
err:
|
||||
return;
|
||||
}
|
||||
|
||||
static void wacom_leds_set_brightness(struct led_classdev *led_dev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
|
@ -91,7 +177,10 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
|
|||
if (buf) {
|
||||
buf[0] = WAC_CMD_LED_CONTROL;
|
||||
buf[1] = led;
|
||||
buf[2] = value;
|
||||
buf[2] = value >> 2;
|
||||
buf[3] = value;
|
||||
/* use fixed brightness for OLEDs */
|
||||
buf[4] = 0x08;
|
||||
hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT);
|
||||
kfree(buf);
|
||||
}
|
||||
|
@ -317,6 +406,34 @@ static ssize_t wacom_store_speed(struct device *dev,
|
|||
static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
wacom_show_speed, wacom_store_speed);
|
||||
|
||||
#define WACOM_STORE(OLED_ID) \
|
||||
static ssize_t wacom_oled##OLED_ID##_store(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, \
|
||||
dev); \
|
||||
\
|
||||
if (count != 256) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
wacom_set_image(hdev, buf, OLED_ID); \
|
||||
\
|
||||
return count; \
|
||||
} \
|
||||
\
|
||||
static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL, \
|
||||
wacom_oled##OLED_ID##_store)
|
||||
|
||||
WACOM_STORE(0);
|
||||
WACOM_STORE(1);
|
||||
WACOM_STORE(2);
|
||||
WACOM_STORE(3);
|
||||
WACOM_STORE(4);
|
||||
WACOM_STORE(5);
|
||||
WACOM_STORE(6);
|
||||
WACOM_STORE(7);
|
||||
|
||||
static int wacom_gr_parse_report(struct hid_device *hdev,
|
||||
struct wacom_data *wdata,
|
||||
struct input_dev *input, unsigned char *data)
|
||||
|
@ -717,17 +834,33 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
hid_warn(hdev,
|
||||
"can't create sysfs speed attribute err: %d\n", ret);
|
||||
|
||||
#define OLED_INIT(OLED_ID) \
|
||||
do { \
|
||||
ret = device_create_file(&hdev->dev, \
|
||||
&dev_attr_oled##OLED_ID##_img); \
|
||||
if (ret) \
|
||||
hid_warn(hdev, \
|
||||
"can't create sysfs oled attribute, err: %d\n", ret);\
|
||||
} while (0)
|
||||
|
||||
OLED_INIT(0);
|
||||
OLED_INIT(1);
|
||||
OLED_INIT(2);
|
||||
OLED_INIT(3);
|
||||
OLED_INIT(4);
|
||||
OLED_INIT(5);
|
||||
OLED_INIT(6);
|
||||
OLED_INIT(7);
|
||||
|
||||
wdata->features = 0;
|
||||
wacom_set_features(hdev, 1);
|
||||
|
||||
if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
|
||||
sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
|
||||
ret = wacom_initialize_leds(hdev);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
hid_warn(hdev,
|
||||
"can't create led attribute, err: %d\n", ret);
|
||||
goto destroy_leds;
|
||||
}
|
||||
}
|
||||
|
||||
wdata->battery.properties = wacom_battery_props;
|
||||
|
@ -740,8 +873,8 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
|
||||
ret = power_supply_register(&hdev->dev, &wdata->battery);
|
||||
if (ret) {
|
||||
hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
|
||||
ret);
|
||||
hid_err(hdev, "can't create sysfs battery attribute, err: %d\n",
|
||||
ret);
|
||||
goto err_battery;
|
||||
}
|
||||
|
||||
|
@ -756,8 +889,8 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
|
||||
ret = power_supply_register(&hdev->dev, &wdata->ac);
|
||||
if (ret) {
|
||||
hid_warn(hdev,
|
||||
"can't create ac battery attribute, err: %d\n", ret);
|
||||
hid_err(hdev,
|
||||
"can't create ac battery attribute, err: %d\n", ret);
|
||||
goto err_ac;
|
||||
}
|
||||
|
||||
|
@ -767,10 +900,17 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
err_ac:
|
||||
power_supply_unregister(&wdata->battery);
|
||||
err_battery:
|
||||
wacom_destroy_leds(hdev);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled0_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled1_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled2_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled3_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled4_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled5_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled6_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled7_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_speed);
|
||||
hid_hw_stop(hdev);
|
||||
destroy_leds:
|
||||
wacom_destroy_leds(hdev);
|
||||
err_free:
|
||||
kfree(wdata);
|
||||
return ret;
|
||||
|
@ -781,6 +921,14 @@ static void wacom_remove(struct hid_device *hdev)
|
|||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
|
||||
wacom_destroy_leds(hdev);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled0_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled1_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled2_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled3_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled4_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled5_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled6_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled7_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_speed);
|
||||
hid_hw_stop(hdev);
|
||||
|
||||
|
|
|
@ -638,28 +638,6 @@ static __u8 sirius_battery_free_tablet_rdesc_fixed[] = {
|
|||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static int waltop_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
|
@ -776,11 +754,6 @@ static int waltop_raw_event(struct hid_device *hdev, struct hid_report *report,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void waltop_remove(struct hid_device *hdev)
|
||||
{
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id waltop_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
|
||||
USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
|
||||
|
@ -803,10 +776,8 @@ MODULE_DEVICE_TABLE(hid, waltop_devices);
|
|||
static struct hid_driver waltop_driver = {
|
||||
.name = "waltop",
|
||||
.id_table = waltop_devices,
|
||||
.probe = waltop_probe,
|
||||
.report_fixup = waltop_report_fixup,
|
||||
.raw_event = waltop_raw_event,
|
||||
.remove = waltop_remove,
|
||||
};
|
||||
|
||||
static int __init waltop_init(void)
|
||||
|
|
|
@ -28,12 +28,14 @@ struct wiimote_ext {
|
|||
bool mp_plugged;
|
||||
bool motionp;
|
||||
__u8 ext_type;
|
||||
__u16 calib[4][3];
|
||||
};
|
||||
|
||||
enum wiiext_type {
|
||||
WIIEXT_NONE, /* placeholder */
|
||||
WIIEXT_CLASSIC, /* Nintendo classic controller */
|
||||
WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */
|
||||
WIIEXT_BALANCE_BOARD, /* Nintendo balance board controller */
|
||||
};
|
||||
|
||||
enum wiiext_keys {
|
||||
|
@ -126,6 +128,7 @@ error:
|
|||
static __u8 ext_read(struct wiimote_ext *ext)
|
||||
{
|
||||
ssize_t ret;
|
||||
__u8 buf[24], i, j, offs = 0;
|
||||
__u8 rmem[2], wmem;
|
||||
__u8 type = WIIEXT_NONE;
|
||||
|
||||
|
@ -151,6 +154,28 @@ static __u8 ext_read(struct wiimote_ext *ext)
|
|||
type = WIIEXT_NUNCHUCK;
|
||||
else if (rmem[0] == 0x01 && rmem[1] == 0x01)
|
||||
type = WIIEXT_CLASSIC;
|
||||
else if (rmem[0] == 0x04 && rmem[1] == 0x02)
|
||||
type = WIIEXT_BALANCE_BOARD;
|
||||
}
|
||||
|
||||
/* get balance board calibration data */
|
||||
if (type == WIIEXT_BALANCE_BOARD) {
|
||||
ret = wiimote_cmd_read(ext->wdata, 0xa40024, buf, 12);
|
||||
ret += wiimote_cmd_read(ext->wdata, 0xa40024 + 12,
|
||||
buf + 12, 12);
|
||||
|
||||
if (ret != 24) {
|
||||
type = WIIEXT_NONE;
|
||||
} else {
|
||||
for (i = 0; i < 3; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
ext->calib[j][i] = buf[offs];
|
||||
ext->calib[j][i] <<= 8;
|
||||
ext->calib[j][i] |= buf[offs + 1];
|
||||
offs += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wiimote_cmd_release(ext->wdata);
|
||||
|
@ -509,6 +534,71 @@ static void handler_classic(struct wiimote_ext *ext, const __u8 *payload)
|
|||
input_sync(ext->input);
|
||||
}
|
||||
|
||||
static void handler_balance_board(struct wiimote_ext *ext, const __u8 *payload)
|
||||
{
|
||||
__s32 val[4], tmp;
|
||||
unsigned int i;
|
||||
|
||||
/* Byte | 8 7 6 5 4 3 2 1 |
|
||||
* -----+--------------------------+
|
||||
* 1 | Top Right <15:8> |
|
||||
* 2 | Top Right <7:0> |
|
||||
* -----+--------------------------+
|
||||
* 3 | Bottom Right <15:8> |
|
||||
* 4 | Bottom Right <7:0> |
|
||||
* -----+--------------------------+
|
||||
* 5 | Top Left <15:8> |
|
||||
* 6 | Top Left <7:0> |
|
||||
* -----+--------------------------+
|
||||
* 7 | Bottom Left <15:8> |
|
||||
* 8 | Bottom Left <7:0> |
|
||||
* -----+--------------------------+
|
||||
*
|
||||
* These values represent the weight-measurements of the Wii-balance
|
||||
* board with 16bit precision.
|
||||
*
|
||||
* The balance-board is never reported interleaved with motionp.
|
||||
*/
|
||||
|
||||
val[0] = payload[0];
|
||||
val[0] <<= 8;
|
||||
val[0] |= payload[1];
|
||||
|
||||
val[1] = payload[2];
|
||||
val[1] <<= 8;
|
||||
val[1] |= payload[3];
|
||||
|
||||
val[2] = payload[4];
|
||||
val[2] <<= 8;
|
||||
val[2] |= payload[5];
|
||||
|
||||
val[3] = payload[6];
|
||||
val[3] <<= 8;
|
||||
val[3] |= payload[7];
|
||||
|
||||
/* apply calibration data */
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (val[i] < ext->calib[i][1]) {
|
||||
tmp = val[i] - ext->calib[i][0];
|
||||
tmp *= 1700;
|
||||
tmp /= ext->calib[i][1] - ext->calib[i][0];
|
||||
} else {
|
||||
tmp = val[i] - ext->calib[i][1];
|
||||
tmp *= 1700;
|
||||
tmp /= ext->calib[i][2] - ext->calib[i][1];
|
||||
tmp += 1700;
|
||||
}
|
||||
val[i] = tmp;
|
||||
}
|
||||
|
||||
input_report_abs(ext->input, ABS_HAT0X, val[0]);
|
||||
input_report_abs(ext->input, ABS_HAT0Y, val[1]);
|
||||
input_report_abs(ext->input, ABS_HAT1X, val[2]);
|
||||
input_report_abs(ext->input, ABS_HAT1Y, val[3]);
|
||||
|
||||
input_sync(ext->input);
|
||||
}
|
||||
|
||||
/* call this with state.lock spinlock held */
|
||||
void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
|
||||
{
|
||||
|
@ -523,6 +613,8 @@ void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
|
|||
handler_nunchuck(ext, payload);
|
||||
} else if (ext->ext_type == WIIEXT_CLASSIC) {
|
||||
handler_classic(ext, payload);
|
||||
} else if (ext->ext_type == WIIEXT_BALANCE_BOARD) {
|
||||
handler_balance_board(ext, payload);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -551,6 +643,11 @@ static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
|
|||
return sprintf(buf, "motionp+classic\n");
|
||||
else
|
||||
return sprintf(buf, "classic\n");
|
||||
} else if (type == WIIEXT_BALANCE_BOARD) {
|
||||
if (motionp)
|
||||
return sprintf(buf, "motionp+balanceboard\n");
|
||||
else
|
||||
return sprintf(buf, "balanceboard\n");
|
||||
} else {
|
||||
if (motionp)
|
||||
return sprintf(buf, "motionp\n");
|
||||
|
|
|
@ -42,6 +42,7 @@ static struct cdev hidraw_cdev;
|
|||
static struct class *hidraw_class;
|
||||
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
|
||||
static DEFINE_MUTEX(minors_lock);
|
||||
static void drop_ref(struct hidraw *hid, int exists_bit);
|
||||
|
||||
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
|
@ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
|
|||
__u8 *buf;
|
||||
int ret = 0;
|
||||
|
||||
if (!hidraw_table[minor]) {
|
||||
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
@ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
|
|||
}
|
||||
|
||||
mutex_lock(&minors_lock);
|
||||
if (!hidraw_table[minor]) {
|
||||
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
|
||||
err = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
@ -298,36 +299,12 @@ out:
|
|||
static int hidraw_release(struct inode * inode, struct file * file)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
struct hidraw *dev;
|
||||
struct hidraw_list *list = file->private_data;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_lock(&minors_lock);
|
||||
if (!hidraw_table[minor]) {
|
||||
ret = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
drop_ref(hidraw_table[minor], 0);
|
||||
list_del(&list->node);
|
||||
dev = hidraw_table[minor];
|
||||
if (!--dev->open) {
|
||||
if (list->hidraw->exist) {
|
||||
hid_hw_power(dev->hid, PM_HINT_NORMAL);
|
||||
hid_hw_close(dev->hid);
|
||||
} else {
|
||||
kfree(list->hidraw);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
|
||||
kfree(list->buffer[i].value);
|
||||
kfree(list);
|
||||
ret = 0;
|
||||
unlock:
|
||||
mutex_unlock(&minors_lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long hidraw_ioctl(struct file *file, unsigned int cmd,
|
||||
|
@ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect);
|
|||
void hidraw_disconnect(struct hid_device *hid)
|
||||
{
|
||||
struct hidraw *hidraw = hid->hidraw;
|
||||
|
||||
mutex_lock(&minors_lock);
|
||||
hidraw->exist = 0;
|
||||
|
||||
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
||||
|
||||
hidraw_table[hidraw->minor] = NULL;
|
||||
|
||||
if (hidraw->open) {
|
||||
hid_hw_close(hid);
|
||||
wake_up_interruptible(&hidraw->wait);
|
||||
} else {
|
||||
kfree(hidraw);
|
||||
}
|
||||
mutex_unlock(&minors_lock);
|
||||
drop_ref(hidraw, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidraw_disconnect);
|
||||
|
||||
|
@ -559,21 +522,28 @@ int __init hidraw_init(void)
|
|||
|
||||
if (result < 0) {
|
||||
pr_warn("can't get major number\n");
|
||||
result = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hidraw_class = class_create(THIS_MODULE, "hidraw");
|
||||
if (IS_ERR(hidraw_class)) {
|
||||
result = PTR_ERR(hidraw_class);
|
||||
unregister_chrdev(hidraw_major, "hidraw");
|
||||
goto out;
|
||||
goto error_cdev;
|
||||
}
|
||||
|
||||
cdev_init(&hidraw_cdev, &hidraw_ops);
|
||||
cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
|
||||
result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
|
||||
if (result < 0)
|
||||
goto error_class;
|
||||
|
||||
out:
|
||||
return result;
|
||||
|
||||
error_class:
|
||||
class_destroy(hidraw_class);
|
||||
error_cdev:
|
||||
unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void hidraw_exit(void)
|
||||
|
@ -585,3 +555,23 @@ void hidraw_exit(void)
|
|||
unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
|
||||
|
||||
}
|
||||
|
||||
static void drop_ref(struct hidraw *hidraw, int exists_bit)
|
||||
{
|
||||
mutex_lock(&minors_lock);
|
||||
if (exists_bit) {
|
||||
hid_hw_close(hidraw->hid);
|
||||
hidraw->exist = 0;
|
||||
if (hidraw->open)
|
||||
wake_up_interruptible(&hidraw->wait);
|
||||
} else {
|
||||
--hidraw->open;
|
||||
}
|
||||
|
||||
if (!hidraw->open && !hidraw->exist) {
|
||||
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
||||
hidraw_table[hidraw->minor] = NULL;
|
||||
kfree(hidraw);
|
||||
}
|
||||
mutex_unlock(&minors_lock);
|
||||
}
|
||||
|
|
|
@ -1415,20 +1415,20 @@ static int hid_post_reset(struct usb_interface *intf)
|
|||
* configuration descriptors passed, we already know that
|
||||
* the size of the HID report descriptor has not changed.
|
||||
*/
|
||||
rdesc = kmalloc(hid->rsize, GFP_KERNEL);
|
||||
rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
|
||||
if (!rdesc) {
|
||||
dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
|
||||
return 1;
|
||||
}
|
||||
status = hid_get_class_descriptor(dev,
|
||||
interface->desc.bInterfaceNumber,
|
||||
HID_DT_REPORT, rdesc, hid->rsize);
|
||||
HID_DT_REPORT, rdesc, hid->dev_rsize);
|
||||
if (status < 0) {
|
||||
dbg_hid("reading report descriptor failed (post_reset)\n");
|
||||
kfree(rdesc);
|
||||
return 1;
|
||||
}
|
||||
status = memcmp(rdesc, hid->rdesc, hid->rsize);
|
||||
status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize);
|
||||
kfree(rdesc);
|
||||
if (status != 0) {
|
||||
dbg_hid("report descriptor changed\n");
|
||||
|
|
|
@ -70,12 +70,13 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
|
||||
|
|
|
@ -54,16 +54,9 @@ struct evdev_client {
|
|||
static struct evdev *evdev_table[EVDEV_MINORS];
|
||||
static DEFINE_MUTEX(evdev_table_mutex);
|
||||
|
||||
static void evdev_pass_event(struct evdev_client *client,
|
||||
struct input_event *event,
|
||||
ktime_t mono, ktime_t real)
|
||||
static void __pass_event(struct evdev_client *client,
|
||||
const struct input_event *event)
|
||||
{
|
||||
event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
|
||||
mono : real);
|
||||
|
||||
/* Interrupts are disabled, just acquire the lock. */
|
||||
spin_lock(&client->buffer_lock);
|
||||
|
||||
client->buffer[client->head++] = *event;
|
||||
client->head &= client->bufsize - 1;
|
||||
|
||||
|
@ -86,8 +79,63 @@ static void evdev_pass_event(struct evdev_client *client,
|
|||
client->packet_head = client->head;
|
||||
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
}
|
||||
|
||||
static void evdev_pass_values(struct evdev_client *client,
|
||||
const struct input_value *vals, unsigned int count,
|
||||
ktime_t mono, ktime_t real)
|
||||
{
|
||||
struct evdev *evdev = client->evdev;
|
||||
const struct input_value *v;
|
||||
struct input_event event;
|
||||
bool wakeup = false;
|
||||
|
||||
event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
|
||||
mono : real);
|
||||
|
||||
/* Interrupts are disabled, just acquire the lock. */
|
||||
spin_lock(&client->buffer_lock);
|
||||
|
||||
for (v = vals; v != vals + count; v++) {
|
||||
event.type = v->type;
|
||||
event.code = v->code;
|
||||
event.value = v->value;
|
||||
__pass_event(client, &event);
|
||||
if (v->type == EV_SYN && v->code == SYN_REPORT)
|
||||
wakeup = true;
|
||||
}
|
||||
|
||||
spin_unlock(&client->buffer_lock);
|
||||
|
||||
if (wakeup)
|
||||
wake_up_interruptible(&evdev->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass incoming events to all connected clients.
|
||||
*/
|
||||
static void evdev_events(struct input_handle *handle,
|
||||
const struct input_value *vals, unsigned int count)
|
||||
{
|
||||
struct evdev *evdev = handle->private;
|
||||
struct evdev_client *client;
|
||||
ktime_t time_mono, time_real;
|
||||
|
||||
time_mono = ktime_get();
|
||||
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
client = rcu_dereference(evdev->grab);
|
||||
|
||||
if (client)
|
||||
evdev_pass_values(client, vals, count, time_mono, time_real);
|
||||
else
|
||||
list_for_each_entry_rcu(client, &evdev->client_list, node)
|
||||
evdev_pass_values(client, vals, count,
|
||||
time_mono, time_real);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -96,32 +144,9 @@ static void evdev_pass_event(struct evdev_client *client,
|
|||
static void evdev_event(struct input_handle *handle,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct evdev *evdev = handle->private;
|
||||
struct evdev_client *client;
|
||||
struct input_event event;
|
||||
ktime_t time_mono, time_real;
|
||||
struct input_value vals[] = { { type, code, value } };
|
||||
|
||||
time_mono = ktime_get();
|
||||
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
|
||||
|
||||
event.type = type;
|
||||
event.code = code;
|
||||
event.value = value;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
client = rcu_dereference(evdev->grab);
|
||||
|
||||
if (client)
|
||||
evdev_pass_event(client, &event, time_mono, time_real);
|
||||
else
|
||||
list_for_each_entry_rcu(client, &evdev->client_list, node)
|
||||
evdev_pass_event(client, &event, time_mono, time_real);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (type == EV_SYN && code == SYN_REPORT)
|
||||
wake_up_interruptible(&evdev->wait);
|
||||
evdev_events(handle, vals, 1);
|
||||
}
|
||||
|
||||
static int evdev_fasync(int fd, struct file *file, int on)
|
||||
|
@ -653,20 +678,22 @@ static int evdev_handle_mt_request(struct input_dev *dev,
|
|||
unsigned int size,
|
||||
int __user *ip)
|
||||
{
|
||||
const struct input_mt_slot *mt = dev->mt;
|
||||
const struct input_mt *mt = dev->mt;
|
||||
unsigned int code;
|
||||
int max_slots;
|
||||
int i;
|
||||
|
||||
if (get_user(code, &ip[0]))
|
||||
return -EFAULT;
|
||||
if (!input_is_mt_value(code))
|
||||
if (!mt || !input_is_mt_value(code))
|
||||
return -EINVAL;
|
||||
|
||||
max_slots = (size - sizeof(__u32)) / sizeof(__s32);
|
||||
for (i = 0; i < dev->mtsize && i < max_slots; i++)
|
||||
if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i]))
|
||||
for (i = 0; i < mt->num_slots && i < max_slots; i++) {
|
||||
int value = input_mt_get_value(&mt->slots[i], code);
|
||||
if (put_user(value, &ip[1 + i]))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1048,6 +1075,7 @@ MODULE_DEVICE_TABLE(input, evdev_ids);
|
|||
|
||||
static struct input_handler evdev_handler = {
|
||||
.event = evdev_event,
|
||||
.events = evdev_events,
|
||||
.connect = evdev_connect,
|
||||
.disconnect = evdev_disconnect,
|
||||
.fops = &evdev_fops,
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
|
||||
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
|
||||
|
||||
static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
|
||||
{
|
||||
if (dev->absinfo && test_bit(src, dev->absbit)) {
|
||||
dev->absinfo[dst] = dev->absinfo[src];
|
||||
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* input_mt_init_slots() - initialize MT input slots
|
||||
* @dev: input device supporting MT events and finger tracking
|
||||
|
@ -25,29 +33,63 @@
|
|||
* May be called repeatedly. Returns -EINVAL if attempting to
|
||||
* reinitialize with a different number of slots.
|
||||
*/
|
||||
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots)
|
||||
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct input_mt *mt = dev->mt;
|
||||
int i;
|
||||
|
||||
if (!num_slots)
|
||||
return 0;
|
||||
if (dev->mt)
|
||||
return dev->mtsize != num_slots ? -EINVAL : 0;
|
||||
if (mt)
|
||||
return mt->num_slots != num_slots ? -EINVAL : 0;
|
||||
|
||||
dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL);
|
||||
if (!dev->mt)
|
||||
return -ENOMEM;
|
||||
mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL);
|
||||
if (!mt)
|
||||
goto err_mem;
|
||||
|
||||
dev->mtsize = num_slots;
|
||||
mt->num_slots = num_slots;
|
||||
mt->flags = flags;
|
||||
input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
|
||||
input_set_events_per_packet(dev, 6 * num_slots);
|
||||
|
||||
if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) {
|
||||
__set_bit(EV_KEY, dev->evbit);
|
||||
__set_bit(BTN_TOUCH, dev->keybit);
|
||||
|
||||
copy_abs(dev, ABS_X, ABS_MT_POSITION_X);
|
||||
copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y);
|
||||
copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE);
|
||||
}
|
||||
if (flags & INPUT_MT_POINTER) {
|
||||
__set_bit(BTN_TOOL_FINGER, dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
|
||||
if (num_slots >= 3)
|
||||
__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
|
||||
if (num_slots >= 4)
|
||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||
if (num_slots >= 5)
|
||||
__set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
|
||||
__set_bit(INPUT_PROP_POINTER, dev->propbit);
|
||||
}
|
||||
if (flags & INPUT_MT_DIRECT)
|
||||
__set_bit(INPUT_PROP_DIRECT, dev->propbit);
|
||||
if (flags & INPUT_MT_TRACK) {
|
||||
unsigned int n2 = num_slots * num_slots;
|
||||
mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
|
||||
if (!mt->red)
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
/* Mark slots as 'unused' */
|
||||
for (i = 0; i < num_slots; i++)
|
||||
input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1);
|
||||
input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
|
||||
|
||||
dev->mt = mt;
|
||||
return 0;
|
||||
err_mem:
|
||||
kfree(mt);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_init_slots);
|
||||
|
||||
|
@ -60,11 +102,11 @@ EXPORT_SYMBOL(input_mt_init_slots);
|
|||
*/
|
||||
void input_mt_destroy_slots(struct input_dev *dev)
|
||||
{
|
||||
kfree(dev->mt);
|
||||
if (dev->mt) {
|
||||
kfree(dev->mt->red);
|
||||
kfree(dev->mt);
|
||||
}
|
||||
dev->mt = NULL;
|
||||
dev->mtsize = 0;
|
||||
dev->slot = 0;
|
||||
dev->trkid = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_destroy_slots);
|
||||
|
||||
|
@ -83,18 +125,24 @@ EXPORT_SYMBOL(input_mt_destroy_slots);
|
|||
void input_mt_report_slot_state(struct input_dev *dev,
|
||||
unsigned int tool_type, bool active)
|
||||
{
|
||||
struct input_mt_slot *mt;
|
||||
struct input_mt *mt = dev->mt;
|
||||
struct input_mt_slot *slot;
|
||||
int id;
|
||||
|
||||
if (!dev->mt || !active) {
|
||||
if (!mt)
|
||||
return;
|
||||
|
||||
slot = &mt->slots[mt->slot];
|
||||
slot->frame = mt->frame;
|
||||
|
||||
if (!active) {
|
||||
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
mt = &dev->mt[dev->slot];
|
||||
id = input_mt_get_value(mt, ABS_MT_TRACKING_ID);
|
||||
if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type)
|
||||
id = input_mt_new_trkid(dev);
|
||||
id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
|
||||
if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type)
|
||||
id = input_mt_new_trkid(mt);
|
||||
|
||||
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
|
||||
input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);
|
||||
|
@ -135,13 +183,19 @@ EXPORT_SYMBOL(input_mt_report_finger_count);
|
|||
*/
|
||||
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
|
||||
{
|
||||
struct input_mt_slot *oldest = NULL;
|
||||
int oldid = dev->trkid;
|
||||
int count = 0;
|
||||
int i;
|
||||
struct input_mt *mt = dev->mt;
|
||||
struct input_mt_slot *oldest;
|
||||
int oldid, count, i;
|
||||
|
||||
for (i = 0; i < dev->mtsize; ++i) {
|
||||
struct input_mt_slot *ps = &dev->mt[i];
|
||||
if (!mt)
|
||||
return;
|
||||
|
||||
oldest = 0;
|
||||
oldid = mt->trkid;
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < mt->num_slots; ++i) {
|
||||
struct input_mt_slot *ps = &mt->slots[i];
|
||||
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
|
||||
|
||||
if (id < 0)
|
||||
|
@ -160,13 +214,208 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
|
|||
if (oldest) {
|
||||
int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
|
||||
int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y);
|
||||
int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
|
||||
|
||||
input_event(dev, EV_ABS, ABS_X, x);
|
||||
input_event(dev, EV_ABS, ABS_Y, y);
|
||||
input_event(dev, EV_ABS, ABS_PRESSURE, p);
|
||||
|
||||
if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
|
||||
int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
|
||||
input_event(dev, EV_ABS, ABS_PRESSURE, p);
|
||||
}
|
||||
} else {
|
||||
input_event(dev, EV_ABS, ABS_PRESSURE, 0);
|
||||
if (test_bit(ABS_MT_PRESSURE, dev->absbit))
|
||||
input_event(dev, EV_ABS, ABS_PRESSURE, 0);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_report_pointer_emulation);
|
||||
|
||||
/**
|
||||
* input_mt_sync_frame() - synchronize mt frame
|
||||
* @dev: input device with allocated MT slots
|
||||
*
|
||||
* Close the frame and prepare the internal state for a new one.
|
||||
* Depending on the flags, marks unused slots as inactive and performs
|
||||
* pointer emulation.
|
||||
*/
|
||||
void input_mt_sync_frame(struct input_dev *dev)
|
||||
{
|
||||
struct input_mt *mt = dev->mt;
|
||||
struct input_mt_slot *s;
|
||||
|
||||
if (!mt)
|
||||
return;
|
||||
|
||||
if (mt->flags & INPUT_MT_DROP_UNUSED) {
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
|
||||
if (s->frame == mt->frame)
|
||||
continue;
|
||||
input_mt_slot(dev, s - mt->slots);
|
||||
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
|
||||
}
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER));
|
||||
|
||||
mt->frame++;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_sync_frame);
|
||||
|
||||
static int adjust_dual(int *begin, int step, int *end, int eq)
|
||||
{
|
||||
int f, *p, s, c;
|
||||
|
||||
if (begin == end)
|
||||
return 0;
|
||||
|
||||
f = *begin;
|
||||
p = begin + step;
|
||||
s = p == end ? f + 1 : *p;
|
||||
|
||||
for (; p != end; p += step)
|
||||
if (*p < f)
|
||||
s = f, f = *p;
|
||||
else if (*p < s)
|
||||
s = *p;
|
||||
|
||||
c = (f + s + 1) / 2;
|
||||
if (c == 0 || (c > 0 && !eq))
|
||||
return 0;
|
||||
if (s < 0)
|
||||
c *= 2;
|
||||
|
||||
for (p = begin; p != end; p += step)
|
||||
*p -= c;
|
||||
|
||||
return (c < s && s <= 0) || (f >= 0 && f < c);
|
||||
}
|
||||
|
||||
static void find_reduced_matrix(int *w, int nr, int nc, int nrc)
|
||||
{
|
||||
int i, k, sum;
|
||||
|
||||
for (k = 0; k < nrc; k++) {
|
||||
for (i = 0; i < nr; i++)
|
||||
adjust_dual(w + i, nr, w + i + nrc, nr <= nc);
|
||||
sum = 0;
|
||||
for (i = 0; i < nrc; i += nr)
|
||||
sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr);
|
||||
if (!sum)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int input_mt_set_matrix(struct input_mt *mt,
|
||||
const struct input_mt_pos *pos, int num_pos)
|
||||
{
|
||||
const struct input_mt_pos *p;
|
||||
struct input_mt_slot *s;
|
||||
int *w = mt->red;
|
||||
int x, y;
|
||||
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
|
||||
if (!input_mt_is_active(s))
|
||||
continue;
|
||||
x = input_mt_get_value(s, ABS_MT_POSITION_X);
|
||||
y = input_mt_get_value(s, ABS_MT_POSITION_Y);
|
||||
for (p = pos; p != pos + num_pos; p++) {
|
||||
int dx = x - p->x, dy = y - p->y;
|
||||
*w++ = dx * dx + dy * dy;
|
||||
}
|
||||
}
|
||||
|
||||
return w - mt->red;
|
||||
}
|
||||
|
||||
static void input_mt_set_slots(struct input_mt *mt,
|
||||
int *slots, int num_pos)
|
||||
{
|
||||
struct input_mt_slot *s;
|
||||
int *w = mt->red, *p;
|
||||
|
||||
for (p = slots; p != slots + num_pos; p++)
|
||||
*p = -1;
|
||||
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
|
||||
if (!input_mt_is_active(s))
|
||||
continue;
|
||||
for (p = slots; p != slots + num_pos; p++)
|
||||
if (*w++ < 0)
|
||||
*p = s - mt->slots;
|
||||
}
|
||||
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
|
||||
if (input_mt_is_active(s))
|
||||
continue;
|
||||
for (p = slots; p != slots + num_pos; p++)
|
||||
if (*p < 0) {
|
||||
*p = s - mt->slots;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* input_mt_assign_slots() - perform a best-match assignment
|
||||
* @dev: input device with allocated MT slots
|
||||
* @slots: the slot assignment to be filled
|
||||
* @pos: the position array to match
|
||||
* @num_pos: number of positions
|
||||
*
|
||||
* Performs a best match against the current contacts and returns
|
||||
* the slot assignment list. New contacts are assigned to unused
|
||||
* slots.
|
||||
*
|
||||
* Returns zero on success, or negative error in case of failure.
|
||||
*/
|
||||
int input_mt_assign_slots(struct input_dev *dev, int *slots,
|
||||
const struct input_mt_pos *pos, int num_pos)
|
||||
{
|
||||
struct input_mt *mt = dev->mt;
|
||||
int nrc;
|
||||
|
||||
if (!mt || !mt->red)
|
||||
return -ENXIO;
|
||||
if (num_pos > mt->num_slots)
|
||||
return -EINVAL;
|
||||
if (num_pos < 1)
|
||||
return 0;
|
||||
|
||||
nrc = input_mt_set_matrix(mt, pos, num_pos);
|
||||
find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc);
|
||||
input_mt_set_slots(mt, slots, num_pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_assign_slots);
|
||||
|
||||
/**
|
||||
* input_mt_get_slot_by_key() - return slot matching key
|
||||
* @dev: input device with allocated MT slots
|
||||
* @key: the key of the sought slot
|
||||
*
|
||||
* Returns the slot of the given key, if it exists, otherwise
|
||||
* set the key on the first unused slot and return.
|
||||
*
|
||||
* If no available slot can be found, -1 is returned.
|
||||
*/
|
||||
int input_mt_get_slot_by_key(struct input_dev *dev, int key)
|
||||
{
|
||||
struct input_mt *mt = dev->mt;
|
||||
struct input_mt_slot *s;
|
||||
|
||||
if (!mt)
|
||||
return -1;
|
||||
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
|
||||
if (input_mt_is_active(s) && s->key == key)
|
||||
return s - mt->slots;
|
||||
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
|
||||
if (!input_mt_is_active(s)) {
|
||||
s->key = key;
|
||||
return s - mt->slots;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL(input_mt_get_slot_by_key);
|
||||
|
|
|
@ -47,6 +47,8 @@ static DEFINE_MUTEX(input_mutex);
|
|||
|
||||
static struct input_handler *input_table[8];
|
||||
|
||||
static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
|
||||
|
||||
static inline int is_event_supported(unsigned int code,
|
||||
unsigned long *bm, unsigned int max)
|
||||
{
|
||||
|
@ -69,79 +71,6 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz)
|
|||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass event first through all filters and then, if event has not been
|
||||
* filtered out, through all open handles. This function is called with
|
||||
* dev->event_lock held and interrupts disabled.
|
||||
*/
|
||||
static void input_pass_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct input_handler *handler;
|
||||
struct input_handle *handle;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
handle = rcu_dereference(dev->grab);
|
||||
if (handle)
|
||||
handle->handler->event(handle, type, code, value);
|
||||
else {
|
||||
bool filtered = false;
|
||||
|
||||
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
|
||||
if (!handle->open)
|
||||
continue;
|
||||
|
||||
handler = handle->handler;
|
||||
if (!handler->filter) {
|
||||
if (filtered)
|
||||
break;
|
||||
|
||||
handler->event(handle, type, code, value);
|
||||
|
||||
} else if (handler->filter(handle, type, code, value))
|
||||
filtered = true;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate software autorepeat event. Note that we take
|
||||
* dev->event_lock here to avoid racing with input_event
|
||||
* which may cause keys get "stuck".
|
||||
*/
|
||||
static void input_repeat_key(unsigned long data)
|
||||
{
|
||||
struct input_dev *dev = (void *) data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
if (test_bit(dev->repeat_key, dev->key) &&
|
||||
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
|
||||
|
||||
input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
|
||||
|
||||
if (dev->sync) {
|
||||
/*
|
||||
* Only send SYN_REPORT if we are not in a middle
|
||||
* of driver parsing a new hardware packet.
|
||||
* Otherwise assume that the driver will send
|
||||
* SYN_REPORT once it's done.
|
||||
*/
|
||||
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
|
||||
}
|
||||
|
||||
if (dev->rep[REP_PERIOD])
|
||||
mod_timer(&dev->timer, jiffies +
|
||||
msecs_to_jiffies(dev->rep[REP_PERIOD]));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
static void input_start_autorepeat(struct input_dev *dev, int code)
|
||||
{
|
||||
if (test_bit(EV_REP, dev->evbit) &&
|
||||
|
@ -158,14 +87,128 @@ static void input_stop_autorepeat(struct input_dev *dev)
|
|||
del_timer(&dev->timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass event first through all filters and then, if event has not been
|
||||
* filtered out, through all open handles. This function is called with
|
||||
* dev->event_lock held and interrupts disabled.
|
||||
*/
|
||||
static unsigned int input_to_handler(struct input_handle *handle,
|
||||
struct input_value *vals, unsigned int count)
|
||||
{
|
||||
struct input_handler *handler = handle->handler;
|
||||
struct input_value *end = vals;
|
||||
struct input_value *v;
|
||||
|
||||
for (v = vals; v != vals + count; v++) {
|
||||
if (handler->filter &&
|
||||
handler->filter(handle, v->type, v->code, v->value))
|
||||
continue;
|
||||
if (end != v)
|
||||
*end = *v;
|
||||
end++;
|
||||
}
|
||||
|
||||
count = end - vals;
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
if (handler->events)
|
||||
handler->events(handle, vals, count);
|
||||
else if (handler->event)
|
||||
for (v = vals; v != end; v++)
|
||||
handler->event(handle, v->type, v->code, v->value);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass values first through all filters and then, if event has not been
|
||||
* filtered out, through all open handles. This function is called with
|
||||
* dev->event_lock held and interrupts disabled.
|
||||
*/
|
||||
static void input_pass_values(struct input_dev *dev,
|
||||
struct input_value *vals, unsigned int count)
|
||||
{
|
||||
struct input_handle *handle;
|
||||
struct input_value *v;
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
handle = rcu_dereference(dev->grab);
|
||||
if (handle) {
|
||||
count = input_to_handler(handle, vals, count);
|
||||
} else {
|
||||
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
|
||||
if (handle->open)
|
||||
count = input_to_handler(handle, vals, count);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
add_input_randomness(vals->type, vals->code, vals->value);
|
||||
|
||||
/* trigger auto repeat for key events */
|
||||
for (v = vals; v != vals + count; v++) {
|
||||
if (v->type == EV_KEY && v->value != 2) {
|
||||
if (v->value)
|
||||
input_start_autorepeat(dev, v->code);
|
||||
else
|
||||
input_stop_autorepeat(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void input_pass_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct input_value vals[] = { { type, code, value } };
|
||||
|
||||
input_pass_values(dev, vals, ARRAY_SIZE(vals));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate software autorepeat event. Note that we take
|
||||
* dev->event_lock here to avoid racing with input_event
|
||||
* which may cause keys get "stuck".
|
||||
*/
|
||||
static void input_repeat_key(unsigned long data)
|
||||
{
|
||||
struct input_dev *dev = (void *) data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
if (test_bit(dev->repeat_key, dev->key) &&
|
||||
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
|
||||
struct input_value vals[] = {
|
||||
{ EV_KEY, dev->repeat_key, 2 },
|
||||
input_value_sync
|
||||
};
|
||||
|
||||
input_pass_values(dev, vals, ARRAY_SIZE(vals));
|
||||
|
||||
if (dev->rep[REP_PERIOD])
|
||||
mod_timer(&dev->timer, jiffies +
|
||||
msecs_to_jiffies(dev->rep[REP_PERIOD]));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
#define INPUT_IGNORE_EVENT 0
|
||||
#define INPUT_PASS_TO_HANDLERS 1
|
||||
#define INPUT_PASS_TO_DEVICE 2
|
||||
#define INPUT_SLOT 4
|
||||
#define INPUT_FLUSH 8
|
||||
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
|
||||
|
||||
static int input_handle_abs_event(struct input_dev *dev,
|
||||
unsigned int code, int *pval)
|
||||
{
|
||||
struct input_mt *mt = dev->mt;
|
||||
bool is_mt_event;
|
||||
int *pold;
|
||||
|
||||
|
@ -174,8 +217,8 @@ static int input_handle_abs_event(struct input_dev *dev,
|
|||
* "Stage" the event; we'll flush it later, when we
|
||||
* get actual touch data.
|
||||
*/
|
||||
if (*pval >= 0 && *pval < dev->mtsize)
|
||||
dev->slot = *pval;
|
||||
if (mt && *pval >= 0 && *pval < mt->num_slots)
|
||||
mt->slot = *pval;
|
||||
|
||||
return INPUT_IGNORE_EVENT;
|
||||
}
|
||||
|
@ -184,9 +227,8 @@ static int input_handle_abs_event(struct input_dev *dev,
|
|||
|
||||
if (!is_mt_event) {
|
||||
pold = &dev->absinfo[code].value;
|
||||
} else if (dev->mt) {
|
||||
struct input_mt_slot *mtslot = &dev->mt[dev->slot];
|
||||
pold = &mtslot->abs[code - ABS_MT_FIRST];
|
||||
} else if (mt) {
|
||||
pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST];
|
||||
} else {
|
||||
/*
|
||||
* Bypass filtering for multi-touch events when
|
||||
|
@ -205,16 +247,16 @@ static int input_handle_abs_event(struct input_dev *dev,
|
|||
}
|
||||
|
||||
/* Flush pending "slot" event */
|
||||
if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
|
||||
input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
|
||||
input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
|
||||
if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
|
||||
input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
|
||||
return INPUT_PASS_TO_HANDLERS | INPUT_SLOT;
|
||||
}
|
||||
|
||||
return INPUT_PASS_TO_HANDLERS;
|
||||
}
|
||||
|
||||
static void input_handle_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
static int input_get_disposition(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
int disposition = INPUT_IGNORE_EVENT;
|
||||
|
||||
|
@ -227,37 +269,34 @@ static void input_handle_event(struct input_dev *dev,
|
|||
break;
|
||||
|
||||
case SYN_REPORT:
|
||||
if (!dev->sync) {
|
||||
dev->sync = true;
|
||||
disposition = INPUT_PASS_TO_HANDLERS;
|
||||
}
|
||||
disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
|
||||
break;
|
||||
case SYN_MT_REPORT:
|
||||
dev->sync = false;
|
||||
disposition = INPUT_PASS_TO_HANDLERS;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_KEY:
|
||||
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
|
||||
!!test_bit(code, dev->key) != value) {
|
||||
if (is_event_supported(code, dev->keybit, KEY_MAX)) {
|
||||
|
||||
if (value != 2) {
|
||||
__change_bit(code, dev->key);
|
||||
if (value)
|
||||
input_start_autorepeat(dev, code);
|
||||
else
|
||||
input_stop_autorepeat(dev);
|
||||
/* auto-repeat bypasses state updates */
|
||||
if (value == 2) {
|
||||
disposition = INPUT_PASS_TO_HANDLERS;
|
||||
break;
|
||||
}
|
||||
|
||||
disposition = INPUT_PASS_TO_HANDLERS;
|
||||
if (!!test_bit(code, dev->key) != !!value) {
|
||||
|
||||
__change_bit(code, dev->key);
|
||||
disposition = INPUT_PASS_TO_HANDLERS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_SW:
|
||||
if (is_event_supported(code, dev->swbit, SW_MAX) &&
|
||||
!!test_bit(code, dev->sw) != value) {
|
||||
!!test_bit(code, dev->sw) != !!value) {
|
||||
|
||||
__change_bit(code, dev->sw);
|
||||
disposition = INPUT_PASS_TO_HANDLERS;
|
||||
|
@ -284,7 +323,7 @@ static void input_handle_event(struct input_dev *dev,
|
|||
|
||||
case EV_LED:
|
||||
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
|
||||
!!test_bit(code, dev->led) != value) {
|
||||
!!test_bit(code, dev->led) != !!value) {
|
||||
|
||||
__change_bit(code, dev->led);
|
||||
disposition = INPUT_PASS_TO_ALL;
|
||||
|
@ -317,14 +356,48 @@ static void input_handle_event(struct input_dev *dev,
|
|||
break;
|
||||
}
|
||||
|
||||
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
||||
dev->sync = false;
|
||||
return disposition;
|
||||
}
|
||||
|
||||
static void input_handle_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
int disposition;
|
||||
|
||||
disposition = input_get_disposition(dev, type, code, value);
|
||||
|
||||
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
|
||||
dev->event(dev, type, code, value);
|
||||
|
||||
if (disposition & INPUT_PASS_TO_HANDLERS)
|
||||
input_pass_event(dev, type, code, value);
|
||||
if (!dev->vals)
|
||||
return;
|
||||
|
||||
if (disposition & INPUT_PASS_TO_HANDLERS) {
|
||||
struct input_value *v;
|
||||
|
||||
if (disposition & INPUT_SLOT) {
|
||||
v = &dev->vals[dev->num_vals++];
|
||||
v->type = EV_ABS;
|
||||
v->code = ABS_MT_SLOT;
|
||||
v->value = dev->mt->slot;
|
||||
}
|
||||
|
||||
v = &dev->vals[dev->num_vals++];
|
||||
v->type = type;
|
||||
v->code = code;
|
||||
v->value = value;
|
||||
}
|
||||
|
||||
if (disposition & INPUT_FLUSH) {
|
||||
if (dev->num_vals >= 2)
|
||||
input_pass_values(dev, dev->vals, dev->num_vals);
|
||||
dev->num_vals = 0;
|
||||
} else if (dev->num_vals >= dev->max_vals - 2) {
|
||||
dev->vals[dev->num_vals++] = input_value_sync;
|
||||
input_pass_values(dev, dev->vals, dev->num_vals);
|
||||
dev->num_vals = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -352,7 +425,6 @@ void input_event(struct input_dev *dev,
|
|||
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
add_input_randomness(type, code, value);
|
||||
input_handle_event(dev, type, code, value);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
@ -831,10 +903,12 @@ int input_set_keycode(struct input_dev *dev,
|
|||
if (test_bit(EV_KEY, dev->evbit) &&
|
||||
!is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
|
||||
__test_and_clear_bit(old_keycode, dev->key)) {
|
||||
struct input_value vals[] = {
|
||||
{ EV_KEY, old_keycode, 0 },
|
||||
input_value_sync
|
||||
};
|
||||
|
||||
input_pass_event(dev, EV_KEY, old_keycode, 0);
|
||||
if (dev->sync)
|
||||
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
|
||||
input_pass_values(dev, vals, ARRAY_SIZE(vals));
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -1416,6 +1490,7 @@ static void input_dev_release(struct device *device)
|
|||
input_ff_destroy(dev);
|
||||
input_mt_destroy_slots(dev);
|
||||
kfree(dev->absinfo);
|
||||
kfree(dev->vals);
|
||||
kfree(dev);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
|
@ -1751,8 +1826,8 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
|
|||
int i;
|
||||
unsigned int events;
|
||||
|
||||
if (dev->mtsize) {
|
||||
mt_slots = dev->mtsize;
|
||||
if (dev->mt) {
|
||||
mt_slots = dev->mt->num_slots;
|
||||
} else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) {
|
||||
mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum -
|
||||
dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1,
|
||||
|
@ -1778,6 +1853,9 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
|
|||
if (test_bit(i, dev->relbit))
|
||||
events++;
|
||||
|
||||
/* Make room for KEY and MSC events */
|
||||
events += 7;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
|
@ -1816,6 +1894,7 @@ int input_register_device(struct input_dev *dev)
|
|||
{
|
||||
static atomic_t input_no = ATOMIC_INIT(0);
|
||||
struct input_handler *handler;
|
||||
unsigned int packet_size;
|
||||
const char *path;
|
||||
int error;
|
||||
|
||||
|
@ -1828,9 +1907,14 @@ int input_register_device(struct input_dev *dev)
|
|||
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
|
||||
input_cleanse_bitmasks(dev);
|
||||
|
||||
if (!dev->hint_events_per_packet)
|
||||
dev->hint_events_per_packet =
|
||||
input_estimate_events_per_packet(dev);
|
||||
packet_size = input_estimate_events_per_packet(dev);
|
||||
if (dev->hint_events_per_packet < packet_size)
|
||||
dev->hint_events_per_packet = packet_size;
|
||||
|
||||
dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
|
||||
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
|
||||
if (!dev->vals)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* If delay and period are pre-set by the driver, then autorepeating
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1620,7 +1620,7 @@ int alps_init(struct psmouse *psmouse)
|
|||
case ALPS_PROTO_V3:
|
||||
case ALPS_PROTO_V4:
|
||||
set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
|
||||
input_mt_init_slots(dev1, 2);
|
||||
input_mt_init_slots(dev1, 2, 0);
|
||||
input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0);
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
#define USB_VENDOR_ID_APPLE 0x05ac
|
||||
|
||||
|
@ -183,26 +184,26 @@ struct tp_finger {
|
|||
__le16 abs_y; /* absolute y coodinate */
|
||||
__le16 rel_x; /* relative x coodinate */
|
||||
__le16 rel_y; /* relative y coodinate */
|
||||
__le16 size_major; /* finger size, major axis? */
|
||||
__le16 size_minor; /* finger size, minor axis? */
|
||||
__le16 tool_major; /* tool area, major axis */
|
||||
__le16 tool_minor; /* tool area, minor axis */
|
||||
__le16 orientation; /* 16384 when point, else 15 bit angle */
|
||||
__le16 force_major; /* trackpad force, major axis? */
|
||||
__le16 force_minor; /* trackpad force, minor axis? */
|
||||
__le16 touch_major; /* touch area, major axis */
|
||||
__le16 touch_minor; /* touch area, minor axis */
|
||||
__le16 unused[3]; /* zeros */
|
||||
__le16 multi; /* one finger: varies, more fingers: constant */
|
||||
} __attribute__((packed,aligned(2)));
|
||||
|
||||
/* trackpad finger data size, empirically at least ten fingers */
|
||||
#define MAX_FINGERS 16
|
||||
#define SIZEOF_FINGER sizeof(struct tp_finger)
|
||||
#define SIZEOF_ALL_FINGERS (16 * SIZEOF_FINGER)
|
||||
#define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER)
|
||||
#define MAX_FINGER_ORIENTATION 16384
|
||||
|
||||
/* device-specific parameters */
|
||||
struct bcm5974_param {
|
||||
int dim; /* logical dimension */
|
||||
int fuzz; /* logical noise value */
|
||||
int devmin; /* device minimum reading */
|
||||
int devmax; /* device maximum reading */
|
||||
int snratio; /* signal-to-noise ratio */
|
||||
int min; /* device minimum reading */
|
||||
int max; /* device maximum reading */
|
||||
};
|
||||
|
||||
/* device-specific configuration */
|
||||
|
@ -219,6 +220,7 @@ struct bcm5974_config {
|
|||
struct bcm5974_param w; /* finger width limits */
|
||||
struct bcm5974_param x; /* horizontal limits */
|
||||
struct bcm5974_param y; /* vertical limits */
|
||||
struct bcm5974_param o; /* orientation limits */
|
||||
};
|
||||
|
||||
/* logical device structure */
|
||||
|
@ -234,23 +236,16 @@ struct bcm5974 {
|
|||
struct bt_data *bt_data; /* button transferred data */
|
||||
struct urb *tp_urb; /* trackpad usb request block */
|
||||
u8 *tp_data; /* trackpad transferred data */
|
||||
int fingers; /* number of fingers on trackpad */
|
||||
const struct tp_finger *index[MAX_FINGERS]; /* finger index data */
|
||||
struct input_mt_pos pos[MAX_FINGERS]; /* position array */
|
||||
int slots[MAX_FINGERS]; /* slot assignments */
|
||||
};
|
||||
|
||||
/* logical dimensions */
|
||||
#define DIM_PRESSURE 256 /* maximum finger pressure */
|
||||
#define DIM_WIDTH 16 /* maximum finger width */
|
||||
#define DIM_X 1280 /* maximum trackpad x value */
|
||||
#define DIM_Y 800 /* maximum trackpad y value */
|
||||
|
||||
/* logical signal quality */
|
||||
#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
|
||||
#define SN_WIDTH 100 /* width signal-to-noise ratio */
|
||||
#define SN_WIDTH 25 /* width signal-to-noise ratio */
|
||||
#define SN_COORD 250 /* coordinate signal-to-noise ratio */
|
||||
|
||||
/* pressure thresholds */
|
||||
#define PRESSURE_LOW (2 * DIM_PRESSURE / SN_PRESSURE)
|
||||
#define PRESSURE_HIGH (3 * PRESSURE_LOW)
|
||||
#define SN_ORIENT 10 /* orientation signal-to-noise ratio */
|
||||
|
||||
/* device constants */
|
||||
static const struct bcm5974_config bcm5974_config_table[] = {
|
||||
|
@ -261,10 +256,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
0,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4824, 5342 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -172, 5820 }
|
||||
{ SN_PRESSURE, 0, 256 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4824, 5342 },
|
||||
{ SN_COORD, -172, 5820 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
|
||||
|
@ -273,10 +269,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
0,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4824, 4824 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -172, 4290 }
|
||||
{ SN_PRESSURE, 0, 256 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4824, 4824 },
|
||||
{ SN_COORD, -172, 4290 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
|
||||
|
@ -285,10 +282,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
HAS_INTEGRATED_BUTTON,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4460, 5166 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -75, 6700 }
|
||||
{ SN_PRESSURE, 0, 300 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4460, 5166 },
|
||||
{ SN_COORD, -75, 6700 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
|
||||
|
@ -297,10 +295,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
HAS_INTEGRATED_BUTTON,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4620, 5140 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
|
||||
{ SN_PRESSURE, 0, 300 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4620, 5140 },
|
||||
{ SN_COORD, -150, 6600 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
|
||||
|
@ -309,10 +308,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
HAS_INTEGRATED_BUTTON,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4616, 5112 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -142, 5234 }
|
||||
{ SN_PRESSURE, 0, 300 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4616, 5112 },
|
||||
{ SN_COORD, -142, 5234 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
|
||||
|
@ -321,10 +321,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
HAS_INTEGRATED_BUTTON,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4415, 5050 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -55, 6680 }
|
||||
{ SN_PRESSURE, 0, 300 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4415, 5050 },
|
||||
{ SN_COORD, -55, 6680 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
|
||||
|
@ -333,10 +334,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
HAS_INTEGRATED_BUTTON,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4620, 5140 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
|
||||
{ SN_PRESSURE, 0, 300 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4620, 5140 },
|
||||
{ SN_COORD, -150, 6600 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
|
||||
|
@ -345,10 +347,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
HAS_INTEGRATED_BUTTON,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4750, 5280 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
|
||||
{ SN_PRESSURE, 0, 300 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4750, 5280 },
|
||||
{ SN_COORD, -150, 6730 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
|
||||
|
@ -357,10 +360,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
HAS_INTEGRATED_BUTTON,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4620, 5140 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
|
||||
{ SN_PRESSURE, 0, 300 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4620, 5140 },
|
||||
{ SN_COORD, -150, 6600 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{
|
||||
USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI,
|
||||
|
@ -369,10 +373,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
|
|||
HAS_INTEGRATED_BUTTON,
|
||||
0x84, sizeof(struct bt_data),
|
||||
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
|
||||
{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
|
||||
{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
|
||||
{ DIM_X, DIM_X / SN_COORD, -4750, 5280 },
|
||||
{ DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
|
||||
{ SN_PRESSURE, 0, 300 },
|
||||
{ SN_WIDTH, 0, 2048 },
|
||||
{ SN_COORD, -4750, 5280 },
|
||||
{ SN_COORD, -150, 6730 },
|
||||
{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
@ -396,18 +401,11 @@ static inline int raw2int(__le16 x)
|
|||
return (signed short)le16_to_cpu(x);
|
||||
}
|
||||
|
||||
/* scale device data to logical dimensions (asserts devmin < devmax) */
|
||||
static inline int int2scale(const struct bcm5974_param *p, int x)
|
||||
static void set_abs(struct input_dev *input, unsigned int code,
|
||||
const struct bcm5974_param *p)
|
||||
{
|
||||
return x * p->dim / (p->devmax - p->devmin);
|
||||
}
|
||||
|
||||
/* all logical value ranges are [0,dim). */
|
||||
static inline int int2bound(const struct bcm5974_param *p, int x)
|
||||
{
|
||||
int s = int2scale(p, x);
|
||||
|
||||
return clamp_val(s, 0, p->dim - 1);
|
||||
int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0;
|
||||
input_set_abs_params(input, code, p->min, p->max, fuzz, 0);
|
||||
}
|
||||
|
||||
/* setup which logical events to report */
|
||||
|
@ -416,48 +414,30 @@ static void setup_events_to_report(struct input_dev *input_dev,
|
|||
{
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE,
|
||||
0, cfg->p.dim, cfg->p.fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_TOOL_WIDTH,
|
||||
0, cfg->w.dim, cfg->w.fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
0, cfg->x.dim, cfg->x.fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
0, cfg->y.dim, cfg->y.fuzz, 0);
|
||||
/* for synaptics only */
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0);
|
||||
input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);
|
||||
|
||||
/* finger touch area */
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
cfg->w.devmin, cfg->w.devmax, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
|
||||
cfg->w.devmin, cfg->w.devmax, 0, 0);
|
||||
set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w);
|
||||
set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);
|
||||
/* finger approach area */
|
||||
input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR,
|
||||
cfg->w.devmin, cfg->w.devmax, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR,
|
||||
cfg->w.devmin, cfg->w.devmax, 0, 0);
|
||||
set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w);
|
||||
set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);
|
||||
/* finger orientation */
|
||||
input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
|
||||
-MAX_FINGER_ORIENTATION,
|
||||
MAX_FINGER_ORIENTATION, 0, 0);
|
||||
set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);
|
||||
/* finger position */
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
cfg->x.devmin, cfg->x.devmax, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
cfg->y.devmin, cfg->y.devmax, 0, 0);
|
||||
set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x);
|
||||
set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
|
||||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
if (cfg->caps & HAS_INTEGRATED_BUTTON)
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
|
||||
|
||||
input_set_events_per_packet(input_dev, 60);
|
||||
input_mt_init_slots(input_dev, MAX_FINGERS,
|
||||
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
|
||||
}
|
||||
|
||||
/* report button data as logical button state */
|
||||
|
@ -477,24 +457,44 @@ static int report_bt_state(struct bcm5974 *dev, int size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void report_finger_data(struct input_dev *input,
|
||||
const struct bcm5974_config *cfg,
|
||||
static void report_finger_data(struct input_dev *input, int slot,
|
||||
const struct input_mt_pos *pos,
|
||||
const struct tp_finger *f)
|
||||
{
|
||||
input_mt_slot(input, slot);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
|
||||
|
||||
input_report_abs(input, ABS_MT_TOUCH_MAJOR,
|
||||
raw2int(f->force_major) << 1);
|
||||
raw2int(f->touch_major) << 1);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MINOR,
|
||||
raw2int(f->force_minor) << 1);
|
||||
raw2int(f->touch_minor) << 1);
|
||||
input_report_abs(input, ABS_MT_WIDTH_MAJOR,
|
||||
raw2int(f->size_major) << 1);
|
||||
raw2int(f->tool_major) << 1);
|
||||
input_report_abs(input, ABS_MT_WIDTH_MINOR,
|
||||
raw2int(f->size_minor) << 1);
|
||||
raw2int(f->tool_minor) << 1);
|
||||
input_report_abs(input, ABS_MT_ORIENTATION,
|
||||
MAX_FINGER_ORIENTATION - raw2int(f->orientation));
|
||||
input_report_abs(input, ABS_MT_POSITION_X, raw2int(f->abs_x));
|
||||
input_report_abs(input, ABS_MT_POSITION_Y,
|
||||
cfg->y.devmin + cfg->y.devmax - raw2int(f->abs_y));
|
||||
input_mt_sync(input);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, pos->x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
|
||||
}
|
||||
|
||||
static void report_synaptics_data(struct input_dev *input,
|
||||
const struct bcm5974_config *cfg,
|
||||
const struct tp_finger *f, int raw_n)
|
||||
{
|
||||
int abs_p = 0, abs_w = 0;
|
||||
|
||||
if (raw_n) {
|
||||
int p = raw2int(f->touch_major);
|
||||
int w = raw2int(f->tool_major);
|
||||
if (p > 0 && raw2int(f->origin)) {
|
||||
abs_p = clamp_val(256 * p / cfg->p.max, 0, 255);
|
||||
abs_w = clamp_val(16 * w / cfg->w.max, 0, 15);
|
||||
}
|
||||
}
|
||||
|
||||
input_report_abs(input, ABS_PRESSURE, abs_p);
|
||||
input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
|
||||
}
|
||||
|
||||
/* report trackpad data as logical trackpad state */
|
||||
|
@ -503,9 +503,7 @@ static int report_tp_state(struct bcm5974 *dev, int size)
|
|||
const struct bcm5974_config *c = &dev->cfg;
|
||||
const struct tp_finger *f;
|
||||
struct input_dev *input = dev->input;
|
||||
int raw_p, raw_w, raw_x, raw_y, raw_n, i;
|
||||
int ptest, origin, ibt = 0, nmin = 0, nmax = 0;
|
||||
int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0;
|
||||
int raw_n, i, n = 0;
|
||||
|
||||
if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
|
||||
return -EIO;
|
||||
|
@ -514,76 +512,29 @@ static int report_tp_state(struct bcm5974 *dev, int size)
|
|||
f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);
|
||||
raw_n = (size - c->tp_offset) / SIZEOF_FINGER;
|
||||
|
||||
/* always track the first finger; when detached, start over */
|
||||
if (raw_n) {
|
||||
|
||||
/* report raw trackpad data */
|
||||
for (i = 0; i < raw_n; i++)
|
||||
report_finger_data(input, c, &f[i]);
|
||||
|
||||
raw_p = raw2int(f->force_major);
|
||||
raw_w = raw2int(f->size_major);
|
||||
raw_x = raw2int(f->abs_x);
|
||||
raw_y = raw2int(f->abs_y);
|
||||
|
||||
dprintk(9,
|
||||
"bcm5974: "
|
||||
"raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n",
|
||||
raw_p, raw_w, raw_x, raw_y, raw_n);
|
||||
|
||||
ptest = int2bound(&c->p, raw_p);
|
||||
origin = raw2int(f->origin);
|
||||
|
||||
/* while tracking finger still valid, count all fingers */
|
||||
if (ptest > PRESSURE_LOW && origin) {
|
||||
abs_p = ptest;
|
||||
abs_w = int2bound(&c->w, raw_w);
|
||||
abs_x = int2bound(&c->x, raw_x - c->x.devmin);
|
||||
abs_y = int2bound(&c->y, c->y.devmax - raw_y);
|
||||
while (raw_n--) {
|
||||
ptest = int2bound(&c->p,
|
||||
raw2int(f->force_major));
|
||||
if (ptest > PRESSURE_LOW)
|
||||
nmax++;
|
||||
if (ptest > PRESSURE_HIGH)
|
||||
nmin++;
|
||||
f++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < raw_n; i++) {
|
||||
if (raw2int(f[i].touch_major) == 0)
|
||||
continue;
|
||||
dev->pos[n].x = raw2int(f[i].abs_x);
|
||||
dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y);
|
||||
dev->index[n++] = &f[i];
|
||||
}
|
||||
|
||||
/* set the integrated button if applicable */
|
||||
if (c->tp_type == TYPE2)
|
||||
ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
|
||||
input_mt_assign_slots(input, dev->slots, dev->pos, n);
|
||||
|
||||
if (dev->fingers < nmin)
|
||||
dev->fingers = nmin;
|
||||
if (dev->fingers > nmax)
|
||||
dev->fingers = nmax;
|
||||
for (i = 0; i < n; i++)
|
||||
report_finger_data(input, dev->slots[i],
|
||||
&dev->pos[i], dev->index[i]);
|
||||
|
||||
input_report_key(input, BTN_TOUCH, dev->fingers > 0);
|
||||
input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2);
|
||||
input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3);
|
||||
input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3);
|
||||
input_mt_sync_frame(input);
|
||||
|
||||
input_report_abs(input, ABS_PRESSURE, abs_p);
|
||||
input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
|
||||
|
||||
if (abs_p) {
|
||||
input_report_abs(input, ABS_X, abs_x);
|
||||
input_report_abs(input, ABS_Y, abs_y);
|
||||
|
||||
dprintk(8,
|
||||
"bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d "
|
||||
"nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w,
|
||||
abs_x, abs_y, nmin, nmax, dev->fingers, ibt);
|
||||
|
||||
}
|
||||
report_synaptics_data(input, c, f, raw_n);
|
||||
|
||||
/* type 2 reports button events via ibt only */
|
||||
if (c->tp_type == TYPE2)
|
||||
if (c->tp_type == TYPE2) {
|
||||
int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
|
||||
input_report_key(input, BTN_LEFT, ibt);
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
|
||||
|
@ -742,9 +693,11 @@ static int bcm5974_start_traffic(struct bcm5974 *dev)
|
|||
goto err_out;
|
||||
}
|
||||
|
||||
error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
|
||||
if (error)
|
||||
goto err_reset_mode;
|
||||
if (dev->bt_urb) {
|
||||
error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
|
||||
if (error)
|
||||
goto err_reset_mode;
|
||||
}
|
||||
|
||||
error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
|
||||
if (error)
|
||||
|
@ -868,19 +821,23 @@ static int bcm5974_probe(struct usb_interface *iface,
|
|||
mutex_init(&dev->pm_mutex);
|
||||
|
||||
/* setup urbs */
|
||||
dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!dev->bt_urb)
|
||||
goto err_free_devs;
|
||||
if (cfg->tp_type == TYPE1) {
|
||||
dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!dev->bt_urb)
|
||||
goto err_free_devs;
|
||||
}
|
||||
|
||||
dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!dev->tp_urb)
|
||||
goto err_free_bt_urb;
|
||||
|
||||
dev->bt_data = usb_alloc_coherent(dev->udev,
|
||||
if (dev->bt_urb) {
|
||||
dev->bt_data = usb_alloc_coherent(dev->udev,
|
||||
dev->cfg.bt_datalen, GFP_KERNEL,
|
||||
&dev->bt_urb->transfer_dma);
|
||||
if (!dev->bt_data)
|
||||
goto err_free_urb;
|
||||
if (!dev->bt_data)
|
||||
goto err_free_urb;
|
||||
}
|
||||
|
||||
dev->tp_data = usb_alloc_coherent(dev->udev,
|
||||
dev->cfg.tp_datalen, GFP_KERNEL,
|
||||
|
@ -888,10 +845,11 @@ static int bcm5974_probe(struct usb_interface *iface,
|
|||
if (!dev->tp_data)
|
||||
goto err_free_bt_buffer;
|
||||
|
||||
usb_fill_int_urb(dev->bt_urb, udev,
|
||||
usb_rcvintpipe(udev, cfg->bt_ep),
|
||||
dev->bt_data, dev->cfg.bt_datalen,
|
||||
bcm5974_irq_button, dev, 1);
|
||||
if (dev->bt_urb)
|
||||
usb_fill_int_urb(dev->bt_urb, udev,
|
||||
usb_rcvintpipe(udev, cfg->bt_ep),
|
||||
dev->bt_data, dev->cfg.bt_datalen,
|
||||
bcm5974_irq_button, dev, 1);
|
||||
|
||||
usb_fill_int_urb(dev->tp_urb, udev,
|
||||
usb_rcvintpipe(udev, cfg->tp_ep),
|
||||
|
@ -929,8 +887,9 @@ err_free_buffer:
|
|||
usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
|
||||
dev->tp_data, dev->tp_urb->transfer_dma);
|
||||
err_free_bt_buffer:
|
||||
usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
|
||||
dev->bt_data, dev->bt_urb->transfer_dma);
|
||||
if (dev->bt_urb)
|
||||
usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
|
||||
dev->bt_data, dev->bt_urb->transfer_dma);
|
||||
err_free_urb:
|
||||
usb_free_urb(dev->tp_urb);
|
||||
err_free_bt_urb:
|
||||
|
@ -951,8 +910,9 @@ static void bcm5974_disconnect(struct usb_interface *iface)
|
|||
input_unregister_device(dev->input);
|
||||
usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
|
||||
dev->tp_data, dev->tp_urb->transfer_dma);
|
||||
usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
|
||||
dev->bt_data, dev->bt_urb->transfer_dma);
|
||||
if (dev->bt_urb)
|
||||
usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
|
||||
dev->bt_data, dev->bt_urb->transfer_dma);
|
||||
usb_free_urb(dev->tp_urb);
|
||||
usb_free_urb(dev->bt_urb);
|
||||
kfree(dev);
|
||||
|
|
|
@ -1004,7 +1004,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
|||
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
|
||||
ETP_WMAX_V2, 0, 0);
|
||||
}
|
||||
input_mt_init_slots(dev, 2);
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
|
||||
break;
|
||||
|
@ -1035,7 +1035,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
|||
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
|
||||
ETP_WMAX_V2, 0, 0);
|
||||
/* Multitouch capable pad, up to 5 fingers. */
|
||||
input_mt_init_slots(dev, ETP_MAX_FINGERS);
|
||||
input_mt_init_slots(dev, ETP_MAX_FINGERS, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
|
||||
input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
|
||||
|
|
|
@ -971,7 +971,7 @@ static int fsp_set_input_params(struct psmouse *psmouse)
|
|||
|
||||
input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
|
||||
input_mt_init_slots(dev, 2);
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
|
||||
}
|
||||
|
|
|
@ -1232,7 +1232,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
|
|||
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
|
||||
|
||||
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
|
||||
input_mt_init_slots(dev, 2);
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
|
||||
ABS_MT_POSITION_Y);
|
||||
/* Image sensors can report per-contact pressure */
|
||||
|
@ -1244,7 +1244,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
|
|||
} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
|
||||
/* Non-image sensors with AGM use semi-mt */
|
||||
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
|
||||
input_mt_init_slots(dev, 2);
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
|
||||
ABS_MT_POSITION_Y);
|
||||
}
|
||||
|
|
|
@ -1530,7 +1530,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
|
||||
|
||||
input_mt_init_slots(input_dev, features->touch_max);
|
||||
input_mt_init_slots(input_dev, features->touch_max, 0);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
0, 255, 0, 0);
|
||||
|
@ -1575,7 +1575,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
|
||||
case TABLETPC2FG:
|
||||
if (features->device_type == BTN_TOOL_FINGER) {
|
||||
input_mt_init_slots(input_dev, features->touch_max);
|
||||
input_mt_init_slots(input_dev, features->touch_max, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
|
||||
0, MT_TOOL_MAX, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
|
@ -1631,7 +1631,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
input_mt_init_slots(input_dev, features->touch_max);
|
||||
input_mt_init_slots(input_dev, features->touch_max, 0);
|
||||
|
||||
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
|
||||
__set_bit(BTN_TOOL_TRIPLETAP,
|
||||
|
|
|
@ -1152,7 +1152,7 @@ static int __devinit mxt_probe(struct i2c_client *client,
|
|||
|
||||
/* For multi touch */
|
||||
num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
|
||||
error = input_mt_init_slots(input_dev, num_mt_slots);
|
||||
error = input_mt_init_slots(input_dev, num_mt_slots, 0);
|
||||
if (error)
|
||||
goto err_free_object;
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
|
|
|
@ -571,7 +571,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
|
|||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
0, CY_MAXZ, 0, 0);
|
||||
|
||||
input_mt_init_slots(input_dev, CY_MAX_ID);
|
||||
input_mt_init_slots(input_dev, CY_MAX_ID, 0);
|
||||
|
||||
error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
|
|
|
@ -782,7 +782,7 @@ static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client,
|
|||
0, tsdata->num_x * 64 - 1, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
0, tsdata->num_y * 64 - 1, 0, 0);
|
||||
error = input_mt_init_slots(input, MAX_SUPPORT_POINTS);
|
||||
error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to init MT slots.\n");
|
||||
goto err_free_mem;
|
||||
|
|
|
@ -204,7 +204,7 @@ static int __devinit egalax_ts_probe(struct i2c_client *client,
|
|||
ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
|
||||
input_set_abs_params(input_dev,
|
||||
ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
|
||||
input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS);
|
||||
input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
|
||||
|
||||
input_set_drvdata(input_dev, ts);
|
||||
|
||||
|
|
|
@ -252,7 +252,7 @@ static int __devinit ili210x_i2c_probe(struct i2c_client *client,
|
|||
input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
|
||||
|
||||
/* Multi touch */
|
||||
input_mt_init_slots(input, MAX_TOUCHES);
|
||||
input_mt_init_slots(input, MAX_TOUCHES, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
|
||||
|
||||
|
|
|
@ -404,7 +404,7 @@ static int __devinit mms114_probe(struct i2c_client *client,
|
|||
input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0);
|
||||
|
||||
/* For multi touch */
|
||||
input_mt_init_slots(input_dev, MMS114_MAX_TOUCH);
|
||||
input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
0, MMS114_MAX_AREA, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
|
|
|
@ -264,7 +264,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
|
|||
input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
|
||||
|
||||
if (pm->maxcontacts > 1) {
|
||||
input_mt_init_slots(pm->dev, pm->maxcontacts);
|
||||
input_mt_init_slots(pm->dev, pm->maxcontacts, 0);
|
||||
input_set_abs_params(pm->dev,
|
||||
ABS_MT_POSITION_X, 0, max_x, 0, 0);
|
||||
input_set_abs_params(pm->dev,
|
||||
|
|
|
@ -471,7 +471,7 @@ static int w8001_setup(struct w8001 *w8001)
|
|||
case 5:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
|
||||
|
||||
input_mt_init_slots(dev, 2);
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X,
|
||||
0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y,
|
||||
|
|
|
@ -414,7 +414,7 @@ struct hid_field {
|
|||
__u16 dpad; /* dpad input code */
|
||||
};
|
||||
|
||||
#define HID_MAX_FIELDS 128
|
||||
#define HID_MAX_FIELDS 256
|
||||
|
||||
struct hid_report {
|
||||
struct list_head list;
|
||||
|
@ -626,6 +626,7 @@ struct hid_usage_id {
|
|||
* @report_fixup: called before report descriptor parsing (NULL means nop)
|
||||
* @input_mapping: invoked on input registering before mapping an usage
|
||||
* @input_mapped: invoked on input registering after mapping an usage
|
||||
* @input_configured: invoked just before the device is registered
|
||||
* @feature_mapping: invoked on feature registering
|
||||
* @suspend: invoked on suspend (NULL means nop)
|
||||
* @resume: invoked on resume if device was not reset (NULL means nop)
|
||||
|
@ -670,6 +671,8 @@ struct hid_driver {
|
|||
int (*input_mapped)(struct hid_device *hdev,
|
||||
struct hid_input *hidinput, struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit, int *max);
|
||||
void (*input_configured)(struct hid_device *hdev,
|
||||
struct hid_input *hidinput);
|
||||
void (*feature_mapping)(struct hid_device *hdev,
|
||||
struct hid_field *field,
|
||||
struct hid_usage *usage);
|
||||
|
|
|
@ -1168,6 +1168,18 @@ struct ff_effect {
|
|||
#include <linux/timer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
/**
|
||||
* struct input_value - input value representation
|
||||
* @type: type of value (EV_KEY, EV_ABS, etc)
|
||||
* @code: the value code
|
||||
* @value: the value
|
||||
*/
|
||||
struct input_value {
|
||||
__u16 type;
|
||||
__u16 code;
|
||||
__s32 value;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct input_dev - represents an input device
|
||||
* @name: name of the device
|
||||
|
@ -1203,11 +1215,7 @@ struct ff_effect {
|
|||
* software autorepeat
|
||||
* @timer: timer for software autorepeat
|
||||
* @rep: current values for autorepeat parameters (delay, rate)
|
||||
* @mt: pointer to array of struct input_mt_slot holding current values
|
||||
* of tracked contacts
|
||||
* @mtsize: number of MT slots the device uses
|
||||
* @slot: MT slot currently being transmitted
|
||||
* @trkid: stores MT tracking ID for the current contact
|
||||
* @mt: pointer to multitouch state
|
||||
* @absinfo: array of &struct input_absinfo elements holding information
|
||||
* about absolute axes (current value, min, max, flat, fuzz,
|
||||
* resolution)
|
||||
|
@ -1244,7 +1252,6 @@ struct ff_effect {
|
|||
* last user closes the device
|
||||
* @going_away: marks devices that are in a middle of unregistering and
|
||||
* causes input_open_device*() fail with -ENODEV.
|
||||
* @sync: set to %true when there were no new events since last EV_SYN
|
||||
* @dev: driver model's view of this device
|
||||
* @h_list: list of input handles associated with the device. When
|
||||
* accessing the list dev->mutex must be held
|
||||
|
@ -1287,10 +1294,7 @@ struct input_dev {
|
|||
|
||||
int rep[REP_CNT];
|
||||
|
||||
struct input_mt_slot *mt;
|
||||
int mtsize;
|
||||
int slot;
|
||||
int trkid;
|
||||
struct input_mt *mt;
|
||||
|
||||
struct input_absinfo *absinfo;
|
||||
|
||||
|
@ -1312,12 +1316,14 @@ struct input_dev {
|
|||
unsigned int users;
|
||||
bool going_away;
|
||||
|
||||
bool sync;
|
||||
|
||||
struct device dev;
|
||||
|
||||
struct list_head h_list;
|
||||
struct list_head node;
|
||||
|
||||
unsigned int num_vals;
|
||||
unsigned int max_vals;
|
||||
struct input_value *vals;
|
||||
};
|
||||
#define to_input_dev(d) container_of(d, struct input_dev, dev)
|
||||
|
||||
|
@ -1378,6 +1384,9 @@ struct input_handle;
|
|||
* @event: event handler. This method is being called by input core with
|
||||
* interrupts disabled and dev->event_lock spinlock held and so
|
||||
* it may not sleep
|
||||
* @events: event sequence handler. This method is being called by
|
||||
* input core with interrupts disabled and dev->event_lock
|
||||
* spinlock held and so it may not sleep
|
||||
* @filter: similar to @event; separates normal event handlers from
|
||||
* "filters".
|
||||
* @match: called after comparing device's id with handler's id_table
|
||||
|
@ -1414,6 +1423,8 @@ struct input_handler {
|
|||
void *private;
|
||||
|
||||
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
||||
void (*events)(struct input_handle *handle,
|
||||
const struct input_value *vals, unsigned int count);
|
||||
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
|
||||
bool (*match)(struct input_handler *handler, struct input_dev *dev);
|
||||
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
|
||||
|
|
|
@ -15,12 +15,41 @@
|
|||
|
||||
#define TRKID_MAX 0xffff
|
||||
|
||||
#define INPUT_MT_POINTER 0x0001 /* pointer device, e.g. trackpad */
|
||||
#define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */
|
||||
#define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */
|
||||
#define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */
|
||||
|
||||
/**
|
||||
* struct input_mt_slot - represents the state of an input MT slot
|
||||
* @abs: holds current values of ABS_MT axes for this slot
|
||||
* @frame: last frame at which input_mt_report_slot_state() was called
|
||||
* @key: optional driver designation of this slot
|
||||
*/
|
||||
struct input_mt_slot {
|
||||
int abs[ABS_MT_LAST - ABS_MT_FIRST + 1];
|
||||
unsigned int frame;
|
||||
unsigned int key;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct input_mt - state of tracked contacts
|
||||
* @trkid: stores MT tracking ID for the next contact
|
||||
* @num_slots: number of MT slots the device uses
|
||||
* @slot: MT slot currently being transmitted
|
||||
* @flags: input_mt operation flags
|
||||
* @frame: increases every time input_mt_sync_frame() is called
|
||||
* @red: reduced cost matrix for in-kernel tracking
|
||||
* @slots: array of slots holding current values of tracked contacts
|
||||
*/
|
||||
struct input_mt {
|
||||
int trkid;
|
||||
int num_slots;
|
||||
int slot;
|
||||
unsigned int flags;
|
||||
unsigned int frame;
|
||||
int *red;
|
||||
struct input_mt_slot slots[];
|
||||
};
|
||||
|
||||
static inline void input_mt_set_value(struct input_mt_slot *slot,
|
||||
|
@ -35,12 +64,18 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot,
|
|||
return slot->abs[code - ABS_MT_FIRST];
|
||||
}
|
||||
|
||||
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots);
|
||||
static inline bool input_mt_is_active(const struct input_mt_slot *slot)
|
||||
{
|
||||
return input_mt_get_value(slot, ABS_MT_TRACKING_ID) >= 0;
|
||||
}
|
||||
|
||||
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
|
||||
unsigned int flags);
|
||||
void input_mt_destroy_slots(struct input_dev *dev);
|
||||
|
||||
static inline int input_mt_new_trkid(struct input_dev *dev)
|
||||
static inline int input_mt_new_trkid(struct input_mt *mt)
|
||||
{
|
||||
return dev->trkid++ & TRKID_MAX;
|
||||
return mt->trkid++ & TRKID_MAX;
|
||||
}
|
||||
|
||||
static inline void input_mt_slot(struct input_dev *dev, int slot)
|
||||
|
@ -64,4 +99,20 @@ void input_mt_report_slot_state(struct input_dev *dev,
|
|||
void input_mt_report_finger_count(struct input_dev *dev, int count);
|
||||
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);
|
||||
|
||||
void input_mt_sync_frame(struct input_dev *dev);
|
||||
|
||||
/**
|
||||
* struct input_mt_pos - contact position
|
||||
* @x: horizontal coordinate
|
||||
* @y: vertical coordinate
|
||||
*/
|
||||
struct input_mt_pos {
|
||||
s16 x, y;
|
||||
};
|
||||
|
||||
int input_mt_assign_slots(struct input_dev *dev, int *slots,
|
||||
const struct input_mt_pos *pos, int num_pos);
|
||||
|
||||
int input_mt_get_slot_by_key(struct input_dev *dev, int key);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue