diff options
-rw-r--r-- | drivers/hid/hid-wiimote.c | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/drivers/hid/hid-wiimote.c b/drivers/hid/hid-wiimote.c index 811ed8921013..bfc50493ec6b 100644 --- a/drivers/hid/hid-wiimote.c +++ b/drivers/hid/hid-wiimote.c | |||
@@ -15,15 +15,28 @@ | |||
15 | #include <linux/hid.h> | 15 | #include <linux/hid.h> |
16 | #include <linux/input.h> | 16 | #include <linux/input.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/spinlock.h> | ||
18 | #include "hid-ids.h" | 19 | #include "hid-ids.h" |
19 | 20 | ||
20 | #define WIIMOTE_VERSION "0.1" | 21 | #define WIIMOTE_VERSION "0.1" |
21 | #define WIIMOTE_NAME "Nintendo Wii Remote" | 22 | #define WIIMOTE_NAME "Nintendo Wii Remote" |
23 | #define WIIMOTE_BUFSIZE 32 | ||
24 | |||
25 | struct wiimote_buf { | ||
26 | __u8 data[HID_MAX_BUFFER_SIZE]; | ||
27 | size_t size; | ||
28 | }; | ||
22 | 29 | ||
23 | struct wiimote_data { | 30 | struct wiimote_data { |
24 | atomic_t ready; | 31 | atomic_t ready; |
25 | struct hid_device *hdev; | 32 | struct hid_device *hdev; |
26 | struct input_dev *input; | 33 | struct input_dev *input; |
34 | |||
35 | spinlock_t qlock; | ||
36 | __u8 head; | ||
37 | __u8 tail; | ||
38 | struct wiimote_buf outq[WIIMOTE_BUFSIZE]; | ||
39 | struct work_struct worker; | ||
27 | }; | 40 | }; |
28 | 41 | ||
29 | static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, | 42 | static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, |
@@ -45,6 +58,65 @@ static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, | |||
45 | return ret; | 58 | return ret; |
46 | } | 59 | } |
47 | 60 | ||
61 | static void wiimote_worker(struct work_struct *work) | ||
62 | { | ||
63 | struct wiimote_data *wdata = container_of(work, struct wiimote_data, | ||
64 | worker); | ||
65 | unsigned long flags; | ||
66 | |||
67 | spin_lock_irqsave(&wdata->qlock, flags); | ||
68 | |||
69 | while (wdata->head != wdata->tail) { | ||
70 | spin_unlock_irqrestore(&wdata->qlock, flags); | ||
71 | wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data, | ||
72 | wdata->outq[wdata->tail].size); | ||
73 | spin_lock_irqsave(&wdata->qlock, flags); | ||
74 | |||
75 | wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE; | ||
76 | } | ||
77 | |||
78 | spin_unlock_irqrestore(&wdata->qlock, flags); | ||
79 | } | ||
80 | |||
81 | static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer, | ||
82 | size_t count) | ||
83 | { | ||
84 | unsigned long flags; | ||
85 | __u8 newhead; | ||
86 | |||
87 | if (count > HID_MAX_BUFFER_SIZE) { | ||
88 | hid_warn(wdata->hdev, "Sending too large output report\n"); | ||
89 | return; | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * Copy new request into our output queue and check whether the | ||
94 | * queue is full. If it is full, discard this request. | ||
95 | * If it is empty we need to start a new worker that will | ||
96 | * send out the buffer to the hid device. | ||
97 | * If the queue is not empty, then there must be a worker | ||
98 | * that is currently sending out our buffer and this worker | ||
99 | * will reschedule itself until the queue is empty. | ||
100 | */ | ||
101 | |||
102 | spin_lock_irqsave(&wdata->qlock, flags); | ||
103 | |||
104 | memcpy(wdata->outq[wdata->head].data, buffer, count); | ||
105 | wdata->outq[wdata->head].size = count; | ||
106 | newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE; | ||
107 | |||
108 | if (wdata->head == wdata->tail) { | ||
109 | wdata->head = newhead; | ||
110 | schedule_work(&wdata->worker); | ||
111 | } else if (newhead != wdata->tail) { | ||
112 | wdata->head = newhead; | ||
113 | } else { | ||
114 | hid_warn(wdata->hdev, "Output queue is full"); | ||
115 | } | ||
116 | |||
117 | spin_unlock_irqrestore(&wdata->qlock, flags); | ||
118 | } | ||
119 | |||
48 | static int wiimote_input_event(struct input_dev *dev, unsigned int type, | 120 | static int wiimote_input_event(struct input_dev *dev, unsigned int type, |
49 | unsigned int code, int value) | 121 | unsigned int code, int value) |
50 | { | 122 | { |
@@ -100,6 +172,9 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev) | |||
100 | wdata->input->id.version = wdata->hdev->version; | 172 | wdata->input->id.version = wdata->hdev->version; |
101 | wdata->input->name = WIIMOTE_NAME; | 173 | wdata->input->name = WIIMOTE_NAME; |
102 | 174 | ||
175 | spin_lock_init(&wdata->qlock); | ||
176 | INIT_WORK(&wdata->worker, wiimote_worker); | ||
177 | |||
103 | return wdata; | 178 | return wdata; |
104 | } | 179 | } |
105 | 180 | ||
@@ -157,8 +232,11 @@ static void wiimote_hid_remove(struct hid_device *hdev) | |||
157 | struct wiimote_data *wdata = hid_get_drvdata(hdev); | 232 | struct wiimote_data *wdata = hid_get_drvdata(hdev); |
158 | 233 | ||
159 | hid_info(hdev, "Device removed\n"); | 234 | hid_info(hdev, "Device removed\n"); |
235 | |||
160 | hid_hw_stop(hdev); | 236 | hid_hw_stop(hdev); |
161 | input_unregister_device(wdata->input); | 237 | input_unregister_device(wdata->input); |
238 | |||
239 | cancel_work_sync(&wdata->worker); | ||
162 | wiimote_destroy(wdata); | 240 | wiimote_destroy(wdata); |
163 | } | 241 | } |
164 | 242 | ||