blob: 156269645f46723b1b58a82f8c46169eaf12c628 [file] [log] [blame]
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +08001/*
2 * Greybus "AP" USB driver for "ES2" controller chips
3 *
Alex Elder142f8dd2015-03-26 21:25:06 -05004 * Copyright 2014-2015 Google Inc.
5 * Copyright 2014-2015 Linaro Ltd.
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +08006 *
7 * Released under the GPLv2 only.
8 */
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +01009#include <linux/kthread.h>
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080010#include <linux/sizes.h>
11#include <linux/usb.h>
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +010012#include <linux/kfifo.h>
13#include <linux/debugfs.h>
Johan Hovold491e60d2015-04-07 11:27:20 +020014#include <asm/unaligned.h>
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080015
16#include "greybus.h"
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080017#include "kernel_ver.h"
Bryan O'Donoghue3f2a8092015-08-11 13:50:52 +010018#include "connection.h"
Bryan O'Donoghue6872c462015-09-22 18:06:39 -070019#include "greybus_trace.h"
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080020
Alex Elder4b1d8202015-10-27 22:18:37 -050021/* Memory sizes for the buffers sent to/from the ES2 controller */
22#define ES2_GBUF_MSG_SIZE_MAX 2048
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080023
24static const struct usb_device_id id_table[] = {
Greg Kroah-Hartman8f0a6542015-10-22 16:40:41 -070025 { USB_DEVICE(0xffff, 0x0002) }, /* Made up number, delete once firmware is fixed to use real number */
26 { USB_DEVICE(0x18d1, 0x1eaf) },
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080027 { },
28};
29MODULE_DEVICE_TABLE(usb, id_table);
30
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +010031#define APB1_LOG_SIZE SZ_16K
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +010032
Alexandre Bailon606addd2015-06-15 18:08:13 +020033/* Number of bulk in and bulk out couple */
34#define NUM_BULKS 7
35
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080036/*
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 */
Alexandre Bailon606addd2015-06-15 18:08:13 +020046#define NUM_CPORT_OUT_URB (8 * NUM_BULKS)
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080047
Alexandre Bailon611c17392015-06-15 18:08:11 +020048/* vendor request APB1 log */
49#define REQUEST_LOG 0x02
50
Alexandre Bailonfc1a5362015-06-15 18:08:14 +020051/* vendor request to map a cport to bulk in and bulk out endpoints */
52#define REQUEST_EP_MAPPING 0x03
53
Fabien Parent24a61122015-09-02 15:50:37 +020054/* vendor request to get the number of cports available */
55#define REQUEST_CPORT_COUNT 0x04
56
Fabien Parent82ee1e62015-10-13 17:34:50 +020057/* vendor request to reset a cport state */
58#define REQUEST_RESET_CPORT 0x05
59
Bryan O'Donoghue608ab2f2015-10-15 16:10:41 +010060/* 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
Alexandre Bailonddc09ac2015-06-15 18:08:12 +020064/*
65 * @endpoint: bulk in endpoint for CPort data
66 * @urb: array of urbs for the CPort in messages
67 * @buffer: array of buffers for the @cport_in_urb urbs
68 */
Alex Elder4b1d8202015-10-27 22:18:37 -050069struct es2_cport_in {
Alexandre Bailonddc09ac2015-06-15 18:08:12 +020070 __u8 endpoint;
71 struct urb *urb[NUM_CPORT_IN_URB];
72 u8 *buffer[NUM_CPORT_IN_URB];
73};
74
75/*
76 * @endpoint: bulk out endpoint for CPort data
77 */
Alex Elder4b1d8202015-10-27 22:18:37 -050078struct es2_cport_out {
Alexandre Bailonddc09ac2015-06-15 18:08:12 +020079 __u8 endpoint;
80};
81
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080082/**
Alex Elder4b1d8202015-10-27 22:18:37 -050083 * es2_ap_dev - ES2 USB Bridge to AP structure
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080084 * @usb_dev: pointer to the USB device we are.
85 * @usb_intf: pointer to the USB interface we are bound to.
Johan Hovold25376362015-11-03 18:03:23 +010086 * @hd: pointer to our gb_host_device structure
Alexandre Bailonddc09ac2015-06-15 18:08:12 +020087
Alexandre Bailonddc09ac2015-06-15 18:08:12 +020088 * @cport_in: endpoint, urbs and buffer for cport in messages
89 * @cport_out: endpoint for for cport out messages
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080090 * @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.
Johan Hovold3e136cc2015-07-01 12:37:21 +020093 * @cport_out_urb_cancelled: array of flags indicating whether the
94 * corresponding @cport_out_urb is being cancelled
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +080095 * @cport_out_urb_lock: locks the @cport_out_urb_busy "list"
Alex Elder1482b3e2015-10-27 22:18:38 -050096 *
Alex Elder3be0e172015-10-27 22:18:41 -050097 * @apb_log_task: task pointer for logging thread
98 * @apb_log_dentry: file system entry for the log file interface
99 * @apb_log_enable_dentry: file system entry for enabling logging
100 * @apb_log_fifo: kernel FIFO to carry logged data
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800101 */
Alex Elder4b1d8202015-10-27 22:18:37 -0500102struct es2_ap_dev {
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800103 struct usb_device *usb_dev;
104 struct usb_interface *usb_intf;
Johan Hovold25376362015-11-03 18:03:23 +0100105 struct gb_host_device *hd;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800106
Alex Elder4b1d8202015-10-27 22:18:37 -0500107 struct es2_cport_in cport_in[NUM_BULKS];
108 struct es2_cport_out cport_out[NUM_BULKS];
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800109 struct urb *cport_out_urb[NUM_CPORT_OUT_URB];
110 bool cport_out_urb_busy[NUM_CPORT_OUT_URB];
Johan Hovold3e136cc2015-07-01 12:37:21 +0200111 bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB];
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800112 spinlock_t cport_out_urb_lock;
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200113
Fabien Parentc011d552015-09-02 15:50:36 +0200114 int *cport_to_ep;
Alex Elder1482b3e2015-10-27 22:18:38 -0500115
Alex Elder3be0e172015-10-27 22:18:41 -0500116 struct task_struct *apb_log_task;
117 struct dentry *apb_log_dentry;
118 struct dentry *apb_log_enable_dentry;
119 DECLARE_KFIFO(apb_log_fifo, char, APB1_LOG_SIZE);
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200120};
121
Alexandre Bailone5acf732015-09-14 18:20:44 +0200122/**
123 * cport_to_ep - information about cport to endpoints mapping
124 * @cport_id: the id of cport to map to endpoints
125 * @endpoint_in: the endpoint number to use for in transfer
126 * @endpoint_out: he endpoint number to use for out transfer
127 */
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200128struct cport_to_ep {
129 __le16 cport_id;
130 __u8 endpoint_in;
131 __u8 endpoint_out;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800132};
133
Johan Hovold25376362015-11-03 18:03:23 +0100134static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800135{
Alex Elder4b1d8202015-10-27 22:18:37 -0500136 return (struct es2_ap_dev *)&hd->hd_priv;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800137}
138
139static void cport_out_callback(struct urb *urb);
Alex Elder4b1d8202015-10-27 22:18:37 -0500140static void usb_log_enable(struct es2_ap_dev *es2);
141static void usb_log_disable(struct es2_ap_dev *es2);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800142
Alexandre Bailone5acf732015-09-14 18:20:44 +0200143/* Get the endpoints pair mapped to the cport */
Alex Elder4b1d8202015-10-27 22:18:37 -0500144static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id)
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200145{
Alex Elder4b1d8202015-10-27 22:18:37 -0500146 if (cport_id >= es2->hd->num_cports)
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200147 return 0;
Alex Elder4b1d8202015-10-27 22:18:37 -0500148 return es2->cport_to_ep[cport_id];
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200149}
150
Alex Elder4b1d8202015-10-27 22:18:37 -0500151#define ES2_TIMEOUT 500 /* 500 ms for the SVC to do something */
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800152
Greg Kroah-Hartman608f6612015-09-22 09:42:52 -0700153/* Disable for now until we work all of this out to keep a warning-free build */
154#if 0
Alexandre Bailone5acf732015-09-14 18:20:44 +0200155/* Test if the endpoints pair is already mapped to a cport */
Alex Elder4b1d8202015-10-27 22:18:37 -0500156static int ep_pair_in_use(struct es2_ap_dev *es2, int ep_pair)
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200157{
158 int i;
159
Alex Elder4b1d8202015-10-27 22:18:37 -0500160 for (i = 0; i < es2->hd->num_cports; i++) {
161 if (es2->cport_to_ep[i] == ep_pair)
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200162 return 1;
163 }
164 return 0;
165}
166
Alexandre Bailone5acf732015-09-14 18:20:44 +0200167/* Configure the endpoint mapping and send the request to APBridge */
Alex Elder4b1d8202015-10-27 22:18:37 -0500168static int map_cport_to_ep(struct es2_ap_dev *es2,
Alexandre Bailon1ff3dc92015-09-14 18:20:42 +0200169 u16 cport_id, int ep_pair)
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200170{
171 int retval;
172 struct cport_to_ep *cport_to_ep;
173
Alexandre Bailon1ff3dc92015-09-14 18:20:42 +0200174 if (ep_pair < 0 || ep_pair >= NUM_BULKS)
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200175 return -EINVAL;
Alex Elder4b1d8202015-10-27 22:18:37 -0500176 if (cport_id >= es2->hd->num_cports)
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200177 return -EINVAL;
Alex Elder4b1d8202015-10-27 22:18:37 -0500178 if (ep_pair && ep_pair_in_use(es2, ep_pair))
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200179 return -EINVAL;
180
181 cport_to_ep = kmalloc(sizeof(*cport_to_ep), GFP_KERNEL);
182 if (!cport_to_ep)
183 return -ENOMEM;
184
Alex Elder4b1d8202015-10-27 22:18:37 -0500185 es2->cport_to_ep[cport_id] = ep_pair;
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200186 cport_to_ep->cport_id = cpu_to_le16(cport_id);
Alex Elder4b1d8202015-10-27 22:18:37 -0500187 cport_to_ep->endpoint_in = es2->cport_in[ep_pair].endpoint;
188 cport_to_ep->endpoint_out = es2->cport_out[ep_pair].endpoint;
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200189
Alex Elder4b1d8202015-10-27 22:18:37 -0500190 retval = usb_control_msg(es2->usb_dev,
191 usb_sndctrlpipe(es2->usb_dev, 0),
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200192 REQUEST_EP_MAPPING,
193 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
194 0x00, 0x00,
195 (char *)cport_to_ep,
196 sizeof(*cport_to_ep),
Alex Elder4b1d8202015-10-27 22:18:37 -0500197 ES2_TIMEOUT);
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200198 if (retval == sizeof(*cport_to_ep))
199 retval = 0;
200 kfree(cport_to_ep);
201
202 return retval;
203}
204
Alexandre Bailone5acf732015-09-14 18:20:44 +0200205/* Unmap a cport: use the muxed endpoints pair */
Alex Elder4b1d8202015-10-27 22:18:37 -0500206static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id)
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200207{
Alex Elder4b1d8202015-10-27 22:18:37 -0500208 return map_cport_to_ep(es2, cport_id, 0);
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200209}
Greg Kroah-Hartman608f6612015-09-22 09:42:52 -0700210#endif
Alexandre Bailonfc1a5362015-06-15 18:08:14 +0200211
Johan Hovold0ce68ce42015-11-04 18:55:14 +0100212static int es2_cport_in_enable(struct es2_ap_dev *es2,
213 struct es2_cport_in *cport_in)
214{
215 struct urb *urb;
216 int ret;
217 int i;
218
219 for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
220 urb = cport_in->urb[i];
221
222 ret = usb_submit_urb(urb, GFP_KERNEL);
223 if (ret) {
224 dev_err(&es2->usb_dev->dev,
225 "failed to submit in-urb: %d\n", ret);
226 goto err_kill_urbs;
227 }
228 }
229
230 return 0;
231
232err_kill_urbs:
233 for (--i; i >= 0; --i) {
234 urb = cport_in->urb[i];
235 usb_kill_urb(urb);
236 }
237
238 return ret;
239}
240
Alex Elder4b1d8202015-10-27 22:18:37 -0500241static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask)
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800242{
243 struct urb *urb = NULL;
244 unsigned long flags;
245 int i;
246
Alex Elder4b1d8202015-10-27 22:18:37 -0500247 spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800248
249 /* Look in our pool of allocated urbs first, as that's the "fastest" */
250 for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500251 if (es2->cport_out_urb_busy[i] == false &&
252 es2->cport_out_urb_cancelled[i] == false) {
253 es2->cport_out_urb_busy[i] = true;
254 urb = es2->cport_out_urb[i];
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800255 break;
256 }
257 }
Alex Elder4b1d8202015-10-27 22:18:37 -0500258 spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800259 if (urb)
260 return urb;
261
262 /*
263 * Crap, pool is empty, complain to the syslog and go allocate one
264 * dynamically as we have to succeed.
265 */
Alex Elder4b1d8202015-10-27 22:18:37 -0500266 dev_err(&es2->usb_dev->dev,
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800267 "No free CPort OUT urbs, having to dynamically allocate one!\n");
268 return usb_alloc_urb(0, gfp_mask);
269}
270
Alex Elder4b1d8202015-10-27 22:18:37 -0500271static void free_urb(struct es2_ap_dev *es2, struct urb *urb)
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800272{
273 unsigned long flags;
274 int i;
275 /*
276 * See if this was an urb in our pool, if so mark it "free", otherwise
277 * we need to free it ourselves.
278 */
Alex Elder4b1d8202015-10-27 22:18:37 -0500279 spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800280 for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500281 if (urb == es2->cport_out_urb[i]) {
282 es2->cport_out_urb_busy[i] = false;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800283 urb = NULL;
284 break;
285 }
286 }
Alex Elder4b1d8202015-10-27 22:18:37 -0500287 spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800288
289 /* If urb is not NULL, then we need to free this urb */
290 usb_free_urb(urb);
291}
292
293/*
Alex Elderd29b3d62015-06-13 11:02:08 -0500294 * We (ab)use the operation-message header pad bytes to transfer the
295 * cport id in order to minimise overhead.
296 */
297static void
298gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id)
299{
Alex Elder4bc13892015-06-13 11:02:11 -0500300 header->pad[0] = cport_id;
Alex Elderd29b3d62015-06-13 11:02:08 -0500301}
302
303/* Clear the pad bytes used for the CPort id */
304static void gb_message_cport_clear(struct gb_operation_msg_hdr *header)
305{
Alex Elder4bc13892015-06-13 11:02:11 -0500306 header->pad[0] = 0;
Alex Elderd29b3d62015-06-13 11:02:08 -0500307}
308
309/* Extract the CPort id packed into the header, and clear it */
310static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header)
311{
Alex Elder4bc13892015-06-13 11:02:11 -0500312 u16 cport_id = header->pad[0];
Alex Elderd29b3d62015-06-13 11:02:08 -0500313
314 gb_message_cport_clear(header);
315
316 return cport_id;
317}
318
319/*
Johan Hovold3e136cc2015-07-01 12:37:21 +0200320 * Returns zero if the message was successfully queued, or a negative errno
321 * otherwise.
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800322 */
Johan Hovold25376362015-11-03 18:03:23 +0100323static int message_send(struct gb_host_device *hd, u16 cport_id,
Johan Hovold7cf7bca2015-04-07 11:27:16 +0200324 struct gb_message *message, gfp_t gfp_mask)
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800325{
Alex Elder4b1d8202015-10-27 22:18:37 -0500326 struct es2_ap_dev *es2 = hd_to_es2(hd);
327 struct usb_device *udev = es2->usb_dev;
Johan Hovold7cf7bca2015-04-07 11:27:16 +0200328 size_t buffer_size;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800329 int retval;
330 struct urb *urb;
Alexandre Bailon1ff3dc92015-09-14 18:20:42 +0200331 int ep_pair;
Johan Hovold3e136cc2015-07-01 12:37:21 +0200332 unsigned long flags;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800333
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800334 /*
335 * The data actually transferred will include an indication
336 * of where the data should be sent. Do one last check of
337 * the target CPort id before filling it in.
338 */
Fabien Parent144670c2015-09-02 15:50:35 +0200339 if (!cport_id_valid(hd, cport_id)) {
Johan Hovold305a0312015-10-13 19:10:20 +0200340 dev_err(&udev->dev, "invalid destination cport 0x%02x\n",
341 cport_id);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200342 return -EINVAL;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800343 }
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800344
345 /* Find a free urb */
Alex Elder4b1d8202015-10-27 22:18:37 -0500346 urb = next_free_urb(es2, gfp_mask);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800347 if (!urb)
Johan Hovold3e136cc2015-07-01 12:37:21 +0200348 return -ENOMEM;
349
Alex Elder4b1d8202015-10-27 22:18:37 -0500350 spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200351 message->hcpriv = urb;
Alex Elder4b1d8202015-10-27 22:18:37 -0500352 spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800353
Alex Elderd29b3d62015-06-13 11:02:08 -0500354 /* Pack the cport id into the message header */
355 gb_message_cport_pack(message->header, cport_id);
Johan Hovold491e60d2015-04-07 11:27:20 +0200356
Alex Elder821c6202015-06-13 11:02:07 -0500357 buffer_size = sizeof(*message->header) + message->payload_size;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800358
Alex Elder4b1d8202015-10-27 22:18:37 -0500359 ep_pair = cport_to_ep_pair(es2, cport_id);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800360 usb_fill_bulk_urb(urb, udev,
Alexandre Bailon606addd2015-06-15 18:08:13 +0200361 usb_sndbulkpipe(udev,
Alex Elder4b1d8202015-10-27 22:18:37 -0500362 es2->cport_out[ep_pair].endpoint),
Alex Elder821c6202015-06-13 11:02:07 -0500363 message->buffer, buffer_size,
Johan Hovold7cf7bca2015-04-07 11:27:16 +0200364 cport_out_callback, message);
Alexandre Bailon977e2092015-08-31 09:00:16 +0200365 urb->transfer_flags |= URB_ZERO_PACKET;
Bryan O'Donoghue6872c462015-09-22 18:06:39 -0700366 trace_gb_host_device_send(hd, cport_id, buffer_size);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800367 retval = usb_submit_urb(urb, gfp_mask);
368 if (retval) {
Johan Hovold305a0312015-10-13 19:10:20 +0200369 dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200370
Alex Elder4b1d8202015-10-27 22:18:37 -0500371 spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200372 message->hcpriv = NULL;
Alex Elder4b1d8202015-10-27 22:18:37 -0500373 spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200374
Alex Elder4b1d8202015-10-27 22:18:37 -0500375 free_urb(es2, urb);
Alex Elderd29b3d62015-06-13 11:02:08 -0500376 gb_message_cport_clear(message->header);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200377
378 return retval;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800379 }
380
Johan Hovold3e136cc2015-07-01 12:37:21 +0200381 return 0;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800382}
383
384/*
Johan Hovold3e136cc2015-07-01 12:37:21 +0200385 * Can not be called in atomic context.
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800386 */
Johan Hovold3e136cc2015-07-01 12:37:21 +0200387static void message_cancel(struct gb_message *message)
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800388{
Johan Hovold25376362015-11-03 18:03:23 +0100389 struct gb_host_device *hd = message->operation->connection->hd;
Alex Elder4b1d8202015-10-27 22:18:37 -0500390 struct es2_ap_dev *es2 = hd_to_es2(hd);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200391 struct urb *urb;
392 int i;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800393
Johan Hovold3e136cc2015-07-01 12:37:21 +0200394 might_sleep();
395
Alex Elder4b1d8202015-10-27 22:18:37 -0500396 spin_lock_irq(&es2->cport_out_urb_lock);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200397 urb = message->hcpriv;
398
399 /* Prevent dynamically allocated urb from being deallocated. */
400 usb_get_urb(urb);
401
402 /* Prevent pre-allocated urb from being reused. */
403 for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500404 if (urb == es2->cport_out_urb[i]) {
405 es2->cport_out_urb_cancelled[i] = true;
Johan Hovold3e136cc2015-07-01 12:37:21 +0200406 break;
407 }
408 }
Alex Elder4b1d8202015-10-27 22:18:37 -0500409 spin_unlock_irq(&es2->cport_out_urb_lock);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200410
411 usb_kill_urb(urb);
412
413 if (i < NUM_CPORT_OUT_URB) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500414 spin_lock_irq(&es2->cport_out_urb_lock);
415 es2->cport_out_urb_cancelled[i] = false;
416 spin_unlock_irq(&es2->cport_out_urb_lock);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200417 }
418
419 usb_free_urb(urb);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800420}
421
Johan Hovold25376362015-11-03 18:03:23 +0100422static int cport_reset(struct gb_host_device *hd, u16 cport_id)
Fabien Parent82ee1e62015-10-13 17:34:50 +0200423{
Alex Elder4b1d8202015-10-27 22:18:37 -0500424 struct es2_ap_dev *es2 = hd_to_es2(hd);
425 struct usb_device *udev = es2->usb_dev;
Fabien Parent82ee1e62015-10-13 17:34:50 +0200426 int retval;
427
428 retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
429 REQUEST_RESET_CPORT,
430 USB_DIR_OUT | USB_TYPE_VENDOR |
431 USB_RECIP_INTERFACE, 0, cport_id,
Alex Elder4b1d8202015-10-27 22:18:37 -0500432 NULL, 0, ES2_TIMEOUT);
Fabien Parent82ee1e62015-10-13 17:34:50 +0200433 if (retval < 0) {
434 dev_err(&udev->dev, "failed to reset cport %hu: %d\n", cport_id,
435 retval);
436 return retval;
437 }
438
439 return 0;
440}
441
Johan Hovold25376362015-11-03 18:03:23 +0100442static int cport_enable(struct gb_host_device *hd, u16 cport_id)
Fabien Parent82ee1e62015-10-13 17:34:50 +0200443{
444 int retval;
445
446 if (cport_id != GB_SVC_CPORT_ID) {
447 retval = cport_reset(hd, cport_id);
448 if (retval)
449 return retval;
450 }
451
452 return 0;
453}
454
Johan Hovold25376362015-11-03 18:03:23 +0100455static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id)
Bryan O'Donoghue608ab2f2015-10-15 16:10:41 +0100456{
457 int retval;
Alex Elder4b1d8202015-10-27 22:18:37 -0500458 struct es2_ap_dev *es2 = hd_to_es2(hd);
459 struct usb_device *udev = es2->usb_dev;
Bryan O'Donoghue608ab2f2015-10-15 16:10:41 +0100460
461 if (!cport_id_valid(hd, cport_id)) {
462 dev_err(&udev->dev, "invalid destination cport 0x%02x\n",
463 cport_id);
464 return -EINVAL;
465 }
466
467 retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
468 REQUEST_LATENCY_TAG_EN,
469 USB_DIR_OUT | USB_TYPE_VENDOR |
470 USB_RECIP_INTERFACE, cport_id, 0, NULL,
Alex Elder4b1d8202015-10-27 22:18:37 -0500471 0, ES2_TIMEOUT);
Bryan O'Donoghue608ab2f2015-10-15 16:10:41 +0100472
473 if (retval < 0)
474 dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n",
475 cport_id);
476 return retval;
477}
478
Johan Hovold25376362015-11-03 18:03:23 +0100479static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id)
Bryan O'Donoghue608ab2f2015-10-15 16:10:41 +0100480{
481 int retval;
Alex Elder4b1d8202015-10-27 22:18:37 -0500482 struct es2_ap_dev *es2 = hd_to_es2(hd);
483 struct usb_device *udev = es2->usb_dev;
Bryan O'Donoghue608ab2f2015-10-15 16:10:41 +0100484
485 if (!cport_id_valid(hd, cport_id)) {
486 dev_err(&udev->dev, "invalid destination cport 0x%02x\n",
487 cport_id);
488 return -EINVAL;
489 }
490
491 retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
492 REQUEST_LATENCY_TAG_DIS,
493 USB_DIR_OUT | USB_TYPE_VENDOR |
494 USB_RECIP_INTERFACE, cport_id, 0, NULL,
Alex Elder4b1d8202015-10-27 22:18:37 -0500495 0, ES2_TIMEOUT);
Bryan O'Donoghue608ab2f2015-10-15 16:10:41 +0100496
497 if (retval < 0)
498 dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n",
499 cport_id);
500 return retval;
501}
502
Johan Hovolda8cc0202015-11-03 18:03:24 +0100503static struct gb_hd_driver es2_driver = {
Alex Elder4b1d8202015-10-27 22:18:37 -0500504 .hd_priv_size = sizeof(struct es2_ap_dev),
Johan Hovold7cf7bca2015-04-07 11:27:16 +0200505 .message_send = message_send,
506 .message_cancel = message_cancel,
Fabien Parent82ee1e62015-10-13 17:34:50 +0200507 .cport_enable = cport_enable,
Bryan O'Donoghue608ab2f2015-10-15 16:10:41 +0100508 .latency_tag_enable = latency_tag_enable,
509 .latency_tag_disable = latency_tag_disable,
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800510};
511
512/* Common function to report consistent warnings based on URB status */
513static int check_urb_status(struct urb *urb)
514{
515 struct device *dev = &urb->dev->dev;
516 int status = urb->status;
517
518 switch (status) {
519 case 0:
520 return 0;
521
522 case -EOVERFLOW:
523 dev_err(dev, "%s: overflow actual length is %d\n",
524 __func__, urb->actual_length);
525 case -ECONNRESET:
526 case -ENOENT:
527 case -ESHUTDOWN:
528 case -EILSEQ:
529 case -EPROTO:
530 /* device is gone, stop sending */
531 return status;
532 }
533 dev_err(dev, "%s: unknown status %d\n", __func__, status);
534
535 return -EAGAIN;
536}
537
538static void ap_disconnect(struct usb_interface *interface)
539{
Alex Elder4b1d8202015-10-27 22:18:37 -0500540 struct es2_ap_dev *es2;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800541 struct usb_device *udev;
Johan Hovolda51e85512015-11-04 18:55:12 +0100542 int *cport_to_ep;
Alexandre Bailon606addd2015-06-15 18:08:13 +0200543 int bulk_in;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800544 int i;
545
Alex Elder4b1d8202015-10-27 22:18:37 -0500546 es2 = usb_get_intfdata(interface);
547 if (!es2)
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800548 return;
549
Alex Elder4b1d8202015-10-27 22:18:37 -0500550 usb_log_disable(es2);
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100551
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800552 /* Tear down everything! */
553 for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500554 struct urb *urb = es2->cport_out_urb[i];
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800555
556 if (!urb)
557 break;
558 usb_kill_urb(urb);
559 usb_free_urb(urb);
Alex Elder4b1d8202015-10-27 22:18:37 -0500560 es2->cport_out_urb[i] = NULL;
561 es2->cport_out_urb_busy[i] = false; /* just to be anal */
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800562 }
563
Alexandre Bailon606addd2015-06-15 18:08:13 +0200564 for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500565 struct es2_cport_in *cport_in = &es2->cport_in[bulk_in];
566
Alexandre Bailon606addd2015-06-15 18:08:13 +0200567 for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
568 struct urb *urb = cport_in->urb[i];
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800569
Alexandre Bailon606addd2015-06-15 18:08:13 +0200570 if (!urb)
571 break;
572 usb_kill_urb(urb);
573 usb_free_urb(urb);
574 kfree(cport_in->buffer[i]);
575 cport_in->buffer[i] = NULL;
576 }
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800577 }
578
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800579 usb_set_intfdata(interface, NULL);
Alex Elder4b1d8202015-10-27 22:18:37 -0500580 udev = es2->usb_dev;
Johan Hovolda51e85512015-11-04 18:55:12 +0100581 cport_to_ep = es2->cport_to_ep;
Johan Hovoldd6e139b2015-11-03 18:03:25 +0100582 gb_hd_remove(es2->hd);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800583
Johan Hovolda51e85512015-11-04 18:55:12 +0100584 kfree(cport_to_ep);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800585 usb_put_dev(udev);
586}
587
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800588static void cport_in_callback(struct urb *urb)
589{
Johan Hovold25376362015-11-03 18:03:23 +0100590 struct gb_host_device *hd = urb->context;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800591 struct device *dev = &urb->dev->dev;
Johan Hovold491e60d2015-04-07 11:27:20 +0200592 struct gb_operation_msg_hdr *header;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800593 int status = check_urb_status(urb);
594 int retval;
595 u16 cport_id;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800596
597 if (status) {
598 if ((status == -EAGAIN) || (status == -EPROTO))
599 goto exit;
600 dev_err(dev, "urb cport in error %d (dropped)\n", status);
601 return;
602 }
603
Johan Hovold491e60d2015-04-07 11:27:20 +0200604 if (urb->actual_length < sizeof(*header)) {
Johan Hovold305a0312015-10-13 19:10:20 +0200605 dev_err(dev, "short message received\n");
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800606 goto exit;
607 }
608
Alex Elderd29b3d62015-06-13 11:02:08 -0500609 /* Extract the CPort id, which is packed in the message header */
Johan Hovold491e60d2015-04-07 11:27:20 +0200610 header = urb->transfer_buffer;
Alex Elderd29b3d62015-06-13 11:02:08 -0500611 cport_id = gb_message_cport_unpack(header);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800612
Bryan O'Donoghue6872c462015-09-22 18:06:39 -0700613 if (cport_id_valid(hd, cport_id)) {
614 trace_gb_host_device_recv(hd, cport_id, urb->actual_length);
Alex Elder821c6202015-06-13 11:02:07 -0500615 greybus_data_rcvd(hd, cport_id, urb->transfer_buffer,
Johan Hovold491e60d2015-04-07 11:27:20 +0200616 urb->actual_length);
Bryan O'Donoghue6872c462015-09-22 18:06:39 -0700617 } else {
Johan Hovold305a0312015-10-13 19:10:20 +0200618 dev_err(dev, "invalid cport id 0x%02x received\n", cport_id);
Bryan O'Donoghue6872c462015-09-22 18:06:39 -0700619 }
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800620exit:
621 /* put our urb back in the request pool */
622 retval = usb_submit_urb(urb, GFP_ATOMIC);
623 if (retval)
Johan Hovold305a0312015-10-13 19:10:20 +0200624 dev_err(dev, "failed to resubmit in-urb: %d\n", retval);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800625}
626
627static void cport_out_callback(struct urb *urb)
628{
Johan Hovold7cf7bca2015-04-07 11:27:16 +0200629 struct gb_message *message = urb->context;
Johan Hovold25376362015-11-03 18:03:23 +0100630 struct gb_host_device *hd = message->operation->connection->hd;
Alex Elder4b1d8202015-10-27 22:18:37 -0500631 struct es2_ap_dev *es2 = hd_to_es2(hd);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800632 int status = check_urb_status(urb);
Johan Hovold3e136cc2015-07-01 12:37:21 +0200633 unsigned long flags;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800634
Alex Elderd29b3d62015-06-13 11:02:08 -0500635 gb_message_cport_clear(message->header);
Johan Hovold491e60d2015-04-07 11:27:20 +0200636
Alex Elder4b1d8202015-10-27 22:18:37 -0500637 spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
Johan Hovold58c85122015-09-26 14:37:59 -0700638 message->hcpriv = NULL;
Alex Elder4b1d8202015-10-27 22:18:37 -0500639 spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
Johan Hovold58c85122015-09-26 14:37:59 -0700640
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800641 /*
Johan Hovold7cf7bca2015-04-07 11:27:16 +0200642 * Tell the submitter that the message send (attempt) is
643 * complete, and report the status.
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800644 */
Johan Hovold7cf7bca2015-04-07 11:27:16 +0200645 greybus_message_sent(hd, message, status);
646
Alex Elder4b1d8202015-10-27 22:18:37 -0500647 free_urb(es2, urb);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800648}
649
Johan Hovoldc15ccab2015-04-07 11:27:12 +0200650#define APB1_LOG_MSG_SIZE 64
Alex Elder3be0e172015-10-27 22:18:41 -0500651static void apb_log_get(struct es2_ap_dev *es2, char *buf)
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100652{
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100653 int retval;
654
655 /* SVC messages go down our control pipe */
656 do {
Alex Elder4b1d8202015-10-27 22:18:37 -0500657 retval = usb_control_msg(es2->usb_dev,
658 usb_rcvctrlpipe(es2->usb_dev, 0),
Alexandre Bailon611c17392015-06-15 18:08:11 +0200659 REQUEST_LOG,
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100660 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
661 0x00, 0x00,
662 buf,
Johan Hovoldc15ccab2015-04-07 11:27:12 +0200663 APB1_LOG_MSG_SIZE,
Alex Elder4b1d8202015-10-27 22:18:37 -0500664 ES2_TIMEOUT);
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100665 if (retval > 0)
Alex Elder3be0e172015-10-27 22:18:41 -0500666 kfifo_in(&es2->apb_log_fifo, buf, retval);
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100667 } while (retval > 0);
668}
669
Alex Elder3be0e172015-10-27 22:18:41 -0500670static int apb_log_poll(void *data)
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100671{
Alex Elder4b1d8202015-10-27 22:18:37 -0500672 struct es2_ap_dev *es2 = data;
Johan Hovoldc15ccab2015-04-07 11:27:12 +0200673 char *buf;
674
675 buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL);
676 if (!buf)
677 return -ENOMEM;
678
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100679 while (!kthread_should_stop()) {
680 msleep(1000);
Alex Elder3be0e172015-10-27 22:18:41 -0500681 apb_log_get(es2, buf);
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100682 }
Johan Hovoldc15ccab2015-04-07 11:27:12 +0200683
684 kfree(buf);
685
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100686 return 0;
687}
688
Alex Elder3be0e172015-10-27 22:18:41 -0500689static ssize_t apb_log_read(struct file *f, char __user *buf,
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100690 size_t count, loff_t *ppos)
691{
Alex Elder8995a392015-10-27 22:18:39 -0500692 struct es2_ap_dev *es2 = f->f_inode->i_private;
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100693 ssize_t ret;
694 size_t copied;
695 char *tmp_buf;
696
697 if (count > APB1_LOG_SIZE)
698 count = APB1_LOG_SIZE;
699
700 tmp_buf = kmalloc(count, GFP_KERNEL);
701 if (!tmp_buf)
702 return -ENOMEM;
703
Alex Elder3be0e172015-10-27 22:18:41 -0500704 copied = kfifo_out(&es2->apb_log_fifo, tmp_buf, count);
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100705 ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied);
706
707 kfree(tmp_buf);
708
709 return ret;
710}
711
Alex Elder3be0e172015-10-27 22:18:41 -0500712static const struct file_operations apb_log_fops = {
713 .read = apb_log_read,
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100714};
715
Alex Elder4b1d8202015-10-27 22:18:37 -0500716static void usb_log_enable(struct es2_ap_dev *es2)
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100717{
Alex Elder3be0e172015-10-27 22:18:41 -0500718 if (!IS_ERR_OR_NULL(es2->apb_log_task))
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100719 return;
720
721 /* get log from APB1 */
Alex Elder3be0e172015-10-27 22:18:41 -0500722 es2->apb_log_task = kthread_run(apb_log_poll, es2, "apb_log");
723 if (IS_ERR(es2->apb_log_task))
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100724 return;
Alex Elder3be0e172015-10-27 22:18:41 -0500725 /* XXX We will need to rename this per APB */
726 es2->apb_log_dentry = debugfs_create_file("apb_log", S_IRUGO,
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100727 gb_debugfs_get(), NULL,
Alex Elder3be0e172015-10-27 22:18:41 -0500728 &apb_log_fops);
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100729}
730
Alex Elder4b1d8202015-10-27 22:18:37 -0500731static void usb_log_disable(struct es2_ap_dev *es2)
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100732{
Alex Elder3be0e172015-10-27 22:18:41 -0500733 if (IS_ERR_OR_NULL(es2->apb_log_task))
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100734 return;
735
Alex Elder3be0e172015-10-27 22:18:41 -0500736 debugfs_remove(es2->apb_log_dentry);
737 es2->apb_log_dentry = NULL;
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100738
Alex Elder3be0e172015-10-27 22:18:41 -0500739 kthread_stop(es2->apb_log_task);
740 es2->apb_log_task = NULL;
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100741}
742
Alex Elder3be0e172015-10-27 22:18:41 -0500743static ssize_t apb_log_enable_read(struct file *f, char __user *buf,
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100744 size_t count, loff_t *ppos)
745{
Alex Elder1482b3e2015-10-27 22:18:38 -0500746 struct es2_ap_dev *es2 = f->f_inode->i_private;
Alex Elder3be0e172015-10-27 22:18:41 -0500747 int enable = !IS_ERR_OR_NULL(es2->apb_log_task);
Alex Elder4b1d8202015-10-27 22:18:37 -0500748 char tmp_buf[3];
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100749
750 sprintf(tmp_buf, "%d\n", enable);
751 return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3);
752}
753
Alex Elder3be0e172015-10-27 22:18:41 -0500754static ssize_t apb_log_enable_write(struct file *f, const char __user *buf,
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100755 size_t count, loff_t *ppos)
756{
757 int enable;
758 ssize_t retval;
Alex Elder1482b3e2015-10-27 22:18:38 -0500759 struct es2_ap_dev *es2 = f->f_inode->i_private;
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100760
761 retval = kstrtoint_from_user(buf, count, 10, &enable);
762 if (retval)
763 return retval;
764
765 if (enable)
Alex Elder4b1d8202015-10-27 22:18:37 -0500766 usb_log_enable(es2);
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100767 else
Alex Elder4b1d8202015-10-27 22:18:37 -0500768 usb_log_disable(es2);
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100769
770 return count;
771}
772
Alex Elder3be0e172015-10-27 22:18:41 -0500773static const struct file_operations apb_log_enable_fops = {
774 .read = apb_log_enable_read,
775 .write = apb_log_enable_write,
Greg Kroah-Hartmanca3ec292015-03-27 11:38:07 +0100776};
777
Alex Elder3be0e172015-10-27 22:18:41 -0500778static int apb_get_cport_count(struct usb_device *udev)
Fabien Parent24a61122015-09-02 15:50:37 +0200779{
780 int retval;
781 __le16 *cport_count;
782
783 cport_count = kmalloc(sizeof(*cport_count), GFP_KERNEL);
784 if (!cport_count)
785 return -ENOMEM;
786
787 retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
788 REQUEST_CPORT_COUNT,
789 USB_DIR_IN | USB_TYPE_VENDOR |
790 USB_RECIP_INTERFACE, 0, 0, cport_count,
Alex Elder4b1d8202015-10-27 22:18:37 -0500791 sizeof(*cport_count), ES2_TIMEOUT);
Fabien Parent24a61122015-09-02 15:50:37 +0200792 if (retval < 0) {
793 dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
794 retval);
795 goto out;
796 }
797
798 retval = le16_to_cpu(*cport_count);
799
800 /* We need to fit a CPort ID in one byte of a message header */
801 if (retval > U8_MAX) {
802 retval = U8_MAX;
803 dev_warn(&udev->dev, "Limiting number of CPorts to U8_MAX\n");
804 }
805
806out:
807 kfree(cport_count);
808 return retval;
809}
810
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800811/*
Johan Hovold4d5c4462015-11-02 11:56:57 +0100812 * The ES2 USB Bridge device has 15 endpoints
813 * 1 Control - usual USB stuff + AP -> APBridgeA messages
814 * 7 Bulk IN - CPort data in
815 * 7 Bulk OUT - CPort data out
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800816 */
817static int ap_probe(struct usb_interface *interface,
818 const struct usb_device_id *id)
819{
Alex Elder4b1d8202015-10-27 22:18:37 -0500820 struct es2_ap_dev *es2;
Johan Hovold25376362015-11-03 18:03:23 +0100821 struct gb_host_device *hd;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800822 struct usb_device *udev;
823 struct usb_host_interface *iface_desc;
824 struct usb_endpoint_descriptor *endpoint;
Alexandre Bailon606addd2015-06-15 18:08:13 +0200825 int bulk_in = 0;
826 int bulk_out = 0;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800827 int retval = -ENOMEM;
828 int i;
Fabien Parent24a61122015-09-02 15:50:37 +0200829 int num_cports;
Alex Elder4bc13892015-06-13 11:02:11 -0500830
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800831 udev = usb_get_dev(interface_to_usbdev(interface));
832
Alex Elder3be0e172015-10-27 22:18:41 -0500833 num_cports = apb_get_cport_count(udev);
Fabien Parent24a61122015-09-02 15:50:37 +0200834 if (num_cports < 0) {
835 usb_put_dev(udev);
836 dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
837 num_cports);
838 return num_cports;
839 }
840
Johan Hovoldd6e139b2015-11-03 18:03:25 +0100841 hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX,
842 num_cports);
Alex Elder8ea70fe2015-05-22 09:52:45 -0500843 if (IS_ERR(hd)) {
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800844 usb_put_dev(udev);
Alex Elder8ea70fe2015-05-22 09:52:45 -0500845 return PTR_ERR(hd);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800846 }
847
Alex Elder4b1d8202015-10-27 22:18:37 -0500848 es2 = hd_to_es2(hd);
849 es2->hd = hd;
850 es2->usb_intf = interface;
851 es2->usb_dev = udev;
852 spin_lock_init(&es2->cport_out_urb_lock);
Alex Elder3be0e172015-10-27 22:18:41 -0500853 INIT_KFIFO(es2->apb_log_fifo);
Alex Elder4b1d8202015-10-27 22:18:37 -0500854 usb_set_intfdata(interface, es2);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800855
Alex Elder4b1d8202015-10-27 22:18:37 -0500856 es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep),
Fabien Parentc011d552015-09-02 15:50:36 +0200857 GFP_KERNEL);
Alex Elder4b1d8202015-10-27 22:18:37 -0500858 if (!es2->cport_to_ep) {
Fabien Parentc011d552015-09-02 15:50:36 +0200859 retval = -ENOMEM;
860 goto error;
861 }
862
Johan Hovold4d5c4462015-11-02 11:56:57 +0100863 /* find all bulk endpoints */
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800864 iface_desc = interface->cur_altsetting;
865 for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
866 endpoint = &iface_desc->endpoint[i].desc;
867
Greg Kroah-Hartmanb767ee42015-07-24 17:09:48 -0700868 if (usb_endpoint_is_bulk_in(endpoint)) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500869 es2->cport_in[bulk_in++].endpoint =
Alexandre Bailon606addd2015-06-15 18:08:13 +0200870 endpoint->bEndpointAddress;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800871 } else if (usb_endpoint_is_bulk_out(endpoint)) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500872 es2->cport_out[bulk_out++].endpoint =
Alexandre Bailon606addd2015-06-15 18:08:13 +0200873 endpoint->bEndpointAddress;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800874 } else {
875 dev_err(&udev->dev,
876 "Unknown endpoint type found, address %x\n",
877 endpoint->bEndpointAddress);
878 }
879 }
Johan Hovoldb6d80852015-11-04 18:55:13 +0100880 if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) {
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800881 dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
882 goto error;
883 }
884
Johan Hovold0ce68ce42015-11-04 18:55:14 +0100885 /* Allocate buffers for our cport in messages */
Alexandre Bailon606addd2015-06-15 18:08:13 +0200886 for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) {
Alex Elder4b1d8202015-10-27 22:18:37 -0500887 struct es2_cport_in *cport_in = &es2->cport_in[bulk_in];
888
Alexandre Bailon606addd2015-06-15 18:08:13 +0200889 for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
890 struct urb *urb;
891 u8 *buffer;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800892
Alexandre Bailon606addd2015-06-15 18:08:13 +0200893 urb = usb_alloc_urb(0, GFP_KERNEL);
894 if (!urb)
895 goto error;
Alex Elder4b1d8202015-10-27 22:18:37 -0500896 buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL);
Alexandre Bailon606addd2015-06-15 18:08:13 +0200897 if (!buffer)
898 goto error;
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800899
Alexandre Bailon606addd2015-06-15 18:08:13 +0200900 usb_fill_bulk_urb(urb, udev,
901 usb_rcvbulkpipe(udev,
902 cport_in->endpoint),
Alex Elder4b1d8202015-10-27 22:18:37 -0500903 buffer, ES2_GBUF_MSG_SIZE_MAX,
Alexandre Bailon606addd2015-06-15 18:08:13 +0200904 cport_in_callback, hd);
905 cport_in->urb[i] = urb;
906 cport_in->buffer[i] = buffer;
Alexandre Bailon606addd2015-06-15 18:08:13 +0200907 }
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800908 }
909
910 /* Allocate urbs for our CPort OUT messages */
911 for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
912 struct urb *urb;
913
914 urb = usb_alloc_urb(0, GFP_KERNEL);
915 if (!urb)
916 goto error;
917
Alex Elder4b1d8202015-10-27 22:18:37 -0500918 es2->cport_out_urb[i] = urb;
919 es2->cport_out_urb_busy[i] = false; /* just to be anal */
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800920 }
921
Alex Elder3be0e172015-10-27 22:18:41 -0500922 /* XXX We will need to rename this per APB */
923 es2->apb_log_enable_dentry = debugfs_create_file("apb_log_enable",
Johan Hovold86f918e2015-06-23 14:17:41 +0200924 (S_IWUSR | S_IRUGO),
Alex Elder4b1d8202015-10-27 22:18:37 -0500925 gb_debugfs_get(), es2,
Alex Elder3be0e172015-10-27 22:18:41 -0500926 &apb_log_enable_fops);
Johan Hovold0ce68ce42015-11-04 18:55:14 +0100927
928 for (i = 0; i < NUM_BULKS; ++i) {
929 retval = es2_cport_in_enable(es2, &es2->cport_in[i]);
930 if (retval)
931 goto error;
932 }
933
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800934 return 0;
935error:
936 ap_disconnect(interface);
937
938 return retval;
939}
940
Alex Elder4b1d8202015-10-27 22:18:37 -0500941static struct usb_driver es2_ap_driver = {
Rob Herringc13c8bf2015-05-05 11:04:22 -0500942 .name = "es2_ap_driver",
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800943 .probe = ap_probe,
944 .disconnect = ap_disconnect,
945 .id_table = id_table,
946};
947
Alex Elder4b1d8202015-10-27 22:18:37 -0500948module_usb_driver(es2_ap_driver);
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800949
Greg Kroah-Hartman6cf42a42015-04-13 19:51:33 +0200950MODULE_LICENSE("GPL v2");
Greg Kroah-Hartmanf5870272015-01-21 10:24:15 +0800951MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");