1b81beec9SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b81beec9SGreg Kroah-Hartman /*
3b81beec9SGreg Kroah-Hartman * Greybus "AP" USB driver for "ES2" controller chips
4b81beec9SGreg Kroah-Hartman *
5b81beec9SGreg Kroah-Hartman * Copyright 2014-2015 Google Inc.
6b81beec9SGreg Kroah-Hartman * Copyright 2014-2015 Linaro Ltd.
7b81beec9SGreg Kroah-Hartman */
8b81beec9SGreg Kroah-Hartman #include <linux/kthread.h>
9b81beec9SGreg Kroah-Hartman #include <linux/sizes.h>
10b81beec9SGreg Kroah-Hartman #include <linux/usb.h>
11b81beec9SGreg Kroah-Hartman #include <linux/kfifo.h>
12b81beec9SGreg Kroah-Hartman #include <linux/debugfs.h>
13b81beec9SGreg Kroah-Hartman #include <linux/list.h>
14b81beec9SGreg Kroah-Hartman #include <linux/greybus.h>
15b81beec9SGreg Kroah-Hartman #include <asm/unaligned.h>
16b81beec9SGreg Kroah-Hartman
17b81beec9SGreg Kroah-Hartman #include "arpc.h"
18b81beec9SGreg Kroah-Hartman #include "greybus_trace.h"
19b81beec9SGreg Kroah-Hartman
20b81beec9SGreg Kroah-Hartman
21b81beec9SGreg Kroah-Hartman /* Default timeout for USB vendor requests. */
22b81beec9SGreg Kroah-Hartman #define ES2_USB_CTRL_TIMEOUT 500
23b81beec9SGreg Kroah-Hartman
24b81beec9SGreg Kroah-Hartman /* Default timeout for ARPC CPort requests */
25b81beec9SGreg Kroah-Hartman #define ES2_ARPC_CPORT_TIMEOUT 500
26b81beec9SGreg Kroah-Hartman
27b81beec9SGreg Kroah-Hartman /* Fixed CPort numbers */
28b81beec9SGreg Kroah-Hartman #define ES2_CPORT_CDSI0 16
29b81beec9SGreg Kroah-Hartman #define ES2_CPORT_CDSI1 17
30b81beec9SGreg Kroah-Hartman
31b81beec9SGreg Kroah-Hartman /* Memory sizes for the buffers sent to/from the ES2 controller */
32b81beec9SGreg Kroah-Hartman #define ES2_GBUF_MSG_SIZE_MAX 2048
33b81beec9SGreg Kroah-Hartman
34b81beec9SGreg Kroah-Hartman /* Memory sizes for the ARPC buffers */
35b81beec9SGreg Kroah-Hartman #define ARPC_OUT_SIZE_MAX U16_MAX
36b81beec9SGreg Kroah-Hartman #define ARPC_IN_SIZE_MAX 128
37b81beec9SGreg Kroah-Hartman
38b81beec9SGreg Kroah-Hartman static const struct usb_device_id id_table[] = {
39b81beec9SGreg Kroah-Hartman { USB_DEVICE(0x18d1, 0x1eaf) },
40b81beec9SGreg Kroah-Hartman { },
41b81beec9SGreg Kroah-Hartman };
42b81beec9SGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, id_table);
43b81beec9SGreg Kroah-Hartman
44b81beec9SGreg Kroah-Hartman #define APB1_LOG_SIZE SZ_16K
45b81beec9SGreg Kroah-Hartman
46b81beec9SGreg Kroah-Hartman /*
47b81beec9SGreg Kroah-Hartman * Number of CPort IN urbs in flight at any point in time.
48b81beec9SGreg Kroah-Hartman * Adjust if we are having stalls in the USB buffer due to not enough urbs in
49b81beec9SGreg Kroah-Hartman * flight.
50b81beec9SGreg Kroah-Hartman */
51b81beec9SGreg Kroah-Hartman #define NUM_CPORT_IN_URB 4
52b81beec9SGreg Kroah-Hartman
53b81beec9SGreg Kroah-Hartman /* Number of CPort OUT urbs in flight at any point in time.
54b81beec9SGreg Kroah-Hartman * Adjust if we get messages saying we are out of urbs in the system log.
55b81beec9SGreg Kroah-Hartman */
56b81beec9SGreg Kroah-Hartman #define NUM_CPORT_OUT_URB 8
57b81beec9SGreg Kroah-Hartman
58b81beec9SGreg Kroah-Hartman /*
59b81beec9SGreg Kroah-Hartman * Number of ARPC in urbs in flight at any point in time.
60b81beec9SGreg Kroah-Hartman */
61b81beec9SGreg Kroah-Hartman #define NUM_ARPC_IN_URB 2
62b81beec9SGreg Kroah-Hartman
63b81beec9SGreg Kroah-Hartman /*
64b81beec9SGreg Kroah-Hartman * @endpoint: bulk in endpoint for CPort data
65b81beec9SGreg Kroah-Hartman * @urb: array of urbs for the CPort in messages
66b81beec9SGreg Kroah-Hartman * @buffer: array of buffers for the @cport_in_urb urbs
67b81beec9SGreg Kroah-Hartman */
68b81beec9SGreg Kroah-Hartman struct es2_cport_in {
69b81beec9SGreg Kroah-Hartman __u8 endpoint;
70b81beec9SGreg Kroah-Hartman struct urb *urb[NUM_CPORT_IN_URB];
71b81beec9SGreg Kroah-Hartman u8 *buffer[NUM_CPORT_IN_URB];
72b81beec9SGreg Kroah-Hartman };
73b81beec9SGreg Kroah-Hartman
74b81beec9SGreg Kroah-Hartman /**
7539d2a789SRandy Dunlap * struct es2_ap_dev - ES2 USB Bridge to AP structure
76b81beec9SGreg Kroah-Hartman * @usb_dev: pointer to the USB device we are.
77b81beec9SGreg Kroah-Hartman * @usb_intf: pointer to the USB interface we are bound to.
78b81beec9SGreg Kroah-Hartman * @hd: pointer to our gb_host_device structure
7939d2a789SRandy Dunlap *
80b81beec9SGreg Kroah-Hartman * @cport_in: endpoint, urbs and buffer for cport in messages
81*909c648eSJason Wang * @cport_out_endpoint: endpoint for cport out messages
82b81beec9SGreg Kroah-Hartman * @cport_out_urb: array of urbs for the CPort out messages
83b81beec9SGreg Kroah-Hartman * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or
84b81beec9SGreg Kroah-Hartman * not.
85b81beec9SGreg Kroah-Hartman * @cport_out_urb_cancelled: array of flags indicating whether the
86b81beec9SGreg Kroah-Hartman * corresponding @cport_out_urb is being cancelled
87b81beec9SGreg Kroah-Hartman * @cport_out_urb_lock: locks the @cport_out_urb_busy "list"
8839d2a789SRandy Dunlap * @cdsi1_in_use: true if cport CDSI1 is in use
89b81beec9SGreg Kroah-Hartman * @apb_log_task: task pointer for logging thread
90b81beec9SGreg Kroah-Hartman * @apb_log_dentry: file system entry for the log file interface
91b81beec9SGreg Kroah-Hartman * @apb_log_enable_dentry: file system entry for enabling logging
92b81beec9SGreg Kroah-Hartman * @apb_log_fifo: kernel FIFO to carry logged data
93b81beec9SGreg Kroah-Hartman * @arpc_urb: array of urbs for the ARPC in messages
94b81beec9SGreg Kroah-Hartman * @arpc_buffer: array of buffers for the @arpc_urb urbs
95b81beec9SGreg Kroah-Hartman * @arpc_endpoint_in: bulk in endpoint for APBridgeA RPC
96b81beec9SGreg Kroah-Hartman * @arpc_id_cycle: gives an unique id to ARPC
97b81beec9SGreg Kroah-Hartman * @arpc_lock: locks ARPC list
98b81beec9SGreg Kroah-Hartman * @arpcs: list of in progress ARPCs
99b81beec9SGreg Kroah-Hartman */
100b81beec9SGreg Kroah-Hartman struct es2_ap_dev {
101b81beec9SGreg Kroah-Hartman struct usb_device *usb_dev;
102b81beec9SGreg Kroah-Hartman struct usb_interface *usb_intf;
103b81beec9SGreg Kroah-Hartman struct gb_host_device *hd;
104b81beec9SGreg Kroah-Hartman
105b81beec9SGreg Kroah-Hartman struct es2_cport_in cport_in;
106b81beec9SGreg Kroah-Hartman __u8 cport_out_endpoint;
107b81beec9SGreg Kroah-Hartman struct urb *cport_out_urb[NUM_CPORT_OUT_URB];
108b81beec9SGreg Kroah-Hartman bool cport_out_urb_busy[NUM_CPORT_OUT_URB];
109b81beec9SGreg Kroah-Hartman bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB];
110b81beec9SGreg Kroah-Hartman spinlock_t cport_out_urb_lock;
111b81beec9SGreg Kroah-Hartman
112b81beec9SGreg Kroah-Hartman bool cdsi1_in_use;
113b81beec9SGreg Kroah-Hartman
114b81beec9SGreg Kroah-Hartman struct task_struct *apb_log_task;
115b81beec9SGreg Kroah-Hartman struct dentry *apb_log_dentry;
116b81beec9SGreg Kroah-Hartman struct dentry *apb_log_enable_dentry;
117b81beec9SGreg Kroah-Hartman DECLARE_KFIFO(apb_log_fifo, char, APB1_LOG_SIZE);
118b81beec9SGreg Kroah-Hartman
119b81beec9SGreg Kroah-Hartman __u8 arpc_endpoint_in;
120b81beec9SGreg Kroah-Hartman struct urb *arpc_urb[NUM_ARPC_IN_URB];
121b81beec9SGreg Kroah-Hartman u8 *arpc_buffer[NUM_ARPC_IN_URB];
122b81beec9SGreg Kroah-Hartman
123b81beec9SGreg Kroah-Hartman int arpc_id_cycle;
124b81beec9SGreg Kroah-Hartman spinlock_t arpc_lock;
125b81beec9SGreg Kroah-Hartman struct list_head arpcs;
126b81beec9SGreg Kroah-Hartman };
127b81beec9SGreg Kroah-Hartman
128b81beec9SGreg Kroah-Hartman struct arpc {
129b81beec9SGreg Kroah-Hartman struct list_head list;
130b81beec9SGreg Kroah-Hartman struct arpc_request_message *req;
131b81beec9SGreg Kroah-Hartman struct arpc_response_message *resp;
132b81beec9SGreg Kroah-Hartman struct completion response_received;
133b81beec9SGreg Kroah-Hartman bool active;
134b81beec9SGreg Kroah-Hartman };
135b81beec9SGreg Kroah-Hartman
hd_to_es2(struct gb_host_device * hd)136b81beec9SGreg Kroah-Hartman static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
137b81beec9SGreg Kroah-Hartman {
138b81beec9SGreg Kroah-Hartman return (struct es2_ap_dev *)&hd->hd_priv;
139b81beec9SGreg Kroah-Hartman }
140b81beec9SGreg Kroah-Hartman
141b81beec9SGreg Kroah-Hartman static void cport_out_callback(struct urb *urb);
142b81beec9SGreg Kroah-Hartman static void usb_log_enable(struct es2_ap_dev *es2);
143b81beec9SGreg Kroah-Hartman static void usb_log_disable(struct es2_ap_dev *es2);
144b81beec9SGreg Kroah-Hartman static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload,
145b81beec9SGreg Kroah-Hartman size_t size, int *result, unsigned int timeout);
146b81beec9SGreg Kroah-Hartman
output_sync(struct es2_ap_dev * es2,void * req,u16 size,u8 cmd)147b81beec9SGreg Kroah-Hartman static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
148b81beec9SGreg Kroah-Hartman {
149b81beec9SGreg Kroah-Hartman struct usb_device *udev = es2->usb_dev;
150b81beec9SGreg Kroah-Hartman u8 *data;
151b81beec9SGreg Kroah-Hartman int retval;
152b81beec9SGreg Kroah-Hartman
153b81beec9SGreg Kroah-Hartman data = kmemdup(req, size, GFP_KERNEL);
154b81beec9SGreg Kroah-Hartman if (!data)
155b81beec9SGreg Kroah-Hartman return -ENOMEM;
156b81beec9SGreg Kroah-Hartman
157b81beec9SGreg Kroah-Hartman retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
158b81beec9SGreg Kroah-Hartman cmd,
159b81beec9SGreg Kroah-Hartman USB_DIR_OUT | USB_TYPE_VENDOR |
160b81beec9SGreg Kroah-Hartman USB_RECIP_INTERFACE,
161b81beec9SGreg Kroah-Hartman 0, 0, data, size, ES2_USB_CTRL_TIMEOUT);
162b81beec9SGreg Kroah-Hartman if (retval < 0)
163b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "%s: return error %d\n", __func__, retval);
164b81beec9SGreg Kroah-Hartman else
165b81beec9SGreg Kroah-Hartman retval = 0;
166b81beec9SGreg Kroah-Hartman
167b81beec9SGreg Kroah-Hartman kfree(data);
168b81beec9SGreg Kroah-Hartman return retval;
169b81beec9SGreg Kroah-Hartman }
170b81beec9SGreg Kroah-Hartman
ap_urb_complete(struct urb * urb)171b81beec9SGreg Kroah-Hartman static void ap_urb_complete(struct urb *urb)
172b81beec9SGreg Kroah-Hartman {
173b81beec9SGreg Kroah-Hartman struct usb_ctrlrequest *dr = urb->context;
174b81beec9SGreg Kroah-Hartman
175b81beec9SGreg Kroah-Hartman kfree(dr);
176b81beec9SGreg Kroah-Hartman usb_free_urb(urb);
177b81beec9SGreg Kroah-Hartman }
178b81beec9SGreg Kroah-Hartman
output_async(struct es2_ap_dev * es2,void * req,u16 size,u8 cmd)179b81beec9SGreg Kroah-Hartman static int output_async(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
180b81beec9SGreg Kroah-Hartman {
181b81beec9SGreg Kroah-Hartman struct usb_device *udev = es2->usb_dev;
182b81beec9SGreg Kroah-Hartman struct urb *urb;
183b81beec9SGreg Kroah-Hartman struct usb_ctrlrequest *dr;
184b81beec9SGreg Kroah-Hartman u8 *buf;
185b81beec9SGreg Kroah-Hartman int retval;
186b81beec9SGreg Kroah-Hartman
187b81beec9SGreg Kroah-Hartman urb = usb_alloc_urb(0, GFP_ATOMIC);
188b81beec9SGreg Kroah-Hartman if (!urb)
189b81beec9SGreg Kroah-Hartman return -ENOMEM;
190b81beec9SGreg Kroah-Hartman
191b81beec9SGreg Kroah-Hartman dr = kmalloc(sizeof(*dr) + size, GFP_ATOMIC);
192b81beec9SGreg Kroah-Hartman if (!dr) {
193b81beec9SGreg Kroah-Hartman usb_free_urb(urb);
194b81beec9SGreg Kroah-Hartman return -ENOMEM;
195b81beec9SGreg Kroah-Hartman }
196b81beec9SGreg Kroah-Hartman
197b81beec9SGreg Kroah-Hartman buf = (u8 *)dr + sizeof(*dr);
198b81beec9SGreg Kroah-Hartman memcpy(buf, req, size);
199b81beec9SGreg Kroah-Hartman
200b81beec9SGreg Kroah-Hartman dr->bRequest = cmd;
201b81beec9SGreg Kroah-Hartman dr->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
202b81beec9SGreg Kroah-Hartman dr->wValue = 0;
203b81beec9SGreg Kroah-Hartman dr->wIndex = 0;
204b81beec9SGreg Kroah-Hartman dr->wLength = cpu_to_le16(size);
205b81beec9SGreg Kroah-Hartman
206b81beec9SGreg Kroah-Hartman usb_fill_control_urb(urb, udev, usb_sndctrlpipe(udev, 0),
207b81beec9SGreg Kroah-Hartman (unsigned char *)dr, buf, size,
208b81beec9SGreg Kroah-Hartman ap_urb_complete, dr);
209b81beec9SGreg Kroah-Hartman retval = usb_submit_urb(urb, GFP_ATOMIC);
210b81beec9SGreg Kroah-Hartman if (retval) {
211b81beec9SGreg Kroah-Hartman usb_free_urb(urb);
212b81beec9SGreg Kroah-Hartman kfree(dr);
213b81beec9SGreg Kroah-Hartman }
214b81beec9SGreg Kroah-Hartman return retval;
215b81beec9SGreg Kroah-Hartman }
216b81beec9SGreg Kroah-Hartman
output(struct gb_host_device * hd,void * req,u16 size,u8 cmd,bool async)217b81beec9SGreg Kroah-Hartman static int output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
218b81beec9SGreg Kroah-Hartman bool async)
219b81beec9SGreg Kroah-Hartman {
220b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
221b81beec9SGreg Kroah-Hartman
222b81beec9SGreg Kroah-Hartman if (async)
223b81beec9SGreg Kroah-Hartman return output_async(es2, req, size, cmd);
224b81beec9SGreg Kroah-Hartman
225b81beec9SGreg Kroah-Hartman return output_sync(es2, req, size, cmd);
226b81beec9SGreg Kroah-Hartman }
227b81beec9SGreg Kroah-Hartman
es2_cport_in_enable(struct es2_ap_dev * es2,struct es2_cport_in * cport_in)228b81beec9SGreg Kroah-Hartman static int es2_cport_in_enable(struct es2_ap_dev *es2,
229b81beec9SGreg Kroah-Hartman struct es2_cport_in *cport_in)
230b81beec9SGreg Kroah-Hartman {
231b81beec9SGreg Kroah-Hartman struct urb *urb;
232b81beec9SGreg Kroah-Hartman int ret;
233b81beec9SGreg Kroah-Hartman int i;
234b81beec9SGreg Kroah-Hartman
235b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
236b81beec9SGreg Kroah-Hartman urb = cport_in->urb[i];
237b81beec9SGreg Kroah-Hartman
238b81beec9SGreg Kroah-Hartman ret = usb_submit_urb(urb, GFP_KERNEL);
239b81beec9SGreg Kroah-Hartman if (ret) {
240b81beec9SGreg Kroah-Hartman dev_err(&es2->usb_dev->dev,
241b81beec9SGreg Kroah-Hartman "failed to submit in-urb: %d\n", ret);
242b81beec9SGreg Kroah-Hartman goto err_kill_urbs;
243b81beec9SGreg Kroah-Hartman }
244b81beec9SGreg Kroah-Hartman }
245b81beec9SGreg Kroah-Hartman
246b81beec9SGreg Kroah-Hartman return 0;
247b81beec9SGreg Kroah-Hartman
248b81beec9SGreg Kroah-Hartman err_kill_urbs:
249b81beec9SGreg Kroah-Hartman for (--i; i >= 0; --i) {
250b81beec9SGreg Kroah-Hartman urb = cport_in->urb[i];
251b81beec9SGreg Kroah-Hartman usb_kill_urb(urb);
252b81beec9SGreg Kroah-Hartman }
253b81beec9SGreg Kroah-Hartman
254b81beec9SGreg Kroah-Hartman return ret;
255b81beec9SGreg Kroah-Hartman }
256b81beec9SGreg Kroah-Hartman
es2_cport_in_disable(struct es2_ap_dev * es2,struct es2_cport_in * cport_in)257b81beec9SGreg Kroah-Hartman static void es2_cport_in_disable(struct es2_ap_dev *es2,
258b81beec9SGreg Kroah-Hartman struct es2_cport_in *cport_in)
259b81beec9SGreg Kroah-Hartman {
260b81beec9SGreg Kroah-Hartman struct urb *urb;
261b81beec9SGreg Kroah-Hartman int i;
262b81beec9SGreg Kroah-Hartman
263b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
264b81beec9SGreg Kroah-Hartman urb = cport_in->urb[i];
265b81beec9SGreg Kroah-Hartman usb_kill_urb(urb);
266b81beec9SGreg Kroah-Hartman }
267b81beec9SGreg Kroah-Hartman }
268b81beec9SGreg Kroah-Hartman
es2_arpc_in_enable(struct es2_ap_dev * es2)269b81beec9SGreg Kroah-Hartman static int es2_arpc_in_enable(struct es2_ap_dev *es2)
270b81beec9SGreg Kroah-Hartman {
271b81beec9SGreg Kroah-Hartman struct urb *urb;
272b81beec9SGreg Kroah-Hartman int ret;
273b81beec9SGreg Kroah-Hartman int i;
274b81beec9SGreg Kroah-Hartman
275b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
276b81beec9SGreg Kroah-Hartman urb = es2->arpc_urb[i];
277b81beec9SGreg Kroah-Hartman
278b81beec9SGreg Kroah-Hartman ret = usb_submit_urb(urb, GFP_KERNEL);
279b81beec9SGreg Kroah-Hartman if (ret) {
280b81beec9SGreg Kroah-Hartman dev_err(&es2->usb_dev->dev,
281b81beec9SGreg Kroah-Hartman "failed to submit arpc in-urb: %d\n", ret);
282b81beec9SGreg Kroah-Hartman goto err_kill_urbs;
283b81beec9SGreg Kroah-Hartman }
284b81beec9SGreg Kroah-Hartman }
285b81beec9SGreg Kroah-Hartman
286b81beec9SGreg Kroah-Hartman return 0;
287b81beec9SGreg Kroah-Hartman
288b81beec9SGreg Kroah-Hartman err_kill_urbs:
289b81beec9SGreg Kroah-Hartman for (--i; i >= 0; --i) {
290b81beec9SGreg Kroah-Hartman urb = es2->arpc_urb[i];
291b81beec9SGreg Kroah-Hartman usb_kill_urb(urb);
292b81beec9SGreg Kroah-Hartman }
293b81beec9SGreg Kroah-Hartman
294b81beec9SGreg Kroah-Hartman return ret;
295b81beec9SGreg Kroah-Hartman }
296b81beec9SGreg Kroah-Hartman
es2_arpc_in_disable(struct es2_ap_dev * es2)297b81beec9SGreg Kroah-Hartman static void es2_arpc_in_disable(struct es2_ap_dev *es2)
298b81beec9SGreg Kroah-Hartman {
299b81beec9SGreg Kroah-Hartman struct urb *urb;
300b81beec9SGreg Kroah-Hartman int i;
301b81beec9SGreg Kroah-Hartman
302b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
303b81beec9SGreg Kroah-Hartman urb = es2->arpc_urb[i];
304b81beec9SGreg Kroah-Hartman usb_kill_urb(urb);
305b81beec9SGreg Kroah-Hartman }
306b81beec9SGreg Kroah-Hartman }
307b81beec9SGreg Kroah-Hartman
next_free_urb(struct es2_ap_dev * es2,gfp_t gfp_mask)308b81beec9SGreg Kroah-Hartman static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask)
309b81beec9SGreg Kroah-Hartman {
310b81beec9SGreg Kroah-Hartman struct urb *urb = NULL;
311b81beec9SGreg Kroah-Hartman unsigned long flags;
312b81beec9SGreg Kroah-Hartman int i;
313b81beec9SGreg Kroah-Hartman
314b81beec9SGreg Kroah-Hartman spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
315b81beec9SGreg Kroah-Hartman
316b81beec9SGreg Kroah-Hartman /* Look in our pool of allocated urbs first, as that's the "fastest" */
317b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
318b81beec9SGreg Kroah-Hartman if (!es2->cport_out_urb_busy[i] &&
319b81beec9SGreg Kroah-Hartman !es2->cport_out_urb_cancelled[i]) {
320b81beec9SGreg Kroah-Hartman es2->cport_out_urb_busy[i] = true;
321b81beec9SGreg Kroah-Hartman urb = es2->cport_out_urb[i];
322b81beec9SGreg Kroah-Hartman break;
323b81beec9SGreg Kroah-Hartman }
324b81beec9SGreg Kroah-Hartman }
325b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
326b81beec9SGreg Kroah-Hartman if (urb)
327b81beec9SGreg Kroah-Hartman return urb;
328b81beec9SGreg Kroah-Hartman
329b81beec9SGreg Kroah-Hartman /*
330b81beec9SGreg Kroah-Hartman * Crap, pool is empty, complain to the syslog and go allocate one
331b81beec9SGreg Kroah-Hartman * dynamically as we have to succeed.
332b81beec9SGreg Kroah-Hartman */
333b81beec9SGreg Kroah-Hartman dev_dbg(&es2->usb_dev->dev,
334b81beec9SGreg Kroah-Hartman "No free CPort OUT urbs, having to dynamically allocate one!\n");
335b81beec9SGreg Kroah-Hartman return usb_alloc_urb(0, gfp_mask);
336b81beec9SGreg Kroah-Hartman }
337b81beec9SGreg Kroah-Hartman
free_urb(struct es2_ap_dev * es2,struct urb * urb)338b81beec9SGreg Kroah-Hartman static void free_urb(struct es2_ap_dev *es2, struct urb *urb)
339b81beec9SGreg Kroah-Hartman {
340b81beec9SGreg Kroah-Hartman unsigned long flags;
341b81beec9SGreg Kroah-Hartman int i;
342b81beec9SGreg Kroah-Hartman /*
343b81beec9SGreg Kroah-Hartman * See if this was an urb in our pool, if so mark it "free", otherwise
344b81beec9SGreg Kroah-Hartman * we need to free it ourselves.
345b81beec9SGreg Kroah-Hartman */
346b81beec9SGreg Kroah-Hartman spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
347b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
348b81beec9SGreg Kroah-Hartman if (urb == es2->cport_out_urb[i]) {
349b81beec9SGreg Kroah-Hartman es2->cport_out_urb_busy[i] = false;
350b81beec9SGreg Kroah-Hartman urb = NULL;
351b81beec9SGreg Kroah-Hartman break;
352b81beec9SGreg Kroah-Hartman }
353b81beec9SGreg Kroah-Hartman }
354b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
355b81beec9SGreg Kroah-Hartman
356b81beec9SGreg Kroah-Hartman /* If urb is not NULL, then we need to free this urb */
357b81beec9SGreg Kroah-Hartman usb_free_urb(urb);
358b81beec9SGreg Kroah-Hartman }
359b81beec9SGreg Kroah-Hartman
360b81beec9SGreg Kroah-Hartman /*
361b81beec9SGreg Kroah-Hartman * We (ab)use the operation-message header pad bytes to transfer the
362b81beec9SGreg Kroah-Hartman * cport id in order to minimise overhead.
363b81beec9SGreg Kroah-Hartman */
364b81beec9SGreg Kroah-Hartman static void
gb_message_cport_pack(struct gb_operation_msg_hdr * header,u16 cport_id)365b81beec9SGreg Kroah-Hartman gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id)
366b81beec9SGreg Kroah-Hartman {
367b81beec9SGreg Kroah-Hartman header->pad[0] = cport_id;
368b81beec9SGreg Kroah-Hartman }
369b81beec9SGreg Kroah-Hartman
370b81beec9SGreg Kroah-Hartman /* Clear the pad bytes used for the CPort id */
gb_message_cport_clear(struct gb_operation_msg_hdr * header)371b81beec9SGreg Kroah-Hartman static void gb_message_cport_clear(struct gb_operation_msg_hdr *header)
372b81beec9SGreg Kroah-Hartman {
373b81beec9SGreg Kroah-Hartman header->pad[0] = 0;
374b81beec9SGreg Kroah-Hartman }
375b81beec9SGreg Kroah-Hartman
376b81beec9SGreg Kroah-Hartman /* Extract the CPort id packed into the header, and clear it */
gb_message_cport_unpack(struct gb_operation_msg_hdr * header)377b81beec9SGreg Kroah-Hartman static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header)
378b81beec9SGreg Kroah-Hartman {
379b81beec9SGreg Kroah-Hartman u16 cport_id = header->pad[0];
380b81beec9SGreg Kroah-Hartman
381b81beec9SGreg Kroah-Hartman gb_message_cport_clear(header);
382b81beec9SGreg Kroah-Hartman
383b81beec9SGreg Kroah-Hartman return cport_id;
384b81beec9SGreg Kroah-Hartman }
385b81beec9SGreg Kroah-Hartman
386b81beec9SGreg Kroah-Hartman /*
387b81beec9SGreg Kroah-Hartman * Returns zero if the message was successfully queued, or a negative errno
388b81beec9SGreg Kroah-Hartman * otherwise.
389b81beec9SGreg Kroah-Hartman */
message_send(struct gb_host_device * hd,u16 cport_id,struct gb_message * message,gfp_t gfp_mask)390b81beec9SGreg Kroah-Hartman static int message_send(struct gb_host_device *hd, u16 cport_id,
391b81beec9SGreg Kroah-Hartman struct gb_message *message, gfp_t gfp_mask)
392b81beec9SGreg Kroah-Hartman {
393b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
394b81beec9SGreg Kroah-Hartman struct usb_device *udev = es2->usb_dev;
395b81beec9SGreg Kroah-Hartman size_t buffer_size;
396b81beec9SGreg Kroah-Hartman int retval;
397b81beec9SGreg Kroah-Hartman struct urb *urb;
398b81beec9SGreg Kroah-Hartman unsigned long flags;
399b81beec9SGreg Kroah-Hartman
400b81beec9SGreg Kroah-Hartman /*
401b81beec9SGreg Kroah-Hartman * The data actually transferred will include an indication
402b81beec9SGreg Kroah-Hartman * of where the data should be sent. Do one last check of
403b81beec9SGreg Kroah-Hartman * the target CPort id before filling it in.
404b81beec9SGreg Kroah-Hartman */
405b81beec9SGreg Kroah-Hartman if (!cport_id_valid(hd, cport_id)) {
406b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "invalid cport %u\n", cport_id);
407b81beec9SGreg Kroah-Hartman return -EINVAL;
408b81beec9SGreg Kroah-Hartman }
409b81beec9SGreg Kroah-Hartman
410b81beec9SGreg Kroah-Hartman /* Find a free urb */
411b81beec9SGreg Kroah-Hartman urb = next_free_urb(es2, gfp_mask);
412b81beec9SGreg Kroah-Hartman if (!urb)
413b81beec9SGreg Kroah-Hartman return -ENOMEM;
414b81beec9SGreg Kroah-Hartman
415b81beec9SGreg Kroah-Hartman spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
416b81beec9SGreg Kroah-Hartman message->hcpriv = urb;
417b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
418b81beec9SGreg Kroah-Hartman
419b81beec9SGreg Kroah-Hartman /* Pack the cport id into the message header */
420b81beec9SGreg Kroah-Hartman gb_message_cport_pack(message->header, cport_id);
421b81beec9SGreg Kroah-Hartman
422b81beec9SGreg Kroah-Hartman buffer_size = sizeof(*message->header) + message->payload_size;
423b81beec9SGreg Kroah-Hartman
424b81beec9SGreg Kroah-Hartman usb_fill_bulk_urb(urb, udev,
425b81beec9SGreg Kroah-Hartman usb_sndbulkpipe(udev,
426b81beec9SGreg Kroah-Hartman es2->cport_out_endpoint),
427b81beec9SGreg Kroah-Hartman message->buffer, buffer_size,
428b81beec9SGreg Kroah-Hartman cport_out_callback, message);
429b81beec9SGreg Kroah-Hartman urb->transfer_flags |= URB_ZERO_PACKET;
430b81beec9SGreg Kroah-Hartman
431b81beec9SGreg Kroah-Hartman trace_gb_message_submit(message);
432b81beec9SGreg Kroah-Hartman
433b81beec9SGreg Kroah-Hartman retval = usb_submit_urb(urb, gfp_mask);
434b81beec9SGreg Kroah-Hartman if (retval) {
435b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval);
436b81beec9SGreg Kroah-Hartman
437b81beec9SGreg Kroah-Hartman spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
438b81beec9SGreg Kroah-Hartman message->hcpriv = NULL;
439b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
440b81beec9SGreg Kroah-Hartman
441b81beec9SGreg Kroah-Hartman free_urb(es2, urb);
442b81beec9SGreg Kroah-Hartman gb_message_cport_clear(message->header);
443b81beec9SGreg Kroah-Hartman
444b81beec9SGreg Kroah-Hartman return retval;
445b81beec9SGreg Kroah-Hartman }
446b81beec9SGreg Kroah-Hartman
447b81beec9SGreg Kroah-Hartman return 0;
448b81beec9SGreg Kroah-Hartman }
449b81beec9SGreg Kroah-Hartman
450b81beec9SGreg Kroah-Hartman /*
451b81beec9SGreg Kroah-Hartman * Can not be called in atomic context.
452b81beec9SGreg Kroah-Hartman */
message_cancel(struct gb_message * message)453b81beec9SGreg Kroah-Hartman static void message_cancel(struct gb_message *message)
454b81beec9SGreg Kroah-Hartman {
455b81beec9SGreg Kroah-Hartman struct gb_host_device *hd = message->operation->connection->hd;
456b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
457b81beec9SGreg Kroah-Hartman struct urb *urb;
458b81beec9SGreg Kroah-Hartman int i;
459b81beec9SGreg Kroah-Hartman
460b81beec9SGreg Kroah-Hartman might_sleep();
461b81beec9SGreg Kroah-Hartman
462b81beec9SGreg Kroah-Hartman spin_lock_irq(&es2->cport_out_urb_lock);
463b81beec9SGreg Kroah-Hartman urb = message->hcpriv;
464b81beec9SGreg Kroah-Hartman
465b81beec9SGreg Kroah-Hartman /* Prevent dynamically allocated urb from being deallocated. */
466b81beec9SGreg Kroah-Hartman usb_get_urb(urb);
467b81beec9SGreg Kroah-Hartman
468b81beec9SGreg Kroah-Hartman /* Prevent pre-allocated urb from being reused. */
469b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
470b81beec9SGreg Kroah-Hartman if (urb == es2->cport_out_urb[i]) {
471b81beec9SGreg Kroah-Hartman es2->cport_out_urb_cancelled[i] = true;
472b81beec9SGreg Kroah-Hartman break;
473b81beec9SGreg Kroah-Hartman }
474b81beec9SGreg Kroah-Hartman }
475b81beec9SGreg Kroah-Hartman spin_unlock_irq(&es2->cport_out_urb_lock);
476b81beec9SGreg Kroah-Hartman
477b81beec9SGreg Kroah-Hartman usb_kill_urb(urb);
478b81beec9SGreg Kroah-Hartman
479b81beec9SGreg Kroah-Hartman if (i < NUM_CPORT_OUT_URB) {
480b81beec9SGreg Kroah-Hartman spin_lock_irq(&es2->cport_out_urb_lock);
481b81beec9SGreg Kroah-Hartman es2->cport_out_urb_cancelled[i] = false;
482b81beec9SGreg Kroah-Hartman spin_unlock_irq(&es2->cport_out_urb_lock);
483b81beec9SGreg Kroah-Hartman }
484b81beec9SGreg Kroah-Hartman
485b81beec9SGreg Kroah-Hartman usb_free_urb(urb);
486b81beec9SGreg Kroah-Hartman }
487b81beec9SGreg Kroah-Hartman
es2_cport_allocate(struct gb_host_device * hd,int cport_id,unsigned long flags)488b81beec9SGreg Kroah-Hartman static int es2_cport_allocate(struct gb_host_device *hd, int cport_id,
489b81beec9SGreg Kroah-Hartman unsigned long flags)
490b81beec9SGreg Kroah-Hartman {
491b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
492b81beec9SGreg Kroah-Hartman struct ida *id_map = &hd->cport_id_map;
493b81beec9SGreg Kroah-Hartman int ida_start, ida_end;
494b81beec9SGreg Kroah-Hartman
495b81beec9SGreg Kroah-Hartman switch (cport_id) {
496b81beec9SGreg Kroah-Hartman case ES2_CPORT_CDSI0:
497b81beec9SGreg Kroah-Hartman case ES2_CPORT_CDSI1:
498b81beec9SGreg Kroah-Hartman dev_err(&hd->dev, "cport %d not available\n", cport_id);
499b81beec9SGreg Kroah-Hartman return -EBUSY;
500b81beec9SGreg Kroah-Hartman }
501b81beec9SGreg Kroah-Hartman
502b81beec9SGreg Kroah-Hartman if (flags & GB_CONNECTION_FLAG_OFFLOADED &&
503b81beec9SGreg Kroah-Hartman flags & GB_CONNECTION_FLAG_CDSI1) {
504b81beec9SGreg Kroah-Hartman if (es2->cdsi1_in_use) {
505b81beec9SGreg Kroah-Hartman dev_err(&hd->dev, "CDSI1 already in use\n");
506b81beec9SGreg Kroah-Hartman return -EBUSY;
507b81beec9SGreg Kroah-Hartman }
508b81beec9SGreg Kroah-Hartman
509b81beec9SGreg Kroah-Hartman es2->cdsi1_in_use = true;
510b81beec9SGreg Kroah-Hartman
511b81beec9SGreg Kroah-Hartman return ES2_CPORT_CDSI1;
512b81beec9SGreg Kroah-Hartman }
513b81beec9SGreg Kroah-Hartman
514b81beec9SGreg Kroah-Hartman if (cport_id < 0) {
515b81beec9SGreg Kroah-Hartman ida_start = 0;
516b81beec9SGreg Kroah-Hartman ida_end = hd->num_cports;
517b81beec9SGreg Kroah-Hartman } else if (cport_id < hd->num_cports) {
518b81beec9SGreg Kroah-Hartman ida_start = cport_id;
519b81beec9SGreg Kroah-Hartman ida_end = cport_id + 1;
520b81beec9SGreg Kroah-Hartman } else {
521b81beec9SGreg Kroah-Hartman dev_err(&hd->dev, "cport %d not available\n", cport_id);
522b81beec9SGreg Kroah-Hartman return -EINVAL;
523b81beec9SGreg Kroah-Hartman }
524b81beec9SGreg Kroah-Hartman
525b81beec9SGreg Kroah-Hartman return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
526b81beec9SGreg Kroah-Hartman }
527b81beec9SGreg Kroah-Hartman
es2_cport_release(struct gb_host_device * hd,u16 cport_id)528b81beec9SGreg Kroah-Hartman static void es2_cport_release(struct gb_host_device *hd, u16 cport_id)
529b81beec9SGreg Kroah-Hartman {
530b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
531b81beec9SGreg Kroah-Hartman
532b81beec9SGreg Kroah-Hartman switch (cport_id) {
533b81beec9SGreg Kroah-Hartman case ES2_CPORT_CDSI1:
534b81beec9SGreg Kroah-Hartman es2->cdsi1_in_use = false;
535b81beec9SGreg Kroah-Hartman return;
536b81beec9SGreg Kroah-Hartman }
537b81beec9SGreg Kroah-Hartman
538b81beec9SGreg Kroah-Hartman ida_simple_remove(&hd->cport_id_map, cport_id);
539b81beec9SGreg Kroah-Hartman }
540b81beec9SGreg Kroah-Hartman
cport_enable(struct gb_host_device * hd,u16 cport_id,unsigned long flags)541b81beec9SGreg Kroah-Hartman static int cport_enable(struct gb_host_device *hd, u16 cport_id,
542b81beec9SGreg Kroah-Hartman unsigned long flags)
543b81beec9SGreg Kroah-Hartman {
544b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
545b81beec9SGreg Kroah-Hartman struct usb_device *udev = es2->usb_dev;
546b81beec9SGreg Kroah-Hartman struct gb_apb_request_cport_flags *req;
547b81beec9SGreg Kroah-Hartman u32 connection_flags;
548b81beec9SGreg Kroah-Hartman int ret;
549b81beec9SGreg Kroah-Hartman
550b81beec9SGreg Kroah-Hartman req = kzalloc(sizeof(*req), GFP_KERNEL);
551b81beec9SGreg Kroah-Hartman if (!req)
552b81beec9SGreg Kroah-Hartman return -ENOMEM;
553b81beec9SGreg Kroah-Hartman
554b81beec9SGreg Kroah-Hartman connection_flags = 0;
555b81beec9SGreg Kroah-Hartman if (flags & GB_CONNECTION_FLAG_CONTROL)
556b81beec9SGreg Kroah-Hartman connection_flags |= GB_APB_CPORT_FLAG_CONTROL;
557b81beec9SGreg Kroah-Hartman if (flags & GB_CONNECTION_FLAG_HIGH_PRIO)
558b81beec9SGreg Kroah-Hartman connection_flags |= GB_APB_CPORT_FLAG_HIGH_PRIO;
559b81beec9SGreg Kroah-Hartman
560b81beec9SGreg Kroah-Hartman req->flags = cpu_to_le32(connection_flags);
561b81beec9SGreg Kroah-Hartman
562b81beec9SGreg Kroah-Hartman dev_dbg(&hd->dev, "%s - cport = %u, flags = %02x\n", __func__,
563b81beec9SGreg Kroah-Hartman cport_id, connection_flags);
564b81beec9SGreg Kroah-Hartman
565b81beec9SGreg Kroah-Hartman ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
566b81beec9SGreg Kroah-Hartman GB_APB_REQUEST_CPORT_FLAGS,
567b81beec9SGreg Kroah-Hartman USB_DIR_OUT | USB_TYPE_VENDOR |
568b81beec9SGreg Kroah-Hartman USB_RECIP_INTERFACE, cport_id, 0,
569b81beec9SGreg Kroah-Hartman req, sizeof(*req), ES2_USB_CTRL_TIMEOUT);
570e4240253SJohan Hovold if (ret < 0) {
571b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "failed to set cport flags for port %d\n",
572b81beec9SGreg Kroah-Hartman cport_id);
573b81beec9SGreg Kroah-Hartman goto out;
574b81beec9SGreg Kroah-Hartman }
575b81beec9SGreg Kroah-Hartman
576b81beec9SGreg Kroah-Hartman ret = 0;
577b81beec9SGreg Kroah-Hartman out:
578b81beec9SGreg Kroah-Hartman kfree(req);
579b81beec9SGreg Kroah-Hartman
580b81beec9SGreg Kroah-Hartman return ret;
581b81beec9SGreg Kroah-Hartman }
582b81beec9SGreg Kroah-Hartman
es2_cport_connected(struct gb_host_device * hd,u16 cport_id)583b81beec9SGreg Kroah-Hartman static int es2_cport_connected(struct gb_host_device *hd, u16 cport_id)
584b81beec9SGreg Kroah-Hartman {
585b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
586b81beec9SGreg Kroah-Hartman struct device *dev = &es2->usb_dev->dev;
587b81beec9SGreg Kroah-Hartman struct arpc_cport_connected_req req;
588b81beec9SGreg Kroah-Hartman int ret;
589b81beec9SGreg Kroah-Hartman
590b81beec9SGreg Kroah-Hartman req.cport_id = cpu_to_le16(cport_id);
591b81beec9SGreg Kroah-Hartman ret = arpc_sync(es2, ARPC_TYPE_CPORT_CONNECTED, &req, sizeof(req),
592b81beec9SGreg Kroah-Hartman NULL, ES2_ARPC_CPORT_TIMEOUT);
593b81beec9SGreg Kroah-Hartman if (ret) {
594b81beec9SGreg Kroah-Hartman dev_err(dev, "failed to set connected state for cport %u: %d\n",
595b81beec9SGreg Kroah-Hartman cport_id, ret);
596b81beec9SGreg Kroah-Hartman return ret;
597b81beec9SGreg Kroah-Hartman }
598b81beec9SGreg Kroah-Hartman
599b81beec9SGreg Kroah-Hartman return 0;
600b81beec9SGreg Kroah-Hartman }
601b81beec9SGreg Kroah-Hartman
es2_cport_flush(struct gb_host_device * hd,u16 cport_id)602b81beec9SGreg Kroah-Hartman static int es2_cport_flush(struct gb_host_device *hd, u16 cport_id)
603b81beec9SGreg Kroah-Hartman {
604b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
605b81beec9SGreg Kroah-Hartman struct device *dev = &es2->usb_dev->dev;
606b81beec9SGreg Kroah-Hartman struct arpc_cport_flush_req req;
607b81beec9SGreg Kroah-Hartman int ret;
608b81beec9SGreg Kroah-Hartman
609b81beec9SGreg Kroah-Hartman req.cport_id = cpu_to_le16(cport_id);
610b81beec9SGreg Kroah-Hartman ret = arpc_sync(es2, ARPC_TYPE_CPORT_FLUSH, &req, sizeof(req),
611b81beec9SGreg Kroah-Hartman NULL, ES2_ARPC_CPORT_TIMEOUT);
612b81beec9SGreg Kroah-Hartman if (ret) {
613b81beec9SGreg Kroah-Hartman dev_err(dev, "failed to flush cport %u: %d\n", cport_id, ret);
614b81beec9SGreg Kroah-Hartman return ret;
615b81beec9SGreg Kroah-Hartman }
616b81beec9SGreg Kroah-Hartman
617b81beec9SGreg Kroah-Hartman return 0;
618b81beec9SGreg Kroah-Hartman }
619b81beec9SGreg Kroah-Hartman
es2_cport_shutdown(struct gb_host_device * hd,u16 cport_id,u8 phase,unsigned int timeout)620b81beec9SGreg Kroah-Hartman static int es2_cport_shutdown(struct gb_host_device *hd, u16 cport_id,
621b81beec9SGreg Kroah-Hartman u8 phase, unsigned int timeout)
622b81beec9SGreg Kroah-Hartman {
623b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
624b81beec9SGreg Kroah-Hartman struct device *dev = &es2->usb_dev->dev;
625b81beec9SGreg Kroah-Hartman struct arpc_cport_shutdown_req req;
626b81beec9SGreg Kroah-Hartman int result;
627b81beec9SGreg Kroah-Hartman int ret;
628b81beec9SGreg Kroah-Hartman
629b81beec9SGreg Kroah-Hartman if (timeout > U16_MAX)
630b81beec9SGreg Kroah-Hartman return -EINVAL;
631b81beec9SGreg Kroah-Hartman
632b81beec9SGreg Kroah-Hartman req.cport_id = cpu_to_le16(cport_id);
633b81beec9SGreg Kroah-Hartman req.timeout = cpu_to_le16(timeout);
634b81beec9SGreg Kroah-Hartman req.phase = phase;
635b81beec9SGreg Kroah-Hartman ret = arpc_sync(es2, ARPC_TYPE_CPORT_SHUTDOWN, &req, sizeof(req),
636b81beec9SGreg Kroah-Hartman &result, ES2_ARPC_CPORT_TIMEOUT + timeout);
637b81beec9SGreg Kroah-Hartman if (ret) {
638b81beec9SGreg Kroah-Hartman dev_err(dev, "failed to send shutdown over cport %u: %d (%d)\n",
639b81beec9SGreg Kroah-Hartman cport_id, ret, result);
640b81beec9SGreg Kroah-Hartman return ret;
641b81beec9SGreg Kroah-Hartman }
642b81beec9SGreg Kroah-Hartman
643b81beec9SGreg Kroah-Hartman return 0;
644b81beec9SGreg Kroah-Hartman }
645b81beec9SGreg Kroah-Hartman
es2_cport_quiesce(struct gb_host_device * hd,u16 cport_id,size_t peer_space,unsigned int timeout)646b81beec9SGreg Kroah-Hartman static int es2_cport_quiesce(struct gb_host_device *hd, u16 cport_id,
647b81beec9SGreg Kroah-Hartman size_t peer_space, unsigned int timeout)
648b81beec9SGreg Kroah-Hartman {
649b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
650b81beec9SGreg Kroah-Hartman struct device *dev = &es2->usb_dev->dev;
651b81beec9SGreg Kroah-Hartman struct arpc_cport_quiesce_req req;
652b81beec9SGreg Kroah-Hartman int result;
653b81beec9SGreg Kroah-Hartman int ret;
654b81beec9SGreg Kroah-Hartman
655b81beec9SGreg Kroah-Hartman if (peer_space > U16_MAX)
656b81beec9SGreg Kroah-Hartman return -EINVAL;
657b81beec9SGreg Kroah-Hartman
658b81beec9SGreg Kroah-Hartman if (timeout > U16_MAX)
659b81beec9SGreg Kroah-Hartman return -EINVAL;
660b81beec9SGreg Kroah-Hartman
661b81beec9SGreg Kroah-Hartman req.cport_id = cpu_to_le16(cport_id);
662b81beec9SGreg Kroah-Hartman req.peer_space = cpu_to_le16(peer_space);
663b81beec9SGreg Kroah-Hartman req.timeout = cpu_to_le16(timeout);
664b81beec9SGreg Kroah-Hartman ret = arpc_sync(es2, ARPC_TYPE_CPORT_QUIESCE, &req, sizeof(req),
665b81beec9SGreg Kroah-Hartman &result, ES2_ARPC_CPORT_TIMEOUT + timeout);
666b81beec9SGreg Kroah-Hartman if (ret) {
667b81beec9SGreg Kroah-Hartman dev_err(dev, "failed to quiesce cport %u: %d (%d)\n",
668b81beec9SGreg Kroah-Hartman cport_id, ret, result);
669b81beec9SGreg Kroah-Hartman return ret;
670b81beec9SGreg Kroah-Hartman }
671b81beec9SGreg Kroah-Hartman
672b81beec9SGreg Kroah-Hartman return 0;
673b81beec9SGreg Kroah-Hartman }
674b81beec9SGreg Kroah-Hartman
es2_cport_clear(struct gb_host_device * hd,u16 cport_id)675b81beec9SGreg Kroah-Hartman static int es2_cport_clear(struct gb_host_device *hd, u16 cport_id)
676b81beec9SGreg Kroah-Hartman {
677b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
678b81beec9SGreg Kroah-Hartman struct device *dev = &es2->usb_dev->dev;
679b81beec9SGreg Kroah-Hartman struct arpc_cport_clear_req req;
680b81beec9SGreg Kroah-Hartman int ret;
681b81beec9SGreg Kroah-Hartman
682b81beec9SGreg Kroah-Hartman req.cport_id = cpu_to_le16(cport_id);
683b81beec9SGreg Kroah-Hartman ret = arpc_sync(es2, ARPC_TYPE_CPORT_CLEAR, &req, sizeof(req),
684b81beec9SGreg Kroah-Hartman NULL, ES2_ARPC_CPORT_TIMEOUT);
685b81beec9SGreg Kroah-Hartman if (ret) {
686b81beec9SGreg Kroah-Hartman dev_err(dev, "failed to clear cport %u: %d\n", cport_id, ret);
687b81beec9SGreg Kroah-Hartman return ret;
688b81beec9SGreg Kroah-Hartman }
689b81beec9SGreg Kroah-Hartman
690b81beec9SGreg Kroah-Hartman return 0;
691b81beec9SGreg Kroah-Hartman }
692b81beec9SGreg Kroah-Hartman
latency_tag_enable(struct gb_host_device * hd,u16 cport_id)693b81beec9SGreg Kroah-Hartman static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id)
694b81beec9SGreg Kroah-Hartman {
695b81beec9SGreg Kroah-Hartman int retval;
696b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
697b81beec9SGreg Kroah-Hartman struct usb_device *udev = es2->usb_dev;
698b81beec9SGreg Kroah-Hartman
699b81beec9SGreg Kroah-Hartman retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
700b81beec9SGreg Kroah-Hartman GB_APB_REQUEST_LATENCY_TAG_EN,
701b81beec9SGreg Kroah-Hartman USB_DIR_OUT | USB_TYPE_VENDOR |
702b81beec9SGreg Kroah-Hartman USB_RECIP_INTERFACE, cport_id, 0, NULL,
703b81beec9SGreg Kroah-Hartman 0, ES2_USB_CTRL_TIMEOUT);
704b81beec9SGreg Kroah-Hartman
705b81beec9SGreg Kroah-Hartman if (retval < 0)
706b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n",
707b81beec9SGreg Kroah-Hartman cport_id);
708b81beec9SGreg Kroah-Hartman return retval;
709b81beec9SGreg Kroah-Hartman }
710b81beec9SGreg Kroah-Hartman
latency_tag_disable(struct gb_host_device * hd,u16 cport_id)711b81beec9SGreg Kroah-Hartman static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id)
712b81beec9SGreg Kroah-Hartman {
713b81beec9SGreg Kroah-Hartman int retval;
714b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
715b81beec9SGreg Kroah-Hartman struct usb_device *udev = es2->usb_dev;
716b81beec9SGreg Kroah-Hartman
717b81beec9SGreg Kroah-Hartman retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
718b81beec9SGreg Kroah-Hartman GB_APB_REQUEST_LATENCY_TAG_DIS,
719b81beec9SGreg Kroah-Hartman USB_DIR_OUT | USB_TYPE_VENDOR |
720b81beec9SGreg Kroah-Hartman USB_RECIP_INTERFACE, cport_id, 0, NULL,
721b81beec9SGreg Kroah-Hartman 0, ES2_USB_CTRL_TIMEOUT);
722b81beec9SGreg Kroah-Hartman
723b81beec9SGreg Kroah-Hartman if (retval < 0)
724b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n",
725b81beec9SGreg Kroah-Hartman cport_id);
726b81beec9SGreg Kroah-Hartman return retval;
727b81beec9SGreg Kroah-Hartman }
728b81beec9SGreg Kroah-Hartman
729b81beec9SGreg Kroah-Hartman static struct gb_hd_driver es2_driver = {
730b81beec9SGreg Kroah-Hartman .hd_priv_size = sizeof(struct es2_ap_dev),
731b81beec9SGreg Kroah-Hartman .message_send = message_send,
732b81beec9SGreg Kroah-Hartman .message_cancel = message_cancel,
733b81beec9SGreg Kroah-Hartman .cport_allocate = es2_cport_allocate,
734b81beec9SGreg Kroah-Hartman .cport_release = es2_cport_release,
735b81beec9SGreg Kroah-Hartman .cport_enable = cport_enable,
736b81beec9SGreg Kroah-Hartman .cport_connected = es2_cport_connected,
737b81beec9SGreg Kroah-Hartman .cport_flush = es2_cport_flush,
738b81beec9SGreg Kroah-Hartman .cport_shutdown = es2_cport_shutdown,
739b81beec9SGreg Kroah-Hartman .cport_quiesce = es2_cport_quiesce,
740b81beec9SGreg Kroah-Hartman .cport_clear = es2_cport_clear,
741b81beec9SGreg Kroah-Hartman .latency_tag_enable = latency_tag_enable,
742b81beec9SGreg Kroah-Hartman .latency_tag_disable = latency_tag_disable,
743b81beec9SGreg Kroah-Hartman .output = output,
744b81beec9SGreg Kroah-Hartman };
745b81beec9SGreg Kroah-Hartman
746b81beec9SGreg Kroah-Hartman /* Common function to report consistent warnings based on URB status */
check_urb_status(struct urb * urb)747b81beec9SGreg Kroah-Hartman static int check_urb_status(struct urb *urb)
748b81beec9SGreg Kroah-Hartman {
749b81beec9SGreg Kroah-Hartman struct device *dev = &urb->dev->dev;
750b81beec9SGreg Kroah-Hartman int status = urb->status;
751b81beec9SGreg Kroah-Hartman
752b81beec9SGreg Kroah-Hartman switch (status) {
753b81beec9SGreg Kroah-Hartman case 0:
754b81beec9SGreg Kroah-Hartman return 0;
755b81beec9SGreg Kroah-Hartman
756b81beec9SGreg Kroah-Hartman case -EOVERFLOW:
757b81beec9SGreg Kroah-Hartman dev_err(dev, "%s: overflow actual length is %d\n",
758b81beec9SGreg Kroah-Hartman __func__, urb->actual_length);
75937b8b73fSGustavo A. R. Silva fallthrough;
760b81beec9SGreg Kroah-Hartman case -ECONNRESET:
761b81beec9SGreg Kroah-Hartman case -ENOENT:
762b81beec9SGreg Kroah-Hartman case -ESHUTDOWN:
763b81beec9SGreg Kroah-Hartman case -EILSEQ:
764b81beec9SGreg Kroah-Hartman case -EPROTO:
765b81beec9SGreg Kroah-Hartman /* device is gone, stop sending */
766b81beec9SGreg Kroah-Hartman return status;
767b81beec9SGreg Kroah-Hartman }
768b81beec9SGreg Kroah-Hartman dev_err(dev, "%s: unknown status %d\n", __func__, status);
769b81beec9SGreg Kroah-Hartman
770b81beec9SGreg Kroah-Hartman return -EAGAIN;
771b81beec9SGreg Kroah-Hartman }
772b81beec9SGreg Kroah-Hartman
es2_destroy(struct es2_ap_dev * es2)773b81beec9SGreg Kroah-Hartman static void es2_destroy(struct es2_ap_dev *es2)
774b81beec9SGreg Kroah-Hartman {
775b81beec9SGreg Kroah-Hartman struct usb_device *udev;
776b81beec9SGreg Kroah-Hartman struct urb *urb;
777b81beec9SGreg Kroah-Hartman int i;
778b81beec9SGreg Kroah-Hartman
779b81beec9SGreg Kroah-Hartman debugfs_remove(es2->apb_log_enable_dentry);
780b81beec9SGreg Kroah-Hartman usb_log_disable(es2);
781b81beec9SGreg Kroah-Hartman
782b81beec9SGreg Kroah-Hartman /* Tear down everything! */
783b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
784b81beec9SGreg Kroah-Hartman urb = es2->cport_out_urb[i];
785b81beec9SGreg Kroah-Hartman usb_kill_urb(urb);
786b81beec9SGreg Kroah-Hartman usb_free_urb(urb);
787b81beec9SGreg Kroah-Hartman es2->cport_out_urb[i] = NULL;
788b81beec9SGreg Kroah-Hartman es2->cport_out_urb_busy[i] = false; /* just to be anal */
789b81beec9SGreg Kroah-Hartman }
790b81beec9SGreg Kroah-Hartman
791b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
792b81beec9SGreg Kroah-Hartman usb_free_urb(es2->arpc_urb[i]);
793b81beec9SGreg Kroah-Hartman kfree(es2->arpc_buffer[i]);
794b81beec9SGreg Kroah-Hartman es2->arpc_buffer[i] = NULL;
795b81beec9SGreg Kroah-Hartman }
796b81beec9SGreg Kroah-Hartman
797b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
798b81beec9SGreg Kroah-Hartman usb_free_urb(es2->cport_in.urb[i]);
799b81beec9SGreg Kroah-Hartman kfree(es2->cport_in.buffer[i]);
800b81beec9SGreg Kroah-Hartman es2->cport_in.buffer[i] = NULL;
801b81beec9SGreg Kroah-Hartman }
802b81beec9SGreg Kroah-Hartman
803b81beec9SGreg Kroah-Hartman /* release reserved CDSI0 and CDSI1 cports */
804b81beec9SGreg Kroah-Hartman gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI1);
805b81beec9SGreg Kroah-Hartman gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI0);
806b81beec9SGreg Kroah-Hartman
807b81beec9SGreg Kroah-Hartman udev = es2->usb_dev;
808b81beec9SGreg Kroah-Hartman gb_hd_put(es2->hd);
809b81beec9SGreg Kroah-Hartman
810b81beec9SGreg Kroah-Hartman usb_put_dev(udev);
811b81beec9SGreg Kroah-Hartman }
812b81beec9SGreg Kroah-Hartman
cport_in_callback(struct urb * urb)813b81beec9SGreg Kroah-Hartman static void cport_in_callback(struct urb *urb)
814b81beec9SGreg Kroah-Hartman {
815b81beec9SGreg Kroah-Hartman struct gb_host_device *hd = urb->context;
816b81beec9SGreg Kroah-Hartman struct device *dev = &urb->dev->dev;
817b81beec9SGreg Kroah-Hartman struct gb_operation_msg_hdr *header;
818b81beec9SGreg Kroah-Hartman int status = check_urb_status(urb);
819b81beec9SGreg Kroah-Hartman int retval;
820b81beec9SGreg Kroah-Hartman u16 cport_id;
821b81beec9SGreg Kroah-Hartman
822b81beec9SGreg Kroah-Hartman if (status) {
823b81beec9SGreg Kroah-Hartman if ((status == -EAGAIN) || (status == -EPROTO))
824b81beec9SGreg Kroah-Hartman goto exit;
825b81beec9SGreg Kroah-Hartman
826b81beec9SGreg Kroah-Hartman /* The urb is being unlinked */
827b81beec9SGreg Kroah-Hartman if (status == -ENOENT || status == -ESHUTDOWN)
828b81beec9SGreg Kroah-Hartman return;
829b81beec9SGreg Kroah-Hartman
830b81beec9SGreg Kroah-Hartman dev_err(dev, "urb cport in error %d (dropped)\n", status);
831b81beec9SGreg Kroah-Hartman return;
832b81beec9SGreg Kroah-Hartman }
833b81beec9SGreg Kroah-Hartman
834b81beec9SGreg Kroah-Hartman if (urb->actual_length < sizeof(*header)) {
835b81beec9SGreg Kroah-Hartman dev_err(dev, "short message received\n");
836b81beec9SGreg Kroah-Hartman goto exit;
837b81beec9SGreg Kroah-Hartman }
838b81beec9SGreg Kroah-Hartman
839b81beec9SGreg Kroah-Hartman /* Extract the CPort id, which is packed in the message header */
840b81beec9SGreg Kroah-Hartman header = urb->transfer_buffer;
841b81beec9SGreg Kroah-Hartman cport_id = gb_message_cport_unpack(header);
842b81beec9SGreg Kroah-Hartman
843b81beec9SGreg Kroah-Hartman if (cport_id_valid(hd, cport_id)) {
844b81beec9SGreg Kroah-Hartman greybus_data_rcvd(hd, cport_id, urb->transfer_buffer,
845b81beec9SGreg Kroah-Hartman urb->actual_length);
846b81beec9SGreg Kroah-Hartman } else {
847b81beec9SGreg Kroah-Hartman dev_err(dev, "invalid cport id %u received\n", cport_id);
848b81beec9SGreg Kroah-Hartman }
849b81beec9SGreg Kroah-Hartman exit:
850b81beec9SGreg Kroah-Hartman /* put our urb back in the request pool */
851b81beec9SGreg Kroah-Hartman retval = usb_submit_urb(urb, GFP_ATOMIC);
852b81beec9SGreg Kroah-Hartman if (retval)
853b81beec9SGreg Kroah-Hartman dev_err(dev, "failed to resubmit in-urb: %d\n", retval);
854b81beec9SGreg Kroah-Hartman }
855b81beec9SGreg Kroah-Hartman
cport_out_callback(struct urb * urb)856b81beec9SGreg Kroah-Hartman static void cport_out_callback(struct urb *urb)
857b81beec9SGreg Kroah-Hartman {
858b81beec9SGreg Kroah-Hartman struct gb_message *message = urb->context;
859b81beec9SGreg Kroah-Hartman struct gb_host_device *hd = message->operation->connection->hd;
860b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = hd_to_es2(hd);
861b81beec9SGreg Kroah-Hartman int status = check_urb_status(urb);
862b81beec9SGreg Kroah-Hartman unsigned long flags;
863b81beec9SGreg Kroah-Hartman
864b81beec9SGreg Kroah-Hartman gb_message_cport_clear(message->header);
865b81beec9SGreg Kroah-Hartman
866b81beec9SGreg Kroah-Hartman spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
867b81beec9SGreg Kroah-Hartman message->hcpriv = NULL;
868b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
869b81beec9SGreg Kroah-Hartman
870b81beec9SGreg Kroah-Hartman /*
871b81beec9SGreg Kroah-Hartman * Tell the submitter that the message send (attempt) is
872b81beec9SGreg Kroah-Hartman * complete, and report the status.
873b81beec9SGreg Kroah-Hartman */
874b81beec9SGreg Kroah-Hartman greybus_message_sent(hd, message, status);
875b81beec9SGreg Kroah-Hartman
876b81beec9SGreg Kroah-Hartman free_urb(es2, urb);
877b81beec9SGreg Kroah-Hartman }
878b81beec9SGreg Kroah-Hartman
arpc_alloc(void * payload,u16 size,u8 type)879b81beec9SGreg Kroah-Hartman static struct arpc *arpc_alloc(void *payload, u16 size, u8 type)
880b81beec9SGreg Kroah-Hartman {
881b81beec9SGreg Kroah-Hartman struct arpc *rpc;
882b81beec9SGreg Kroah-Hartman
883b81beec9SGreg Kroah-Hartman if (size + sizeof(*rpc->req) > ARPC_OUT_SIZE_MAX)
884b81beec9SGreg Kroah-Hartman return NULL;
885b81beec9SGreg Kroah-Hartman
886b81beec9SGreg Kroah-Hartman rpc = kzalloc(sizeof(*rpc), GFP_KERNEL);
887b81beec9SGreg Kroah-Hartman if (!rpc)
888b81beec9SGreg Kroah-Hartman return NULL;
889b81beec9SGreg Kroah-Hartman
890b81beec9SGreg Kroah-Hartman INIT_LIST_HEAD(&rpc->list);
891b81beec9SGreg Kroah-Hartman rpc->req = kzalloc(sizeof(*rpc->req) + size, GFP_KERNEL);
892b81beec9SGreg Kroah-Hartman if (!rpc->req)
893b81beec9SGreg Kroah-Hartman goto err_free_rpc;
894b81beec9SGreg Kroah-Hartman
895b81beec9SGreg Kroah-Hartman rpc->resp = kzalloc(sizeof(*rpc->resp), GFP_KERNEL);
896b81beec9SGreg Kroah-Hartman if (!rpc->resp)
897b81beec9SGreg Kroah-Hartman goto err_free_req;
898b81beec9SGreg Kroah-Hartman
899b81beec9SGreg Kroah-Hartman rpc->req->type = type;
900b81beec9SGreg Kroah-Hartman rpc->req->size = cpu_to_le16(sizeof(*rpc->req) + size);
901b81beec9SGreg Kroah-Hartman memcpy(rpc->req->data, payload, size);
902b81beec9SGreg Kroah-Hartman
903b81beec9SGreg Kroah-Hartman init_completion(&rpc->response_received);
904b81beec9SGreg Kroah-Hartman
905b81beec9SGreg Kroah-Hartman return rpc;
906b81beec9SGreg Kroah-Hartman
907b81beec9SGreg Kroah-Hartman err_free_req:
908b81beec9SGreg Kroah-Hartman kfree(rpc->req);
909b81beec9SGreg Kroah-Hartman err_free_rpc:
910b81beec9SGreg Kroah-Hartman kfree(rpc);
911b81beec9SGreg Kroah-Hartman
912b81beec9SGreg Kroah-Hartman return NULL;
913b81beec9SGreg Kroah-Hartman }
914b81beec9SGreg Kroah-Hartman
arpc_free(struct arpc * rpc)915b81beec9SGreg Kroah-Hartman static void arpc_free(struct arpc *rpc)
916b81beec9SGreg Kroah-Hartman {
917b81beec9SGreg Kroah-Hartman kfree(rpc->req);
918b81beec9SGreg Kroah-Hartman kfree(rpc->resp);
919b81beec9SGreg Kroah-Hartman kfree(rpc);
920b81beec9SGreg Kroah-Hartman }
921b81beec9SGreg Kroah-Hartman
arpc_find(struct es2_ap_dev * es2,__le16 id)922b81beec9SGreg Kroah-Hartman static struct arpc *arpc_find(struct es2_ap_dev *es2, __le16 id)
923b81beec9SGreg Kroah-Hartman {
924b81beec9SGreg Kroah-Hartman struct arpc *rpc;
925b81beec9SGreg Kroah-Hartman
926b81beec9SGreg Kroah-Hartman list_for_each_entry(rpc, &es2->arpcs, list) {
927b81beec9SGreg Kroah-Hartman if (rpc->req->id == id)
928b81beec9SGreg Kroah-Hartman return rpc;
929b81beec9SGreg Kroah-Hartman }
930b81beec9SGreg Kroah-Hartman
931b81beec9SGreg Kroah-Hartman return NULL;
932b81beec9SGreg Kroah-Hartman }
933b81beec9SGreg Kroah-Hartman
arpc_add(struct es2_ap_dev * es2,struct arpc * rpc)934b81beec9SGreg Kroah-Hartman static void arpc_add(struct es2_ap_dev *es2, struct arpc *rpc)
935b81beec9SGreg Kroah-Hartman {
936b81beec9SGreg Kroah-Hartman rpc->active = true;
937b81beec9SGreg Kroah-Hartman rpc->req->id = cpu_to_le16(es2->arpc_id_cycle++);
938b81beec9SGreg Kroah-Hartman list_add_tail(&rpc->list, &es2->arpcs);
939b81beec9SGreg Kroah-Hartman }
940b81beec9SGreg Kroah-Hartman
arpc_del(struct es2_ap_dev * es2,struct arpc * rpc)941b81beec9SGreg Kroah-Hartman static void arpc_del(struct es2_ap_dev *es2, struct arpc *rpc)
942b81beec9SGreg Kroah-Hartman {
943b81beec9SGreg Kroah-Hartman if (rpc->active) {
944b81beec9SGreg Kroah-Hartman rpc->active = false;
945b81beec9SGreg Kroah-Hartman list_del(&rpc->list);
946b81beec9SGreg Kroah-Hartman }
947b81beec9SGreg Kroah-Hartman }
948b81beec9SGreg Kroah-Hartman
arpc_send(struct es2_ap_dev * es2,struct arpc * rpc,int timeout)949b81beec9SGreg Kroah-Hartman static int arpc_send(struct es2_ap_dev *es2, struct arpc *rpc, int timeout)
950b81beec9SGreg Kroah-Hartman {
951b81beec9SGreg Kroah-Hartman struct usb_device *udev = es2->usb_dev;
952b81beec9SGreg Kroah-Hartman int retval;
953b81beec9SGreg Kroah-Hartman
954b81beec9SGreg Kroah-Hartman retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
955b81beec9SGreg Kroah-Hartman GB_APB_REQUEST_ARPC_RUN,
956b81beec9SGreg Kroah-Hartman USB_DIR_OUT | USB_TYPE_VENDOR |
957b81beec9SGreg Kroah-Hartman USB_RECIP_INTERFACE,
958b81beec9SGreg Kroah-Hartman 0, 0,
959b81beec9SGreg Kroah-Hartman rpc->req, le16_to_cpu(rpc->req->size),
960b81beec9SGreg Kroah-Hartman ES2_USB_CTRL_TIMEOUT);
961e4240253SJohan Hovold if (retval < 0) {
962b81beec9SGreg Kroah-Hartman dev_err(&udev->dev,
963b81beec9SGreg Kroah-Hartman "failed to send ARPC request %d: %d\n",
964b81beec9SGreg Kroah-Hartman rpc->req->type, retval);
965b81beec9SGreg Kroah-Hartman return retval;
966b81beec9SGreg Kroah-Hartman }
967b81beec9SGreg Kroah-Hartman
968b81beec9SGreg Kroah-Hartman return 0;
969b81beec9SGreg Kroah-Hartman }
970b81beec9SGreg Kroah-Hartman
arpc_sync(struct es2_ap_dev * es2,u8 type,void * payload,size_t size,int * result,unsigned int timeout)971b81beec9SGreg Kroah-Hartman static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload,
972b81beec9SGreg Kroah-Hartman size_t size, int *result, unsigned int timeout)
973b81beec9SGreg Kroah-Hartman {
974b81beec9SGreg Kroah-Hartman struct arpc *rpc;
975b81beec9SGreg Kroah-Hartman unsigned long flags;
976b81beec9SGreg Kroah-Hartman int retval;
977b81beec9SGreg Kroah-Hartman
978b81beec9SGreg Kroah-Hartman if (result)
979b81beec9SGreg Kroah-Hartman *result = 0;
980b81beec9SGreg Kroah-Hartman
981b81beec9SGreg Kroah-Hartman rpc = arpc_alloc(payload, size, type);
982b81beec9SGreg Kroah-Hartman if (!rpc)
983b81beec9SGreg Kroah-Hartman return -ENOMEM;
984b81beec9SGreg Kroah-Hartman
985b81beec9SGreg Kroah-Hartman spin_lock_irqsave(&es2->arpc_lock, flags);
986b81beec9SGreg Kroah-Hartman arpc_add(es2, rpc);
987b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->arpc_lock, flags);
988b81beec9SGreg Kroah-Hartman
989b81beec9SGreg Kroah-Hartman retval = arpc_send(es2, rpc, timeout);
990b81beec9SGreg Kroah-Hartman if (retval)
991b81beec9SGreg Kroah-Hartman goto out_arpc_del;
992b81beec9SGreg Kroah-Hartman
993b81beec9SGreg Kroah-Hartman retval = wait_for_completion_interruptible_timeout(
994b81beec9SGreg Kroah-Hartman &rpc->response_received,
995b81beec9SGreg Kroah-Hartman msecs_to_jiffies(timeout));
996b81beec9SGreg Kroah-Hartman if (retval <= 0) {
997b81beec9SGreg Kroah-Hartman if (!retval)
998b81beec9SGreg Kroah-Hartman retval = -ETIMEDOUT;
999b81beec9SGreg Kroah-Hartman goto out_arpc_del;
1000b81beec9SGreg Kroah-Hartman }
1001b81beec9SGreg Kroah-Hartman
1002b81beec9SGreg Kroah-Hartman if (rpc->resp->result) {
1003b81beec9SGreg Kroah-Hartman retval = -EREMOTEIO;
1004b81beec9SGreg Kroah-Hartman if (result)
1005b81beec9SGreg Kroah-Hartman *result = rpc->resp->result;
1006b81beec9SGreg Kroah-Hartman } else {
1007b81beec9SGreg Kroah-Hartman retval = 0;
1008b81beec9SGreg Kroah-Hartman }
1009b81beec9SGreg Kroah-Hartman
1010b81beec9SGreg Kroah-Hartman out_arpc_del:
1011b81beec9SGreg Kroah-Hartman spin_lock_irqsave(&es2->arpc_lock, flags);
1012b81beec9SGreg Kroah-Hartman arpc_del(es2, rpc);
1013b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->arpc_lock, flags);
1014b81beec9SGreg Kroah-Hartman arpc_free(rpc);
1015b81beec9SGreg Kroah-Hartman
1016b81beec9SGreg Kroah-Hartman if (retval < 0 && retval != -EREMOTEIO) {
1017b81beec9SGreg Kroah-Hartman dev_err(&es2->usb_dev->dev,
1018b81beec9SGreg Kroah-Hartman "failed to execute ARPC: %d\n", retval);
1019b81beec9SGreg Kroah-Hartman }
1020b81beec9SGreg Kroah-Hartman
1021b81beec9SGreg Kroah-Hartman return retval;
1022b81beec9SGreg Kroah-Hartman }
1023b81beec9SGreg Kroah-Hartman
arpc_in_callback(struct urb * urb)1024b81beec9SGreg Kroah-Hartman static void arpc_in_callback(struct urb *urb)
1025b81beec9SGreg Kroah-Hartman {
1026b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = urb->context;
1027b81beec9SGreg Kroah-Hartman struct device *dev = &urb->dev->dev;
1028b81beec9SGreg Kroah-Hartman int status = check_urb_status(urb);
1029b81beec9SGreg Kroah-Hartman struct arpc *rpc;
1030b81beec9SGreg Kroah-Hartman struct arpc_response_message *resp;
1031b81beec9SGreg Kroah-Hartman unsigned long flags;
1032b81beec9SGreg Kroah-Hartman int retval;
1033b81beec9SGreg Kroah-Hartman
1034b81beec9SGreg Kroah-Hartman if (status) {
1035b81beec9SGreg Kroah-Hartman if ((status == -EAGAIN) || (status == -EPROTO))
1036b81beec9SGreg Kroah-Hartman goto exit;
1037b81beec9SGreg Kroah-Hartman
1038b81beec9SGreg Kroah-Hartman /* The urb is being unlinked */
1039b81beec9SGreg Kroah-Hartman if (status == -ENOENT || status == -ESHUTDOWN)
1040b81beec9SGreg Kroah-Hartman return;
1041b81beec9SGreg Kroah-Hartman
1042b81beec9SGreg Kroah-Hartman dev_err(dev, "arpc in-urb error %d (dropped)\n", status);
1043b81beec9SGreg Kroah-Hartman return;
1044b81beec9SGreg Kroah-Hartman }
1045b81beec9SGreg Kroah-Hartman
1046b81beec9SGreg Kroah-Hartman if (urb->actual_length < sizeof(*resp)) {
1047b81beec9SGreg Kroah-Hartman dev_err(dev, "short aprc response received\n");
1048b81beec9SGreg Kroah-Hartman goto exit;
1049b81beec9SGreg Kroah-Hartman }
1050b81beec9SGreg Kroah-Hartman
1051b81beec9SGreg Kroah-Hartman resp = urb->transfer_buffer;
1052b81beec9SGreg Kroah-Hartman spin_lock_irqsave(&es2->arpc_lock, flags);
1053b81beec9SGreg Kroah-Hartman rpc = arpc_find(es2, resp->id);
1054b81beec9SGreg Kroah-Hartman if (!rpc) {
1055b81beec9SGreg Kroah-Hartman dev_err(dev, "invalid arpc response id received: %u\n",
1056b81beec9SGreg Kroah-Hartman le16_to_cpu(resp->id));
1057b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->arpc_lock, flags);
1058b81beec9SGreg Kroah-Hartman goto exit;
1059b81beec9SGreg Kroah-Hartman }
1060b81beec9SGreg Kroah-Hartman
1061b81beec9SGreg Kroah-Hartman arpc_del(es2, rpc);
1062b81beec9SGreg Kroah-Hartman memcpy(rpc->resp, resp, sizeof(*resp));
1063b81beec9SGreg Kroah-Hartman complete(&rpc->response_received);
1064b81beec9SGreg Kroah-Hartman spin_unlock_irqrestore(&es2->arpc_lock, flags);
1065b81beec9SGreg Kroah-Hartman
1066b81beec9SGreg Kroah-Hartman exit:
1067b81beec9SGreg Kroah-Hartman /* put our urb back in the request pool */
1068b81beec9SGreg Kroah-Hartman retval = usb_submit_urb(urb, GFP_ATOMIC);
1069b81beec9SGreg Kroah-Hartman if (retval)
1070b81beec9SGreg Kroah-Hartman dev_err(dev, "failed to resubmit arpc in-urb: %d\n", retval);
1071b81beec9SGreg Kroah-Hartman }
1072b81beec9SGreg Kroah-Hartman
1073b81beec9SGreg Kroah-Hartman #define APB1_LOG_MSG_SIZE 64
apb_log_get(struct es2_ap_dev * es2,char * buf)1074b81beec9SGreg Kroah-Hartman static void apb_log_get(struct es2_ap_dev *es2, char *buf)
1075b81beec9SGreg Kroah-Hartman {
1076b81beec9SGreg Kroah-Hartman int retval;
1077b81beec9SGreg Kroah-Hartman
1078b81beec9SGreg Kroah-Hartman do {
1079b81beec9SGreg Kroah-Hartman retval = usb_control_msg(es2->usb_dev,
1080b81beec9SGreg Kroah-Hartman usb_rcvctrlpipe(es2->usb_dev, 0),
1081b81beec9SGreg Kroah-Hartman GB_APB_REQUEST_LOG,
1082b81beec9SGreg Kroah-Hartman USB_DIR_IN | USB_TYPE_VENDOR |
1083b81beec9SGreg Kroah-Hartman USB_RECIP_INTERFACE,
1084b81beec9SGreg Kroah-Hartman 0x00, 0x00,
1085b81beec9SGreg Kroah-Hartman buf,
1086b81beec9SGreg Kroah-Hartman APB1_LOG_MSG_SIZE,
1087b81beec9SGreg Kroah-Hartman ES2_USB_CTRL_TIMEOUT);
1088b81beec9SGreg Kroah-Hartman if (retval > 0)
1089b81beec9SGreg Kroah-Hartman kfifo_in(&es2->apb_log_fifo, buf, retval);
1090b81beec9SGreg Kroah-Hartman } while (retval > 0);
1091b81beec9SGreg Kroah-Hartman }
1092b81beec9SGreg Kroah-Hartman
apb_log_poll(void * data)1093b81beec9SGreg Kroah-Hartman static int apb_log_poll(void *data)
1094b81beec9SGreg Kroah-Hartman {
1095b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = data;
1096b81beec9SGreg Kroah-Hartman char *buf;
1097b81beec9SGreg Kroah-Hartman
1098b81beec9SGreg Kroah-Hartman buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL);
1099b81beec9SGreg Kroah-Hartman if (!buf)
1100b81beec9SGreg Kroah-Hartman return -ENOMEM;
1101b81beec9SGreg Kroah-Hartman
1102b81beec9SGreg Kroah-Hartman while (!kthread_should_stop()) {
1103b81beec9SGreg Kroah-Hartman msleep(1000);
1104b81beec9SGreg Kroah-Hartman apb_log_get(es2, buf);
1105b81beec9SGreg Kroah-Hartman }
1106b81beec9SGreg Kroah-Hartman
1107b81beec9SGreg Kroah-Hartman kfree(buf);
1108b81beec9SGreg Kroah-Hartman
1109b81beec9SGreg Kroah-Hartman return 0;
1110b81beec9SGreg Kroah-Hartman }
1111b81beec9SGreg Kroah-Hartman
apb_log_read(struct file * f,char __user * buf,size_t count,loff_t * ppos)1112b81beec9SGreg Kroah-Hartman static ssize_t apb_log_read(struct file *f, char __user *buf,
1113b81beec9SGreg Kroah-Hartman size_t count, loff_t *ppos)
1114b81beec9SGreg Kroah-Hartman {
1115b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = file_inode(f)->i_private;
1116b81beec9SGreg Kroah-Hartman ssize_t ret;
1117b81beec9SGreg Kroah-Hartman size_t copied;
1118b81beec9SGreg Kroah-Hartman char *tmp_buf;
1119b81beec9SGreg Kroah-Hartman
1120b81beec9SGreg Kroah-Hartman if (count > APB1_LOG_SIZE)
1121b81beec9SGreg Kroah-Hartman count = APB1_LOG_SIZE;
1122b81beec9SGreg Kroah-Hartman
1123b81beec9SGreg Kroah-Hartman tmp_buf = kmalloc(count, GFP_KERNEL);
1124b81beec9SGreg Kroah-Hartman if (!tmp_buf)
1125b81beec9SGreg Kroah-Hartman return -ENOMEM;
1126b81beec9SGreg Kroah-Hartman
1127b81beec9SGreg Kroah-Hartman copied = kfifo_out(&es2->apb_log_fifo, tmp_buf, count);
1128b81beec9SGreg Kroah-Hartman ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied);
1129b81beec9SGreg Kroah-Hartman
1130b81beec9SGreg Kroah-Hartman kfree(tmp_buf);
1131b81beec9SGreg Kroah-Hartman
1132b81beec9SGreg Kroah-Hartman return ret;
1133b81beec9SGreg Kroah-Hartman }
1134b81beec9SGreg Kroah-Hartman
1135b81beec9SGreg Kroah-Hartman static const struct file_operations apb_log_fops = {
1136b81beec9SGreg Kroah-Hartman .read = apb_log_read,
1137b81beec9SGreg Kroah-Hartman };
1138b81beec9SGreg Kroah-Hartman
usb_log_enable(struct es2_ap_dev * es2)1139b81beec9SGreg Kroah-Hartman static void usb_log_enable(struct es2_ap_dev *es2)
1140b81beec9SGreg Kroah-Hartman {
1141b81beec9SGreg Kroah-Hartman if (!IS_ERR_OR_NULL(es2->apb_log_task))
1142b81beec9SGreg Kroah-Hartman return;
1143b81beec9SGreg Kroah-Hartman
1144b81beec9SGreg Kroah-Hartman /* get log from APB1 */
1145b81beec9SGreg Kroah-Hartman es2->apb_log_task = kthread_run(apb_log_poll, es2, "apb_log");
1146b81beec9SGreg Kroah-Hartman if (IS_ERR(es2->apb_log_task))
1147b81beec9SGreg Kroah-Hartman return;
1148b81beec9SGreg Kroah-Hartman /* XXX We will need to rename this per APB */
1149b81beec9SGreg Kroah-Hartman es2->apb_log_dentry = debugfs_create_file("apb_log", 0444,
1150b81beec9SGreg Kroah-Hartman gb_debugfs_get(), es2,
1151b81beec9SGreg Kroah-Hartman &apb_log_fops);
1152b81beec9SGreg Kroah-Hartman }
1153b81beec9SGreg Kroah-Hartman
usb_log_disable(struct es2_ap_dev * es2)1154b81beec9SGreg Kroah-Hartman static void usb_log_disable(struct es2_ap_dev *es2)
1155b81beec9SGreg Kroah-Hartman {
1156b81beec9SGreg Kroah-Hartman if (IS_ERR_OR_NULL(es2->apb_log_task))
1157b81beec9SGreg Kroah-Hartman return;
1158b81beec9SGreg Kroah-Hartman
1159b81beec9SGreg Kroah-Hartman debugfs_remove(es2->apb_log_dentry);
1160b81beec9SGreg Kroah-Hartman es2->apb_log_dentry = NULL;
1161b81beec9SGreg Kroah-Hartman
1162b81beec9SGreg Kroah-Hartman kthread_stop(es2->apb_log_task);
1163b81beec9SGreg Kroah-Hartman es2->apb_log_task = NULL;
1164b81beec9SGreg Kroah-Hartman }
1165b81beec9SGreg Kroah-Hartman
apb_log_enable_read(struct file * f,char __user * buf,size_t count,loff_t * ppos)1166b81beec9SGreg Kroah-Hartman static ssize_t apb_log_enable_read(struct file *f, char __user *buf,
1167b81beec9SGreg Kroah-Hartman size_t count, loff_t *ppos)
1168b81beec9SGreg Kroah-Hartman {
1169b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = file_inode(f)->i_private;
1170b81beec9SGreg Kroah-Hartman int enable = !IS_ERR_OR_NULL(es2->apb_log_task);
1171b81beec9SGreg Kroah-Hartman char tmp_buf[3];
1172b81beec9SGreg Kroah-Hartman
1173b81beec9SGreg Kroah-Hartman sprintf(tmp_buf, "%d\n", enable);
1174012ac583SRasmus Villemoes return simple_read_from_buffer(buf, count, ppos, tmp_buf, 2);
1175b81beec9SGreg Kroah-Hartman }
1176b81beec9SGreg Kroah-Hartman
apb_log_enable_write(struct file * f,const char __user * buf,size_t count,loff_t * ppos)1177b81beec9SGreg Kroah-Hartman static ssize_t apb_log_enable_write(struct file *f, const char __user *buf,
1178b81beec9SGreg Kroah-Hartman size_t count, loff_t *ppos)
1179b81beec9SGreg Kroah-Hartman {
1180b81beec9SGreg Kroah-Hartman int enable;
1181b81beec9SGreg Kroah-Hartman ssize_t retval;
1182b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = file_inode(f)->i_private;
1183b81beec9SGreg Kroah-Hartman
1184b81beec9SGreg Kroah-Hartman retval = kstrtoint_from_user(buf, count, 10, &enable);
1185b81beec9SGreg Kroah-Hartman if (retval)
1186b81beec9SGreg Kroah-Hartman return retval;
1187b81beec9SGreg Kroah-Hartman
1188b81beec9SGreg Kroah-Hartman if (enable)
1189b81beec9SGreg Kroah-Hartman usb_log_enable(es2);
1190b81beec9SGreg Kroah-Hartman else
1191b81beec9SGreg Kroah-Hartman usb_log_disable(es2);
1192b81beec9SGreg Kroah-Hartman
1193b81beec9SGreg Kroah-Hartman return count;
1194b81beec9SGreg Kroah-Hartman }
1195b81beec9SGreg Kroah-Hartman
1196b81beec9SGreg Kroah-Hartman static const struct file_operations apb_log_enable_fops = {
1197b81beec9SGreg Kroah-Hartman .read = apb_log_enable_read,
1198b81beec9SGreg Kroah-Hartman .write = apb_log_enable_write,
1199b81beec9SGreg Kroah-Hartman };
1200b81beec9SGreg Kroah-Hartman
apb_get_cport_count(struct usb_device * udev)1201b81beec9SGreg Kroah-Hartman static int apb_get_cport_count(struct usb_device *udev)
1202b81beec9SGreg Kroah-Hartman {
1203b81beec9SGreg Kroah-Hartman int retval;
1204b81beec9SGreg Kroah-Hartman __le16 *cport_count;
1205b81beec9SGreg Kroah-Hartman
1206b81beec9SGreg Kroah-Hartman cport_count = kzalloc(sizeof(*cport_count), GFP_KERNEL);
1207b81beec9SGreg Kroah-Hartman if (!cport_count)
1208b81beec9SGreg Kroah-Hartman return -ENOMEM;
1209b81beec9SGreg Kroah-Hartman
1210b81beec9SGreg Kroah-Hartman retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
1211b81beec9SGreg Kroah-Hartman GB_APB_REQUEST_CPORT_COUNT,
1212b81beec9SGreg Kroah-Hartman USB_DIR_IN | USB_TYPE_VENDOR |
1213b81beec9SGreg Kroah-Hartman USB_RECIP_INTERFACE, 0, 0, cport_count,
1214b81beec9SGreg Kroah-Hartman sizeof(*cport_count), ES2_USB_CTRL_TIMEOUT);
1215b81beec9SGreg Kroah-Hartman if (retval != sizeof(*cport_count)) {
1216b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
1217b81beec9SGreg Kroah-Hartman retval);
1218b81beec9SGreg Kroah-Hartman
1219b81beec9SGreg Kroah-Hartman if (retval >= 0)
1220b81beec9SGreg Kroah-Hartman retval = -EIO;
1221b81beec9SGreg Kroah-Hartman
1222b81beec9SGreg Kroah-Hartman goto out;
1223b81beec9SGreg Kroah-Hartman }
1224b81beec9SGreg Kroah-Hartman
1225b81beec9SGreg Kroah-Hartman retval = le16_to_cpu(*cport_count);
1226b81beec9SGreg Kroah-Hartman
1227b81beec9SGreg Kroah-Hartman /* We need to fit a CPort ID in one byte of a message header */
1228b81beec9SGreg Kroah-Hartman if (retval > U8_MAX) {
1229b81beec9SGreg Kroah-Hartman retval = U8_MAX;
1230b81beec9SGreg Kroah-Hartman dev_warn(&udev->dev, "Limiting number of CPorts to U8_MAX\n");
1231b81beec9SGreg Kroah-Hartman }
1232b81beec9SGreg Kroah-Hartman
1233b81beec9SGreg Kroah-Hartman out:
1234b81beec9SGreg Kroah-Hartman kfree(cport_count);
1235b81beec9SGreg Kroah-Hartman return retval;
1236b81beec9SGreg Kroah-Hartman }
1237b81beec9SGreg Kroah-Hartman
1238b81beec9SGreg Kroah-Hartman /*
1239b81beec9SGreg Kroah-Hartman * The ES2 USB Bridge device has 15 endpoints
1240b81beec9SGreg Kroah-Hartman * 1 Control - usual USB stuff + AP -> APBridgeA messages
1241b81beec9SGreg Kroah-Hartman * 7 Bulk IN - CPort data in
1242b81beec9SGreg Kroah-Hartman * 7 Bulk OUT - CPort data out
1243b81beec9SGreg Kroah-Hartman */
ap_probe(struct usb_interface * interface,const struct usb_device_id * id)1244b81beec9SGreg Kroah-Hartman static int ap_probe(struct usb_interface *interface,
1245b81beec9SGreg Kroah-Hartman const struct usb_device_id *id)
1246b81beec9SGreg Kroah-Hartman {
1247b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2;
1248b81beec9SGreg Kroah-Hartman struct gb_host_device *hd;
1249b81beec9SGreg Kroah-Hartman struct usb_device *udev;
1250b81beec9SGreg Kroah-Hartman struct usb_host_interface *iface_desc;
1251b81beec9SGreg Kroah-Hartman struct usb_endpoint_descriptor *endpoint;
1252b81beec9SGreg Kroah-Hartman __u8 ep_addr;
1253b81beec9SGreg Kroah-Hartman int retval;
1254b81beec9SGreg Kroah-Hartman int i;
1255b81beec9SGreg Kroah-Hartman int num_cports;
1256b81beec9SGreg Kroah-Hartman bool bulk_out_found = false;
1257b81beec9SGreg Kroah-Hartman bool bulk_in_found = false;
1258b81beec9SGreg Kroah-Hartman bool arpc_in_found = false;
1259b81beec9SGreg Kroah-Hartman
1260b81beec9SGreg Kroah-Hartman udev = usb_get_dev(interface_to_usbdev(interface));
1261b81beec9SGreg Kroah-Hartman
1262b81beec9SGreg Kroah-Hartman num_cports = apb_get_cport_count(udev);
1263b81beec9SGreg Kroah-Hartman if (num_cports < 0) {
1264b81beec9SGreg Kroah-Hartman usb_put_dev(udev);
1265b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
1266b81beec9SGreg Kroah-Hartman num_cports);
1267b81beec9SGreg Kroah-Hartman return num_cports;
1268b81beec9SGreg Kroah-Hartman }
1269b81beec9SGreg Kroah-Hartman
1270b81beec9SGreg Kroah-Hartman hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX,
1271b81beec9SGreg Kroah-Hartman num_cports);
1272b81beec9SGreg Kroah-Hartman if (IS_ERR(hd)) {
1273b81beec9SGreg Kroah-Hartman usb_put_dev(udev);
1274b81beec9SGreg Kroah-Hartman return PTR_ERR(hd);
1275b81beec9SGreg Kroah-Hartman }
1276b81beec9SGreg Kroah-Hartman
1277b81beec9SGreg Kroah-Hartman es2 = hd_to_es2(hd);
1278b81beec9SGreg Kroah-Hartman es2->hd = hd;
1279b81beec9SGreg Kroah-Hartman es2->usb_intf = interface;
1280b81beec9SGreg Kroah-Hartman es2->usb_dev = udev;
1281b81beec9SGreg Kroah-Hartman spin_lock_init(&es2->cport_out_urb_lock);
1282b81beec9SGreg Kroah-Hartman INIT_KFIFO(es2->apb_log_fifo);
1283b81beec9SGreg Kroah-Hartman usb_set_intfdata(interface, es2);
1284b81beec9SGreg Kroah-Hartman
1285b81beec9SGreg Kroah-Hartman /*
1286b81beec9SGreg Kroah-Hartman * Reserve the CDSI0 and CDSI1 CPorts so they won't be allocated
1287b81beec9SGreg Kroah-Hartman * dynamically.
1288b81beec9SGreg Kroah-Hartman */
1289b81beec9SGreg Kroah-Hartman retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI0);
1290b81beec9SGreg Kroah-Hartman if (retval)
1291b81beec9SGreg Kroah-Hartman goto error;
1292b81beec9SGreg Kroah-Hartman retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI1);
1293b81beec9SGreg Kroah-Hartman if (retval)
1294b81beec9SGreg Kroah-Hartman goto error;
1295b81beec9SGreg Kroah-Hartman
1296b81beec9SGreg Kroah-Hartman /* find all bulk endpoints */
1297b81beec9SGreg Kroah-Hartman iface_desc = interface->cur_altsetting;
1298b81beec9SGreg Kroah-Hartman for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
1299b81beec9SGreg Kroah-Hartman endpoint = &iface_desc->endpoint[i].desc;
1300b81beec9SGreg Kroah-Hartman ep_addr = endpoint->bEndpointAddress;
1301b81beec9SGreg Kroah-Hartman
1302b81beec9SGreg Kroah-Hartman if (usb_endpoint_is_bulk_in(endpoint)) {
1303b81beec9SGreg Kroah-Hartman if (!bulk_in_found) {
1304b81beec9SGreg Kroah-Hartman es2->cport_in.endpoint = ep_addr;
1305b81beec9SGreg Kroah-Hartman bulk_in_found = true;
1306b81beec9SGreg Kroah-Hartman } else if (!arpc_in_found) {
1307b81beec9SGreg Kroah-Hartman es2->arpc_endpoint_in = ep_addr;
1308b81beec9SGreg Kroah-Hartman arpc_in_found = true;
1309b81beec9SGreg Kroah-Hartman } else {
1310b81beec9SGreg Kroah-Hartman dev_warn(&udev->dev,
1311b81beec9SGreg Kroah-Hartman "Unused bulk IN endpoint found: 0x%02x\n",
1312b81beec9SGreg Kroah-Hartman ep_addr);
1313b81beec9SGreg Kroah-Hartman }
1314b81beec9SGreg Kroah-Hartman continue;
1315b81beec9SGreg Kroah-Hartman }
1316b81beec9SGreg Kroah-Hartman if (usb_endpoint_is_bulk_out(endpoint)) {
1317b81beec9SGreg Kroah-Hartman if (!bulk_out_found) {
1318b81beec9SGreg Kroah-Hartman es2->cport_out_endpoint = ep_addr;
1319b81beec9SGreg Kroah-Hartman bulk_out_found = true;
1320b81beec9SGreg Kroah-Hartman } else {
1321b81beec9SGreg Kroah-Hartman dev_warn(&udev->dev,
1322b81beec9SGreg Kroah-Hartman "Unused bulk OUT endpoint found: 0x%02x\n",
1323b81beec9SGreg Kroah-Hartman ep_addr);
1324b81beec9SGreg Kroah-Hartman }
1325b81beec9SGreg Kroah-Hartman continue;
1326b81beec9SGreg Kroah-Hartman }
1327b81beec9SGreg Kroah-Hartman dev_warn(&udev->dev,
1328b81beec9SGreg Kroah-Hartman "Unknown endpoint type found, address 0x%02x\n",
1329b81beec9SGreg Kroah-Hartman ep_addr);
1330b81beec9SGreg Kroah-Hartman }
1331b81beec9SGreg Kroah-Hartman if (!bulk_in_found || !arpc_in_found || !bulk_out_found) {
1332b81beec9SGreg Kroah-Hartman dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
1333b81beec9SGreg Kroah-Hartman retval = -ENODEV;
1334b81beec9SGreg Kroah-Hartman goto error;
1335b81beec9SGreg Kroah-Hartman }
1336b81beec9SGreg Kroah-Hartman
1337b81beec9SGreg Kroah-Hartman /* Allocate buffers for our cport in messages */
1338b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
1339b81beec9SGreg Kroah-Hartman struct urb *urb;
1340b81beec9SGreg Kroah-Hartman u8 *buffer;
1341b81beec9SGreg Kroah-Hartman
1342b81beec9SGreg Kroah-Hartman urb = usb_alloc_urb(0, GFP_KERNEL);
1343b81beec9SGreg Kroah-Hartman if (!urb) {
1344b81beec9SGreg Kroah-Hartman retval = -ENOMEM;
1345b81beec9SGreg Kroah-Hartman goto error;
1346b81beec9SGreg Kroah-Hartman }
1347b81beec9SGreg Kroah-Hartman es2->cport_in.urb[i] = urb;
1348b81beec9SGreg Kroah-Hartman
1349b81beec9SGreg Kroah-Hartman buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL);
1350b81beec9SGreg Kroah-Hartman if (!buffer) {
1351b81beec9SGreg Kroah-Hartman retval = -ENOMEM;
1352b81beec9SGreg Kroah-Hartman goto error;
1353b81beec9SGreg Kroah-Hartman }
1354b81beec9SGreg Kroah-Hartman
1355b81beec9SGreg Kroah-Hartman usb_fill_bulk_urb(urb, udev,
1356b81beec9SGreg Kroah-Hartman usb_rcvbulkpipe(udev, es2->cport_in.endpoint),
1357b81beec9SGreg Kroah-Hartman buffer, ES2_GBUF_MSG_SIZE_MAX,
1358b81beec9SGreg Kroah-Hartman cport_in_callback, hd);
1359b81beec9SGreg Kroah-Hartman
1360b81beec9SGreg Kroah-Hartman es2->cport_in.buffer[i] = buffer;
1361b81beec9SGreg Kroah-Hartman }
1362b81beec9SGreg Kroah-Hartman
1363b81beec9SGreg Kroah-Hartman /* Allocate buffers for ARPC in messages */
1364b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
1365b81beec9SGreg Kroah-Hartman struct urb *urb;
1366b81beec9SGreg Kroah-Hartman u8 *buffer;
1367b81beec9SGreg Kroah-Hartman
1368b81beec9SGreg Kroah-Hartman urb = usb_alloc_urb(0, GFP_KERNEL);
1369b81beec9SGreg Kroah-Hartman if (!urb) {
1370b81beec9SGreg Kroah-Hartman retval = -ENOMEM;
1371b81beec9SGreg Kroah-Hartman goto error;
1372b81beec9SGreg Kroah-Hartman }
1373b81beec9SGreg Kroah-Hartman es2->arpc_urb[i] = urb;
1374b81beec9SGreg Kroah-Hartman
1375b81beec9SGreg Kroah-Hartman buffer = kmalloc(ARPC_IN_SIZE_MAX, GFP_KERNEL);
1376b81beec9SGreg Kroah-Hartman if (!buffer) {
1377b81beec9SGreg Kroah-Hartman retval = -ENOMEM;
1378b81beec9SGreg Kroah-Hartman goto error;
1379b81beec9SGreg Kroah-Hartman }
1380b81beec9SGreg Kroah-Hartman
1381b81beec9SGreg Kroah-Hartman usb_fill_bulk_urb(urb, udev,
1382b81beec9SGreg Kroah-Hartman usb_rcvbulkpipe(udev,
1383b81beec9SGreg Kroah-Hartman es2->arpc_endpoint_in),
1384b81beec9SGreg Kroah-Hartman buffer, ARPC_IN_SIZE_MAX,
1385b81beec9SGreg Kroah-Hartman arpc_in_callback, es2);
1386b81beec9SGreg Kroah-Hartman
1387b81beec9SGreg Kroah-Hartman es2->arpc_buffer[i] = buffer;
1388b81beec9SGreg Kroah-Hartman }
1389b81beec9SGreg Kroah-Hartman
1390b81beec9SGreg Kroah-Hartman /* Allocate urbs for our CPort OUT messages */
1391b81beec9SGreg Kroah-Hartman for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
1392b81beec9SGreg Kroah-Hartman struct urb *urb;
1393b81beec9SGreg Kroah-Hartman
1394b81beec9SGreg Kroah-Hartman urb = usb_alloc_urb(0, GFP_KERNEL);
1395b81beec9SGreg Kroah-Hartman if (!urb) {
1396b81beec9SGreg Kroah-Hartman retval = -ENOMEM;
1397b81beec9SGreg Kroah-Hartman goto error;
1398b81beec9SGreg Kroah-Hartman }
1399b81beec9SGreg Kroah-Hartman
1400b81beec9SGreg Kroah-Hartman es2->cport_out_urb[i] = urb;
1401b81beec9SGreg Kroah-Hartman es2->cport_out_urb_busy[i] = false; /* just to be anal */
1402b81beec9SGreg Kroah-Hartman }
1403b81beec9SGreg Kroah-Hartman
1404b81beec9SGreg Kroah-Hartman /* XXX We will need to rename this per APB */
1405b81beec9SGreg Kroah-Hartman es2->apb_log_enable_dentry = debugfs_create_file("apb_log_enable",
1406b81beec9SGreg Kroah-Hartman 0644,
1407b81beec9SGreg Kroah-Hartman gb_debugfs_get(), es2,
1408b81beec9SGreg Kroah-Hartman &apb_log_enable_fops);
1409b81beec9SGreg Kroah-Hartman
1410b81beec9SGreg Kroah-Hartman INIT_LIST_HEAD(&es2->arpcs);
1411b81beec9SGreg Kroah-Hartman spin_lock_init(&es2->arpc_lock);
1412b81beec9SGreg Kroah-Hartman
1413b81beec9SGreg Kroah-Hartman retval = es2_arpc_in_enable(es2);
1414b81beec9SGreg Kroah-Hartman if (retval)
1415b81beec9SGreg Kroah-Hartman goto error;
1416b81beec9SGreg Kroah-Hartman
1417b81beec9SGreg Kroah-Hartman retval = gb_hd_add(hd);
1418b81beec9SGreg Kroah-Hartman if (retval)
1419b81beec9SGreg Kroah-Hartman goto err_disable_arpc_in;
1420b81beec9SGreg Kroah-Hartman
1421b81beec9SGreg Kroah-Hartman retval = es2_cport_in_enable(es2, &es2->cport_in);
1422b81beec9SGreg Kroah-Hartman if (retval)
1423b81beec9SGreg Kroah-Hartman goto err_hd_del;
1424b81beec9SGreg Kroah-Hartman
1425b81beec9SGreg Kroah-Hartman return 0;
1426b81beec9SGreg Kroah-Hartman
1427b81beec9SGreg Kroah-Hartman err_hd_del:
1428b81beec9SGreg Kroah-Hartman gb_hd_del(hd);
1429b81beec9SGreg Kroah-Hartman err_disable_arpc_in:
1430b81beec9SGreg Kroah-Hartman es2_arpc_in_disable(es2);
1431b81beec9SGreg Kroah-Hartman error:
1432b81beec9SGreg Kroah-Hartman es2_destroy(es2);
1433b81beec9SGreg Kroah-Hartman
1434b81beec9SGreg Kroah-Hartman return retval;
1435b81beec9SGreg Kroah-Hartman }
1436b81beec9SGreg Kroah-Hartman
ap_disconnect(struct usb_interface * interface)1437b81beec9SGreg Kroah-Hartman static void ap_disconnect(struct usb_interface *interface)
1438b81beec9SGreg Kroah-Hartman {
1439b81beec9SGreg Kroah-Hartman struct es2_ap_dev *es2 = usb_get_intfdata(interface);
1440b81beec9SGreg Kroah-Hartman
1441b81beec9SGreg Kroah-Hartman gb_hd_del(es2->hd);
1442b81beec9SGreg Kroah-Hartman
1443b81beec9SGreg Kroah-Hartman es2_cport_in_disable(es2, &es2->cport_in);
1444b81beec9SGreg Kroah-Hartman es2_arpc_in_disable(es2);
1445b81beec9SGreg Kroah-Hartman
1446b81beec9SGreg Kroah-Hartman es2_destroy(es2);
1447b81beec9SGreg Kroah-Hartman }
1448b81beec9SGreg Kroah-Hartman
1449b81beec9SGreg Kroah-Hartman static struct usb_driver es2_ap_driver = {
1450b81beec9SGreg Kroah-Hartman .name = "es2_ap_driver",
1451b81beec9SGreg Kroah-Hartman .probe = ap_probe,
1452b81beec9SGreg Kroah-Hartman .disconnect = ap_disconnect,
1453b81beec9SGreg Kroah-Hartman .id_table = id_table,
1454b81beec9SGreg Kroah-Hartman .soft_unbind = 1,
1455b81beec9SGreg Kroah-Hartman };
1456b81beec9SGreg Kroah-Hartman
1457b81beec9SGreg Kroah-Hartman module_usb_driver(es2_ap_driver);
1458b81beec9SGreg Kroah-Hartman
1459b81beec9SGreg Kroah-Hartman MODULE_LICENSE("GPL v2");
1460b81beec9SGreg Kroah-Hartman MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
1461