]>
Commit | Line | Data |
---|---|---|
f587027e GKH |
1 | /* |
2 | * Greybus "AP" USB driver for "ES2" controller chips | |
3 | * | |
142f8ddf AE |
4 | * Copyright 2014-2015 Google Inc. |
5 | * Copyright 2014-2015 Linaro Ltd. | |
f587027e GKH |
6 | * |
7 | * Released under the GPLv2 only. | |
8 | */ | |
ca3ec299 | 9 | #include <linux/kthread.h> |
f587027e GKH |
10 | #include <linux/sizes.h> |
11 | #include <linux/usb.h> | |
ca3ec299 GKH |
12 | #include <linux/kfifo.h> |
13 | #include <linux/debugfs.h> | |
491e60d6 | 14 | #include <asm/unaligned.h> |
f587027e GKH |
15 | |
16 | #include "greybus.h" | |
f587027e | 17 | #include "kernel_ver.h" |
3f2a809e | 18 | #include "connection.h" |
f587027e | 19 | |
f587027e | 20 | /* Memory sizes for the buffers sent to/from the ES1 controller */ |
cce31036 | 21 | #define ES1_GBUF_MSG_SIZE_MAX 2048 |
f587027e GKH |
22 | |
23 | static const struct usb_device_id id_table[] = { | |
2bf4c876 GKH |
24 | /* Made up numbers for the SVC USB Bridge in ES2 */ |
25 | { USB_DEVICE(0xffff, 0x0002) }, | |
f587027e GKH |
26 | { }, |
27 | }; | |
28 | MODULE_DEVICE_TABLE(usb, id_table); | |
29 | ||
ca3ec299 GKH |
30 | #define APB1_LOG_SIZE SZ_16K |
31 | static struct dentry *apb1_log_dentry; | |
32 | static struct dentry *apb1_log_enable_dentry; | |
33 | static struct task_struct *apb1_log_task; | |
34 | static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); | |
35 | ||
fc1a536e | 36 | /* Number of cport present on USB bridge */ |
e074a2e2 | 37 | #define CPORT_COUNT 44 |
fc1a536e | 38 | |
606addd2 AB |
39 | /* Number of bulk in and bulk out couple */ |
40 | #define NUM_BULKS 7 | |
41 | ||
f587027e GKH |
42 | /* |
43 | * Number of CPort IN urbs in flight at any point in time. | |
44 | * Adjust if we are having stalls in the USB buffer due to not enough urbs in | |
45 | * flight. | |
46 | */ | |
47 | #define NUM_CPORT_IN_URB 4 | |
48 | ||
49 | /* Number of CPort OUT urbs in flight at any point in time. | |
50 | * Adjust if we get messages saying we are out of urbs in the system log. | |
51 | */ | |
606addd2 | 52 | #define NUM_CPORT_OUT_URB (8 * NUM_BULKS) |
f587027e | 53 | |
611c1739 AB |
54 | /* vendor request AP message */ |
55 | #define REQUEST_SVC 0x01 | |
56 | ||
57 | /* vendor request APB1 log */ | |
58 | #define REQUEST_LOG 0x02 | |
59 | ||
fc1a536e AB |
60 | /* vendor request to map a cport to bulk in and bulk out endpoints */ |
61 | #define REQUEST_EP_MAPPING 0x03 | |
62 | ||
ddc09acd AB |
63 | /* |
64 | * @endpoint: bulk in endpoint for CPort data | |
65 | * @urb: array of urbs for the CPort in messages | |
66 | * @buffer: array of buffers for the @cport_in_urb urbs | |
67 | */ | |
68 | struct es1_cport_in { | |
69 | __u8 endpoint; | |
70 | struct urb *urb[NUM_CPORT_IN_URB]; | |
71 | u8 *buffer[NUM_CPORT_IN_URB]; | |
72 | }; | |
73 | ||
74 | /* | |
75 | * @endpoint: bulk out endpoint for CPort data | |
76 | */ | |
77 | struct es1_cport_out { | |
78 | __u8 endpoint; | |
79 | }; | |
80 | ||
f587027e GKH |
81 | /** |
82 | * es1_ap_dev - ES1 USB Bridge to AP structure | |
83 | * @usb_dev: pointer to the USB device we are. | |
84 | * @usb_intf: pointer to the USB interface we are bound to. | |
85 | * @hd: pointer to our greybus_host_device structure | |
86 | * @control_endpoint: endpoint to send data to SVC | |
ddc09acd | 87 | |
ddc09acd AB |
88 | * @cport_in: endpoint, urbs and buffer for cport in messages |
89 | * @cport_out: endpoint for for cport out messages | |
f587027e GKH |
90 | * @cport_out_urb: array of urbs for the CPort out messages |
91 | * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or | |
92 | * not. | |
3e136cc9 JH |
93 | * @cport_out_urb_cancelled: array of flags indicating whether the |
94 | * corresponding @cport_out_urb is being cancelled | |
f587027e GKH |
95 | * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" |
96 | */ | |
97 | struct es1_ap_dev { | |
98 | struct usb_device *usb_dev; | |
99 | struct usb_interface *usb_intf; | |
100 | struct greybus_host_device *hd; | |
101 | ||
102 | __u8 control_endpoint; | |
f587027e | 103 | |
606addd2 AB |
104 | struct es1_cport_in cport_in[NUM_BULKS]; |
105 | struct es1_cport_out cport_out[NUM_BULKS]; | |
f587027e GKH |
106 | struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; |
107 | bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; | |
3e136cc9 | 108 | bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; |
f587027e | 109 | spinlock_t cport_out_urb_lock; |
fc1a536e | 110 | |
c011d558 | 111 | int *cport_to_ep; |
fc1a536e AB |
112 | }; |
113 | ||
114 | struct cport_to_ep { | |
115 | __le16 cport_id; | |
116 | __u8 endpoint_in; | |
117 | __u8 endpoint_out; | |
f587027e GKH |
118 | }; |
119 | ||
120 | static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) | |
121 | { | |
122 | return (struct es1_ap_dev *)&hd->hd_priv; | |
123 | } | |
124 | ||
125 | static void cport_out_callback(struct urb *urb); | |
ca3ec299 GKH |
126 | static void usb_log_enable(struct es1_ap_dev *es1); |
127 | static void usb_log_disable(struct es1_ap_dev *es1); | |
f587027e | 128 | |
fc1a536e AB |
129 | static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) |
130 | { | |
144670c2 | 131 | if (cport_id >= es1->hd->num_cports) |
fc1a536e AB |
132 | return 0; |
133 | return es1->cport_to_ep[cport_id]; | |
134 | } | |
135 | ||
f587027e | 136 | #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ |
f587027e | 137 | |
fc1a536e AB |
138 | static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) |
139 | { | |
140 | int i; | |
141 | ||
144670c2 | 142 | for (i = 0; i < es1->hd->num_cports; i++) { |
fc1a536e AB |
143 | if (es1->cport_to_ep[i] == bulk_ep_set) |
144 | return 1; | |
145 | } | |
146 | return 0; | |
147 | } | |
148 | ||
149 | int map_cport_to_ep(struct es1_ap_dev *es1, | |
150 | u16 cport_id, int bulk_ep_set) | |
151 | { | |
152 | int retval; | |
153 | struct cport_to_ep *cport_to_ep; | |
154 | ||
155 | if (bulk_ep_set == 0 || bulk_ep_set >= NUM_BULKS) | |
156 | return -EINVAL; | |
144670c2 | 157 | if (cport_id >= es1->hd->num_cports) |
fc1a536e AB |
158 | return -EINVAL; |
159 | if (bulk_ep_set && ep_in_use(es1, bulk_ep_set)) | |
160 | return -EINVAL; | |
161 | ||
162 | cport_to_ep = kmalloc(sizeof(*cport_to_ep), GFP_KERNEL); | |
163 | if (!cport_to_ep) | |
164 | return -ENOMEM; | |
165 | ||
166 | es1->cport_to_ep[cport_id] = bulk_ep_set; | |
167 | cport_to_ep->cport_id = cpu_to_le16(cport_id); | |
168 | cport_to_ep->endpoint_in = es1->cport_in[bulk_ep_set].endpoint; | |
169 | cport_to_ep->endpoint_out = es1->cport_out[bulk_ep_set].endpoint; | |
170 | ||
171 | retval = usb_control_msg(es1->usb_dev, | |
172 | usb_sndctrlpipe(es1->usb_dev, | |
173 | es1->control_endpoint), | |
174 | REQUEST_EP_MAPPING, | |
175 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, | |
176 | 0x00, 0x00, | |
177 | (char *)cport_to_ep, | |
178 | sizeof(*cport_to_ep), | |
179 | ES1_TIMEOUT); | |
180 | if (retval == sizeof(*cport_to_ep)) | |
181 | retval = 0; | |
182 | kfree(cport_to_ep); | |
183 | ||
184 | return retval; | |
185 | } | |
186 | ||
187 | int unmap_cport(struct es1_ap_dev *es1, u16 cport_id) | |
188 | { | |
189 | return map_cport_to_ep(es1, cport_id, 0); | |
190 | } | |
191 | ||
f587027e GKH |
192 | static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) |
193 | { | |
194 | struct urb *urb = NULL; | |
195 | unsigned long flags; | |
196 | int i; | |
197 | ||
198 | spin_lock_irqsave(&es1->cport_out_urb_lock, flags); | |
199 | ||
200 | /* Look in our pool of allocated urbs first, as that's the "fastest" */ | |
201 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
3e136cc9 JH |
202 | if (es1->cport_out_urb_busy[i] == false && |
203 | es1->cport_out_urb_cancelled[i] == false) { | |
f587027e GKH |
204 | es1->cport_out_urb_busy[i] = true; |
205 | urb = es1->cport_out_urb[i]; | |
206 | break; | |
207 | } | |
208 | } | |
209 | spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); | |
210 | if (urb) | |
211 | return urb; | |
212 | ||
213 | /* | |
214 | * Crap, pool is empty, complain to the syslog and go allocate one | |
215 | * dynamically as we have to succeed. | |
216 | */ | |
217 | dev_err(&es1->usb_dev->dev, | |
218 | "No free CPort OUT urbs, having to dynamically allocate one!\n"); | |
219 | return usb_alloc_urb(0, gfp_mask); | |
220 | } | |
221 | ||
222 | static void free_urb(struct es1_ap_dev *es1, struct urb *urb) | |
223 | { | |
224 | unsigned long flags; | |
225 | int i; | |
226 | /* | |
227 | * See if this was an urb in our pool, if so mark it "free", otherwise | |
228 | * we need to free it ourselves. | |
229 | */ | |
230 | spin_lock_irqsave(&es1->cport_out_urb_lock, flags); | |
231 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
232 | if (urb == es1->cport_out_urb[i]) { | |
233 | es1->cport_out_urb_busy[i] = false; | |
234 | urb = NULL; | |
235 | break; | |
236 | } | |
237 | } | |
238 | spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); | |
239 | ||
240 | /* If urb is not NULL, then we need to free this urb */ | |
241 | usb_free_urb(urb); | |
242 | } | |
243 | ||
d29b3d63 AE |
244 | /* |
245 | * We (ab)use the operation-message header pad bytes to transfer the | |
246 | * cport id in order to minimise overhead. | |
247 | */ | |
248 | static void | |
249 | gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) | |
250 | { | |
4bc1389d | 251 | header->pad[0] = cport_id; |
d29b3d63 AE |
252 | } |
253 | ||
254 | /* Clear the pad bytes used for the CPort id */ | |
255 | static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) | |
256 | { | |
4bc1389d | 257 | header->pad[0] = 0; |
d29b3d63 AE |
258 | } |
259 | ||
260 | /* Extract the CPort id packed into the header, and clear it */ | |
261 | static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) | |
262 | { | |
4bc1389d | 263 | u16 cport_id = header->pad[0]; |
d29b3d63 AE |
264 | |
265 | gb_message_cport_clear(header); | |
266 | ||
267 | return cport_id; | |
268 | } | |
269 | ||
f587027e | 270 | /* |
3e136cc9 JH |
271 | * Returns zero if the message was successfully queued, or a negative errno |
272 | * otherwise. | |
f587027e | 273 | */ |
3e136cc9 | 274 | static int message_send(struct greybus_host_device *hd, u16 cport_id, |
7cf7bca9 | 275 | struct gb_message *message, gfp_t gfp_mask) |
f587027e GKH |
276 | { |
277 | struct es1_ap_dev *es1 = hd_to_es1(hd); | |
278 | struct usb_device *udev = es1->usb_dev; | |
7cf7bca9 | 279 | size_t buffer_size; |
f587027e GKH |
280 | int retval; |
281 | struct urb *urb; | |
fc1a536e | 282 | int bulk_ep_set; |
3e136cc9 | 283 | unsigned long flags; |
f587027e | 284 | |
f587027e GKH |
285 | /* |
286 | * The data actually transferred will include an indication | |
287 | * of where the data should be sent. Do one last check of | |
288 | * the target CPort id before filling it in. | |
289 | */ | |
144670c2 | 290 | if (!cport_id_valid(hd, cport_id)) { |
821c620a | 291 | pr_err("invalid destination cport 0x%02x\n", cport_id); |
3e136cc9 | 292 | return -EINVAL; |
f587027e | 293 | } |
f587027e GKH |
294 | |
295 | /* Find a free urb */ | |
296 | urb = next_free_urb(es1, gfp_mask); | |
297 | if (!urb) | |
3e136cc9 JH |
298 | return -ENOMEM; |
299 | ||
300 | spin_lock_irqsave(&es1->cport_out_urb_lock, flags); | |
301 | message->hcpriv = urb; | |
302 | spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); | |
f587027e | 303 | |
d29b3d63 AE |
304 | /* Pack the cport id into the message header */ |
305 | gb_message_cport_pack(message->header, cport_id); | |
491e60d6 | 306 | |
821c620a | 307 | buffer_size = sizeof(*message->header) + message->payload_size; |
491e60d6 | 308 | |
fc1a536e | 309 | bulk_ep_set = cport_to_ep(es1, cport_id); |
f587027e | 310 | usb_fill_bulk_urb(urb, udev, |
606addd2 AB |
311 | usb_sndbulkpipe(udev, |
312 | es1->cport_out[bulk_ep_set].endpoint), | |
821c620a | 313 | message->buffer, buffer_size, |
7cf7bca9 | 314 | cport_out_callback, message); |
977e209a | 315 | urb->transfer_flags |= URB_ZERO_PACKET; |
3f2a809e | 316 | gb_connection_push_timestamp(message->operation->connection); |
f587027e GKH |
317 | retval = usb_submit_urb(urb, gfp_mask); |
318 | if (retval) { | |
319 | pr_err("error %d submitting URB\n", retval); | |
3e136cc9 JH |
320 | |
321 | spin_lock_irqsave(&es1->cport_out_urb_lock, flags); | |
322 | message->hcpriv = NULL; | |
323 | spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); | |
324 | ||
f587027e | 325 | free_urb(es1, urb); |
d29b3d63 | 326 | gb_message_cport_clear(message->header); |
3e136cc9 JH |
327 | |
328 | return retval; | |
f587027e GKH |
329 | } |
330 | ||
3e136cc9 | 331 | return 0; |
f587027e GKH |
332 | } |
333 | ||
334 | /* | |
3e136cc9 | 335 | * Can not be called in atomic context. |
f587027e | 336 | */ |
3e136cc9 | 337 | static void message_cancel(struct gb_message *message) |
f587027e | 338 | { |
3e136cc9 JH |
339 | struct greybus_host_device *hd = message->operation->connection->hd; |
340 | struct es1_ap_dev *es1 = hd_to_es1(hd); | |
341 | struct urb *urb; | |
342 | int i; | |
f587027e | 343 | |
3e136cc9 JH |
344 | might_sleep(); |
345 | ||
346 | spin_lock_irq(&es1->cport_out_urb_lock); | |
347 | urb = message->hcpriv; | |
348 | ||
349 | /* Prevent dynamically allocated urb from being deallocated. */ | |
350 | usb_get_urb(urb); | |
351 | ||
352 | /* Prevent pre-allocated urb from being reused. */ | |
353 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
354 | if (urb == es1->cport_out_urb[i]) { | |
355 | es1->cport_out_urb_cancelled[i] = true; | |
356 | break; | |
357 | } | |
358 | } | |
359 | spin_unlock_irq(&es1->cport_out_urb_lock); | |
360 | ||
361 | usb_kill_urb(urb); | |
362 | ||
363 | if (i < NUM_CPORT_OUT_URB) { | |
364 | spin_lock_irq(&es1->cport_out_urb_lock); | |
365 | es1->cport_out_urb_cancelled[i] = false; | |
366 | spin_unlock_irq(&es1->cport_out_urb_lock); | |
367 | } | |
368 | ||
369 | usb_free_urb(urb); | |
f587027e GKH |
370 | } |
371 | ||
372 | static struct greybus_host_driver es1_driver = { | |
373 | .hd_priv_size = sizeof(struct es1_ap_dev), | |
7cf7bca9 JH |
374 | .message_send = message_send, |
375 | .message_cancel = message_cancel, | |
f587027e GKH |
376 | }; |
377 | ||
378 | /* Common function to report consistent warnings based on URB status */ | |
379 | static int check_urb_status(struct urb *urb) | |
380 | { | |
381 | struct device *dev = &urb->dev->dev; | |
382 | int status = urb->status; | |
383 | ||
384 | switch (status) { | |
385 | case 0: | |
386 | return 0; | |
387 | ||
388 | case -EOVERFLOW: | |
389 | dev_err(dev, "%s: overflow actual length is %d\n", | |
390 | __func__, urb->actual_length); | |
391 | case -ECONNRESET: | |
392 | case -ENOENT: | |
393 | case -ESHUTDOWN: | |
394 | case -EILSEQ: | |
395 | case -EPROTO: | |
396 | /* device is gone, stop sending */ | |
397 | return status; | |
398 | } | |
399 | dev_err(dev, "%s: unknown status %d\n", __func__, status); | |
400 | ||
401 | return -EAGAIN; | |
402 | } | |
403 | ||
404 | static void ap_disconnect(struct usb_interface *interface) | |
405 | { | |
406 | struct es1_ap_dev *es1; | |
407 | struct usb_device *udev; | |
606addd2 | 408 | int bulk_in; |
f587027e GKH |
409 | int i; |
410 | ||
411 | es1 = usb_get_intfdata(interface); | |
412 | if (!es1) | |
413 | return; | |
414 | ||
ca3ec299 GKH |
415 | usb_log_disable(es1); |
416 | ||
f587027e GKH |
417 | /* Tear down everything! */ |
418 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
419 | struct urb *urb = es1->cport_out_urb[i]; | |
420 | ||
421 | if (!urb) | |
422 | break; | |
423 | usb_kill_urb(urb); | |
424 | usb_free_urb(urb); | |
425 | es1->cport_out_urb[i] = NULL; | |
426 | es1->cport_out_urb_busy[i] = false; /* just to be anal */ | |
427 | } | |
428 | ||
606addd2 AB |
429 | for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { |
430 | struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; | |
431 | for (i = 0; i < NUM_CPORT_IN_URB; ++i) { | |
432 | struct urb *urb = cport_in->urb[i]; | |
433 | ||
434 | if (!urb) | |
435 | break; | |
436 | usb_kill_urb(urb); | |
437 | usb_free_urb(urb); | |
438 | kfree(cport_in->buffer[i]); | |
439 | cport_in->buffer[i] = NULL; | |
440 | } | |
f587027e GKH |
441 | } |
442 | ||
f587027e GKH |
443 | usb_set_intfdata(interface, NULL); |
444 | udev = es1->usb_dev; | |
445 | greybus_remove_hd(es1->hd); | |
c011d558 | 446 | kfree(es1->cport_to_ep); |
f587027e GKH |
447 | |
448 | usb_put_dev(udev); | |
449 | } | |
450 | ||
f587027e GKH |
451 | static void cport_in_callback(struct urb *urb) |
452 | { | |
453 | struct greybus_host_device *hd = urb->context; | |
454 | struct device *dev = &urb->dev->dev; | |
491e60d6 | 455 | struct gb_operation_msg_hdr *header; |
f587027e GKH |
456 | int status = check_urb_status(urb); |
457 | int retval; | |
458 | u16 cport_id; | |
f587027e GKH |
459 | |
460 | if (status) { | |
461 | if ((status == -EAGAIN) || (status == -EPROTO)) | |
462 | goto exit; | |
463 | dev_err(dev, "urb cport in error %d (dropped)\n", status); | |
464 | return; | |
465 | } | |
466 | ||
491e60d6 JH |
467 | if (urb->actual_length < sizeof(*header)) { |
468 | dev_err(dev, "%s: short message received\n", __func__); | |
f587027e GKH |
469 | goto exit; |
470 | } | |
471 | ||
d29b3d63 | 472 | /* Extract the CPort id, which is packed in the message header */ |
491e60d6 | 473 | header = urb->transfer_buffer; |
d29b3d63 | 474 | cport_id = gb_message_cport_unpack(header); |
f587027e | 475 | |
144670c2 | 476 | if (cport_id_valid(hd, cport_id)) |
821c620a | 477 | greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, |
491e60d6 | 478 | urb->actual_length); |
821c620a AE |
479 | else |
480 | dev_err(dev, "%s: invalid cport id 0x%02x received\n", | |
481 | __func__, cport_id); | |
f587027e GKH |
482 | exit: |
483 | /* put our urb back in the request pool */ | |
484 | retval = usb_submit_urb(urb, GFP_ATOMIC); | |
485 | if (retval) | |
486 | dev_err(dev, "%s: error %d in submitting urb.\n", | |
487 | __func__, retval); | |
488 | } | |
489 | ||
490 | static void cport_out_callback(struct urb *urb) | |
491 | { | |
7cf7bca9 JH |
492 | struct gb_message *message = urb->context; |
493 | struct greybus_host_device *hd = message->operation->connection->hd; | |
f587027e GKH |
494 | struct es1_ap_dev *es1 = hd_to_es1(hd); |
495 | int status = check_urb_status(urb); | |
3e136cc9 | 496 | unsigned long flags; |
f587027e | 497 | |
d29b3d63 | 498 | gb_message_cport_clear(message->header); |
491e60d6 | 499 | |
f587027e | 500 | /* |
7cf7bca9 JH |
501 | * Tell the submitter that the message send (attempt) is |
502 | * complete, and report the status. | |
f587027e | 503 | */ |
7cf7bca9 JH |
504 | greybus_message_sent(hd, message, status); |
505 | ||
3e136cc9 JH |
506 | spin_lock_irqsave(&es1->cport_out_urb_lock, flags); |
507 | message->hcpriv = NULL; | |
508 | spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); | |
509 | ||
f587027e | 510 | free_urb(es1, urb); |
f587027e GKH |
511 | } |
512 | ||
c15ccabe JH |
513 | #define APB1_LOG_MSG_SIZE 64 |
514 | static void apb1_log_get(struct es1_ap_dev *es1, char *buf) | |
ca3ec299 | 515 | { |
ca3ec299 GKH |
516 | int retval; |
517 | ||
518 | /* SVC messages go down our control pipe */ | |
519 | do { | |
ca3ec299 GKH |
520 | retval = usb_control_msg(es1->usb_dev, |
521 | usb_rcvctrlpipe(es1->usb_dev, | |
522 | es1->control_endpoint), | |
611c1739 | 523 | REQUEST_LOG, |
ca3ec299 GKH |
524 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, |
525 | 0x00, 0x00, | |
526 | buf, | |
c15ccabe | 527 | APB1_LOG_MSG_SIZE, |
ca3ec299 GKH |
528 | ES1_TIMEOUT); |
529 | if (retval > 0) | |
530 | kfifo_in(&apb1_log_fifo, buf, retval); | |
531 | } while (retval > 0); | |
532 | } | |
533 | ||
534 | static int apb1_log_poll(void *data) | |
535 | { | |
c15ccabe JH |
536 | struct es1_ap_dev *es1 = data; |
537 | char *buf; | |
538 | ||
539 | buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL); | |
540 | if (!buf) | |
541 | return -ENOMEM; | |
542 | ||
ca3ec299 GKH |
543 | while (!kthread_should_stop()) { |
544 | msleep(1000); | |
c15ccabe | 545 | apb1_log_get(es1, buf); |
ca3ec299 | 546 | } |
c15ccabe JH |
547 | |
548 | kfree(buf); | |
549 | ||
ca3ec299 GKH |
550 | return 0; |
551 | } | |
552 | ||
553 | static ssize_t apb1_log_read(struct file *f, char __user *buf, | |
554 | size_t count, loff_t *ppos) | |
555 | { | |
556 | ssize_t ret; | |
557 | size_t copied; | |
558 | char *tmp_buf; | |
559 | ||
560 | if (count > APB1_LOG_SIZE) | |
561 | count = APB1_LOG_SIZE; | |
562 | ||
563 | tmp_buf = kmalloc(count, GFP_KERNEL); | |
564 | if (!tmp_buf) | |
565 | return -ENOMEM; | |
566 | ||
567 | copied = kfifo_out(&apb1_log_fifo, tmp_buf, count); | |
568 | ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); | |
569 | ||
570 | kfree(tmp_buf); | |
571 | ||
572 | return ret; | |
573 | } | |
574 | ||
575 | static const struct file_operations apb1_log_fops = { | |
576 | .read = apb1_log_read, | |
577 | }; | |
578 | ||
579 | static void usb_log_enable(struct es1_ap_dev *es1) | |
580 | { | |
e0feaf14 | 581 | if (!IS_ERR_OR_NULL(apb1_log_task)) |
ca3ec299 GKH |
582 | return; |
583 | ||
584 | /* get log from APB1 */ | |
585 | apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); | |
e0feaf14 | 586 | if (IS_ERR(apb1_log_task)) |
ca3ec299 GKH |
587 | return; |
588 | apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, | |
589 | gb_debugfs_get(), NULL, | |
590 | &apb1_log_fops); | |
591 | } | |
592 | ||
593 | static void usb_log_disable(struct es1_ap_dev *es1) | |
594 | { | |
e0feaf14 | 595 | if (IS_ERR_OR_NULL(apb1_log_task)) |
ca3ec299 GKH |
596 | return; |
597 | ||
598 | debugfs_remove(apb1_log_dentry); | |
599 | apb1_log_dentry = NULL; | |
600 | ||
601 | kthread_stop(apb1_log_task); | |
602 | apb1_log_task = NULL; | |
603 | } | |
604 | ||
605 | static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, | |
606 | size_t count, loff_t *ppos) | |
607 | { | |
608 | char tmp_buf[3]; | |
e0feaf14 | 609 | int enable = !IS_ERR_OR_NULL(apb1_log_task); |
ca3ec299 GKH |
610 | |
611 | sprintf(tmp_buf, "%d\n", enable); | |
612 | return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); | |
613 | } | |
614 | ||
615 | static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, | |
616 | size_t count, loff_t *ppos) | |
617 | { | |
618 | int enable; | |
619 | ssize_t retval; | |
620 | struct es1_ap_dev *es1 = (struct es1_ap_dev *)f->f_inode->i_private; | |
621 | ||
622 | retval = kstrtoint_from_user(buf, count, 10, &enable); | |
623 | if (retval) | |
624 | return retval; | |
625 | ||
626 | if (enable) | |
627 | usb_log_enable(es1); | |
628 | else | |
629 | usb_log_disable(es1); | |
630 | ||
631 | return count; | |
632 | } | |
633 | ||
634 | static const struct file_operations apb1_log_enable_fops = { | |
635 | .read = apb1_log_enable_read, | |
636 | .write = apb1_log_enable_write, | |
637 | }; | |
638 | ||
f587027e GKH |
639 | /* |
640 | * The ES1 USB Bridge device contains 4 endpoints | |
641 | * 1 Control - usual USB stuff + AP -> SVC messages | |
642 | * 1 Interrupt IN - SVC -> AP messages | |
643 | * 1 Bulk IN - CPort data in | |
644 | * 1 Bulk OUT - CPort data out | |
645 | */ | |
646 | static int ap_probe(struct usb_interface *interface, | |
647 | const struct usb_device_id *id) | |
648 | { | |
649 | struct es1_ap_dev *es1; | |
650 | struct greybus_host_device *hd; | |
651 | struct usb_device *udev; | |
652 | struct usb_host_interface *iface_desc; | |
653 | struct usb_endpoint_descriptor *endpoint; | |
606addd2 AB |
654 | int bulk_in = 0; |
655 | int bulk_out = 0; | |
f587027e GKH |
656 | int retval = -ENOMEM; |
657 | int i; | |
f587027e | 658 | |
4bc1389d AE |
659 | /* We need to fit a CPort ID in one byte of a message header */ |
660 | BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); | |
661 | ||
f587027e GKH |
662 | udev = usb_get_dev(interface_to_usbdev(interface)); |
663 | ||
144670c2 FP |
664 | hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX, |
665 | CPORT_COUNT); | |
8ea70fe0 | 666 | if (IS_ERR(hd)) { |
f587027e | 667 | usb_put_dev(udev); |
8ea70fe0 | 668 | return PTR_ERR(hd); |
f587027e GKH |
669 | } |
670 | ||
f587027e GKH |
671 | es1 = hd_to_es1(hd); |
672 | es1->hd = hd; | |
673 | es1->usb_intf = interface; | |
674 | es1->usb_dev = udev; | |
675 | spin_lock_init(&es1->cport_out_urb_lock); | |
676 | usb_set_intfdata(interface, es1); | |
677 | ||
678 | /* Control endpoint is the pipe to talk to this AP, so save it off */ | |
679 | endpoint = &udev->ep0.desc; | |
680 | es1->control_endpoint = endpoint->bEndpointAddress; | |
681 | ||
c011d558 FP |
682 | es1->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es1->cport_to_ep), |
683 | GFP_KERNEL); | |
684 | if (!es1->cport_to_ep) { | |
685 | retval = -ENOMEM; | |
686 | goto error; | |
687 | } | |
688 | ||
f587027e GKH |
689 | /* find all 3 of our endpoints */ |
690 | iface_desc = interface->cur_altsetting; | |
691 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | |
692 | endpoint = &iface_desc->endpoint[i].desc; | |
693 | ||
b767ee40 | 694 | if (usb_endpoint_is_bulk_in(endpoint)) { |
606addd2 AB |
695 | es1->cport_in[bulk_in++].endpoint = |
696 | endpoint->bEndpointAddress; | |
f587027e | 697 | } else if (usb_endpoint_is_bulk_out(endpoint)) { |
606addd2 AB |
698 | es1->cport_out[bulk_out++].endpoint = |
699 | endpoint->bEndpointAddress; | |
f587027e GKH |
700 | } else { |
701 | dev_err(&udev->dev, | |
702 | "Unknown endpoint type found, address %x\n", | |
703 | endpoint->bEndpointAddress); | |
704 | } | |
705 | } | |
b767ee40 | 706 | if ((bulk_in == 0) || |
606addd2 | 707 | (bulk_out == 0)) { |
f587027e GKH |
708 | dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); |
709 | goto error; | |
710 | } | |
711 | ||
f587027e | 712 | /* Allocate buffers for our cport in messages and start them up */ |
606addd2 AB |
713 | for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { |
714 | struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; | |
715 | for (i = 0; i < NUM_CPORT_IN_URB; ++i) { | |
716 | struct urb *urb; | |
717 | u8 *buffer; | |
718 | ||
719 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
720 | if (!urb) | |
721 | goto error; | |
722 | buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); | |
723 | if (!buffer) | |
724 | goto error; | |
725 | ||
726 | usb_fill_bulk_urb(urb, udev, | |
727 | usb_rcvbulkpipe(udev, | |
728 | cport_in->endpoint), | |
729 | buffer, ES1_GBUF_MSG_SIZE_MAX, | |
730 | cport_in_callback, hd); | |
731 | cport_in->urb[i] = urb; | |
732 | cport_in->buffer[i] = buffer; | |
733 | retval = usb_submit_urb(urb, GFP_KERNEL); | |
734 | if (retval) | |
735 | goto error; | |
736 | } | |
f587027e GKH |
737 | } |
738 | ||
739 | /* Allocate urbs for our CPort OUT messages */ | |
740 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
741 | struct urb *urb; | |
742 | ||
743 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
744 | if (!urb) | |
745 | goto error; | |
746 | ||
747 | es1->cport_out_urb[i] = urb; | |
748 | es1->cport_out_urb_busy[i] = false; /* just to be anal */ | |
749 | } | |
750 | ||
86f918ee JH |
751 | apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", |
752 | (S_IWUSR | S_IRUGO), | |
753 | gb_debugfs_get(), es1, | |
754 | &apb1_log_enable_fops); | |
f587027e GKH |
755 | return 0; |
756 | error: | |
757 | ap_disconnect(interface); | |
758 | ||
759 | return retval; | |
760 | } | |
761 | ||
762 | static struct usb_driver es1_ap_driver = { | |
c13c8bf0 | 763 | .name = "es2_ap_driver", |
f587027e GKH |
764 | .probe = ap_probe, |
765 | .disconnect = ap_disconnect, | |
766 | .id_table = id_table, | |
767 | }; | |
768 | ||
769 | module_usb_driver(es1_ap_driver); | |
770 | ||
6cf42a44 | 771 | MODULE_LICENSE("GPL v2"); |
f587027e | 772 | MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>"); |