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 ;
2012-05-04 19:04:19 +04:00
void nfc_llcp_sock_link ( struct llcp_sock_list * l , struct sock * sk )
2011-12-14 19:43:12 +04:00
{
2012-05-04 19:04:19 +04:00
write_lock ( & l - > lock ) ;
sk_add_node ( sk , & l - > head ) ;
write_unlock ( & l - > lock ) ;
}
2011-12-14 19:43:12 +04:00
2012-05-04 19:04:19 +04:00
void nfc_llcp_sock_unlink ( struct llcp_sock_list * l , struct sock * sk )
{
write_lock ( & l - > lock ) ;
sk_del_node_init ( sk ) ;
write_unlock ( & l - > lock ) ;
}
2011-12-14 19:43:12 +04:00
2012-10-26 20:20:10 +04:00
static void nfc_llcp_socket_purge ( struct nfc_llcp_sock * sock )
{
struct nfc_llcp_local * local = sock - > local ;
struct sk_buff * s , * tmp ;
pr_debug ( " %p \n " , & sock - > sk ) ;
skb_queue_purge ( & sock - > tx_queue ) ;
skb_queue_purge ( & sock - > tx_pending_queue ) ;
skb_queue_purge ( & sock - > tx_backlog_queue ) ;
if ( local = = NULL )
return ;
/* Search for local pending SKBs that are related to this socket */
skb_queue_walk_safe ( & local - > tx_queue , s , tmp ) {
if ( s - > sk ! = & sock - > sk )
continue ;
skb_unlink ( s , & local - > tx_queue ) ;
kfree_skb ( s ) ;
}
}
2012-06-22 16:40:34 +04:00
static void nfc_llcp_socket_release ( struct nfc_llcp_local * local , bool listen )
2012-05-04 19:04:19 +04:00
{
struct sock * sk ;
struct hlist_node * node , * tmp ;
struct nfc_llcp_sock * llcp_sock ;
2011-12-14 19:43:12 +04:00
2012-10-26 20:20:10 +04:00
skb_queue_purge ( & local - > tx_queue ) ;
2012-05-04 19:04:19 +04:00
write_lock ( & local - > sockets . lock ) ;
2012-03-05 04:03:51 +04:00
2012-05-04 19:04:19 +04:00
sk_for_each_safe ( sk , node , tmp , & local - > sockets . head ) {
llcp_sock = nfc_llcp_sock ( sk ) ;
2011-12-14 19:43:12 +04:00
2012-09-26 16:22:10 +04:00
bh_lock_sock ( sk ) ;
2011-12-14 19:43:12 +04:00
2012-10-26 20:20:10 +04:00
nfc_llcp_socket_purge ( llcp_sock ) ;
2012-05-04 19:04:19 +04:00
if ( sk - > sk_state = = LLCP_CONNECTED )
nfc_put_device ( llcp_sock - > dev ) ;
2011-12-14 19:43:12 +04:00
2012-05-04 19:04:19 +04:00
if ( sk - > sk_state = = LLCP_LISTEN ) {
2011-12-14 19:43:12 +04:00
struct nfc_llcp_sock * lsk , * n ;
struct sock * accept_sk ;
2012-10-17 17:23:39 +04:00
list_for_each_entry_safe ( lsk , n ,
& llcp_sock - > accept_queue ,
2012-03-05 04:03:52 +04:00
accept_queue ) {
2011-12-14 19:43:12 +04:00
accept_sk = & lsk - > sk ;
2012-09-26 16:22:10 +04:00
bh_lock_sock ( accept_sk ) ;
2011-12-14 19:43:12 +04:00
nfc_llcp_accept_unlink ( accept_sk ) ;
accept_sk - > sk_state = LLCP_CLOSED ;
2012-09-26 16:22:10 +04:00
bh_unlock_sock ( accept_sk ) ;
2011-12-14 19:43:12 +04:00
sock_orphan ( accept_sk ) ;
}
2012-06-22 16:40:34 +04:00
if ( listen = = true ) {
2012-09-26 16:22:10 +04:00
bh_unlock_sock ( sk ) ;
2012-06-22 16:40:34 +04:00
continue ;
}
2011-12-14 19:43:12 +04:00
}
2012-10-15 17:09:52 +04:00
/*
* If we have a connection less socket bound , we keep it alive
* if the device is still present .
*/
if ( sk - > sk_state = = LLCP_BOUND & & sk - > sk_type = = SOCK_DGRAM & &
listen = = true ) {
bh_unlock_sock ( sk ) ;
continue ;
}
2012-05-04 19:04:19 +04:00
sk - > sk_state = LLCP_CLOSED ;
2011-12-14 19:43:12 +04:00
2012-09-26 16:22:10 +04:00
bh_unlock_sock ( sk ) ;
2011-12-14 19:43:12 +04:00
2012-05-04 19:04:19 +04:00
sock_orphan ( sk ) ;
2012-03-05 04:03:51 +04:00
2012-05-04 19:04:19 +04:00
sk_del_node_init ( sk ) ;
2011-12-14 19:43:12 +04:00
}
2012-05-04 19:04:19 +04:00
write_unlock ( & local - > sockets . lock ) ;
2011-12-14 19:43:12 +04:00
}
2012-05-04 13:24:16 +04:00
struct nfc_llcp_local * nfc_llcp_local_get ( struct nfc_llcp_local * local )
{
kref_get ( & local - > ref ) ;
return local ;
}
static void local_release ( struct kref * ref )
{
struct nfc_llcp_local * local ;
local = container_of ( ref , struct nfc_llcp_local , ref ) ;
list_del ( & local - > list ) ;
2012-06-22 16:40:34 +04:00
nfc_llcp_socket_release ( local , false ) ;
2012-05-04 13:24:16 +04:00
del_timer_sync ( & local - > link_timer ) ;
skb_queue_purge ( & local - > tx_queue ) ;
2012-08-23 03:22:16 +04:00
cancel_work_sync ( & local - > tx_work ) ;
cancel_work_sync ( & local - > rx_work ) ;
cancel_work_sync ( & local - > timeout_work ) ;
2012-05-04 13:24:16 +04:00
kfree_skb ( local - > rx_pending ) ;
kfree ( local ) ;
}
int nfc_llcp_local_put ( struct nfc_llcp_local * local )
{
2012-05-04 19:04:19 +04:00
if ( local = = NULL )
return 0 ;
2012-05-04 13:24:16 +04:00
return kref_put ( & local - > ref , local_release ) ;
}
2012-06-25 17:46:28 +04:00
static struct nfc_llcp_sock * nfc_llcp_sock_get ( struct nfc_llcp_local * local ,
u8 ssap , u8 dsap )
{
struct sock * sk ;
struct hlist_node * node ;
2012-10-16 23:15:59 +04:00
struct nfc_llcp_sock * llcp_sock , * tmp_sock ;
2012-06-25 17:46:28 +04:00
pr_debug ( " ssap dsap %d %d \n " , ssap , dsap ) ;
if ( ssap = = 0 & & dsap = = 0 )
return NULL ;
read_lock ( & local - > sockets . lock ) ;
llcp_sock = NULL ;
sk_for_each ( sk , node , & local - > sockets . head ) {
2012-10-16 23:15:59 +04:00
tmp_sock = nfc_llcp_sock ( sk ) ;
2012-06-25 17:46:28 +04:00
2012-10-16 23:15:59 +04:00
if ( tmp_sock - > ssap = = ssap & & tmp_sock - > dsap = = dsap ) {
llcp_sock = tmp_sock ;
2012-06-25 17:46:28 +04:00
break ;
2012-10-16 23:15:59 +04:00
}
2012-06-25 17:46:28 +04:00
}
read_unlock ( & local - > sockets . lock ) ;
if ( llcp_sock = = NULL )
return NULL ;
sock_hold ( & llcp_sock - > sk ) ;
return llcp_sock ;
}
static void nfc_llcp_sock_put ( struct nfc_llcp_sock * sock )
{
sock_put ( & sock - > sk ) ;
}
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 " ) ;
2012-10-03 03:01:31 +04:00
schedule_work ( & local - > timeout_work ) ;
2011-12-14 19:43:12 +04:00
}
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 ;
}
2012-06-25 17:46:28 +04:00
static
struct nfc_llcp_sock * nfc_llcp_sock_from_sn ( struct nfc_llcp_local * local ,
u8 * sn , size_t sn_len )
{
struct sock * sk ;
struct hlist_node * node ;
struct nfc_llcp_sock * llcp_sock , * tmp_sock ;
pr_debug ( " sn %zd %p \n " , sn_len , sn ) ;
if ( sn = = NULL | | sn_len = = 0 )
return NULL ;
read_lock ( & local - > sockets . lock ) ;
llcp_sock = NULL ;
sk_for_each ( sk , node , & local - > sockets . head ) {
tmp_sock = nfc_llcp_sock ( sk ) ;
pr_debug ( " llcp sock %p \n " , tmp_sock ) ;
2012-10-15 17:08:29 +04:00
if ( tmp_sock - > sk . sk_type = = SOCK_STREAM & &
tmp_sock - > sk . sk_state ! = LLCP_LISTEN )
continue ;
if ( tmp_sock - > sk . sk_type = = SOCK_DGRAM & &
tmp_sock - > sk . sk_state ! = LLCP_BOUND )
2012-06-25 17:46:28 +04:00
continue ;
if ( tmp_sock - > service_name = = NULL | |
tmp_sock - > service_name_len = = 0 )
continue ;
if ( tmp_sock - > service_name_len ! = sn_len )
continue ;
if ( memcmp ( sn , tmp_sock - > service_name , sn_len ) = = 0 ) {
llcp_sock = tmp_sock ;
break ;
}
}
read_unlock ( & local - > sockets . lock ) ;
pr_debug ( " Found llcp sock %p \n " , llcp_sock ) ;
return llcp_sock ;
}
2011-12-14 19:43:12 +04:00
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 ;
}
/*
2012-06-25 17:46:28 +04:00
* Check if there already is a non WKS socket bound
* to this service name .
2011-12-14 19:43:12 +04:00
*/
2012-06-25 17:46:28 +04:00
if ( nfc_llcp_sock_from_sn ( local , sock - > service_name ,
sock - > service_name_len ) ! = NULL ) {
2011-12-14 19:43:12 +04:00
mutex_unlock ( & local - > sdp_lock ) ;
return LLCP_SAP_MAX ;
}
mutex_unlock ( & local - > sdp_lock ) ;
2012-06-25 17:46:28 +04:00
return LLCP_SDP_UNBOUND ;
2011-12-14 19:43:12 +04:00
2012-06-22 19:15:20 +04:00
} else if ( sock - > ssap ! = 0 & & sock - > ssap < LLCP_WKS_NUM_SAP ) {
if ( ! test_bit ( sock - > ssap , & local - > local_wks ) ) {
set_bit ( sock - > ssap , & local - > local_wks ) ;
mutex_unlock ( & local - > sdp_lock ) ;
2011-12-14 19:43:12 +04:00
2012-06-22 19:15:20 +04:00
return sock - > ssap ;
2011-12-14 19:43:12 +04:00
}
}
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 ) {
2012-06-25 17:46:28 +04:00
atomic_t * client_cnt ;
2011-12-14 19:43:12 +04:00
local_ssap = ssap - LLCP_WKS_NUM_SAP ;
sdp = & local - > local_sdp ;
2012-06-25 17:46:28 +04:00
client_cnt = & local - > local_sdp_cnt [ local_ssap ] ;
pr_debug ( " %d clients \n " , atomic_read ( client_cnt ) ) ;
mutex_lock ( & local - > sdp_lock ) ;
if ( atomic_dec_and_test ( client_cnt ) ) {
struct nfc_llcp_sock * l_sock ;
pr_debug ( " No more clients for SAP %d \n " , ssap ) ;
clear_bit ( local_ssap , sdp ) ;
/* Find the listening sock and set it back to UNBOUND */
l_sock = nfc_llcp_sock_get ( local , ssap , LLCP_SAP_SDP ) ;
if ( l_sock ) {
l_sock - > ssap = LLCP_SDP_UNBOUND ;
nfc_llcp_sock_put ( l_sock ) ;
}
}
mutex_unlock ( & local - > sdp_lock ) ;
return ;
2011-12-14 19:43:12 +04:00
} 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-06-25 17:46:28 +04:00
static u8 nfc_llcp_reserve_sdp_ssap ( struct nfc_llcp_local * local )
{
u8 ssap ;
mutex_lock ( & local - > sdp_lock ) ;
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 ) ;
set_bit ( ssap , & local - > local_sdp ) ;
mutex_unlock ( & local - > sdp_lock ) ;
return LLCP_WKS_NUM_SAP + ssap ;
}
2011-12-14 19:43:12 +04:00
static int nfc_llcp_build_gb ( struct nfc_llcp_local * local )
{
u8 * gb_cur , * version_tlv , version , version_length ;
2012-10-17 16:43:39 +04:00
u8 * lto_tlv , lto_length ;
2011-12-14 19:43:12 +04:00
u8 * wks_tlv , wks_length ;
2012-04-10 21:43:19 +04:00
u8 * miux_tlv , miux_length ;
2011-12-14 19:43:12 +04:00
u8 gb_len = 0 ;
2012-09-02 17:21:46 +04:00
int ret = 0 ;
2011-12-14 19:43:12 +04:00
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 ;
2012-10-17 16:43:39 +04:00
lto_tlv = nfc_llcp_build_tlv ( LLCP_TLV_LTO , & local - > 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-10-17 16:43:39 +04:00
miux_tlv = nfc_llcp_build_tlv ( LLCP_TLV_MIUX , ( u8 * ) & local - > miux , 0 ,
2012-04-10 21:43:19 +04:00
& 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 ) {
2012-09-02 17:21:46 +04:00
ret = - EINVAL ;
goto out ;
2011-12-14 19:43:12 +04:00
}
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 ;
2012-09-02 17:21:46 +04:00
local - > gb_len = gb_len ;
out :
2011-12-14 19:43:12 +04:00
kfree ( version_tlv ) ;
kfree ( lto_tlv ) ;
2012-09-02 17:21:46 +04:00
kfree ( wks_tlv ) ;
kfree ( miux_tlv ) ;
2011-12-14 19:43:12 +04:00
2012-09-02 17:21:46 +04:00
return ret ;
2011-12-14 19:43:12 +04:00
}
2012-06-22 04:04:53 +04:00
u8 * nfc_llcp_general_bytes ( struct nfc_dev * dev , size_t * general_bytes_len )
{
struct nfc_llcp_local * local ;
local = nfc_llcp_find_local ( dev ) ;
if ( local = = NULL ) {
* general_bytes_len = 0 ;
return NULL ;
}
nfc_llcp_build_gb ( local ) ;
* general_bytes_len = local - > gb_len ;
return local - > gb ;
}
2011-12-14 19:43:12 +04:00
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 ;
}
2012-05-08 00:03:34 +04:00
return nfc_llcp_parse_gb_tlv ( local ,
& local - > remote_gb [ 3 ] ,
local - > remote_gb_len - 3 ) ;
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 ;
}
2012-09-26 20:16:44 +04:00
void nfc_llcp_send_to_raw_sock ( struct nfc_llcp_local * local ,
struct sk_buff * skb , u8 direction )
{
struct hlist_node * node ;
struct sk_buff * skb_copy = NULL , * nskb ;
struct sock * sk ;
u8 * data ;
read_lock ( & local - > raw_sockets . lock ) ;
sk_for_each ( sk , node , & local - > raw_sockets . head ) {
if ( sk - > sk_state ! = LLCP_BOUND )
continue ;
if ( skb_copy = = NULL ) {
skb_copy = __pskb_copy ( skb , NFC_LLCP_RAW_HEADER_SIZE ,
GFP_ATOMIC ) ;
if ( skb_copy = = NULL )
continue ;
data = skb_push ( skb_copy , NFC_LLCP_RAW_HEADER_SIZE ) ;
data [ 0 ] = local - > dev ? local - > dev - > idx : 0xFF ;
data [ 1 ] = direction ;
}
nskb = skb_clone ( skb_copy , GFP_ATOMIC ) ;
if ( ! nskb )
continue ;
if ( sock_queue_rcv_skb ( sk , nskb ) )
kfree_skb ( nskb ) ;
}
read_unlock ( & local - > raw_sockets . lock ) ;
kfree_skb ( skb_copy ) ;
}
2012-05-30 19:48:29 +04:00
static void nfc_llcp_tx_work ( struct work_struct * work )
{
struct nfc_llcp_local * local = container_of ( work , struct nfc_llcp_local ,
tx_work ) ;
struct sk_buff * skb ;
struct sock * sk ;
struct nfc_llcp_sock * llcp_sock ;
skb = skb_dequeue ( & local - > tx_queue ) ;
if ( skb ! = NULL ) {
sk = skb - > sk ;
llcp_sock = nfc_llcp_sock ( sk ) ;
2012-10-05 02:37:22 +04:00
if ( llcp_sock = = NULL & & nfc_llcp_ptype ( skb ) = = LLCP_PDU_I ) {
nfc_llcp_send_symm ( local - > dev ) ;
} else {
2012-05-30 19:48:29 +04:00
int ret ;
pr_debug ( " Sending pending skb \n " ) ;
print_hex_dump ( KERN_DEBUG , " LLCP Tx: " ,
DUMP_PREFIX_OFFSET , 16 , 1 ,
skb - > data , skb - > len , true ) ;
2012-09-26 20:16:44 +04:00
nfc_llcp_send_to_raw_sock ( local , skb ,
NFC_LLCP_DIRECTION_TX ) ;
2012-05-30 19:48:29 +04:00
ret = nfc_data_exchange ( local - > dev , local - > target_idx ,
skb , nfc_llcp_recv , local ) ;
if ( ! ret & & nfc_llcp_ptype ( skb ) = = LLCP_PDU_I ) {
skb = skb_get ( skb ) ;
skb_queue_tail ( & llcp_sock - > tx_pending_queue ,
skb ) ;
}
}
} else {
nfc_llcp_send_symm ( local - > dev ) ;
}
mod_timer ( & local - > link_timer ,
jiffies + msecs_to_jiffies ( 2 * local - > remote_lto ) ) ;
}
2012-05-04 19:04:19 +04:00
static struct nfc_llcp_sock * nfc_llcp_connecting_sock_get ( struct nfc_llcp_local * local ,
u8 ssap )
{
struct sock * sk ;
struct nfc_llcp_sock * llcp_sock ;
struct hlist_node * node ;
read_lock ( & local - > connecting_sockets . lock ) ;
sk_for_each ( sk , node , & local - > connecting_sockets . head ) {
llcp_sock = nfc_llcp_sock ( sk ) ;
2012-05-21 13:44:42 +04:00
if ( llcp_sock - > ssap = = ssap ) {
sock_hold ( & llcp_sock - > sk ) ;
2012-05-04 19:04:19 +04:00
goto out ;
2012-05-21 13:44:42 +04:00
}
2012-05-04 19:04:19 +04:00
}
llcp_sock = NULL ;
out :
read_unlock ( & local - > connecting_sockets . lock ) ;
return llcp_sock ;
}
static struct nfc_llcp_sock * nfc_llcp_sock_get_sn ( struct nfc_llcp_local * local ,
u8 * sn , size_t sn_len )
{
struct nfc_llcp_sock * llcp_sock ;
2012-06-25 17:46:28 +04:00
llcp_sock = nfc_llcp_sock_from_sn ( local , sn , sn_len ) ;
2011-12-14 19:43:12 +04:00
2012-05-04 19:04:19 +04:00
if ( llcp_sock = = NULL )
return NULL ;
2011-12-14 19:43:12 +04:00
2012-05-04 19:04:19 +04:00
sock_hold ( & llcp_sock - > sk ) ;
return llcp_sock ;
2011-12-14 19:43:12 +04:00
}
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 ;
}
2012-10-15 18:14:37 +04:00
static void nfc_llcp_recv_ui ( struct nfc_llcp_local * local ,
struct sk_buff * skb )
{
struct nfc_llcp_sock * llcp_sock ;
struct nfc_llcp_ui_cb * ui_cb ;
u8 dsap , ssap ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
ui_cb = nfc_llcp_ui_skb_cb ( skb ) ;
ui_cb - > dsap = dsap ;
ui_cb - > ssap = ssap ;
printk ( " %s %d %d \n " , __func__ , dsap , ssap ) ;
pr_debug ( " %d %d \n " , dsap , ssap ) ;
/* We're looking for a bound socket, not a client one */
llcp_sock = nfc_llcp_sock_get ( local , dsap , LLCP_SAP_SDP ) ;
if ( llcp_sock = = NULL | | llcp_sock - > sk . sk_type ! = SOCK_DGRAM )
return ;
/* There is no sequence with UI frames */
skb_pull ( skb , LLCP_HEADER_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 ) ;
}
nfc_llcp_sock_put ( llcp_sock ) ;
}
2011-12-14 19:43:12 +04:00
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 ;
2012-05-04 19:04:19 +04:00
u8 dsap , ssap , reason ;
2011-12-14 19:43:12 +04:00
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
pr_debug ( " %d %d \n " , dsap , ssap ) ;
if ( dsap ! = LLCP_SAP_SDP ) {
2012-05-04 19:04:19 +04:00
sock = nfc_llcp_sock_get ( local , dsap , LLCP_SAP_SDP ) ;
if ( sock = = NULL | | sock - > sk . sk_state ! = LLCP_LISTEN ) {
2011-12-14 19:43:12 +04:00
reason = LLCP_DM_NOBOUND ;
goto fail ;
}
} 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 ) ;
2012-05-04 19:04:19 +04:00
sock = nfc_llcp_sock_get_sn ( local , sn , sn_len ) ;
if ( sock = = NULL ) {
reason = LLCP_DM_NOBOUND ;
goto fail ;
2011-12-14 19:43:12 +04:00
}
}
2012-05-04 19:04:19 +04:00
lock_sock ( & sock - > sk ) ;
2011-12-14 19:43:12 +04:00
parent = & sock - > sk ;
if ( sk_acceptq_is_full ( parent ) ) {
reason = LLCP_DM_REJ ;
release_sock ( & sock - > sk ) ;
sock_put ( & sock - > sk ) ;
goto fail ;
}
2012-06-25 17:46:28 +04:00
if ( sock - > ssap = = LLCP_SDP_UNBOUND ) {
u8 ssap = nfc_llcp_reserve_sdp_ssap ( local ) ;
pr_debug ( " First client, reserving %d \n " , ssap ) ;
if ( ssap = = LLCP_SAP_MAX ) {
reason = LLCP_DM_REJ ;
release_sock ( & sock - > sk ) ;
sock_put ( & sock - > sk ) ;
goto fail ;
}
sock - > ssap = ssap ;
}
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 ;
2012-05-04 13:24:16 +04:00
new_sock - > local = nfc_llcp_local_get ( local ) ;
2012-05-14 19:37:32 +04:00
new_sock - > miu = local - > remote_miu ;
2011-12-14 19:43:12 +04:00
new_sock - > nfc_protocol = sock - > nfc_protocol ;
new_sock - > dsap = ssap ;
2012-06-18 23:38:09 +04:00
new_sock - > target_idx = local - > target_idx ;
2011-12-14 19:43:12 +04:00
new_sock - > parent = parent ;
2012-06-25 17:46:28 +04:00
new_sock - > ssap = sock - > ssap ;
if ( sock - > ssap < LLCP_LOCAL_NUM_SAP & & sock - > ssap > = LLCP_WKS_NUM_SAP ) {
atomic_t * client_count ;
pr_debug ( " reserved_ssap %d for %p \n " , sock - > ssap , new_sock ) ;
client_count =
& local - > local_sdp_cnt [ sock - > ssap - LLCP_WKS_NUM_SAP ] ;
atomic_inc ( client_count ) ;
new_sock - > reserved_ssap = sock - > ssap ;
}
2011-12-14 19:43:12 +04:00
2012-05-08 00:03:34 +04:00
nfc_llcp_parse_connection_tlv ( new_sock , & skb - > data [ LLCP_HEADER_SIZE ] ,
skb - > len - LLCP_HEADER_SIZE ) ;
2011-12-14 19:43:12 +04:00
pr_debug ( " new sock %p sk %p \n " , new_sock , & new_sock - > sk ) ;
2012-05-04 19:04:19 +04:00
nfc_llcp_sock_link ( & local - > sockets , new_sk ) ;
2011-12-14 19:43:12 +04:00
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 ) ;
}
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 ) ,
2012-05-08 00:03:34 +04:00
sock - > rw ) ;
2012-03-05 04:03:35 +04:00
/* Try to queue some I frames for transmission */
while ( sock - > remote_ready & &
2012-05-08 00:03:34 +04:00
skb_queue_len ( & sock - > tx_pending_queue ) < sock - > rw ) {
2012-05-30 19:48:29 +04:00
struct sk_buff * pdu ;
2012-03-05 04:03:35 +04:00
pdu = skb_dequeue ( & sock - > tx_queue ) ;
if ( pdu = = NULL )
break ;
/* Update N(S)/N(R) */
nfc_llcp_set_nrns ( sock , pdu ) ;
skb_queue_tail ( & local - > tx_queue , 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 ;
2012-05-30 19:48:29 +04:00
/* Remove and free all skbs until ns == nr */
skb_queue_walk_safe ( & llcp_sock - > tx_pending_queue , s , tmp ) {
skb_unlink ( s , & llcp_sock - > tx_pending_queue ) ;
kfree_skb ( s ) ;
if ( nfc_llcp_ns ( s ) = = nr )
break ;
}
/* Re-queue the remaining skbs for transmission */
skb_queue_reverse_walk_safe ( & llcp_sock - > tx_pending_queue ,
s , tmp ) {
skb_unlink ( s , & llcp_sock - > tx_pending_queue ) ;
skb_queue_head ( & local - > tx_queue , s ) ;
}
2011-12-14 19:43:12 +04:00
}
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-05-30 20:06:11 +04:00
if ( nfc_llcp_queue_i_frames ( llcp_sock ) = = 0 & & ptype = = LLCP_PDU_I )
2012-03-05 04:03:42 +04:00
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 ) ;
2012-10-26 20:20:10 +04:00
nfc_llcp_socket_purge ( llcp_sock ) ;
2011-12-14 19:43:12 +04:00
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 ;
2012-05-07 14:31:19 +04:00
struct sock * sk ;
2011-12-14 19:43:12 +04:00
u8 dsap , ssap ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
2012-05-04 19:04:19 +04:00
llcp_sock = nfc_llcp_connecting_sock_get ( local , dsap ) ;
2011-12-14 19:43:12 +04:00
if ( llcp_sock = = NULL ) {
pr_err ( " Invalid CC \n " ) ;
nfc_llcp_send_dm ( local , dsap , ssap , LLCP_DM_NOCONN ) ;
return ;
}
2012-05-07 14:31:19 +04:00
sk = & llcp_sock - > sk ;
2011-12-14 19:43:12 +04:00
2012-05-04 19:04:19 +04:00
/* Unlink from connecting and link to the client array */
nfc_llcp_sock_unlink ( & local - > connecting_sockets , sk ) ;
nfc_llcp_sock_link ( & local - > sockets , sk ) ;
llcp_sock - > dsap = ssap ;
2012-05-08 00:03:34 +04:00
nfc_llcp_parse_connection_tlv ( llcp_sock , & skb - > data [ LLCP_HEADER_SIZE ] ,
skb - > len - LLCP_HEADER_SIZE ) ;
2011-12-14 19:43:12 +04:00
2012-05-07 14:31:19 +04:00
sk - > sk_state = LLCP_CONNECTED ;
sk - > sk_state_change ( sk ) ;
2011-12-14 19:43:12 +04:00
nfc_llcp_sock_put ( llcp_sock ) ;
}
2012-06-26 18:13:29 +04:00
static void nfc_llcp_recv_dm ( struct nfc_llcp_local * local , struct sk_buff * skb )
{
struct nfc_llcp_sock * llcp_sock ;
struct sock * sk ;
u8 dsap , ssap , reason ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
reason = skb - > data [ 2 ] ;
pr_debug ( " %d %d reason %d \n " , ssap , dsap , reason ) ;
switch ( reason ) {
case LLCP_DM_NOBOUND :
case LLCP_DM_REJ :
llcp_sock = nfc_llcp_connecting_sock_get ( local , dsap ) ;
break ;
default :
llcp_sock = nfc_llcp_sock_get ( local , dsap , ssap ) ;
break ;
}
if ( llcp_sock = = NULL ) {
2012-10-16 23:15:59 +04:00
pr_debug ( " Already closed \n " ) ;
2012-06-26 18:13:29 +04:00
return ;
}
sk = & llcp_sock - > sk ;
sk - > sk_err = ENXIO ;
sk - > sk_state = LLCP_CLOSED ;
sk - > sk_state_change ( sk ) ;
nfc_llcp_sock_put ( llcp_sock ) ;
}
2012-10-05 03:21:47 +04:00
static void nfc_llcp_recv_snl ( struct nfc_llcp_local * local ,
struct sk_buff * skb )
{
struct nfc_llcp_sock * llcp_sock ;
u8 dsap , ssap , * tlv , type , length , tid , sap ;
u16 tlv_len , offset ;
char * service_name ;
size_t service_name_len ;
dsap = nfc_llcp_dsap ( skb ) ;
ssap = nfc_llcp_ssap ( skb ) ;
pr_debug ( " %d %d \n " , dsap , ssap ) ;
if ( dsap ! = LLCP_SAP_SDP | | ssap ! = LLCP_SAP_SDP ) {
pr_err ( " Wrong SNL SAP \n " ) ;
return ;
}
tlv = & skb - > data [ LLCP_HEADER_SIZE ] ;
tlv_len = skb - > len - LLCP_HEADER_SIZE ;
offset = 0 ;
2012-10-17 17:23:39 +04:00
while ( offset < tlv_len ) {
2012-10-05 03:21:47 +04:00
type = tlv [ 0 ] ;
length = tlv [ 1 ] ;
switch ( type ) {
case LLCP_TLV_SDREQ :
tid = tlv [ 2 ] ;
service_name = ( char * ) & tlv [ 3 ] ;
service_name_len = length - 1 ;
2012-10-15 16:28:13 +04:00
pr_debug ( " Looking for %.16s \n " , service_name ) ;
2012-10-05 03:21:47 +04:00
if ( service_name_len = = strlen ( " urn:nfc:sn:sdp " ) & &
! strncmp ( service_name , " urn:nfc:sn:sdp " ,
service_name_len ) ) {
sap = 1 ;
2012-10-15 16:28:13 +04:00
goto send_snl ;
}
llcp_sock = nfc_llcp_sock_from_sn ( local , service_name ,
service_name_len ) ;
if ( ! llcp_sock ) {
sap = 0 ;
goto send_snl ;
}
/*
* We found a socket but its ssap has not been reserved
* yet . We need to assign it for good and send a reply .
* The ssap will be freed when the socket is closed .
*/
if ( llcp_sock - > ssap = = LLCP_SDP_UNBOUND ) {
atomic_t * client_count ;
sap = nfc_llcp_reserve_sdp_ssap ( local ) ;
pr_debug ( " Reserving %d \n " , sap ) ;
if ( sap = = LLCP_SAP_MAX ) {
sap = 0 ;
goto send_snl ;
}
client_count =
& local - > local_sdp_cnt [ sap -
LLCP_WKS_NUM_SAP ] ;
atomic_inc ( client_count ) ;
llcp_sock - > ssap = sap ;
llcp_sock - > reserved_ssap = sap ;
2012-10-05 03:21:47 +04:00
} else {
2012-10-15 16:28:13 +04:00
sap = llcp_sock - > ssap ;
2012-10-05 03:21:47 +04:00
}
2012-10-15 16:28:13 +04:00
pr_debug ( " %p %d \n " , llcp_sock , sap ) ;
2012-10-17 17:23:39 +04:00
send_snl :
2012-10-05 03:21:47 +04:00
nfc_llcp_send_snl ( local , tid , sap ) ;
break ;
default :
pr_err ( " Invalid SNL tlv value 0x%x \n " , type ) ;
break ;
}
offset + = length + 2 ;
tlv + = length + 2 ;
}
}
2011-12-14 19:43:12 +04:00
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 ) ;
2012-09-26 20:16:44 +04:00
nfc_llcp_send_to_raw_sock ( local , skb , NFC_LLCP_DIRECTION_RX ) ;
2011-12-14 19:43:12 +04:00
switch ( ptype ) {
case LLCP_PDU_SYMM :
pr_debug ( " SYMM \n " ) ;
break ;
2012-10-15 18:14:37 +04:00
case LLCP_PDU_UI :
pr_debug ( " UI \n " ) ;
nfc_llcp_recv_ui ( local , skb ) ;
break ;
2011-12-14 19:43:12 +04:00
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 ;
2012-06-26 18:13:29 +04:00
case LLCP_PDU_DM :
pr_debug ( " DM \n " ) ;
nfc_llcp_recv_dm ( local , skb ) ;
break ;
2012-10-05 03:21:47 +04:00
case LLCP_PDU_SNL :
pr_debug ( " SNL \n " ) ;
nfc_llcp_recv_snl ( local , skb ) ;
break ;
2011-12-14 19:43:12 +04:00
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 ;
}
2012-10-03 03:01:31 +04:00
schedule_work ( & local - > tx_work ) ;
2011-12-14 19:43:12 +04:00
kfree_skb ( local - > rx_pending ) ;
local - > rx_pending = NULL ;
}
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 ) ;
2012-10-03 03:01:31 +04:00
schedule_work ( & local - > rx_work ) ;
2011-12-14 19:43:12 +04:00
}
2012-05-31 02:05:50 +04:00
int nfc_llcp_data_received ( struct nfc_dev * dev , struct sk_buff * skb )
{
struct nfc_llcp_local * local ;
local = nfc_llcp_find_local ( dev ) ;
if ( local = = NULL )
return - ENODEV ;
local - > rx_pending = skb_get ( skb ) ;
del_timer ( & local - > link_timer ) ;
2012-10-03 03:01:31 +04:00
schedule_work ( & local - > rx_work ) ;
2012-05-31 02:05:50 +04:00
return 0 ;
}
2011-12-14 19:43:12 +04:00
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 ;
/* Close and purge all existing sockets */
2012-06-22 16:40:34 +04:00
nfc_llcp_socket_release ( local , true ) ;
2011-12-14 19:43:12 +04:00
}
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 " ) ;
2012-10-03 03:01:31 +04:00
schedule_work ( & local - > tx_work ) ;
2011-12-14 19:43:12 +04:00
} 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 nfc_llcp_local * local ;
local = kzalloc ( sizeof ( struct nfc_llcp_local ) , GFP_KERNEL ) ;
if ( local = = NULL )
return - ENOMEM ;
local - > dev = ndev ;
INIT_LIST_HEAD ( & local - > list ) ;
2012-05-04 13:24:16 +04:00
kref_init ( & local - > ref ) ;
2011-12-14 19:43:12 +04:00
mutex_init ( & local - > sdp_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 ) ;
local - > rx_pending = NULL ;
INIT_WORK ( & local - > rx_work , nfc_llcp_rx_work ) ;
INIT_WORK ( & local - > timeout_work , nfc_llcp_timeout_work ) ;
2012-09-25 14:42:50 +04:00
rwlock_init ( & local - > sockets . lock ) ;
rwlock_init ( & local - > connecting_sockets . lock ) ;
2012-09-26 20:16:44 +04:00
rwlock_init ( & local - > raw_sockets . lock ) ;
2012-05-04 19:04:19 +04:00
2012-10-17 16:43:39 +04:00
local - > lto = 150 ; /* 1500 ms */
local - > rw = LLCP_MAX_RW ;
local - > miux = cpu_to_be16 ( LLCP_MAX_MIUX ) ;
2011-12-14 19:43:12 +04:00
nfc_llcp_build_gb ( local ) ;
local - > remote_miu = LLCP_DEFAULT_MIU ;
local - > remote_lto = LLCP_DEFAULT_LTO ;
list_add ( & llcp_devices , & local - > list ) ;
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 ;
}
2012-05-04 13:24:16 +04:00
nfc_llcp_local_put ( local ) ;
2011-12-14 19:43:12 +04:00
}
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 ( ) ;
}