2011-07-02 02:31:36 +04:00
/*
* Copyright ( C ) 2011 Instituto Nokia de Tecnologia
*
* Authors :
* Aloisio Almeida Jr < aloisio . almeida @ openbossa . org >
* Lauro Ramos Venancio < lauro . venancio @ openbossa . org >
*
* 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 .
*/
2011-12-14 19:43:05 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
2011-11-29 23:37:32 +04:00
2011-07-02 02:31:36 +04:00
# include <net/tcp_states.h>
# include <linux/nfc.h>
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2011-07-02 02:31:36 +04:00
# include "nfc.h"
static void rawsock_write_queue_purge ( struct sock * sk )
{
2011-11-29 23:37:33 +04:00
pr_debug ( " sk=%p \n " , sk ) ;
2011-07-02 02:31:36 +04:00
spin_lock_bh ( & sk - > sk_write_queue . lock ) ;
__skb_queue_purge ( & sk - > sk_write_queue ) ;
nfc_rawsock ( sk ) - > tx_work_scheduled = false ;
spin_unlock_bh ( & sk - > sk_write_queue . lock ) ;
}
static void rawsock_report_error ( struct sock * sk , int err )
{
2011-11-29 23:37:33 +04:00
pr_debug ( " sk=%p err=%d \n " , sk , err ) ;
2011-07-02 02:31:36 +04:00
sk - > sk_shutdown = SHUTDOWN_MASK ;
sk - > sk_err = - err ;
sk - > sk_error_report ( sk ) ;
rawsock_write_queue_purge ( sk ) ;
}
static int rawsock_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
2011-11-29 23:37:33 +04:00
pr_debug ( " sock=%p \n " , sock ) ;
2011-07-02 02:31:36 +04:00
sock_orphan ( sk ) ;
sock_put ( sk ) ;
return 0 ;
}
static int rawsock_connect ( struct socket * sock , struct sockaddr * _addr ,
2012-03-05 04:03:53 +04:00
int len , int flags )
2011-07-02 02:31:36 +04:00
{
struct sock * sk = sock - > sk ;
struct sockaddr_nfc * addr = ( struct sockaddr_nfc * ) _addr ;
struct nfc_dev * dev ;
int rc = 0 ;
2011-11-29 23:37:33 +04:00
pr_debug ( " sock=%p sk=%p flags=%d \n " , sock , sk , flags ) ;
2011-07-02 02:31:36 +04:00
if ( ! addr | | len < sizeof ( struct sockaddr_nfc ) | |
2012-03-05 04:03:53 +04:00
addr - > sa_family ! = AF_NFC )
2011-07-02 02:31:36 +04:00
return - EINVAL ;
2011-11-29 23:37:33 +04:00
pr_debug ( " addr dev_idx=%u target_idx=%u protocol=%u \n " ,
addr - > dev_idx , addr - > target_idx , addr - > nfc_protocol ) ;
2011-07-02 02:31:36 +04:00
lock_sock ( sk ) ;
if ( sock - > state = = SS_CONNECTED ) {
rc = - EISCONN ;
goto error ;
}
dev = nfc_get_device ( addr - > dev_idx ) ;
if ( ! dev ) {
rc = - ENODEV ;
goto error ;
}
rc = nfc_activate_target ( dev , addr - > target_idx , addr - > nfc_protocol ) ;
if ( rc )
goto put_dev ;
nfc_rawsock ( sk ) - > dev = dev ;
nfc_rawsock ( sk ) - > target_idx = addr - > target_idx ;
sock - > state = SS_CONNECTED ;
sk - > sk_state = TCP_ESTABLISHED ;
sk - > sk_state_change ( sk ) ;
release_sock ( sk ) ;
return 0 ;
put_dev :
nfc_put_device ( dev ) ;
error :
release_sock ( sk ) ;
return rc ;
}
static int rawsock_add_header ( struct sk_buff * skb )
{
2011-08-19 17:47:11 +04:00
* skb_push ( skb , NFC_HEADER_SIZE ) = 0 ;
2011-07-02 02:31:36 +04:00
return 0 ;
}
static void rawsock_data_exchange_complete ( void * context , struct sk_buff * skb ,
2012-03-05 04:03:53 +04:00
int err )
2011-07-02 02:31:36 +04:00
{
struct sock * sk = ( struct sock * ) context ;
BUG_ON ( in_irq ( ) ) ;
2011-11-29 23:37:33 +04:00
pr_debug ( " sk=%p err=%d \n " , sk , err ) ;
2011-07-02 02:31:36 +04:00
if ( err )
goto error ;
err = rawsock_add_header ( skb ) ;
if ( err )
goto error ;
err = sock_queue_rcv_skb ( sk , skb ) ;
if ( err )
goto error ;
spin_lock_bh ( & sk - > sk_write_queue . lock ) ;
if ( ! skb_queue_empty ( & sk - > sk_write_queue ) )
schedule_work ( & nfc_rawsock ( sk ) - > tx_work ) ;
else
nfc_rawsock ( sk ) - > tx_work_scheduled = false ;
spin_unlock_bh ( & sk - > sk_write_queue . lock ) ;
sock_put ( sk ) ;
return ;
error :
rawsock_report_error ( sk , err ) ;
sock_put ( sk ) ;
}
static void rawsock_tx_work ( struct work_struct * work )
{
struct sock * sk = to_rawsock_sk ( work ) ;
struct nfc_dev * dev = nfc_rawsock ( sk ) - > dev ;
u32 target_idx = nfc_rawsock ( sk ) - > target_idx ;
struct sk_buff * skb ;
int rc ;
2011-11-29 23:37:33 +04:00
pr_debug ( " sk=%p target_idx=%u \n " , sk , target_idx ) ;
2011-07-02 02:31:36 +04:00
if ( sk - > sk_shutdown & SEND_SHUTDOWN ) {
rawsock_write_queue_purge ( sk ) ;
return ;
}
skb = skb_dequeue ( & sk - > sk_write_queue ) ;
sock_hold ( sk ) ;
rc = nfc_data_exchange ( dev , target_idx , skb ,
2012-03-05 04:03:53 +04:00
rawsock_data_exchange_complete , sk ) ;
2011-07-02 02:31:36 +04:00
if ( rc ) {
rawsock_report_error ( sk , rc ) ;
sock_put ( sk ) ;
}
}
static int rawsock_sendmsg ( struct kiocb * iocb , struct socket * sock ,
2012-03-05 04:03:53 +04:00
struct msghdr * msg , size_t len )
2011-07-02 02:31:36 +04:00
{
struct sock * sk = sock - > sk ;
2011-08-19 17:47:11 +04:00
struct nfc_dev * dev = nfc_rawsock ( sk ) - > dev ;
2011-07-02 02:31:36 +04:00
struct sk_buff * skb ;
int rc ;
2011-11-29 23:37:33 +04:00
pr_debug ( " sock=%p sk=%p len=%zu \n " , sock , sk , len ) ;
2011-07-02 02:31:36 +04:00
if ( msg - > msg_namelen )
return - EOPNOTSUPP ;
if ( sock - > state ! = SS_CONNECTED )
return - ENOTCONN ;
2011-12-14 19:43:06 +04:00
skb = nfc_alloc_send_skb ( dev , sk , msg - > msg_flags , len , & rc ) ;
if ( skb = = NULL )
2011-07-02 02:31:36 +04:00
return rc ;
rc = memcpy_fromiovec ( skb_put ( skb , len ) , msg - > msg_iov , len ) ;
if ( rc < 0 ) {
kfree_skb ( skb ) ;
return rc ;
}
spin_lock_bh ( & sk - > sk_write_queue . lock ) ;
__skb_queue_tail ( & sk - > sk_write_queue , skb ) ;
if ( ! nfc_rawsock ( sk ) - > tx_work_scheduled ) {
schedule_work ( & nfc_rawsock ( sk ) - > tx_work ) ;
nfc_rawsock ( sk ) - > tx_work_scheduled = true ;
}
spin_unlock_bh ( & sk - > sk_write_queue . lock ) ;
return len ;
}
static int rawsock_recvmsg ( struct kiocb * iocb , struct socket * sock ,
2012-03-05 04:03:53 +04:00
struct msghdr * msg , size_t len , int flags )
2011-07-02 02:31:36 +04:00
{
int noblock = flags & MSG_DONTWAIT ;
struct sock * sk = sock - > sk ;
struct sk_buff * skb ;
int copied ;
int rc ;
2011-11-29 23:37:33 +04:00
pr_debug ( " sock=%p sk=%p len=%zu flags=%d \n " , sock , sk , len , flags ) ;
2011-07-02 02:31:36 +04:00
skb = skb_recv_datagram ( sk , flags , noblock , & rc ) ;
if ( ! skb )
return rc ;
msg - > msg_namelen = 0 ;
copied = skb - > len ;
if ( len < copied ) {
msg - > msg_flags | = MSG_TRUNC ;
copied = len ;
}
rc = skb_copy_datagram_iovec ( skb , 0 , msg - > msg_iov , copied ) ;
skb_free_datagram ( sk , skb ) ;
return rc ? : copied ;
}
static const struct proto_ops rawsock_ops = {
. family = PF_NFC ,
. owner = THIS_MODULE ,
. release = rawsock_release ,
. bind = sock_no_bind ,
. connect = rawsock_connect ,
. socketpair = sock_no_socketpair ,
. accept = sock_no_accept ,
. getname = sock_no_getname ,
. poll = datagram_poll ,
. ioctl = sock_no_ioctl ,
. listen = sock_no_listen ,
. shutdown = sock_no_shutdown ,
. setsockopt = sock_no_setsockopt ,
. getsockopt = sock_no_getsockopt ,
. sendmsg = rawsock_sendmsg ,
. recvmsg = rawsock_recvmsg ,
. mmap = sock_no_mmap ,
} ;
static void rawsock_destruct ( struct sock * sk )
{
2011-11-29 23:37:33 +04:00
pr_debug ( " sk=%p \n " , sk ) ;
2011-07-02 02:31:36 +04:00
if ( sk - > sk_state = = TCP_ESTABLISHED ) {
nfc_deactivate_target ( nfc_rawsock ( sk ) - > dev ,
2012-03-05 04:03:53 +04:00
nfc_rawsock ( sk ) - > target_idx ) ;
2011-07-02 02:31:36 +04:00
nfc_put_device ( nfc_rawsock ( sk ) - > dev ) ;
}
skb_queue_purge ( & sk - > sk_receive_queue ) ;
if ( ! sock_flag ( sk , SOCK_DEAD ) ) {
2011-11-29 23:37:32 +04:00
pr_err ( " Freeing alive NFC raw socket %p \n " , sk ) ;
2011-07-02 02:31:36 +04:00
return ;
}
}
static int rawsock_create ( struct net * net , struct socket * sock ,
2012-03-05 04:03:53 +04:00
const struct nfc_protocol * nfc_proto )
2011-07-02 02:31:36 +04:00
{
struct sock * sk ;
2011-11-29 23:37:33 +04:00
pr_debug ( " sock=%p \n " , sock ) ;
2011-07-02 02:31:36 +04:00
if ( sock - > type ! = SOCK_SEQPACKET )
return - ESOCKTNOSUPPORT ;
sock - > ops = & rawsock_ops ;
2011-12-14 19:43:08 +04:00
sk = sk_alloc ( net , PF_NFC , GFP_ATOMIC , nfc_proto - > proto ) ;
2011-07-02 02:31:36 +04:00
if ( ! sk )
return - ENOMEM ;
sock_init_data ( sock , sk ) ;
sk - > sk_protocol = nfc_proto - > id ;
sk - > sk_destruct = rawsock_destruct ;
sock - > state = SS_UNCONNECTED ;
INIT_WORK ( & nfc_rawsock ( sk ) - > tx_work , rawsock_tx_work ) ;
nfc_rawsock ( sk ) - > tx_work_scheduled = false ;
return 0 ;
}
static struct proto rawsock_proto = {
. name = " NFC_RAW " ,
. owner = THIS_MODULE ,
. obj_size = sizeof ( struct nfc_rawsock ) ,
} ;
static const struct nfc_protocol rawsock_nfc_proto = {
. id = NFC_SOCKPROTO_RAW ,
. proto = & rawsock_proto ,
. owner = THIS_MODULE ,
. create = rawsock_create
} ;
int __init rawsock_init ( void )
{
int rc ;
rc = nfc_proto_register ( & rawsock_nfc_proto ) ;
return rc ;
}
void rawsock_exit ( void )
{
nfc_proto_unregister ( & rawsock_nfc_proto ) ;
}