2014-01-07 00:58:19 +04:00
/**
* Marvell NFC - over - USB driver : USB interface related functions
*
* Copyright ( C ) 2014 , Marvell International Ltd .
*
* This software file ( the " File " ) is distributed by Marvell International
* Ltd . under the terms of the GNU General Public License Version 2 , June 1991
* ( the " License " ) . You may use , redistribute and / or modify this File in
* accordance with the terms and conditions of the License , a copy of which
* is available on the worldwide web at
* http : //www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS - IS , WITHOUT WARRANTY OF ANY KIND , AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED . The License provides additional details about
* this warranty disclaimer .
* */
# include <linux/module.h>
# include <linux/usb.h>
# include <linux/nfc.h>
# include <net/nfc/nci.h>
# include <net/nfc/nci_core.h>
# include "nfcmrvl.h"
static struct usb_device_id nfcmrvl_table [ ] = {
2015-06-11 12:25:45 +03:00
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1286 , 0x2046 ,
USB_CLASS_VENDOR_SPEC , 4 , 1 ) } ,
2014-01-07 00:58:19 +04:00
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , nfcmrvl_table ) ;
# define NFCMRVL_USB_BULK_RUNNING 1
# define NFCMRVL_USB_SUSPENDING 2
struct nfcmrvl_usb_drv_data {
struct usb_device * udev ;
struct usb_interface * intf ;
unsigned long flags ;
struct work_struct waker ;
struct usb_anchor tx_anchor ;
struct usb_anchor bulk_anchor ;
struct usb_anchor deferred ;
int tx_in_flight ;
/* protects tx_in_flight */
spinlock_t txlock ;
struct usb_endpoint_descriptor * bulk_tx_ep ;
struct usb_endpoint_descriptor * bulk_rx_ep ;
int suspend_count ;
struct nfcmrvl_private * priv ;
} ;
static int nfcmrvl_inc_tx ( struct nfcmrvl_usb_drv_data * drv_data )
{
unsigned long flags ;
int rv ;
spin_lock_irqsave ( & drv_data - > txlock , flags ) ;
rv = test_bit ( NFCMRVL_USB_SUSPENDING , & drv_data - > flags ) ;
if ( ! rv )
drv_data - > tx_in_flight + + ;
spin_unlock_irqrestore ( & drv_data - > txlock , flags ) ;
return rv ;
}
static void nfcmrvl_bulk_complete ( struct urb * urb )
{
struct nfcmrvl_usb_drv_data * drv_data = urb - > context ;
2015-06-11 12:25:44 +03:00
struct sk_buff * skb ;
2014-01-07 00:58:19 +04:00
int err ;
2015-06-11 12:25:44 +03:00
dev_dbg ( & drv_data - > udev - > dev , " urb %p status %d count %d \n " ,
2014-01-07 00:58:19 +04:00
urb , urb - > status , urb - > actual_length ) ;
if ( ! test_bit ( NFCMRVL_NCI_RUNNING , & drv_data - > flags ) )
return ;
if ( ! urb - > status ) {
2015-06-11 12:25:44 +03:00
skb = nci_skb_alloc ( drv_data - > priv - > ndev , urb - > actual_length ,
GFP_ATOMIC ) ;
if ( ! skb ) {
nfc_err ( & drv_data - > udev - > dev , " failed to alloc mem \n " ) ;
} else {
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:20 +03:00
skb_put_data ( skb , urb - > transfer_buffer ,
urb - > actual_length ) ;
2015-06-11 12:25:44 +03:00
if ( nfcmrvl_nci_recv_frame ( drv_data - > priv , skb ) < 0 )
nfc_err ( & drv_data - > udev - > dev ,
" corrupted Rx packet \n " ) ;
}
2014-01-07 00:58:19 +04:00
}
if ( ! test_bit ( NFCMRVL_USB_BULK_RUNNING , & drv_data - > flags ) )
return ;
usb_anchor_urb ( urb , & drv_data - > bulk_anchor ) ;
usb_mark_last_busy ( drv_data - > udev ) ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err ) {
/* -EPERM: urb is being killed;
* - ENODEV : device got disconnected
*/
if ( err ! = - EPERM & & err ! = - ENODEV )
nfc_err ( & drv_data - > udev - > dev ,
2015-04-07 10:17:00 +03:00
" urb %p failed to resubmit (%d) \n " , urb , - err ) ;
2014-01-07 00:58:19 +04:00
usb_unanchor_urb ( urb ) ;
}
}
static int
nfcmrvl_submit_bulk_urb ( struct nfcmrvl_usb_drv_data * drv_data , gfp_t mem_flags )
{
struct urb * urb ;
unsigned char * buf ;
unsigned int pipe ;
int err , size = NFCMRVL_NCI_MAX_EVENT_SIZE ;
if ( ! drv_data - > bulk_rx_ep )
return - ENODEV ;
urb = usb_alloc_urb ( 0 , mem_flags ) ;
if ( ! urb )
return - ENOMEM ;
buf = kmalloc ( size , mem_flags ) ;
if ( ! buf ) {
usb_free_urb ( urb ) ;
return - ENOMEM ;
}
pipe = usb_rcvbulkpipe ( drv_data - > udev ,
drv_data - > bulk_rx_ep - > bEndpointAddress ) ;
usb_fill_bulk_urb ( urb , drv_data - > udev , pipe , buf , size ,
nfcmrvl_bulk_complete , drv_data ) ;
urb - > transfer_flags | = URB_FREE_BUFFER ;
usb_mark_last_busy ( drv_data - > udev ) ;
usb_anchor_urb ( urb , & drv_data - > bulk_anchor ) ;
err = usb_submit_urb ( urb , mem_flags ) ;
if ( err ) {
if ( err ! = - EPERM & & err ! = - ENODEV )
nfc_err ( & drv_data - > udev - > dev ,
2015-04-07 10:17:00 +03:00
" urb %p submission failed (%d) \n " , urb , - err ) ;
2014-01-07 00:58:19 +04:00
usb_unanchor_urb ( urb ) ;
}
usb_free_urb ( urb ) ;
return err ;
}
static void nfcmrvl_tx_complete ( struct urb * urb )
{
struct sk_buff * skb = urb - > context ;
struct nci_dev * ndev = ( struct nci_dev * ) skb - > dev ;
struct nfcmrvl_private * priv = nci_get_drvdata ( ndev ) ;
struct nfcmrvl_usb_drv_data * drv_data = priv - > drv_data ;
2018-06-20 22:39:06 +03:00
unsigned long flags ;
2014-01-07 00:58:19 +04:00
2015-04-07 10:17:00 +03:00
nfc_info ( priv - > dev , " urb %p status %d count %d \n " ,
2014-01-07 00:58:19 +04:00
urb , urb - > status , urb - > actual_length ) ;
2018-06-20 22:39:06 +03:00
spin_lock_irqsave ( & drv_data - > txlock , flags ) ;
2014-01-07 00:58:19 +04:00
drv_data - > tx_in_flight - - ;
2018-06-20 22:39:06 +03:00
spin_unlock_irqrestore ( & drv_data - > txlock , flags ) ;
2014-01-07 00:58:19 +04:00
kfree ( urb - > setup_packet ) ;
kfree_skb ( skb ) ;
}
static int nfcmrvl_usb_nci_open ( struct nfcmrvl_private * priv )
{
struct nfcmrvl_usb_drv_data * drv_data = priv - > drv_data ;
int err ;
err = usb_autopm_get_interface ( drv_data - > intf ) ;
if ( err )
return err ;
drv_data - > intf - > needs_remote_wakeup = 1 ;
err = nfcmrvl_submit_bulk_urb ( drv_data , GFP_KERNEL ) ;
if ( err )
goto failed ;
set_bit ( NFCMRVL_USB_BULK_RUNNING , & drv_data - > flags ) ;
nfcmrvl_submit_bulk_urb ( drv_data , GFP_KERNEL ) ;
usb_autopm_put_interface ( drv_data - > intf ) ;
return 0 ;
failed :
usb_autopm_put_interface ( drv_data - > intf ) ;
return err ;
}
static void nfcmrvl_usb_stop_traffic ( struct nfcmrvl_usb_drv_data * drv_data )
{
usb_kill_anchored_urbs ( & drv_data - > bulk_anchor ) ;
}
static int nfcmrvl_usb_nci_close ( struct nfcmrvl_private * priv )
{
struct nfcmrvl_usb_drv_data * drv_data = priv - > drv_data ;
int err ;
cancel_work_sync ( & drv_data - > waker ) ;
clear_bit ( NFCMRVL_USB_BULK_RUNNING , & drv_data - > flags ) ;
nfcmrvl_usb_stop_traffic ( drv_data ) ;
usb_kill_anchored_urbs ( & drv_data - > tx_anchor ) ;
err = usb_autopm_get_interface ( drv_data - > intf ) ;
if ( err )
goto failed ;
drv_data - > intf - > needs_remote_wakeup = 0 ;
usb_autopm_put_interface ( drv_data - > intf ) ;
failed :
usb_scuttle_anchored_urbs ( & drv_data - > deferred ) ;
return 0 ;
}
static int nfcmrvl_usb_nci_send ( struct nfcmrvl_private * priv ,
struct sk_buff * skb )
{
struct nfcmrvl_usb_drv_data * drv_data = priv - > drv_data ;
struct urb * urb ;
unsigned int pipe ;
int err ;
if ( ! drv_data - > bulk_tx_ep )
return - ENODEV ;
urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
if ( ! urb )
return - ENOMEM ;
pipe = usb_sndbulkpipe ( drv_data - > udev ,
drv_data - > bulk_tx_ep - > bEndpointAddress ) ;
usb_fill_bulk_urb ( urb , drv_data - > udev , pipe , skb - > data , skb - > len ,
nfcmrvl_tx_complete , skb ) ;
err = nfcmrvl_inc_tx ( drv_data ) ;
if ( err ) {
usb_anchor_urb ( urb , & drv_data - > deferred ) ;
schedule_work ( & drv_data - > waker ) ;
err = 0 ;
goto done ;
}
usb_anchor_urb ( urb , & drv_data - > tx_anchor ) ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err ) {
if ( err ! = - EPERM & & err ! = - ENODEV )
nfc_err ( & drv_data - > udev - > dev ,
2015-04-07 10:17:00 +03:00
" urb %p submission failed (%d) \n " , urb , - err ) ;
2014-01-07 00:58:19 +04:00
kfree ( urb - > setup_packet ) ;
usb_unanchor_urb ( urb ) ;
} else {
usb_mark_last_busy ( drv_data - > udev ) ;
}
done :
usb_free_urb ( urb ) ;
return err ;
}
static struct nfcmrvl_if_ops usb_ops = {
. nci_open = nfcmrvl_usb_nci_open ,
. nci_close = nfcmrvl_usb_nci_close ,
. nci_send = nfcmrvl_usb_nci_send ,
} ;
static void nfcmrvl_waker ( struct work_struct * work )
{
struct nfcmrvl_usb_drv_data * drv_data =
container_of ( work , struct nfcmrvl_usb_drv_data , waker ) ;
int err ;
err = usb_autopm_get_interface ( drv_data - > intf ) ;
if ( err )
return ;
usb_autopm_put_interface ( drv_data - > intf ) ;
}
static int nfcmrvl_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
struct usb_endpoint_descriptor * ep_desc ;
struct nfcmrvl_usb_drv_data * drv_data ;
struct nfcmrvl_private * priv ;
int i ;
struct usb_device * udev = interface_to_usbdev ( intf ) ;
2015-06-11 15:00:19 +03:00
struct nfcmrvl_platform_data config ;
/* No configuration for USB */
memset ( & config , 0 , sizeof ( config ) ) ;
2014-01-07 00:58:19 +04:00
2015-04-07 10:17:00 +03:00
nfc_info ( & udev - > dev , " intf %p id %p \n " , intf , id ) ;
2014-01-07 00:58:19 +04:00
drv_data = devm_kzalloc ( & intf - > dev , sizeof ( * drv_data ) , GFP_KERNEL ) ;
if ( ! drv_data )
return - ENOMEM ;
for ( i = 0 ; i < intf - > cur_altsetting - > desc . bNumEndpoints ; i + + ) {
ep_desc = & intf - > cur_altsetting - > endpoint [ i ] . desc ;
if ( ! drv_data - > bulk_tx_ep & &
usb_endpoint_is_bulk_out ( ep_desc ) ) {
drv_data - > bulk_tx_ep = ep_desc ;
continue ;
}
if ( ! drv_data - > bulk_rx_ep & &
usb_endpoint_is_bulk_in ( ep_desc ) ) {
drv_data - > bulk_rx_ep = ep_desc ;
continue ;
}
}
if ( ! drv_data - > bulk_tx_ep | | ! drv_data - > bulk_rx_ep )
return - ENODEV ;
drv_data - > udev = udev ;
drv_data - > intf = intf ;
INIT_WORK ( & drv_data - > waker , nfcmrvl_waker ) ;
spin_lock_init ( & drv_data - > txlock ) ;
init_usb_anchor ( & drv_data - > tx_anchor ) ;
init_usb_anchor ( & drv_data - > bulk_anchor ) ;
init_usb_anchor ( & drv_data - > deferred ) ;
2015-10-26 12:27:40 +03:00
priv = nfcmrvl_nci_register_dev ( NFCMRVL_PHY_USB , drv_data , & usb_ops ,
2017-03-30 13:15:41 +03:00
& intf - > dev , & config ) ;
2014-01-07 00:58:19 +04:00
if ( IS_ERR ( priv ) )
return PTR_ERR ( priv ) ;
drv_data - > priv = priv ;
2015-10-26 12:27:39 +03:00
drv_data - > priv - > support_fw_dnld = false ;
2014-01-07 00:58:19 +04:00
usb_set_intfdata ( intf , drv_data ) ;
return 0 ;
}
static void nfcmrvl_disconnect ( struct usb_interface * intf )
{
struct nfcmrvl_usb_drv_data * drv_data = usb_get_intfdata ( intf ) ;
if ( ! drv_data )
return ;
2015-04-07 10:17:00 +03:00
nfc_info ( & drv_data - > udev - > dev , " intf %p \n " , intf ) ;
2014-01-07 00:58:19 +04:00
nfcmrvl_nci_unregister_dev ( drv_data - > priv ) ;
usb_set_intfdata ( drv_data - > intf , NULL ) ;
}
# ifdef CONFIG_PM
static int nfcmrvl_suspend ( struct usb_interface * intf , pm_message_t message )
{
struct nfcmrvl_usb_drv_data * drv_data = usb_get_intfdata ( intf ) ;
2015-04-07 10:17:00 +03:00
nfc_info ( & drv_data - > udev - > dev , " intf %p \n " , intf ) ;
2014-01-07 00:58:19 +04:00
if ( drv_data - > suspend_count + + )
return 0 ;
spin_lock_irq ( & drv_data - > txlock ) ;
if ( ! ( PMSG_IS_AUTO ( message ) & & drv_data - > tx_in_flight ) ) {
set_bit ( NFCMRVL_USB_SUSPENDING , & drv_data - > flags ) ;
spin_unlock_irq ( & drv_data - > txlock ) ;
} else {
spin_unlock_irq ( & drv_data - > txlock ) ;
drv_data - > suspend_count - - ;
return - EBUSY ;
}
nfcmrvl_usb_stop_traffic ( drv_data ) ;
usb_kill_anchored_urbs ( & drv_data - > tx_anchor ) ;
return 0 ;
}
static void nfcmrvl_play_deferred ( struct nfcmrvl_usb_drv_data * drv_data )
{
struct urb * urb ;
int err ;
while ( ( urb = usb_get_from_anchor ( & drv_data - > deferred ) ) ) {
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err )
break ;
drv_data - > tx_in_flight + + ;
}
usb_scuttle_anchored_urbs ( & drv_data - > deferred ) ;
}
static int nfcmrvl_resume ( struct usb_interface * intf )
{
struct nfcmrvl_usb_drv_data * drv_data = usb_get_intfdata ( intf ) ;
int err = 0 ;
2015-04-07 10:17:00 +03:00
nfc_info ( & drv_data - > udev - > dev , " intf %p \n " , intf ) ;
2014-01-07 00:58:19 +04:00
if ( - - drv_data - > suspend_count )
return 0 ;
if ( ! test_bit ( NFCMRVL_NCI_RUNNING , & drv_data - > flags ) )
goto done ;
if ( test_bit ( NFCMRVL_USB_BULK_RUNNING , & drv_data - > flags ) ) {
err = nfcmrvl_submit_bulk_urb ( drv_data , GFP_NOIO ) ;
if ( err ) {
clear_bit ( NFCMRVL_USB_BULK_RUNNING , & drv_data - > flags ) ;
goto failed ;
}
nfcmrvl_submit_bulk_urb ( drv_data , GFP_NOIO ) ;
}
spin_lock_irq ( & drv_data - > txlock ) ;
nfcmrvl_play_deferred ( drv_data ) ;
clear_bit ( NFCMRVL_USB_SUSPENDING , & drv_data - > flags ) ;
spin_unlock_irq ( & drv_data - > txlock ) ;
return 0 ;
failed :
usb_scuttle_anchored_urbs ( & drv_data - > deferred ) ;
done :
spin_lock_irq ( & drv_data - > txlock ) ;
clear_bit ( NFCMRVL_USB_SUSPENDING , & drv_data - > flags ) ;
spin_unlock_irq ( & drv_data - > txlock ) ;
return err ;
}
# endif
static struct usb_driver nfcmrvl_usb_driver = {
. name = " nfcmrvl " ,
. probe = nfcmrvl_probe ,
. disconnect = nfcmrvl_disconnect ,
# ifdef CONFIG_PM
. suspend = nfcmrvl_suspend ,
. resume = nfcmrvl_resume ,
. reset_resume = nfcmrvl_resume ,
# endif
. id_table = nfcmrvl_table ,
. supports_autosuspend = 1 ,
. disable_hub_initiated_lpm = 1 ,
. soft_unbind = 1 ,
} ;
module_usb_driver ( nfcmrvl_usb_driver ) ;
MODULE_AUTHOR ( " Marvell International Ltd. " ) ;
2015-10-26 12:27:37 +03:00
MODULE_DESCRIPTION ( " Marvell NFC-over-USB driver " ) ;
2014-01-07 00:58:19 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;