Commit | Line | Data |
---|---|---|
f26e30cc AK |
1 | /** |
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 | ||
f26e30cc | 26 | static struct usb_device_id nfcmrvl_table[] = { |
8a81a96b VC |
27 | { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046, |
28 | USB_CLASS_VENDOR_SPEC, 4, 1) }, | |
f26e30cc AK |
29 | { } /* Terminating entry */ |
30 | }; | |
31 | ||
32 | MODULE_DEVICE_TABLE(usb, nfcmrvl_table); | |
33 | ||
34 | #define NFCMRVL_USB_BULK_RUNNING 1 | |
35 | #define NFCMRVL_USB_SUSPENDING 2 | |
36 | ||
37 | struct nfcmrvl_usb_drv_data { | |
38 | struct usb_device *udev; | |
39 | struct usb_interface *intf; | |
40 | unsigned long flags; | |
41 | struct work_struct waker; | |
42 | struct usb_anchor tx_anchor; | |
43 | struct usb_anchor bulk_anchor; | |
44 | struct usb_anchor deferred; | |
45 | int tx_in_flight; | |
46 | /* protects tx_in_flight */ | |
47 | spinlock_t txlock; | |
48 | struct usb_endpoint_descriptor *bulk_tx_ep; | |
49 | struct usb_endpoint_descriptor *bulk_rx_ep; | |
50 | int suspend_count; | |
51 | struct nfcmrvl_private *priv; | |
52 | }; | |
53 | ||
54 | static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) | |
55 | { | |
56 | unsigned long flags; | |
57 | int rv; | |
58 | ||
59 | spin_lock_irqsave(&drv_data->txlock, flags); | |
60 | rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); | |
61 | if (!rv) | |
62 | drv_data->tx_in_flight++; | |
63 | spin_unlock_irqrestore(&drv_data->txlock, flags); | |
64 | ||
65 | return rv; | |
66 | } | |
67 | ||
68 | static void nfcmrvl_bulk_complete(struct urb *urb) | |
69 | { | |
70 | struct nfcmrvl_usb_drv_data *drv_data = urb->context; | |
e1bf80c2 | 71 | struct sk_buff *skb; |
f26e30cc AK |
72 | int err; |
73 | ||
e1bf80c2 | 74 | dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n", |
f26e30cc AK |
75 | urb, urb->status, urb->actual_length); |
76 | ||
77 | if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) | |
78 | return; | |
79 | ||
80 | if (!urb->status) { | |
e1bf80c2 VC |
81 | skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, |
82 | GFP_ATOMIC); | |
83 | if (!skb) { | |
84 | nfc_err(&drv_data->udev->dev, "failed to alloc mem\n"); | |
85 | } else { | |
59ae1d12 JB |
86 | skb_put_data(skb, urb->transfer_buffer, |
87 | urb->actual_length); | |
e1bf80c2 VC |
88 | if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0) |
89 | nfc_err(&drv_data->udev->dev, | |
90 | "corrupted Rx packet\n"); | |
91 | } | |
f26e30cc AK |
92 | } |
93 | ||
94 | if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) | |
95 | return; | |
96 | ||
97 | usb_anchor_urb(urb, &drv_data->bulk_anchor); | |
98 | usb_mark_last_busy(drv_data->udev); | |
99 | ||
100 | err = usb_submit_urb(urb, GFP_ATOMIC); | |
101 | if (err) { | |
102 | /* -EPERM: urb is being killed; | |
103 | * -ENODEV: device got disconnected | |
104 | */ | |
105 | if (err != -EPERM && err != -ENODEV) | |
106 | nfc_err(&drv_data->udev->dev, | |
3590ebc0 | 107 | "urb %p failed to resubmit (%d)\n", urb, -err); |
f26e30cc AK |
108 | usb_unanchor_urb(urb); |
109 | } | |
110 | } | |
111 | ||
112 | static int | |
113 | nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) | |
114 | { | |
115 | struct urb *urb; | |
116 | unsigned char *buf; | |
117 | unsigned int pipe; | |
118 | int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE; | |
119 | ||
120 | if (!drv_data->bulk_rx_ep) | |
121 | return -ENODEV; | |
122 | ||
123 | urb = usb_alloc_urb(0, mem_flags); | |
124 | if (!urb) | |
125 | return -ENOMEM; | |
126 | ||
127 | buf = kmalloc(size, mem_flags); | |
128 | if (!buf) { | |
129 | usb_free_urb(urb); | |
130 | return -ENOMEM; | |
131 | } | |
132 | ||
133 | pipe = usb_rcvbulkpipe(drv_data->udev, | |
134 | drv_data->bulk_rx_ep->bEndpointAddress); | |
135 | ||
136 | usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, | |
137 | nfcmrvl_bulk_complete, drv_data); | |
138 | ||
139 | urb->transfer_flags |= URB_FREE_BUFFER; | |
140 | ||
141 | usb_mark_last_busy(drv_data->udev); | |
142 | usb_anchor_urb(urb, &drv_data->bulk_anchor); | |
143 | ||
144 | err = usb_submit_urb(urb, mem_flags); | |
145 | if (err) { | |
146 | if (err != -EPERM && err != -ENODEV) | |
147 | nfc_err(&drv_data->udev->dev, | |
3590ebc0 | 148 | "urb %p submission failed (%d)\n", urb, -err); |
f26e30cc AK |
149 | usb_unanchor_urb(urb); |
150 | } | |
151 | ||
152 | usb_free_urb(urb); | |
153 | ||
154 | return err; | |
155 | } | |
156 | ||
157 | static void nfcmrvl_tx_complete(struct urb *urb) | |
158 | { | |
159 | struct sk_buff *skb = urb->context; | |
160 | struct nci_dev *ndev = (struct nci_dev *)skb->dev; | |
161 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); | |
162 | struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; | |
24b2068e | 163 | unsigned long flags; |
f26e30cc | 164 | |
3590ebc0 | 165 | nfc_info(priv->dev, "urb %p status %d count %d\n", |
f26e30cc AK |
166 | urb, urb->status, urb->actual_length); |
167 | ||
24b2068e | 168 | spin_lock_irqsave(&drv_data->txlock, flags); |
f26e30cc | 169 | drv_data->tx_in_flight--; |
24b2068e | 170 | spin_unlock_irqrestore(&drv_data->txlock, flags); |
f26e30cc AK |
171 | |
172 | kfree(urb->setup_packet); | |
173 | kfree_skb(skb); | |
174 | } | |
175 | ||
176 | static 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 | ||
197 | failed: | |
198 | usb_autopm_put_interface(drv_data->intf); | |
199 | return err; | |
200 | } | |
201 | ||
202 | static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) | |
203 | { | |
204 | usb_kill_anchored_urbs(&drv_data->bulk_anchor); | |
205 | } | |
206 | ||
207 | static 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 | ||
225 | failed: | |
226 | usb_scuttle_anchored_urbs(&drv_data->deferred); | |
227 | return 0; | |
228 | } | |
229 | ||
230 | static 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, | |
3590ebc0 | 265 | "urb %p submission failed (%d)\n", urb, -err); |
f26e30cc AK |
266 | kfree(urb->setup_packet); |
267 | usb_unanchor_urb(urb); | |
268 | } else { | |
269 | usb_mark_last_busy(drv_data->udev); | |
270 | } | |
271 | ||
272 | done: | |
273 | usb_free_urb(urb); | |
274 | return err; | |
275 | } | |
276 | ||
277 | static 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 | ||
283 | static 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 | ||
296 | static 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); | |
dc14bdef VC |
304 | struct nfcmrvl_platform_data config; |
305 | ||
306 | /* No configuration for USB */ | |
307 | memset(&config, 0, sizeof(config)); | |
f26e30cc | 308 | |
3590ebc0 | 309 | nfc_info(&udev->dev, "intf %p id %p\n", intf, id); |
f26e30cc AK |
310 | |
311 | drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); | |
312 | if (!drv_data) | |
313 | return -ENOMEM; | |
314 | ||
315 | for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { | |
316 | ep_desc = &intf->cur_altsetting->endpoint[i].desc; | |
317 | ||
318 | if (!drv_data->bulk_tx_ep && | |
319 | usb_endpoint_is_bulk_out(ep_desc)) { | |
320 | drv_data->bulk_tx_ep = ep_desc; | |
321 | continue; | |
322 | } | |
323 | ||
324 | if (!drv_data->bulk_rx_ep && | |
325 | usb_endpoint_is_bulk_in(ep_desc)) { | |
326 | drv_data->bulk_rx_ep = ep_desc; | |
327 | continue; | |
328 | } | |
329 | } | |
330 | ||
331 | if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) | |
332 | return -ENODEV; | |
333 | ||
334 | drv_data->udev = udev; | |
335 | drv_data->intf = intf; | |
336 | ||
337 | INIT_WORK(&drv_data->waker, nfcmrvl_waker); | |
338 | spin_lock_init(&drv_data->txlock); | |
339 | ||
340 | init_usb_anchor(&drv_data->tx_anchor); | |
341 | init_usb_anchor(&drv_data->bulk_anchor); | |
342 | init_usb_anchor(&drv_data->deferred); | |
343 | ||
58d34aa6 | 344 | priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops, |
0d1ca88b | 345 | &intf->dev, &config); |
f26e30cc AK |
346 | if (IS_ERR(priv)) |
347 | return PTR_ERR(priv); | |
348 | ||
349 | drv_data->priv = priv; | |
3194c687 VC |
350 | drv_data->priv->support_fw_dnld = false; |
351 | ||
f26e30cc AK |
352 | usb_set_intfdata(intf, drv_data); |
353 | ||
354 | return 0; | |
355 | } | |
356 | ||
357 | static void nfcmrvl_disconnect(struct usb_interface *intf) | |
358 | { | |
359 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); | |
360 | ||
361 | if (!drv_data) | |
362 | return; | |
363 | ||
3590ebc0 | 364 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
f26e30cc AK |
365 | |
366 | nfcmrvl_nci_unregister_dev(drv_data->priv); | |
367 | ||
368 | usb_set_intfdata(drv_data->intf, NULL); | |
369 | } | |
370 | ||
371 | #ifdef CONFIG_PM | |
372 | static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) | |
373 | { | |
374 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); | |
375 | ||
3590ebc0 | 376 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
f26e30cc AK |
377 | |
378 | if (drv_data->suspend_count++) | |
379 | return 0; | |
380 | ||
381 | spin_lock_irq(&drv_data->txlock); | |
382 | if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { | |
383 | set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); | |
384 | spin_unlock_irq(&drv_data->txlock); | |
385 | } else { | |
386 | spin_unlock_irq(&drv_data->txlock); | |
387 | drv_data->suspend_count--; | |
388 | return -EBUSY; | |
389 | } | |
390 | ||
391 | nfcmrvl_usb_stop_traffic(drv_data); | |
392 | usb_kill_anchored_urbs(&drv_data->tx_anchor); | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
397 | static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) | |
398 | { | |
399 | struct urb *urb; | |
400 | int err; | |
401 | ||
402 | while ((urb = usb_get_from_anchor(&drv_data->deferred))) { | |
403 | err = usb_submit_urb(urb, GFP_ATOMIC); | |
404 | if (err) | |
405 | break; | |
406 | ||
407 | drv_data->tx_in_flight++; | |
408 | } | |
409 | usb_scuttle_anchored_urbs(&drv_data->deferred); | |
410 | } | |
411 | ||
412 | static int nfcmrvl_resume(struct usb_interface *intf) | |
413 | { | |
414 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); | |
415 | int err = 0; | |
416 | ||
3590ebc0 | 417 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
f26e30cc AK |
418 | |
419 | if (--drv_data->suspend_count) | |
420 | return 0; | |
421 | ||
422 | if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) | |
423 | goto done; | |
424 | ||
425 | if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { | |
426 | err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); | |
427 | if (err) { | |
428 | clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); | |
429 | goto failed; | |
430 | } | |
431 | ||
432 | nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); | |
433 | } | |
434 | ||
435 | spin_lock_irq(&drv_data->txlock); | |
436 | nfcmrvl_play_deferred(drv_data); | |
437 | clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); | |
438 | spin_unlock_irq(&drv_data->txlock); | |
439 | ||
440 | return 0; | |
441 | ||
442 | failed: | |
443 | usb_scuttle_anchored_urbs(&drv_data->deferred); | |
444 | done: | |
445 | spin_lock_irq(&drv_data->txlock); | |
446 | clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); | |
447 | spin_unlock_irq(&drv_data->txlock); | |
448 | ||
449 | return err; | |
450 | } | |
451 | #endif | |
452 | ||
453 | static struct usb_driver nfcmrvl_usb_driver = { | |
454 | .name = "nfcmrvl", | |
455 | .probe = nfcmrvl_probe, | |
456 | .disconnect = nfcmrvl_disconnect, | |
457 | #ifdef CONFIG_PM | |
458 | .suspend = nfcmrvl_suspend, | |
459 | .resume = nfcmrvl_resume, | |
460 | .reset_resume = nfcmrvl_resume, | |
461 | #endif | |
462 | .id_table = nfcmrvl_table, | |
463 | .supports_autosuspend = 1, | |
464 | .disable_hub_initiated_lpm = 1, | |
465 | .soft_unbind = 1, | |
466 | }; | |
467 | module_usb_driver(nfcmrvl_usb_driver); | |
468 | ||
469 | MODULE_AUTHOR("Marvell International Ltd."); | |
fb101c0e | 470 | MODULE_DESCRIPTION("Marvell NFC-over-USB driver"); |
f26e30cc | 471 | MODULE_LICENSE("GPL v2"); |