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 | 15 | |
8e2b7daa | 16 | #include "es2.h" |
f587027e | 17 | #include "greybus.h" |
f587027e | 18 | #include "kernel_ver.h" |
3f2a809e | 19 | #include "connection.h" |
6872c461 | 20 | #include "greybus_trace.h" |
f587027e | 21 | |
4b1d8204 AE |
22 | /* Memory sizes for the buffers sent to/from the ES2 controller */ |
23 | #define ES2_GBUF_MSG_SIZE_MAX 2048 | |
f587027e GKH |
24 | |
25 | static const struct usb_device_id id_table[] = { | |
8f0a654f | 26 | { USB_DEVICE(0x18d1, 0x1eaf) }, |
f587027e GKH |
27 | { }, |
28 | }; | |
29 | MODULE_DEVICE_TABLE(usb, id_table); | |
30 | ||
ca3ec299 | 31 | #define APB1_LOG_SIZE SZ_16K |
ca3ec299 | 32 | |
606addd2 AB |
33 | /* Number of bulk in and bulk out couple */ |
34 | #define NUM_BULKS 7 | |
35 | ||
f587027e GKH |
36 | /* |
37 | * Number of CPort IN urbs in flight at any point in time. | |
38 | * Adjust if we are having stalls in the USB buffer due to not enough urbs in | |
39 | * flight. | |
40 | */ | |
41 | #define NUM_CPORT_IN_URB 4 | |
42 | ||
43 | /* Number of CPort OUT urbs in flight at any point in time. | |
44 | * Adjust if we get messages saying we are out of urbs in the system log. | |
45 | */ | |
606addd2 | 46 | #define NUM_CPORT_OUT_URB (8 * NUM_BULKS) |
f587027e | 47 | |
611c1739 AB |
48 | /* vendor request APB1 log */ |
49 | #define REQUEST_LOG 0x02 | |
50 | ||
fc1a536e AB |
51 | /* vendor request to map a cport to bulk in and bulk out endpoints */ |
52 | #define REQUEST_EP_MAPPING 0x03 | |
53 | ||
24a6112f FP |
54 | /* vendor request to get the number of cports available */ |
55 | #define REQUEST_CPORT_COUNT 0x04 | |
56 | ||
82ee1e6c FP |
57 | /* vendor request to reset a cport state */ |
58 | #define REQUEST_RESET_CPORT 0x05 | |
59 | ||
608ab2fe BD |
60 | /* vendor request to time the latency of messages on a given cport */ |
61 | #define REQUEST_LATENCY_TAG_EN 0x06 | |
62 | #define REQUEST_LATENCY_TAG_DIS 0x07 | |
63 | ||
8e2b7daa LP |
64 | /* vendor request to control the CSI transmitter */ |
65 | #define REQUEST_CSI_TX_CONTROL 0x08 | |
66 | ||
ddc09acd AB |
67 | /* |
68 | * @endpoint: bulk in endpoint for CPort data | |
69 | * @urb: array of urbs for the CPort in messages | |
70 | * @buffer: array of buffers for the @cport_in_urb urbs | |
71 | */ | |
4b1d8204 | 72 | struct es2_cport_in { |
ddc09acd AB |
73 | __u8 endpoint; |
74 | struct urb *urb[NUM_CPORT_IN_URB]; | |
75 | u8 *buffer[NUM_CPORT_IN_URB]; | |
76 | }; | |
77 | ||
78 | /* | |
79 | * @endpoint: bulk out endpoint for CPort data | |
80 | */ | |
4b1d8204 | 81 | struct es2_cport_out { |
ddc09acd AB |
82 | __u8 endpoint; |
83 | }; | |
84 | ||
f587027e | 85 | /** |
4b1d8204 | 86 | * es2_ap_dev - ES2 USB Bridge to AP structure |
f587027e GKH |
87 | * @usb_dev: pointer to the USB device we are. |
88 | * @usb_intf: pointer to the USB interface we are bound to. | |
2537636a | 89 | * @hd: pointer to our gb_host_device structure |
ddc09acd | 90 | |
ddc09acd AB |
91 | * @cport_in: endpoint, urbs and buffer for cport in messages |
92 | * @cport_out: endpoint for for cport out messages | |
f587027e GKH |
93 | * @cport_out_urb: array of urbs for the CPort out messages |
94 | * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or | |
95 | * not. | |
3e136cc9 JH |
96 | * @cport_out_urb_cancelled: array of flags indicating whether the |
97 | * corresponding @cport_out_urb is being cancelled | |
f587027e | 98 | * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" |
1482b3e1 | 99 | * |
3be0e17d AE |
100 | * @apb_log_task: task pointer for logging thread |
101 | * @apb_log_dentry: file system entry for the log file interface | |
102 | * @apb_log_enable_dentry: file system entry for enabling logging | |
103 | * @apb_log_fifo: kernel FIFO to carry logged data | |
f587027e | 104 | */ |
4b1d8204 | 105 | struct es2_ap_dev { |
f587027e GKH |
106 | struct usb_device *usb_dev; |
107 | struct usb_interface *usb_intf; | |
2537636a | 108 | struct gb_host_device *hd; |
f587027e | 109 | |
4b1d8204 AE |
110 | struct es2_cport_in cport_in[NUM_BULKS]; |
111 | struct es2_cport_out cport_out[NUM_BULKS]; | |
f587027e GKH |
112 | struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; |
113 | bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; | |
3e136cc9 | 114 | bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; |
f587027e | 115 | spinlock_t cport_out_urb_lock; |
fc1a536e | 116 | |
c011d558 | 117 | int *cport_to_ep; |
1482b3e1 | 118 | |
3be0e17d AE |
119 | struct task_struct *apb_log_task; |
120 | struct dentry *apb_log_dentry; | |
121 | struct dentry *apb_log_enable_dentry; | |
122 | DECLARE_KFIFO(apb_log_fifo, char, APB1_LOG_SIZE); | |
fc1a536e AB |
123 | }; |
124 | ||
e5acf736 AB |
125 | /** |
126 | * cport_to_ep - information about cport to endpoints mapping | |
127 | * @cport_id: the id of cport to map to endpoints | |
128 | * @endpoint_in: the endpoint number to use for in transfer | |
129 | * @endpoint_out: he endpoint number to use for out transfer | |
130 | */ | |
fc1a536e AB |
131 | struct cport_to_ep { |
132 | __le16 cport_id; | |
133 | __u8 endpoint_in; | |
134 | __u8 endpoint_out; | |
f587027e GKH |
135 | }; |
136 | ||
8e2b7daa | 137 | struct es2_ap_csi_config_request { |
a5a7723a JH |
138 | __u8 csi_id; |
139 | __u8 clock_mode; | |
140 | __u8 num_lanes; | |
141 | __u8 padding; | |
8e2b7daa | 142 | __le32 bus_freq; |
a5a7723a | 143 | } __packed; |
8e2b7daa | 144 | |
2537636a | 145 | static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) |
f587027e | 146 | { |
4b1d8204 | 147 | return (struct es2_ap_dev *)&hd->hd_priv; |
f587027e GKH |
148 | } |
149 | ||
150 | static void cport_out_callback(struct urb *urb); | |
4b1d8204 AE |
151 | static void usb_log_enable(struct es2_ap_dev *es2); |
152 | static void usb_log_disable(struct es2_ap_dev *es2); | |
f587027e | 153 | |
e5acf736 | 154 | /* Get the endpoints pair mapped to the cport */ |
4b1d8204 | 155 | static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id) |
fc1a536e | 156 | { |
4b1d8204 | 157 | if (cport_id >= es2->hd->num_cports) |
fc1a536e | 158 | return 0; |
4b1d8204 | 159 | return es2->cport_to_ep[cport_id]; |
fc1a536e AB |
160 | } |
161 | ||
4b1d8204 | 162 | #define ES2_TIMEOUT 500 /* 500 ms for the SVC to do something */ |
f587027e | 163 | |
608f6619 GKH |
164 | /* Disable for now until we work all of this out to keep a warning-free build */ |
165 | #if 0 | |
e5acf736 | 166 | /* Test if the endpoints pair is already mapped to a cport */ |
4b1d8204 | 167 | static int ep_pair_in_use(struct es2_ap_dev *es2, int ep_pair) |
fc1a536e AB |
168 | { |
169 | int i; | |
170 | ||
4b1d8204 AE |
171 | for (i = 0; i < es2->hd->num_cports; i++) { |
172 | if (es2->cport_to_ep[i] == ep_pair) | |
fc1a536e AB |
173 | return 1; |
174 | } | |
175 | return 0; | |
176 | } | |
177 | ||
e5acf736 | 178 | /* Configure the endpoint mapping and send the request to APBridge */ |
4b1d8204 | 179 | static int map_cport_to_ep(struct es2_ap_dev *es2, |
1ff3dc92 | 180 | u16 cport_id, int ep_pair) |
fc1a536e AB |
181 | { |
182 | int retval; | |
183 | struct cport_to_ep *cport_to_ep; | |
184 | ||
1ff3dc92 | 185 | if (ep_pair < 0 || ep_pair >= NUM_BULKS) |
fc1a536e | 186 | return -EINVAL; |
4b1d8204 | 187 | if (cport_id >= es2->hd->num_cports) |
fc1a536e | 188 | return -EINVAL; |
4b1d8204 | 189 | if (ep_pair && ep_pair_in_use(es2, ep_pair)) |
fc1a536e AB |
190 | return -EINVAL; |
191 | ||
192 | cport_to_ep = kmalloc(sizeof(*cport_to_ep), GFP_KERNEL); | |
193 | if (!cport_to_ep) | |
194 | return -ENOMEM; | |
195 | ||
4b1d8204 | 196 | es2->cport_to_ep[cport_id] = ep_pair; |
fc1a536e | 197 | cport_to_ep->cport_id = cpu_to_le16(cport_id); |
4b1d8204 AE |
198 | cport_to_ep->endpoint_in = es2->cport_in[ep_pair].endpoint; |
199 | cport_to_ep->endpoint_out = es2->cport_out[ep_pair].endpoint; | |
fc1a536e | 200 | |
4b1d8204 AE |
201 | retval = usb_control_msg(es2->usb_dev, |
202 | usb_sndctrlpipe(es2->usb_dev, 0), | |
fc1a536e AB |
203 | REQUEST_EP_MAPPING, |
204 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, | |
205 | 0x00, 0x00, | |
206 | (char *)cport_to_ep, | |
207 | sizeof(*cport_to_ep), | |
4b1d8204 | 208 | ES2_TIMEOUT); |
fc1a536e AB |
209 | if (retval == sizeof(*cport_to_ep)) |
210 | retval = 0; | |
211 | kfree(cport_to_ep); | |
212 | ||
213 | return retval; | |
214 | } | |
215 | ||
e5acf736 | 216 | /* Unmap a cport: use the muxed endpoints pair */ |
4b1d8204 | 217 | static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id) |
fc1a536e | 218 | { |
4b1d8204 | 219 | return map_cport_to_ep(es2, cport_id, 0); |
fc1a536e | 220 | } |
608f6619 | 221 | #endif |
fc1a536e | 222 | |
8e2b7daa LP |
223 | int es2_ap_csi_setup(struct gb_host_device *hd, bool start, |
224 | struct es2_ap_csi_config *cfg) | |
225 | { | |
ccb58035 | 226 | struct es2_ap_csi_config_request *cfg_req; |
8e2b7daa LP |
227 | struct es2_ap_dev *es2 = hd_to_es2(hd); |
228 | struct usb_device *udev = es2->usb_dev; | |
229 | int retval; | |
230 | ||
ccb58035 LP |
231 | cfg_req = kzalloc(sizeof(*cfg_req), GFP_KERNEL); |
232 | if (!cfg_req) | |
233 | return -ENOMEM; | |
234 | ||
235 | cfg_req->csi_id = cfg->csi_id; | |
8e2b7daa LP |
236 | |
237 | if (start) { | |
ccb58035 LP |
238 | cfg_req->clock_mode = cfg->clock_mode; |
239 | cfg_req->num_lanes = cfg->num_lanes; | |
240 | cfg_req->bus_freq = cpu_to_le32(cfg->bus_freq); | |
8e2b7daa LP |
241 | } |
242 | ||
243 | retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | |
244 | REQUEST_CSI_TX_CONTROL, | |
245 | USB_DIR_OUT | USB_TYPE_VENDOR | | |
ccb58035 LP |
246 | USB_RECIP_INTERFACE, 0, 0, cfg_req, |
247 | sizeof(*cfg_req), ES2_TIMEOUT); | |
248 | if (retval < 0) | |
8e2b7daa | 249 | dev_err(&udev->dev, "failed to setup csi: %d\n", retval); |
8e2b7daa | 250 | |
ccb58035 LP |
251 | kfree(cfg_req); |
252 | return retval; | |
8e2b7daa LP |
253 | } |
254 | EXPORT_SYMBOL_GPL(es2_ap_csi_setup); | |
255 | ||
0ce68ce4 JH |
256 | static int es2_cport_in_enable(struct es2_ap_dev *es2, |
257 | struct es2_cport_in *cport_in) | |
258 | { | |
259 | struct urb *urb; | |
260 | int ret; | |
261 | int i; | |
262 | ||
263 | for (i = 0; i < NUM_CPORT_IN_URB; ++i) { | |
264 | urb = cport_in->urb[i]; | |
265 | ||
266 | ret = usb_submit_urb(urb, GFP_KERNEL); | |
267 | if (ret) { | |
268 | dev_err(&es2->usb_dev->dev, | |
269 | "failed to submit in-urb: %d\n", ret); | |
270 | goto err_kill_urbs; | |
271 | } | |
272 | } | |
273 | ||
274 | return 0; | |
275 | ||
276 | err_kill_urbs: | |
277 | for (--i; i >= 0; --i) { | |
278 | urb = cport_in->urb[i]; | |
279 | usb_kill_urb(urb); | |
280 | } | |
281 | ||
282 | return ret; | |
283 | } | |
284 | ||
f6624ca7 JH |
285 | static void es2_cport_in_disable(struct es2_ap_dev *es2, |
286 | struct es2_cport_in *cport_in) | |
287 | { | |
288 | struct urb *urb; | |
289 | int i; | |
290 | ||
291 | for (i = 0; i < NUM_CPORT_IN_URB; ++i) { | |
292 | urb = cport_in->urb[i]; | |
293 | usb_kill_urb(urb); | |
294 | } | |
295 | } | |
296 | ||
4b1d8204 | 297 | static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask) |
f587027e GKH |
298 | { |
299 | struct urb *urb = NULL; | |
300 | unsigned long flags; | |
301 | int i; | |
302 | ||
4b1d8204 | 303 | spin_lock_irqsave(&es2->cport_out_urb_lock, flags); |
f587027e GKH |
304 | |
305 | /* Look in our pool of allocated urbs first, as that's the "fastest" */ | |
306 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
4b1d8204 AE |
307 | if (es2->cport_out_urb_busy[i] == false && |
308 | es2->cport_out_urb_cancelled[i] == false) { | |
309 | es2->cport_out_urb_busy[i] = true; | |
310 | urb = es2->cport_out_urb[i]; | |
f587027e GKH |
311 | break; |
312 | } | |
313 | } | |
4b1d8204 | 314 | spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); |
f587027e GKH |
315 | if (urb) |
316 | return urb; | |
317 | ||
318 | /* | |
319 | * Crap, pool is empty, complain to the syslog and go allocate one | |
320 | * dynamically as we have to succeed. | |
321 | */ | |
ed972e3a | 322 | dev_dbg(&es2->usb_dev->dev, |
f587027e GKH |
323 | "No free CPort OUT urbs, having to dynamically allocate one!\n"); |
324 | return usb_alloc_urb(0, gfp_mask); | |
325 | } | |
326 | ||
4b1d8204 | 327 | static void free_urb(struct es2_ap_dev *es2, struct urb *urb) |
f587027e GKH |
328 | { |
329 | unsigned long flags; | |
330 | int i; | |
331 | /* | |
332 | * See if this was an urb in our pool, if so mark it "free", otherwise | |
333 | * we need to free it ourselves. | |
334 | */ | |
4b1d8204 | 335 | spin_lock_irqsave(&es2->cport_out_urb_lock, flags); |
f587027e | 336 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { |
4b1d8204 AE |
337 | if (urb == es2->cport_out_urb[i]) { |
338 | es2->cport_out_urb_busy[i] = false; | |
f587027e GKH |
339 | urb = NULL; |
340 | break; | |
341 | } | |
342 | } | |
4b1d8204 | 343 | spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); |
f587027e GKH |
344 | |
345 | /* If urb is not NULL, then we need to free this urb */ | |
346 | usb_free_urb(urb); | |
347 | } | |
348 | ||
d29b3d63 AE |
349 | /* |
350 | * We (ab)use the operation-message header pad bytes to transfer the | |
351 | * cport id in order to minimise overhead. | |
352 | */ | |
353 | static void | |
354 | gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) | |
355 | { | |
4bc1389d | 356 | header->pad[0] = cport_id; |
d29b3d63 AE |
357 | } |
358 | ||
359 | /* Clear the pad bytes used for the CPort id */ | |
360 | static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) | |
361 | { | |
4bc1389d | 362 | header->pad[0] = 0; |
d29b3d63 AE |
363 | } |
364 | ||
365 | /* Extract the CPort id packed into the header, and clear it */ | |
366 | static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) | |
367 | { | |
4bc1389d | 368 | u16 cport_id = header->pad[0]; |
d29b3d63 AE |
369 | |
370 | gb_message_cport_clear(header); | |
371 | ||
372 | return cport_id; | |
373 | } | |
374 | ||
f587027e | 375 | /* |
3e136cc9 JH |
376 | * Returns zero if the message was successfully queued, or a negative errno |
377 | * otherwise. | |
f587027e | 378 | */ |
2537636a | 379 | static int message_send(struct gb_host_device *hd, u16 cport_id, |
7cf7bca9 | 380 | struct gb_message *message, gfp_t gfp_mask) |
f587027e | 381 | { |
4b1d8204 AE |
382 | struct es2_ap_dev *es2 = hd_to_es2(hd); |
383 | struct usb_device *udev = es2->usb_dev; | |
7cf7bca9 | 384 | size_t buffer_size; |
f587027e GKH |
385 | int retval; |
386 | struct urb *urb; | |
1ff3dc92 | 387 | int ep_pair; |
3e136cc9 | 388 | unsigned long flags; |
f587027e | 389 | |
f587027e GKH |
390 | /* |
391 | * The data actually transferred will include an indication | |
392 | * of where the data should be sent. Do one last check of | |
393 | * the target CPort id before filling it in. | |
394 | */ | |
144670c2 | 395 | if (!cport_id_valid(hd, cport_id)) { |
100e9000 | 396 | dev_err(&udev->dev, "invalid cport %u\n", cport_id); |
3e136cc9 | 397 | return -EINVAL; |
f587027e | 398 | } |
f587027e GKH |
399 | |
400 | /* Find a free urb */ | |
4b1d8204 | 401 | urb = next_free_urb(es2, gfp_mask); |
f587027e | 402 | if (!urb) |
3e136cc9 JH |
403 | return -ENOMEM; |
404 | ||
4b1d8204 | 405 | spin_lock_irqsave(&es2->cport_out_urb_lock, flags); |
3e136cc9 | 406 | message->hcpriv = urb; |
4b1d8204 | 407 | spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); |
f587027e | 408 | |
d29b3d63 AE |
409 | /* Pack the cport id into the message header */ |
410 | gb_message_cport_pack(message->header, cport_id); | |
491e60d6 | 411 | |
821c620a | 412 | buffer_size = sizeof(*message->header) + message->payload_size; |
491e60d6 | 413 | |
4b1d8204 | 414 | ep_pair = cport_to_ep_pair(es2, cport_id); |
f587027e | 415 | usb_fill_bulk_urb(urb, udev, |
606addd2 | 416 | usb_sndbulkpipe(udev, |
4b1d8204 | 417 | es2->cport_out[ep_pair].endpoint), |
821c620a | 418 | message->buffer, buffer_size, |
7cf7bca9 | 419 | cport_out_callback, message); |
977e209a | 420 | urb->transfer_flags |= URB_ZERO_PACKET; |
6872c461 | 421 | trace_gb_host_device_send(hd, cport_id, buffer_size); |
f587027e GKH |
422 | retval = usb_submit_urb(urb, gfp_mask); |
423 | if (retval) { | |
305a031e | 424 | dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); |
3e136cc9 | 425 | |
4b1d8204 | 426 | spin_lock_irqsave(&es2->cport_out_urb_lock, flags); |
3e136cc9 | 427 | message->hcpriv = NULL; |
4b1d8204 | 428 | spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); |
3e136cc9 | 429 | |
4b1d8204 | 430 | free_urb(es2, urb); |
d29b3d63 | 431 | gb_message_cport_clear(message->header); |
3e136cc9 JH |
432 | |
433 | return retval; | |
f587027e GKH |
434 | } |
435 | ||
3e136cc9 | 436 | return 0; |
f587027e GKH |
437 | } |
438 | ||
439 | /* | |
3e136cc9 | 440 | * Can not be called in atomic context. |
f587027e | 441 | */ |
3e136cc9 | 442 | static void message_cancel(struct gb_message *message) |
f587027e | 443 | { |
2537636a | 444 | struct gb_host_device *hd = message->operation->connection->hd; |
4b1d8204 | 445 | struct es2_ap_dev *es2 = hd_to_es2(hd); |
3e136cc9 JH |
446 | struct urb *urb; |
447 | int i; | |
f587027e | 448 | |
3e136cc9 JH |
449 | might_sleep(); |
450 | ||
4b1d8204 | 451 | spin_lock_irq(&es2->cport_out_urb_lock); |
3e136cc9 JH |
452 | urb = message->hcpriv; |
453 | ||
454 | /* Prevent dynamically allocated urb from being deallocated. */ | |
455 | usb_get_urb(urb); | |
456 | ||
457 | /* Prevent pre-allocated urb from being reused. */ | |
458 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
4b1d8204 AE |
459 | if (urb == es2->cport_out_urb[i]) { |
460 | es2->cport_out_urb_cancelled[i] = true; | |
3e136cc9 JH |
461 | break; |
462 | } | |
463 | } | |
4b1d8204 | 464 | spin_unlock_irq(&es2->cport_out_urb_lock); |
3e136cc9 JH |
465 | |
466 | usb_kill_urb(urb); | |
467 | ||
468 | if (i < NUM_CPORT_OUT_URB) { | |
4b1d8204 AE |
469 | spin_lock_irq(&es2->cport_out_urb_lock); |
470 | es2->cport_out_urb_cancelled[i] = false; | |
471 | spin_unlock_irq(&es2->cport_out_urb_lock); | |
3e136cc9 JH |
472 | } |
473 | ||
474 | usb_free_urb(urb); | |
f587027e GKH |
475 | } |
476 | ||
2537636a | 477 | static int cport_reset(struct gb_host_device *hd, u16 cport_id) |
82ee1e6c | 478 | { |
4b1d8204 AE |
479 | struct es2_ap_dev *es2 = hd_to_es2(hd); |
480 | struct usb_device *udev = es2->usb_dev; | |
82ee1e6c FP |
481 | int retval; |
482 | ||
483 | retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | |
484 | REQUEST_RESET_CPORT, | |
485 | USB_DIR_OUT | USB_TYPE_VENDOR | | |
69166d25 | 486 | USB_RECIP_INTERFACE, cport_id, 0, |
4b1d8204 | 487 | NULL, 0, ES2_TIMEOUT); |
82ee1e6c | 488 | if (retval < 0) { |
2f3db927 | 489 | dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id, |
82ee1e6c FP |
490 | retval); |
491 | return retval; | |
492 | } | |
493 | ||
494 | return 0; | |
495 | } | |
496 | ||
2537636a | 497 | static int cport_enable(struct gb_host_device *hd, u16 cport_id) |
82ee1e6c FP |
498 | { |
499 | int retval; | |
500 | ||
501 | if (cport_id != GB_SVC_CPORT_ID) { | |
502 | retval = cport_reset(hd, cport_id); | |
503 | if (retval) | |
504 | return retval; | |
505 | } | |
506 | ||
507 | return 0; | |
508 | } | |
509 | ||
2537636a | 510 | static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) |
608ab2fe BD |
511 | { |
512 | int retval; | |
4b1d8204 AE |
513 | struct es2_ap_dev *es2 = hd_to_es2(hd); |
514 | struct usb_device *udev = es2->usb_dev; | |
608ab2fe BD |
515 | |
516 | if (!cport_id_valid(hd, cport_id)) { | |
100e9000 | 517 | dev_err(&udev->dev, "invalid cport %u\n", cport_id); |
608ab2fe BD |
518 | return -EINVAL; |
519 | } | |
520 | ||
521 | retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | |
522 | REQUEST_LATENCY_TAG_EN, | |
523 | USB_DIR_OUT | USB_TYPE_VENDOR | | |
524 | USB_RECIP_INTERFACE, cport_id, 0, NULL, | |
4b1d8204 | 525 | 0, ES2_TIMEOUT); |
608ab2fe BD |
526 | |
527 | if (retval < 0) | |
528 | dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n", | |
529 | cport_id); | |
530 | return retval; | |
531 | } | |
532 | ||
2537636a | 533 | static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) |
608ab2fe BD |
534 | { |
535 | int retval; | |
4b1d8204 AE |
536 | struct es2_ap_dev *es2 = hd_to_es2(hd); |
537 | struct usb_device *udev = es2->usb_dev; | |
608ab2fe BD |
538 | |
539 | if (!cport_id_valid(hd, cport_id)) { | |
100e9000 | 540 | dev_err(&udev->dev, "invalid cport %u\n", cport_id); |
608ab2fe BD |
541 | return -EINVAL; |
542 | } | |
543 | ||
544 | retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | |
545 | REQUEST_LATENCY_TAG_DIS, | |
546 | USB_DIR_OUT | USB_TYPE_VENDOR | | |
547 | USB_RECIP_INTERFACE, cport_id, 0, NULL, | |
4b1d8204 | 548 | 0, ES2_TIMEOUT); |
608ab2fe BD |
549 | |
550 | if (retval < 0) | |
551 | dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n", | |
552 | cport_id); | |
553 | return retval; | |
554 | } | |
555 | ||
a8cc020f | 556 | static struct gb_hd_driver es2_driver = { |
4b1d8204 | 557 | .hd_priv_size = sizeof(struct es2_ap_dev), |
7cf7bca9 JH |
558 | .message_send = message_send, |
559 | .message_cancel = message_cancel, | |
82ee1e6c | 560 | .cport_enable = cport_enable, |
608ab2fe BD |
561 | .latency_tag_enable = latency_tag_enable, |
562 | .latency_tag_disable = latency_tag_disable, | |
f587027e GKH |
563 | }; |
564 | ||
565 | /* Common function to report consistent warnings based on URB status */ | |
566 | static int check_urb_status(struct urb *urb) | |
567 | { | |
568 | struct device *dev = &urb->dev->dev; | |
569 | int status = urb->status; | |
570 | ||
571 | switch (status) { | |
572 | case 0: | |
573 | return 0; | |
574 | ||
575 | case -EOVERFLOW: | |
576 | dev_err(dev, "%s: overflow actual length is %d\n", | |
577 | __func__, urb->actual_length); | |
578 | case -ECONNRESET: | |
579 | case -ENOENT: | |
580 | case -ESHUTDOWN: | |
581 | case -EILSEQ: | |
582 | case -EPROTO: | |
583 | /* device is gone, stop sending */ | |
584 | return status; | |
585 | } | |
586 | dev_err(dev, "%s: unknown status %d\n", __func__, status); | |
587 | ||
588 | return -EAGAIN; | |
589 | } | |
590 | ||
57bc17ff | 591 | static void es2_destroy(struct es2_ap_dev *es2) |
f587027e | 592 | { |
f587027e | 593 | struct usb_device *udev; |
606addd2 | 594 | int bulk_in; |
f587027e GKH |
595 | int i; |
596 | ||
74cd6503 | 597 | debugfs_remove(es2->apb_log_enable_dentry); |
4b1d8204 | 598 | usb_log_disable(es2); |
ca3ec299 | 599 | |
f587027e GKH |
600 | /* Tear down everything! */ |
601 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
4b1d8204 | 602 | struct urb *urb = es2->cport_out_urb[i]; |
f587027e GKH |
603 | |
604 | if (!urb) | |
605 | break; | |
606 | usb_kill_urb(urb); | |
607 | usb_free_urb(urb); | |
4b1d8204 AE |
608 | es2->cport_out_urb[i] = NULL; |
609 | es2->cport_out_urb_busy[i] = false; /* just to be anal */ | |
f587027e GKH |
610 | } |
611 | ||
606addd2 | 612 | for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { |
4b1d8204 AE |
613 | struct es2_cport_in *cport_in = &es2->cport_in[bulk_in]; |
614 | ||
606addd2 AB |
615 | for (i = 0; i < NUM_CPORT_IN_URB; ++i) { |
616 | struct urb *urb = cport_in->urb[i]; | |
617 | ||
618 | if (!urb) | |
619 | break; | |
606addd2 AB |
620 | usb_free_urb(urb); |
621 | kfree(cport_in->buffer[i]); | |
622 | cport_in->buffer[i] = NULL; | |
623 | } | |
f587027e GKH |
624 | } |
625 | ||
a5202862 JH |
626 | kfree(es2->cport_to_ep); |
627 | ||
4b1d8204 | 628 | udev = es2->usb_dev; |
c1700479 | 629 | gb_hd_put(es2->hd); |
f587027e GKH |
630 | |
631 | usb_put_dev(udev); | |
632 | } | |
633 | ||
57bc17ff JH |
634 | static void ap_disconnect(struct usb_interface *interface) |
635 | { | |
636 | struct es2_ap_dev *es2 = usb_get_intfdata(interface); | |
637 | int i; | |
638 | ||
639 | for (i = 0; i < NUM_BULKS; ++i) | |
640 | es2_cport_in_disable(es2, &es2->cport_in[i]); | |
641 | ||
c1700479 JH |
642 | gb_hd_del(es2->hd); |
643 | ||
57bc17ff JH |
644 | es2_destroy(es2); |
645 | } | |
646 | ||
f587027e GKH |
647 | static void cport_in_callback(struct urb *urb) |
648 | { | |
2537636a | 649 | struct gb_host_device *hd = urb->context; |
f587027e | 650 | struct device *dev = &urb->dev->dev; |
491e60d6 | 651 | struct gb_operation_msg_hdr *header; |
f587027e GKH |
652 | int status = check_urb_status(urb); |
653 | int retval; | |
654 | u16 cport_id; | |
f587027e GKH |
655 | |
656 | if (status) { | |
657 | if ((status == -EAGAIN) || (status == -EPROTO)) | |
658 | goto exit; | |
659 | dev_err(dev, "urb cport in error %d (dropped)\n", status); | |
660 | return; | |
661 | } | |
662 | ||
491e60d6 | 663 | if (urb->actual_length < sizeof(*header)) { |
305a031e | 664 | dev_err(dev, "short message received\n"); |
f587027e GKH |
665 | goto exit; |
666 | } | |
667 | ||
d29b3d63 | 668 | /* Extract the CPort id, which is packed in the message header */ |
491e60d6 | 669 | header = urb->transfer_buffer; |
d29b3d63 | 670 | cport_id = gb_message_cport_unpack(header); |
f587027e | 671 | |
6872c461 BD |
672 | if (cport_id_valid(hd, cport_id)) { |
673 | trace_gb_host_device_recv(hd, cport_id, urb->actual_length); | |
821c620a | 674 | greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, |
491e60d6 | 675 | urb->actual_length); |
6872c461 | 676 | } else { |
100e9000 | 677 | dev_err(dev, "invalid cport id %u received\n", cport_id); |
6872c461 | 678 | } |
f587027e GKH |
679 | exit: |
680 | /* put our urb back in the request pool */ | |
681 | retval = usb_submit_urb(urb, GFP_ATOMIC); | |
682 | if (retval) | |
305a031e | 683 | dev_err(dev, "failed to resubmit in-urb: %d\n", retval); |
f587027e GKH |
684 | } |
685 | ||
686 | static void cport_out_callback(struct urb *urb) | |
687 | { | |
7cf7bca9 | 688 | struct gb_message *message = urb->context; |
2537636a | 689 | struct gb_host_device *hd = message->operation->connection->hd; |
4b1d8204 | 690 | struct es2_ap_dev *es2 = hd_to_es2(hd); |
f587027e | 691 | int status = check_urb_status(urb); |
3e136cc9 | 692 | unsigned long flags; |
f587027e | 693 | |
d29b3d63 | 694 | gb_message_cport_clear(message->header); |
491e60d6 | 695 | |
4b1d8204 | 696 | spin_lock_irqsave(&es2->cport_out_urb_lock, flags); |
58c85123 | 697 | message->hcpriv = NULL; |
4b1d8204 | 698 | spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); |
58c85123 | 699 | |
f587027e | 700 | /* |
7cf7bca9 JH |
701 | * Tell the submitter that the message send (attempt) is |
702 | * complete, and report the status. | |
f587027e | 703 | */ |
7cf7bca9 JH |
704 | greybus_message_sent(hd, message, status); |
705 | ||
4b1d8204 | 706 | free_urb(es2, urb); |
f587027e GKH |
707 | } |
708 | ||
c15ccabe | 709 | #define APB1_LOG_MSG_SIZE 64 |
3be0e17d | 710 | static void apb_log_get(struct es2_ap_dev *es2, char *buf) |
ca3ec299 | 711 | { |
ca3ec299 GKH |
712 | int retval; |
713 | ||
714 | /* SVC messages go down our control pipe */ | |
715 | do { | |
4b1d8204 AE |
716 | retval = usb_control_msg(es2->usb_dev, |
717 | usb_rcvctrlpipe(es2->usb_dev, 0), | |
611c1739 | 718 | REQUEST_LOG, |
ca3ec299 GKH |
719 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, |
720 | 0x00, 0x00, | |
721 | buf, | |
c15ccabe | 722 | APB1_LOG_MSG_SIZE, |
4b1d8204 | 723 | ES2_TIMEOUT); |
ca3ec299 | 724 | if (retval > 0) |
3be0e17d | 725 | kfifo_in(&es2->apb_log_fifo, buf, retval); |
ca3ec299 GKH |
726 | } while (retval > 0); |
727 | } | |
728 | ||
3be0e17d | 729 | static int apb_log_poll(void *data) |
ca3ec299 | 730 | { |
4b1d8204 | 731 | struct es2_ap_dev *es2 = data; |
c15ccabe JH |
732 | char *buf; |
733 | ||
734 | buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL); | |
735 | if (!buf) | |
736 | return -ENOMEM; | |
737 | ||
ca3ec299 GKH |
738 | while (!kthread_should_stop()) { |
739 | msleep(1000); | |
3be0e17d | 740 | apb_log_get(es2, buf); |
ca3ec299 | 741 | } |
c15ccabe JH |
742 | |
743 | kfree(buf); | |
744 | ||
ca3ec299 GKH |
745 | return 0; |
746 | } | |
747 | ||
3be0e17d | 748 | static ssize_t apb_log_read(struct file *f, char __user *buf, |
ca3ec299 GKH |
749 | size_t count, loff_t *ppos) |
750 | { | |
8995a39d | 751 | struct es2_ap_dev *es2 = f->f_inode->i_private; |
ca3ec299 GKH |
752 | ssize_t ret; |
753 | size_t copied; | |
754 | char *tmp_buf; | |
755 | ||
756 | if (count > APB1_LOG_SIZE) | |
757 | count = APB1_LOG_SIZE; | |
758 | ||
759 | tmp_buf = kmalloc(count, GFP_KERNEL); | |
760 | if (!tmp_buf) | |
761 | return -ENOMEM; | |
762 | ||
3be0e17d | 763 | copied = kfifo_out(&es2->apb_log_fifo, tmp_buf, count); |
ca3ec299 GKH |
764 | ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); |
765 | ||
766 | kfree(tmp_buf); | |
767 | ||
768 | return ret; | |
769 | } | |
770 | ||
3be0e17d AE |
771 | static const struct file_operations apb_log_fops = { |
772 | .read = apb_log_read, | |
ca3ec299 GKH |
773 | }; |
774 | ||
4b1d8204 | 775 | static void usb_log_enable(struct es2_ap_dev *es2) |
ca3ec299 | 776 | { |
3be0e17d | 777 | if (!IS_ERR_OR_NULL(es2->apb_log_task)) |
ca3ec299 GKH |
778 | return; |
779 | ||
780 | /* get log from APB1 */ | |
3be0e17d AE |
781 | es2->apb_log_task = kthread_run(apb_log_poll, es2, "apb_log"); |
782 | if (IS_ERR(es2->apb_log_task)) | |
ca3ec299 | 783 | return; |
3be0e17d AE |
784 | /* XXX We will need to rename this per APB */ |
785 | es2->apb_log_dentry = debugfs_create_file("apb_log", S_IRUGO, | |
ca3ec299 | 786 | gb_debugfs_get(), NULL, |
3be0e17d | 787 | &apb_log_fops); |
ca3ec299 GKH |
788 | } |
789 | ||
4b1d8204 | 790 | static void usb_log_disable(struct es2_ap_dev *es2) |
ca3ec299 | 791 | { |
3be0e17d | 792 | if (IS_ERR_OR_NULL(es2->apb_log_task)) |
ca3ec299 GKH |
793 | return; |
794 | ||
3be0e17d AE |
795 | debugfs_remove(es2->apb_log_dentry); |
796 | es2->apb_log_dentry = NULL; | |
ca3ec299 | 797 | |
3be0e17d AE |
798 | kthread_stop(es2->apb_log_task); |
799 | es2->apb_log_task = NULL; | |
ca3ec299 GKH |
800 | } |
801 | ||
3be0e17d | 802 | static ssize_t apb_log_enable_read(struct file *f, char __user *buf, |
ca3ec299 GKH |
803 | size_t count, loff_t *ppos) |
804 | { | |
1482b3e1 | 805 | struct es2_ap_dev *es2 = f->f_inode->i_private; |
3be0e17d | 806 | int enable = !IS_ERR_OR_NULL(es2->apb_log_task); |
4b1d8204 | 807 | char tmp_buf[3]; |
ca3ec299 GKH |
808 | |
809 | sprintf(tmp_buf, "%d\n", enable); | |
810 | return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); | |
811 | } | |
812 | ||
3be0e17d | 813 | static ssize_t apb_log_enable_write(struct file *f, const char __user *buf, |
ca3ec299 GKH |
814 | size_t count, loff_t *ppos) |
815 | { | |
816 | int enable; | |
817 | ssize_t retval; | |
1482b3e1 | 818 | struct es2_ap_dev *es2 = f->f_inode->i_private; |
ca3ec299 GKH |
819 | |
820 | retval = kstrtoint_from_user(buf, count, 10, &enable); | |
821 | if (retval) | |
822 | return retval; | |
823 | ||
824 | if (enable) | |
4b1d8204 | 825 | usb_log_enable(es2); |
ca3ec299 | 826 | else |
4b1d8204 | 827 | usb_log_disable(es2); |
ca3ec299 GKH |
828 | |
829 | return count; | |
830 | } | |
831 | ||
3be0e17d AE |
832 | static const struct file_operations apb_log_enable_fops = { |
833 | .read = apb_log_enable_read, | |
834 | .write = apb_log_enable_write, | |
ca3ec299 GKH |
835 | }; |
836 | ||
3be0e17d | 837 | static int apb_get_cport_count(struct usb_device *udev) |
24a6112f FP |
838 | { |
839 | int retval; | |
840 | __le16 *cport_count; | |
841 | ||
842 | cport_count = kmalloc(sizeof(*cport_count), GFP_KERNEL); | |
843 | if (!cport_count) | |
844 | return -ENOMEM; | |
845 | ||
846 | retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), | |
847 | REQUEST_CPORT_COUNT, | |
848 | USB_DIR_IN | USB_TYPE_VENDOR | | |
849 | USB_RECIP_INTERFACE, 0, 0, cport_count, | |
4b1d8204 | 850 | sizeof(*cport_count), ES2_TIMEOUT); |
24a6112f FP |
851 | if (retval < 0) { |
852 | dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", | |
853 | retval); | |
854 | goto out; | |
855 | } | |
856 | ||
857 | retval = le16_to_cpu(*cport_count); | |
858 | ||
859 | /* We need to fit a CPort ID in one byte of a message header */ | |
860 | if (retval > U8_MAX) { | |
861 | retval = U8_MAX; | |
862 | dev_warn(&udev->dev, "Limiting number of CPorts to U8_MAX\n"); | |
863 | } | |
864 | ||
865 | out: | |
866 | kfree(cport_count); | |
867 | return retval; | |
868 | } | |
869 | ||
f587027e | 870 | /* |
4d5c446b JH |
871 | * The ES2 USB Bridge device has 15 endpoints |
872 | * 1 Control - usual USB stuff + AP -> APBridgeA messages | |
873 | * 7 Bulk IN - CPort data in | |
874 | * 7 Bulk OUT - CPort data out | |
f587027e GKH |
875 | */ |
876 | static int ap_probe(struct usb_interface *interface, | |
877 | const struct usb_device_id *id) | |
878 | { | |
4b1d8204 | 879 | struct es2_ap_dev *es2; |
2537636a | 880 | struct gb_host_device *hd; |
f587027e GKH |
881 | struct usb_device *udev; |
882 | struct usb_host_interface *iface_desc; | |
883 | struct usb_endpoint_descriptor *endpoint; | |
606addd2 AB |
884 | int bulk_in = 0; |
885 | int bulk_out = 0; | |
f587027e GKH |
886 | int retval = -ENOMEM; |
887 | int i; | |
24a6112f | 888 | int num_cports; |
a75fd8ba | 889 | int cport_id; |
4bc1389d | 890 | |
f587027e GKH |
891 | udev = usb_get_dev(interface_to_usbdev(interface)); |
892 | ||
3be0e17d | 893 | num_cports = apb_get_cport_count(udev); |
24a6112f FP |
894 | if (num_cports < 0) { |
895 | usb_put_dev(udev); | |
896 | dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", | |
897 | num_cports); | |
898 | return num_cports; | |
899 | } | |
900 | ||
d6e139bc JH |
901 | hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX, |
902 | num_cports); | |
8ea70fe0 | 903 | if (IS_ERR(hd)) { |
f587027e | 904 | usb_put_dev(udev); |
8ea70fe0 | 905 | return PTR_ERR(hd); |
f587027e GKH |
906 | } |
907 | ||
a75fd8ba LP |
908 | /* |
909 | * CPorts 16 and 17 are reserved for CDSI0 and CDSI1, make sure they | |
910 | * won't be allocated dynamically. | |
911 | */ | |
912 | do { | |
913 | cport_id = ida_simple_get(&hd->cport_id_map, 16, 18, GFP_KERNEL); | |
914 | } while (cport_id > 0); | |
915 | ||
4b1d8204 AE |
916 | es2 = hd_to_es2(hd); |
917 | es2->hd = hd; | |
918 | es2->usb_intf = interface; | |
919 | es2->usb_dev = udev; | |
920 | spin_lock_init(&es2->cport_out_urb_lock); | |
3be0e17d | 921 | INIT_KFIFO(es2->apb_log_fifo); |
4b1d8204 | 922 | usb_set_intfdata(interface, es2); |
f587027e | 923 | |
4b1d8204 | 924 | es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep), |
c011d558 | 925 | GFP_KERNEL); |
4b1d8204 | 926 | if (!es2->cport_to_ep) { |
c011d558 FP |
927 | retval = -ENOMEM; |
928 | goto error; | |
929 | } | |
930 | ||
4d5c446b | 931 | /* find all bulk endpoints */ |
f587027e GKH |
932 | iface_desc = interface->cur_altsetting; |
933 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | |
934 | endpoint = &iface_desc->endpoint[i].desc; | |
935 | ||
b767ee40 | 936 | if (usb_endpoint_is_bulk_in(endpoint)) { |
4b1d8204 | 937 | es2->cport_in[bulk_in++].endpoint = |
606addd2 | 938 | endpoint->bEndpointAddress; |
f587027e | 939 | } else if (usb_endpoint_is_bulk_out(endpoint)) { |
4b1d8204 | 940 | es2->cport_out[bulk_out++].endpoint = |
606addd2 | 941 | endpoint->bEndpointAddress; |
f587027e GKH |
942 | } else { |
943 | dev_err(&udev->dev, | |
b933fa4a | 944 | "Unknown endpoint type found, address 0x%02x\n", |
f587027e GKH |
945 | endpoint->bEndpointAddress); |
946 | } | |
947 | } | |
b6d80852 | 948 | if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) { |
f587027e GKH |
949 | dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); |
950 | goto error; | |
951 | } | |
952 | ||
0ce68ce4 | 953 | /* Allocate buffers for our cport in messages */ |
606addd2 | 954 | for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { |
4b1d8204 AE |
955 | struct es2_cport_in *cport_in = &es2->cport_in[bulk_in]; |
956 | ||
606addd2 AB |
957 | for (i = 0; i < NUM_CPORT_IN_URB; ++i) { |
958 | struct urb *urb; | |
959 | u8 *buffer; | |
960 | ||
961 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
962 | if (!urb) | |
963 | goto error; | |
4b1d8204 | 964 | buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL); |
606addd2 AB |
965 | if (!buffer) |
966 | goto error; | |
967 | ||
968 | usb_fill_bulk_urb(urb, udev, | |
969 | usb_rcvbulkpipe(udev, | |
970 | cport_in->endpoint), | |
4b1d8204 | 971 | buffer, ES2_GBUF_MSG_SIZE_MAX, |
606addd2 AB |
972 | cport_in_callback, hd); |
973 | cport_in->urb[i] = urb; | |
974 | cport_in->buffer[i] = buffer; | |
606addd2 | 975 | } |
f587027e GKH |
976 | } |
977 | ||
978 | /* Allocate urbs for our CPort OUT messages */ | |
979 | for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { | |
980 | struct urb *urb; | |
981 | ||
982 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
983 | if (!urb) | |
984 | goto error; | |
985 | ||
4b1d8204 AE |
986 | es2->cport_out_urb[i] = urb; |
987 | es2->cport_out_urb_busy[i] = false; /* just to be anal */ | |
f587027e GKH |
988 | } |
989 | ||
3be0e17d AE |
990 | /* XXX We will need to rename this per APB */ |
991 | es2->apb_log_enable_dentry = debugfs_create_file("apb_log_enable", | |
86f918ee | 992 | (S_IWUSR | S_IRUGO), |
4b1d8204 | 993 | gb_debugfs_get(), es2, |
3be0e17d | 994 | &apb_log_enable_fops); |
0ce68ce4 | 995 | |
c1700479 JH |
996 | retval = gb_hd_add(hd); |
997 | if (retval) | |
998 | goto error; | |
999 | ||
0ce68ce4 JH |
1000 | for (i = 0; i < NUM_BULKS; ++i) { |
1001 | retval = es2_cport_in_enable(es2, &es2->cport_in[i]); | |
1002 | if (retval) | |
57bc17ff | 1003 | goto err_disable_cport_in; |
0ce68ce4 JH |
1004 | } |
1005 | ||
f587027e | 1006 | return 0; |
57bc17ff JH |
1007 | |
1008 | err_disable_cport_in: | |
1009 | for (--i; i >= 0; --i) | |
1010 | es2_cport_in_disable(es2, &es2->cport_in[i]); | |
c1700479 | 1011 | gb_hd_del(hd); |
f587027e | 1012 | error: |
57bc17ff | 1013 | es2_destroy(es2); |
f587027e GKH |
1014 | |
1015 | return retval; | |
1016 | } | |
1017 | ||
4b1d8204 | 1018 | static struct usb_driver es2_ap_driver = { |
c13c8bf0 | 1019 | .name = "es2_ap_driver", |
f587027e GKH |
1020 | .probe = ap_probe, |
1021 | .disconnect = ap_disconnect, | |
1022 | .id_table = id_table, | |
1023 | }; | |
1024 | ||
4b1d8204 | 1025 | module_usb_driver(es2_ap_driver); |
f587027e | 1026 | |
6cf42a44 | 1027 | MODULE_LICENSE("GPL v2"); |
f587027e | 1028 | MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>"); |