1
0
Files
Documentation
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
cpufreq
cpuidle
crypto
dax
dca
devfreq
dio
dma
dma-buf
edac
eisa
extcon
firewire
firmware
fmc
fpga
gpio
gpu
hid
i2c-hid
intel-ish-hid
usbhid
Kconfig
Makefile
hid-a4tech.c
hid-alps.c
hid-apple.c
hid-appleir.c
hid-asus.c
hid-aureal.c
hid-axff.c
hid-belkin.c
hid-betopff.c
hid-cherry.c
hid-chicony.c
hid-cmedia.c
hid-core.c
hid-corsair.c
hid-cp2112.c
hid-cypress.c
hid-debug.c
hid-dr.c
hid-elecom.c
hid-elo.c
hid-emsff.c
hid-ezkey.c
hid-gaff.c
hid-gembird.c
hid-generic.c
hid-gfrm.c
hid-gt683r.c
hid-gyration.c
hid-holtek-kbd.c
hid-holtek-mouse.c
hid-holtekff.c
hid-hyperv.c
hid-icade.c
hid-ids.h
hid-input.c
hid-kensington.c
hid-keytouch.c
hid-kye.c
hid-lcpower.c
hid-led.c
hid-lenovo.c
hid-lg.c
hid-lg.h
hid-lg2ff.c
hid-lg3ff.c
hid-lg4ff.c
hid-lg4ff.h
hid-lgff.c
hid-logitech-dj.c
hid-logitech-hidpp.c
hid-magicmouse.c
hid-microsoft.c
hid-monterey.c
hid-multitouch.c
hid-nintendo.c
hid-ntrig.c
hid-ortek.c
hid-penmount.c
hid-petalynx.c
hid-picolcd.h
hid-picolcd_backlight.c
hid-picolcd_cir.c
hid-picolcd_core.c
hid-picolcd_debugfs.c
hid-picolcd_fb.c
hid-picolcd_lcd.c
hid-picolcd_leds.c
hid-pl.c
hid-plantronics.c
hid-primax.c
hid-prodikeys.c
hid-rmi.c
hid-roccat-arvo.c
hid-roccat-arvo.h
hid-roccat-common.c
hid-roccat-common.h
hid-roccat-isku.c
hid-roccat-isku.h
hid-roccat-kone.c
hid-roccat-kone.h
hid-roccat-koneplus.c
hid-roccat-koneplus.h
hid-roccat-konepure.c
hid-roccat-kovaplus.c
hid-roccat-kovaplus.h
hid-roccat-lua.c
hid-roccat-lua.h
hid-roccat-pyra.c
hid-roccat-pyra.h
hid-roccat-ryos.c
hid-roccat-savu.c
hid-roccat-savu.h
hid-roccat.c
hid-saitek.c
hid-samsung.c
hid-sensor-custom.c
hid-sensor-hub.c
hid-sjoy.c
hid-sony.c
hid-speedlink.c
hid-steam.c
hid-steelseries.c
hid-sunplus.c
hid-tivo.c
hid-tmff.c
hid-topseed.c
hid-twinhan.c
hid-uclogic.c
hid-waltop.c
hid-wiimote-core.c
hid-wiimote-debug.c
hid-wiimote-modules.c
hid-wiimote.h
hid-xinmo.c
hid-zpff.c
hid-zydacron.c
hidraw.c
uhid.c
wacom.h
wacom_sys.c
wacom_wac.c
wacom_wac.h
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
ide
idle
iio
infiniband
input
iommu
ipack
irqchip
isdn
leds
lguest
lightnvm
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
mtd
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
sn
soc
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
uio
usb
uwb
vfio
vhost
video
virt
virtio
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
firmware
fs
include
init
ipc
kernel
lib
mm
ndm
net
samples
scripts
security
sound
tools
usr
virt
.cocciconfig
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
build.config.aarch64
build.config.common
build.config.cuttlefish.aarch64
build.config.cuttlefish.x86_64
build.config.goldfish.arm
build.config.goldfish.arm64
build.config.goldfish.mips
build.config.goldfish.mips64
build.config.goldfish.x86
build.config.goldfish.x86_64
build.config.x86_64
localversion-ndm
verity_dev_keys.x509
Greg Kroah-Hartman eabebf2314 Merge 4.9.199 into android-4.9-q
Changes in 4.9.199
	dm snapshot: use mutex instead of rw_semaphore
	dm snapshot: introduce account_start_copy() and account_end_copy()
	dm snapshot: rework COW throttling to fix deadlock
	dm: Use kzalloc for all structs with embedded biosets/mempools
	sc16is7xx: Fix for "Unexpected interrupt: 8"
	HID: i2c-hid: add Direkt-Tek DTLAPY133-1 to descriptor override
	x86/cpu: Add Atom Tremont (Jacobsville)
	HID: i2c-hid: Add Odys Winbook 13 to descriptor override
	scripts/setlocalversion: Improve -dirty check with git-status --no-optional-locks
	usb: handle warm-reset port requests on hub resume
	rtc: pcf8523: set xtal load capacitance from DT
	exec: load_script: Do not exec truncated interpreter path
	iio: fix center temperature of bmc150-accel-core
	perf map: Fix overlapped map handling
	perf jevents: Fix period for Intel fixed counters
	staging: rtl8188eu: fix null dereference when kzalloc fails
	RDMA/iwcm: Fix a lock inversion issue
	gpio: max77620: Use correct unit for debounce times
	fs: cifs: mute -Wunused-const-variable message
	serial: mctrl_gpio: Check for NULL pointer
	efi/cper: Fix endianness of PCIe class code
	efi/x86: Do not clean dummy variable in kexec path
	ocfs2: clear zero in unaligned direct IO
	fs: ocfs2: fix possible null-pointer dereferences in ocfs2_xa_prepare_entry()
	fs: ocfs2: fix a possible null-pointer dereference in ocfs2_write_end_nolock()
	fs: ocfs2: fix a possible null-pointer dereference in ocfs2_info_scan_inode_alloc()
	MIPS: fw: sni: Fix out of bounds init of o32 stack
	NFSv4: Fix leak of clp->cl_acceptor string
	s390/uaccess: avoid (false positive) compiler warnings
	tracing: Initialize iter->seq after zeroing in tracing_read_pipe()
	USB: legousbtower: fix a signedness bug in tower_probe()
	thunderbolt: Use 32-bit writes when writing ring producer/consumer
	ath6kl: fix a NULL-ptr-deref bug in ath6kl_usb_alloc_urb_from_pipe()
	fuse: flush dirty data/metadata before non-truncate setattr
	fuse: truncate pending writes on O_TRUNC
	ALSA: bebob: Fix prototype of helper function to return negative value
	UAS: Revert commit 3ae62a42090f ("UAS: fix alignment of scatter/gather segments")
	USB: gadget: Reject endpoints with 0 maxpacket value
	usb-storage: Revert commit 747668dbc061 ("usb-storage: Set virt_boundary_mask to avoid SG overflows")
	USB: ldusb: fix ring-buffer locking
	USB: ldusb: fix control-message timeout
	USB: serial: whiteheat: fix potential slab corruption
	USB: serial: whiteheat: fix line-speed endianness
	HID: i2c-hid: add Trekstor Primebook C11B to descriptor override
	HID: Fix assumption that devices have inputs
	HID: fix error message in hid_open_report()
	nl80211: fix validation of mesh path nexthop
	s390/cmm: fix information leak in cmm_timeout_handler()
	rtlwifi: Fix potential overflow on P2P code
	dmaengine: cppi41: Fix cppi41_dma_prep_slave_sg() when idle
	llc: fix sk_buff leak in llc_sap_state_process()
	llc: fix sk_buff leak in llc_conn_service()
	bonding: fix potential NULL deref in bond_update_slave_arr
	net: usb: sr9800: fix uninitialized local variable
	sch_netem: fix rcu splat in netem_enqueue()
	sctp: fix the issue that flags are ignored when using kernel_connect
	sctp: not bind the socket in sctp_connect
	xfs: Correctly invert xfs_buftarg LRU isolation logic
	ALSA: timer: Follow standard EXPORT_SYMBOL() declarations
	ALSA: timer: Limit max instances per timer
	ALSA: timer: Simplify error path in snd_timer_open()
	ALSA: timer: Fix mutex deadlock at releasing card
	Revert "ALSA: hda: Flush interrupts on disabling"
	Linux 4.9.199

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
2019-11-12 21:28:55 +03:00

