blob: f81861af5b257cda6eb8fbf0e7777d7549df3cdc [file] [log] [blame]
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -08001/**
2 * Marvell NFC-over-USB driver: USB interface related functions
3 *
4 * Copyright (C) 2014, Marvell International Ltd.
5 *
6 * This software file (the "File") is distributed by Marvell International
7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
8 * (the "License"). You may use, redistribute and/or modify this File in
9 * accordance with the terms and conditions of the License, a copy of which
10 * is available on the worldwide web at
11 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12 *
13 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
14 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
15 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
16 * this warranty disclaimer.
17 **/
18
19#include <linux/module.h>
20#include <linux/usb.h>
21#include <linux/nfc.h>
22#include <net/nfc/nci.h>
23#include <net/nfc/nci_core.h>
24#include "nfcmrvl.h"
25
26#define VERSION "1.0"
27
28static struct usb_device_id nfcmrvl_table[] = {
29 { USB_DEVICE_INTERFACE_CLASS(0x1286, 0x2046, 0xff) },
30 { } /* Terminating entry */
31};
32
33MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
34
35#define NFCMRVL_USB_BULK_RUNNING 1
36#define NFCMRVL_USB_SUSPENDING 2
37
38struct nfcmrvl_usb_drv_data {
39 struct usb_device *udev;
40 struct usb_interface *intf;
41 unsigned long flags;
42 struct work_struct waker;
43 struct usb_anchor tx_anchor;
44 struct usb_anchor bulk_anchor;
45 struct usb_anchor deferred;
46 int tx_in_flight;
47 /* protects tx_in_flight */
48 spinlock_t txlock;
49 struct usb_endpoint_descriptor *bulk_tx_ep;
50 struct usb_endpoint_descriptor *bulk_rx_ep;
51 int suspend_count;
52 struct nfcmrvl_private *priv;
53};
54
55static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
56{
57 unsigned long flags;
58 int rv;
59
60 spin_lock_irqsave(&drv_data->txlock, flags);
61 rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
62 if (!rv)
63 drv_data->tx_in_flight++;
64 spin_unlock_irqrestore(&drv_data->txlock, flags);
65
66 return rv;
67}
68
69static void nfcmrvl_bulk_complete(struct urb *urb)
70{
71 struct nfcmrvl_usb_drv_data *drv_data = urb->context;
Vincent Cuissarde1bf80c2015-06-11 11:25:44 +020072 struct sk_buff *skb;
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -080073 int err;
74
Vincent Cuissarde1bf80c2015-06-11 11:25:44 +020075 dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -080076 urb, urb->status, urb->actual_length);
77
78 if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
79 return;
80
81 if (!urb->status) {
Vincent Cuissarde1bf80c2015-06-11 11:25:44 +020082 skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
83 GFP_ATOMIC);
84 if (!skb) {
85 nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
86 } else {
87 memcpy(skb_put(skb, urb->actual_length),
88 urb->transfer_buffer, urb->actual_length);
89 if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
90 nfc_err(&drv_data->udev->dev,
91 "corrupted Rx packet\n");
92 }
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -080093 }
94
95 if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
96 return;
97
98 usb_anchor_urb(urb, &drv_data->bulk_anchor);
99 usb_mark_last_busy(drv_data->udev);
100
101 err = usb_submit_urb(urb, GFP_ATOMIC);
102 if (err) {
103 /* -EPERM: urb is being killed;
104 * -ENODEV: device got disconnected
105 */
106 if (err != -EPERM && err != -ENODEV)
107 nfc_err(&drv_data->udev->dev,
Joe Perches3590ebc2015-04-07 00:17:00 -0700108 "urb %p failed to resubmit (%d)\n", urb, -err);
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800109 usb_unanchor_urb(urb);
110 }
111}
112
113static int
114nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
115{
116 struct urb *urb;
117 unsigned char *buf;
118 unsigned int pipe;
119 int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
120
121 if (!drv_data->bulk_rx_ep)
122 return -ENODEV;
123
124 urb = usb_alloc_urb(0, mem_flags);
125 if (!urb)
126 return -ENOMEM;
127
128 buf = kmalloc(size, mem_flags);
129 if (!buf) {
130 usb_free_urb(urb);
131 return -ENOMEM;
132 }
133
134 pipe = usb_rcvbulkpipe(drv_data->udev,
135 drv_data->bulk_rx_ep->bEndpointAddress);
136
137 usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
138 nfcmrvl_bulk_complete, drv_data);
139
140 urb->transfer_flags |= URB_FREE_BUFFER;
141
142 usb_mark_last_busy(drv_data->udev);
143 usb_anchor_urb(urb, &drv_data->bulk_anchor);
144
145 err = usb_submit_urb(urb, mem_flags);
146 if (err) {
147 if (err != -EPERM && err != -ENODEV)
148 nfc_err(&drv_data->udev->dev,
Joe Perches3590ebc2015-04-07 00:17:00 -0700149 "urb %p submission failed (%d)\n", urb, -err);
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800150 usb_unanchor_urb(urb);
151 }
152
153 usb_free_urb(urb);
154
155 return err;
156}
157
158static void nfcmrvl_tx_complete(struct urb *urb)
159{
160 struct sk_buff *skb = urb->context;
161 struct nci_dev *ndev = (struct nci_dev *)skb->dev;
162 struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
163 struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
164
Joe Perches3590ebc2015-04-07 00:17:00 -0700165 nfc_info(priv->dev, "urb %p status %d count %d\n",
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800166 urb, urb->status, urb->actual_length);
167
168 spin_lock(&drv_data->txlock);
169 drv_data->tx_in_flight--;
170 spin_unlock(&drv_data->txlock);
171
172 kfree(urb->setup_packet);
173 kfree_skb(skb);
174}
175
176static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
177{
178 struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
179 int err;
180
181 err = usb_autopm_get_interface(drv_data->intf);
182 if (err)
183 return err;
184
185 drv_data->intf->needs_remote_wakeup = 1;
186
187 err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
188 if (err)
189 goto failed;
190
191 set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
192 nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
193
194 usb_autopm_put_interface(drv_data->intf);
195 return 0;
196
197failed:
198 usb_autopm_put_interface(drv_data->intf);
199 return err;
200}
201
202static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
203{
204 usb_kill_anchored_urbs(&drv_data->bulk_anchor);
205}
206
207static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
208{
209 struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
210 int err;
211
212 cancel_work_sync(&drv_data->waker);
213
214 clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
215
216 nfcmrvl_usb_stop_traffic(drv_data);
217 usb_kill_anchored_urbs(&drv_data->tx_anchor);
218 err = usb_autopm_get_interface(drv_data->intf);
219 if (err)
220 goto failed;
221
222 drv_data->intf->needs_remote_wakeup = 0;
223 usb_autopm_put_interface(drv_data->intf);
224
225failed:
226 usb_scuttle_anchored_urbs(&drv_data->deferred);
227 return 0;
228}
229
230static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
231 struct sk_buff *skb)
232{
233 struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
234 struct urb *urb;
235 unsigned int pipe;
236 int err;
237
238 if (!drv_data->bulk_tx_ep)
239 return -ENODEV;
240
241 urb = usb_alloc_urb(0, GFP_ATOMIC);
242 if (!urb)
243 return -ENOMEM;
244
245 pipe = usb_sndbulkpipe(drv_data->udev,
246 drv_data->bulk_tx_ep->bEndpointAddress);
247
248 usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
249 nfcmrvl_tx_complete, skb);
250
251 err = nfcmrvl_inc_tx(drv_data);
252 if (err) {
253 usb_anchor_urb(urb, &drv_data->deferred);
254 schedule_work(&drv_data->waker);
255 err = 0;
256 goto done;
257 }
258
259 usb_anchor_urb(urb, &drv_data->tx_anchor);
260
261 err = usb_submit_urb(urb, GFP_ATOMIC);
262 if (err) {
263 if (err != -EPERM && err != -ENODEV)
264 nfc_err(&drv_data->udev->dev,
Joe Perches3590ebc2015-04-07 00:17:00 -0700265 "urb %p submission failed (%d)\n", urb, -err);
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800266 kfree(urb->setup_packet);
267 usb_unanchor_urb(urb);
268 } else {
269 usb_mark_last_busy(drv_data->udev);
270 }
271
272done:
273 usb_free_urb(urb);
274 return err;
275}
276
277static struct nfcmrvl_if_ops usb_ops = {
278 .nci_open = nfcmrvl_usb_nci_open,
279 .nci_close = nfcmrvl_usb_nci_close,
280 .nci_send = nfcmrvl_usb_nci_send,
281};
282
283static void nfcmrvl_waker(struct work_struct *work)
284{
285 struct nfcmrvl_usb_drv_data *drv_data =
286 container_of(work, struct nfcmrvl_usb_drv_data, waker);
287 int err;
288
289 err = usb_autopm_get_interface(drv_data->intf);
290 if (err)
291 return;
292
293 usb_autopm_put_interface(drv_data->intf);
294}
295
296static int nfcmrvl_probe(struct usb_interface *intf,
297 const struct usb_device_id *id)
298{
299 struct usb_endpoint_descriptor *ep_desc;
300 struct nfcmrvl_usb_drv_data *drv_data;
301 struct nfcmrvl_private *priv;
302 int i;
303 struct usb_device *udev = interface_to_usbdev(intf);
304
Joe Perches3590ebc2015-04-07 00:17:00 -0700305 nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800306
307 drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
308 if (!drv_data)
309 return -ENOMEM;
310
311 for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
312 ep_desc = &intf->cur_altsetting->endpoint[i].desc;
313
314 if (!drv_data->bulk_tx_ep &&
315 usb_endpoint_is_bulk_out(ep_desc)) {
316 drv_data->bulk_tx_ep = ep_desc;
317 continue;
318 }
319
320 if (!drv_data->bulk_rx_ep &&
321 usb_endpoint_is_bulk_in(ep_desc)) {
322 drv_data->bulk_rx_ep = ep_desc;
323 continue;
324 }
325 }
326
327 if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep)
328 return -ENODEV;
329
330 drv_data->udev = udev;
331 drv_data->intf = intf;
332
333 INIT_WORK(&drv_data->waker, nfcmrvl_waker);
334 spin_lock_init(&drv_data->txlock);
335
336 init_usb_anchor(&drv_data->tx_anchor);
337 init_usb_anchor(&drv_data->bulk_anchor);
338 init_usb_anchor(&drv_data->deferred);
339
340 priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops,
Vincent Cuissardf1f1a7d2015-06-11 11:25:43 +0200341 &drv_data->udev->dev, 0);
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800342 if (IS_ERR(priv))
343 return PTR_ERR(priv);
344
345 drv_data->priv = priv;
346 priv->dev = &drv_data->udev->dev;
347
348 usb_set_intfdata(intf, drv_data);
349
350 return 0;
351}
352
353static void nfcmrvl_disconnect(struct usb_interface *intf)
354{
355 struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
356
357 if (!drv_data)
358 return;
359
Joe Perches3590ebc2015-04-07 00:17:00 -0700360 nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800361
362 nfcmrvl_nci_unregister_dev(drv_data->priv);
363
364 usb_set_intfdata(drv_data->intf, NULL);
365}
366
367#ifdef CONFIG_PM
368static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
369{
370 struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
371
Joe Perches3590ebc2015-04-07 00:17:00 -0700372 nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800373
374 if (drv_data->suspend_count++)
375 return 0;
376
377 spin_lock_irq(&drv_data->txlock);
378 if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
379 set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
380 spin_unlock_irq(&drv_data->txlock);
381 } else {
382 spin_unlock_irq(&drv_data->txlock);
383 drv_data->suspend_count--;
384 return -EBUSY;
385 }
386
387 nfcmrvl_usb_stop_traffic(drv_data);
388 usb_kill_anchored_urbs(&drv_data->tx_anchor);
389
390 return 0;
391}
392
393static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
394{
395 struct urb *urb;
396 int err;
397
398 while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
399 err = usb_submit_urb(urb, GFP_ATOMIC);
400 if (err)
401 break;
402
403 drv_data->tx_in_flight++;
404 }
405 usb_scuttle_anchored_urbs(&drv_data->deferred);
406}
407
408static int nfcmrvl_resume(struct usb_interface *intf)
409{
410 struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
411 int err = 0;
412
Joe Perches3590ebc2015-04-07 00:17:00 -0700413 nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
Amitkumar Karwarf26e30c2014-01-06 12:58:19 -0800414
415 if (--drv_data->suspend_count)
416 return 0;
417
418 if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
419 goto done;
420
421 if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
422 err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
423 if (err) {
424 clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
425 goto failed;
426 }
427
428 nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
429 }
430
431 spin_lock_irq(&drv_data->txlock);
432 nfcmrvl_play_deferred(drv_data);
433 clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
434 spin_unlock_irq(&drv_data->txlock);
435
436 return 0;
437
438failed:
439 usb_scuttle_anchored_urbs(&drv_data->deferred);
440done:
441 spin_lock_irq(&drv_data->txlock);
442 clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
443 spin_unlock_irq(&drv_data->txlock);
444
445 return err;
446}
447#endif
448
449static struct usb_driver nfcmrvl_usb_driver = {
450 .name = "nfcmrvl",
451 .probe = nfcmrvl_probe,
452 .disconnect = nfcmrvl_disconnect,
453#ifdef CONFIG_PM
454 .suspend = nfcmrvl_suspend,
455 .resume = nfcmrvl_resume,
456 .reset_resume = nfcmrvl_resume,
457#endif
458 .id_table = nfcmrvl_table,
459 .supports_autosuspend = 1,
460 .disable_hub_initiated_lpm = 1,
461 .soft_unbind = 1,
462};
463module_usb_driver(nfcmrvl_usb_driver);
464
465MODULE_AUTHOR("Marvell International Ltd.");
466MODULE_DESCRIPTION("Marvell NFC-over-USB driver ver " VERSION);
467MODULE_VERSION(VERSION);
468MODULE_LICENSE("GPL v2");