162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Marvell NFC-over-USB driver: USB interface related functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014, Marvell International Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/usb.h> 1062306a36Sopenharmony_ci#include <linux/nfc.h> 1162306a36Sopenharmony_ci#include <net/nfc/nci.h> 1262306a36Sopenharmony_ci#include <net/nfc/nci_core.h> 1362306a36Sopenharmony_ci#include "nfcmrvl.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic struct usb_device_id nfcmrvl_table[] = { 1662306a36Sopenharmony_ci { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046, 1762306a36Sopenharmony_ci USB_CLASS_VENDOR_SPEC, 4, 1) }, 1862306a36Sopenharmony_ci { } /* Terminating entry */ 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, nfcmrvl_table); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define NFCMRVL_USB_BULK_RUNNING 1 2462306a36Sopenharmony_ci#define NFCMRVL_USB_SUSPENDING 2 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct nfcmrvl_usb_drv_data { 2762306a36Sopenharmony_ci struct usb_device *udev; 2862306a36Sopenharmony_ci struct usb_interface *intf; 2962306a36Sopenharmony_ci unsigned long flags; 3062306a36Sopenharmony_ci struct work_struct waker; 3162306a36Sopenharmony_ci struct usb_anchor tx_anchor; 3262306a36Sopenharmony_ci struct usb_anchor bulk_anchor; 3362306a36Sopenharmony_ci struct usb_anchor deferred; 3462306a36Sopenharmony_ci int tx_in_flight; 3562306a36Sopenharmony_ci /* protects tx_in_flight */ 3662306a36Sopenharmony_ci spinlock_t txlock; 3762306a36Sopenharmony_ci struct usb_endpoint_descriptor *bulk_tx_ep; 3862306a36Sopenharmony_ci struct usb_endpoint_descriptor *bulk_rx_ep; 3962306a36Sopenharmony_ci int suspend_count; 4062306a36Sopenharmony_ci struct nfcmrvl_private *priv; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci unsigned long flags; 4662306a36Sopenharmony_ci int rv; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci spin_lock_irqsave(&drv_data->txlock, flags); 4962306a36Sopenharmony_ci rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 5062306a36Sopenharmony_ci if (!rv) 5162306a36Sopenharmony_ci drv_data->tx_in_flight++; 5262306a36Sopenharmony_ci spin_unlock_irqrestore(&drv_data->txlock, flags); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return rv; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void nfcmrvl_bulk_complete(struct urb *urb) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = urb->context; 6062306a36Sopenharmony_ci int err; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n", 6362306a36Sopenharmony_ci urb, urb->status, urb->actual_length); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!urb->status) { 6962306a36Sopenharmony_ci struct sk_buff *skb; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, 7262306a36Sopenharmony_ci GFP_ATOMIC); 7362306a36Sopenharmony_ci if (!skb) { 7462306a36Sopenharmony_ci nfc_err(&drv_data->udev->dev, "failed to alloc mem\n"); 7562306a36Sopenharmony_ci } else { 7662306a36Sopenharmony_ci skb_put_data(skb, urb->transfer_buffer, 7762306a36Sopenharmony_ci urb->actual_length); 7862306a36Sopenharmony_ci if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0) 7962306a36Sopenharmony_ci nfc_err(&drv_data->udev->dev, 8062306a36Sopenharmony_ci "corrupted Rx packet\n"); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) 8562306a36Sopenharmony_ci return; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci usb_anchor_urb(urb, &drv_data->bulk_anchor); 8862306a36Sopenharmony_ci usb_mark_last_busy(drv_data->udev); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 9162306a36Sopenharmony_ci if (err) { 9262306a36Sopenharmony_ci /* -EPERM: urb is being killed; 9362306a36Sopenharmony_ci * -ENODEV: device got disconnected 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci if (err != -EPERM && err != -ENODEV) 9662306a36Sopenharmony_ci nfc_err(&drv_data->udev->dev, 9762306a36Sopenharmony_ci "urb %p failed to resubmit (%d)\n", urb, -err); 9862306a36Sopenharmony_ci usb_unanchor_urb(urb); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int 10362306a36Sopenharmony_cinfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct urb *urb; 10662306a36Sopenharmony_ci unsigned char *buf; 10762306a36Sopenharmony_ci unsigned int pipe; 10862306a36Sopenharmony_ci int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!drv_data->bulk_rx_ep) 11162306a36Sopenharmony_ci return -ENODEV; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci urb = usb_alloc_urb(0, mem_flags); 11462306a36Sopenharmony_ci if (!urb) 11562306a36Sopenharmony_ci return -ENOMEM; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci buf = kmalloc(size, mem_flags); 11862306a36Sopenharmony_ci if (!buf) { 11962306a36Sopenharmony_ci usb_free_urb(urb); 12062306a36Sopenharmony_ci return -ENOMEM; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci pipe = usb_rcvbulkpipe(drv_data->udev, 12462306a36Sopenharmony_ci drv_data->bulk_rx_ep->bEndpointAddress); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, 12762306a36Sopenharmony_ci nfcmrvl_bulk_complete, drv_data); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci urb->transfer_flags |= URB_FREE_BUFFER; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci usb_mark_last_busy(drv_data->udev); 13262306a36Sopenharmony_ci usb_anchor_urb(urb, &drv_data->bulk_anchor); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci err = usb_submit_urb(urb, mem_flags); 13562306a36Sopenharmony_ci if (err) { 13662306a36Sopenharmony_ci if (err != -EPERM && err != -ENODEV) 13762306a36Sopenharmony_ci nfc_err(&drv_data->udev->dev, 13862306a36Sopenharmony_ci "urb %p submission failed (%d)\n", urb, -err); 13962306a36Sopenharmony_ci usb_unanchor_urb(urb); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci usb_free_urb(urb); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return err; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void nfcmrvl_tx_complete(struct urb *urb) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct sk_buff *skb = urb->context; 15062306a36Sopenharmony_ci struct nci_dev *ndev = (struct nci_dev *)skb->dev; 15162306a36Sopenharmony_ci struct nfcmrvl_private *priv = nci_get_drvdata(ndev); 15262306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 15362306a36Sopenharmony_ci unsigned long flags; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci nfc_info(priv->dev, "urb %p status %d count %d\n", 15662306a36Sopenharmony_ci urb, urb->status, urb->actual_length); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci spin_lock_irqsave(&drv_data->txlock, flags); 15962306a36Sopenharmony_ci drv_data->tx_in_flight--; 16062306a36Sopenharmony_ci spin_unlock_irqrestore(&drv_data->txlock, flags); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci kfree(urb->setup_packet); 16362306a36Sopenharmony_ci kfree_skb(skb); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 16962306a36Sopenharmony_ci int err; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci err = usb_autopm_get_interface(drv_data->intf); 17262306a36Sopenharmony_ci if (err) 17362306a36Sopenharmony_ci return err; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci drv_data->intf->needs_remote_wakeup = 1; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); 17862306a36Sopenharmony_ci if (err) 17962306a36Sopenharmony_ci goto failed; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 18262306a36Sopenharmony_ci nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci usb_autopm_put_interface(drv_data->intf); 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cifailed: 18862306a36Sopenharmony_ci usb_autopm_put_interface(drv_data->intf); 18962306a36Sopenharmony_ci return err; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci usb_kill_anchored_urbs(&drv_data->bulk_anchor); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 20062306a36Sopenharmony_ci int err; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci cancel_work_sync(&drv_data->waker); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci nfcmrvl_usb_stop_traffic(drv_data); 20762306a36Sopenharmony_ci usb_kill_anchored_urbs(&drv_data->tx_anchor); 20862306a36Sopenharmony_ci err = usb_autopm_get_interface(drv_data->intf); 20962306a36Sopenharmony_ci if (err) 21062306a36Sopenharmony_ci goto failed; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci drv_data->intf->needs_remote_wakeup = 0; 21362306a36Sopenharmony_ci usb_autopm_put_interface(drv_data->intf); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cifailed: 21662306a36Sopenharmony_ci usb_scuttle_anchored_urbs(&drv_data->deferred); 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, 22162306a36Sopenharmony_ci struct sk_buff *skb) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; 22462306a36Sopenharmony_ci struct urb *urb; 22562306a36Sopenharmony_ci unsigned int pipe; 22662306a36Sopenharmony_ci int err; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!drv_data->bulk_tx_ep) 22962306a36Sopenharmony_ci return -ENODEV; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 23262306a36Sopenharmony_ci if (!urb) 23362306a36Sopenharmony_ci return -ENOMEM; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci pipe = usb_sndbulkpipe(drv_data->udev, 23662306a36Sopenharmony_ci drv_data->bulk_tx_ep->bEndpointAddress); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len, 23962306a36Sopenharmony_ci nfcmrvl_tx_complete, skb); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci err = nfcmrvl_inc_tx(drv_data); 24262306a36Sopenharmony_ci if (err) { 24362306a36Sopenharmony_ci usb_anchor_urb(urb, &drv_data->deferred); 24462306a36Sopenharmony_ci schedule_work(&drv_data->waker); 24562306a36Sopenharmony_ci err = 0; 24662306a36Sopenharmony_ci goto done; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci usb_anchor_urb(urb, &drv_data->tx_anchor); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 25262306a36Sopenharmony_ci if (err) { 25362306a36Sopenharmony_ci if (err != -EPERM && err != -ENODEV) 25462306a36Sopenharmony_ci nfc_err(&drv_data->udev->dev, 25562306a36Sopenharmony_ci "urb %p submission failed (%d)\n", urb, -err); 25662306a36Sopenharmony_ci kfree(urb->setup_packet); 25762306a36Sopenharmony_ci usb_unanchor_urb(urb); 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci usb_mark_last_busy(drv_data->udev); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cidone: 26362306a36Sopenharmony_ci usb_free_urb(urb); 26462306a36Sopenharmony_ci return err; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic const struct nfcmrvl_if_ops usb_ops = { 26862306a36Sopenharmony_ci .nci_open = nfcmrvl_usb_nci_open, 26962306a36Sopenharmony_ci .nci_close = nfcmrvl_usb_nci_close, 27062306a36Sopenharmony_ci .nci_send = nfcmrvl_usb_nci_send, 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void nfcmrvl_waker(struct work_struct *work) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = 27662306a36Sopenharmony_ci container_of(work, struct nfcmrvl_usb_drv_data, waker); 27762306a36Sopenharmony_ci int err; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci err = usb_autopm_get_interface(drv_data->intf); 28062306a36Sopenharmony_ci if (err) 28162306a36Sopenharmony_ci return; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci usb_autopm_put_interface(drv_data->intf); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int nfcmrvl_probe(struct usb_interface *intf, 28762306a36Sopenharmony_ci const struct usb_device_id *id) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data; 29062306a36Sopenharmony_ci struct nfcmrvl_private *priv; 29162306a36Sopenharmony_ci int i; 29262306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 29362306a36Sopenharmony_ci struct nfcmrvl_platform_data config; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* No configuration for USB */ 29662306a36Sopenharmony_ci memset(&config, 0, sizeof(config)); 29762306a36Sopenharmony_ci config.reset_n_io = -EINVAL; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci nfc_info(&udev->dev, "intf %p id %p\n", intf, id); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); 30262306a36Sopenharmony_ci if (!drv_data) 30362306a36Sopenharmony_ci return -ENOMEM; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { 30662306a36Sopenharmony_ci struct usb_endpoint_descriptor *ep_desc; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ep_desc = &intf->cur_altsetting->endpoint[i].desc; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!drv_data->bulk_tx_ep && 31162306a36Sopenharmony_ci usb_endpoint_is_bulk_out(ep_desc)) { 31262306a36Sopenharmony_ci drv_data->bulk_tx_ep = ep_desc; 31362306a36Sopenharmony_ci } else if (!drv_data->bulk_rx_ep && 31462306a36Sopenharmony_ci usb_endpoint_is_bulk_in(ep_desc)) { 31562306a36Sopenharmony_ci drv_data->bulk_rx_ep = ep_desc; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) 32062306a36Sopenharmony_ci return -ENODEV; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci drv_data->udev = udev; 32362306a36Sopenharmony_ci drv_data->intf = intf; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci INIT_WORK(&drv_data->waker, nfcmrvl_waker); 32662306a36Sopenharmony_ci spin_lock_init(&drv_data->txlock); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci init_usb_anchor(&drv_data->tx_anchor); 32962306a36Sopenharmony_ci init_usb_anchor(&drv_data->bulk_anchor); 33062306a36Sopenharmony_ci init_usb_anchor(&drv_data->deferred); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_USB, drv_data, &usb_ops, 33362306a36Sopenharmony_ci &intf->dev, &config); 33462306a36Sopenharmony_ci if (IS_ERR(priv)) 33562306a36Sopenharmony_ci return PTR_ERR(priv); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci drv_data->priv = priv; 33862306a36Sopenharmony_ci drv_data->priv->support_fw_dnld = false; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci usb_set_intfdata(intf, drv_data); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void nfcmrvl_disconnect(struct usb_interface *intf) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!drv_data) 35062306a36Sopenharmony_ci return; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci nfcmrvl_nci_unregister_dev(drv_data->priv); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci usb_set_intfdata(drv_data->intf, NULL); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci#ifdef CONFIG_PM 36062306a36Sopenharmony_cistatic int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (drv_data->suspend_count++) 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci spin_lock_irq(&drv_data->txlock); 37062306a36Sopenharmony_ci if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { 37162306a36Sopenharmony_ci set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 37262306a36Sopenharmony_ci spin_unlock_irq(&drv_data->txlock); 37362306a36Sopenharmony_ci } else { 37462306a36Sopenharmony_ci spin_unlock_irq(&drv_data->txlock); 37562306a36Sopenharmony_ci drv_data->suspend_count--; 37662306a36Sopenharmony_ci return -EBUSY; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci nfcmrvl_usb_stop_traffic(drv_data); 38062306a36Sopenharmony_ci usb_kill_anchored_urbs(&drv_data->tx_anchor); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct urb *urb; 38862306a36Sopenharmony_ci int err; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci while ((urb = usb_get_from_anchor(&drv_data->deferred))) { 39162306a36Sopenharmony_ci usb_anchor_urb(urb, &drv_data->tx_anchor); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 39462306a36Sopenharmony_ci if (err) { 39562306a36Sopenharmony_ci kfree(urb->setup_packet); 39662306a36Sopenharmony_ci usb_unanchor_urb(urb); 39762306a36Sopenharmony_ci usb_free_urb(urb); 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci drv_data->tx_in_flight++; 40262306a36Sopenharmony_ci usb_free_urb(urb); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* Cleanup the rest deferred urbs. */ 40662306a36Sopenharmony_ci while ((urb = usb_get_from_anchor(&drv_data->deferred))) { 40762306a36Sopenharmony_ci kfree(urb->setup_packet); 40862306a36Sopenharmony_ci usb_free_urb(urb); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int nfcmrvl_resume(struct usb_interface *intf) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); 41562306a36Sopenharmony_ci int err = 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci nfc_info(&drv_data->udev->dev, "intf %p\n", intf); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (--drv_data->suspend_count) 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) 42362306a36Sopenharmony_ci goto done; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { 42662306a36Sopenharmony_ci err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); 42762306a36Sopenharmony_ci if (err) { 42862306a36Sopenharmony_ci clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); 42962306a36Sopenharmony_ci goto failed; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci spin_lock_irq(&drv_data->txlock); 43662306a36Sopenharmony_ci nfcmrvl_play_deferred(drv_data); 43762306a36Sopenharmony_ci clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 43862306a36Sopenharmony_ci spin_unlock_irq(&drv_data->txlock); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cifailed: 44362306a36Sopenharmony_ci usb_scuttle_anchored_urbs(&drv_data->deferred); 44462306a36Sopenharmony_cidone: 44562306a36Sopenharmony_ci spin_lock_irq(&drv_data->txlock); 44662306a36Sopenharmony_ci clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); 44762306a36Sopenharmony_ci spin_unlock_irq(&drv_data->txlock); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return err; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci#endif 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic struct usb_driver nfcmrvl_usb_driver = { 45462306a36Sopenharmony_ci .name = "nfcmrvl", 45562306a36Sopenharmony_ci .probe = nfcmrvl_probe, 45662306a36Sopenharmony_ci .disconnect = nfcmrvl_disconnect, 45762306a36Sopenharmony_ci#ifdef CONFIG_PM 45862306a36Sopenharmony_ci .suspend = nfcmrvl_suspend, 45962306a36Sopenharmony_ci .resume = nfcmrvl_resume, 46062306a36Sopenharmony_ci .reset_resume = nfcmrvl_resume, 46162306a36Sopenharmony_ci#endif 46262306a36Sopenharmony_ci .id_table = nfcmrvl_table, 46362306a36Sopenharmony_ci .supports_autosuspend = 1, 46462306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 46562306a36Sopenharmony_ci .soft_unbind = 1, 46662306a36Sopenharmony_ci}; 46762306a36Sopenharmony_cimodule_usb_driver(nfcmrvl_usb_driver); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 47062306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell NFC-over-USB driver"); 47162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 472