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 { | |
86 | memcpy(skb_put(skb, urb->actual_length), | |
87 | urb->transfer_buffer, urb->actual_length); | |
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; | |
163 | ||
3590ebc0 | 164 | nfc_info(priv->dev, "urb %p status %d count %d\n", |
f26e30cc AK |
165 | urb, urb->status, urb->actual_length); |
166 | ||
167 | spin_lock(&drv_data->txlock); | |
168 | drv_data->tx_in_flight--; | |
169 | spin_unlock(&drv_data->txlock); | |
170 | ||
171 | kfree(urb->setup_packet); | |
172 | kfree_skb(skb); | |
173 | } | |
174 | ||
175 | static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) | |
176 | { | |
177 | struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; | |
178 | int err; | |
179 | ||
180 | err = usb_autopm_get_interface(drv_data->intf); | |
181 | if (err) | |
182 | return err; | |
183 | ||
184 | drv_data->intf->needs_remote_wakeup = 1; | |
185 | ||
186 | err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); | |
187 | if (err) | |
188 | goto failed; | |
189 | ||
190 | set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); | |
191 | nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); | |
192 | ||
193 | usb_autopm_put_interface(drv_data->intf); | |
194 | return 0; | |
195 | ||
196 | failed: | |
197 | usb_autopm_put_interface(drv_data->intf); | |
198 | return err; | |
199 | } | |
200 | ||
201 | static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) | |
202 | { | |
203 | usb_kill_anchored_urbs(&drv_data->bulk_anchor); | |
204 | } | |
205 | ||
206 | static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv) | |
207 | { | |
208 | struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; | |
209 | int err; | |
210 | ||
211 | cancel_work_sync(&drv_data->waker); | |
212 | ||
213 | clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); | |
214 | ||
215 | nfcmrvl_usb_stop_traffic(drv_data); | |
216 | usb_kill_anchored_urbs(&drv_data->tx_anchor); | |
217 | err = usb_autopm_get_interface(drv_data->intf); | |
218 | if (err) | |
219 | goto failed; | |
220 | ||
221 | drv_data->intf->needs_remote_wakeup = 0; | |
222 | usb_autopm_put_interface(drv_data->intf); | |
223 | ||
224 | failed: | |
225 | usb_scuttle_anchored_urbs(&drv_data->deferred); | |
226 | return 0; | |
227 | } | |
228 | ||
229 | static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, | |
230 | struct sk_buff *skb) | |
231 | { | |
232 | struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; | |
233 | struct urb *urb; | |
234 | unsigned int pipe; | |
235 | int err; | |
236 | ||
237 | if (!drv_data->bulk_tx_ep) | |
238 | return -ENODEV; | |
239 | ||
240 | urb = usb_alloc_urb(0, GFP_ATOMIC); | |
241 | if (!urb) | |
242 | return -ENOMEM; | |
243 | ||
244 | pipe = usb_sndbulkpipe(drv_data->udev, | |
245 | drv_data->bulk_tx_ep->bEndpointAddress); | |
246 | ||
247 | usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len, | |
248 | nfcmrvl_tx_complete, skb); | |
249 | ||
250 | err = nfcmrvl_inc_tx(drv_data); | |
251 | if (err) { | |
252 | usb_anchor_urb(urb, &drv_data->deferred); | |
253 | schedule_work(&drv_data->waker); | |
254 | err = 0; | |
255 | goto done; | |
256 | } | |
257 | ||
258 | usb_anchor_urb(urb, &drv_data->tx_anchor); | |
259 | ||
260 | err = usb_submit_urb(urb, GFP_ATOMIC); | |
261 | if (err) { | |
262 | if (err != -EPERM && err != -ENODEV) | |
263 | nfc_err(&drv_data->udev->dev, | |
3590ebc0 | 264 | "urb %p submission failed (%d)\n", urb, -err); |
f26e30cc AK |
265 | kfree(urb->setup_packet); |
266 | usb_unanchor_urb(urb); | |
267 | } else { | |
268 | usb_mark_last_busy(drv_data->udev); | |
269 | } | |
270 | ||
271 | done: | |
272 | usb_free_urb(urb); | |
273 | return err; | |
274 | } | |
275 | ||
276 | static struct nfcmrvl_if_ops usb_ops = { | |
277 | .nci_open = nfcmrvl_usb_nci_open, | |
278 | .nci_close = nfcmrvl_usb_nci_close, | |
279 | .nci_send = nfcmrvl_usb_nci_send, | |
280 | }; | |
281 | ||
282 | static void nfcmrvl_waker(struct work_struct *work) | |
283 | { | |
284 | struct nfcmrvl_usb_drv_data *drv_data = | |
285 | container_of(work, struct nfcmrvl_usb_drv_data, waker); | |
286 | int err; | |
287 | ||
288 | err = usb_autopm_get_interface(drv_data->intf); | |
289 | if (err) | |
290 | return; | |
291 | ||
292 | usb_autopm_put_interface(drv_data->intf); | |
293 | } | |
294 | ||
295 | static int nfcmrvl_probe(struct usb_interface *intf, | |
296 | const struct usb_device_id *id) | |
297 | { | |
298 | struct usb_endpoint_descriptor *ep_desc; | |
299 | struct nfcmrvl_usb_drv_data *drv_data; | |
300 | struct nfcmrvl_private *priv; | |
301 | int i; | |
302 | struct usb_device *udev = interface_to_usbdev(intf); | |
dc14bdef VC |
303 | struct nfcmrvl_platform_data config; |
304 | ||
305 | /* No configuration for USB */ | |
306 | memset(&config, 0, sizeof(config)); | |
f26e30cc | 307 | |
3590ebc0 | 308 | nfc_info(&udev->dev, "intf %p id %p\n", intf, id); |
f26e30cc AK |
309 | |
310 | drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); | |
311 | if (!drv_data) | |
312 | return -ENOMEM; | |
313 | ||
314 | for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { | |
315 | ep_desc = &intf->cur_altsetting->endpoint[i].desc; | |
316 | ||
317 | if (!drv_data->bulk_tx_ep && | |
318 | usb_endpoint_is_bulk_out(ep_desc)) { | |
319 | drv_data->bulk_tx_ep = ep_desc; | |
320 | continue; | |
321 | } | |
322 | ||
323 | if (!drv_data->bulk_rx_ep && | |
324 | usb_endpoint_is_bulk_in(ep_desc)) { | |
325 | drv_data->bulk_rx_ep = ep_desc; | |
326 | continue; | |
327 | } | |
328 | } | |
329 | ||
330 | if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) | |
331 | return -ENODEV; | |
332 | ||
333 | drv_data->udev = udev; | |
334 | drv_data->intf = intf; | |
335 | ||
336 | INIT_WORK(&drv_data->waker, nfcmrvl_waker); | |
337 | spin_lock_init(&drv_data->txlock); | |
338 | ||
339 | init_usb_anchor(&drv_data->tx_anchor); | |
340 | init_usb_anchor(&drv_data->bulk_anchor); | |
341 | init_usb_anchor(&drv_data->deferred); | |
342 | ||
343 | priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops, | |
dc14bdef | 344 | &drv_data->udev->dev, &config); |
f26e30cc AK |
345 | if (IS_ERR(priv)) |
346 | return PTR_ERR(priv); | |
347 | ||
348 | drv_data->priv = priv; | |
43465b7a | 349 | drv_data->priv->phy = NFCMRVL_PHY_USB; |
3194c687 VC |
350 | drv_data->priv->support_fw_dnld = false; |
351 | ||
f26e30cc AK |
352 | priv->dev = &drv_data->udev->dev; |
353 | ||
354 | usb_set_intfdata(intf, drv_data); | |
355 | ||
356 | return 0; | |
357 | } | |
358 | ||
359 | static void nfcmrvl_disconnect(struct usb_interface *intf) | |
360 | { | |
361 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); | |
362 | ||
363 | if (!drv_data) | |
364 | return; | |
365 | ||
3590ebc0 | 366 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
f26e30cc AK |
367 | |
368 | nfcmrvl_nci_unregister_dev(drv_data->priv); | |
369 | ||
370 | usb_set_intfdata(drv_data->intf, NULL); | |
371 | } | |
372 | ||
373 | #ifdef CONFIG_PM | |
374 | static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) | |
375 | { | |
376 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); | |
377 | ||
3590ebc0 | 378 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
f26e30cc AK |
379 | |
380 | if (drv_data->suspend_count++) | |
381 | return 0; | |
382 | ||
383 | spin_lock_irq(&drv_data->txlock); | |
384 | if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { | |
385 | set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); | |
386 | spin_unlock_irq(&drv_data->txlock); | |
387 | } else { | |
388 | spin_unlock_irq(&drv_data->txlock); | |
389 | drv_data->suspend_count--; | |
390 | return -EBUSY; | |
391 | } | |
392 | ||
393 | nfcmrvl_usb_stop_traffic(drv_data); | |
394 | usb_kill_anchored_urbs(&drv_data->tx_anchor); | |
395 | ||
396 | return 0; | |
397 | } | |
398 | ||
399 | static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) | |
400 | { | |
401 | struct urb *urb; | |
402 | int err; | |
403 | ||
404 | while ((urb = usb_get_from_anchor(&drv_data->deferred))) { | |
405 | err = usb_submit_urb(urb, GFP_ATOMIC); | |
406 | if (err) | |
407 | break; | |
408 | ||
409 | drv_data->tx_in_flight++; | |
410 | } | |
411 | usb_scuttle_anchored_urbs(&drv_data->deferred); | |
412 | } | |
413 | ||
414 | static int nfcmrvl_resume(struct usb_interface *intf) | |
415 | { | |
416 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); | |
417 | int err = 0; | |
418 | ||
3590ebc0 | 419 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
f26e30cc AK |
420 | |
421 | if (--drv_data->suspend_count) | |
422 | return 0; | |
423 | ||
424 | if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) | |
425 | goto done; | |
426 | ||
427 | if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { | |
428 | err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); | |
429 | if (err) { | |
430 | clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); | |
431 | goto failed; | |
432 | } | |
433 | ||
434 | nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); | |
435 | } | |
436 | ||
437 | spin_lock_irq(&drv_data->txlock); | |
438 | nfcmrvl_play_deferred(drv_data); | |
439 | clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); | |
440 | spin_unlock_irq(&drv_data->txlock); | |
441 | ||
442 | return 0; | |
443 | ||
444 | failed: | |
445 | usb_scuttle_anchored_urbs(&drv_data->deferred); | |
446 | done: | |
447 | spin_lock_irq(&drv_data->txlock); | |
448 | clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); | |
449 | spin_unlock_irq(&drv_data->txlock); | |
450 | ||
451 | return err; | |
452 | } | |
453 | #endif | |
454 | ||
455 | static struct usb_driver nfcmrvl_usb_driver = { | |
456 | .name = "nfcmrvl", | |
457 | .probe = nfcmrvl_probe, | |
458 | .disconnect = nfcmrvl_disconnect, | |
459 | #ifdef CONFIG_PM | |
460 | .suspend = nfcmrvl_suspend, | |
461 | .resume = nfcmrvl_resume, | |
462 | .reset_resume = nfcmrvl_resume, | |
463 | #endif | |
464 | .id_table = nfcmrvl_table, | |
465 | .supports_autosuspend = 1, | |
466 | .disable_hub_initiated_lpm = 1, | |
467 | .soft_unbind = 1, | |
468 | }; | |
469 | module_usb_driver(nfcmrvl_usb_driver); | |
470 | ||
471 | MODULE_AUTHOR("Marvell International Ltd."); | |
fb101c0e | 472 | MODULE_DESCRIPTION("Marvell NFC-over-USB driver"); |
f26e30cc | 473 | MODULE_LICENSE("GPL v2"); |