hid-pl.c 5.9 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11
 *  Force feedback support for PantherLord/GreenAsia based devices
 *
 *  The devices are distributed under various names and the same USB device ID
 *  can be used in both adapters and actual game controllers.
 *
 *  0810:0001 "Twin USB Joystick"
 *   - tested with PantherLord USB/PS2 2in1 Adapter
 *   - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
 *
 *  0e8f:0003 "GreenAsia Inc.    USB Joystick     "
12
 *   - tested with Knig Gaming gamepad
13
 *
14 15 16 17
 *  0e8f:0003 "GASIA USB Gamepad"
 *   - another version of the Knig gamepad
 *
 *  Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com>
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */


/* #define DEBUG */

#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg)

#include <linux/input.h>
#include <linux/usb.h>
#include <linux/hid.h>
J
Jiri Slaby 已提交
44 45 46 47 48

#include "hid-ids.h"

#ifdef CONFIG_PANTHERLORD_FF
#include "usbhid/usbhid.h"
49 50 51

struct plff_device {
	struct hid_report *report;
52 53
	s32 *strong;
	s32 *weak;
54 55 56 57 58
};

static int hid_plff_play(struct input_dev *dev, void *data,
			 struct ff_effect *effect)
{
59
	struct hid_device *hid = input_get_drvdata(dev);
60 61 62 63 64 65 66 67 68 69
	struct plff_device *plff = data;
	int left, right;

	left = effect->u.rumble.strong_magnitude;
	right = effect->u.rumble.weak_magnitude;
	debug("called with 0x%04x 0x%04x", left, right);

	left = left * 0x7f / 0xffff;
	right = right * 0x7f / 0xffff;

70 71
	*plff->strong = left;
	*plff->weak = right;
72 73 74 75 76 77
	debug("running with 0x%02x 0x%02x", left, right);
	usbhid_submit_report(hid, plff->report, USB_DIR_OUT);

	return 0;
}

J
Jiri Slaby 已提交
78
static int plff_init(struct hid_device *hid)
79 80 81 82 83 84 85 86 87
{
	struct plff_device *plff;
	struct hid_report *report;
	struct hid_input *hidinput;
	struct list_head *report_list =
			&hid->report_enum[HID_OUTPUT_REPORT].report_list;
	struct list_head *report_ptr = report_list;
	struct input_dev *dev;
	int error;
88 89
	s32 *strong;
	s32 *weak;
90

91 92 93
	/* The device contains one output report per physical device, all
	   containing 1 field, which contains 4 ff00.0002 usages and 4 16bit
	   absolute values.
94

95
	   The input reports also contain a field which contains
96
	   8 ff00.0001 usages and 8 boolean values. Their meaning is
97 98 99 100 101 102
	   currently unknown.
	   
	   A version of the 0e8f:0003 exists that has all the values in
	   separate fields and misses the extra input field, thus resembling
	   Zeroplus (hid-zpff) devices.
	*/
103 104

	if (list_empty(report_list)) {
J
Jiri Slaby 已提交
105
		dev_err(&hid->dev, "no output reports found\n");
106 107 108 109 110 111 112 113
		return -ENODEV;
	}

	list_for_each_entry(hidinput, &hid->inputs, list) {

		report_ptr = report_ptr->next;

		if (report_ptr == report_list) {
J
Jiri Slaby 已提交
114 115
			dev_err(&hid->dev, "required output report is "
					"missing\n");
116 117 118 119 120
			return -ENODEV;
		}

		report = list_entry(report_ptr, struct hid_report, list);
		if (report->maxfield < 1) {
J
Jiri Slaby 已提交
121
			dev_err(&hid->dev, "no fields in the report\n");
122 123 124
			return -ENODEV;
		}

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
		if (report->field[0]->report_count >= 4) {
			report->field[0]->value[0] = 0x00;
			report->field[0]->value[1] = 0x00;
			strong = &report->field[0]->value[2];
			weak = &report->field[0]->value[3];
			debug("detected single-field device");
		} else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 &&
				report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) {
			report->field[0]->value[0] = 0x00;
			report->field[1]->value[0] = 0x00;
			strong = &report->field[2]->value[0];
			weak = &report->field[3]->value[0];
			debug("detected 4-field device");
		} else {
			dev_err(&hid->dev, "not enough fields or values\n");
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
			return -ENODEV;
		}

		plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL);
		if (!plff)
			return -ENOMEM;

		dev = hidinput->input;

		set_bit(FF_RUMBLE, dev->ffbit);

		error = input_ff_create_memless(dev, plff, hid_plff_play);
		if (error) {
			kfree(plff);
			return error;
		}

		plff->report = report;
158 159 160 161 162
		plff->strong = strong;
		plff->weak = weak;

		*strong = 0x00;
		*weak = 0x00;
163 164 165
		usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
	}

J
Jiri Slaby 已提交
166
	dev_info(&hid->dev, "Force feedback for PantherLord/GreenAsia "
167
	       "devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
168 169 170

	return 0;
}
J
Jiri Slaby 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
#else
static inline int plff_init(struct hid_device *hid)
{
	return 0;
}
#endif

static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
	int ret;

	if (id->driver_data)
		hdev->quirks |= HID_QUIRK_MULTI_INPUT;

	ret = hid_parse(hdev);
	if (ret) {
		dev_err(&hdev->dev, "parse failed\n");
		goto err;
	}

	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
	if (ret) {
		dev_err(&hdev->dev, "hw start failed\n");
		goto err;
	}

	plff_init(hdev);

	return 0;
err:
	return ret;
}

static const struct hid_device_id pl_devices[] = {
	{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR),
		.driver_data = 1 }, /* Twin USB Joystick */
207 208
	{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR),
		.driver_data = 1 }, /* Twin USB Joystick */
209
	{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), },
J
Jiri Slaby 已提交
210 211 212 213 214 215 216 217 218 219
	{ }
};
MODULE_DEVICE_TABLE(hid, pl_devices);

static struct hid_driver pl_driver = {
	.name = "pantherlord",
	.id_table = pl_devices,
	.probe = pl_probe,
};

220
static int __init pl_init(void)
J
Jiri Slaby 已提交
221 222 223 224
{
	return hid_register_driver(&pl_driver);
}

225
static void __exit pl_exit(void)
J
Jiri Slaby 已提交
226 227 228 229 230 231 232
{
	hid_unregister_driver(&pl_driver);
}

module_init(pl_init);
module_exit(pl_exit);
MODULE_LICENSE("GPL");