brintos

brintos / linux-shallow public Read only

0
0
Text · 19.5 KiB · 7e83fee Raw
669 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  HID driver for Steelseries devices4 *5 *  Copyright (c) 2013 Simon Wood6 *  Copyright (c) 2023 Bastien Nocera7 */8 9/*10 */11 12#include <linux/device.h>13#include <linux/hid.h>14#include <linux/module.h>15#include <linux/usb.h>16#include <linux/leds.h>17 18#include "hid-ids.h"19 20#define STEELSERIES_SRWS1		BIT(0)21#define STEELSERIES_ARCTIS_1		BIT(1)22 23struct steelseries_device {24	struct hid_device *hdev;25	unsigned long quirks;26 27	struct delayed_work battery_work;28	spinlock_t lock;29	bool removed;30 31	struct power_supply_desc battery_desc;32	struct power_supply *battery;33	uint8_t battery_capacity;34	bool headset_connected;35};36 37#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \38    (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))39#define SRWS1_NUMBER_LEDS 1540struct steelseries_srws1_data {41	__u16 led_state;42	/* the last element is used for setting all leds simultaneously */43	struct led_classdev *led[SRWS1_NUMBER_LEDS + 1];44};45#endif46 47/* Fixed report descriptor for Steelseries SRW-S1 wheel controller48 *49 * The original descriptor hides the sensitivity and assists dials50 * a custom vendor usage page. This inserts a patch to make them51 * appear in the 'Generic Desktop' usage.52 */53 54static const __u8 steelseries_srws1_rdesc_fixed[] = {550x05, 0x01,         /*  Usage Page (Desktop)                */560x09, 0x08,         /*  Usage (MultiAxis), Changed          */570xA1, 0x01,         /*  Collection (Application),           */580xA1, 0x02,         /*      Collection (Logical),           */590x95, 0x01,         /*          Report Count (1),           */600x05, 0x01,         /* Changed  Usage Page (Desktop),       */610x09, 0x30,         /* Changed  Usage (X),                  */620x16, 0xF8, 0xF8,   /*          Logical Minimum (-1800),    */630x26, 0x08, 0x07,   /*          Logical Maximum (1800),     */640x65, 0x14,         /*          Unit (Degrees),             */650x55, 0x0F,         /*          Unit Exponent (15),         */660x75, 0x10,         /*          Report Size (16),           */670x81, 0x02,         /*          Input (Variable),           */680x09, 0x31,         /* Changed  Usage (Y),                  */690x15, 0x00,         /*          Logical Minimum (0),        */700x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */710x75, 0x0C,         /*          Report Size (12),           */720x81, 0x02,         /*          Input (Variable),           */730x09, 0x32,         /* Changed  Usage (Z),                  */740x15, 0x00,         /*          Logical Minimum (0),        */750x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */760x75, 0x0C,         /*          Report Size (12),           */770x81, 0x02,         /*          Input (Variable),           */780x05, 0x01,         /*          Usage Page (Desktop),       */790x09, 0x39,         /*          Usage (Hat Switch),         */800x25, 0x07,         /*          Logical Maximum (7),        */810x35, 0x00,         /*          Physical Minimum (0),       */820x46, 0x3B, 0x01,   /*          Physical Maximum (315),     */830x65, 0x14,         /*          Unit (Degrees),             */840x75, 0x04,         /*          Report Size (4),            */850x95, 0x01,         /*          Report Count (1),           */860x81, 0x02,         /*          Input (Variable),           */870x25, 0x01,         /*          Logical Maximum (1),        */880x45, 0x01,         /*          Physical Maximum (1),       */890x65, 0x00,         /*          Unit,                       */900x75, 0x01,         /*          Report Size (1),            */910x95, 0x03,         /*          Report Count (3),           */920x81, 0x01,         /*          Input (Constant),           */930x05, 0x09,         /*          Usage Page (Button),        */940x19, 0x01,         /*          Usage Minimum (01h),        */950x29, 0x11,         /*          Usage Maximum (11h),        */960x95, 0x11,         /*          Report Count (17),          */970x81, 0x02,         /*          Input (Variable),           */98                    /*   ---- Dial patch starts here ----   */990x05, 0x01,         /*          Usage Page (Desktop),       */1000x09, 0x33,         /*          Usage (RX),                 */1010x75, 0x04,         /*          Report Size (4),            */1020x95, 0x02,         /*          Report Count (2),           */1030x15, 0x00,         /*          Logical Minimum (0),        */1040x25, 0x0b,         /*          Logical Maximum (b),        */1050x81, 0x02,         /*          Input (Variable),           */1060x09, 0x35,         /*          Usage (RZ),                 */1070x75, 0x04,         /*          Report Size (4),            */1080x95, 0x01,         /*          Report Count (1),           */1090x25, 0x03,         /*          Logical Maximum (3),        */1100x81, 0x02,         /*          Input (Variable),           */111                    /*    ---- Dial patch ends here ----    */1120x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */1130x09, 0x01,         /*          Usage (01h),                */1140x75, 0x04,         /* Changed  Report Size (4),            */1150x95, 0x0D,         /* Changed  Report Count (13),          */1160x81, 0x02,         /*          Input (Variable),           */1170xC0,               /*      End Collection,                 */1180xA1, 0x02,         /*      Collection (Logical),           */1190x09, 0x02,         /*          Usage (02h),                */1200x75, 0x08,         /*          Report Size (8),            */1210x95, 0x10,         /*          Report Count (16),          */1220x91, 0x02,         /*          Output (Variable),          */1230xC0,               /*      End Collection,                 */1240xC0                /*  End Collection                      */125};126 127#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \128    (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))129static void steelseries_srws1_set_leds(struct hid_device *hdev, __u16 leds)130{131	struct list_head *report_list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;132	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);133	__s32 *value = report->field[0]->value;134 135	value[0] = 0x40;136	value[1] = leds & 0xFF;137	value[2] = leds >> 8;138	value[3] = 0x00;139	value[4] = 0x00;140	value[5] = 0x00;141	value[6] = 0x00;142	value[7] = 0x00;143	value[8] = 0x00;144	value[9] = 0x00;145	value[10] = 0x00;146	value[11] = 0x00;147	value[12] = 0x00;148	value[13] = 0x00;149	value[14] = 0x00;150	value[15] = 0x00;151 152	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);153 154	/* Note: LED change does not show on device until the device is read/polled */155}156 157static void steelseries_srws1_led_all_set_brightness(struct led_classdev *led_cdev,158			enum led_brightness value)159{160	struct device *dev = led_cdev->dev->parent;161	struct hid_device *hid = to_hid_device(dev);162	struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid);163 164	if (!drv_data) {165		hid_err(hid, "Device data not found.");166		return;167	}168 169	if (value == LED_OFF)170		drv_data->led_state = 0;171	else172		drv_data->led_state = (1 << (SRWS1_NUMBER_LEDS + 1)) - 1;173 174	steelseries_srws1_set_leds(hid, drv_data->led_state);175}176 177static enum led_brightness steelseries_srws1_led_all_get_brightness(struct led_classdev *led_cdev)178{179	struct device *dev = led_cdev->dev->parent;180	struct hid_device *hid = to_hid_device(dev);181	struct steelseries_srws1_data *drv_data;182 183	drv_data = hid_get_drvdata(hid);184 185	if (!drv_data) {186		hid_err(hid, "Device data not found.");187		return LED_OFF;188	}189 190	return (drv_data->led_state >> SRWS1_NUMBER_LEDS) ? LED_FULL : LED_OFF;191}192 193static void steelseries_srws1_led_set_brightness(struct led_classdev *led_cdev,194			enum led_brightness value)195{196	struct device *dev = led_cdev->dev->parent;197	struct hid_device *hid = to_hid_device(dev);198	struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid);199	int i, state = 0;200 201	if (!drv_data) {202		hid_err(hid, "Device data not found.");203		return;204	}205 206	for (i = 0; i < SRWS1_NUMBER_LEDS; i++) {207		if (led_cdev != drv_data->led[i])208			continue;209 210		state = (drv_data->led_state >> i) & 1;211		if (value == LED_OFF && state) {212			drv_data->led_state &= ~(1 << i);213			steelseries_srws1_set_leds(hid, drv_data->led_state);214		} else if (value != LED_OFF && !state) {215			drv_data->led_state |= 1 << i;216			steelseries_srws1_set_leds(hid, drv_data->led_state);217		}218		break;219	}220}221 222static enum led_brightness steelseries_srws1_led_get_brightness(struct led_classdev *led_cdev)223{224	struct device *dev = led_cdev->dev->parent;225	struct hid_device *hid = to_hid_device(dev);226	struct steelseries_srws1_data *drv_data;227	int i, value = 0;228 229	drv_data = hid_get_drvdata(hid);230 231	if (!drv_data) {232		hid_err(hid, "Device data not found.");233		return LED_OFF;234	}235 236	for (i = 0; i < SRWS1_NUMBER_LEDS; i++)237		if (led_cdev == drv_data->led[i]) {238			value = (drv_data->led_state >> i) & 1;239			break;240		}241 242	return value ? LED_FULL : LED_OFF;243}244 245static int steelseries_srws1_probe(struct hid_device *hdev,246		const struct hid_device_id *id)247{248	int ret, i;249	struct led_classdev *led;250	size_t name_sz;251	char *name;252 253	struct steelseries_srws1_data *drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);254 255	if (drv_data == NULL) {256		hid_err(hdev, "can't alloc SRW-S1 memory\n");257		return -ENOMEM;258	}259 260	hid_set_drvdata(hdev, drv_data);261 262	ret = hid_parse(hdev);263	if (ret) {264		hid_err(hdev, "parse failed\n");265		goto err_free;266	}267 268	if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) {269		ret = -ENODEV;270		goto err_free;271	}272 273	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);274	if (ret) {275		hid_err(hdev, "hw start failed\n");276		goto err_free;277	}278 279	/* register led subsystem */280	drv_data->led_state = 0;281	for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++)282		drv_data->led[i] = NULL;283 284	steelseries_srws1_set_leds(hdev, 0);285 286	name_sz = strlen(hdev->uniq) + 16;287 288	/* 'ALL', for setting all LEDs simultaneously */289	led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);290	if (!led) {291		hid_err(hdev, "can't allocate memory for LED ALL\n");292		goto err_led;293	}294 295	name = (void *)(&led[1]);296	snprintf(name, name_sz, "SRWS1::%s::RPMALL", hdev->uniq);297	led->name = name;298	led->brightness = 0;299	led->max_brightness = 1;300	led->brightness_get = steelseries_srws1_led_all_get_brightness;301	led->brightness_set = steelseries_srws1_led_all_set_brightness;302 303	drv_data->led[SRWS1_NUMBER_LEDS] = led;304	ret = led_classdev_register(&hdev->dev, led);305	if (ret)306		goto err_led;307 308	/* Each individual LED */309	for (i = 0; i < SRWS1_NUMBER_LEDS; i++) {310		led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);311		if (!led) {312			hid_err(hdev, "can't allocate memory for LED %d\n", i);313			goto err_led;314		}315 316		name = (void *)(&led[1]);317		snprintf(name, name_sz, "SRWS1::%s::RPM%d", hdev->uniq, i+1);318		led->name = name;319		led->brightness = 0;320		led->max_brightness = 1;321		led->brightness_get = steelseries_srws1_led_get_brightness;322		led->brightness_set = steelseries_srws1_led_set_brightness;323 324		drv_data->led[i] = led;325		ret = led_classdev_register(&hdev->dev, led);326 327		if (ret) {328			hid_err(hdev, "failed to register LED %d. Aborting.\n", i);329err_led:330			/* Deregister all LEDs (if any) */331			for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++) {332				led = drv_data->led[i];333				drv_data->led[i] = NULL;334				if (!led)335					continue;336				led_classdev_unregister(led);337				kfree(led);338			}339			goto out;	/* but let the driver continue without LEDs */340		}341	}342out:343	return 0;344err_free:345	kfree(drv_data);346	return ret;347}348 349static void steelseries_srws1_remove(struct hid_device *hdev)350{351	int i;352	struct led_classdev *led;353 354	struct steelseries_srws1_data *drv_data = hid_get_drvdata(hdev);355 356	if (drv_data) {357		/* Deregister LEDs (if any) */358		for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++) {359			led = drv_data->led[i];360			drv_data->led[i] = NULL;361			if (!led)362				continue;363			led_classdev_unregister(led);364			kfree(led);365		}366 367	}368 369	hid_hw_stop(hdev);370	kfree(drv_data);371	return;372}373#endif374 375#define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS	3000376 377#define ARCTIS_1_BATTERY_RESPONSE_LEN		8378static const char arctis_1_battery_request[] = { 0x06, 0x12 };379 380static int steelseries_headset_arctis_1_fetch_battery(struct hid_device *hdev)381{382	u8 *write_buf;383	int ret;384 385	/* Request battery information */386	write_buf = kmemdup(arctis_1_battery_request, sizeof(arctis_1_battery_request), GFP_KERNEL);387	if (!write_buf)388		return -ENOMEM;389 390	ret = hid_hw_raw_request(hdev, arctis_1_battery_request[0],391				 write_buf, sizeof(arctis_1_battery_request),392				 HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);393	if (ret < (int)sizeof(arctis_1_battery_request)) {394		hid_err(hdev, "hid_hw_raw_request() failed with %d\n", ret);395		ret = -ENODATA;396	}397	kfree(write_buf);398	return ret;399}400 401static void steelseries_headset_fetch_battery(struct hid_device *hdev)402{403	struct steelseries_device *sd = hid_get_drvdata(hdev);404	int ret = 0;405 406	if (sd->quirks & STEELSERIES_ARCTIS_1)407		ret = steelseries_headset_arctis_1_fetch_battery(hdev);408 409	if (ret < 0)410		hid_dbg(hdev,411			"Battery query failed (err: %d)\n", ret);412}413 414static void steelseries_headset_battery_timer_tick(struct work_struct *work)415{416	struct steelseries_device *sd = container_of(work,417		struct steelseries_device, battery_work.work);418	struct hid_device *hdev = sd->hdev;419 420	steelseries_headset_fetch_battery(hdev);421}422 423static int steelseries_headset_battery_get_property(struct power_supply *psy,424				enum power_supply_property psp,425				union power_supply_propval *val)426{427	struct steelseries_device *sd = power_supply_get_drvdata(psy);428	int ret = 0;429 430	switch (psp) {431	case POWER_SUPPLY_PROP_PRESENT:432		val->intval = 1;433		break;434	case POWER_SUPPLY_PROP_STATUS:435		val->intval = sd->headset_connected ?436			POWER_SUPPLY_STATUS_DISCHARGING :437			POWER_SUPPLY_STATUS_UNKNOWN;438		break;439	case POWER_SUPPLY_PROP_SCOPE:440		val->intval = POWER_SUPPLY_SCOPE_DEVICE;441		break;442	case POWER_SUPPLY_PROP_CAPACITY:443		val->intval = sd->battery_capacity;444		break;445	default:446		ret = -EINVAL;447		break;448	}449	return ret;450}451 452static void453steelseries_headset_set_wireless_status(struct hid_device *hdev,454					bool connected)455{456	struct usb_interface *intf;457 458	if (!hid_is_usb(hdev))459		return;460 461	intf = to_usb_interface(hdev->dev.parent);462	usb_set_wireless_status(intf, connected ?463				USB_WIRELESS_STATUS_CONNECTED :464				USB_WIRELESS_STATUS_DISCONNECTED);465}466 467static enum power_supply_property steelseries_headset_battery_props[] = {468	POWER_SUPPLY_PROP_PRESENT,469	POWER_SUPPLY_PROP_STATUS,470	POWER_SUPPLY_PROP_SCOPE,471	POWER_SUPPLY_PROP_CAPACITY,472};473 474static int steelseries_headset_battery_register(struct steelseries_device *sd)475{476	static atomic_t battery_no = ATOMIC_INIT(0);477	struct power_supply_config battery_cfg = { .drv_data = sd, };478	unsigned long n;479	int ret;480 481	sd->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;482	sd->battery_desc.properties = steelseries_headset_battery_props;483	sd->battery_desc.num_properties = ARRAY_SIZE(steelseries_headset_battery_props);484	sd->battery_desc.get_property = steelseries_headset_battery_get_property;485	sd->battery_desc.use_for_apm = 0;486	n = atomic_inc_return(&battery_no) - 1;487	sd->battery_desc.name = devm_kasprintf(&sd->hdev->dev, GFP_KERNEL,488						    "steelseries_headset_battery_%ld", n);489	if (!sd->battery_desc.name)490		return -ENOMEM;491 492	/* avoid the warning of 0% battery while waiting for the first info */493	steelseries_headset_set_wireless_status(sd->hdev, false);494	sd->battery_capacity = 100;495 496	sd->battery = devm_power_supply_register(&sd->hdev->dev,497			&sd->battery_desc, &battery_cfg);498	if (IS_ERR(sd->battery)) {499		ret = PTR_ERR(sd->battery);500		hid_err(sd->hdev,501				"%s:power_supply_register failed with error %d\n",502				__func__, ret);503		return ret;504	}505	power_supply_powers(sd->battery, &sd->hdev->dev);506 507	INIT_DELAYED_WORK(&sd->battery_work, steelseries_headset_battery_timer_tick);508	steelseries_headset_fetch_battery(sd->hdev);509 510	return 0;511}512 513static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id *id)514{515	struct steelseries_device *sd;516	int ret;517 518	sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);519	if (!sd)520		return -ENOMEM;521	hid_set_drvdata(hdev, sd);522	sd->hdev = hdev;523	sd->quirks = id->driver_data;524 525	if (sd->quirks & STEELSERIES_SRWS1) {526#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \527    (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))528		return steelseries_srws1_probe(hdev, id);529#else530		return -ENODEV;531#endif532	}533 534	ret = hid_parse(hdev);535	if (ret)536		return ret;537 538	spin_lock_init(&sd->lock);539 540	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);541	if (ret)542		return ret;543 544	if (steelseries_headset_battery_register(sd) < 0)545		hid_err(sd->hdev,546			"Failed to register battery for headset\n");547 548	return ret;549}550 551static void steelseries_remove(struct hid_device *hdev)552{553	struct steelseries_device *sd = hid_get_drvdata(hdev);554	unsigned long flags;555 556	if (sd->quirks & STEELSERIES_SRWS1) {557#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \558    (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))559		steelseries_srws1_remove(hdev);560#endif561		return;562	}563 564	spin_lock_irqsave(&sd->lock, flags);565	sd->removed = true;566	spin_unlock_irqrestore(&sd->lock, flags);567 568	cancel_delayed_work_sync(&sd->battery_work);569 570	hid_hw_stop(hdev);571}572 573static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev,574		__u8 *rdesc, unsigned int *rsize)575{576	if (hdev->vendor != USB_VENDOR_ID_STEELSERIES ||577	    hdev->product != USB_DEVICE_ID_STEELSERIES_SRWS1)578		return rdesc;579 580	if (*rsize >= 115 && rdesc[11] == 0x02 && rdesc[13] == 0xc8581			&& rdesc[29] == 0xbb && rdesc[40] == 0xc5) {582		hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n");583		*rsize = sizeof(steelseries_srws1_rdesc_fixed);584		return steelseries_srws1_rdesc_fixed;585	}586	return rdesc;587}588 589static int steelseries_headset_raw_event(struct hid_device *hdev,590					struct hid_report *report, u8 *read_buf,591					int size)592{593	struct steelseries_device *sd = hid_get_drvdata(hdev);594	int capacity = sd->battery_capacity;595	bool connected = sd->headset_connected;596	unsigned long flags;597 598	/* Not a headset */599	if (sd->quirks & STEELSERIES_SRWS1)600		return 0;601 602	if (sd->quirks & STEELSERIES_ARCTIS_1) {603		hid_dbg(sd->hdev,604			"Parsing raw event for Arctis 1 headset (%*ph)\n", size, read_buf);605		if (size < ARCTIS_1_BATTERY_RESPONSE_LEN ||606		    memcmp (read_buf, arctis_1_battery_request, sizeof(arctis_1_battery_request)))607			return 0;608		if (read_buf[2] == 0x01) {609			connected = false;610			capacity = 100;611		} else {612			connected = true;613			capacity = read_buf[3];614		}615	}616 617	if (connected != sd->headset_connected) {618		hid_dbg(sd->hdev,619			"Connected status changed from %sconnected to %sconnected\n",620			sd->headset_connected ? "" : "not ",621			connected ? "" : "not ");622		sd->headset_connected = connected;623		steelseries_headset_set_wireless_status(hdev, connected);624	}625 626	if (capacity != sd->battery_capacity) {627		hid_dbg(sd->hdev,628			"Battery capacity changed from %d%% to %d%%\n",629			sd->battery_capacity, capacity);630		sd->battery_capacity = capacity;631		power_supply_changed(sd->battery);632	}633 634	spin_lock_irqsave(&sd->lock, flags);635	if (!sd->removed)636		schedule_delayed_work(&sd->battery_work,637				msecs_to_jiffies(STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS));638	spin_unlock_irqrestore(&sd->lock, flags);639 640	return 0;641}642 643static const struct hid_device_id steelseries_devices[] = {644	{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1),645	  .driver_data = STEELSERIES_SRWS1 },646 647	{ /* SteelSeries Arctis 1 Wireless for XBox */648	  HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 0x12b6),649	.driver_data = STEELSERIES_ARCTIS_1 },650 651	{ }652};653MODULE_DEVICE_TABLE(hid, steelseries_devices);654 655static struct hid_driver steelseries_driver = {656	.name = "steelseries",657	.id_table = steelseries_devices,658	.probe = steelseries_probe,659	.remove = steelseries_remove,660	.report_fixup = steelseries_srws1_report_fixup,661	.raw_event = steelseries_headset_raw_event,662};663 664module_hid_driver(steelseries_driver);665MODULE_DESCRIPTION("HID driver for Steelseries devices");666MODULE_LICENSE("GPL");667MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");668MODULE_AUTHOR("Simon Wood <simon@mungewell.org>");669