Commit 5cf65713 authored by Linus Torvalds's avatar Linus Torvalds

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

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (30 commits)
  Revert "HID: add support for the Wacom Intuos 4 wireless"
  HID: fix up Kconfig entry for ACRUX driver
  HID: add ACRUX game controller force feedback support
  HID: Force input registration for "VEC footpedal"
  HID: add HID_QUIRK_HIDINPUT_FORCE
  HID: hid-input.c: indentation fixes
  HID: hiddev: use usb_find_interface, get rid of BKL
  HID: ignore digitizer usage Undefined (0x00)
  HID: Add support for Conceptronic CLLRCMCE
  HID: hid-ids.h: Whitespace fixup, align using TABs
  HID: picolcd: implement refcounting of framebuffer
  HID: picolcd: do not reallocate memory on depth change
  HID: picolcd: Add minimal palette required by fbcon on 8bpp
  HID: magicmouse: Correct parsing of large X and Y motions.
  HID: magicmouse: report last touch up
  HID: picolcd: fix deferred_io init/cleanup to fb ordering
  HID: hid-ids.h: keep vendor ids in alphabetical order
  HID: add proper support for Elecom BM084 bluetooth mouse
  HID: magicmouse: enable horizontal scrolling
  HID: magicmouse: add param for scroll speed
  ...
