1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz> |
4 | * Copyright (c) 2001, 2007 Johann Deneux <johann.deneux@gmail.com> |
5 | * |
6 | * USB/RS232 I-Force joysticks and wheels. |
7 | */ |
8 | |
9 | #include <linux/serio.h> |
10 | #include "iforce.h" |
11 | |
12 | struct iforce_serio { |
13 | struct iforce iforce; |
14 | |
15 | struct serio *serio; |
16 | int idx, pkt, len, id; |
17 | u8 csum; |
18 | u8 expect_packet; |
19 | u8 cmd_response[IFORCE_MAX_LENGTH]; |
20 | u8 cmd_response_len; |
21 | u8 data_in[IFORCE_MAX_LENGTH]; |
22 | }; |
23 | |
24 | static void iforce_serio_xmit(struct iforce *iforce) |
25 | { |
26 | struct iforce_serio *iforce_serio = container_of(iforce, |
27 | struct iforce_serio, |
28 | iforce); |
29 | unsigned char cs; |
30 | int i; |
31 | unsigned long flags; |
32 | |
33 | if (test_and_set_bit(IFORCE_XMIT_RUNNING, addr: iforce->xmit_flags)) { |
34 | set_bit(IFORCE_XMIT_AGAIN, addr: iforce->xmit_flags); |
35 | return; |
36 | } |
37 | |
38 | spin_lock_irqsave(&iforce->xmit_lock, flags); |
39 | |
40 | again: |
41 | if (iforce->xmit.head == iforce->xmit.tail) { |
42 | iforce_clear_xmit_and_wake(iforce); |
43 | spin_unlock_irqrestore(lock: &iforce->xmit_lock, flags); |
44 | return; |
45 | } |
46 | |
47 | cs = 0x2b; |
48 | |
49 | serio_write(serio: iforce_serio->serio, data: 0x2b); |
50 | |
51 | serio_write(serio: iforce_serio->serio, data: iforce->xmit.buf[iforce->xmit.tail]); |
52 | cs ^= iforce->xmit.buf[iforce->xmit.tail]; |
53 | XMIT_INC(iforce->xmit.tail, 1); |
54 | |
55 | for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { |
56 | serio_write(serio: iforce_serio->serio, |
57 | data: iforce->xmit.buf[iforce->xmit.tail]); |
58 | cs ^= iforce->xmit.buf[iforce->xmit.tail]; |
59 | XMIT_INC(iforce->xmit.tail, 1); |
60 | } |
61 | |
62 | serio_write(serio: iforce_serio->serio, data: cs); |
63 | |
64 | if (test_and_clear_bit(IFORCE_XMIT_AGAIN, addr: iforce->xmit_flags)) |
65 | goto again; |
66 | |
67 | iforce_clear_xmit_and_wake(iforce); |
68 | |
69 | spin_unlock_irqrestore(lock: &iforce->xmit_lock, flags); |
70 | } |
71 | |
72 | static int iforce_serio_get_id(struct iforce *iforce, u8 id, |
73 | u8 *response_data, size_t *response_len) |
74 | { |
75 | struct iforce_serio *iforce_serio = container_of(iforce, |
76 | struct iforce_serio, |
77 | iforce); |
78 | |
79 | iforce_serio->expect_packet = HI(FF_CMD_QUERY); |
80 | iforce_serio->cmd_response_len = 0; |
81 | |
82 | iforce_send_packet(iforce, FF_CMD_QUERY, data: &id); |
83 | |
84 | wait_event_interruptible_timeout(iforce->wait, |
85 | !iforce_serio->expect_packet, HZ); |
86 | |
87 | if (iforce_serio->expect_packet) { |
88 | iforce_serio->expect_packet = 0; |
89 | return -ETIMEDOUT; |
90 | } |
91 | |
92 | if (iforce_serio->cmd_response[0] != id) |
93 | return -EIO; |
94 | |
95 | memcpy(response_data, iforce_serio->cmd_response, |
96 | iforce_serio->cmd_response_len); |
97 | *response_len = iforce_serio->cmd_response_len; |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static int iforce_serio_start_io(struct iforce *iforce) |
103 | { |
104 | /* No special handling required */ |
105 | return 0; |
106 | } |
107 | |
108 | static void iforce_serio_stop_io(struct iforce *iforce) |
109 | { |
110 | //TODO: Wait for the last packets to be sent |
111 | } |
112 | |
113 | static const struct iforce_xport_ops iforce_serio_xport_ops = { |
114 | .xmit = iforce_serio_xmit, |
115 | .get_id = iforce_serio_get_id, |
116 | .start_io = iforce_serio_start_io, |
117 | .stop_io = iforce_serio_stop_io, |
118 | }; |
119 | |
120 | static void iforce_serio_write_wakeup(struct serio *serio) |
121 | { |
122 | struct iforce *iforce = serio_get_drvdata(serio); |
123 | |
124 | iforce_serio_xmit(iforce); |
125 | } |
126 | |
127 | static irqreturn_t iforce_serio_irq(struct serio *serio, |
128 | unsigned char data, unsigned int flags) |
129 | { |
130 | struct iforce_serio *iforce_serio = serio_get_drvdata(serio); |
131 | struct iforce *iforce = &iforce_serio->iforce; |
132 | |
133 | if (!iforce_serio->pkt) { |
134 | if (data == 0x2b) |
135 | iforce_serio->pkt = 1; |
136 | goto out; |
137 | } |
138 | |
139 | if (!iforce_serio->id) { |
140 | if (data > 3 && data != 0xff) |
141 | iforce_serio->pkt = 0; |
142 | else |
143 | iforce_serio->id = data; |
144 | goto out; |
145 | } |
146 | |
147 | if (!iforce_serio->len) { |
148 | if (data > IFORCE_MAX_LENGTH) { |
149 | iforce_serio->pkt = 0; |
150 | iforce_serio->id = 0; |
151 | } else { |
152 | iforce_serio->len = data; |
153 | } |
154 | goto out; |
155 | } |
156 | |
157 | if (iforce_serio->idx < iforce_serio->len) { |
158 | iforce_serio->data_in[iforce_serio->idx++] = data; |
159 | iforce_serio->csum += data; |
160 | goto out; |
161 | } |
162 | |
163 | if (iforce_serio->idx == iforce_serio->len) { |
164 | /* Handle command completion */ |
165 | if (iforce_serio->expect_packet == iforce_serio->id) { |
166 | iforce_serio->expect_packet = 0; |
167 | memcpy(iforce_serio->cmd_response, |
168 | iforce_serio->data_in, IFORCE_MAX_LENGTH); |
169 | iforce_serio->cmd_response_len = iforce_serio->len; |
170 | |
171 | /* Signal that command is done */ |
172 | wake_up_all(&iforce->wait); |
173 | } else if (likely(iforce->type)) { |
174 | iforce_process_packet(iforce, packet_id: iforce_serio->id, |
175 | data: iforce_serio->data_in, |
176 | len: iforce_serio->len); |
177 | } |
178 | |
179 | iforce_serio->pkt = 0; |
180 | iforce_serio->id = 0; |
181 | iforce_serio->len = 0; |
182 | iforce_serio->idx = 0; |
183 | iforce_serio->csum = 0; |
184 | } |
185 | out: |
186 | return IRQ_HANDLED; |
187 | } |
188 | |
189 | static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv) |
190 | { |
191 | struct iforce_serio *iforce_serio; |
192 | int err; |
193 | |
194 | iforce_serio = kzalloc(size: sizeof(*iforce_serio), GFP_KERNEL); |
195 | if (!iforce_serio) |
196 | return -ENOMEM; |
197 | |
198 | iforce_serio->iforce.xport_ops = &iforce_serio_xport_ops; |
199 | |
200 | iforce_serio->serio = serio; |
201 | serio_set_drvdata(serio, data: iforce_serio); |
202 | |
203 | err = serio_open(serio, drv); |
204 | if (err) |
205 | goto fail1; |
206 | |
207 | err = iforce_init_device(parent: &serio->dev, BUS_RS232, iforce: &iforce_serio->iforce); |
208 | if (err) |
209 | goto fail2; |
210 | |
211 | return 0; |
212 | |
213 | fail2: serio_close(serio); |
214 | fail1: serio_set_drvdata(serio, NULL); |
215 | kfree(objp: iforce_serio); |
216 | return err; |
217 | } |
218 | |
219 | static void iforce_serio_disconnect(struct serio *serio) |
220 | { |
221 | struct iforce_serio *iforce_serio = serio_get_drvdata(serio); |
222 | |
223 | input_unregister_device(iforce_serio->iforce.dev); |
224 | serio_close(serio); |
225 | serio_set_drvdata(serio, NULL); |
226 | kfree(objp: iforce_serio); |
227 | } |
228 | |
229 | static const struct serio_device_id iforce_serio_ids[] = { |
230 | { |
231 | .type = SERIO_RS232, |
232 | .proto = SERIO_IFORCE, |
233 | .id = SERIO_ANY, |
234 | .extra = SERIO_ANY, |
235 | }, |
236 | { 0 } |
237 | }; |
238 | |
239 | MODULE_DEVICE_TABLE(serio, iforce_serio_ids); |
240 | |
241 | struct serio_driver iforce_serio_drv = { |
242 | .driver = { |
243 | .name = "iforce" , |
244 | }, |
245 | .description = "RS232 I-Force joysticks and wheels driver" , |
246 | .id_table = iforce_serio_ids, |
247 | .write_wakeup = iforce_serio_write_wakeup, |
248 | .interrupt = iforce_serio_irq, |
249 | .connect = iforce_serio_connect, |
250 | .disconnect = iforce_serio_disconnect, |
251 | }; |
252 | |
253 | module_serio_driver(iforce_serio_drv); |
254 | |
255 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>" ); |
256 | MODULE_DESCRIPTION("RS232 I-Force joysticks and wheels driver" ); |
257 | MODULE_LICENSE("GPL" ); |
258 | |