brintos

brintos / linux-shallow public Read only

0
0
Text · 15.8 KiB · 9f05465 Raw
495 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 *  LED & force feedback support for BigBen Interactive5 *6 *  0x146b:0x0902 "Bigben Interactive Bigben Game Pad"7 *  "Kid-friendly Wired Controller" PS3OFMINIPAD SONY8 *  sold for use with the PS39 *10 *  Copyright (c) 2018 Hanno Zulla <kontakt@hanno.de>11 */12 13#include <linux/input.h>14#include <linux/slab.h>15#include <linux/module.h>16#include <linux/leds.h>17#include <linux/hid.h>18 19#include "hid-ids.h"20 21 22/*23 * The original descriptor for 0x146b:0x090224 *25 *   0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)26 *   0x09, 0x05,        // Usage (Game Pad)27 *   0xA1, 0x01,        // Collection (Application)28 *   0x15, 0x00,        //   Logical Minimum (0)29 *   0x25, 0x01,        //   Logical Maximum (1)30 *   0x35, 0x00,        //   Physical Minimum (0)31 *   0x45, 0x01,        //   Physical Maximum (1)32 *   0x75, 0x01,        //   Report Size (1)33 *   0x95, 0x0D,        //   Report Count (13)34 *   0x05, 0x09,        //   Usage Page (Button)35 *   0x19, 0x01,        //   Usage Minimum (0x01)36 *   0x29, 0x0D,        //   Usage Maximum (0x0D)37 *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)38 *   0x95, 0x03,        //   Report Count (3)39 *   0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)40 *   0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)41 *   0x25, 0x07,        //   Logical Maximum (7)42 *   0x46, 0x3B, 0x01,  //   Physical Maximum (315)43 *   0x75, 0x04,        //   Report Size (4)44 *   0x95, 0x01,        //   Report Count (1)45 *   0x65, 0x14,        //   Unit (System: English Rotation, Length: Centimeter)46 *   0x09, 0x39,        //   Usage (Hat switch)47 *   0x81, 0x42,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)48 *   0x65, 0x00,        //   Unit (None)49 *   0x95, 0x01,        //   Report Count (1)50 *   0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)51 *   0x26, 0xFF, 0x00,  //   Logical Maximum (255)52 *   0x46, 0xFF, 0x00,  //   Physical Maximum (255)53 *   0x09, 0x30,        //   Usage (X)54 *   0x09, 0x31,        //   Usage (Y)55 *   0x09, 0x32,        //   Usage (Z)56 *   0x09, 0x35,        //   Usage (Rz)57 *   0x75, 0x08,        //   Report Size (8)58 *   0x95, 0x04,        //   Report Count (4)59 *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)60 *   0x06, 0x00, 0xFF,  //   Usage Page (Vendor Defined 0xFF00)61 *   0x09, 0x20,        //   Usage (0x20)62 *   0x09, 0x21,        //   Usage (0x21)63 *   0x09, 0x22,        //   Usage (0x22)64 *   0x09, 0x23,        //   Usage (0x23)65 *   0x09, 0x24,        //   Usage (0x24)66 *   0x09, 0x25,        //   Usage (0x25)67 *   0x09, 0x26,        //   Usage (0x26)68 *   0x09, 0x27,        //   Usage (0x27)69 *   0x09, 0x28,        //   Usage (0x28)70 *   0x09, 0x29,        //   Usage (0x29)71 *   0x09, 0x2A,        //   Usage (0x2A)72 *   0x09, 0x2B,        //   Usage (0x2B)73 *   0x95, 0x0C,        //   Report Count (12)74 *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)75 *   0x0A, 0x21, 0x26,  //   Usage (0x2621)76 *   0x95, 0x08,        //   Report Count (8)77 *   0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)78 *   0x0A, 0x21, 0x26,  //   Usage (0x2621)79 *   0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)80 *   0x26, 0xFF, 0x03,  //   Logical Maximum (1023)81 *   0x46, 0xFF, 0x03,  //   Physical Maximum (1023)82 *   0x09, 0x2C,        //   Usage (0x2C)83 *   0x09, 0x2D,        //   Usage (0x2D)84 *   0x09, 0x2E,        //   Usage (0x2E)85 *   0x09, 0x2F,        //   Usage (0x2F)86 *   0x75, 0x10,        //   Report Size (16)87 *   0x95, 0x04,        //   Report Count (4)88 *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)89 *   0xC0,              // End Collection90 */91 92#define PID0902_RDESC_ORIG_SIZE 13793 94/*95 * The fixed descriptor for 0x146b:0x090296 *97 * - map buttons according to gamepad.rst98 * - assign right stick from Z/Rz to Rx/Ry99 * - map previously unused analog trigger data to Z/RZ100 * - simplify feature and output descriptor101 */102static const __u8 pid0902_rdesc_fixed[] = {103	0x05, 0x01,        /* Usage Page (Generic Desktop Ctrls) */104	0x09, 0x05,        /* Usage (Game Pad) */105	0xA1, 0x01,        /* Collection (Application) */106	0x15, 0x00,        /*   Logical Minimum (0) */107	0x25, 0x01,        /*   Logical Maximum (1) */108	0x35, 0x00,        /*   Physical Minimum (0) */109	0x45, 0x01,        /*   Physical Maximum (1) */110	0x75, 0x01,        /*   Report Size (1) */111	0x95, 0x0D,        /*   Report Count (13) */112	0x05, 0x09,        /*   Usage Page (Button) */113	0x09, 0x05,        /*   Usage (BTN_WEST) */114	0x09, 0x01,        /*   Usage (BTN_SOUTH) */115	0x09, 0x02,        /*   Usage (BTN_EAST) */116	0x09, 0x04,        /*   Usage (BTN_NORTH) */117	0x09, 0x07,        /*   Usage (BTN_TL) */118	0x09, 0x08,        /*   Usage (BTN_TR) */119	0x09, 0x09,        /*   Usage (BTN_TL2) */120	0x09, 0x0A,        /*   Usage (BTN_TR2) */121	0x09, 0x0B,        /*   Usage (BTN_SELECT) */122	0x09, 0x0C,        /*   Usage (BTN_START) */123	0x09, 0x0E,        /*   Usage (BTN_THUMBL) */124	0x09, 0x0F,        /*   Usage (BTN_THUMBR) */125	0x09, 0x0D,        /*   Usage (BTN_MODE) */126	0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */127	0x75, 0x01,        /*   Report Size (1) */128	0x95, 0x03,        /*   Report Count (3) */129	0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */130	0x05, 0x01,        /*   Usage Page (Generic Desktop Ctrls) */131	0x25, 0x07,        /*   Logical Maximum (7) */132	0x46, 0x3B, 0x01,  /*   Physical Maximum (315) */133	0x75, 0x04,        /*   Report Size (4) */134	0x95, 0x01,        /*   Report Count (1) */135	0x65, 0x14,        /*   Unit (System: English Rotation, Length: Centimeter) */136	0x09, 0x39,        /*   Usage (Hat switch) */137	0x81, 0x42,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) */138	0x65, 0x00,        /*   Unit (None) */139	0x95, 0x01,        /*   Report Count (1) */140	0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */141	0x26, 0xFF, 0x00,  /*   Logical Maximum (255) */142	0x46, 0xFF, 0x00,  /*   Physical Maximum (255) */143	0x09, 0x30,        /*   Usage (X) */144	0x09, 0x31,        /*   Usage (Y) */145	0x09, 0x33,        /*   Usage (Rx) */146	0x09, 0x34,        /*   Usage (Ry) */147	0x75, 0x08,        /*   Report Size (8) */148	0x95, 0x04,        /*   Report Count (4) */149	0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */150	0x95, 0x0A,        /*   Report Count (10) */151	0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */152	0x05, 0x01,        /*   Usage Page (Generic Desktop Ctrls) */153	0x26, 0xFF, 0x00,  /*   Logical Maximum (255) */154	0x46, 0xFF, 0x00,  /*   Physical Maximum (255) */155	0x09, 0x32,        /*   Usage (Z) */156	0x09, 0x35,        /*   Usage (Rz) */157	0x95, 0x02,        /*   Report Count (2) */158	0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */159	0x95, 0x08,        /*   Report Count (8) */160	0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */161	0x06, 0x00, 0xFF,  /*   Usage Page (Vendor Defined 0xFF00) */162	0xB1, 0x02,        /*   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */163	0x0A, 0x21, 0x26,  /*   Usage (0x2621) */164	0x95, 0x08,        /*   Report Count (8) */165	0x91, 0x02,        /*   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */166	0x0A, 0x21, 0x26,  /*   Usage (0x2621) */167	0x95, 0x08,        /*   Report Count (8) */168	0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */169	0xC0,              /* End Collection */170};171 172#define NUM_LEDS 4173 174struct bigben_device {175	struct hid_device *hid;176	struct hid_report *report;177	spinlock_t lock;178	bool removed;179	u8 led_state;         /* LED1 = 1 .. LED4 = 8 */180	u8 right_motor_on;    /* right motor off/on 0/1 */181	u8 left_motor_force;  /* left motor force 0-255 */182	struct led_classdev *leds[NUM_LEDS];183	bool work_led;184	bool work_ff;185	struct work_struct worker;186};187 188static inline void bigben_schedule_work(struct bigben_device *bigben)189{190	unsigned long flags;191 192	spin_lock_irqsave(&bigben->lock, flags);193	if (!bigben->removed)194		schedule_work(&bigben->worker);195	spin_unlock_irqrestore(&bigben->lock, flags);196}197 198static void bigben_worker(struct work_struct *work)199{200	struct bigben_device *bigben = container_of(work,201		struct bigben_device, worker);202	struct hid_field *report_field = bigben->report->field[0];203	bool do_work_led = false;204	bool do_work_ff = false;205	u8 *buf;206	u32 len;207	unsigned long flags;208 209	buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);210	if (!buf)211		return;212 213	len = hid_report_len(bigben->report);214 215	/* LED work */216	spin_lock_irqsave(&bigben->lock, flags);217 218	if (bigben->work_led) {219		bigben->work_led = false;220		do_work_led = true;221		report_field->value[0] = 0x01; /* 1 = led message */222		report_field->value[1] = 0x08; /* reserved value, always 8 */223		report_field->value[2] = bigben->led_state;224		report_field->value[3] = 0x00; /* padding */225		report_field->value[4] = 0x00; /* padding */226		report_field->value[5] = 0x00; /* padding */227		report_field->value[6] = 0x00; /* padding */228		report_field->value[7] = 0x00; /* padding */229		hid_output_report(bigben->report, buf);230	}231 232	spin_unlock_irqrestore(&bigben->lock, flags);233 234	if (do_work_led) {235		hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,236				   bigben->report->type, HID_REQ_SET_REPORT);237	}238 239	/* FF work */240	spin_lock_irqsave(&bigben->lock, flags);241 242	if (bigben->work_ff) {243		bigben->work_ff = false;244		do_work_ff = true;245		report_field->value[0] = 0x02; /* 2 = rumble effect message */246		report_field->value[1] = 0x08; /* reserved value, always 8 */247		report_field->value[2] = bigben->right_motor_on;248		report_field->value[3] = bigben->left_motor_force;249		report_field->value[4] = 0xff; /* duration 0-254 (255 = nonstop) */250		report_field->value[5] = 0x00; /* padding */251		report_field->value[6] = 0x00; /* padding */252		report_field->value[7] = 0x00; /* padding */253		hid_output_report(bigben->report, buf);254	}255 256	spin_unlock_irqrestore(&bigben->lock, flags);257 258	if (do_work_ff) {259		hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,260				   bigben->report->type, HID_REQ_SET_REPORT);261	}262 263	kfree(buf);264}265 266static int hid_bigben_play_effect(struct input_dev *dev, void *data,267			 struct ff_effect *effect)268{269	struct hid_device *hid = input_get_drvdata(dev);270	struct bigben_device *bigben = hid_get_drvdata(hid);271	u8 right_motor_on;272	u8 left_motor_force;273	unsigned long flags;274 275	if (!bigben) {276		hid_err(hid, "no device data\n");277		return 0;278	}279 280	if (effect->type != FF_RUMBLE)281		return 0;282 283	right_motor_on   = effect->u.rumble.weak_magnitude ? 1 : 0;284	left_motor_force = effect->u.rumble.strong_magnitude / 256;285 286	if (right_motor_on != bigben->right_motor_on ||287			left_motor_force != bigben->left_motor_force) {288		spin_lock_irqsave(&bigben->lock, flags);289		bigben->right_motor_on   = right_motor_on;290		bigben->left_motor_force = left_motor_force;291		bigben->work_ff = true;292		spin_unlock_irqrestore(&bigben->lock, flags);293 294		bigben_schedule_work(bigben);295	}296 297	return 0;298}299 300static void bigben_set_led(struct led_classdev *led,301	enum led_brightness value)302{303	struct device *dev = led->dev->parent;304	struct hid_device *hid = to_hid_device(dev);305	struct bigben_device *bigben = hid_get_drvdata(hid);306	int n;307	bool work;308	unsigned long flags;309 310	if (!bigben) {311		hid_err(hid, "no device data\n");312		return;313	}314 315	for (n = 0; n < NUM_LEDS; n++) {316		if (led == bigben->leds[n]) {317			spin_lock_irqsave(&bigben->lock, flags);318			if (value == LED_OFF) {319				work = (bigben->led_state & BIT(n));320				bigben->led_state &= ~BIT(n);321			} else {322				work = !(bigben->led_state & BIT(n));323				bigben->led_state |= BIT(n);324			}325			spin_unlock_irqrestore(&bigben->lock, flags);326 327			if (work) {328				bigben->work_led = true;329				bigben_schedule_work(bigben);330			}331			return;332		}333	}334}335 336static enum led_brightness bigben_get_led(struct led_classdev *led)337{338	struct device *dev = led->dev->parent;339	struct hid_device *hid = to_hid_device(dev);340	struct bigben_device *bigben = hid_get_drvdata(hid);341	int n;342 343	if (!bigben) {344		hid_err(hid, "no device data\n");345		return LED_OFF;346	}347 348	for (n = 0; n < NUM_LEDS; n++) {349		if (led == bigben->leds[n])350			return (bigben->led_state & BIT(n)) ? LED_ON : LED_OFF;351	}352 353	return LED_OFF;354}355 356static void bigben_remove(struct hid_device *hid)357{358	struct bigben_device *bigben = hid_get_drvdata(hid);359	unsigned long flags;360 361	spin_lock_irqsave(&bigben->lock, flags);362	bigben->removed = true;363	spin_unlock_irqrestore(&bigben->lock, flags);364 365	cancel_work_sync(&bigben->worker);366	hid_hw_stop(hid);367}368 369static int bigben_probe(struct hid_device *hid,370	const struct hid_device_id *id)371{372	struct bigben_device *bigben;373	struct hid_input *hidinput;374	struct led_classdev *led;375	char *name;376	size_t name_sz;377	int n, error;378 379	bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL);380	if (!bigben)381		return -ENOMEM;382	hid_set_drvdata(hid, bigben);383	bigben->hid = hid;384	bigben->removed = false;385 386	error = hid_parse(hid);387	if (error) {388		hid_err(hid, "parse failed\n");389		return error;390	}391 392	error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);393	if (error) {394		hid_err(hid, "hw start failed\n");395		return error;396	}397 398	bigben->report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 8);399	if (!bigben->report) {400		hid_err(hid, "no output report found\n");401		error = -ENODEV;402		goto error_hw_stop;403	}404 405	if (list_empty(&hid->inputs)) {406		hid_err(hid, "no inputs found\n");407		error = -ENODEV;408		goto error_hw_stop;409	}410 411	hidinput = list_first_entry(&hid->inputs, struct hid_input, list);412	set_bit(FF_RUMBLE, hidinput->input->ffbit);413 414	INIT_WORK(&bigben->worker, bigben_worker);415	spin_lock_init(&bigben->lock);416 417	error = input_ff_create_memless(hidinput->input, NULL,418		hid_bigben_play_effect);419	if (error)420		goto error_hw_stop;421 422	name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1;423 424	for (n = 0; n < NUM_LEDS; n++) {425		led = devm_kzalloc(426			&hid->dev,427			sizeof(struct led_classdev) + name_sz,428			GFP_KERNEL429		);430		if (!led) {431			error = -ENOMEM;432			goto error_hw_stop;433		}434		name = (void *)(&led[1]);435		snprintf(name, name_sz,436			"%s:red:bigben%d",437			dev_name(&hid->dev), n + 1438		);439		led->name = name;440		led->brightness = (n == 0) ? LED_ON : LED_OFF;441		led->max_brightness = 1;442		led->brightness_get = bigben_get_led;443		led->brightness_set = bigben_set_led;444		bigben->leds[n] = led;445		error = devm_led_classdev_register(&hid->dev, led);446		if (error)447			goto error_hw_stop;448	}449 450	/* initial state: LED1 is on, no rumble effect */451	bigben->led_state = BIT(0);452	bigben->right_motor_on = 0;453	bigben->left_motor_force = 0;454	bigben->work_led = true;455	bigben->work_ff = true;456	bigben_schedule_work(bigben);457 458	hid_info(hid, "LED and force feedback support for BigBen gamepad\n");459 460	return 0;461 462error_hw_stop:463	hid_hw_stop(hid);464	return error;465}466 467static const __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc,468	unsigned int *rsize)469{470	if (*rsize == PID0902_RDESC_ORIG_SIZE) {471		*rsize = sizeof(pid0902_rdesc_fixed);472		return pid0902_rdesc_fixed;473	} else474		hid_warn(hid, "unexpected rdesc, please submit for review\n");475	return rdesc;476}477 478static const struct hid_device_id bigben_devices[] = {479	{ HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) },480	{ }481};482MODULE_DEVICE_TABLE(hid, bigben_devices);483 484static struct hid_driver bigben_driver = {485	.name = "bigben",486	.id_table = bigben_devices,487	.probe = bigben_probe,488	.report_fixup = bigben_report_fixup,489	.remove = bigben_remove,490};491module_hid_driver(bigben_driver);492 493MODULE_DESCRIPTION("LED & force feedback support for BigBen Interactive");494MODULE_LICENSE("GPL");495