blob: cf2987ef94e928426afc9d031ce77d7f5ccb9e43 [file] [log] [blame]
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -07001/*
2 * Greybus "AP" USB driver
3 *
4 * Copyright 2014 Google Inc.
5 *
6 * Released under the GPLv2 only.
7 */
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/slab.h>
11#include <linux/errno.h>
12#include <linux/usb.h>
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -070013#include "greybus.h"
Greg Kroah-Hartman43cc32a2014-09-07 13:51:12 -070014#include "svc_msg.h"
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -070015
16static const struct usb_device_id id_table[] = {
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070017 { USB_DEVICE(0xffff, 0x0001) }, // FIXME
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -070018 { },
19};
20MODULE_DEVICE_TABLE(usb, id_table);
21
Greg Kroah-Hartman11299732014-09-12 21:17:37 -070022/*
23 * Number of CPort IN urbs in flight at any point in time.
24 * Adjust if we are having stalls in the USB buffer due to not enough urbs in
25 * flight.
26 */
27#define NUM_CPORT_IN_URB 4
28
29/* Number of CPort OUT urbs in flight at any point in time.
30 * Adjust if we get messages saying we are out of urbs in the system log.
31 */
32#define NUM_CPORT_OUT_URB 8
33
34
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -070035struct es1_ap_dev {
36 struct usb_device *usb_dev;
37 struct usb_interface *usb_intf;
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070038 struct greybus_host_device *hd;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -070039
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -070040 __u8 control_endpoint; /* endpoint to send data to SVC */
41 __u8 svc_endpoint; /* endpoint for SVC data */
42 __u8 cport_in_endpoint; /* bulk in for CPort data */
43 __u8 cport_out_endpoint; /* bulk out for CPort data */
44 u8 *svc_buffer; /* buffer for SVC messages coming in */
45 struct urb *svc_urb; /* urb for SVC messages coming in */
Greg Kroah-Hartman11299732014-09-12 21:17:37 -070046 struct urb *cport_in_urb[NUM_CPORT_IN_URB]; /* CPort IN urbs */
47 u8 *cport_in_buffer[NUM_CPORT_IN_URB]; /* CPort IN buffers */
48 struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; /* CPort OUT urbs */
Greg Kroah-Hartman0dad95d2014-09-13 09:54:35 -070049 bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; /* CPort OUT urb busy marker */
50 spinlock_t cport_out_urb_lock; /* locks list of cport out urbs */
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -070051};
52
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070053static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd)
54{
55 return (struct es1_ap_dev *)(hd->hd_priv);
56}
57
Greg Kroah-Hartman11299732014-09-12 21:17:37 -070058static void cport_out_callback(struct urb *urb);
59
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -070060/*
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070061 * Allocate the actual buffer for this gbuf and device and cport
62 *
63 * We are responsible for setting the following fields in a struct gbuf:
64 * void *hcpriv;
65 * void *transfer_buffer;
66 * u32 transfer_buffer_length;
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -070067 */
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070068static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask)
69{
70 struct es1_ap_dev *es1 = hd_to_es1(gbuf->gdev->hd);
71 u8 *buffer;
72
73 /* For ES2 we need to figure out what cport is going to what endpoint,
74 * but for ES1, it's so dirt simple, we don't have a choice...
75 *
76 * Also, do a "slow" allocation now, if we need speed, use a cache
77 */
78 buffer = kmalloc(size + 1, gfp_mask);
79 if (!buffer)
80 return -ENOMEM;
81
82 /*
83 * we will encode the cport number in the first byte of the buffer, so
84 * set the second byte to be the "transfer buffer"
85 */
86 buffer[0] = gbuf->cport->number;
87 gbuf->transfer_buffer = &buffer[1];
88 gbuf->transfer_buffer_length = size;
89
Greg Kroah-Hartman11299732014-09-12 21:17:37 -070090 /* When we send the gbuf, we need this pointer to be here */
91 gbuf->hdpriv = es1;
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070092
93 return 0;
94}
95
96/* Free the memory we allocated with a gbuf */
97static void free_gbuf(struct gbuf *gbuf)
98{
99 u8 *transfer_buffer;
100 u8 *buffer;
101
102 transfer_buffer = gbuf->transfer_buffer;
103 buffer = &transfer_buffer[-1]; /* yes, we mean -1 */
104 kfree(buffer);
105}
106
Greg Kroah-Hartman8c53e072014-09-12 20:47:11 -0700107#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */
108static int send_svc_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd)
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700109{
110 struct es1_ap_dev *es1 = hd_to_es1(hd);
Greg Kroah-Hartman8c53e072014-09-12 20:47:11 -0700111 int retval;
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700112
Greg Kroah-Hartman8c53e072014-09-12 20:47:11 -0700113 /* SVC messages go down our control pipe */
114 retval = usb_control_msg(es1->usb_dev,
115 usb_sndctrlpipe(es1->usb_dev,
116 es1->control_endpoint),
117 0x01, /* vendor request AP message */
118 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
119 0x00, 0x00,
120 (char *)svc_msg,
121 sizeof(*svc_msg),
122 ES1_TIMEOUT);
123 if (retval != sizeof(*svc_msg))
124 return retval;
125
126 return 0;
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700127}
128
Greg Kroah-Hartman0dad95d2014-09-13 09:54:35 -0700129static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask)
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700130{
Greg Kroah-Hartman0dad95d2014-09-13 09:54:35 -0700131 struct urb *urb = NULL;
132 unsigned long flags;
133 int i;
134
135 spin_lock_irqsave(&es1->cport_out_urb_lock, flags);
136
137 /* Look in our pool of allocated urbs first, as that's the "fastest" */
138 for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
139 if (es1->cport_out_urb_busy[i] == false) {
140 es1->cport_out_urb_busy[i] = true;
141 urb = es1->cport_out_urb[i];
142 break;
143 }
144 }
145 spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags);
146 if (urb)
147 return urb;
148
149 /*
150 * Crap, pool is empty, complain to the syslog and go allocate one
151 * dynamically as we have to succeed.
152 */
153 dev_err(&es1->usb_dev->dev,
154 "No free CPort OUT urbs, having to dynamically allocate one!\n");
155 urb = usb_alloc_urb(0, gfp_mask);
156 if (!urb)
157 return NULL;
158
159 return urb;
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700160}
161
162static int send_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd,
163 gfp_t gfp_mask)
164{
165 struct es1_ap_dev *es1 = hd_to_es1(hd);
166 struct usb_device *udev = es1->usb_dev;
167 int retval;
168 u8 *transfer_buffer;
169 u8 *buffer;
170 struct urb *urb;
171
172 transfer_buffer = gbuf->transfer_buffer;
173 buffer = &transfer_buffer[-1]; /* yes, we mean -1 */
174
175 /* Find a free urb */
Greg Kroah-Hartman0dad95d2014-09-13 09:54:35 -0700176 urb = next_free_urb(es1, gfp_mask);
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700177 if (!urb)
178 return -ENOMEM;
179
180 usb_fill_bulk_urb(urb, udev,
181 usb_sndbulkpipe(udev, es1->cport_out_endpoint),
182 buffer, gbuf->transfer_buffer_length + 1,
183 cport_out_callback, gbuf);
184 retval = usb_submit_urb(urb, gfp_mask);
185 return retval;
186}
187
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700188static struct greybus_host_driver es1_driver = {
189 .hd_priv_size = sizeof(struct es1_ap_dev),
190 .alloc_gbuf = alloc_gbuf,
191 .free_gbuf = free_gbuf,
Greg Kroah-Hartman8c53e072014-09-12 20:47:11 -0700192 .send_svc_msg = send_svc_msg,
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700193 .send_gbuf = send_gbuf,
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700194};
195
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700196/* Callback for when we get a SVC message */
197static void svc_callback(struct urb *urb)
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700198{
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700199 struct es1_ap_dev *es1 = urb->context;
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700200 struct device *dev = &urb->dev->dev;
201 int status = urb->status;
202 int retval;
203
204 switch (status) {
205 case 0:
206 break;
207 case -EOVERFLOW:
208 dev_err(dev, "%s: overflow actual length is %d\n",
209 __func__, urb->actual_length);
210 case -ECONNRESET:
211 case -ENOENT:
212 case -ESHUTDOWN:
213 case -EILSEQ:
214 /* device is gone, stop sending */
215 return;
216 default:
217 dev_err(dev, "%s: unknown status %d\n", __func__, status);
218 goto exit;
219 }
220
221 /* We have a message, create a new message structure, add it to the
222 * list, and wake up our thread that will process the messages.
223 */
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700224 gb_new_ap_msg(urb->transfer_buffer, urb->actual_length, es1->hd);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700225
226exit:
227 /* resubmit the urb to get more messages */
228 retval = usb_submit_urb(urb, GFP_ATOMIC);
229 if (retval)
230 dev_err(dev, "Can not submit urb for AP data: %d\n", retval);
231}
232
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700233static void cport_in_callback(struct urb *urb)
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700234{
235 struct device *dev = &urb->dev->dev;
236 int status = urb->status;
237
238 switch (status) {
239 case 0:
240 break;
241 case -EOVERFLOW:
242 dev_err(dev, "%s: overflow actual length is %d\n",
243 __func__, urb->actual_length);
244 case -ECONNRESET:
245 case -ENOENT:
246 case -ESHUTDOWN:
247 case -EILSEQ:
248 /* device is gone, stop sending */
249 return;
250 default:
251 dev_err(dev, "%s: unknown status %d\n", __func__, status);
252 goto exit;
253 }
254
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700255 // FIXME - handle the CPort in data
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700256exit:
257 return;
258}
259
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700260static void cport_out_callback(struct urb *urb)
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700261{
262 struct device *dev = &urb->dev->dev;
263 int status = urb->status;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700264
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700265 switch (status) {
266 case 0:
267 break;
268 case -EOVERFLOW:
269 dev_err(dev, "%s: overflow actual length is %d\n",
270 __func__, urb->actual_length);
271 case -ECONNRESET:
272 case -ENOENT:
273 case -ESHUTDOWN:
274 case -EILSEQ:
275 /* device is gone, stop sending */
276 return;
277 default:
278 dev_err(dev, "%s: unknown status %d\n", __func__, status);
279 goto exit;
280 }
281
282 // FIXME - handle the CPort out data callback
283exit:
284 return;
285}
286
287/*
288 * The ES1 USB Bridge device contains 4 endpoints
289 * 1 Control - usual USB stuff + AP -> SVC messages
290 * 1 Interrupt IN - SVC -> AP messages
291 * 1 Bulk IN - CPort data in
292 * 1 Bulk OUT - CPorta data out
293 */
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700294static int ap_probe(struct usb_interface *interface,
295 const struct usb_device_id *id)
296{
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700297 struct es1_ap_dev *es1;
298 struct greybus_host_device *hd;
299 struct usb_device *udev;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700300 struct usb_host_interface *iface_desc;
301 struct usb_endpoint_descriptor *endpoint;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700302 bool int_in_found = false;
303 bool bulk_in_found = false;
304 bool bulk_out_found = false;
305 int retval = -ENOMEM;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700306 int i;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700307 int buffer_size = 0;
308 u8 svc_interval = 0;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700309
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700310 udev = usb_get_dev(interface_to_usbdev(interface));
311
312 hd = greybus_create_hd(&es1_driver, &udev->dev);
313 if (!hd)
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -0700314 return -ENOMEM;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700315
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700316 es1 = hd_to_es1(hd);
317 es1->hd = hd;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700318 es1->usb_intf = interface;
319 es1->usb_dev = udev;
Greg Kroah-Hartman0dad95d2014-09-13 09:54:35 -0700320 spin_lock_init(&es1->cport_out_urb_lock);
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700321 usb_set_intfdata(interface, es1);
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700322
323 /* Control endpoint is the pipe to talk to this AP, so save it off */
324 endpoint = &udev->ep0.desc;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700325 es1->control_endpoint = endpoint->bEndpointAddress;
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700326
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700327 /* find all 3 of our endpoints */
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700328 iface_desc = interface->cur_altsetting;
329 for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
330 endpoint = &iface_desc->endpoint[i].desc;
331
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700332 if (usb_endpoint_is_int_in(endpoint)) {
333 es1->svc_endpoint = endpoint->bEndpointAddress;
334 buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
335 svc_interval = endpoint->bInterval;
336 int_in_found = true;
337 } else if (usb_endpoint_is_bulk_in(endpoint)) {
338 es1->cport_in_endpoint = endpoint->bEndpointAddress;
339 bulk_in_found = true;
340 } else if (usb_endpoint_is_bulk_out(endpoint)) {
341 es1->cport_out_endpoint = endpoint->bEndpointAddress;
342 bulk_out_found = true;
343 } else {
344 dev_err(&udev->dev,
345 "Unknown endpoint type found, address %x\n",
346 endpoint->bEndpointAddress);
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700347 }
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700348 }
349 if ((int_in_found == false) ||
350 (bulk_in_found == false) ||
351 (bulk_out_found == false)) {
352 dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
353 goto error;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700354 }
355
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700356 /* Create our buffer and URB to get SVC messages, and start it up */
357 es1->svc_buffer = kmalloc(buffer_size, GFP_KERNEL);
358 if (!es1->svc_buffer)
359 goto error;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700360
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700361 es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL);
362 if (!es1->svc_urb)
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700363 goto error_int_urb;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700364
365 usb_fill_int_urb(es1->svc_urb, udev,
366 usb_rcvintpipe(udev, es1->svc_endpoint),
367 es1->svc_buffer, buffer_size, svc_callback,
368 es1, svc_interval);
369 retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL);
370 if (retval)
371 goto error_submit_urb;
372
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700373 /* Allocate buffers for our cport in messages and start them up */
374 for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
375 struct urb *urb;
376 u8 *buffer;
377
378 urb = usb_alloc_urb(0, GFP_KERNEL);
379 if (!urb)
380 goto error_bulk_in_urb;
381 buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
382 if (!buffer)
383 goto error_bulk_in_urb;
384
385 usb_fill_bulk_urb(urb, udev,
386 usb_rcvbulkpipe(udev, es1->cport_in_endpoint),
387 buffer, PAGE_SIZE, cport_in_callback, es1);
388 es1->cport_in_urb[i] = urb;
389 es1->cport_in_buffer[i] = buffer;
390 retval = usb_submit_urb(urb, GFP_KERNEL);
391 if (retval)
392 goto error_bulk_in_urb;
393 }
394
395 /* Allocate urbs for our CPort OUT messages */
396 for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
397 struct urb *urb;
398
399 urb = usb_alloc_urb(0, GFP_KERNEL);
400 if (!urb)
401 goto error_bulk_out_urb;
402
403 es1->cport_out_urb[i] = urb;
404 es1->cport_out_urb_busy[i] = false; /* just to be anal */
405 }
406
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700407 return 0;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700408
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700409error_bulk_out_urb:
410 for (i = 0; i < NUM_CPORT_OUT_URB; ++i)
411 usb_free_urb(es1->cport_out_urb[i]);
412
413error_bulk_in_urb:
414 for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
415 usb_kill_urb(es1->cport_in_urb[i]);
416 usb_free_urb(es1->cport_in_urb[i]);
417 kfree(es1->cport_in_buffer[i]);
418 }
419
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700420error_submit_urb:
421 usb_free_urb(es1->svc_urb);
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700422error_int_urb:
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700423 kfree(es1->svc_buffer);
424error:
425 greybus_remove_hd(es1->hd);
426 return retval;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700427}
428
429static void ap_disconnect(struct usb_interface *interface)
430{
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700431 struct es1_ap_dev *es1;
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700432 int i;
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700433
434 es1 = usb_get_intfdata(interface);
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700435 if (!es1)
436 return;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700437
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -0700438 /* Tear down everything! */
Greg Kroah-Hartman11299732014-09-12 21:17:37 -0700439 for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
440 usb_kill_urb(es1->cport_out_urb[i]);
441 usb_free_urb(es1->cport_out_urb[i]);
442 }
443
444 for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
445 usb_kill_urb(es1->cport_in_urb[i]);
446 usb_free_urb(es1->cport_in_urb[i]);
447 kfree(es1->cport_in_buffer[i]);
448 }
449
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700450 usb_kill_urb(es1->svc_urb);
Greg Kroah-Hartmand9d077f2014-09-08 20:11:18 -0700451 usb_free_urb(es1->svc_urb);
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700452 usb_put_dev(es1->usb_dev);
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700453 kfree(es1->svc_buffer);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700454 greybus_remove_hd(es1->hd);
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700455 usb_set_intfdata(interface, NULL);
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700456}
457
458static struct usb_driver es1_ap_driver = {
459 .name = "es1_ap_driver",
460 .probe = ap_probe,
461 .disconnect = ap_disconnect,
462 .id_table = id_table,
463};
464
465module_usb_driver(es1_ap_driver);
466
467MODULE_LICENSE("GPL");
468MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");