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; 72f26e30ccSAmitkumar Karwar int err; 73f26e30ccSAmitkumar Karwar 74f26e30ccSAmitkumar Karwar dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d", 75f26e30ccSAmitkumar Karwar urb, urb->status, urb->actual_length); 76f26e30ccSAmitkumar Karwar 77f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) 78f26e30ccSAmitkumar Karwar return; 79f26e30ccSAmitkumar Karwar 80f26e30ccSAmitkumar Karwar if (!urb->status) { 81f26e30ccSAmitkumar Karwar if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer, 82f26e30ccSAmitkumar Karwar urb->actual_length) < 0) 833590ebc0SJoe Perches nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n"); 84f26e30ccSAmitkumar Karwar } 85f26e30ccSAmitkumar Karwar 86f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) 87f26e30ccSAmitkumar Karwar return; 88f26e30ccSAmitkumar Karwar 89f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->bulk_anchor); 90f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 91f26e30ccSAmitkumar Karwar 92f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 93f26e30ccSAmitkumar Karwar if (err) { 94f26e30ccSAmitkumar Karwar /* -EPERM: urb is being killed; 95f26e30ccSAmitkumar Karwar * -ENODEV: device got disconnected 96f26e30ccSAmitkumar Karwar */ 97f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 98f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 993590ebc0SJoe Perches "urb %p failed to resubmit (%d)\n", urb, -err); 100f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 101f26e30ccSAmitkumar Karwar } 102f26e30ccSAmitkumar Karwar } 103f26e30ccSAmitkumar Karwar 104f26e30ccSAmitkumar Karwar static int 105f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) 106f26e30ccSAmitkumar Karwar { 107f26e30ccSAmitkumar Karwar struct urb *urb; 108f26e30ccSAmitkumar Karwar unsigned char *buf; 109f26e30ccSAmitkumar Karwar unsigned int pipe; 110f26e30ccSAmitkumar Karwar int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE; 111f26e30ccSAmitkumar Karwar 112f26e30ccSAmitkumar Karwar if (!drv_data->bulk_rx_ep) 113f26e30ccSAmitkumar Karwar return -ENODEV; 114f26e30ccSAmitkumar Karwar 115f26e30ccSAmitkumar Karwar urb = usb_alloc_urb(0, mem_flags); 116f26e30ccSAmitkumar Karwar if (!urb) 117f26e30ccSAmitkumar Karwar return -ENOMEM; 118f26e30ccSAmitkumar Karwar 119f26e30ccSAmitkumar Karwar buf = kmalloc(size, mem_flags); 120f26e30ccSAmitkumar Karwar if (!buf) { 121f26e30ccSAmitkumar Karwar usb_free_urb(urb); 122f26e30ccSAmitkumar Karwar return -ENOMEM; 123f26e30ccSAmitkumar Karwar } 124f26e30ccSAmitkumar Karwar 125f26e30ccSAmitkumar Karwar pipe = usb_rcvbulkpipe(drv_data->udev, 126f26e30ccSAmitkumar Karwar drv_data->bulk_rx_ep->bEndpointAddress); 127f26e30ccSAmitkumar Karwar 128f26e30ccSAmitkumar Karwar usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, 129f26e30ccSAmitkumar Karwar nfcmrvl_bulk_complete, drv_data); 130f26e30ccSAmitkumar Karwar 131f26e30ccSAmitkumar Karwar urb->transfer_flags |= URB_FREE_BUFFER; 132f26e30ccSAmitkumar Karwar 133f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 134f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->bulk_anchor); 135f26e30ccSAmitkumar Karwar 136f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, mem_flags); 137f26e30ccSAmitkumar Karwar if (err) { 138f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 139f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 1403590ebc0SJoe Perches "urb %p submission failed (%d)\n", urb, -err); 141f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 142f26e30ccSAmitkumar Karwar } 143f26e30ccSAmitkumar Karwar 144f26e30ccSAmitkumar Karwar usb_free_urb(urb); 145f26e30ccSAmitkumar Karwar 146f26e30ccSAmitkumar Karwar return err; 147f26e30ccSAmitkumar Karwar } 148f26e30ccSAmitkumar Karwar 149f26e30ccSAmitkumar Karwar static void nfcmrvl_tx_complete(struct urb *urb) 150f26e30ccSAmitkumar Karwar { 151f26e30ccSAmitkumar Karwar struct sk_buff *skb = urb->context; 152f26e30ccSAmitkumar Karwar struct nci_dev *ndev = (struct nci_dev *)skb->dev; 153f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv = nci_get_drvdata(ndev); 154f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 155f26e30ccSAmitkumar Karwar 1563590ebc0SJoe Perches nfc_info(priv->dev, "urb %p status %d count %d\n", 157f26e30ccSAmitkumar Karwar urb, urb->status, urb->actual_length); 158f26e30ccSAmitkumar Karwar 159f26e30ccSAmitkumar Karwar spin_lock(&drv_data->txlock); 160f26e30ccSAmitkumar Karwar drv_data->tx_in_flight--; 161f26e30ccSAmitkumar Karwar spin_unlock(&drv_data->txlock); 162f26e30ccSAmitkumar Karwar 163f26e30ccSAmitkumar Karwar kfree(urb->setup_packet); 164f26e30ccSAmitkumar Karwar kfree_skb(skb); 165f26e30ccSAmitkumar Karwar } 166f26e30ccSAmitkumar Karwar 167f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) 168f26e30ccSAmitkumar Karwar { 169f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 170f26e30ccSAmitkumar Karwar int err; 171f26e30ccSAmitkumar Karwar 172f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 173f26e30ccSAmitkumar Karwar if (err) 174f26e30ccSAmitkumar Karwar return err; 175f26e30ccSAmitkumar Karwar 176f26e30ccSAmitkumar Karwar drv_data->intf->needs_remote_wakeup = 1; 177f26e30ccSAmitkumar Karwar 178f26e30ccSAmitkumar Karwar err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); 179f26e30ccSAmitkumar Karwar if (err) 180f26e30ccSAmitkumar Karwar goto failed; 181f26e30ccSAmitkumar Karwar 182f26e30ccSAmitkumar Karwar set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 183f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); 184f26e30ccSAmitkumar Karwar 185f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 186f26e30ccSAmitkumar Karwar return 0; 187f26e30ccSAmitkumar Karwar 188f26e30ccSAmitkumar Karwar failed: 189f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 190f26e30ccSAmitkumar Karwar return err; 191f26e30ccSAmitkumar Karwar } 192f26e30ccSAmitkumar Karwar 193f26e30ccSAmitkumar Karwar static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) 194f26e30ccSAmitkumar Karwar { 195f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->bulk_anchor); 196f26e30ccSAmitkumar Karwar } 197f26e30ccSAmitkumar Karwar 198f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv) 199f26e30ccSAmitkumar Karwar { 200f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 201f26e30ccSAmitkumar Karwar int err; 202f26e30ccSAmitkumar Karwar 203f26e30ccSAmitkumar Karwar cancel_work_sync(&drv_data->waker); 204f26e30ccSAmitkumar Karwar 205f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 206f26e30ccSAmitkumar Karwar 207f26e30ccSAmitkumar Karwar nfcmrvl_usb_stop_traffic(drv_data); 208f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->tx_anchor); 209f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 210f26e30ccSAmitkumar Karwar if (err) 211f26e30ccSAmitkumar Karwar goto failed; 212f26e30ccSAmitkumar Karwar 213f26e30ccSAmitkumar Karwar drv_data->intf->needs_remote_wakeup = 0; 214f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 215f26e30ccSAmitkumar Karwar 216f26e30ccSAmitkumar Karwar failed: 217f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 218f26e30ccSAmitkumar Karwar return 0; 219f26e30ccSAmitkumar Karwar } 220f26e30ccSAmitkumar Karwar 221f26e30ccSAmitkumar Karwar static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, 222f26e30ccSAmitkumar Karwar struct sk_buff *skb) 223f26e30ccSAmitkumar Karwar { 224f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 225f26e30ccSAmitkumar Karwar struct urb *urb; 226f26e30ccSAmitkumar Karwar unsigned int pipe; 227f26e30ccSAmitkumar Karwar int err; 228f26e30ccSAmitkumar Karwar 229f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep) 230f26e30ccSAmitkumar Karwar return -ENODEV; 231f26e30ccSAmitkumar Karwar 232f26e30ccSAmitkumar Karwar urb = usb_alloc_urb(0, GFP_ATOMIC); 233f26e30ccSAmitkumar Karwar if (!urb) 234f26e30ccSAmitkumar Karwar return -ENOMEM; 235f26e30ccSAmitkumar Karwar 236f26e30ccSAmitkumar Karwar pipe = usb_sndbulkpipe(drv_data->udev, 237f26e30ccSAmitkumar Karwar drv_data->bulk_tx_ep->bEndpointAddress); 238f26e30ccSAmitkumar Karwar 239f26e30ccSAmitkumar Karwar usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len, 240f26e30ccSAmitkumar Karwar nfcmrvl_tx_complete, skb); 241f26e30ccSAmitkumar Karwar 242f26e30ccSAmitkumar Karwar err = nfcmrvl_inc_tx(drv_data); 243f26e30ccSAmitkumar Karwar if (err) { 244f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->deferred); 245f26e30ccSAmitkumar Karwar schedule_work(&drv_data->waker); 246f26e30ccSAmitkumar Karwar err = 0; 247f26e30ccSAmitkumar Karwar goto done; 248f26e30ccSAmitkumar Karwar } 249f26e30ccSAmitkumar Karwar 250f26e30ccSAmitkumar Karwar usb_anchor_urb(urb, &drv_data->tx_anchor); 251f26e30ccSAmitkumar Karwar 252f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 253f26e30ccSAmitkumar Karwar if (err) { 254f26e30ccSAmitkumar Karwar if (err != -EPERM && err != -ENODEV) 255f26e30ccSAmitkumar Karwar nfc_err(&drv_data->udev->dev, 2563590ebc0SJoe Perches "urb %p submission failed (%d)\n", urb, -err); 257f26e30ccSAmitkumar Karwar kfree(urb->setup_packet); 258f26e30ccSAmitkumar Karwar usb_unanchor_urb(urb); 259f26e30ccSAmitkumar Karwar } else { 260f26e30ccSAmitkumar Karwar usb_mark_last_busy(drv_data->udev); 261f26e30ccSAmitkumar Karwar } 262f26e30ccSAmitkumar Karwar 263f26e30ccSAmitkumar Karwar done: 264f26e30ccSAmitkumar Karwar usb_free_urb(urb); 265f26e30ccSAmitkumar Karwar return err; 266f26e30ccSAmitkumar Karwar } 267f26e30ccSAmitkumar Karwar 268f26e30ccSAmitkumar Karwar static struct nfcmrvl_if_ops usb_ops = { 269f26e30ccSAmitkumar Karwar .nci_open = nfcmrvl_usb_nci_open, 270f26e30ccSAmitkumar Karwar .nci_close = nfcmrvl_usb_nci_close, 271f26e30ccSAmitkumar Karwar .nci_send = nfcmrvl_usb_nci_send, 272f26e30ccSAmitkumar Karwar }; 273f26e30ccSAmitkumar Karwar 274f26e30ccSAmitkumar Karwar static void nfcmrvl_waker(struct work_struct *work) 275f26e30ccSAmitkumar Karwar { 276f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = 277f26e30ccSAmitkumar Karwar container_of(work, struct nfcmrvl_usb_drv_data, waker); 278f26e30ccSAmitkumar Karwar int err; 279f26e30ccSAmitkumar Karwar 280f26e30ccSAmitkumar Karwar err = usb_autopm_get_interface(drv_data->intf); 281f26e30ccSAmitkumar Karwar if (err) 282f26e30ccSAmitkumar Karwar return; 283f26e30ccSAmitkumar Karwar 284f26e30ccSAmitkumar Karwar usb_autopm_put_interface(drv_data->intf); 285f26e30ccSAmitkumar Karwar } 286f26e30ccSAmitkumar Karwar 287f26e30ccSAmitkumar Karwar static int nfcmrvl_probe(struct usb_interface *intf, 288f26e30ccSAmitkumar Karwar const struct usb_device_id *id) 289f26e30ccSAmitkumar Karwar { 290f26e30ccSAmitkumar Karwar struct usb_endpoint_descriptor *ep_desc; 291f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data; 292f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv; 293f26e30ccSAmitkumar Karwar int i; 294f26e30ccSAmitkumar Karwar struct usb_device *udev = interface_to_usbdev(intf); 295f26e30ccSAmitkumar Karwar 2963590ebc0SJoe Perches nfc_info(&udev->dev, "intf %p id %p\n", intf, id); 297f26e30ccSAmitkumar Karwar 298f26e30ccSAmitkumar Karwar drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); 299f26e30ccSAmitkumar Karwar if (!drv_data) 300f26e30ccSAmitkumar Karwar return -ENOMEM; 301f26e30ccSAmitkumar Karwar 302f26e30ccSAmitkumar Karwar for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { 303f26e30ccSAmitkumar Karwar ep_desc = &intf->cur_altsetting->endpoint[i].desc; 304f26e30ccSAmitkumar Karwar 305f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep && 306f26e30ccSAmitkumar Karwar usb_endpoint_is_bulk_out(ep_desc)) { 307f26e30ccSAmitkumar Karwar drv_data->bulk_tx_ep = ep_desc; 308f26e30ccSAmitkumar Karwar continue; 309f26e30ccSAmitkumar Karwar } 310f26e30ccSAmitkumar Karwar 311f26e30ccSAmitkumar Karwar if (!drv_data->bulk_rx_ep && 312f26e30ccSAmitkumar Karwar usb_endpoint_is_bulk_in(ep_desc)) { 313f26e30ccSAmitkumar Karwar drv_data->bulk_rx_ep = ep_desc; 314f26e30ccSAmitkumar Karwar continue; 315f26e30ccSAmitkumar Karwar } 316f26e30ccSAmitkumar Karwar } 317f26e30ccSAmitkumar Karwar 318f26e30ccSAmitkumar Karwar if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) 319f26e30ccSAmitkumar Karwar return -ENODEV; 320f26e30ccSAmitkumar Karwar 321f26e30ccSAmitkumar Karwar drv_data->udev = udev; 322f26e30ccSAmitkumar Karwar drv_data->intf = intf; 323f26e30ccSAmitkumar Karwar 324f26e30ccSAmitkumar Karwar INIT_WORK(&drv_data->waker, nfcmrvl_waker); 325f26e30ccSAmitkumar Karwar spin_lock_init(&drv_data->txlock); 326f26e30ccSAmitkumar Karwar 327f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->tx_anchor); 328f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->bulk_anchor); 329f26e30ccSAmitkumar Karwar init_usb_anchor(&drv_data->deferred); 330f26e30ccSAmitkumar Karwar 331f26e30ccSAmitkumar Karwar priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops, 332f26e30ccSAmitkumar Karwar &drv_data->udev->dev); 333f26e30ccSAmitkumar Karwar if (IS_ERR(priv)) 334f26e30ccSAmitkumar Karwar return PTR_ERR(priv); 335f26e30ccSAmitkumar Karwar 336f26e30ccSAmitkumar Karwar drv_data->priv = priv; 337f26e30ccSAmitkumar Karwar priv->dev = &drv_data->udev->dev; 338f26e30ccSAmitkumar Karwar 339f26e30ccSAmitkumar Karwar usb_set_intfdata(intf, drv_data); 340f26e30ccSAmitkumar Karwar 341f26e30ccSAmitkumar Karwar return 0; 342f26e30ccSAmitkumar Karwar } 343f26e30ccSAmitkumar Karwar 344f26e30ccSAmitkumar Karwar static void nfcmrvl_disconnect(struct usb_interface *intf) 345f26e30ccSAmitkumar Karwar { 346f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 347f26e30ccSAmitkumar Karwar 348f26e30ccSAmitkumar Karwar if (!drv_data) 349f26e30ccSAmitkumar Karwar return; 350f26e30ccSAmitkumar Karwar 3513590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 352f26e30ccSAmitkumar Karwar 353f26e30ccSAmitkumar Karwar nfcmrvl_nci_unregister_dev(drv_data->priv); 354f26e30ccSAmitkumar Karwar 355f26e30ccSAmitkumar Karwar usb_set_intfdata(drv_data->intf, NULL); 356f26e30ccSAmitkumar Karwar } 357f26e30ccSAmitkumar Karwar 358f26e30ccSAmitkumar Karwar #ifdef CONFIG_PM 359f26e30ccSAmitkumar Karwar static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) 360f26e30ccSAmitkumar Karwar { 361f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 362f26e30ccSAmitkumar Karwar 3633590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 364f26e30ccSAmitkumar Karwar 365f26e30ccSAmitkumar Karwar if (drv_data->suspend_count++) 366f26e30ccSAmitkumar Karwar return 0; 367f26e30ccSAmitkumar Karwar 368f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 369f26e30ccSAmitkumar Karwar if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { 370f26e30ccSAmitkumar Karwar set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 371f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 372f26e30ccSAmitkumar Karwar } else { 373f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 374f26e30ccSAmitkumar Karwar drv_data->suspend_count--; 375f26e30ccSAmitkumar Karwar return -EBUSY; 376f26e30ccSAmitkumar Karwar } 377f26e30ccSAmitkumar Karwar 378f26e30ccSAmitkumar Karwar nfcmrvl_usb_stop_traffic(drv_data); 379f26e30ccSAmitkumar Karwar usb_kill_anchored_urbs(&drv_data->tx_anchor); 380f26e30ccSAmitkumar Karwar 381f26e30ccSAmitkumar Karwar return 0; 382f26e30ccSAmitkumar Karwar } 383f26e30ccSAmitkumar Karwar 384f26e30ccSAmitkumar Karwar static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) 385f26e30ccSAmitkumar Karwar { 386f26e30ccSAmitkumar Karwar struct urb *urb; 387f26e30ccSAmitkumar Karwar int err; 388f26e30ccSAmitkumar Karwar 389f26e30ccSAmitkumar Karwar while ((urb = usb_get_from_anchor(&drv_data->deferred))) { 390f26e30ccSAmitkumar Karwar err = usb_submit_urb(urb, GFP_ATOMIC); 391f26e30ccSAmitkumar Karwar if (err) 392f26e30ccSAmitkumar Karwar break; 393f26e30ccSAmitkumar Karwar 394f26e30ccSAmitkumar Karwar drv_data->tx_in_flight++; 395f26e30ccSAmitkumar Karwar } 396f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 397f26e30ccSAmitkumar Karwar } 398f26e30ccSAmitkumar Karwar 399f26e30ccSAmitkumar Karwar static int nfcmrvl_resume(struct usb_interface *intf) 400f26e30ccSAmitkumar Karwar { 401f26e30ccSAmitkumar Karwar struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 402f26e30ccSAmitkumar Karwar int err = 0; 403f26e30ccSAmitkumar Karwar 4043590ebc0SJoe Perches nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 405f26e30ccSAmitkumar Karwar 406f26e30ccSAmitkumar Karwar if (--drv_data->suspend_count) 407f26e30ccSAmitkumar Karwar return 0; 408f26e30ccSAmitkumar Karwar 409f26e30ccSAmitkumar Karwar if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) 410f26e30ccSAmitkumar Karwar goto done; 411f26e30ccSAmitkumar Karwar 412f26e30ccSAmitkumar Karwar if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { 413f26e30ccSAmitkumar Karwar err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); 414f26e30ccSAmitkumar Karwar if (err) { 415f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 416f26e30ccSAmitkumar Karwar goto failed; 417f26e30ccSAmitkumar Karwar } 418f26e30ccSAmitkumar Karwar 419f26e30ccSAmitkumar Karwar nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); 420f26e30ccSAmitkumar Karwar } 421f26e30ccSAmitkumar Karwar 422f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 423f26e30ccSAmitkumar Karwar nfcmrvl_play_deferred(drv_data); 424f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 425f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 426f26e30ccSAmitkumar Karwar 427f26e30ccSAmitkumar Karwar return 0; 428f26e30ccSAmitkumar Karwar 429f26e30ccSAmitkumar Karwar failed: 430f26e30ccSAmitkumar Karwar usb_scuttle_anchored_urbs(&drv_data->deferred); 431f26e30ccSAmitkumar Karwar done: 432f26e30ccSAmitkumar Karwar spin_lock_irq(&drv_data->txlock); 433f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 434f26e30ccSAmitkumar Karwar spin_unlock_irq(&drv_data->txlock); 435f26e30ccSAmitkumar Karwar 436f26e30ccSAmitkumar Karwar return err; 437f26e30ccSAmitkumar Karwar } 438f26e30ccSAmitkumar Karwar #endif 439f26e30ccSAmitkumar Karwar 440f26e30ccSAmitkumar Karwar static struct usb_driver nfcmrvl_usb_driver = { 441f26e30ccSAmitkumar Karwar .name = "nfcmrvl", 442f26e30ccSAmitkumar Karwar .probe = nfcmrvl_probe, 443f26e30ccSAmitkumar Karwar .disconnect = nfcmrvl_disconnect, 444f26e30ccSAmitkumar Karwar #ifdef CONFIG_PM 445f26e30ccSAmitkumar Karwar .suspend = nfcmrvl_suspend, 446f26e30ccSAmitkumar Karwar .resume = nfcmrvl_resume, 447f26e30ccSAmitkumar Karwar .reset_resume = nfcmrvl_resume, 448f26e30ccSAmitkumar Karwar #endif 449f26e30ccSAmitkumar Karwar .id_table = nfcmrvl_table, 450f26e30ccSAmitkumar Karwar .supports_autosuspend = 1, 451f26e30ccSAmitkumar Karwar .disable_hub_initiated_lpm = 1, 452f26e30ccSAmitkumar Karwar .soft_unbind = 1, 453f26e30ccSAmitkumar Karwar }; 454f26e30ccSAmitkumar Karwar module_usb_driver(nfcmrvl_usb_driver); 455f26e30ccSAmitkumar Karwar 456f26e30ccSAmitkumar Karwar MODULE_AUTHOR("Marvell International Ltd."); 457f26e30ccSAmitkumar Karwar MODULE_DESCRIPTION("Marvell NFC-over-USB driver ver " VERSION); 458f26e30ccSAmitkumar Karwar MODULE_VERSION(VERSION); 459f26e30ccSAmitkumar Karwar MODULE_LICENSE("GPL v2"); 460