HID: driver for PS2/3 Buzz controllers
This patch adds support for PS2/3 Buzz controllers into hid-sony It has been tested on Debian 7 with kernel version 3.10.0-rc2. Unfortunately I can't test the patch with a regular six-axis controller myself. Signed-off-by: Colin Leitner <colin.leitner@gmail.com> Cc: Jiri Kosina <jkosina@suse.cz> Cc: linux-input@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
		
					parent
					
						
							
								f755407dd1
							
						
					
				
			
			
				commit
				
					
						f04d51404f
					
				
			
		
					 4 changed files with 312 additions and 11 deletions
				
			
		| 
						 | 
					@ -594,10 +594,15 @@ config HID_SAMSUNG
 | 
				
			||||||
	Support for Samsung InfraRed remote control or keyboards.
 | 
						Support for Samsung InfraRed remote control or keyboards.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config HID_SONY
 | 
					config HID_SONY
 | 
				
			||||||
	tristate "Sony PS3 controller"
 | 
						tristate "Sony PS2/3 accessories"
 | 
				
			||||||
	depends on USB_HID
 | 
						depends on USB_HID
 | 
				
			||||||
 | 
						select NEW_LEDS
 | 
				
			||||||
 | 
						select LEDS_CLASS
 | 
				
			||||||
	---help---
 | 
						---help---
 | 
				
			||||||
	Support for Sony PS3 6-axis controllers.
 | 
						Support for
 | 
				
			||||||
 | 
						  
 | 
				
			||||||
 | 
						  * Sony PS3 6-axis controllers
 | 
				
			||||||
 | 
						  * Buzz controllers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE.
 | 
						Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1680,6 +1680,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
 | 
				
			||||||
	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
 | 
				
			||||||
	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
 | 
				
			||||||
	{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
 | 
				
			||||||
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
 | 
				
			||||||
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
 | 
				
			||||||
	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
 | 
						{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
 | 
				
			||||||
	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
 | 
				
			||||||
	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -734,6 +734,8 @@
 | 
				
			||||||
#define USB_DEVICE_ID_SONY_PS3_BDREMOTE		0x0306
 | 
					#define USB_DEVICE_ID_SONY_PS3_BDREMOTE		0x0306
 | 
				
			||||||
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER	0x0268
 | 
					#define USB_DEVICE_ID_SONY_PS3_CONTROLLER	0x0268
 | 
				
			||||||
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER	0x042f
 | 
					#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER	0x042f
 | 
				
			||||||
 | 
					#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER		0x0002
 | 
				
			||||||
 | 
					#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER	0x1000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define USB_VENDOR_ID_SOUNDGRAPH	0x15c2
 | 
					#define USB_VENDOR_ID_SOUNDGRAPH	0x15c2
 | 
				
			||||||
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST	0x0034
 | 
					#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST	0x0034
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@
 | 
				
			||||||
 *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
 | 
					 *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
 | 
				
			||||||
 *  Copyright (c) 2008 Jiri Slaby
 | 
					 *  Copyright (c) 2008 Jiri Slaby
 | 
				
			||||||
 *  Copyright (c) 2006-2008 Jiri Kosina
 | 
					 *  Copyright (c) 2006-2008 Jiri Kosina
 | 
				
			||||||
 | 
					 *  Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,7 @@
 | 
				
			||||||
#define VAIO_RDESC_CONSTANT     (1 << 0)
 | 
					#define VAIO_RDESC_CONSTANT     (1 << 0)
 | 
				
			||||||
#define SIXAXIS_CONTROLLER_USB  (1 << 1)
 | 
					#define SIXAXIS_CONTROLLER_USB  (1 << 1)
 | 
				
			||||||
#define SIXAXIS_CONTROLLER_BT   (1 << 2)
 | 
					#define SIXAXIS_CONTROLLER_BT   (1 << 2)
 | 
				
			||||||
 | 
					#define BUZZ_CONTROLLER         (1 << 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const u8 sixaxis_rdesc_fixup[] = {
 | 
					static const u8 sixaxis_rdesc_fixup[] = {
 | 
				
			||||||
	0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C,
 | 
						0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C,
 | 
				
			||||||
| 
						 | 
					@ -55,8 +57,56 @@ static const u8 sixaxis_rdesc_fixup2[] = {
 | 
				
			||||||
	0xb1, 0x02, 0xc0, 0xc0,
 | 
						0xb1, 0x02, 0xc0, 0xc0,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const unsigned int buzz_keymap[] = {
 | 
				
			||||||
 | 
						/* The controller has 4 remote buzzers, each with one LED and 5
 | 
				
			||||||
 | 
						 * buttons.
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * We use the mapping chosen by the controller, which is:
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Key          Offset
 | 
				
			||||||
 | 
						 * -------------------
 | 
				
			||||||
 | 
						 * Buzz              1
 | 
				
			||||||
 | 
						 * Blue              5
 | 
				
			||||||
 | 
						 * Orange            4
 | 
				
			||||||
 | 
						 * Green             3
 | 
				
			||||||
 | 
						 * Yellow            2
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * So, for example, the orange button on the third buzzer is mapped to
 | 
				
			||||||
 | 
						 * BTN_TRIGGER_HAPPY14
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						[ 1] = BTN_TRIGGER_HAPPY1,
 | 
				
			||||||
 | 
						[ 2] = BTN_TRIGGER_HAPPY2,
 | 
				
			||||||
 | 
						[ 3] = BTN_TRIGGER_HAPPY3,
 | 
				
			||||||
 | 
						[ 4] = BTN_TRIGGER_HAPPY4,
 | 
				
			||||||
 | 
						[ 5] = BTN_TRIGGER_HAPPY5,
 | 
				
			||||||
 | 
						[ 6] = BTN_TRIGGER_HAPPY6,
 | 
				
			||||||
 | 
						[ 7] = BTN_TRIGGER_HAPPY7,
 | 
				
			||||||
 | 
						[ 8] = BTN_TRIGGER_HAPPY8,
 | 
				
			||||||
 | 
						[ 9] = BTN_TRIGGER_HAPPY9,
 | 
				
			||||||
 | 
						[10] = BTN_TRIGGER_HAPPY10,
 | 
				
			||||||
 | 
						[11] = BTN_TRIGGER_HAPPY11,
 | 
				
			||||||
 | 
						[12] = BTN_TRIGGER_HAPPY12,
 | 
				
			||||||
 | 
						[13] = BTN_TRIGGER_HAPPY13,
 | 
				
			||||||
 | 
						[14] = BTN_TRIGGER_HAPPY14,
 | 
				
			||||||
 | 
						[15] = BTN_TRIGGER_HAPPY15,
 | 
				
			||||||
 | 
						[16] = BTN_TRIGGER_HAPPY16,
 | 
				
			||||||
 | 
						[17] = BTN_TRIGGER_HAPPY17,
 | 
				
			||||||
 | 
						[18] = BTN_TRIGGER_HAPPY18,
 | 
				
			||||||
 | 
						[19] = BTN_TRIGGER_HAPPY19,
 | 
				
			||||||
 | 
						[20] = BTN_TRIGGER_HAPPY20,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct sony_sc {
 | 
					struct sony_sc {
 | 
				
			||||||
	unsigned long quirks;
 | 
						unsigned long quirks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void *extra;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct buzz_extra {
 | 
				
			||||||
 | 
					#ifdef CONFIG_LEDS_CLASS
 | 
				
			||||||
 | 
						int led_state;
 | 
				
			||||||
 | 
						struct led_classdev *leds[4];
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Sony Vaio VGX has wrongly mouse pointer declared as constant */
 | 
					/* Sony Vaio VGX has wrongly mouse pointer declared as constant */
 | 
				
			||||||
| 
						 | 
					@ -117,6 +167,38 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
 | 
				
			||||||
 | 
								struct hid_field *field, struct hid_usage *usage,
 | 
				
			||||||
 | 
								unsigned long **bit, int *max)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sony_sc *sc = hid_get_drvdata(hdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sc->quirks & BUZZ_CONTROLLER) {
 | 
				
			||||||
 | 
							unsigned int key = usage->hid & HID_USAGE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (usage->collection_index) {
 | 
				
			||||||
 | 
							case 1:
 | 
				
			||||||
 | 
								if (key >= ARRAY_SIZE(buzz_keymap))
 | 
				
			||||||
 | 
									return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								key = buzz_keymap[key];
 | 
				
			||||||
 | 
								if (!key)
 | 
				
			||||||
 | 
									return -1;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
 | 
					 * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
 | 
				
			||||||
 * like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
 | 
					 * like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
 | 
				
			||||||
| 
						 | 
					@ -192,11 +274,201 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
 | 
				
			||||||
	return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
 | 
						return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_LEDS_CLASS
 | 
				
			||||||
 | 
					static void buzz_set_leds(struct hid_device *hdev, int leds)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct list_head *report_list =
 | 
				
			||||||
 | 
							&hdev->report_enum[HID_OUTPUT_REPORT].report_list;
 | 
				
			||||||
 | 
						struct hid_report *report = list_entry(report_list->next,
 | 
				
			||||||
 | 
							struct hid_report, list);
 | 
				
			||||||
 | 
						__s32 *value = report->field[0]->value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						value[0] = 0x00;
 | 
				
			||||||
 | 
						value[1] = (leds & 1) ? 0xff : 0x00;
 | 
				
			||||||
 | 
						value[2] = (leds & 2) ? 0xff : 0x00;
 | 
				
			||||||
 | 
						value[3] = (leds & 4) ? 0xff : 0x00;
 | 
				
			||||||
 | 
						value[4] = (leds & 8) ? 0xff : 0x00;
 | 
				
			||||||
 | 
						value[5] = 0x00;
 | 
				
			||||||
 | 
						value[6] = 0x00;
 | 
				
			||||||
 | 
						hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void buzz_led_set_brightness(struct led_classdev *led,
 | 
				
			||||||
 | 
									    enum led_brightness value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct device *dev = led->dev->parent;
 | 
				
			||||||
 | 
						struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 | 
				
			||||||
 | 
						struct sony_sc *drv_data;
 | 
				
			||||||
 | 
						struct buzz_extra *buzz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drv_data = hid_get_drvdata(hdev);
 | 
				
			||||||
 | 
						if (!drv_data || !drv_data->extra) {
 | 
				
			||||||
 | 
							hid_err(hdev, "No device data\n");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						buzz = drv_data->extra;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (n = 0; n < 4; n++) {
 | 
				
			||||||
 | 
							if (led == buzz->leds[n]) {
 | 
				
			||||||
 | 
								int on = !! (buzz->led_state & (1 << n));
 | 
				
			||||||
 | 
								if (value == LED_OFF && on) {
 | 
				
			||||||
 | 
									buzz->led_state &= ~(1 << n);
 | 
				
			||||||
 | 
									buzz_set_leds(hdev, buzz->led_state);
 | 
				
			||||||
 | 
								} else if (value != LED_OFF && !on) {
 | 
				
			||||||
 | 
									buzz->led_state |= (1 << n);
 | 
				
			||||||
 | 
									buzz_set_leds(hdev, buzz->led_state);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static enum led_brightness buzz_led_get_brightness(struct led_classdev *led)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct device *dev = led->dev->parent;
 | 
				
			||||||
 | 
						struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 | 
				
			||||||
 | 
						struct sony_sc *drv_data;
 | 
				
			||||||
 | 
						struct buzz_extra *buzz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int n;
 | 
				
			||||||
 | 
						int on = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drv_data = hid_get_drvdata(hdev);
 | 
				
			||||||
 | 
						if (!drv_data || !drv_data->extra) {
 | 
				
			||||||
 | 
							hid_err(hdev, "No device data\n");
 | 
				
			||||||
 | 
							return LED_OFF;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						buzz = drv_data->extra;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (n = 0; n < 4; n++) {
 | 
				
			||||||
 | 
							if (led == buzz->leds[n]) {
 | 
				
			||||||
 | 
								on = !! (buzz->led_state & (1 << n));
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return on ? LED_FULL : LED_OFF;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int buzz_init(struct hid_device *hdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sony_sc *drv_data;
 | 
				
			||||||
 | 
						struct buzz_extra *buzz;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drv_data = hid_get_drvdata(hdev);
 | 
				
			||||||
 | 
						BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buzz = kzalloc(sizeof(*buzz), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!buzz) {
 | 
				
			||||||
 | 
							hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						drv_data->extra = buzz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Clear LEDs as we have no way of reading their initial state. This is
 | 
				
			||||||
 | 
						 * only relevant if the driver is loaded after somebody actively set the
 | 
				
			||||||
 | 
						 * LEDs to on */
 | 
				
			||||||
 | 
						buzz_set_leds(hdev, 0x00);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_LEDS_CLASS
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int n;
 | 
				
			||||||
 | 
							struct led_classdev *led;
 | 
				
			||||||
 | 
							size_t name_sz;
 | 
				
			||||||
 | 
							char *name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							name_sz = strlen(dev_name(&hdev->dev)) + strlen("::buzz#") + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (n = 0; n < 4; n++) {
 | 
				
			||||||
 | 
								led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
 | 
				
			||||||
 | 
								if (!led) {
 | 
				
			||||||
 | 
									hid_err(hdev, "Couldn't allocate memory for LED %d\n", n);
 | 
				
			||||||
 | 
									goto error_leds;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								name = (void *)(&led[1]);
 | 
				
			||||||
 | 
								snprintf(name, name_sz, "%s::buzz%d", dev_name(&hdev->dev), n + 1);
 | 
				
			||||||
 | 
								led->name = name;
 | 
				
			||||||
 | 
								led->brightness = 0;
 | 
				
			||||||
 | 
								led->max_brightness = 1;
 | 
				
			||||||
 | 
								led->brightness_get = buzz_led_get_brightness;
 | 
				
			||||||
 | 
								led->brightness_set = buzz_led_set_brightness;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (led_classdev_register(&hdev->dev, led)) {
 | 
				
			||||||
 | 
									hid_err(hdev, "Failed to register LED %d\n", n);
 | 
				
			||||||
 | 
									kfree(led);
 | 
				
			||||||
 | 
									goto error_leds;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								buzz->leds[n] = led;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_LEDS_CLASS
 | 
				
			||||||
 | 
					error_leds:
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int n;
 | 
				
			||||||
 | 
							struct led_classdev *led;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (n = 0; n < 4; n++) {
 | 
				
			||||||
 | 
								led = buzz->leds[n];
 | 
				
			||||||
 | 
								buzz->leds[n] = NULL;
 | 
				
			||||||
 | 
								if (!led)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								led_classdev_unregister(led);
 | 
				
			||||||
 | 
								kfree(led);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kfree(drv_data->extra);
 | 
				
			||||||
 | 
						drv_data->extra = NULL;
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void buzz_remove(struct hid_device *hdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sony_sc *drv_data;
 | 
				
			||||||
 | 
						struct buzz_extra *buzz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drv_data = hid_get_drvdata(hdev);
 | 
				
			||||||
 | 
						BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buzz = drv_data->extra;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					#ifdef CONFIG_LEDS_CLASS
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int n;
 | 
				
			||||||
 | 
							struct led_classdev *led;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (n = 0; n < 4; n++) {
 | 
				
			||||||
 | 
								led = buzz->leds[n];
 | 
				
			||||||
 | 
								buzz->leds[n] = NULL;
 | 
				
			||||||
 | 
								if (!led)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								led_classdev_unregister(led);
 | 
				
			||||||
 | 
								kfree(led);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kfree(drv_data->extra);
 | 
				
			||||||
 | 
						drv_data->extra = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 | 
					static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
	unsigned long quirks = id->driver_data;
 | 
						unsigned long quirks = id->driver_data;
 | 
				
			||||||
	struct sony_sc *sc;
 | 
						struct sony_sc *sc;
 | 
				
			||||||
 | 
						unsigned int connect_mask = HID_CONNECT_DEFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sc = kzalloc(sizeof(*sc), GFP_KERNEL);
 | 
						sc = kzalloc(sizeof(*sc), GFP_KERNEL);
 | 
				
			||||||
	if (sc == NULL) {
 | 
						if (sc == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -213,8 +485,14 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 | 
				
			||||||
		goto err_free;
 | 
							goto err_free;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
 | 
						if (sc->quirks & VAIO_RDESC_CONSTANT)
 | 
				
			||||||
			HID_CONNECT_HIDDEV_FORCE);
 | 
							connect_mask |= HID_CONNECT_HIDDEV_FORCE;
 | 
				
			||||||
 | 
						else if (sc->quirks & SIXAXIS_CONTROLLER_USB)
 | 
				
			||||||
 | 
							connect_mask |= HID_CONNECT_HIDDEV_FORCE;
 | 
				
			||||||
 | 
						else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 | 
				
			||||||
 | 
							connect_mask |= HID_CONNECT_HIDDEV_FORCE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = hid_hw_start(hdev, connect_mask);
 | 
				
			||||||
	if (ret) {
 | 
						if (ret) {
 | 
				
			||||||
		hid_err(hdev, "hw start failed\n");
 | 
							hid_err(hdev, "hw start failed\n");
 | 
				
			||||||
		goto err_free;
 | 
							goto err_free;
 | 
				
			||||||
| 
						 | 
					@ -226,6 +504,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 | 
						else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
 | 
				
			||||||
		ret = sixaxis_set_operational_bt(hdev);
 | 
							ret = sixaxis_set_operational_bt(hdev);
 | 
				
			||||||
 | 
						else if (sc->quirks & BUZZ_CONTROLLER)
 | 
				
			||||||
 | 
							ret = buzz_init(hdev);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		ret = 0;
 | 
							ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -242,8 +522,13 @@ err_free:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sony_remove(struct hid_device *hdev)
 | 
					static void sony_remove(struct hid_device *hdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct sony_sc *sc = hid_get_drvdata(hdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sc->quirks & BUZZ_CONTROLLER)
 | 
				
			||||||
 | 
							buzz_remove(hdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hid_hw_stop(hdev);
 | 
						hid_hw_stop(hdev);
 | 
				
			||||||
	kfree(hid_get_drvdata(hdev));
 | 
						kfree(sc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct hid_device_id sony_devices[] = {
 | 
					static const struct hid_device_id sony_devices[] = {
 | 
				
			||||||
| 
						 | 
					@ -257,6 +542,12 @@ static const struct hid_device_id sony_devices[] = {
 | 
				
			||||||
		.driver_data = VAIO_RDESC_CONSTANT },
 | 
							.driver_data = VAIO_RDESC_CONSTANT },
 | 
				
			||||||
	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE),
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE),
 | 
				
			||||||
		.driver_data = VAIO_RDESC_CONSTANT },
 | 
							.driver_data = VAIO_RDESC_CONSTANT },
 | 
				
			||||||
 | 
						/* Wired Buzz Controller. Reported as Sony Hub from its USB ID and as
 | 
				
			||||||
 | 
						 * Logitech joystick from the device descriptor. */
 | 
				
			||||||
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER),
 | 
				
			||||||
 | 
							.driver_data = BUZZ_CONTROLLER },
 | 
				
			||||||
 | 
						{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER),
 | 
				
			||||||
 | 
							.driver_data = BUZZ_CONTROLLER },
 | 
				
			||||||
	{ }
 | 
						{ }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
MODULE_DEVICE_TABLE(hid, sony_devices);
 | 
					MODULE_DEVICE_TABLE(hid, sony_devices);
 | 
				
			||||||
| 
						 | 
					@ -264,6 +555,7 @@ MODULE_DEVICE_TABLE(hid, sony_devices);
 | 
				
			||||||
static struct hid_driver sony_driver = {
 | 
					static struct hid_driver sony_driver = {
 | 
				
			||||||
	.name          = "sony",
 | 
						.name          = "sony",
 | 
				
			||||||
	.id_table      = sony_devices,
 | 
						.id_table      = sony_devices,
 | 
				
			||||||
 | 
						.input_mapping = sony_mapping,
 | 
				
			||||||
	.probe         = sony_probe,
 | 
						.probe         = sony_probe,
 | 
				
			||||||
	.remove        = sony_remove,
 | 
						.remove        = sony_remove,
 | 
				
			||||||
	.report_fixup  = sony_report_fixup,
 | 
						.report_fixup  = sony_report_fixup,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue