mirror of
https://github.com/torvalds/linux.git
synced 2024-12-30 14:52:05 +00:00
2f43f8749e
Paul Walmsley has implemented dynamic quirk handling back in 2007 through commits:2eb5dc30eb
("USB HID: encapsulate quirk handling into hid-quirks.c")8222fbe67c
("USB HID: clarify static quirk handling as squirks")8cef908235
("USB HID: add support for dynamically-created quirks")876b9276b9
("USB HID: add 'quirks' module parameter") and as such, his copyright rightly belongs to drivers/hid/usbhid/hid-quirks.c file. However when generic HID code has been converted to bus and individual quirks separated out to individual drivers on the bus, the copyright has been blindly transfered into all the tiny drivers, which actually don't contain any of Pauls' copyrighted code. Remove the copyright from those sub-drivers. Signed-off-by: Jiri Kosina <jkosina@suse.cz> Acked-by: Paul Walmsley <paul@pwsan.com>
230 lines
6.0 KiB
C
230 lines
6.0 KiB
C
/*
|
|
* HID driver for some microsoft "special" devices
|
|
*
|
|
* 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) 2006-2007 Jiri Kosina
|
|
* Copyright (c) 2008 Jiri Slaby
|
|
*/
|
|
|
|
/*
|
|
* 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/input.h>
|
|
#include <linux/hid.h>
|
|
#include <linux/module.h>
|
|
|
|
#include "hid-ids.h"
|
|
|
|
#define MS_HIDINPUT 0x01
|
|
#define MS_ERGONOMY 0x02
|
|
#define MS_PRESENTER 0x04
|
|
#define MS_RDESC 0x08
|
|
#define MS_NOGET 0x10
|
|
#define MS_DUPLICATE_USAGES 0x20
|
|
|
|
/*
|
|
* Microsoft Wireless Desktop Receiver (Model 1028) has
|
|
* 'Usage Min/Max' where it ought to have 'Physical Min/Max'
|
|
*/
|
|
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|
unsigned int *rsize)
|
|
{
|
|
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
|
|
|
if ((quirks & MS_RDESC) && *rsize == 571 && rdesc[557] == 0x19 &&
|
|
rdesc[559] == 0x29) {
|
|
hid_info(hdev, "fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n");
|
|
rdesc[557] = 0x35;
|
|
rdesc[559] = 0x45;
|
|
}
|
|
return rdesc;
|
|
}
|
|
|
|
#define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
|
EV_KEY, (c))
|
|
static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
|
|
unsigned long **bit, int *max)
|
|
{
|
|
struct input_dev *input = hi->input;
|
|
|
|
switch (usage->hid & HID_USAGE) {
|
|
case 0xfd06: ms_map_key_clear(KEY_CHAT); break;
|
|
case 0xfd07: ms_map_key_clear(KEY_PHONE); break;
|
|
case 0xff05:
|
|
set_bit(EV_REP, input->evbit);
|
|
ms_map_key_clear(KEY_F13);
|
|
set_bit(KEY_F14, input->keybit);
|
|
set_bit(KEY_F15, input->keybit);
|
|
set_bit(KEY_F16, input->keybit);
|
|
set_bit(KEY_F17, input->keybit);
|
|
set_bit(KEY_F18, input->keybit);
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
|
|
unsigned long **bit, int *max)
|
|
{
|
|
set_bit(EV_REP, hi->input->evbit);
|
|
switch (usage->hid & HID_USAGE) {
|
|
case 0xfd08: ms_map_key_clear(KEY_FORWARD); break;
|
|
case 0xfd09: ms_map_key_clear(KEY_BACK); break;
|
|
case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE); break;
|
|
case 0xfd0e: ms_map_key_clear(KEY_CLOSE); break;
|
|
case 0xfd0f: ms_map_key_clear(KEY_PLAY); break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
unsigned long **bit, int *max)
|
|
{
|
|
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
|
|
|
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
|
|
return 0;
|
|
|
|
if (quirks & MS_ERGONOMY) {
|
|
int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if ((quirks & MS_PRESENTER) &&
|
|
ms_presenter_8k_quirk(hi, usage, bit, max))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
unsigned long **bit, int *max)
|
|
{
|
|
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
|
|
|
if (quirks & MS_DUPLICATE_USAGES)
|
|
clear_bit(usage->code, *bit);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ms_event(struct hid_device *hdev, struct hid_field *field,
|
|
struct hid_usage *usage, __s32 value)
|
|
{
|
|
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
|
|
|
if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
|
|
!usage->type)
|
|
return 0;
|
|
|
|
/* Handling MS keyboards special buttons */
|
|
if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
|
|
struct input_dev *input = field->hidinput->input;
|
|
static unsigned int last_key = 0;
|
|
unsigned int key = 0;
|
|
switch (value) {
|
|
case 0x01: key = KEY_F14; break;
|
|
case 0x02: key = KEY_F15; break;
|
|
case 0x04: key = KEY_F16; break;
|
|
case 0x08: key = KEY_F17; break;
|
|
case 0x10: key = KEY_F18; break;
|
|
}
|
|
if (key) {
|
|
input_event(input, usage->type, key, 1);
|
|
last_key = key;
|
|
} else
|
|
input_event(input, usage->type, last_key, 0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
{
|
|
unsigned long quirks = id->driver_data;
|
|
int ret;
|
|
|
|
hid_set_drvdata(hdev, (void *)quirks);
|
|
|
|
if (quirks & MS_NOGET)
|
|
hdev->quirks |= HID_QUIRK_NOGET;
|
|
|
|
ret = hid_parse(hdev);
|
|
if (ret) {
|
|
hid_err(hdev, "parse failed\n");
|
|
goto err_free;
|
|
}
|
|
|
|
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ?
|
|
HID_CONNECT_HIDINPUT_FORCE : 0));
|
|
if (ret) {
|
|
hid_err(hdev, "hw start failed\n");
|
|
goto err_free;
|
|
}
|
|
|
|
return 0;
|
|
err_free:
|
|
return ret;
|
|
}
|
|
|
|
static const struct hid_device_id ms_devices[] = {
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
|
|
.driver_data = MS_HIDINPUT },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
|
|
.driver_data = MS_ERGONOMY },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K),
|
|
.driver_data = MS_ERGONOMY | MS_RDESC },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
|
|
.driver_data = MS_PRESENTER },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K),
|
|
.driver_data = MS_ERGONOMY },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0),
|
|
.driver_data = MS_NOGET },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
|
|
.driver_data = MS_DUPLICATE_USAGES },
|
|
|
|
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
|
|
.driver_data = MS_PRESENTER },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(hid, ms_devices);
|
|
|
|
static struct hid_driver ms_driver = {
|
|
.name = "microsoft",
|
|
.id_table = ms_devices,
|
|
.report_fixup = ms_report_fixup,
|
|
.input_mapping = ms_input_mapping,
|
|
.input_mapped = ms_input_mapped,
|
|
.event = ms_event,
|
|
.probe = ms_probe,
|
|
};
|
|
|
|
static int __init ms_init(void)
|
|
{
|
|
return hid_register_driver(&ms_driver);
|
|
}
|
|
|
|
static void __exit ms_exit(void)
|
|
{
|
|
hid_unregister_driver(&ms_driver);
|
|
}
|
|
|
|
module_init(ms_init);
|
|
module_exit(ms_exit);
|
|
MODULE_LICENSE("GPL");
|