2011-12-14 19:43:12 +04:00
/*
* Copyright ( C ) 2011 Intel Corporation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the
* Free Software Foundation , Inc . ,
* 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# define pr_fmt(fmt) "llcp: %s: " fmt, __func__
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/nfc.h>
# include "../nfc.h"
# include "llcp.h"
static u8 llcp_magic [ 3 ] = { 0x46 , 0x66 , 0x6d } ;
static struct list_head llcp_devices ;
static void nfc_llcp_socket_release ( struct nfc_llcp_local * local )
{
struct nfc_llcp_sock * parent , * s , * n ;
struct sock * sk , * parent_sk ;
int i ;
mutex_lock ( & local - > socket_lock ) ;
for ( i = 0 ; i < LLCP_MAX_SAP ; i + + ) {
parent = local - > sockets [ i ] ;
if ( parent = = NULL )
continue ;
/* Release all child sockets */
list_for_each_entry_safe ( s , n , & parent - > list , list ) {
2012-03-05 04:03:51 +04:00
list_del_init ( & s - > list ) ;
2011-12-14 19:43:12 +04:00
sk = & s - > sk ;
lock_sock ( sk ) ;
if ( sk - > sk_state = = LLCP_CONNECTED )
nfc_put_device ( s - > dev ) ;
sk - > sk_state = LLCP_CLOSED ;
release_sock ( sk ) ;
2012-03-05 04:03:51 +04:00
sock_orphan ( sk ) ;
s - > local = NULL ;
2011-12-14 19:43:12 +04:00
}
parent_sk = & parent - > sk ;
lock_sock ( parent_sk ) ;
if ( parent_sk - > sk_state = = LLCP_LISTEN ) {
struct nfc_llcp_sock * lsk , * n ;
struct sock * accept_sk ;
list_for_each_entry_safe ( lsk , n , & parent - > accept_queue ,
2012-03-05 04:03:52 +04:00
accept_queue ) {
2011-12-14 19:43:12 +04:00
accept_sk = & lsk - > sk ;
lock_sock ( accept_sk ) ;
nfc_llcp_accept_unlink ( accept_sk ) ;
accept_sk - > sk_state = LLCP_CLOSED ;
release_sock ( accept_sk ) ;
sock_orphan ( accept_sk ) ;
2012-03-05 04:03:51 +04:00
lsk - > local = NULL ;
2011-12-14 19:43:12 +04:00
}
}
if ( parent_sk - > sk_state = = LLCP_CONNECTED )
nfc_put_device ( parent - > dev ) ;
parent_sk - > sk_state = LLCP_CLOSED ;
release_sock ( parent_sk ) ;
2012-03-05 04:03:51 +04:00
sock_orphan ( parent_sk ) ;
parent - > local = NULL ;
2011-12-14 19:43:12 +04:00
}
mutex_unlock ( & local - > socket_lock ) ;
}
2012-03-05 04:03:40 +04:00
static void nfc_llcp_clear_sdp ( struct nfc_llcp_local * local )
{
mutex_lock ( & local - > sdp_lock ) ;
local - > local_wks = 0 ;
local - > local_sdp = 0 ;
local - > local_sap = 0 ;
mutex_unlock ( & local - > sdp_lock ) ;
}
2011-12-14 19:43:12 +04:00
static void nfc_llcp_timeout_work ( struct work_struct * work )
{
struct nfc_llcp_local * local = container_of ( work , struct nfc_llcp_local ,
2012-03-05 04:03:52 +04:00
timeout_work ) ;
2011-12-14 19:43:12 +04:00
nfc_dep_link_down ( local - > dev ) ;
}
static void nfc_llcp_symm_timer ( unsigned long data )
{
struct nfc_llcp_local * local = ( struct nfc_llcp_local * ) data ;
pr_err ( " SYMM timeout \n " ) ;
queue_work ( local - > timeout_wq , & local - > timeout_work ) ;
}
struct nfc_llcp_local * nfc_llcp_find_local ( struct nfc_dev * dev )
{
struct nfc_llcp_local * local , * n ;
list_for_each_entry_safe ( local , n , & llcp_devices , list )
if ( local - > dev = = dev )
return local ;
pr_debug ( " No device found \n " ) ;
return NULL ;
}
static char * wks [ ] = {
NULL ,
NULL , /* SDP */
" urn:nfc:sn:ip " ,
" urn:nfc:sn:obex " ,
" urn:nfc:sn:snep " ,
} ;
static int nfc_llcp_wks_sap ( char * service_name , size_t service_name_len )
{
int sap , num_wks ;
pr_debug ( " %s \n " , service_name ) ;
if ( service_name = = NULL )
return - EINVAL ;
num_wks = ARRAY_SIZE ( wks ) ;
2012-03-05 04:03:52 +04:00
for ( sap = 0 ; sap < num_wks ; sap + + ) {
2011-12-14 19:43:12 +04:00
if ( wks [ sap ] = = NULL )
continue ;
if ( strncmp ( wks [ sap ] , service_name , service_name_len ) = = 0 )
return sap ;
}
return - EINVAL ;
}
u8 nfc_llcp_get_sdp_ssap ( struct nfc_llcp_local * local ,
2012-03-05 04:03:52 +04:00
struct nfc_llcp_sock * sock )
2011-12-14 19:43:12 +04:00
{
mutex_lock ( & local - > sdp_lock ) ;
if ( sock - > service_name ! = NULL & & sock - > service_name_len > 0 ) {
int ssap = nfc_llcp_wks_sap ( sock - > service_name ,
2012-03-05 04:03:52 +04:00
sock - > service_name_len ) ;
2011-12-14 19:43:12 +04:00
if ( ssap > 0 ) {
pr_debug ( " WKS %d \n " , ssap ) ;
/* This is a WKS, let's check if it's free */
if ( local - > local_wks & BIT ( ssap ) ) {
mutex_unlock ( & local - > sdp_lock ) ;
return LLCP_SAP_MAX ;
}
2012-03-05 04:03:38 +04:00
set_bit ( ssap , & local - > local_wks ) ;
2011-12-14 19:43:12 +04:00
mutex_unlock ( & local - > sdp_lock ) ;
return ssap ;
}
/*
* This is not a well known service ,
* we should try to find a local SDP free spot
*/
ssap = find_first_zero_bit ( & local - > local_sdp , LLCP_SDP_NUM_SAP ) ;
if ( ssap = = LLCP_SDP_NUM_SAP ) {
mutex_unlock ( & local - > sdp_lock ) ;
return LLCP_SAP_MAX ;
}
pr_debug ( " SDP ssap %d \n " , LLCP_WKS_NUM_SAP + ssap ) ;
2012-03-05 04:03:38 +04:00
set_bit ( ssap , & local - > local_sdp ) ;
2011-12-14 19:43:12 +04:00
mutex_unlock ( & local - > sdp_lock ) ;
return LLCP_WKS_NUM_SAP + ssap ;
} else if ( sock - > ssap ! = 0 ) {
if ( sock - > ssap < LLCP_WKS_NUM_SAP ) {
2012-03-05 04:03:38 +04:00
if ( ! test_bit ( sock - > ssap , & local - > local_wks ) ) {
set_bit ( sock - > ssap , & local - > local_wks ) ;
2011-12-14 19:43:12 +04:00
mutex_unlock ( & local - > sdp_lock ) ;
return sock - > ssap ;
}
} else if ( sock - > ssap < LLCP_SDP_NUM_SAP ) {
2012-03-05 04:03:38 +04:00
if ( ! test_bit ( sock - > ssap - LLCP_WKS_NUM_SAP ,
& local - > local_sdp ) ) {
set_bit ( sock - > ssap - LLCP_WKS_NUM_SAP ,
& local - > local_sdp ) ;
2011-12-14 19:43:12 +04:00
mutex_unlock ( & local - > sdp_lock ) ;
return sock - > ssap ;
}
}
}
mutex_unlock ( & local - > sdp_lock ) ;
return LLCP_SAP_MAX ;
}
u8 nfc_llcp_get_local_ssap ( struct nfc_llcp_local * local )
{
u8 local_ssap ;
mutex_lock ( & local - > sdp_lock ) ;
local_ssap = find_first_zero_bit ( & local - > local_sap , LLCP_LOCAL_NUM_SAP ) ;
if ( local_ssap = = LLCP_LOCAL_NUM_SAP ) {
mutex_unlock ( & local - > sdp_lock ) ;
return LLCP_SAP_MAX ;
}
2012-03-05 04:03:38 +04:00
set_bit ( local_ssap , & local - > local_sap ) ;
2011-12-14 19:43:12 +04:00
mutex_unlock ( & local - > sdp_lock ) ;
return local_ssap + LLCP_LOCAL_SAP_OFFSET ;
}
void nfc_llcp_put_ssap ( struct nfc_llcp_local * local , u8 ssap )
{
u8 local_ssap ;
unsigned long * sdp ;
if ( ssap < LLCP_WKS_NUM_SAP ) {
local_ssap = ssap ;
sdp = & local - > local_wks ;
} else if ( ssap < LLCP_LOCAL_NUM_SAP ) {
local_ssap = ssap - LLCP_WKS_NUM_SAP ;
sdp = & local - > local_sdp ;
} else if ( ssap < LLCP_MAX_SAP ) {
local_ssap = ssap - LLCP_LOCAL_NUM_SAP ;
sdp = & local - > local_sap ;
} else {
return ;
}
mutex_lock ( & local - > sdp_lock ) ;
2012-03-05 04:03:38 +04:00
clear_bit ( local_ssap , sdp ) ;
2011-12-14 19:43:12 +04:00
mutex_unlock ( & local - > sdp_lock ) ;
}
2012-03-05 04:03:50 +04:00
u8 * nfc_llcp_general_bytes ( struct nfc_dev * dev , size_t * general_bytes_len )
2011-12-14 19:43:12 +04:00
{
struct nfc_llcp_local * local ;
local = nfc_llcp_find_local ( dev ) ;
if ( local = = NULL ) {
* general_bytes_len = 0 ;
return NULL ;
}
* general_bytes_len = local - > gb_len ;
return local - > gb ;
}
static int nfc_llcp_build_gb ( struct nfc_llcp_local * local )
{
u8 * gb_cur , * version_tlv , version , version_length ;
u8 * lto_tlv , lto , lto_length ;
u8 * wks_tlv , wks_length ;
2012-04-10 21:43:19 +04:00
u8 * miux_tlv , miux_length ;
__be16 miux ;
2011-12-14 19:43:12 +04:00
u8 gb_len = 0 ;
version = LLCP_VERSION_11 ;
version_tlv = nfc_llcp_build_tlv ( LLCP_TLV_VERSION , & version ,
2012-03-05 04:03:52 +04:00
1 , & version_length ) ;
2011-12-14 19:43:12 +04:00
gb_len + = version_length ;
/* 1500 ms */
lto = 150 ;
2012-04-10 21:43:20 +04:00
lto_tlv = nfc_llcp_build_tlv ( LLCP_TLV_LTO , & lto , 1 , & lto_length ) ;
2011-12-14 19:43:12 +04:00
gb_len + = lto_length ;
pr_debug ( " Local wks 0x%lx \n " , local - > local_wks ) ;
wks_tlv = nfc_llcp_build_tlv ( LLCP_TLV_WKS , ( u8 * ) & local - > local_wks , 2 ,
2012-03-05 04:03:52 +04:00
& wks_length ) ;
2011-12-14 19:43:12 +04:00
gb_len + = wks_length ;
2012-04-10 21:43:19 +04:00
miux = cpu_to_be16 ( LLCP_MAX_MIUX ) ;
miux_tlv = nfc_llcp_build_tlv ( LLCP_TLV_MIUX , ( u8 * ) & miux , 0 ,
& miux_length ) ;
gb_len + = miux_length ;
2011-12-14 19:43:12 +04:00
gb_len + = ARRAY_SIZE ( llcp_magic ) ;
if ( gb_len > NFC_MAX_GT_LEN ) {
kfree ( version_tlv ) ;
return - EINVAL ;
}
gb_cur = local - > gb ;
memcpy ( gb_cur , llcp_magic , ARRAY_SIZE ( llcp_magic ) ) ;
gb_cur + = ARRAY_SIZE ( llcp_magic ) ;
memcpy ( gb_cur , version_tlv , version_length ) ;
gb_cur + = version_length ;
memcpy ( gb_cur , lto_tlv , lto_length ) ;
gb_cur + = lto_length ;
memcpy ( gb_cur , wks_tlv , wks_length ) ;
gb_cur + = wks_length ;
2012-04-10 21:43:19 +04:00
memcpy ( gb_cur , miux_tlv , miux_length ) ;
gb_cur + = miux_length ;
2011-12-14 19:43:12 +04:00
kfree ( version_tlv ) ;
kfree ( lto_tlv ) ;
local - > gb_len = gb_len ;
return 0 ;
}
int nfc_llcp_set_remote_gb ( struct nfc_dev * dev , u8 * gb , u8 gb_len )
{
struct nfc_llcp_local * local = nfc_llcp_find_local ( dev ) ;
if ( local = = NULL ) {
pr_err ( " No LLCP device \n " ) ;
return - ENODEV ;
}
memset ( local - > remote_gb , 0 , NFC_MAX_GT_LEN ) ;
memcpy ( local - > remote_gb , gb , gb_len ) ;
local - > remote_gb_len = gb_len ;
2012-03-05 04:03:52 +04:00
if ( local - > remote_gb = = NULL | | local - > remote_gb_len = = 0 )
2011-12-14 19:43:12 +04:00
return - ENODEV ;
if ( memcmp ( local - > remote_gb , llcp_magic , 3 ) ) {
pr_err ( " MAC does not support LLCP \n " ) ;
return - EINVAL ;
}
return nfc_llcp_parse_tlv ( local ,
2012-03-05 04:03:52 +04:00
& local - > remote_gb [ 3 ] ,
local - > remote_gb_len - 3 ) ;
2011-12-14 19:43:12 +04:00
}
static void nfc_llcp_tx_work ( struct work_struct * work )
{
struct nfc_llcp_local * local = container_of ( work , struct nfc_llcp_local ,
2012-03-05 04:03:52 +04:00
tx_work ) ;
2011-12-14 19:43:12 +04:00
struct sk_buff * skb ;
skb = skb_dequeue ( & local - > tx_queue ) ;
if ( skb ! = NULL ) {
pr_debug ( " Sending pending skb \n " ) ;
2012-04-10 21:43:13 +04:00
print_hex_dump ( KERN_DEBUG , " LLCP Tx: " , DUMP_PREFIX_OFFSET ,
16 , 1 , skb - > data , skb - > len , true ) ;
2011-12-14 19:43:12 +04:00
nfc_data_exchange ( local - > dev , local - > target_idx ,
2012-03-05 04:03:52 +04:00
skb , nfc_llcp_recv , local ) ;
2011-12-14 19:43:12 +04:00
} else {
nfc_llcp_send_symm ( local - > dev ) ;
}
mod_timer ( & local - > link_timer ,
2012-03-05 04:03:52 +04:00
jiffies + msecs_to_jiffies ( local - > remote_lto ) ) ;
2011-12-14 19:43:12 +04:00
}
static u8 nfc_llcp_dsap ( struct sk_buff * pdu )
{
return ( pdu - > data [ 0 ] & 0xfc ) > > 2 ;
}
static u8 nfc_llcp_ptype ( struct sk_buff * pdu )
{
return ( ( pdu - > data [ 0 ] & 0x03 ) < < 2 ) | ( ( pdu - > data [ 1 ] & 0xc0 ) > > 6 ) ;
}
static u8 nfc_llcp_ssap ( struct sk_buff * pdu )
{
return pdu - > data [ 1 ] & 0x3f ;
}
static u8 nfc_llcp_ns ( struct sk_buff * pdu )
{
return pdu - > data [ 2 ] > > 4 ;
}
static u8 nfc_llcp_nr ( struct sk_buff * pdu )
{
return pdu - > data [ 2 ] & 0xf ;
}
static void nfc_llcp_set_nrns ( struct nfc_llcp_sock * sock , struct sk_buff * pdu )
{
2012-04-10 21:43:14 +04:00
pdu - > data [ 2 ] = ( sock - > send_n < < 4 ) | ( sock - > recv_n ) ;
2011-12-14 19:43:12 +04:00
sock - > send_n = ( sock - > send_n + 1 ) % 16 ;
sock - > recv_ack_n = ( sock - > recv_n - 1 ) % 16 ;
}
static struct nfc_llcp_sock * nfc_llcp_sock_get ( struct nfc_llcp_local * local ,
2012-03-05 04:03:52 +04:00
u8 ssap , u8 dsap )
2011-12-14 19:43:12 +04:00
{
struct nfc_llcp_sock * sock , * llcp_sock , * n ;
if ( ssap = = 0 & & dsap = = 0 )
return NULL ;
mutex_lock ( & local - > socket_lock ) ;
sock = local - > sockets [ ssap ] ;
if ( sock = = NULL ) {
mutex_unlock ( & local - > socket_lock ) ;
return NULL ;
}
pr_debug ( " root dsap %d (%d) \n " , sock - > dsap , dsap ) ;
if ( sock - > dsap = = dsap ) {
sock_hold ( & sock - > sk ) ;
mutex_unlock ( & local - > socket_lock ) ;
return sock ;
}
list_for_each_entry_safe ( llcp_sock , n , & sock - > list , list ) {
pr_debug ( " llcp_sock %p sk %p dsap %d \n " , llcp_sock ,
2012-03-05 04:03:52 +04:00
& llcp_sock - > sk , llcp_sock - > dsap ) ;
2011-12-14 19:43:12 +04:00
if ( llcp_sock - > dsap = = dsap ) {
sock_hold ( & llcp_sock - > sk ) ;
mutex_unlock ( & local - > socket_lock ) ;
return llcp_sock ;
}
}
pr_err ( " Could not find socket for %d %d \n " , ssap , dsap ) ;
mutex_unlock ( & local - > socket_lock ) ;
return NULL ;
}
static void nfc_llcp_sock_put ( struct nfc_llcp_sock * sock )
{
sock_put ( & sock - > sk ) ;
}
static u8 * nfc_llcp_connect_sn ( struct sk_buff * skb , size_t * sn_len )
{
u8 * tlv = & skb - > data [ 2 ] , type , length ;
size_t tlv_array_len = skb - > len - LLCP_HEADER_SIZE , offset = 0 ;
while ( offset < tlv_array_len ) {
type = tlv [ 0 ] ;
length = tlv [ 1 ] ;
pr_debug ( " type 0x%x length %d \n " , type , length ) ;
if ( type = = LLCP_TLV_SN ) {
* sn_len = length ;
return & tlv [ 2 ] ;
}
offset + = length + 2 ;
tlv + = length + 2 ;
}
return NULL ;
}
static void nfc_llcp_recv_connect ( struct nfc_llcp_local * local ,
2012-03-05 04:03:52 +04:00
struct sk_buff * skb )
2011-12-14 19:43:12 +04:00
{
struct sock * new_sk , * parent ;
struct nfc_llcp_sock * sock , * new_sock ;
u8 dsap , ssap , bound_sap , reason ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
pr_debug ( " %d %d \n " , dsap , ssap ) ;
nfc_llcp_parse_tlv ( local , & skb - > data [ LLCP_HEADER_SIZE ] ,
2012-03-05 04:03:52 +04:00
skb - > len - LLCP_HEADER_SIZE ) ;
2011-12-14 19:43:12 +04:00
if ( dsap ! = LLCP_SAP_SDP ) {
bound_sap = dsap ;
mutex_lock ( & local - > socket_lock ) ;
sock = local - > sockets [ dsap ] ;
if ( sock = = NULL ) {
mutex_unlock ( & local - > socket_lock ) ;
reason = LLCP_DM_NOBOUND ;
goto fail ;
}
sock_hold ( & sock - > sk ) ;
mutex_unlock ( & local - > socket_lock ) ;
lock_sock ( & sock - > sk ) ;
if ( sock - > dsap = = LLCP_SAP_SDP & &
2012-03-05 04:03:52 +04:00
sock - > sk . sk_state = = LLCP_LISTEN )
2011-12-14 19:43:12 +04:00
goto enqueue ;
} else {
u8 * sn ;
size_t sn_len ;
sn = nfc_llcp_connect_sn ( skb , & sn_len ) ;
if ( sn = = NULL ) {
reason = LLCP_DM_NOBOUND ;
goto fail ;
}
pr_debug ( " Service name length %zu \n " , sn_len ) ;
mutex_lock ( & local - > socket_lock ) ;
for ( bound_sap = 0 ; bound_sap < LLCP_LOCAL_SAP_OFFSET ;
2012-03-05 04:03:52 +04:00
bound_sap + + ) {
2011-12-14 19:43:12 +04:00
sock = local - > sockets [ bound_sap ] ;
if ( sock = = NULL )
continue ;
if ( sock - > service_name = = NULL | |
2012-03-05 04:03:52 +04:00
sock - > service_name_len = = 0 )
2011-12-14 19:43:12 +04:00
continue ;
if ( sock - > service_name_len ! = sn_len )
continue ;
if ( sock - > dsap = = LLCP_SAP_SDP & &
2012-03-05 04:03:52 +04:00
sock - > sk . sk_state = = LLCP_LISTEN & &
! memcmp ( sn , sock - > service_name , sn_len ) ) {
2011-12-14 19:43:12 +04:00
pr_debug ( " Found service name at SAP %d \n " ,
2012-03-05 04:03:52 +04:00
bound_sap ) ;
2011-12-14 19:43:12 +04:00
sock_hold ( & sock - > sk ) ;
mutex_unlock ( & local - > socket_lock ) ;
lock_sock ( & sock - > sk ) ;
goto enqueue ;
}
}
2011-12-17 00:25:29 +04:00
mutex_unlock ( & local - > socket_lock ) ;
2011-12-14 19:43:12 +04:00
}
reason = LLCP_DM_NOBOUND ;
goto fail ;
enqueue :
parent = & sock - > sk ;
if ( sk_acceptq_is_full ( parent ) ) {
reason = LLCP_DM_REJ ;
release_sock ( & sock - > sk ) ;
sock_put ( & sock - > sk ) ;
goto fail ;
}
2012-03-05 04:03:52 +04:00
new_sk = nfc_llcp_sock_alloc ( NULL , parent - > sk_type , GFP_ATOMIC ) ;
2011-12-14 19:43:12 +04:00
if ( new_sk = = NULL ) {
reason = LLCP_DM_REJ ;
release_sock ( & sock - > sk ) ;
sock_put ( & sock - > sk ) ;
goto fail ;
}
new_sock = nfc_llcp_sock ( new_sk ) ;
new_sock - > dev = local - > dev ;
new_sock - > local = local ;
new_sock - > nfc_protocol = sock - > nfc_protocol ;
new_sock - > ssap = bound_sap ;
new_sock - > dsap = ssap ;
new_sock - > parent = parent ;
pr_debug ( " new sock %p sk %p \n " , new_sock , & new_sock - > sk ) ;
list_add_tail ( & new_sock - > list , & sock - > list ) ;
nfc_llcp_accept_enqueue ( & sock - > sk , new_sk ) ;
nfc_get_device ( local - > dev - > idx ) ;
new_sk - > sk_state = LLCP_CONNECTED ;
/* Wake the listening processes */
parent - > sk_data_ready ( parent , 0 ) ;
/* Send CC */
nfc_llcp_send_cc ( new_sock ) ;
release_sock ( & sock - > sk ) ;
sock_put ( & sock - > sk ) ;
return ;
fail :
/* Send DM */
nfc_llcp_send_dm ( local , dsap , ssap , reason ) ;
return ;
}
2012-03-05 04:03:42 +04:00
int nfc_llcp_queue_i_frames ( struct nfc_llcp_sock * sock )
2012-03-05 04:03:35 +04:00
{
2012-03-05 04:03:42 +04:00
int nr_frames = 0 ;
2012-03-05 04:03:35 +04:00
struct nfc_llcp_local * local = sock - > local ;
pr_debug ( " Remote ready %d tx queue len %d remote rw %d " ,
2012-03-05 04:03:52 +04:00
sock - > remote_ready , skb_queue_len ( & sock - > tx_pending_queue ) ,
local - > remote_rw ) ;
2012-03-05 04:03:35 +04:00
/* Try to queue some I frames for transmission */
while ( sock - > remote_ready & &
2012-03-05 04:03:52 +04:00
skb_queue_len ( & sock - > tx_pending_queue ) < local - > remote_rw ) {
2012-03-05 04:03:35 +04:00
struct sk_buff * pdu , * pending_pdu ;
pdu = skb_dequeue ( & sock - > tx_queue ) ;
if ( pdu = = NULL )
break ;
/* Update N(S)/N(R) */
nfc_llcp_set_nrns ( sock , pdu ) ;
pending_pdu = skb_clone ( pdu , GFP_KERNEL ) ;
skb_queue_tail ( & local - > tx_queue , pdu ) ;
skb_queue_tail ( & sock - > tx_pending_queue , pending_pdu ) ;
2012-03-05 04:03:42 +04:00
nr_frames + + ;
2012-03-05 04:03:35 +04:00
}
2012-03-05 04:03:42 +04:00
return nr_frames ;
2012-03-05 04:03:35 +04:00
}
2011-12-14 19:43:12 +04:00
static void nfc_llcp_recv_hdlc ( struct nfc_llcp_local * local ,
2012-03-05 04:03:52 +04:00
struct sk_buff * skb )
2011-12-14 19:43:12 +04:00
{
struct nfc_llcp_sock * llcp_sock ;
struct sock * sk ;
u8 dsap , ssap , ptype , ns , nr ;
ptype = nfc_llcp_ptype ( skb ) ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
ns = nfc_llcp_ns ( skb ) ;
nr = nfc_llcp_nr ( skb ) ;
pr_debug ( " %d %d R %d S %d \n " , dsap , ssap , nr , ns ) ;
llcp_sock = nfc_llcp_sock_get ( local , dsap , ssap ) ;
if ( llcp_sock = = NULL ) {
nfc_llcp_send_dm ( local , dsap , ssap , LLCP_DM_NOCONN ) ;
return ;
}
sk = & llcp_sock - > sk ;
lock_sock ( sk ) ;
if ( sk - > sk_state = = LLCP_CLOSED ) {
release_sock ( sk ) ;
nfc_llcp_sock_put ( llcp_sock ) ;
}
/* Pass the payload upstream */
if ( ptype = = LLCP_PDU_I ) {
pr_debug ( " I frame, queueing on %p \n " , & llcp_sock - > sk ) ;
2012-03-05 04:03:36 +04:00
if ( ns = = llcp_sock - > recv_n )
llcp_sock - > recv_n = ( llcp_sock - > recv_n + 1 ) % 16 ;
else
pr_err ( " Received out of sequence I PDU \n " ) ;
2011-12-14 19:43:12 +04:00
skb_pull ( skb , LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE ) ;
if ( sock_queue_rcv_skb ( & llcp_sock - > sk , skb ) ) {
pr_err ( " receive queue is full \n " ) ;
skb_queue_head ( & llcp_sock - > tx_backlog_queue , skb ) ;
}
}
/* Remove skbs from the pending queue */
if ( llcp_sock - > send_ack_n ! = nr ) {
struct sk_buff * s , * tmp ;
llcp_sock - > send_ack_n = nr ;
skb_queue_walk_safe ( & llcp_sock - > tx_pending_queue , s , tmp )
if ( nfc_llcp_ns ( s ) < = nr ) {
skb_unlink ( s , & llcp_sock - > tx_pending_queue ) ;
kfree_skb ( s ) ;
}
}
2012-03-05 04:03:36 +04:00
if ( ptype = = LLCP_PDU_RR )
llcp_sock - > remote_ready = true ;
2012-03-05 04:03:52 +04:00
else if ( ptype = = LLCP_PDU_RNR )
2012-03-05 04:03:36 +04:00
llcp_sock - > remote_ready = false ;
2012-03-05 04:03:42 +04:00
if ( nfc_llcp_queue_i_frames ( llcp_sock ) = = 0 )
nfc_llcp_send_rr ( llcp_sock ) ;
2011-12-14 19:43:12 +04:00
release_sock ( sk ) ;
nfc_llcp_sock_put ( llcp_sock ) ;
}
static void nfc_llcp_recv_disc ( struct nfc_llcp_local * local ,
2012-03-05 04:03:52 +04:00
struct sk_buff * skb )
2011-12-14 19:43:12 +04:00
{
struct nfc_llcp_sock * llcp_sock ;
struct sock * sk ;
u8 dsap , ssap ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
llcp_sock = nfc_llcp_sock_get ( local , dsap , ssap ) ;
if ( llcp_sock = = NULL ) {
nfc_llcp_send_dm ( local , dsap , ssap , LLCP_DM_NOCONN ) ;
return ;
}
sk = & llcp_sock - > sk ;
lock_sock ( sk ) ;
if ( sk - > sk_state = = LLCP_CLOSED ) {
release_sock ( sk ) ;
nfc_llcp_sock_put ( llcp_sock ) ;
}
if ( sk - > sk_state = = LLCP_CONNECTED ) {
nfc_put_device ( local - > dev ) ;
sk - > sk_state = LLCP_CLOSED ;
sk - > sk_state_change ( sk ) ;
}
nfc_llcp_send_dm ( local , dsap , ssap , LLCP_DM_DISC ) ;
release_sock ( sk ) ;
nfc_llcp_sock_put ( llcp_sock ) ;
}
2012-03-05 04:03:52 +04:00
static void nfc_llcp_recv_cc ( struct nfc_llcp_local * local , struct sk_buff * skb )
2011-12-14 19:43:12 +04:00
{
struct nfc_llcp_sock * llcp_sock ;
u8 dsap , ssap ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
llcp_sock = nfc_llcp_sock_get ( local , dsap , ssap ) ;
if ( llcp_sock = = NULL )
llcp_sock = nfc_llcp_sock_get ( local , dsap , LLCP_SAP_SDP ) ;
if ( llcp_sock = = NULL ) {
pr_err ( " Invalid CC \n " ) ;
nfc_llcp_send_dm ( local , dsap , ssap , LLCP_DM_NOCONN ) ;
return ;
}
llcp_sock - > dsap = ssap ;
nfc_llcp_parse_tlv ( local , & skb - > data [ LLCP_HEADER_SIZE ] ,
2012-03-05 04:03:52 +04:00
skb - > len - LLCP_HEADER_SIZE ) ;
2011-12-14 19:43:12 +04:00
nfc_llcp_sock_put ( llcp_sock ) ;
}
static void nfc_llcp_rx_work ( struct work_struct * work )
{
struct nfc_llcp_local * local = container_of ( work , struct nfc_llcp_local ,
2012-03-05 04:03:52 +04:00
rx_work ) ;
2011-12-14 19:43:12 +04:00
u8 dsap , ssap , ptype ;
struct sk_buff * skb ;
skb = local - > rx_pending ;
if ( skb = = NULL ) {
pr_debug ( " No pending SKB \n " ) ;
return ;
}
ptype = nfc_llcp_ptype ( skb ) ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
pr_debug ( " ptype 0x%x dsap 0x%x ssap 0x%x \n " , ptype , dsap , ssap ) ;
2012-04-10 21:43:13 +04:00
if ( ptype ! = LLCP_PDU_SYMM )
print_hex_dump ( KERN_DEBUG , " LLCP Rx: " , DUMP_PREFIX_OFFSET ,
16 , 1 , skb - > data , skb - > len , true ) ;
2011-12-14 19:43:12 +04:00
switch ( ptype ) {
case LLCP_PDU_SYMM :
pr_debug ( " SYMM \n " ) ;
break ;
case LLCP_PDU_CONNECT :
pr_debug ( " CONNECT \n " ) ;
nfc_llcp_recv_connect ( local , skb ) ;
break ;
case LLCP_PDU_DISC :
pr_debug ( " DISC \n " ) ;
nfc_llcp_recv_disc ( local , skb ) ;
break ;
case LLCP_PDU_CC :
pr_debug ( " CC \n " ) ;
nfc_llcp_recv_cc ( local , skb ) ;
break ;
case LLCP_PDU_I :
case LLCP_PDU_RR :
2012-03-05 04:03:36 +04:00
case LLCP_PDU_RNR :
2011-12-14 19:43:12 +04:00
pr_debug ( " I frame \n " ) ;
nfc_llcp_recv_hdlc ( local , skb ) ;
break ;
}
queue_work ( local - > tx_wq , & local - > tx_work ) ;
kfree_skb ( local - > rx_pending ) ;
local - > rx_pending = NULL ;
return ;
}
void nfc_llcp_recv ( void * data , struct sk_buff * skb , int err )
{
struct nfc_llcp_local * local = ( struct nfc_llcp_local * ) data ;
pr_debug ( " Received an LLCP PDU \n " ) ;
if ( err < 0 ) {
2012-03-05 04:03:52 +04:00
pr_err ( " err %d \n " , err ) ;
2011-12-14 19:43:12 +04:00
return ;
}
local - > rx_pending = skb_get ( skb ) ;
del_timer ( & local - > link_timer ) ;
queue_work ( local - > rx_wq , & local - > rx_work ) ;
return ;
}
void nfc_llcp_mac_is_down ( struct nfc_dev * dev )
{
struct nfc_llcp_local * local ;
local = nfc_llcp_find_local ( dev ) ;
if ( local = = NULL )
return ;
2012-03-05 04:03:40 +04:00
nfc_llcp_clear_sdp ( local ) ;
2011-12-14 19:43:12 +04:00
/* Close and purge all existing sockets */
nfc_llcp_socket_release ( local ) ;
}
void nfc_llcp_mac_is_up ( struct nfc_dev * dev , u32 target_idx ,
u8 comm_mode , u8 rf_mode )
{
struct nfc_llcp_local * local ;
pr_debug ( " rf mode %d \n " , rf_mode ) ;
local = nfc_llcp_find_local ( dev ) ;
if ( local = = NULL )
return ;
local - > target_idx = target_idx ;
local - > comm_mode = comm_mode ;
local - > rf_mode = rf_mode ;
if ( rf_mode = = NFC_RF_INITIATOR ) {
pr_debug ( " Queueing Tx work \n " ) ;
queue_work ( local - > tx_wq , & local - > tx_work ) ;
} else {
mod_timer ( & local - > link_timer ,
2012-03-05 04:03:52 +04:00
jiffies + msecs_to_jiffies ( local - > remote_lto ) ) ;
2011-12-14 19:43:12 +04:00
}
}
int nfc_llcp_register_device ( struct nfc_dev * ndev )
{
struct device * dev = & ndev - > dev ;
struct nfc_llcp_local * local ;
char name [ 32 ] ;
int err ;
local = kzalloc ( sizeof ( struct nfc_llcp_local ) , GFP_KERNEL ) ;
if ( local = = NULL )
return - ENOMEM ;
local - > dev = ndev ;
INIT_LIST_HEAD ( & local - > list ) ;
mutex_init ( & local - > sdp_lock ) ;
mutex_init ( & local - > socket_lock ) ;
init_timer ( & local - > link_timer ) ;
local - > link_timer . data = ( unsigned long ) local ;
local - > link_timer . function = nfc_llcp_symm_timer ;
skb_queue_head_init ( & local - > tx_queue ) ;
INIT_WORK ( & local - > tx_work , nfc_llcp_tx_work ) ;
snprintf ( name , sizeof ( name ) , " %s_llcp_tx_wq " , dev_name ( dev ) ) ;
2012-03-05 04:03:52 +04:00
local - > tx_wq =
alloc_workqueue ( name ,
WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM ,
1 ) ;
2011-12-14 19:43:12 +04:00
if ( local - > tx_wq = = NULL ) {
err = - ENOMEM ;
goto err_local ;
}
local - > rx_pending = NULL ;
INIT_WORK ( & local - > rx_work , nfc_llcp_rx_work ) ;
snprintf ( name , sizeof ( name ) , " %s_llcp_rx_wq " , dev_name ( dev ) ) ;
2012-03-05 04:03:52 +04:00
local - > rx_wq =
alloc_workqueue ( name ,
WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM ,
1 ) ;
2011-12-14 19:43:12 +04:00
if ( local - > rx_wq = = NULL ) {
err = - ENOMEM ;
goto err_tx_wq ;
}
INIT_WORK ( & local - > timeout_work , nfc_llcp_timeout_work ) ;
snprintf ( name , sizeof ( name ) , " %s_llcp_timeout_wq " , dev_name ( dev ) ) ;
2012-03-05 04:03:52 +04:00
local - > timeout_wq =
alloc_workqueue ( name ,
WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM ,
1 ) ;
2011-12-14 19:43:12 +04:00
if ( local - > timeout_wq = = NULL ) {
err = - ENOMEM ;
goto err_rx_wq ;
}
nfc_llcp_build_gb ( local ) ;
local - > remote_miu = LLCP_DEFAULT_MIU ;
local - > remote_lto = LLCP_DEFAULT_LTO ;
local - > remote_rw = LLCP_DEFAULT_RW ;
list_add ( & llcp_devices , & local - > list ) ;
return 0 ;
err_rx_wq :
destroy_workqueue ( local - > rx_wq ) ;
err_tx_wq :
destroy_workqueue ( local - > tx_wq ) ;
err_local :
kfree ( local ) ;
return 0 ;
}
void nfc_llcp_unregister_device ( struct nfc_dev * dev )
{
struct nfc_llcp_local * local = nfc_llcp_find_local ( dev ) ;
if ( local = = NULL ) {
pr_debug ( " No such device \n " ) ;
return ;
}
list_del ( & local - > list ) ;
nfc_llcp_socket_release ( local ) ;
del_timer_sync ( & local - > link_timer ) ;
skb_queue_purge ( & local - > tx_queue ) ;
destroy_workqueue ( local - > tx_wq ) ;
destroy_workqueue ( local - > rx_wq ) ;
2011-12-17 00:26:00 +04:00
kfree_skb ( local - > rx_pending ) ;
2011-12-14 19:43:12 +04:00
kfree ( local ) ;
}
int __init nfc_llcp_init ( void )
{
INIT_LIST_HEAD ( & llcp_devices ) ;
return nfc_llcp_sock_init ( ) ;
}
void nfc_llcp_exit ( void )
{
nfc_llcp_sock_exit ( ) ;
}