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