332 lines
9.8 KiB
C

/*
* Force feedback support for DragonRise Inc. game controllers
*
* From what I have gathered, these devices are mass produced in China and are
* distributed under several vendors. They often share the same design as
* the original PlayStation DualShock controller.
*
* 0079:0006 "DragonRise Inc. Generic USB Joystick "
* - tested with a Tesun USB-703 game controller.
*
* Copyright (c) 2009 Richard Walmsley <richwalm@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.
*
* 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/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
#ifdef CONFIG_DRAGONRISE_FF
struct drff_device {
struct hid_report *report;
};
static int drff_play(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct drff_device *drff = data;
int strong, weak;
strong = effect->u.rumble.strong_magnitude;
weak = effect->u.rumble.weak_magnitude;
dbg_hid("called with 0x%04x 0x%04x", strong, weak);
if (strong || weak) {
strong = strong * 0xff / 0xffff;
weak = weak * 0xff / 0xffff;
/* While reverse engineering this device, I found that when
this value is set, it causes the strong rumble to function
at a near maximum speed, so we'll bypass it. */
if (weak == 0x0a)
weak = 0x0b;
drff->report->field[0]->value[0] = 0x51;
drff->report->field[0]->value[1] = 0x00;
drff->report->field[0]->value[2] = weak;
drff->report->field[0]->value[4] = strong;
hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
drff->report->field[0]->value[0] = 0xfa;
drff->report->field[0]->value[1] = 0xfe;
} else {
drff->report->field[0]->value[0] = 0xf3;
drff->report->field[0]->value[1] = 0x00;
}
drff->report->field[0]->value[2] = 0x00;
drff->report->field[0]->value[4] = 0x00;
dbg_hid("running with 0x%02x 0x%02x", strong, weak);
hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
return 0;
}
static int drff_init(struct hid_device *hid)
{
struct drff_device *drff;
struct hid_report *report;
struct hid_input *hidinput;
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev;
int error;
if (list_empty(&hid->inputs)) {
hid_err(hid, "no inputs found\n");
return -ENODEV;
}
hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
dev = hidinput->input;
if (list_empty(report_list)) {
hid_err(hid, "no output reports found\n");
return -ENODEV;
}
report = list_first_entry(report_list, struct hid_report, list);
if (report->maxfield < 1) {
hid_err(hid, "no fields in the report\n");
return -ENODEV;
}
if (report->field[0]->report_count < 7) {
hid_err(hid, "not enough values in the field\n");
return -ENODEV;
}
drff = kzalloc(sizeof(struct drff_device), GFP_KERNEL);
if (!drff)
return -ENOMEM;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, drff, drff_play);
if (error) {
kfree(drff);
return error;
}
drff->report = report;
drff->report->field[0]->value[0] = 0xf3;
drff->report->field[0]->value[1] = 0x00;
drff->report->field[0]->value[2] = 0x00;
drff->report->field[0]->value[3] = 0x00;
drff->report->field[0]->value[4] = 0x00;
drff->report->field[0]->value[5] = 0x00;
drff->report->field[0]->value[6] = 0x00;
hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
hid_info(hid, "Force Feedback for DragonRise Inc. "
"game controllers by Richard Walmsley <richwalm@gmail.com>\n");
return 0;
}
#else
static inline int drff_init(struct hid_device *hid)
{
return 0;
}
#endif
/*
* The original descriptor of joystick with PID 0x0011, represented by DVTech PC
* JS19. It seems both copied from another device and a result of confusion
* either about the specification or about the program used to create the
* descriptor. In any case, it's a wonder it works on Windows.
*
* Usage Page (Desktop), ; Generic desktop controls (01h)
* Usage (Joystick), ; Joystick (04h, application collection)
* Collection (Application),
* Collection (Logical),
* Report Size (8),
* Report Count (5),
* Logical Minimum (0),
* Logical Maximum (255),
* Physical Minimum (0),
* Physical Maximum (255),
* Usage (X), ; X (30h, dynamic value)
* Usage (X), ; X (30h, dynamic value)
* Usage (X), ; X (30h, dynamic value)
* Usage (X), ; X (30h, dynamic value)
* Usage (Y), ; Y (31h, dynamic value)
* Input (Variable),
* Report Size (4),
* Report Count (1),
* Logical Maximum (7),
* Physical Maximum (315),
* Unit (Degrees),
* Usage (00h),
* Input (Variable, Null State),
* Unit,
* Report Size (1),
* Report Count (10),
* Logical Maximum (1),
* Physical Maximum (1),
* Usage Page (Button), ; Button (09h)
* Usage Minimum (01h),
* Usage Maximum (0Ah),
* Input (Variable),
* Usage Page (FF00h), ; FF00h, vendor-defined
* Report Size (1),
* Report Count (10),
* Logical Maximum (1),
* Physical Maximum (1),
* Usage (01h),
* Input (Variable),
* End Collection,
* Collection (Logical),
* Report Size (8),
* Report Count (4),
* Physical Maximum (255),
* Logical Maximum (255),
* Usage (02h),
* Output (Variable),
* End Collection,
* End Collection
*/
/* Size of the original descriptor of the PID 0x0011 joystick */
#define PID0011_RDESC_ORIG_SIZE 101
/* Fixed report descriptor for PID 0x011 joystick */
static __u8 pid0011_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x14, /* Logical Minimum (0), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x01, /* Input (Constant), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x95, 0x02, /* Report Count (2), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x01, /* Input (Constant), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x0A, /* Report Count (10), */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x0A, /* Usage Maximum (0Ah), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x0A, /* Report Count (10), */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
switch (hdev->product) {
case 0x0011:
if (*rsize == PID0011_RDESC_ORIG_SIZE) {
rdesc = pid0011_rdesc_fixed;
*rsize = sizeof(pid0011_rdesc_fixed);
}
break;
}
return rdesc;
}
#define map_abs(c) hid_map_usage(hi, usage, bit, max, EV_ABS, (c))
#define map_rel(c) hid_map_usage(hi, usage, bit, max, EV_REL, (c))
static int dr_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
switch (usage->hid) {
/*
* revert to the old hid-input behavior where axes
* can be randomly assigned when hid->usage is reused.
*/
case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
if (field->flags & HID_MAIN_ITEM_RELATIVE)
map_rel(usage->hid & 0xf);
else
map_abs(usage->hid & 0xf);
return 1;
}
return 0;
}
static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
dev_dbg(&hdev->dev, "DragonRise Inc. HID hardware probe...");
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
goto err;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
if (ret) {
hid_err(hdev, "hw start failed\n");
goto err;
}
switch (hdev->product) {
case 0x0006:
ret = drff_init(hdev);
if (ret) {
dev_err(&hdev->dev, "force feedback init failed\n");
hid_hw_stop(hdev);
goto err;
}
break;
}
return 0;
err:
return ret;
}
static const struct hid_device_id dr_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006), },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011), },
{ }
};
MODULE_DEVICE_TABLE(hid, dr_devices);
static struct hid_driver dr_driver = {
.name = "dragonrise",
.id_table = dr_devices,
.report_fixup = dr_report_fixup,
.probe = dr_probe,
.input_mapping = dr_input_mapping,
};
module_hid_driver(dr_driver);
MODULE_LICENSE("GPL");