Blame view

drivers/hid/hid-zpff.c 3.9 KB
bb3caf7f4   Anssi Hannula   Input: add force ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   *  Force feedback support for Zeroplus based devices
   *
   *  Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
   */
  
  /*
   * 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
   */
987fbc1f7   Jiri Slaby   HID: move zeroplu...
22
  #include <linux/hid.h>
bb3caf7f4   Anssi Hannula   Input: add force ...
23
24
  #include <linux/input.h>
  #include <linux/usb.h>
987fbc1f7   Jiri Slaby   HID: move zeroplu...
25
26
27
28
  
  #include "hid-ids.h"
  
  #include "usbhid/usbhid.h"
bb3caf7f4   Anssi Hannula   Input: add force ...
29
30
31
32
  
  struct zpff_device {
  	struct hid_report *report;
  };
987fbc1f7   Jiri Slaby   HID: move zeroplu...
33
  static int zpff_play(struct input_dev *dev, void *data,
bb3caf7f4   Anssi Hannula   Input: add force ...
34
35
  			 struct ff_effect *effect)
  {
e07129858   Dmitry Torokhov   HID: switch to us...
36
  	struct hid_device *hid = input_get_drvdata(dev);
bb3caf7f4   Anssi Hannula   Input: add force ...
37
38
39
40
41
42
43
44
45
46
47
  	struct zpff_device *zpff = data;
  	int left, right;
  
  	/*
  	 * The following is specified the other way around in the Zeroplus
  	 * datasheet but the order below is correct for the XFX Executioner;
  	 * however it is possible that the XFX Executioner is an exception
  	 */
  
  	left = effect->u.rumble.strong_magnitude;
  	right = effect->u.rumble.weak_magnitude;
58037eb96   Jiri Kosina   HID: make debuggi...
48
49
  	dbg_hid("called with 0x%04x 0x%04x
  ", left, right);
bb3caf7f4   Anssi Hannula   Input: add force ...
50
51
52
53
54
55
  
  	left = left * 0x7f / 0xffff;
  	right = right * 0x7f / 0xffff;
  
  	zpff->report->field[2]->value[0] = left;
  	zpff->report->field[3]->value[0] = right;
58037eb96   Jiri Kosina   HID: make debuggi...
56
57
  	dbg_hid("running with 0x%02x 0x%02x
  ", left, right);
4916b3a57   Jiri Kosina   [PATCH] Generic H...
58
  	usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
bb3caf7f4   Anssi Hannula   Input: add force ...
59
60
61
  
  	return 0;
  }
987fbc1f7   Jiri Slaby   HID: move zeroplu...
62
  static int zpff_init(struct hid_device *hid)
bb3caf7f4   Anssi Hannula   Input: add force ...
63
64
65
66
67
68
69
70
71
72
73
  {
  	struct zpff_device *zpff;
  	struct hid_report *report;
  	struct hid_input *hidinput = list_entry(hid->inputs.next,
  						struct hid_input, list);
  	struct list_head *report_list =
  			&hid->report_enum[HID_OUTPUT_REPORT].report_list;
  	struct input_dev *dev = hidinput->input;
  	int error;
  
  	if (list_empty(report_list)) {
795750197   Jiri Slaby   HID: convert to d...
74
75
  		dev_err(&hid->dev, "no output report found
  ");
bb3caf7f4   Anssi Hannula   Input: add force ...
76
77
78
79
80
81
  		return -ENODEV;
  	}
  
  	report = list_entry(report_list->next, struct hid_report, list);
  
  	if (report->maxfield < 4) {
795750197   Jiri Slaby   HID: convert to d...
82
83
  		dev_err(&hid->dev, "not enough fields in report
  ");
bb3caf7f4   Anssi Hannula   Input: add force ...
84
85
86
87
88
89
90
91
  		return -ENODEV;
  	}
  
  	zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
  	if (!zpff)
  		return -ENOMEM;
  
  	set_bit(FF_RUMBLE, dev->ffbit);
987fbc1f7   Jiri Slaby   HID: move zeroplu...
92
  	error = input_ff_create_memless(dev, zpff, zpff_play);
bb3caf7f4   Anssi Hannula   Input: add force ...
93
94
95
96
97
98
99
100
101
102
  	if (error) {
  		kfree(zpff);
  		return error;
  	}
  
  	zpff->report = report;
  	zpff->report->field[0]->value[0] = 0x00;
  	zpff->report->field[1]->value[0] = 0x02;
  	zpff->report->field[2]->value[0] = 0x00;
  	zpff->report->field[3]->value[0] = 0x00;
4916b3a57   Jiri Kosina   [PATCH] Generic H...
103
  	usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
bb3caf7f4   Anssi Hannula   Input: add force ...
104

795750197   Jiri Slaby   HID: convert to d...
105
  	dev_info(&hid->dev, "force feedback for Zeroplus based devices by "
bb3caf7f4   Anssi Hannula   Input: add force ...
106
107
108
109
110
  	       "Anssi Hannula <anssi.hannula@gmail.com>
  ");
  
  	return 0;
  }
987fbc1f7   Jiri Slaby   HID: move zeroplu...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  
  static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  	int ret;
  
  	ret = hid_parse(hdev);
  	if (ret) {
  		dev_err(&hdev->dev, "parse failed
  ");
  		goto err;
  	}
  
  	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
  	if (ret) {
  		dev_err(&hdev->dev, "hw start failed
  ");
  		goto err;
  	}
  
  	zpff_init(hdev);
  
  	return 0;
  err:
  	return ret;
  }
  
  static const struct hid_device_id zp_devices[] = {
  	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
  	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
  	{ }
  };
  MODULE_DEVICE_TABLE(hid, zp_devices);
  
  static struct hid_driver zp_driver = {
  	.name = "zeroplus",
  	.id_table = zp_devices,
  	.probe = zp_probe,
  };
  
  static int zp_init(void)
  {
  	return hid_register_driver(&zp_driver);
  }
  
  static void zp_exit(void)
  {
  	hid_unregister_driver(&zp_driver);
  }
  
  module_init(zp_init);
  module_exit(zp_exit);
  MODULE_LICENSE("GPL");
  
  HID_COMPAT_LOAD_DRIVER(zeroplus);