162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci *  LED & force feedback support for BigBen Interactive
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  0x146b:0x0902 "Bigben Interactive Bigben Game Pad"
762306a36Sopenharmony_ci *  "Kid-friendly Wired Controller" PS3OFMINIPAD SONY
862306a36Sopenharmony_ci *  sold for use with the PS3
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *  Copyright (c) 2018 Hanno Zulla <kontakt@hanno.de>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/input.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/leds.h>
1762306a36Sopenharmony_ci#include <linux/hid.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "hid-ids.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * The original descriptor for 0x146b:0x0902
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci *   0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
2662306a36Sopenharmony_ci *   0x09, 0x05,        // Usage (Game Pad)
2762306a36Sopenharmony_ci *   0xA1, 0x01,        // Collection (Application)
2862306a36Sopenharmony_ci *   0x15, 0x00,        //   Logical Minimum (0)
2962306a36Sopenharmony_ci *   0x25, 0x01,        //   Logical Maximum (1)
3062306a36Sopenharmony_ci *   0x35, 0x00,        //   Physical Minimum (0)
3162306a36Sopenharmony_ci *   0x45, 0x01,        //   Physical Maximum (1)
3262306a36Sopenharmony_ci *   0x75, 0x01,        //   Report Size (1)
3362306a36Sopenharmony_ci *   0x95, 0x0D,        //   Report Count (13)
3462306a36Sopenharmony_ci *   0x05, 0x09,        //   Usage Page (Button)
3562306a36Sopenharmony_ci *   0x19, 0x01,        //   Usage Minimum (0x01)
3662306a36Sopenharmony_ci *   0x29, 0x0D,        //   Usage Maximum (0x0D)
3762306a36Sopenharmony_ci *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
3862306a36Sopenharmony_ci *   0x95, 0x03,        //   Report Count (3)
3962306a36Sopenharmony_ci *   0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
4062306a36Sopenharmony_ci *   0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
4162306a36Sopenharmony_ci *   0x25, 0x07,        //   Logical Maximum (7)
4262306a36Sopenharmony_ci *   0x46, 0x3B, 0x01,  //   Physical Maximum (315)
4362306a36Sopenharmony_ci *   0x75, 0x04,        //   Report Size (4)
4462306a36Sopenharmony_ci *   0x95, 0x01,        //   Report Count (1)
4562306a36Sopenharmony_ci *   0x65, 0x14,        //   Unit (System: English Rotation, Length: Centimeter)
4662306a36Sopenharmony_ci *   0x09, 0x39,        //   Usage (Hat switch)
4762306a36Sopenharmony_ci *   0x81, 0x42,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
4862306a36Sopenharmony_ci *   0x65, 0x00,        //   Unit (None)
4962306a36Sopenharmony_ci *   0x95, 0x01,        //   Report Count (1)
5062306a36Sopenharmony_ci *   0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
5162306a36Sopenharmony_ci *   0x26, 0xFF, 0x00,  //   Logical Maximum (255)
5262306a36Sopenharmony_ci *   0x46, 0xFF, 0x00,  //   Physical Maximum (255)
5362306a36Sopenharmony_ci *   0x09, 0x30,        //   Usage (X)
5462306a36Sopenharmony_ci *   0x09, 0x31,        //   Usage (Y)
5562306a36Sopenharmony_ci *   0x09, 0x32,        //   Usage (Z)
5662306a36Sopenharmony_ci *   0x09, 0x35,        //   Usage (Rz)
5762306a36Sopenharmony_ci *   0x75, 0x08,        //   Report Size (8)
5862306a36Sopenharmony_ci *   0x95, 0x04,        //   Report Count (4)
5962306a36Sopenharmony_ci *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
6062306a36Sopenharmony_ci *   0x06, 0x00, 0xFF,  //   Usage Page (Vendor Defined 0xFF00)
6162306a36Sopenharmony_ci *   0x09, 0x20,        //   Usage (0x20)
6262306a36Sopenharmony_ci *   0x09, 0x21,        //   Usage (0x21)
6362306a36Sopenharmony_ci *   0x09, 0x22,        //   Usage (0x22)
6462306a36Sopenharmony_ci *   0x09, 0x23,        //   Usage (0x23)
6562306a36Sopenharmony_ci *   0x09, 0x24,        //   Usage (0x24)
6662306a36Sopenharmony_ci *   0x09, 0x25,        //   Usage (0x25)
6762306a36Sopenharmony_ci *   0x09, 0x26,        //   Usage (0x26)
6862306a36Sopenharmony_ci *   0x09, 0x27,        //   Usage (0x27)
6962306a36Sopenharmony_ci *   0x09, 0x28,        //   Usage (0x28)
7062306a36Sopenharmony_ci *   0x09, 0x29,        //   Usage (0x29)
7162306a36Sopenharmony_ci *   0x09, 0x2A,        //   Usage (0x2A)
7262306a36Sopenharmony_ci *   0x09, 0x2B,        //   Usage (0x2B)
7362306a36Sopenharmony_ci *   0x95, 0x0C,        //   Report Count (12)
7462306a36Sopenharmony_ci *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
7562306a36Sopenharmony_ci *   0x0A, 0x21, 0x26,  //   Usage (0x2621)
7662306a36Sopenharmony_ci *   0x95, 0x08,        //   Report Count (8)
7762306a36Sopenharmony_ci *   0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
7862306a36Sopenharmony_ci *   0x0A, 0x21, 0x26,  //   Usage (0x2621)
7962306a36Sopenharmony_ci *   0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
8062306a36Sopenharmony_ci *   0x26, 0xFF, 0x03,  //   Logical Maximum (1023)
8162306a36Sopenharmony_ci *   0x46, 0xFF, 0x03,  //   Physical Maximum (1023)
8262306a36Sopenharmony_ci *   0x09, 0x2C,        //   Usage (0x2C)
8362306a36Sopenharmony_ci *   0x09, 0x2D,        //   Usage (0x2D)
8462306a36Sopenharmony_ci *   0x09, 0x2E,        //   Usage (0x2E)
8562306a36Sopenharmony_ci *   0x09, 0x2F,        //   Usage (0x2F)
8662306a36Sopenharmony_ci *   0x75, 0x10,        //   Report Size (16)
8762306a36Sopenharmony_ci *   0x95, 0x04,        //   Report Count (4)
8862306a36Sopenharmony_ci *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
8962306a36Sopenharmony_ci *   0xC0,              // End Collection
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define PID0902_RDESC_ORIG_SIZE 137
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * The fixed descriptor for 0x146b:0x0902
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * - map buttons according to gamepad.rst
9862306a36Sopenharmony_ci * - assign right stick from Z/Rz to Rx/Ry
9962306a36Sopenharmony_ci * - map previously unused analog trigger data to Z/RZ
10062306a36Sopenharmony_ci * - simplify feature and output descriptor
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic __u8 pid0902_rdesc_fixed[] = {
10362306a36Sopenharmony_ci	0x05, 0x01,        /* Usage Page (Generic Desktop Ctrls) */
10462306a36Sopenharmony_ci	0x09, 0x05,        /* Usage (Game Pad) */
10562306a36Sopenharmony_ci	0xA1, 0x01,        /* Collection (Application) */
10662306a36Sopenharmony_ci	0x15, 0x00,        /*   Logical Minimum (0) */
10762306a36Sopenharmony_ci	0x25, 0x01,        /*   Logical Maximum (1) */
10862306a36Sopenharmony_ci	0x35, 0x00,        /*   Physical Minimum (0) */
10962306a36Sopenharmony_ci	0x45, 0x01,        /*   Physical Maximum (1) */
11062306a36Sopenharmony_ci	0x75, 0x01,        /*   Report Size (1) */
11162306a36Sopenharmony_ci	0x95, 0x0D,        /*   Report Count (13) */
11262306a36Sopenharmony_ci	0x05, 0x09,        /*   Usage Page (Button) */
11362306a36Sopenharmony_ci	0x09, 0x05,        /*   Usage (BTN_WEST) */
11462306a36Sopenharmony_ci	0x09, 0x01,        /*   Usage (BTN_SOUTH) */
11562306a36Sopenharmony_ci	0x09, 0x02,        /*   Usage (BTN_EAST) */
11662306a36Sopenharmony_ci	0x09, 0x04,        /*   Usage (BTN_NORTH) */
11762306a36Sopenharmony_ci	0x09, 0x07,        /*   Usage (BTN_TL) */
11862306a36Sopenharmony_ci	0x09, 0x08,        /*   Usage (BTN_TR) */
11962306a36Sopenharmony_ci	0x09, 0x09,        /*   Usage (BTN_TL2) */
12062306a36Sopenharmony_ci	0x09, 0x0A,        /*   Usage (BTN_TR2) */
12162306a36Sopenharmony_ci	0x09, 0x0B,        /*   Usage (BTN_SELECT) */
12262306a36Sopenharmony_ci	0x09, 0x0C,        /*   Usage (BTN_START) */
12362306a36Sopenharmony_ci	0x09, 0x0E,        /*   Usage (BTN_THUMBL) */
12462306a36Sopenharmony_ci	0x09, 0x0F,        /*   Usage (BTN_THUMBR) */
12562306a36Sopenharmony_ci	0x09, 0x0D,        /*   Usage (BTN_MODE) */
12662306a36Sopenharmony_ci	0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
12762306a36Sopenharmony_ci	0x75, 0x01,        /*   Report Size (1) */
12862306a36Sopenharmony_ci	0x95, 0x03,        /*   Report Count (3) */
12962306a36Sopenharmony_ci	0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
13062306a36Sopenharmony_ci	0x05, 0x01,        /*   Usage Page (Generic Desktop Ctrls) */
13162306a36Sopenharmony_ci	0x25, 0x07,        /*   Logical Maximum (7) */
13262306a36Sopenharmony_ci	0x46, 0x3B, 0x01,  /*   Physical Maximum (315) */
13362306a36Sopenharmony_ci	0x75, 0x04,        /*   Report Size (4) */
13462306a36Sopenharmony_ci	0x95, 0x01,        /*   Report Count (1) */
13562306a36Sopenharmony_ci	0x65, 0x14,        /*   Unit (System: English Rotation, Length: Centimeter) */
13662306a36Sopenharmony_ci	0x09, 0x39,        /*   Usage (Hat switch) */
13762306a36Sopenharmony_ci	0x81, 0x42,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) */
13862306a36Sopenharmony_ci	0x65, 0x00,        /*   Unit (None) */
13962306a36Sopenharmony_ci	0x95, 0x01,        /*   Report Count (1) */
14062306a36Sopenharmony_ci	0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
14162306a36Sopenharmony_ci	0x26, 0xFF, 0x00,  /*   Logical Maximum (255) */
14262306a36Sopenharmony_ci	0x46, 0xFF, 0x00,  /*   Physical Maximum (255) */
14362306a36Sopenharmony_ci	0x09, 0x30,        /*   Usage (X) */
14462306a36Sopenharmony_ci	0x09, 0x31,        /*   Usage (Y) */
14562306a36Sopenharmony_ci	0x09, 0x33,        /*   Usage (Rx) */
14662306a36Sopenharmony_ci	0x09, 0x34,        /*   Usage (Ry) */
14762306a36Sopenharmony_ci	0x75, 0x08,        /*   Report Size (8) */
14862306a36Sopenharmony_ci	0x95, 0x04,        /*   Report Count (4) */
14962306a36Sopenharmony_ci	0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
15062306a36Sopenharmony_ci	0x95, 0x0A,        /*   Report Count (10) */
15162306a36Sopenharmony_ci	0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
15262306a36Sopenharmony_ci	0x05, 0x01,        /*   Usage Page (Generic Desktop Ctrls) */
15362306a36Sopenharmony_ci	0x26, 0xFF, 0x00,  /*   Logical Maximum (255) */
15462306a36Sopenharmony_ci	0x46, 0xFF, 0x00,  /*   Physical Maximum (255) */
15562306a36Sopenharmony_ci	0x09, 0x32,        /*   Usage (Z) */
15662306a36Sopenharmony_ci	0x09, 0x35,        /*   Usage (Rz) */
15762306a36Sopenharmony_ci	0x95, 0x02,        /*   Report Count (2) */
15862306a36Sopenharmony_ci	0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
15962306a36Sopenharmony_ci	0x95, 0x08,        /*   Report Count (8) */
16062306a36Sopenharmony_ci	0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
16162306a36Sopenharmony_ci	0x06, 0x00, 0xFF,  /*   Usage Page (Vendor Defined 0xFF00) */
16262306a36Sopenharmony_ci	0xB1, 0x02,        /*   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
16362306a36Sopenharmony_ci	0x0A, 0x21, 0x26,  /*   Usage (0x2621) */
16462306a36Sopenharmony_ci	0x95, 0x08,        /*   Report Count (8) */
16562306a36Sopenharmony_ci	0x91, 0x02,        /*   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
16662306a36Sopenharmony_ci	0x0A, 0x21, 0x26,  /*   Usage (0x2621) */
16762306a36Sopenharmony_ci	0x95, 0x08,        /*   Report Count (8) */
16862306a36Sopenharmony_ci	0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
16962306a36Sopenharmony_ci	0xC0,              /* End Collection */
17062306a36Sopenharmony_ci};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci#define NUM_LEDS 4
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistruct bigben_device {
17562306a36Sopenharmony_ci	struct hid_device *hid;
17662306a36Sopenharmony_ci	struct hid_report *report;
17762306a36Sopenharmony_ci	spinlock_t lock;
17862306a36Sopenharmony_ci	bool removed;
17962306a36Sopenharmony_ci	u8 led_state;         /* LED1 = 1 .. LED4 = 8 */
18062306a36Sopenharmony_ci	u8 right_motor_on;    /* right motor off/on 0/1 */
18162306a36Sopenharmony_ci	u8 left_motor_force;  /* left motor force 0-255 */
18262306a36Sopenharmony_ci	struct led_classdev *leds[NUM_LEDS];
18362306a36Sopenharmony_ci	bool work_led;
18462306a36Sopenharmony_ci	bool work_ff;
18562306a36Sopenharmony_ci	struct work_struct worker;
18662306a36Sopenharmony_ci};
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic inline void bigben_schedule_work(struct bigben_device *bigben)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	unsigned long flags;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	spin_lock_irqsave(&bigben->lock, flags);
19362306a36Sopenharmony_ci	if (!bigben->removed)
19462306a36Sopenharmony_ci		schedule_work(&bigben->worker);
19562306a36Sopenharmony_ci	spin_unlock_irqrestore(&bigben->lock, flags);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void bigben_worker(struct work_struct *work)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct bigben_device *bigben = container_of(work,
20162306a36Sopenharmony_ci		struct bigben_device, worker);
20262306a36Sopenharmony_ci	struct hid_field *report_field = bigben->report->field[0];
20362306a36Sopenharmony_ci	bool do_work_led = false;
20462306a36Sopenharmony_ci	bool do_work_ff = false;
20562306a36Sopenharmony_ci	u8 *buf;
20662306a36Sopenharmony_ci	u32 len;
20762306a36Sopenharmony_ci	unsigned long flags;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);
21062306a36Sopenharmony_ci	if (!buf)
21162306a36Sopenharmony_ci		return;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	len = hid_report_len(bigben->report);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* LED work */
21662306a36Sopenharmony_ci	spin_lock_irqsave(&bigben->lock, flags);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (bigben->work_led) {
21962306a36Sopenharmony_ci		bigben->work_led = false;
22062306a36Sopenharmony_ci		do_work_led = true;
22162306a36Sopenharmony_ci		report_field->value[0] = 0x01; /* 1 = led message */
22262306a36Sopenharmony_ci		report_field->value[1] = 0x08; /* reserved value, always 8 */
22362306a36Sopenharmony_ci		report_field->value[2] = bigben->led_state;
22462306a36Sopenharmony_ci		report_field->value[3] = 0x00; /* padding */
22562306a36Sopenharmony_ci		report_field->value[4] = 0x00; /* padding */
22662306a36Sopenharmony_ci		report_field->value[5] = 0x00; /* padding */
22762306a36Sopenharmony_ci		report_field->value[6] = 0x00; /* padding */
22862306a36Sopenharmony_ci		report_field->value[7] = 0x00; /* padding */
22962306a36Sopenharmony_ci		hid_output_report(bigben->report, buf);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	spin_unlock_irqrestore(&bigben->lock, flags);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (do_work_led) {
23562306a36Sopenharmony_ci		hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
23662306a36Sopenharmony_ci				   bigben->report->type, HID_REQ_SET_REPORT);
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* FF work */
24062306a36Sopenharmony_ci	spin_lock_irqsave(&bigben->lock, flags);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (bigben->work_ff) {
24362306a36Sopenharmony_ci		bigben->work_ff = false;
24462306a36Sopenharmony_ci		do_work_ff = true;
24562306a36Sopenharmony_ci		report_field->value[0] = 0x02; /* 2 = rumble effect message */
24662306a36Sopenharmony_ci		report_field->value[1] = 0x08; /* reserved value, always 8 */
24762306a36Sopenharmony_ci		report_field->value[2] = bigben->right_motor_on;
24862306a36Sopenharmony_ci		report_field->value[3] = bigben->left_motor_force;
24962306a36Sopenharmony_ci		report_field->value[4] = 0xff; /* duration 0-254 (255 = nonstop) */
25062306a36Sopenharmony_ci		report_field->value[5] = 0x00; /* padding */
25162306a36Sopenharmony_ci		report_field->value[6] = 0x00; /* padding */
25262306a36Sopenharmony_ci		report_field->value[7] = 0x00; /* padding */
25362306a36Sopenharmony_ci		hid_output_report(bigben->report, buf);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	spin_unlock_irqrestore(&bigben->lock, flags);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (do_work_ff) {
25962306a36Sopenharmony_ci		hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
26062306a36Sopenharmony_ci				   bigben->report->type, HID_REQ_SET_REPORT);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	kfree(buf);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int hid_bigben_play_effect(struct input_dev *dev, void *data,
26762306a36Sopenharmony_ci			 struct ff_effect *effect)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct hid_device *hid = input_get_drvdata(dev);
27062306a36Sopenharmony_ci	struct bigben_device *bigben = hid_get_drvdata(hid);
27162306a36Sopenharmony_ci	u8 right_motor_on;
27262306a36Sopenharmony_ci	u8 left_motor_force;
27362306a36Sopenharmony_ci	unsigned long flags;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (!bigben) {
27662306a36Sopenharmony_ci		hid_err(hid, "no device data\n");
27762306a36Sopenharmony_ci		return 0;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (effect->type != FF_RUMBLE)
28162306a36Sopenharmony_ci		return 0;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	right_motor_on   = effect->u.rumble.weak_magnitude ? 1 : 0;
28462306a36Sopenharmony_ci	left_motor_force = effect->u.rumble.strong_magnitude / 256;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (right_motor_on != bigben->right_motor_on ||
28762306a36Sopenharmony_ci			left_motor_force != bigben->left_motor_force) {
28862306a36Sopenharmony_ci		spin_lock_irqsave(&bigben->lock, flags);
28962306a36Sopenharmony_ci		bigben->right_motor_on   = right_motor_on;
29062306a36Sopenharmony_ci		bigben->left_motor_force = left_motor_force;
29162306a36Sopenharmony_ci		bigben->work_ff = true;
29262306a36Sopenharmony_ci		spin_unlock_irqrestore(&bigben->lock, flags);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		bigben_schedule_work(bigben);
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return 0;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic void bigben_set_led(struct led_classdev *led,
30162306a36Sopenharmony_ci	enum led_brightness value)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct device *dev = led->dev->parent;
30462306a36Sopenharmony_ci	struct hid_device *hid = to_hid_device(dev);
30562306a36Sopenharmony_ci	struct bigben_device *bigben = hid_get_drvdata(hid);
30662306a36Sopenharmony_ci	int n;
30762306a36Sopenharmony_ci	bool work;
30862306a36Sopenharmony_ci	unsigned long flags;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (!bigben) {
31162306a36Sopenharmony_ci		hid_err(hid, "no device data\n");
31262306a36Sopenharmony_ci		return;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	for (n = 0; n < NUM_LEDS; n++) {
31662306a36Sopenharmony_ci		if (led == bigben->leds[n]) {
31762306a36Sopenharmony_ci			spin_lock_irqsave(&bigben->lock, flags);
31862306a36Sopenharmony_ci			if (value == LED_OFF) {
31962306a36Sopenharmony_ci				work = (bigben->led_state & BIT(n));
32062306a36Sopenharmony_ci				bigben->led_state &= ~BIT(n);
32162306a36Sopenharmony_ci			} else {
32262306a36Sopenharmony_ci				work = !(bigben->led_state & BIT(n));
32362306a36Sopenharmony_ci				bigben->led_state |= BIT(n);
32462306a36Sopenharmony_ci			}
32562306a36Sopenharmony_ci			spin_unlock_irqrestore(&bigben->lock, flags);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci			if (work) {
32862306a36Sopenharmony_ci				bigben->work_led = true;
32962306a36Sopenharmony_ci				bigben_schedule_work(bigben);
33062306a36Sopenharmony_ci			}
33162306a36Sopenharmony_ci			return;
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic enum led_brightness bigben_get_led(struct led_classdev *led)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct device *dev = led->dev->parent;
33962306a36Sopenharmony_ci	struct hid_device *hid = to_hid_device(dev);
34062306a36Sopenharmony_ci	struct bigben_device *bigben = hid_get_drvdata(hid);
34162306a36Sopenharmony_ci	int n;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (!bigben) {
34462306a36Sopenharmony_ci		hid_err(hid, "no device data\n");
34562306a36Sopenharmony_ci		return LED_OFF;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	for (n = 0; n < NUM_LEDS; n++) {
34962306a36Sopenharmony_ci		if (led == bigben->leds[n])
35062306a36Sopenharmony_ci			return (bigben->led_state & BIT(n)) ? LED_ON : LED_OFF;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return LED_OFF;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic void bigben_remove(struct hid_device *hid)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct bigben_device *bigben = hid_get_drvdata(hid);
35962306a36Sopenharmony_ci	unsigned long flags;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	spin_lock_irqsave(&bigben->lock, flags);
36262306a36Sopenharmony_ci	bigben->removed = true;
36362306a36Sopenharmony_ci	spin_unlock_irqrestore(&bigben->lock, flags);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	cancel_work_sync(&bigben->worker);
36662306a36Sopenharmony_ci	hid_hw_stop(hid);
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int bigben_probe(struct hid_device *hid,
37062306a36Sopenharmony_ci	const struct hid_device_id *id)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct bigben_device *bigben;
37362306a36Sopenharmony_ci	struct hid_input *hidinput;
37462306a36Sopenharmony_ci	struct led_classdev *led;
37562306a36Sopenharmony_ci	char *name;
37662306a36Sopenharmony_ci	size_t name_sz;
37762306a36Sopenharmony_ci	int n, error;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL);
38062306a36Sopenharmony_ci	if (!bigben)
38162306a36Sopenharmony_ci		return -ENOMEM;
38262306a36Sopenharmony_ci	hid_set_drvdata(hid, bigben);
38362306a36Sopenharmony_ci	bigben->hid = hid;
38462306a36Sopenharmony_ci	bigben->removed = false;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	error = hid_parse(hid);
38762306a36Sopenharmony_ci	if (error) {
38862306a36Sopenharmony_ci		hid_err(hid, "parse failed\n");
38962306a36Sopenharmony_ci		return error;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
39362306a36Sopenharmony_ci	if (error) {
39462306a36Sopenharmony_ci		hid_err(hid, "hw start failed\n");
39562306a36Sopenharmony_ci		return error;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	bigben->report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 8);
39962306a36Sopenharmony_ci	if (!bigben->report) {
40062306a36Sopenharmony_ci		hid_err(hid, "no output report found\n");
40162306a36Sopenharmony_ci		error = -ENODEV;
40262306a36Sopenharmony_ci		goto error_hw_stop;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (list_empty(&hid->inputs)) {
40662306a36Sopenharmony_ci		hid_err(hid, "no inputs found\n");
40762306a36Sopenharmony_ci		error = -ENODEV;
40862306a36Sopenharmony_ci		goto error_hw_stop;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
41262306a36Sopenharmony_ci	set_bit(FF_RUMBLE, hidinput->input->ffbit);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	INIT_WORK(&bigben->worker, bigben_worker);
41562306a36Sopenharmony_ci	spin_lock_init(&bigben->lock);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	error = input_ff_create_memless(hidinput->input, NULL,
41862306a36Sopenharmony_ci		hid_bigben_play_effect);
41962306a36Sopenharmony_ci	if (error)
42062306a36Sopenharmony_ci		goto error_hw_stop;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	for (n = 0; n < NUM_LEDS; n++) {
42562306a36Sopenharmony_ci		led = devm_kzalloc(
42662306a36Sopenharmony_ci			&hid->dev,
42762306a36Sopenharmony_ci			sizeof(struct led_classdev) + name_sz,
42862306a36Sopenharmony_ci			GFP_KERNEL
42962306a36Sopenharmony_ci		);
43062306a36Sopenharmony_ci		if (!led) {
43162306a36Sopenharmony_ci			error = -ENOMEM;
43262306a36Sopenharmony_ci			goto error_hw_stop;
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci		name = (void *)(&led[1]);
43562306a36Sopenharmony_ci		snprintf(name, name_sz,
43662306a36Sopenharmony_ci			"%s:red:bigben%d",
43762306a36Sopenharmony_ci			dev_name(&hid->dev), n + 1
43862306a36Sopenharmony_ci		);
43962306a36Sopenharmony_ci		led->name = name;
44062306a36Sopenharmony_ci		led->brightness = (n == 0) ? LED_ON : LED_OFF;
44162306a36Sopenharmony_ci		led->max_brightness = 1;
44262306a36Sopenharmony_ci		led->brightness_get = bigben_get_led;
44362306a36Sopenharmony_ci		led->brightness_set = bigben_set_led;
44462306a36Sopenharmony_ci		bigben->leds[n] = led;
44562306a36Sopenharmony_ci		error = devm_led_classdev_register(&hid->dev, led);
44662306a36Sopenharmony_ci		if (error)
44762306a36Sopenharmony_ci			goto error_hw_stop;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* initial state: LED1 is on, no rumble effect */
45162306a36Sopenharmony_ci	bigben->led_state = BIT(0);
45262306a36Sopenharmony_ci	bigben->right_motor_on = 0;
45362306a36Sopenharmony_ci	bigben->left_motor_force = 0;
45462306a36Sopenharmony_ci	bigben->work_led = true;
45562306a36Sopenharmony_ci	bigben->work_ff = true;
45662306a36Sopenharmony_ci	bigben_schedule_work(bigben);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	hid_info(hid, "LED and force feedback support for BigBen gamepad\n");
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	return 0;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cierror_hw_stop:
46362306a36Sopenharmony_ci	hid_hw_stop(hid);
46462306a36Sopenharmony_ci	return error;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc,
46862306a36Sopenharmony_ci	unsigned int *rsize)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	if (*rsize == PID0902_RDESC_ORIG_SIZE) {
47162306a36Sopenharmony_ci		rdesc = pid0902_rdesc_fixed;
47262306a36Sopenharmony_ci		*rsize = sizeof(pid0902_rdesc_fixed);
47362306a36Sopenharmony_ci	} else
47462306a36Sopenharmony_ci		hid_warn(hid, "unexpected rdesc, please submit for review\n");
47562306a36Sopenharmony_ci	return rdesc;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic const struct hid_device_id bigben_devices[] = {
47962306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) },
48062306a36Sopenharmony_ci	{ }
48162306a36Sopenharmony_ci};
48262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, bigben_devices);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic struct hid_driver bigben_driver = {
48562306a36Sopenharmony_ci	.name = "bigben",
48662306a36Sopenharmony_ci	.id_table = bigben_devices,
48762306a36Sopenharmony_ci	.probe = bigben_probe,
48862306a36Sopenharmony_ci	.report_fixup = bigben_report_fixup,
48962306a36Sopenharmony_ci	.remove = bigben_remove,
49062306a36Sopenharmony_ci};
49162306a36Sopenharmony_cimodule_hid_driver(bigben_driver);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
494