blob: 767981a248c832bb3e4fdc84cb73325843db25ef [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-Hartman6f83ab72014-08-30 17:30:04 -070022struct es1_ap_dev {
23 struct usb_device *usb_dev;
24 struct usb_interface *usb_intf;
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070025 struct greybus_host_device *hd;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -070026
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -070027 __u8 control_endpoint; /* endpoint to send data to SVC */
28 __u8 svc_endpoint; /* endpoint for SVC data */
29 __u8 cport_in_endpoint; /* bulk in for CPort data */
30 __u8 cport_out_endpoint; /* bulk out for CPort data */
31 u8 *svc_buffer; /* buffer for SVC messages coming in */
32 struct urb *svc_urb; /* urb for SVC messages coming in */
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -070033};
34
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070035static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd)
36{
37 return (struct es1_ap_dev *)(hd->hd_priv);
38}
39
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -070040/*
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070041 * Allocate the actual buffer for this gbuf and device and cport
42 *
43 * We are responsible for setting the following fields in a struct gbuf:
44 * void *hcpriv;
45 * void *transfer_buffer;
46 * u32 transfer_buffer_length;
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -070047 */
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -070048static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask)
49{
50 struct es1_ap_dev *es1 = hd_to_es1(gbuf->gdev->hd);
51 u8 *buffer;
52
53 /* For ES2 we need to figure out what cport is going to what endpoint,
54 * but for ES1, it's so dirt simple, we don't have a choice...
55 *
56 * Also, do a "slow" allocation now, if we need speed, use a cache
57 */
58 buffer = kmalloc(size + 1, gfp_mask);
59 if (!buffer)
60 return -ENOMEM;
61
62 /*
63 * we will encode the cport number in the first byte of the buffer, so
64 * set the second byte to be the "transfer buffer"
65 */
66 buffer[0] = gbuf->cport->number;
67 gbuf->transfer_buffer = &buffer[1];
68 gbuf->transfer_buffer_length = size;
69
70 gbuf->hdpriv = es1; /* really, we could do something else here... */
71
72 return 0;
73}
74
75/* Free the memory we allocated with a gbuf */
76static void free_gbuf(struct gbuf *gbuf)
77{
78 u8 *transfer_buffer;
79 u8 *buffer;
80
81 transfer_buffer = gbuf->transfer_buffer;
82 buffer = &transfer_buffer[-1]; /* yes, we mean -1 */
83 kfree(buffer);
84}
85
Greg Kroah-Hartman43cc32a2014-09-07 13:51:12 -070086static struct svc_msg *svc_msg_alloc(enum svc_function_type type)
87{
88 struct svc_msg *svc_msg;
89
90 svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL);
91 if (!svc_msg)
92 return NULL;
93
94 // FIXME - verify we are only sending message types we should be
95 svc_msg->header.type = type;
96 return svc_msg;
97}
98
99static void svc_msg_free(struct svc_msg *svc_msg)
100{
101 kfree(svc_msg);
102}
103
104static int svc_msg_send(struct svc_msg *svc_msg)
105{
106 // FIXME - Do something with this message!
107
108
109 svc_msg_free(svc_msg);
110 return 0;
111}
112
113
114static void svc_handshake(struct svc_function_handshake *handshake,
115 struct es1_ap_dev *es1)
116{
117 struct svc_msg *svc_msg;
118
119 /* A new SVC communication channel, let's verify it was for us */
120 if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) {
121 /* we don't know what to do with this, log it and return */
122 dev_dbg(&es1->usb_intf->dev,
123 "received invalid handshake type %d\n",
124 handshake->handshake_type);
125 return;
126 }
127
128 /* Send back a AP_HELLO message */
129 svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE);
130 if (!svc_msg)
131 return;
132
133 svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO;
134 svc_msg_send(svc_msg);
135}
136
137static void svc_management(struct svc_function_unipro_management *management,
138 struct es1_ap_dev *es1)
139{
140 /* What? An AP should not get this message */
141 dev_err(&es1->usb_intf->dev, "Got an svc management message???\n");
142}
143
144static void svc_hotplug(struct svc_function_hotplug *hotplug,
145 struct es1_ap_dev *es1)
146{
147 u8 module_id = hotplug->module_id;
148
149 switch (hotplug->hotplug_event) {
150 case SVC_HOTPLUG_EVENT:
151 dev_dbg(&es1->usb_intf->dev, "module id %d added\n",
152 module_id);
153 // FIXME - add the module to the system
154 break;
155
156 case SVC_HOTUNPLUG_EVENT:
157 dev_dbg(&es1->usb_intf->dev, "module id %d removed\n",
158 module_id);
159 // FIXME - remove the module from the system
160 break;
161
162 default:
163 dev_err(&es1->usb_intf->dev, "received invalid hotplug message type %d\n",
164 hotplug->hotplug_event);
165 break;
166 }
167}
168
169static void svc_ddb(struct svc_function_ddb *ddb, struct es1_ap_dev *es1)
170{
171 /* What? An AP should not get this message */
172 dev_err(&es1->usb_intf->dev, "Got an svc DDB message???\n");
173}
174
175static void svc_power(struct svc_function_power *power, struct es1_ap_dev *es1)
176{
177 u8 module_id = power->module_id;
178
179 if (power->power_type != SVC_POWER_BATTERY_STATUS) {
180 dev_err(&es1->usb_intf->dev, "received invalid power type %d\n",
181 power->power_type);
182 return;
183 }
184
185 dev_dbg(&es1->usb_intf->dev, "power status for module id %d is %d\n",
186 module_id, power->status.status);
187
188 // FIXME - do something with the power information, like update our
189 // battery information...
190}
191
192static void svc_epm(struct svc_function_epm *epm, struct es1_ap_dev *es1)
193{
194 /* What? An AP should not get this message */
195 dev_err(&es1->usb_intf->dev, "Got an EPM message???\n");
196}
197
198static void svc_suspend(struct svc_function_suspend *suspend,
199 struct es1_ap_dev *es1)
200{
201 /* What? An AP should not get this message */
202 dev_err(&es1->usb_intf->dev, "Got an suspend message???\n");
203}
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700204
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700205/* Main message loop for ap messages */
206/* Odds are, most of this logic can move to core.c someday, but as we only have
207 * one host controller driver for now, let's leave it here */
208static void ap_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd)
209{
210 struct es1_ap_dev *es1 = hd_to_es1(hd);
211
212 /* Look at the message to figure out what to do with it */
Greg Kroah-Hartman43cc32a2014-09-07 13:51:12 -0700213 switch (svc_msg->header.type) {
214 case SVC_FUNCTION_HANDSHAKE:
215 svc_handshake(&svc_msg->handshake, es1);
216 break;
217 case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT:
218 svc_management(&svc_msg->management, es1);
219 break;
220 case SVC_FUNCTION_HOTPLUG:
221 svc_hotplug(&svc_msg->hotplug, es1);
222 break;
223 case SVC_FUNCTION_DDB:
224 svc_ddb(&svc_msg->ddb, es1);
225 break;
226 case SVC_FUNCTION_POWER:
227 svc_power(&svc_msg->power, es1);
228 break;
229 case SVC_FUNCTION_EPM:
230 svc_epm(&svc_msg->epm, es1);
231 break;
232 case SVC_FUNCTION_SUSPEND:
233 svc_suspend(&svc_msg->suspend, es1);
234 break;
235 default:
236 dev_err(&es1->usb_intf->dev, "received invalid SVC message type %d\n",
237 svc_msg->header.type);
238 }
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700239}
240
241
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700242static struct greybus_host_driver es1_driver = {
243 .hd_priv_size = sizeof(struct es1_ap_dev),
244 .alloc_gbuf = alloc_gbuf,
245 .free_gbuf = free_gbuf,
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700246 .ap_msg = ap_msg,
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700247};
248
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700249/* Callback for when we get a SVC message */
250static void svc_callback(struct urb *urb)
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700251{
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700252 struct es1_ap_dev *es1 = urb->context;
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700253 struct device *dev = &urb->dev->dev;
254 int status = urb->status;
255 int retval;
256
257 switch (status) {
258 case 0:
259 break;
260 case -EOVERFLOW:
261 dev_err(dev, "%s: overflow actual length is %d\n",
262 __func__, urb->actual_length);
263 case -ECONNRESET:
264 case -ENOENT:
265 case -ESHUTDOWN:
266 case -EILSEQ:
267 /* device is gone, stop sending */
268 return;
269 default:
270 dev_err(dev, "%s: unknown status %d\n", __func__, status);
271 goto exit;
272 }
273
274 /* We have a message, create a new message structure, add it to the
275 * list, and wake up our thread that will process the messages.
276 */
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700277 gb_new_ap_msg(urb->transfer_buffer, urb->actual_length, es1->hd);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700278
279exit:
280 /* resubmit the urb to get more messages */
281 retval = usb_submit_urb(urb, GFP_ATOMIC);
282 if (retval)
283 dev_err(dev, "Can not submit urb for AP data: %d\n", retval);
284}
285
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700286void cport_in_callback(struct urb *urb)
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700287{
288 struct device *dev = &urb->dev->dev;
289 int status = urb->status;
290
291 switch (status) {
292 case 0:
293 break;
294 case -EOVERFLOW:
295 dev_err(dev, "%s: overflow actual length is %d\n",
296 __func__, urb->actual_length);
297 case -ECONNRESET:
298 case -ENOENT:
299 case -ESHUTDOWN:
300 case -EILSEQ:
301 /* device is gone, stop sending */
302 return;
303 default:
304 dev_err(dev, "%s: unknown status %d\n", __func__, status);
305 goto exit;
306 }
307
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700308 // FIXME - handle the CPort in data
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700309exit:
310 return;
311}
312
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700313void cport_out_callback(struct urb *urb)
314{
315 struct device *dev = &urb->dev->dev;
316 int status = urb->status;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700317
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700318 switch (status) {
319 case 0:
320 break;
321 case -EOVERFLOW:
322 dev_err(dev, "%s: overflow actual length is %d\n",
323 __func__, urb->actual_length);
324 case -ECONNRESET:
325 case -ENOENT:
326 case -ESHUTDOWN:
327 case -EILSEQ:
328 /* device is gone, stop sending */
329 return;
330 default:
331 dev_err(dev, "%s: unknown status %d\n", __func__, status);
332 goto exit;
333 }
334
335 // FIXME - handle the CPort out data callback
336exit:
337 return;
338}
339
340/*
341 * The ES1 USB Bridge device contains 4 endpoints
342 * 1 Control - usual USB stuff + AP -> SVC messages
343 * 1 Interrupt IN - SVC -> AP messages
344 * 1 Bulk IN - CPort data in
345 * 1 Bulk OUT - CPorta data out
346 */
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700347static int ap_probe(struct usb_interface *interface,
348 const struct usb_device_id *id)
349{
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700350 struct es1_ap_dev *es1;
351 struct greybus_host_device *hd;
352 struct usb_device *udev;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700353 struct usb_host_interface *iface_desc;
354 struct usb_endpoint_descriptor *endpoint;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700355 bool int_in_found = false;
356 bool bulk_in_found = false;
357 bool bulk_out_found = false;
358 int retval = -ENOMEM;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700359 int i;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700360 int buffer_size = 0;
361 u8 svc_interval = 0;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700362
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700363 udev = usb_get_dev(interface_to_usbdev(interface));
364
365 hd = greybus_create_hd(&es1_driver, &udev->dev);
366 if (!hd)
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -0700367 return -ENOMEM;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700368
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700369 es1 = hd_to_es1(hd);
370 es1->hd = hd;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700371 es1->usb_intf = interface;
372 es1->usb_dev = udev;
373 usb_set_intfdata(interface, es1);
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700374
375 /* Control endpoint is the pipe to talk to this AP, so save it off */
376 endpoint = &udev->ep0.desc;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700377 es1->control_endpoint = endpoint->bEndpointAddress;
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700378
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700379 /* find all 3 of our endpoints */
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700380 iface_desc = interface->cur_altsetting;
381 for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
382 endpoint = &iface_desc->endpoint[i].desc;
383
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700384 if (usb_endpoint_is_int_in(endpoint)) {
385 es1->svc_endpoint = endpoint->bEndpointAddress;
386 buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
387 svc_interval = endpoint->bInterval;
388 int_in_found = true;
389 } else if (usb_endpoint_is_bulk_in(endpoint)) {
390 es1->cport_in_endpoint = endpoint->bEndpointAddress;
391 bulk_in_found = true;
392 } else if (usb_endpoint_is_bulk_out(endpoint)) {
393 es1->cport_out_endpoint = endpoint->bEndpointAddress;
394 bulk_out_found = true;
395 } else {
396 dev_err(&udev->dev,
397 "Unknown endpoint type found, address %x\n",
398 endpoint->bEndpointAddress);
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700399 }
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700400 }
401 if ((int_in_found == false) ||
402 (bulk_in_found == false) ||
403 (bulk_out_found == false)) {
404 dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
405 goto error;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700406 }
407
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700408 /* Create our buffer and URB to get SVC messages, and start it up */
409 es1->svc_buffer = kmalloc(buffer_size, GFP_KERNEL);
410 if (!es1->svc_buffer)
411 goto error;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700412
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700413 es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL);
414 if (!es1->svc_urb)
415 goto error_urb;
416
417 usb_fill_int_urb(es1->svc_urb, udev,
418 usb_rcvintpipe(udev, es1->svc_endpoint),
419 es1->svc_buffer, buffer_size, svc_callback,
420 es1, svc_interval);
421 retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL);
422 if (retval)
423 goto error_submit_urb;
424
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700425 return 0;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700426
427error_submit_urb:
428 usb_free_urb(es1->svc_urb);
429error_urb:
430 kfree(es1->svc_buffer);
431error:
432 greybus_remove_hd(es1->hd);
433 return retval;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700434}
435
436static void ap_disconnect(struct usb_interface *interface)
437{
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700438 struct es1_ap_dev *es1;
439
440 es1 = usb_get_intfdata(interface);
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700441
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -0700442 /* Tear down everything! */
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700443 usb_kill_urb(es1->svc_urb);
Greg Kroah-Hartmand9d077f2014-09-08 20:11:18 -0700444 usb_free_urb(es1->svc_urb);
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700445 usb_put_dev(es1->usb_dev);
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700446 kfree(es1->svc_buffer);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700447 greybus_remove_hd(es1->hd);
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700448 usb_set_intfdata(interface, NULL);
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700449}
450
451static struct usb_driver es1_ap_driver = {
452 .name = "es1_ap_driver",
453 .probe = ap_probe,
454 .disconnect = ap_disconnect,
455 .id_table = id_table,
456};
457
458module_usb_driver(es1_ap_driver);
459
460MODULE_LICENSE("GPL");
461MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");