-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathjoystick.c
351 lines (273 loc) · 9.76 KB
/
joystick.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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
165
166
167
168
169
170
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*
https://github.com/torvalds/linux/blob/master/drivers/hid/usbhid/usbmouse.c
https://github.com/mavam/ml-driver
https://github.com/smalinux/mouse-driver
https://elixir.bootlin.com/linux/latest/source/include/linux/usb.h
https://elixir.bootlin.com/linux/latest/source/drivers/usb/usb-skeleton.c
*/
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* USB HIDBP Mouse support
*/
/*
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <[email protected]>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
/*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2001 Vojtech Pavlik
* Copyright (c) 2006 Jiri Kosina
*/
/*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/input.h>
/* API provided by hid-core.c for USB HID drivers */
void usbhid_init_reports(struct hid_device *hid);
struct usb_interface *usbhid_find_interface(int minor);
/* iofl flags */
#define HID_CTRL_RUNNING 1
#define HID_OUT_RUNNING 2
#define HID_IN_RUNNING 3
#define HID_RESET_PENDING 4
#define HID_SUSPENDED 5
#define HID_CLEAR_HALT 6
#define HID_DISCONNECTED 7
#define HID_STARTED 8
#define HID_KEYS_PRESSED 10
#define HID_NO_BANDWIDTH 11
#define HID_RESUME_RUNNING 12
/*
* The device is opened, meaning there is a client that is interested
* in data coming from the device.
*/
#define HID_OPENED 13
/*
* We are polling input endpoint by [re]submitting IN URB, because
* either HID device is opened or ALWAYS POLL quirk is set for the
* device.
*/
#define HID_IN_POLLING 14
/*
* USB-specific HID struct, to be pointed to
* from struct hid_device->driver_data
*/
struct usbhid_device {
struct hid_device *hid; /* pointer to corresponding HID dev */
struct usb_interface *intf; /* USB interface */
int ifnum; /* USB interface number */
unsigned int bufsize; /* URB buffer size */
struct urb *urbin; /* Input URB */
char *inbuf; /* Input buffer */
dma_addr_t inbuf_dma; /* Input buffer dma */
struct urb *urbctrl; /* Control URB */
struct usb_ctrlrequest *cr; /* Control request struct */
struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
char *ctrlbuf; /* Control buffer */
dma_addr_t ctrlbuf_dma; /* Control buffer dma */
unsigned long last_ctrl; /* record of last output for timeouts */
struct urb *urbout; /* Output URB */
struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
unsigned char outhead, outtail; /* Output pipe fifo head & tail */
char *outbuf; /* Output buffer */
dma_addr_t outbuf_dma; /* Output buffer dma */
unsigned long last_out; /* record of last output for timeouts */
struct mutex mutex; /* start/stop/open/close */
spinlock_t lock; /* fifo spinlock */
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
struct timer_list io_retry; /* Retry timer */
unsigned long stop_retry; /* Time to give up, in jiffies */
unsigned int retry_delay; /* Delay length in ms */
struct work_struct reset_work; /* Task context for resets */
wait_queue_head_t wait; /* For sleeping */
};
#define hid_to_usb_dev(hid_dev) \
to_usb_device(hid_dev->dev.parent->parent)
/*
* Version Information
*/
#define DRIVER_VERSION "v1.6"
#define DRIVER_AUTHOR "Vojtech Pavlik <[email protected]>"
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
struct usb_mouse {
char name[128];
char phys[64];
struct usb_device *usbdev;
struct input_dev *dev;
struct urb *irq;
signed char *data;
dma_addr_t data_dma;
};
static void usb_mouse_irq(struct urb *urb)
{
printk("KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\n");
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = mouse->dev;
int status;
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
input_report_rel(dev, REL_X, data[1]);
input_report_rel(dev, REL_Y, data[2]);
input_report_rel(dev, REL_WHEEL, data[3]);
input_sync(dev);
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
dev_err(&mouse->usbdev->dev,
"can't resubmit intr, %s-%s/input0, status %d\n",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}
static int usb_mouse_open(struct input_dev *dev)
{
printk("KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\n");
struct usb_mouse *mouse = input_get_drvdata(dev);
mouse->irq->dev = mouse->usbdev;
if (usb_submit_urb(mouse->irq, GFP_KERNEL))
return -EIO;
return 0;
}
static void usb_mouse_close(struct input_dev *dev)
{
printk("KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\n");
struct usb_mouse *mouse = input_get_drvdata(dev);
usb_kill_urb(mouse->irq);
}
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM;
interface = intf->cur_altsetting;
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1;
mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);
if (!mouse->data)
goto fail1;
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!mouse->irq)
goto fail2;
mouse->usbdev = dev;
mouse->dev = input_dev;
if (dev->manufacturer)
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(mouse->name, " ", sizeof(mouse->name));
strlcat(mouse->name, dev->product, sizeof(mouse->name));
}
if (!strlen(mouse->name))
snprintf(mouse->name, sizeof(mouse->name),
"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
BIT_MASK(BTN_EXTRA);
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
input_set_drvdata(input_dev, mouse);
input_dev->open = usb_mouse_open;
input_dev->close = usb_mouse_close;
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > 8 ? 8 : maxp),
usb_mouse_irq, mouse, endpoint->bInterval);
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
error = input_register_device(mouse->dev);
if (error)
goto fail3;
usb_set_intfdata(intf, mouse);
printk("KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\n");
return 0;
fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}
static void usb_mouse_disconnect(struct usb_interface *intf)
{
printk("KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\n");
struct usb_mouse *mouse = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL);
if (mouse) {
usb_kill_urb(mouse->irq);
input_unregister_device(mouse->dev);
usb_free_urb(mouse->irq);
usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
kfree(mouse);
}
}
static const struct usb_device_id usb_mouse_id_table[] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
static struct usb_driver usb_mouse_driver = {
.name = "per_driver",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
module_usb_driver(usb_mouse_driver);