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