parents b7c8e55d 9f17d516
...@@ -33,19 +33,6 @@ Description: When read, this file returns the raw integer version number of the ...@@ -33,19 +33,6 @@ Description: When read, this file returns the raw integer version number of the
left. E.g. a returned value of 138 means 1.38 left. E.g. a returned value of 138 means 1.38
This file is readonly. This file is readonly.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/kone_driver_version
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: When read, this file returns the driver version.
The format of the string is "v<major>.<minor>.<patchlevel>".
This attribute is used by the userland tools to find the sysfs-
paths of installed kone-mice and determine the capabilites of
the driver. Versions of this driver for old kernels replace
usbhid instead of generic-usb. The way to scan for this file
has been chosen to provide a consistent way for all supported
kernel versions.
This file is readonly.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5] What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]
Date: March 2010 Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net> Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
......
...@@ -68,6 +68,14 @@ config HID_A4TECH ...@@ -68,6 +68,14 @@ config HID_A4TECH
---help--- ---help---
Support for A4 tech X5 and WOP-35 / Trust 450L mice. Support for A4 tech X5 and WOP-35 / Trust 450L mice.
config HID_ACRUX_FF
tristate "ACRUX force feedback support"
depends on USB_HID
select INPUT_FF_MEMLESS
---help---
Say Y here if you want to enable force feedback support for ACRUX
game controllers.
config HID_APPLE config HID_APPLE
tristate "Apple" if EMBEDDED tristate "Apple" if EMBEDDED
depends on (USB_HID || BT_HIDP) depends on (USB_HID || BT_HIDP)
...@@ -148,6 +156,12 @@ config HID_EGALAX ...@@ -148,6 +156,12 @@ config HID_EGALAX
---help--- ---help---
Support for the eGalax dual-touch panel. Support for the eGalax dual-touch panel.
config HID_ELECOM
tristate "ELECOM"
depends on BT_HIDP
---help---
Support for the ELECOM BM084 (bluetooth mouse).
config HID_EZKEY config HID_EZKEY
tristate "Ezkey" if EMBEDDED tristate "Ezkey" if EMBEDDED
depends on USB_HID depends on USB_HID
...@@ -417,10 +431,11 @@ config SMARTJOYPLUS_FF ...@@ -417,10 +431,11 @@ config SMARTJOYPLUS_FF
enable force feedback support for it. enable force feedback support for it.
config HID_TOPSEED config HID_TOPSEED
tristate "TopSeed Cyberlink remote control support" tristate "TopSeed Cyberlink, BTC Emprex, Conceptronic remote control support"
depends on USB_HID depends on USB_HID
---help--- ---help---
Say Y if you have a TopSeed Cyberlink or BTC Emprex remote control. Say Y if you have a TopSeed Cyberlink or BTC Emprex or Conceptronic
CLLRCMCE remote control.
config HID_THRUSTMASTER config HID_THRUSTMASTER
tristate "ThrustMaster devices support" tristate "ThrustMaster devices support"
......
...@@ -24,6 +24,7 @@ endif ...@@ -24,6 +24,7 @@ endif
obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_ACRUX_FF) += hid-axff.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_APPLE) += hid-apple.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CANDO) += hid-cando.o obj-$(CONFIG_HID_CANDO) += hid-cando.o
...@@ -32,6 +33,7 @@ obj-$(CONFIG_HID_CHICONY) += hid-chicony.o ...@@ -32,6 +33,7 @@ obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
obj-$(CONFIG_HID_DRAGONRISE) += hid-drff.o obj-$(CONFIG_HID_DRAGONRISE) += hid-drff.o
obj-$(CONFIG_HID_EGALAX) += hid-egalax.o obj-$(CONFIG_HID_EGALAX) += hid-egalax.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
......
/*
* Force feedback support for ACRUX game controllers
*
* From what I have gathered, these devices are mass produced in China
* by several vendors. They often share the same design as the original
* Xbox 360 controller.
*
* 1a34:0802 "ACRUX USB GAMEPAD 8116"
* - tested with a EXEQ EQ-PCU-02090 game controller.
*
* Copyright (c) 2010 Sergei Kolzun <x0r@dv-life.ru>
*/
/*
* 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.
*
* This program 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 program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include "hid-ids.h"
#include "usbhid/usbhid.h"
struct axff_device {
struct hid_report *report;
};
static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct axff_device *axff = data;
int left, right;
left = effect->u.rumble.strong_magnitude;
right = effect->u.rumble.weak_magnitude;
dbg_hid("called with 0x%04x 0x%04x", left, right);
left = left * 0xff / 0xffff;
right = right * 0xff / 0xffff;
axff->report->field[0]->value[0] = left;
axff->report->field[1]->value[0] = right;
axff->report->field[2]->value[0] = left;
axff->report->field[3]->value[0] = right;
dbg_hid("running with 0x%02x 0x%02x", left, right);
usbhid_submit_report(hid, axff->report, USB_DIR_OUT);
return 0;
}
static int axff_init(struct hid_device *hid)
{
struct axff_device *axff;
struct hid_report *report;
struct hid_input *hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
int error;
if (list_empty(report_list)) {
dev_err(&hid->dev, "no output reports found\n");
return -ENODEV;
}
report = list_first_entry(report_list, struct hid_report, list);
if (report->maxfield < 4) {
dev_err(&hid->dev, "no fields in the report: %d\n", report->maxfield);
return -ENODEV;
}
axff = kzalloc(sizeof(struct axff_device), GFP_KERNEL);
if (!axff)
return -ENOMEM;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, axff, axff_play);
if (error)
goto err_free_mem;
axff->report = report;
axff->report->field[0]->value[0] = 0x00;
axff->report->field[1]->value[0] = 0x00;
axff->report->field[2]->value[0] = 0x00;
axff->report->field[3]->value[0] = 0x00;
usbhid_submit_report(hid, axff->report, USB_DIR_OUT);
dev_info(&hid->dev, "Force Feedback for ACRUX game controllers by Sergei Kolzun<x0r@dv-life.ru>\n");
return 0;
err_free_mem:
kfree(axff);
return error;
}
static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int error;
dev_dbg(&hdev->dev, "ACRUX HID hardware probe...");
error = hid_parse(hdev);
if (error) {
dev_err(&hdev->dev, "parse failed\n");
return error;
}
error = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
if (error) {
dev_err(&hdev->dev, "hw start failed\n");
return error;
}
error = axff_init(hdev);
if (error) {
/*
* Do not fail device initialization completely as device
* may still be partially operable, just warn.
*/
dev_warn(&hdev->dev,
"Failed to enable force feedback support, error: %d\n",
error);
}
return 0;
}
static const struct hid_device_id ax_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), },
{ }
};
MODULE_DEVICE_TABLE(hid, ax_devices);
static struct hid_driver ax_driver = {
.name = "acrux",
.id_table = ax_devices,
.probe = ax_probe,
};
static int __init ax_init(void)
{
return hid_register_driver(&ax_driver);
}
static void __exit ax_exit(void)
{
hid_unregister_driver(&ax_driver);
}
module_init(ax_init);
module_exit(ax_exit);
MODULE_AUTHOR("Sergei Kolzun");
MODULE_DESCRIPTION("Force feedback support for ACRUX game controllers");
MODULE_LICENSE("GPL");
...@@ -1157,6 +1157,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) ...@@ -1157,6 +1157,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE)
connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV);
if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE)
connect_mask |= HID_CONNECT_HIDINPUT_FORCE;
if (hdev->bus != BUS_USB) if (hdev->bus != BUS_USB)
connect_mask &= ~HID_CONNECT_HIDDEV; connect_mask &= ~HID_CONNECT_HIDDEV;
if (hid_hiddev(hdev)) if (hid_hiddev(hdev))
...@@ -1239,6 +1241,9 @@ static const struct hid_device_id hid_blacklist[] = { ...@@ -1239,6 +1241,9 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, { 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) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE)
{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
...@@ -1294,6 +1299,7 @@ static const struct hid_device_id hid_blacklist[] = { ...@@ -1294,6 +1299,7 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) },
...@@ -1375,10 +1381,10 @@ static const struct hid_device_id hid_blacklist[] = { ...@@ -1375,10 +1381,10 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
......
/*
* HID driver for Elecom BM084 (bluetooth mouse).
* Removes a non-existing horizontal wheel from
* the HID descriptor.
* (This module is based on "hid-ortek".)
*
* Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com>
*/
/*
* 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.
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
static void elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize)
{
if (rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) {
dev_info(&hdev->dev, "Fixing up Elecom BM084 "
"report descriptor.\n");
rdesc[47] = 0x00;
}
}
static const struct hid_device_id elecom_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084)},
{ }
};
MODULE_DEVICE_TABLE(hid, elecom_devices);
static struct hid_driver elecom_driver = {
.name = "elecom",
.id_table = elecom_devices,
.report_fixup = elecom_report_fixup
};
static int __init elecom_init(void)
{
return hid_register_driver(&elecom_driver);
}
static void __exit elecom_exit(void)
{
hid_unregister_driver(&elecom_driver);
}
module_init(elecom_init);
module_exit(elecom_exit);
MODULE_LICENSE("GPL");
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 #define USB_DEVICE_ID_ACECAD_FLAIR 0x0004
#define USB_DEVICE_ID_ACECAD_302 0x0008 #define USB_DEVICE_ID_ACECAD_302 0x0008
#define USB_VENDOR_ID_ACRUX 0x1a34
#define USB_VENDOR_ID_ADS_TECH 0x06e1 #define USB_VENDOR_ID_ADS_TECH 0x06e1
#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155 #define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155
...@@ -187,19 +189,22 @@ ...@@ -187,19 +189,22 @@
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
#define USB_VENDOR_ID_ELO 0x04E7 #define USB_VENDOR_ID_ELO 0x04E7
#define USB_DEVICE_ID_ELO_TS2700 0x0020 #define USB_DEVICE_ID_ELO_TS2700 0x0020
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
#define USB_VENDOR_ID_ETURBOTOUCH 0x22b9
#define USB_DEVICE_ID_ETURBOTOUCH 0x0006
#define USB_VENDOR_ID_ETT 0x0664 #define USB_VENDOR_ID_ETT 0x0664
#define USB_DEVICE_ID_TC5UH 0x0309 #define USB_DEVICE_ID_TC5UH 0x0309
#define USB_DEVICE_ID_TC4UM 0x0306 #define USB_DEVICE_ID_TC4UM 0x0306
#define USB_VENDOR_ID_ETURBOTOUCH 0x22b9
#define USB_DEVICE_ID_ETURBOTOUCH 0x0006
#define USB_VENDOR_ID_EZKEY 0x0518 #define USB_VENDOR_ID_EZKEY 0x0518
#define USB_DEVICE_ID_BTC_8193 0x0002 #define USB_DEVICE_ID_BTC_8193 0x0002
...@@ -297,9 +302,16 @@ ...@@ -297,9 +302,16 @@
#define USB_VENDOR_ID_KBGEAR 0x084e #define USB_VENDOR_ID_KBGEAR 0x084e
#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
#define USB_VENDOR_ID_KENSINGTON 0x047d
#define USB_DEVICE_ID_KS_SLIMBLADE 0x2041
#define USB_VENDOR_ID_KWORLD 0x1b80 #define USB_VENDOR_ID_KWORLD 0x1b80
#define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700 #define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700
#define USB_VENDOR_ID_KYE 0x0458
#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003
#define USB_VENDOR_ID_LABTEC 0x1020 #define USB_VENDOR_ID_LABTEC 0x1020
#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006
...@@ -319,9 +331,6 @@ ...@@ -319,9 +331,6 @@
#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 #define USB_DEVICE_ID_LD_POWERCONTROL 0x2030
#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 #define USB_DEVICE_ID_LD_MACHINETEST 0x2040
#define USB_VENDOR_ID_KENSINGTON 0x047d
#define USB_DEVICE_ID_KS_SLIMBLADE 0x2041
#define USB_VENDOR_ID_LOGITECH 0x046d #define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
...@@ -377,16 +386,16 @@ ...@@ -377,16 +386,16 @@
#define USB_VENDOR_ID_MONTEREY 0x0566 #define USB_VENDOR_ID_MONTEREY 0x0566
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004 #define USB_DEVICE_ID_GENIUS_KB29E 0x3004
#define USB_VENDOR_ID_NCR 0x0404
#define USB_DEVICE_ID_NCR_FIRST 0x0300
#define USB_DEVICE_ID_NCR_LAST 0x03ff
#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
#define USB_DEVICE_ID_N_S_HARMONY 0xc359 #define USB_DEVICE_ID_N_S_HARMONY 0xc359
#define USB_VENDOR_ID_NATSU 0x08b7 #define USB_VENDOR_ID_NATSU 0x08b7
#define USB_DEVICE_ID_NATSU_GAMEPAD 0x0001 #define USB_DEVICE_ID_NATSU_GAMEPAD 0x0001
#define USB_VENDOR_ID_NCR 0x0404
#define USB_DEVICE_ID_NCR_FIRST 0x0300
#define USB_DEVICE_ID_NCR_LAST 0x03ff
#define USB_VENDOR_ID_NEC 0x073e #define USB_VENDOR_ID_NEC 0x073e
#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
...@@ -431,6 +440,9 @@ ...@@ -431,6 +440,9 @@
#define USB_VENDOR_ID_PHILIPS 0x0471 #define USB_VENDOR_ID_PHILIPS 0x0471
#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617 #define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617
#define USB_VENDOR_ID_PI_ENGINEERING 0x05f3
#define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff
#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 #define USB_VENDOR_ID_PLAYDOTCOM 0x0b43
#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003
...@@ -440,16 +452,16 @@ ...@@ -440,16 +452,16 @@
#define USB_VENDOR_ID_PRODIGE 0x05af #define USB_VENDOR_ID_PRODIGE 0x05af
#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 #define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062
#define USB_VENDOR_ID_QUANTA 0x0408
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001
#define USB_VENDOR_ID_ROCCAT 0x1e7d #define USB_VENDOR_ID_ROCCAT 0x1e7d
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
#define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_VENDOR_ID_SAITEK 0x06a3
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
#define USB_VENDOR_ID_QUANTA 0x0408
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001
#define USB_VENDOR_ID_SAMSUNG 0x0419 #define USB_VENDOR_ID_SAMSUNG 0x0419
#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001
#define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600 #define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600
...@@ -473,14 +485,17 @@ ...@@ -473,14 +485,17 @@
#define USB_VENDOR_ID_THRUSTMASTER 0x044f #define USB_VENDOR_ID_THRUSTMASTER 0x044f
#define USB_VENDOR_ID_TOUCHPACK 0x1bfd #define USB_VENDOR_ID_TOPSEED 0x0766
#define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688 #define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204
#define USB_VENDOR_ID_TOPSEED2 0x1784
#define USB_DEVICE_ID_TOPSEED2_RF_COMBO 0x0004
#define USB_VENDOR_ID_TOPMAX 0x0663 #define USB_VENDOR_ID_TOPMAX 0x0663
#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 #define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
#define USB_VENDOR_ID_TOPSEED 0x0766 #define USB_VENDOR_ID_TOUCHPACK 0x1bfd
#define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204 #define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688
#define USB_VENDOR_ID_TURBOX 0x062a #define USB_VENDOR_ID_TURBOX 0x062a
#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 #define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
...@@ -501,7 +516,6 @@ ...@@ -501,7 +516,6 @@
#define USB_VENDOR_ID_WACOM 0x056a #define USB_VENDOR_ID_WACOM 0x056a
#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81 #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0xbd
#define USB_VENDOR_ID_WISEGROUP 0x0925 #define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005 #define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
...@@ -523,9 +537,4 @@ ...@@ -523,9 +537,4 @@
#define USB_VENDOR_ID_ZYDACRON 0x13EC #define USB_VENDOR_ID_ZYDACRON 0x13EC
#define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006 #define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006
#define USB_VENDOR_ID_KYE 0x0458
#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003
#endif #endif
...@@ -301,6 +301,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -301,6 +301,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case HID_UP_DIGITIZER: case HID_UP_DIGITIZER:
switch (usage->hid & 0xff) { switch (usage->hid & 0xff) {
case 0x00: /* Undefined */
goto ignore;
case 0x30: /* TipPressure */ case 0x30: /* TipPressure */
if (!test_bit(BTN_TOUCH, input->keybit)) { if (!test_bit(BTN_TOUCH, input->keybit)) {
device->quirks |= HID_QUIRK_NOTOUCH; device->quirks |= HID_QUIRK_NOTOUCH;
......
...@@ -30,6 +30,21 @@ static bool emulate_scroll_wheel = true; ...@@ -30,6 +30,21 @@ static bool emulate_scroll_wheel = true;
module_param(emulate_scroll_wheel, bool, 0644); module_param(emulate_scroll_wheel, bool, 0644);
MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel");
static unsigned int scroll_speed = 32;
static int param_set_scroll_speed(const char *val, struct kernel_param *kp) {
unsigned long speed;
if (!val || strict_strtoul(val, 0, &speed) || speed > 63)
return -EINVAL;
scroll_speed = speed;
return 0;
}
module_param_call(scroll_speed, param_set_scroll_speed, param_get_uint, &scroll_speed, 0644);
MODULE_PARM_DESC(scroll_speed, "Scroll speed, value from 0 (slow) to 63 (fast)");
static bool scroll_acceleration = false;
module_param(scroll_acceleration, bool, 0644);
MODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events");
static bool report_touches = true; static bool report_touches = true;
module_param(report_touches, bool, 0644); module_param(report_touches, bool, 0644);
MODULE_PARM_DESC(report_touches, "Emit touch records (otherwise, only use them for emulation)"); MODULE_PARM_DESC(report_touches, "Emit touch records (otherwise, only use them for emulation)");
...@@ -50,6 +65,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie ...@@ -50,6 +65,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
#define TOUCH_STATE_START 0x30 #define TOUCH_STATE_START 0x30
#define TOUCH_STATE_DRAG 0x40 #define TOUCH_STATE_DRAG 0x40
#define SCROLL_ACCEL_DEFAULT 7
/** /**
* struct magicmouse_sc - Tracks Magic Mouse-specific data. * struct magicmouse_sc - Tracks Magic Mouse-specific data.
* @input: Input device through which we report events. * @input: Input device through which we report events.
...@@ -78,8 +95,10 @@ struct magicmouse_sc { ...@@ -78,8 +95,10 @@ struct magicmouse_sc {
struct { struct {
short x; short x;
short y; short y;
short scroll_x;
short scroll_y; short scroll_y;
u8 size; u8 size;
u8 down;
} touches[16]; } touches[16];
int tracking_ids[16]; int tracking_ids[16];
}; };
...@@ -141,7 +160,7 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state) ...@@ -141,7 +160,7 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
input_report_key(msc->input, BTN_RIGHT, state & 2); input_report_key(msc->input, BTN_RIGHT, state & 2);
if (state != last_state) if (state != last_state)
msc->scroll_accel = 0; msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
} }
static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata) static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
...@@ -152,6 +171,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda ...@@ -152,6 +171,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
int id = (misc >> 6) & 15; int id = (misc >> 6) & 15;
int x = x_y << 12 >> 20; int x = x_y << 12 >> 20;
int y = -(x_y >> 20); int y = -(x_y >> 20);
int down = (tdata[7] & TOUCH_STATE_MASK) != TOUCH_STATE_NONE;
/* Store tracking ID and other fields. */ /* Store tracking ID and other fields. */
msc->tracking_ids[raw_id] = id; msc->tracking_ids[raw_id] = id;
...@@ -160,42 +180,54 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda ...@@ -160,42 +180,54 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
msc->touches[id].size = misc & 63; msc->touches[id].size = misc & 63;
/* If requested, emulate a scroll wheel by detecting small /* If requested, emulate a scroll wheel by detecting small
* vertical touch motions along the middle of the mouse. * vertical touch motions.
*/ */
if (emulate_scroll_wheel && if (emulate_scroll_wheel) {
middle_button_start < x && x < middle_button_stop) {
static const int accel_profile[] = {
256, 228, 192, 160, 128, 96, 64, 32,
};
unsigned long now = jiffies; unsigned long now = jiffies;
int step = msc->touches[id].scroll_y - y; int step_x = msc->touches[id].scroll_x - x;
int step_y = msc->touches[id].scroll_y - y;
/* Reset acceleration after half a second. */
if (time_after(now, msc->scroll_jiffies + HZ / 2))
msc->scroll_accel = 0;
/* Calculate and apply the scroll motion. */ /* Calculate and apply the scroll motion. */
switch (tdata[7] & TOUCH_STATE_MASK) { switch (tdata[7] & TOUCH_STATE_MASK) {
case TOUCH_STATE_START: case TOUCH_STATE_START:
msc->touches[id].scroll_x = x;
msc->touches[id].scroll_y = y; msc->touches[id].scroll_y = y;
msc->scroll_accel = min_t(int, msc->scroll_accel + 1,
ARRAY_SIZE(accel_profile) - 1); /* Reset acceleration after half a second. */
if (scroll_acceleration && time_before(now,
msc->scroll_jiffies + HZ / 2))
msc->scroll_accel = max_t(int,
msc->scroll_accel - 1, 1);
else
msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
break; break;
case TOUCH_STATE_DRAG: case TOUCH_STATE_DRAG:
step = step / accel_profile[msc->scroll_accel]; step_x /= (64 - (int)scroll_speed) * msc->scroll_accel;
if (step != 0) { if (step_x != 0) {
msc->touches[id].scroll_y = y; msc->touches[id].scroll_x -= step_x *
(64 - scroll_speed) * msc->scroll_accel;
msc->scroll_jiffies = now; msc->scroll_jiffies = now;
input_report_rel(input, REL_WHEEL, step); input_report_rel(input, REL_HWHEEL, -step_x);
}
step_y /= (64 - (int)scroll_speed) * msc->scroll_accel;
if (step_y != 0) {
msc->touches[id].scroll_y -= step_y *
(64 - scroll_speed) * msc->scroll_accel;
msc->scroll_jiffies = now;
input_report_rel(input, REL_WHEEL, step_y);
} }
break; break;
} }
} }
/* Generate the input events for this touch. */ /* Generate the input events for this touch. */
if (report_touches) { if (report_touches && down) {
int orientation = (misc >> 10) - 32; int orientation = (misc >> 10) - 32;
msc->touches[id].down = 1;
input_report_abs(input, ABS_MT_TRACKING_ID, id); input_report_abs(input, ABS_MT_TRACKING_ID, id);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]); input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]);
input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]); input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]);
...@@ -215,7 +247,7 @@ static int magicmouse_raw_event(struct hid_device *hdev, ...@@ -215,7 +247,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
{ {
struct magicmouse_sc *msc = hid_get_drvdata(hdev); struct magicmouse_sc *msc = hid_get_drvdata(hdev);
struct input_dev *input = msc->input; struct input_dev *input = msc->input;
int x, y, ts, ii, clicks; int x, y, ts, ii, clicks, last_up;
switch (data[0]) { switch (data[0]) {
case 0x10: case 0x10:
...@@ -235,12 +267,26 @@ static int magicmouse_raw_event(struct hid_device *hdev, ...@@ -235,12 +267,26 @@ static int magicmouse_raw_event(struct hid_device *hdev,
msc->ntouches = (size - 6) / 8; msc->ntouches = (size - 6) / 8;
for (ii = 0; ii < msc->ntouches; ii++) for (ii = 0; ii < msc->ntouches; ii++)
magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
if (report_touches) {
last_up = 1;
for (ii = 0; ii < ARRAY_SIZE(msc->touches); ii++) {
if (msc->touches[ii].down) {
last_up = 0;
msc->touches[ii].down = 0;
}
}
if (last_up) {
input_mt_sync(input);
}
}
/* When emulating three-button mode, it is important /* When emulating three-button mode, it is important
* to have the current touch information before * to have the current touch information before
* generating a click event. * generating a click event.
*/ */
x = (signed char)data[1]; x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
y = (signed char)data[2]; y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
clicks = data[3]; clicks = data[3];
break; break;
case 0x20: /* Theoretically battery status (0-100), but I have case 0x20: /* Theoretically battery status (0-100), but I have
...@@ -301,8 +347,10 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h ...@@ -301,8 +347,10 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
__set_bit(EV_REL, input->evbit); __set_bit(EV_REL, input->evbit);
__set_bit(REL_X, input->relbit); __set_bit(REL_X, input->relbit);
__set_bit(REL_Y, input->relbit); __set_bit(REL_Y, input->relbit);
if (emulate_scroll_wheel) if (emulate_scroll_wheel) {
__set_bit(REL_WHEEL, input->relbit); __set_bit(REL_WHEEL, input->relbit);
__set_bit(REL_HWHEEL, input->relbit);
}
if (report_touches) { if (report_touches) {
__set_bit(EV_ABS, input->evbit); __set_bit(EV_ABS, input->evbit);
...@@ -345,6 +393,8 @@ static int magicmouse_probe(struct hid_device *hdev, ...@@ -345,6 +393,8 @@ static int magicmouse_probe(struct hid_device *hdev,
return -ENOMEM; return -ENOMEM;
} }
msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
msc->quirks = id->driver_data; msc->quirks = id->driver_data;
hid_set_drvdata(hdev, msc); hid_set_drvdata(hdev, msc);
......
...@@ -127,6 +127,26 @@ static const struct fb_var_screeninfo picolcdfb_var = { ...@@ -127,6 +127,26 @@ static const struct fb_var_screeninfo picolcdfb_var = {
.height = 26, .height = 26,
.bits_per_pixel = 1, .bits_per_pixel = 1,
.grayscale = 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,
},
}; };
#endif /* CONFIG_HID_PICOLCD_FB */ #endif /* CONFIG_HID_PICOLCD_FB */
...@@ -188,6 +208,7 @@ struct picolcd_data { ...@@ -188,6 +208,7 @@ struct picolcd_data {
/* Framebuffer stuff */ /* Framebuffer stuff */
u8 fb_update_rate; u8 fb_update_rate;
u8 fb_bpp; u8 fb_bpp;
u8 fb_force;
u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */ u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */
u8 *fb_bitmap; /* framebuffer */ u8 *fb_bitmap; /* framebuffer */
struct fb_info *fb_info; struct fb_info *fb_info;
...@@ -346,7 +367,7 @@ static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp, ...@@ -346,7 +367,7 @@ static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32; const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
for (i = 0; i < 64; i++) { for (i = 0; i < 64; i++) {
tdata[i] <<= 1; tdata[i] <<= 1;
tdata[i] |= (bdata[i/8] >> (7 - i % 8)) & 0x01; tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
} }
} }
} else if (bpp == 8) { } else if (bpp == 8) {
...@@ -399,13 +420,10 @@ static int picolcd_fb_reset(struct picolcd_data *data, int clear) ...@@ -399,13 +420,10 @@ static int picolcd_fb_reset(struct picolcd_data *data, int clear)
if (data->fb_bitmap) { if (data->fb_bitmap) {
if (clear) { if (clear) {
memset(data->fb_vbitmap, 0xff, PICOLCDFB_SIZE); memset(data->fb_vbitmap, 0, PICOLCDFB_SIZE);
memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp); memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp);
} else {
/* invert 1 byte in each tile to force resend */
for (i = 0; i < PICOLCDFB_SIZE; i += 64)
data->fb_vbitmap[i] = ~data->fb_vbitmap[i];
} }
data->fb_force = 1;
} }
/* schedule first output of framebuffer */ /* schedule first output of framebuffer */
...@@ -421,6 +439,9 @@ static void picolcd_fb_update(struct picolcd_data *data) ...@@ -421,6 +439,9 @@ static void picolcd_fb_update(struct picolcd_data *data)
int chip, tile, n; int chip, tile, n;
unsigned long flags; unsigned long flags;
if (!data)
return;
spin_lock_irqsave(&data->lock, flags); spin_lock_irqsave(&data->lock, flags);
if (!(data->status & PICOLCD_READY_FB)) { if (!(data->status & PICOLCD_READY_FB)) {
spin_unlock_irqrestore(&data->lock, flags); spin_unlock_irqrestore(&data->lock, flags);
...@@ -440,14 +461,18 @@ static void picolcd_fb_update(struct picolcd_data *data) ...@@ -440,14 +461,18 @@ static void picolcd_fb_update(struct picolcd_data *data)
for (chip = 0; chip < 4; chip++) for (chip = 0; chip < 4; chip++)
for (tile = 0; tile < 8; tile++) for (tile = 0; tile < 8; tile++)
if (picolcd_fb_update_tile(data->fb_vbitmap, if (picolcd_fb_update_tile(data->fb_vbitmap,
data->fb_bitmap, data->fb_bpp, chip, tile)) { data->fb_bitmap, data->fb_bpp, chip, tile) ||
data->fb_force) {
n += 2; n += 2;
if (!data->fb_info->par)
return; /* device lost! */
if (n >= HID_OUTPUT_FIFO_SIZE / 2) { if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
usbhid_wait_io(data->hdev); usbhid_wait_io(data->hdev);
n = 0; n = 0;
} }
picolcd_fb_send_tile(data->hdev, chip, tile); picolcd_fb_send_tile(data->hdev, chip, tile);
} }
data->fb_force = false;
if (n) if (n)
usbhid_wait_io(data->hdev); usbhid_wait_io(data->hdev);
} }
...@@ -511,11 +536,23 @@ static int picolcd_fb_blank(int blank, struct fb_info *info) ...@@ -511,11 +536,23 @@ static int picolcd_fb_blank(int blank, struct fb_info *info)
static void picolcd_fb_destroy(struct fb_info *info) static void picolcd_fb_destroy(struct fb_info *info)
{ {
struct picolcd_data *data = info->par; struct picolcd_data *data = info->par;
u32 *ref_cnt = info->pseudo_palette;
int may_release;
info->par = NULL; info->par = NULL;
if (data) if (data)
data->fb_info = NULL; data->fb_info = NULL;
fb_deferred_io_cleanup(info); fb_deferred_io_cleanup(info);
ref_cnt--;
mutex_lock(&info->lock);
(*ref_cnt)--;
may_release = !ref_cnt;
mutex_unlock(&info->lock);
if (may_release) {
framebuffer_release(info); framebuffer_release(info);
vfree((u8 *)info->fix.smem_start);
}
} }
static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
...@@ -526,17 +563,26 @@ static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *i ...@@ -526,17 +563,26 @@ static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *i
/* only allow 1/8 bit depth (8-bit is grayscale) */ /* only allow 1/8 bit depth (8-bit is grayscale) */
*var = picolcdfb_var; *var = picolcdfb_var;
var->activate = activate; var->activate = activate;
if (bpp >= 8) if (bpp >= 8) {
var->bits_per_pixel = 8; var->bits_per_pixel = 8;
else var->red.length = 8;
var->green.length = 8;
var->blue.length = 8;
} else {
var->bits_per_pixel = 1; var->bits_per_pixel = 1;
var->red.length = 1;
var->green.length = 1;
var->blue.length = 1;
}
return 0; return 0;
} }
static int picolcd_set_par(struct fb_info *info) static int picolcd_set_par(struct fb_info *info)
{ {
struct picolcd_data *data = info->par; struct picolcd_data *data = info->par;
u8 *o_fb, *n_fb; u8 *tmp_fb, *o_fb;
if (!data)
return -ENODEV;
if (info->var.bits_per_pixel == data->fb_bpp) if (info->var.bits_per_pixel == data->fb_bpp)
return 0; return 0;
/* switch between 1/8 bit depths */ /* switch between 1/8 bit depths */
...@@ -544,11 +590,10 @@ static int picolcd_set_par(struct fb_info *info) ...@@ -544,11 +590,10 @@ static int picolcd_set_par(struct fb_info *info)
return -EINVAL; return -EINVAL;
o_fb = data->fb_bitmap; o_fb = data->fb_bitmap;
n_fb = vmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel); tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
if (!n_fb) if (!tmp_fb)
return -ENOMEM; return -ENOMEM;
fb_deferred_io_cleanup(info);
/* translate FB content to new bits-per-pixel */ /* translate FB content to new bits-per-pixel */
if (info->var.bits_per_pixel == 1) { if (info->var.bits_per_pixel == 1) {
int i, b; int i, b;
...@@ -558,24 +603,87 @@ static int picolcd_set_par(struct fb_info *info) ...@@ -558,24 +603,87 @@ static int picolcd_set_par(struct fb_info *info)
p <<= 1; p <<= 1;
p |= o_fb[i*8+b] ? 0x01 : 0x00; 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.visual = FB_VISUAL_MONO01;
info->fix.line_length = PICOLCDFB_WIDTH / 8; info->fix.line_length = PICOLCDFB_WIDTH / 8;
} else { } else {
int i; int i;
memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
for (i = 0; i < PICOLCDFB_SIZE * 8; i++) for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
n_fb[i] = o_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00; o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
info->fix.visual = FB_VISUAL_TRUECOLOR; info->fix.visual = FB_VISUAL_DIRECTCOLOR;
info->fix.line_length = PICOLCDFB_WIDTH; info->fix.line_length = PICOLCDFB_WIDTH;
} }
data->fb_bitmap = n_fb; kfree(tmp_fb);
data->fb_bpp = info->var.bits_per_pixel; data->fb_bpp = info->var.bits_per_pixel;
info->screen_base = (char __force __iomem *)n_fb; return 0;
info->fix.smem_start = (unsigned long)n_fb; }
info->fix.smem_len = PICOLCDFB_SIZE*data->fb_bpp;
fb_deferred_io_init(info); /* Do refcounting on our FB and cleanup per worker if FB is
vfree(o_fb); * closed after unplug of our device
* (fb_release holds info->lock and still touches info after
* we return so we can't release it immediately.
*/
struct picolcd_fb_cleanup_item {
struct fb_info *info;
struct picolcd_fb_cleanup_item *next;
};
static struct picolcd_fb_cleanup_item *fb_pending;
DEFINE_SPINLOCK(fb_pending_lock);
static void picolcd_fb_do_cleanup(struct work_struct *data)
{
struct picolcd_fb_cleanup_item *item;
unsigned long flags;
do {
spin_lock_irqsave(&fb_pending_lock, flags);
item = fb_pending;
fb_pending = item ? item->next : NULL;
spin_unlock_irqrestore(&fb_pending_lock, flags);
if (item) {
u8 *fb = (u8 *)item->info->fix.smem_start;
/* make sure we do not race against fb core when
* releasing */
mutex_lock(&item->info->lock);
mutex_unlock(&item->info->lock);
framebuffer_release(item->info);
vfree(fb);
}
} while (item);
}
DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
static int picolcd_fb_open(struct fb_info *info, int u)
{
u32 *ref_cnt = info->pseudo_palette;
ref_cnt--;
(*ref_cnt)++;
return 0;
}
static int picolcd_fb_release(struct fb_info *info, int u)
{
u32 *ref_cnt = info->pseudo_palette;
ref_cnt--;
(*ref_cnt)++;
if (!*ref_cnt) {
unsigned long flags;
struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt;
item--;
spin_lock_irqsave(&fb_pending_lock, flags);
item->next = fb_pending;
fb_pending = item;
spin_unlock_irqrestore(&fb_pending_lock, flags);
schedule_work(&picolcd_fb_cleanup);
}
return 0; return 0;
} }
...@@ -583,6 +691,8 @@ static int picolcd_set_par(struct fb_info *info) ...@@ -583,6 +691,8 @@ static int picolcd_set_par(struct fb_info *info)
static struct fb_ops picolcdfb_ops = { static struct fb_ops picolcdfb_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.fb_destroy = picolcd_fb_destroy, .fb_destroy = picolcd_fb_destroy,
.fb_open = picolcd_fb_open,
.fb_release = picolcd_fb_release,
.fb_read = fb_sys_read, .fb_read = fb_sys_read,
.fb_write = picolcd_fb_write, .fb_write = picolcd_fb_write,
.fb_blank = picolcd_fb_blank, .fb_blank = picolcd_fb_blank,
...@@ -660,11 +770,12 @@ static int picolcd_init_framebuffer(struct picolcd_data *data) ...@@ -660,11 +770,12 @@ static int picolcd_init_framebuffer(struct picolcd_data *data)
{ {
struct device *dev = &data->hdev->dev; struct device *dev = &data->hdev->dev;
struct fb_info *info = NULL; struct fb_info *info = NULL;
int error = -ENOMEM; int i, error = -ENOMEM;
u8 *fb_vbitmap = NULL; u8 *fb_vbitmap = NULL;
u8 *fb_bitmap = NULL; u8 *fb_bitmap = NULL;
u32 *palette;
fb_bitmap = vmalloc(PICOLCDFB_SIZE*picolcdfb_var.bits_per_pixel); fb_bitmap = vmalloc(PICOLCDFB_SIZE*8);
if (fb_bitmap == NULL) { if (fb_bitmap == NULL) {
dev_err(dev, "can't get a free page for framebuffer\n"); dev_err(dev, "can't get a free page for framebuffer\n");
goto err_nomem; goto err_nomem;
...@@ -678,18 +789,29 @@ static int picolcd_init_framebuffer(struct picolcd_data *data) ...@@ -678,18 +789,29 @@ static int picolcd_init_framebuffer(struct picolcd_data *data)
data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
data->fb_defio = picolcd_fb_defio; data->fb_defio = picolcd_fb_defio;
info = framebuffer_alloc(0, dev); /* The extra memory is:
* - struct picolcd_fb_cleanup_item
* - u32 for ref_count
* - 256*u32 for pseudo_palette
*/
info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev);
if (info == NULL) { if (info == NULL) {
dev_err(dev, "failed to allocate a framebuffer\n"); dev_err(dev, "failed to allocate a framebuffer\n");
goto err_nomem; goto err_nomem;
} }
palette = info->par + sizeof(struct picolcd_fb_cleanup_item);
*palette = 1;
palette++;
for (i = 0; i < 256; i++)
palette[i] = i > 0 && i < 16 ? 0xff : 0;
info->pseudo_palette = palette;
info->fbdefio = &data->fb_defio; info->fbdefio = &data->fb_defio;
info->screen_base = (char __force __iomem *)fb_bitmap; info->screen_base = (char __force __iomem *)fb_bitmap;
info->fbops = &picolcdfb_ops; info->fbops = &picolcdfb_ops;
info->var = picolcdfb_var; info->var = picolcdfb_var;
info->fix = picolcdfb_fix; info->fix = picolcdfb_fix;
info->fix.smem_len = PICOLCDFB_SIZE; info->fix.smem_len = PICOLCDFB_SIZE*8;
info->fix.smem_start = (unsigned long)fb_bitmap; info->fix.smem_start = (unsigned long)fb_bitmap;
info->par = data; info->par = data;
info->flags = FBINFO_FLAG_DEFAULT; info->flags = FBINFO_FLAG_DEFAULT;
...@@ -707,18 +829,20 @@ static int picolcd_init_framebuffer(struct picolcd_data *data) ...@@ -707,18 +829,20 @@ static int picolcd_init_framebuffer(struct picolcd_data *data)
dev_err(dev, "failed to create sysfs attributes\n"); dev_err(dev, "failed to create sysfs attributes\n");
goto err_cleanup; goto err_cleanup;
} }
fb_deferred_io_init(info);
data->fb_info = info; data->fb_info = info;
error = register_framebuffer(info); error = register_framebuffer(info);
if (error) { if (error) {
dev_err(dev, "failed to register framebuffer\n"); dev_err(dev, "failed to register framebuffer\n");
goto err_sysfs; goto err_sysfs;
} }
fb_deferred_io_init(info);
/* schedule first output of framebuffer */ /* schedule first output of framebuffer */
data->fb_force = 1;
schedule_delayed_work(&info->deferred_work, 0); schedule_delayed_work(&info->deferred_work, 0);
return 0; return 0;
err_sysfs: err_sysfs:
fb_deferred_io_cleanup(info);
device_remove_file(dev, &dev_attr_fb_update_rate); device_remove_file(dev, &dev_attr_fb_update_rate);
err_cleanup: err_cleanup:
data->fb_vbitmap = NULL; data->fb_vbitmap = NULL;
...@@ -737,19 +861,17 @@ static void picolcd_exit_framebuffer(struct picolcd_data *data) ...@@ -737,19 +861,17 @@ static void picolcd_exit_framebuffer(struct picolcd_data *data)
{ {
struct fb_info *info = data->fb_info; struct fb_info *info = data->fb_info;
u8 *fb_vbitmap = data->fb_vbitmap; u8 *fb_vbitmap = data->fb_vbitmap;
u8 *fb_bitmap = data->fb_bitmap;
if (!info) if (!info)
return; return;
info->par = NULL;
device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
unregister_framebuffer(info);
data->fb_vbitmap = NULL; data->fb_vbitmap = NULL;
data->fb_bitmap = NULL; data->fb_bitmap = NULL;
data->fb_bpp = 0; data->fb_bpp = 0;
data->fb_info = NULL; data->fb_info = NULL;
device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
fb_deferred_io_cleanup(info);
unregister_framebuffer(info);
vfree(fb_bitmap);
kfree(fb_vbitmap); kfree(fb_vbitmap);
} }
...@@ -2566,6 +2688,13 @@ static void picolcd_remove(struct hid_device *hdev) ...@@ -2566,6 +2688,13 @@ static void picolcd_remove(struct hid_device *hdev)
spin_lock_irqsave(&data->lock, flags); spin_lock_irqsave(&data->lock, flags);
data->status |= PICOLCD_FAILED; data->status |= PICOLCD_FAILED;
spin_unlock_irqrestore(&data->lock, flags); spin_unlock_irqrestore(&data->lock, flags);
#ifdef CONFIG_HID_PICOLCD_FB
/* short-circuit FB as early as possible in order to
* avoid long delays if we host console.
*/
if (data->fb_info)
data->fb_info->par = NULL;
#endif
picolcd_exit_devfs(data); picolcd_exit_devfs(data);
device_remove_file(&hdev->dev, &dev_attr_operation_mode); device_remove_file(&hdev->dev, &dev_attr_operation_mode);
...@@ -2623,6 +2752,10 @@ static int __init picolcd_init(void) ...@@ -2623,6 +2752,10 @@ static int __init picolcd_init(void)
static void __exit picolcd_exit(void) static void __exit picolcd_exit(void)
{ {
hid_unregister_driver(&picolcd_driver); hid_unregister_driver(&picolcd_driver);
#ifdef CONFIG_HID_PICOLCD_FB
flush_scheduled_work();
WARN_ON(fb_pending);
#endif
} }
module_init(picolcd_init); module_init(picolcd_init);
......
...@@ -22,11 +22,6 @@ ...@@ -22,11 +22,6 @@
* Is it possible to remove and reinstall the urb in raw-event- or any * Is it possible to remove and reinstall the urb in raw-event- or any
* other handler, or to defer this action to be executed somewhere else? * other handler, or to defer this action to be executed somewhere else?
* *
* TODO implement notification mechanism for overlong macro execution
* If user wants to execute an overlong macro only the names of macroset
* and macro are given. Should userland tap hidraw or is there an
* additional streaming mechanism?
*
* TODO is it possible to overwrite group for sysfs attributes via udev? * TODO is it possible to overwrite group for sysfs attributes via udev?
*/ */
...@@ -277,7 +272,7 @@ static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj, ...@@ -277,7 +272,7 @@ static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj,
count = sizeof(struct kone_settings) - off; count = sizeof(struct kone_settings) - off;
mutex_lock(&kone->kone_lock); mutex_lock(&kone->kone_lock);
memcpy(buf, &kone->settings + off, count); memcpy(buf, ((char const *)&kone->settings) + off, count);
mutex_unlock(&kone->kone_lock); mutex_unlock(&kone->kone_lock);
return count; return count;
...@@ -337,7 +332,7 @@ static ssize_t kone_sysfs_read_profilex(struct kobject *kobj, ...@@ -337,7 +332,7 @@ static ssize_t kone_sysfs_read_profilex(struct kobject *kobj,
count = sizeof(struct kone_profile) - off; count = sizeof(struct kone_profile) - off;
mutex_lock(&kone->kone_lock); mutex_lock(&kone->kone_lock);
memcpy(buf, &kone->profiles[number - 1], sizeof(struct kone_profile)); memcpy(buf, ((char const *)&kone->profiles[number - 1]) + off, count);
mutex_unlock(&kone->kone_lock); mutex_unlock(&kone->kone_lock);
return count; return count;
...@@ -622,18 +617,6 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev, ...@@ -622,18 +617,6 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev,
return size; return size;
} }
/*
* This file is used by userland software to find devices that are handled by
* this driver. This provides a consistent way for actual and older kernels
* where this driver replaced usbhid instead of generic-usb.
* Driver capabilities are determined by version number.
*/
static ssize_t kone_sysfs_show_driver_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, ROCCAT_KONE_DRIVER_VERSION "\n");
}
/* /*
* Read actual dpi settings. * Read actual dpi settings.
* Returns raw value for further processing. Refer to enum kone_polling_rates to * Returns raw value for further processing. Refer to enum kone_polling_rates to
...@@ -671,9 +654,6 @@ static DEVICE_ATTR(startup_profile, 0660, ...@@ -671,9 +654,6 @@ static DEVICE_ATTR(startup_profile, 0660,
kone_sysfs_show_startup_profile, kone_sysfs_show_startup_profile,
kone_sysfs_set_startup_profile); kone_sysfs_set_startup_profile);
static DEVICE_ATTR(kone_driver_version, 0440,
kone_sysfs_show_driver_version, NULL);
static struct attribute *kone_attributes[] = { static struct attribute *kone_attributes[] = {
&dev_attr_actual_dpi.attr, &dev_attr_actual_dpi.attr,
&dev_attr_actual_profile.attr, &dev_attr_actual_profile.attr,
...@@ -681,7 +661,6 @@ static struct attribute *kone_attributes[] = { ...@@ -681,7 +661,6 @@ static struct attribute *kone_attributes[] = {
&dev_attr_firmware_version.attr, &dev_attr_firmware_version.attr,
&dev_attr_tcu.attr, &dev_attr_tcu.attr,
&dev_attr_startup_profile.attr, &dev_attr_startup_profile.attr,
&dev_attr_kone_driver_version.attr,
NULL NULL
}; };
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
#include <linux/types.h> #include <linux/types.h>
#define ROCCAT_KONE_DRIVER_VERSION "v0.3.1"
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
......
...@@ -168,7 +168,7 @@ static int roccat_open(struct inode *inode, struct file *file) ...@@ -168,7 +168,7 @@ static int roccat_open(struct inode *inode, struct file *file)
printk(KERN_EMERG "roccat device with minor %d doesn't exist\n", printk(KERN_EMERG "roccat device with minor %d doesn't exist\n",
minor); minor);
error = -ENODEV; error = -ENODEV;
goto exit_unlock; goto exit_err;
} }
if (!device->open++) { if (!device->open++) {
...@@ -178,7 +178,7 @@ static int roccat_open(struct inode *inode, struct file *file) ...@@ -178,7 +178,7 @@ static int roccat_open(struct inode *inode, struct file *file)
PM_HINT_FULLON); PM_HINT_FULLON);
if (error < 0) { if (error < 0) {
--device->open; --device->open;
goto exit_unlock; goto exit_err;
} }
} }
error = device->hid->ll_driver->open(device->hid); error = device->hid->ll_driver->open(device->hid);
...@@ -187,7 +187,7 @@ static int roccat_open(struct inode *inode, struct file *file) ...@@ -187,7 +187,7 @@ static int roccat_open(struct inode *inode, struct file *file)
device->hid->ll_driver->power(device->hid, device->hid->ll_driver->power(device->hid,
PM_HINT_NORMAL); PM_HINT_NORMAL);
--device->open; --device->open;
goto exit_unlock; goto exit_err;
} }
} }
...@@ -202,6 +202,9 @@ static int roccat_open(struct inode *inode, struct file *file) ...@@ -202,6 +202,9 @@ static int roccat_open(struct inode *inode, struct file *file)
mutex_unlock(&device->readers_lock); mutex_unlock(&device->readers_lock);
mutex_unlock(&devices_lock); mutex_unlock(&devices_lock);
return error; return error;
exit_err:
kfree(reader);
goto exit_unlock;
} }
static int roccat_release(struct inode *inode, struct file *file) static int roccat_release(struct inode *inode, struct file *file)
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/types.h> #include <linux/types.h>
#if defined(CONFIG_HID_ROCCAT) || defined (CONFIG_HID_ROCCAT_MODULE) #if defined(CONFIG_HID_ROCCAT) || defined(CONFIG_HID_ROCCAT_MODULE)
int roccat_connect(struct hid_device *hid); int roccat_connect(struct hid_device *hid);
void roccat_disconnect(int minor); void roccat_disconnect(int minor);
int roccat_report_event(int minor, u8 const *data, int len); int roccat_report_event(int minor, u8 const *data, int len);
......
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
* *
* Modified to also support BTC "Emprex 3009URF III Vista MCE Remote" by * Modified to also support BTC "Emprex 3009URF III Vista MCE Remote" by
* Wayne Thomas 2010. * Wayne Thomas 2010.
*
* Modified to support Conceptronic CLLRCMCE by
* Kees Bakker 2010.
*/ */
/* /*
...@@ -34,6 +37,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -34,6 +37,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case 0x00d: ts_map_key_clear(KEY_MEDIA); break; case 0x00d: ts_map_key_clear(KEY_MEDIA); break;
case 0x024: ts_map_key_clear(KEY_MENU); break; case 0x024: ts_map_key_clear(KEY_MENU); break;
case 0x025: ts_map_key_clear(KEY_TV); break; case 0x025: ts_map_key_clear(KEY_TV); break;
case 0x027: ts_map_key_clear(KEY_MODE); break;
case 0x031: ts_map_key_clear(KEY_AUDIO); break; case 0x031: ts_map_key_clear(KEY_AUDIO); break;
case 0x032: ts_map_key_clear(KEY_TEXT); break; case 0x032: ts_map_key_clear(KEY_TEXT); break;
case 0x033: ts_map_key_clear(KEY_CHANNEL); break; case 0x033: ts_map_key_clear(KEY_CHANNEL); break;
...@@ -60,6 +64,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -60,6 +64,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
static const struct hid_device_id ts_devices[] = { static const struct hid_device_id ts_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },
{ } { }
}; };
MODULE_DEVICE_TABLE(hid, ts_devices); MODULE_DEVICE_TABLE(hid, ts_devices);
......
...@@ -436,7 +436,7 @@ static void wacom_remove(struct hid_device *hdev) ...@@ -436,7 +436,7 @@ static void wacom_remove(struct hid_device *hdev)
static const struct hid_device_id wacom_devices[] = { static const struct hid_device_id wacom_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
{ } { }
}; };
MODULE_DEVICE_TABLE(hid, wacom_devices); MODULE_DEVICE_TABLE(hid, wacom_devices);
......
...@@ -46,7 +46,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, ...@@ -46,7 +46,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
{ {
struct hidraw_list *list = file->private_data; struct hidraw_list *list = file->private_data;
int ret = 0, len; int ret = 0, len;
char *report;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
mutex_lock(&list->read_mutex); mutex_lock(&list->read_mutex);
...@@ -84,7 +83,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, ...@@ -84,7 +83,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
if (ret) if (ret)
goto out; goto out;
report = list->buffer[list->tail].value;
len = list->buffer[list->tail].len > count ? len = list->buffer[list->tail].len > count ?
count : list->buffer[list->tail].len; count : list->buffer[list->tail].len;
......
...@@ -75,6 +75,8 @@ static const struct hid_blacklist { ...@@ -75,6 +75,8 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_PI_ENGINEERING, USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL, HID_QUIRK_HIDINPUT_FORCE },
{ 0, 0 } { 0, 0 }
}; };
......
...@@ -67,7 +67,7 @@ struct hiddev_list { ...@@ -67,7 +67,7 @@ struct hiddev_list {
struct mutex thread_lock; struct mutex thread_lock;
}; };
static struct hiddev *hiddev_table[HIDDEV_MINORS]; static struct usb_driver hiddev_driver;
/* /*
* Find a report, given the report's type and ID. The ID can be specified * Find a report, given the report's type and ID. The ID can be specified
...@@ -265,22 +265,19 @@ static int hiddev_release(struct inode * inode, struct file * file) ...@@ -265,22 +265,19 @@ static int hiddev_release(struct inode * inode, struct file * file)
static int hiddev_open(struct inode *inode, struct file *file) static int hiddev_open(struct inode *inode, struct file *file)
{ {
struct hiddev_list *list; struct hiddev_list *list;
int res, i; struct usb_interface *intf;
struct hiddev *hiddev;
/* See comment in hiddev_connect() for BKL explanation */ int res;
lock_kernel();
i = iminor(inode) - HIDDEV_MINOR_BASE;
if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i]) intf = usb_find_interface(&hiddev_driver, iminor(inode));
if (!intf)
return -ENODEV; return -ENODEV;
hiddev = usb_get_intfdata(intf);
if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
return -ENOMEM; return -ENOMEM;
mutex_init(&list->thread_lock); mutex_init(&list->thread_lock);
list->hiddev = hiddev;
list->hiddev = hiddev_table[i];
file->private_data = list; file->private_data = list;
/* /*
...@@ -289,7 +286,7 @@ static int hiddev_open(struct inode *inode, struct file *file) ...@@ -289,7 +286,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
*/ */
if (list->hiddev->exist) { if (list->hiddev->exist) {
if (!list->hiddev->open++) { if (!list->hiddev->open++) {
res = usbhid_open(hiddev_table[i]->hid); res = usbhid_open(hiddev->hid);
if (res < 0) { if (res < 0) {
res = -EIO; res = -EIO;
goto bail; goto bail;
...@@ -301,12 +298,12 @@ static int hiddev_open(struct inode *inode, struct file *file) ...@@ -301,12 +298,12 @@ static int hiddev_open(struct inode *inode, struct file *file)
} }
spin_lock_irq(&list->hiddev->list_lock); spin_lock_irq(&list->hiddev->list_lock);
list_add_tail(&list->node, &hiddev_table[i]->list); list_add_tail(&list->node, &hiddev->list);
spin_unlock_irq(&list->hiddev->list_lock); spin_unlock_irq(&list->hiddev->list_lock);
if (!list->hiddev->open++) if (!list->hiddev->open++)
if (list->hiddev->exist) { if (list->hiddev->exist) {
struct hid_device *hid = hiddev_table[i]->hid; struct hid_device *hid = hiddev->hid;
res = usbhid_get_power(hid); res = usbhid_get_power(hid);
if (res < 0) { if (res < 0) {
res = -EIO; res = -EIO;
...@@ -314,13 +311,10 @@ static int hiddev_open(struct inode *inode, struct file *file) ...@@ -314,13 +311,10 @@ static int hiddev_open(struct inode *inode, struct file *file)
} }
usbhid_open(hid); usbhid_open(hid);
} }
unlock_kernel();
return 0; return 0;
bail: bail:
file->private_data = NULL; file->private_data = NULL;
kfree(list); kfree(list);
unlock_kernel();
return res; return res;
} }
...@@ -894,37 +888,14 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) ...@@ -894,37 +888,14 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
hid->hiddev = hiddev; hid->hiddev = hiddev;
hiddev->hid = hid; hiddev->hid = hid;
hiddev->exist = 1; hiddev->exist = 1;
usb_set_intfdata(usbhid->intf, usbhid);
/*
* BKL here is used to avoid race after usb_register_dev().
* Once the device node has been created, open() could happen on it.
* The code below will then fail, as hiddev_table hasn't been
* updated.
*
* The obvious fix -- introducing mutex to guard hiddev_table[]
* doesn't work, as usb_open() and usb_register_dev() both take
* minor_rwsem, thus we'll have ABBA deadlock.
*
* Before BKL pushdown, usb_open() had been acquiring it in right
* order, so _open() was safe to use it to protect from this race.
* Now the order is different, but AB-BA deadlock still doesn't occur
* as BKL is dropped on schedule() (i.e. while sleeping on
* minor_rwsem). Fugly.
*/
lock_kernel();
retval = usb_register_dev(usbhid->intf, &hiddev_class); retval = usb_register_dev(usbhid->intf, &hiddev_class);
if (retval) { if (retval) {
err_hid("Not able to get a minor for this device."); err_hid("Not able to get a minor for this device.");
hid->hiddev = NULL; hid->hiddev = NULL;
unlock_kernel();
kfree(hiddev); kfree(hiddev);
return -1; return -1;
} else {
hid->minor = usbhid->intf->minor;
hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
} }
unlock_kernel();
return 0; return 0;
} }
...@@ -942,7 +913,6 @@ void hiddev_disconnect(struct hid_device *hid) ...@@ -942,7 +913,6 @@ void hiddev_disconnect(struct hid_device *hid)
hiddev->exist = 0; hiddev->exist = 0;
mutex_unlock(&hiddev->existancelock); mutex_unlock(&hiddev->existancelock);
hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
usb_deregister_dev(usbhid->intf, &hiddev_class); usb_deregister_dev(usbhid->intf, &hiddev_class);
if (hiddev->open) { if (hiddev->open) {
......
...@@ -311,6 +311,7 @@ struct hid_item { ...@@ -311,6 +311,7 @@ struct hid_item {
#define HID_QUIRK_HIDDEV_FORCE 0x00000010 #define HID_QUIRK_HIDDEV_FORCE 0x00000010
#define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_BADPAD 0x00000020
#define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_HIDINPUT_FORCE 0x00000080
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment