blob: f32023e7f22e215f798e66255106ef2fe377bc77 [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-Hartman8c53e072014-09-12 20:47:11 -070086#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */
87static int send_svc_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd)
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -070088{
89 struct es1_ap_dev *es1 = hd_to_es1(hd);
Greg Kroah-Hartman8c53e072014-09-12 20:47:11 -070090 int retval;
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -070091
Greg Kroah-Hartman8c53e072014-09-12 20:47:11 -070092 /* SVC messages go down our control pipe */
93 retval = usb_control_msg(es1->usb_dev,
94 usb_sndctrlpipe(es1->usb_dev,
95 es1->control_endpoint),
96 0x01, /* vendor request AP message */
97 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
98 0x00, 0x00,
99 (char *)svc_msg,
100 sizeof(*svc_msg),
101 ES1_TIMEOUT);
102 if (retval != sizeof(*svc_msg))
103 return retval;
104
105 return 0;
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700106}
107
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700108static struct greybus_host_driver es1_driver = {
109 .hd_priv_size = sizeof(struct es1_ap_dev),
110 .alloc_gbuf = alloc_gbuf,
111 .free_gbuf = free_gbuf,
Greg Kroah-Hartman8c53e072014-09-12 20:47:11 -0700112 .send_svc_msg = send_svc_msg,
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700113};
114
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700115/* Callback for when we get a SVC message */
116static void svc_callback(struct urb *urb)
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700117{
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700118 struct es1_ap_dev *es1 = urb->context;
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700119 struct device *dev = &urb->dev->dev;
120 int status = urb->status;
121 int retval;
122
123 switch (status) {
124 case 0:
125 break;
126 case -EOVERFLOW:
127 dev_err(dev, "%s: overflow actual length is %d\n",
128 __func__, urb->actual_length);
129 case -ECONNRESET:
130 case -ENOENT:
131 case -ESHUTDOWN:
132 case -EILSEQ:
133 /* device is gone, stop sending */
134 return;
135 default:
136 dev_err(dev, "%s: unknown status %d\n", __func__, status);
137 goto exit;
138 }
139
140 /* We have a message, create a new message structure, add it to the
141 * list, and wake up our thread that will process the messages.
142 */
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700143 gb_new_ap_msg(urb->transfer_buffer, urb->actual_length, es1->hd);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700144
145exit:
146 /* resubmit the urb to get more messages */
147 retval = usb_submit_urb(urb, GFP_ATOMIC);
148 if (retval)
149 dev_err(dev, "Can not submit urb for AP data: %d\n", retval);
150}
151
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700152void cport_in_callback(struct urb *urb)
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700153{
154 struct device *dev = &urb->dev->dev;
155 int status = urb->status;
156
157 switch (status) {
158 case 0:
159 break;
160 case -EOVERFLOW:
161 dev_err(dev, "%s: overflow actual length is %d\n",
162 __func__, urb->actual_length);
163 case -ECONNRESET:
164 case -ENOENT:
165 case -ESHUTDOWN:
166 case -EILSEQ:
167 /* device is gone, stop sending */
168 return;
169 default:
170 dev_err(dev, "%s: unknown status %d\n", __func__, status);
171 goto exit;
172 }
173
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700174 // FIXME - handle the CPort in data
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700175exit:
176 return;
177}
178
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700179void cport_out_callback(struct urb *urb)
180{
181 struct device *dev = &urb->dev->dev;
182 int status = urb->status;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700183
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700184 switch (status) {
185 case 0:
186 break;
187 case -EOVERFLOW:
188 dev_err(dev, "%s: overflow actual length is %d\n",
189 __func__, urb->actual_length);
190 case -ECONNRESET:
191 case -ENOENT:
192 case -ESHUTDOWN:
193 case -EILSEQ:
194 /* device is gone, stop sending */
195 return;
196 default:
197 dev_err(dev, "%s: unknown status %d\n", __func__, status);
198 goto exit;
199 }
200
201 // FIXME - handle the CPort out data callback
202exit:
203 return;
204}
205
206/*
207 * The ES1 USB Bridge device contains 4 endpoints
208 * 1 Control - usual USB stuff + AP -> SVC messages
209 * 1 Interrupt IN - SVC -> AP messages
210 * 1 Bulk IN - CPort data in
211 * 1 Bulk OUT - CPorta data out
212 */
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700213static int ap_probe(struct usb_interface *interface,
214 const struct usb_device_id *id)
215{
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700216 struct es1_ap_dev *es1;
217 struct greybus_host_device *hd;
218 struct usb_device *udev;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700219 struct usb_host_interface *iface_desc;
220 struct usb_endpoint_descriptor *endpoint;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700221 bool int_in_found = false;
222 bool bulk_in_found = false;
223 bool bulk_out_found = false;
224 int retval = -ENOMEM;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700225 int i;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700226 int buffer_size = 0;
227 u8 svc_interval = 0;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700228
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700229 udev = usb_get_dev(interface_to_usbdev(interface));
230
231 hd = greybus_create_hd(&es1_driver, &udev->dev);
232 if (!hd)
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -0700233 return -ENOMEM;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700234
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700235 es1 = hd_to_es1(hd);
236 es1->hd = hd;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700237 es1->usb_intf = interface;
238 es1->usb_dev = udev;
239 usb_set_intfdata(interface, es1);
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700240
241 /* Control endpoint is the pipe to talk to this AP, so save it off */
242 endpoint = &udev->ep0.desc;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700243 es1->control_endpoint = endpoint->bEndpointAddress;
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700244
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700245 /* find all 3 of our endpoints */
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700246 iface_desc = interface->cur_altsetting;
247 for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
248 endpoint = &iface_desc->endpoint[i].desc;
249
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700250 if (usb_endpoint_is_int_in(endpoint)) {
251 es1->svc_endpoint = endpoint->bEndpointAddress;
252 buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
253 svc_interval = endpoint->bInterval;
254 int_in_found = true;
255 } else if (usb_endpoint_is_bulk_in(endpoint)) {
256 es1->cport_in_endpoint = endpoint->bEndpointAddress;
257 bulk_in_found = true;
258 } else if (usb_endpoint_is_bulk_out(endpoint)) {
259 es1->cport_out_endpoint = endpoint->bEndpointAddress;
260 bulk_out_found = true;
261 } else {
262 dev_err(&udev->dev,
263 "Unknown endpoint type found, address %x\n",
264 endpoint->bEndpointAddress);
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700265 }
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700266 }
267 if ((int_in_found == false) ||
268 (bulk_in_found == false) ||
269 (bulk_out_found == false)) {
270 dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
271 goto error;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700272 }
273
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700274 /* Create our buffer and URB to get SVC messages, and start it up */
275 es1->svc_buffer = kmalloc(buffer_size, GFP_KERNEL);
276 if (!es1->svc_buffer)
277 goto error;
Greg Kroah-Hartman6f83ab72014-08-30 17:30:04 -0700278
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700279 es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL);
280 if (!es1->svc_urb)
281 goto error_urb;
282
283 usb_fill_int_urb(es1->svc_urb, udev,
284 usb_rcvintpipe(udev, es1->svc_endpoint),
285 es1->svc_buffer, buffer_size, svc_callback,
286 es1, svc_interval);
287 retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL);
288 if (retval)
289 goto error_submit_urb;
290
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700291 return 0;
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700292
293error_submit_urb:
294 usb_free_urb(es1->svc_urb);
295error_urb:
296 kfree(es1->svc_buffer);
297error:
298 greybus_remove_hd(es1->hd);
299 return retval;
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700300}
301
302static void ap_disconnect(struct usb_interface *interface)
303{
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700304 struct es1_ap_dev *es1;
305
306 es1 = usb_get_intfdata(interface);
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700307
Greg Kroah-Hartmanf1eec302014-08-30 17:18:14 -0700308 /* Tear down everything! */
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700309 usb_kill_urb(es1->svc_urb);
Greg Kroah-Hartmand9d077f2014-09-08 20:11:18 -0700310 usb_free_urb(es1->svc_urb);
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700311 usb_put_dev(es1->usb_dev);
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700312 kfree(es1->svc_buffer);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700313 greybus_remove_hd(es1->hd);
Greg Kroah-Hartman47f6ef12014-09-08 20:09:08 -0700314 usb_set_intfdata(interface, NULL);
Greg Kroah-Hartmanba4468d42014-08-30 17:06:54 -0700315}
316
317static struct usb_driver es1_ap_driver = {
318 .name = "es1_ap_driver",
319 .probe = ap_probe,
320 .disconnect = ap_disconnect,
321 .id_table = id_table,
322};
323
324module_usb_driver(es1_ap_driver);
325
326MODULE_LICENSE("GPL");
327MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");