2007-02-09 17:24:33 +03:00
/*
2005-04-17 02:20:36 +04:00
BlueZ - Bluetooth protocol stack for Linux
Copyright ( C ) 2000 - 2001 Qualcomm Incorporated
Written 2000 , 2001 by Maxim Krasnyansky < maxk @ qualcomm . com >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation ;
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS
OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS .
IN NO EVENT SHALL THE COPYRIGHT HOLDER ( S ) AND AUTHOR ( S ) BE LIABLE FOR ANY
2007-02-09 17:24:33 +03:00
CLAIM , OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES , OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
2005-04-17 02:20:36 +04:00
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
2007-02-09 17:24:33 +03:00
ALL LIABILITY , INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS ,
COPYRIGHTS , TRADEMARKS OR OTHER RIGHTS , RELATING TO USE OF THIS
2005-04-17 02:20:36 +04:00
SOFTWARE IS DISCLAIMED .
*/
/* Bluetooth SCO sockets. */
# include <linux/module.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/fcntl.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/socket.h>
# include <linux/skbuff.h>
2005-11-08 20:57:38 +03:00
# include <linux/device.h>
2010-03-21 07:27:45 +03:00
# include <linux/debugfs.h>
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/list.h>
2011-10-07 13:40:59 +04:00
# include <linux/security.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h>
# include <asm/system.h>
2010-12-01 17:58:22 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
# include <net/bluetooth/sco.h>
2010-12-01 17:58:22 +03:00
static int disable_esco ;
2005-04-17 02:20:36 +04:00
2005-12-22 23:49:22 +03:00
static const struct proto_ops sco_sock_ops ;
2005-04-17 02:20:36 +04:00
static struct bt_sock_list sco_sk_list = {
2008-03-29 02:17:38 +03:00
. lock = __RW_LOCK_UNLOCKED ( sco_sk_list . lock )
2005-04-17 02:20:36 +04:00
} ;
static void __sco_chan_add ( struct sco_conn * conn , struct sock * sk , struct sock * parent ) ;
static void sco_chan_del ( struct sock * sk , int err ) ;
static int sco_conn_del ( struct hci_conn * conn , int err ) ;
static void sco_sock_close ( struct sock * sk ) ;
static void sco_sock_kill ( struct sock * sk ) ;
/* ---- SCO timers ---- */
static void sco_sock_timeout ( unsigned long arg )
{
struct sock * sk = ( struct sock * ) arg ;
BT_DBG ( " sock %p state %d " , sk , sk - > sk_state ) ;
bh_lock_sock ( sk ) ;
sk - > sk_err = ETIMEDOUT ;
sk - > sk_state_change ( sk ) ;
bh_unlock_sock ( sk ) ;
sco_sock_kill ( sk ) ;
sock_put ( sk ) ;
}
static void sco_sock_set_timer ( struct sock * sk , long timeout )
{
BT_DBG ( " sock %p state %d timeout %ld " , sk , sk - > sk_state , timeout ) ;
sk_reset_timer ( sk , & sk - > sk_timer , jiffies + timeout ) ;
}
static void sco_sock_clear_timer ( struct sock * sk )
{
BT_DBG ( " sock %p state %d " , sk , sk - > sk_state ) ;
sk_stop_timer ( sk , & sk - > sk_timer ) ;
}
/* ---- SCO connections ---- */
static struct sco_conn * sco_conn_add ( struct hci_conn * hcon , __u8 status )
{
struct hci_dev * hdev = hcon - > hdev ;
2006-07-06 17:40:09 +04:00
struct sco_conn * conn = hcon - > sco_data ;
2005-04-17 02:20:36 +04:00
2006-07-06 17:40:09 +04:00
if ( conn | | status )
2005-04-17 02:20:36 +04:00
return conn ;
2006-07-06 17:40:09 +04:00
conn = kzalloc ( sizeof ( struct sco_conn ) , GFP_ATOMIC ) ;
if ( ! conn )
2005-04-17 02:20:36 +04:00
return NULL ;
spin_lock_init ( & conn - > lock ) ;
hcon - > sco_data = conn ;
conn - > hcon = hcon ;
conn - > src = & hdev - > bdaddr ;
conn - > dst = & hcon - > dst ;
if ( hdev - > sco_mtu > 0 )
conn - > mtu = hdev - > sco_mtu ;
else
conn - > mtu = 60 ;
BT_DBG ( " hcon %p conn %p " , hcon , conn ) ;
2006-07-06 17:40:09 +04:00
2005-04-17 02:20:36 +04:00
return conn ;
}
static inline struct sock * sco_chan_get ( struct sco_conn * conn )
{
struct sock * sk = NULL ;
sco_conn_lock ( conn ) ;
sk = conn - > sk ;
sco_conn_unlock ( conn ) ;
return sk ;
}
static int sco_conn_del ( struct hci_conn * hcon , int err )
{
2010-12-01 17:58:22 +03:00
struct sco_conn * conn = hcon - > sco_data ;
2005-04-17 02:20:36 +04:00
struct sock * sk ;
2010-12-01 17:58:22 +03:00
if ( ! conn )
2005-04-17 02:20:36 +04:00
return 0 ;
BT_DBG ( " hcon %p conn %p, err %d " , hcon , conn , err ) ;
/* Kill socket */
2010-12-01 17:58:22 +03:00
sk = sco_chan_get ( conn ) ;
if ( sk ) {
2005-04-17 02:20:36 +04:00
bh_lock_sock ( sk ) ;
sco_sock_clear_timer ( sk ) ;
sco_chan_del ( sk , err ) ;
bh_unlock_sock ( sk ) ;
sco_sock_kill ( sk ) ;
}
hcon - > sco_data = NULL ;
kfree ( conn ) ;
return 0 ;
}
static inline int sco_chan_add ( struct sco_conn * conn , struct sock * sk , struct sock * parent )
{
int err = 0 ;
sco_conn_lock ( conn ) ;
2010-05-01 23:15:35 +04:00
if ( conn - > sk )
2005-04-17 02:20:36 +04:00
err = - EBUSY ;
2010-05-01 23:15:35 +04:00
else
2005-04-17 02:20:36 +04:00
__sco_chan_add ( conn , sk , parent ) ;
2010-05-01 23:15:35 +04:00
2005-04-17 02:20:36 +04:00
sco_conn_unlock ( conn ) ;
return err ;
}
static int sco_connect ( struct sock * sk )
{
bdaddr_t * src = & bt_sk ( sk ) - > src ;
bdaddr_t * dst = & bt_sk ( sk ) - > dst ;
struct sco_conn * conn ;
struct hci_conn * hcon ;
struct hci_dev * hdev ;
2007-10-20 16:55:10 +04:00
int err , type ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " %s -> %s " , batostr ( src ) , batostr ( dst ) ) ;
2010-12-01 17:58:22 +03:00
hdev = hci_get_route ( dst , src ) ;
if ( ! hdev )
2005-04-17 02:20:36 +04:00
return - EHOSTUNREACH ;
hci_dev_lock_bh ( hdev ) ;
2008-07-14 22:13:53 +04:00
if ( lmp_esco_capable ( hdev ) & & ! disable_esco )
type = ESCO_LINK ;
else
type = SCO_LINK ;
2007-10-20 16:55:10 +04:00
2009-01-15 23:58:04 +03:00
hcon = hci_connect ( hdev , type , dst , BT_SECURITY_LOW , HCI_AT_NO_BONDING ) ;
2011-02-22 22:10:53 +03:00
if ( IS_ERR ( hcon ) ) {
err = PTR_ERR ( hcon ) ;
2005-04-17 02:20:36 +04:00
goto done ;
2011-02-22 22:10:53 +03:00
}
2005-04-17 02:20:36 +04:00
conn = sco_conn_add ( hcon , 0 ) ;
if ( ! conn ) {
hci_conn_put ( hcon ) ;
2011-02-22 22:10:53 +03:00
err = - ENOMEM ;
2005-04-17 02:20:36 +04:00
goto done ;
}
/* Update source addr of the socket */
bacpy ( src , conn - > src ) ;
err = sco_chan_add ( conn , sk , NULL ) ;
if ( err )
goto done ;
if ( hcon - > state = = BT_CONNECTED ) {
sco_sock_clear_timer ( sk ) ;
sk - > sk_state = BT_CONNECTED ;
} else {
sk - > sk_state = BT_CONNECT ;
sco_sock_set_timer ( sk , sk - > sk_sndtimeo ) ;
}
2007-10-20 16:55:10 +04:00
2005-04-17 02:20:36 +04:00
done :
hci_dev_unlock_bh ( hdev ) ;
hci_dev_put ( hdev ) ;
return err ;
}
static inline int sco_send_frame ( struct sock * sk , struct msghdr * msg , int len )
{
struct sco_conn * conn = sco_pi ( sk ) - > conn ;
struct sk_buff * skb ;
int err , count ;
/* Check outgoing MTU */
if ( len > conn - > mtu )
return - EINVAL ;
BT_DBG ( " sk %p len %d " , sk , len ) ;
count = min_t ( unsigned int , conn - > mtu , len ) ;
2010-05-01 23:15:35 +04:00
skb = bt_skb_send_alloc ( sk , count ,
msg - > msg_flags & MSG_DONTWAIT , & err ) ;
if ( ! skb )
2005-04-17 02:20:36 +04:00
return err ;
if ( memcpy_fromiovec ( skb_put ( skb , count ) , msg - > msg_iov , count ) ) {
2010-05-01 23:15:35 +04:00
kfree_skb ( skb ) ;
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}
2010-05-01 23:15:35 +04:00
hci_send_sco ( conn - > hcon , skb ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
static inline void sco_recv_frame ( struct sco_conn * conn , struct sk_buff * skb )
{
struct sock * sk = sco_chan_get ( conn ) ;
if ( ! sk )
goto drop ;
BT_DBG ( " sk %p len %d " , sk , skb - > len ) ;
if ( sk - > sk_state ! = BT_CONNECTED )
goto drop ;
if ( ! sock_queue_rcv_skb ( sk , skb ) )
return ;
drop :
kfree_skb ( skb ) ;
}
/* -------- Socket interface ---------- */
static struct sock * __sco_get_sock_by_addr ( bdaddr_t * ba )
{
struct sock * sk ;
struct hlist_node * node ;
sk_for_each ( sk , node , & sco_sk_list . head )
if ( ! bacmp ( & bt_sk ( sk ) - > src , ba ) )
goto found ;
sk = NULL ;
found :
return sk ;
}
/* Find socket listening on source bdaddr.
* Returns closest match .
*/
static struct sock * sco_get_sock_listen ( bdaddr_t * src )
{
struct sock * sk = NULL , * sk1 = NULL ;
struct hlist_node * node ;
read_lock ( & sco_sk_list . lock ) ;
sk_for_each ( sk , node , & sco_sk_list . head ) {
if ( sk - > sk_state ! = BT_LISTEN )
continue ;
/* Exact match. */
if ( ! bacmp ( & bt_sk ( sk ) - > src , src ) )
break ;
/* Closest match */
if ( ! bacmp ( & bt_sk ( sk ) - > src , BDADDR_ANY ) )
sk1 = sk ;
}
read_unlock ( & sco_sk_list . lock ) ;
return node ? sk : sk1 ;
}
static void sco_sock_destruct ( struct sock * sk )
{
BT_DBG ( " sk %p " , sk ) ;
skb_queue_purge ( & sk - > sk_receive_queue ) ;
skb_queue_purge ( & sk - > sk_write_queue ) ;
}
static void sco_sock_cleanup_listen ( struct sock * parent )
{
struct sock * sk ;
BT_DBG ( " parent %p " , parent ) ;
/* Close not yet accepted channels */
while ( ( sk = bt_accept_dequeue ( parent , NULL ) ) ) {
sco_sock_close ( sk ) ;
sco_sock_kill ( sk ) ;
}
parent - > sk_state = BT_CLOSED ;
sock_set_flag ( parent , SOCK_ZAPPED ) ;
}
/* Kill socket (only if zapped and orphan)
* Must be called on unlocked socket .
*/
static void sco_sock_kill ( struct sock * sk )
{
if ( ! sock_flag ( sk , SOCK_ZAPPED ) | | sk - > sk_socket )
return ;
BT_DBG ( " sk %p state %d " , sk , sk - > sk_state ) ;
/* Kill poor orphan */
bt_sock_unlink ( & sco_sk_list , sk ) ;
sock_set_flag ( sk , SOCK_DEAD ) ;
sock_put ( sk ) ;
}
2009-06-16 02:01:49 +04:00
static void __sco_sock_close ( struct sock * sk )
2005-04-17 02:20:36 +04:00
{
2009-06-16 02:01:49 +04:00
BT_DBG ( " sk %p state %d socket %p " , sk , sk - > sk_state , sk - > sk_socket ) ;
2005-04-17 02:20:36 +04:00
switch ( sk - > sk_state ) {
case BT_LISTEN :
sco_sock_cleanup_listen ( sk ) ;
break ;
case BT_CONNECTED :
case BT_CONFIG :
2011-05-12 12:13:15 +04:00
if ( sco_pi ( sk ) - > conn ) {
sk - > sk_state = BT_DISCONN ;
sco_sock_set_timer ( sk , SCO_DISCONN_TIMEOUT ) ;
hci_conn_put ( sco_pi ( sk ) - > conn - > hcon ) ;
sco_pi ( sk ) - > conn - > hcon = NULL ;
} else
sco_chan_del ( sk , ECONNRESET ) ;
break ;
2005-04-17 02:20:36 +04:00
case BT_CONNECT :
case BT_DISCONN :
sco_chan_del ( sk , ECONNRESET ) ;
break ;
default :
sock_set_flag ( sk , SOCK_ZAPPED ) ;
break ;
2007-04-21 04:09:22 +04:00
}
2009-06-16 02:01:49 +04:00
}
2005-04-17 02:20:36 +04:00
2009-06-16 02:01:49 +04:00
/* Must be called on unlocked socket. */
static void sco_sock_close ( struct sock * sk )
{
sco_sock_clear_timer ( sk ) ;
lock_sock ( sk ) ;
__sco_sock_close ( sk ) ;
2005-04-17 02:20:36 +04:00
release_sock ( sk ) ;
sco_sock_kill ( sk ) ;
}
static void sco_sock_init ( struct sock * sk , struct sock * parent )
{
BT_DBG ( " sk %p " , sk ) ;
2011-10-07 13:40:59 +04:00
if ( parent ) {
2005-04-17 02:20:36 +04:00
sk - > sk_type = parent - > sk_type ;
2011-10-07 13:40:59 +04:00
security_sk_clone ( parent , sk ) ;
}
2005-04-17 02:20:36 +04:00
}
static struct proto sco_proto = {
. name = " SCO " ,
. owner = THIS_MODULE ,
. obj_size = sizeof ( struct sco_pinfo )
} ;
2007-10-09 10:24:22 +04:00
static struct sock * sco_sock_alloc ( struct net * net , struct socket * sock , int proto , gfp_t prio )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
2007-11-01 10:39:31 +03:00
sk = sk_alloc ( net , PF_BLUETOOTH , prio , & sco_proto ) ;
2005-04-17 02:20:36 +04:00
if ( ! sk )
return NULL ;
sock_init_data ( sock , sk ) ;
INIT_LIST_HEAD ( & bt_sk ( sk ) - > accept_q ) ;
sk - > sk_destruct = sco_sock_destruct ;
sk - > sk_sndtimeo = SCO_CONN_TIMEOUT ;
sock_reset_flag ( sk , SOCK_ZAPPED ) ;
sk - > sk_protocol = proto ;
sk - > sk_state = BT_OPEN ;
2008-01-24 08:20:07 +03:00
setup_timer ( & sk - > sk_timer , sco_sock_timeout , ( unsigned long ) sk ) ;
2005-04-17 02:20:36 +04:00
bt_sock_link ( & sco_sk_list , sk ) ;
return sk ;
}
2009-11-06 09:18:14 +03:00
static int sco_sock_create ( struct net * net , struct socket * sock , int protocol ,
int kern )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
BT_DBG ( " sock %p " , sock ) ;
sock - > state = SS_UNCONNECTED ;
if ( sock - > type ! = SOCK_SEQPACKET )
return - ESOCKTNOSUPPORT ;
sock - > ops = & sco_sock_ops ;
2007-10-09 10:24:22 +04:00
sk = sco_sock_alloc ( net , sock , protocol , GFP_ATOMIC ) ;
2006-10-15 19:31:14 +04:00
if ( ! sk )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
sco_sock_init ( sk , NULL ) ;
return 0 ;
}
static int sco_sock_bind ( struct socket * sock , struct sockaddr * addr , int addr_len )
{
struct sockaddr_sco * sa = ( struct sockaddr_sco * ) addr ;
struct sock * sk = sock - > sk ;
bdaddr_t * src = & sa - > sco_bdaddr ;
int err = 0 ;
BT_DBG ( " sk %p %s " , sk , batostr ( & sa - > sco_bdaddr ) ) ;
if ( ! addr | | addr - > sa_family ! = AF_BLUETOOTH )
return - EINVAL ;
lock_sock ( sk ) ;
if ( sk - > sk_state ! = BT_OPEN ) {
err = - EBADFD ;
goto done ;
}
write_lock_bh ( & sco_sk_list . lock ) ;
if ( bacmp ( src , BDADDR_ANY ) & & __sco_get_sock_by_addr ( src ) ) {
err = - EADDRINUSE ;
} else {
/* Save source address */
bacpy ( & bt_sk ( sk ) - > src , & sa - > sco_bdaddr ) ;
sk - > sk_state = BT_BOUND ;
}
write_unlock_bh ( & sco_sk_list . lock ) ;
done :
release_sock ( sk ) ;
return err ;
}
static int sco_sock_connect ( struct socket * sock , struct sockaddr * addr , int alen , int flags )
{
struct sockaddr_sco * sa = ( struct sockaddr_sco * ) addr ;
struct sock * sk = sock - > sk ;
int err = 0 ;
BT_DBG ( " sk %p " , sk ) ;
2010-04-01 02:58:26 +04:00
if ( alen < sizeof ( struct sockaddr_sco ) | |
addr - > sa_family ! = AF_BLUETOOTH )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
if ( sk - > sk_state ! = BT_OPEN & & sk - > sk_state ! = BT_BOUND )
return - EBADFD ;
if ( sk - > sk_type ! = SOCK_SEQPACKET )
return - EINVAL ;
lock_sock ( sk ) ;
/* Set destination address and psm */
bacpy ( & bt_sk ( sk ) - > dst , & sa - > sco_bdaddr ) ;
2010-12-01 17:58:22 +03:00
err = sco_connect ( sk ) ;
if ( err )
2005-04-17 02:20:36 +04:00
goto done ;
2007-02-09 17:24:33 +03:00
err = bt_sock_wait_state ( sk , BT_CONNECTED ,
2005-04-17 02:20:36 +04:00
sock_sndtimeo ( sk , flags & O_NONBLOCK ) ) ;
done :
release_sock ( sk ) ;
return err ;
}
static int sco_sock_listen ( struct socket * sock , int backlog )
{
struct sock * sk = sock - > sk ;
int err = 0 ;
BT_DBG ( " sk %p backlog %d " , sk , backlog ) ;
lock_sock ( sk ) ;
if ( sk - > sk_state ! = BT_BOUND | | sock - > type ! = SOCK_SEQPACKET ) {
err = - EBADFD ;
goto done ;
}
sk - > sk_max_ack_backlog = backlog ;
sk - > sk_ack_backlog = 0 ;
sk - > sk_state = BT_LISTEN ;
done :
release_sock ( sk ) ;
return err ;
}
static int sco_sock_accept ( struct socket * sock , struct socket * newsock , int flags )
{
DECLARE_WAITQUEUE ( wait , current ) ;
struct sock * sk = sock - > sk , * ch ;
long timeo ;
int err = 0 ;
lock_sock ( sk ) ;
timeo = sock_rcvtimeo ( sk , flags & O_NONBLOCK ) ;
BT_DBG ( " sk %p timeo %ld " , sk , timeo ) ;
/* Wait for an incoming connection. (wake-one). */
2010-04-20 17:03:51 +04:00
add_wait_queue_exclusive ( sk_sleep ( sk ) , & wait ) ;
2011-07-24 08:11:01 +04:00
while ( 1 ) {
2005-04-17 02:20:36 +04:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
2011-07-24 08:11:01 +04:00
if ( sk - > sk_state ! = BT_LISTEN ) {
err = - EBADFD ;
2005-04-17 02:20:36 +04:00
break ;
}
2011-07-24 08:11:01 +04:00
ch = bt_accept_dequeue ( sk , newsock ) ;
if ( ch )
break ;
2005-04-17 02:20:36 +04:00
2011-07-24 08:11:01 +04:00
if ( ! timeo ) {
err = - EAGAIN ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( signal_pending ( current ) ) {
err = sock_intr_errno ( timeo ) ;
break ;
}
2011-07-24 08:11:01 +04:00
release_sock ( sk ) ;
timeo = schedule_timeout ( timeo ) ;
lock_sock ( sk ) ;
2005-04-17 02:20:36 +04:00
}
2011-07-24 08:11:01 +04:00
__set_current_state ( TASK_RUNNING ) ;
2010-04-20 17:03:51 +04:00
remove_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto done ;
newsock - > state = SS_CONNECTED ;
BT_DBG ( " new socket %p " , ch ) ;
done :
release_sock ( sk ) ;
return err ;
}
static int sco_sock_getname ( struct socket * sock , struct sockaddr * addr , int * len , int peer )
{
struct sockaddr_sco * sa = ( struct sockaddr_sco * ) addr ;
struct sock * sk = sock - > sk ;
BT_DBG ( " sock %p, sk %p " , sock , sk ) ;
addr - > sa_family = AF_BLUETOOTH ;
* len = sizeof ( struct sockaddr_sco ) ;
if ( peer )
bacpy ( & sa - > sco_bdaddr , & bt_sk ( sk ) - > dst ) ;
else
bacpy ( & sa - > sco_bdaddr , & bt_sk ( sk ) - > src ) ;
return 0 ;
}
2007-02-09 17:24:33 +03:00
static int sco_sock_sendmsg ( struct kiocb * iocb , struct socket * sock ,
2005-04-17 02:20:36 +04:00
struct msghdr * msg , size_t len )
{
struct sock * sk = sock - > sk ;
2010-05-01 23:15:35 +04:00
int err ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " sock %p, sk %p " , sock , sk ) ;
2005-12-14 10:22:19 +03:00
err = sock_error ( sk ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
if ( msg - > msg_flags & MSG_OOB )
return - EOPNOTSUPP ;
lock_sock ( sk ) ;
if ( sk - > sk_state = = BT_CONNECTED )
err = sco_send_frame ( sk , msg , len ) ;
else
err = - ENOTCONN ;
release_sock ( sk ) ;
return err ;
}
2009-10-01 03:12:20 +04:00
static int sco_sock_setsockopt ( struct socket * sock , int level , int optname , char __user * optval , unsigned int optlen )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sock - > sk ;
int err = 0 ;
BT_DBG ( " sk %p " , sk ) ;
lock_sock ( sk ) ;
switch ( optname ) {
default :
err = - ENOPROTOOPT ;
break ;
}
release_sock ( sk ) ;
return err ;
}
2009-01-15 23:52:14 +03:00
static int sco_sock_getsockopt_old ( struct socket * sock , int optname , char __user * optval , int __user * optlen )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sock - > sk ;
struct sco_options opts ;
struct sco_conninfo cinfo ;
2007-02-09 17:24:33 +03:00
int len , err = 0 ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " sk %p " , sk ) ;
if ( get_user ( len , optlen ) )
return - EFAULT ;
lock_sock ( sk ) ;
switch ( optname ) {
case SCO_OPTIONS :
if ( sk - > sk_state ! = BT_CONNECTED ) {
err = - ENOTCONN ;
break ;
}
opts . mtu = sco_pi ( sk ) - > conn - > mtu ;
BT_DBG ( " mtu %d " , opts . mtu ) ;
len = min_t ( unsigned int , len , sizeof ( opts ) ) ;
if ( copy_to_user ( optval , ( char * ) & opts , len ) )
err = - EFAULT ;
break ;
case SCO_CONNINFO :
if ( sk - > sk_state ! = BT_CONNECTED ) {
err = - ENOTCONN ;
break ;
}
2011-02-14 13:54:26 +03:00
memset ( & cinfo , 0 , sizeof ( cinfo ) ) ;
2005-04-17 02:20:36 +04:00
cinfo . hci_handle = sco_pi ( sk ) - > conn - > hcon - > handle ;
memcpy ( cinfo . dev_class , sco_pi ( sk ) - > conn - > hcon - > dev_class , 3 ) ;
len = min_t ( unsigned int , len , sizeof ( cinfo ) ) ;
if ( copy_to_user ( optval , ( char * ) & cinfo , len ) )
err = - EFAULT ;
break ;
default :
err = - ENOPROTOOPT ;
break ;
}
release_sock ( sk ) ;
return err ;
}
2009-01-15 23:52:14 +03:00
static int sco_sock_getsockopt ( struct socket * sock , int level , int optname , char __user * optval , int __user * optlen )
{
struct sock * sk = sock - > sk ;
int len , err = 0 ;
BT_DBG ( " sk %p " , sk ) ;
if ( level = = SOL_SCO )
return sco_sock_getsockopt_old ( sock , optname , optval , optlen ) ;
if ( get_user ( len , optlen ) )
return - EFAULT ;
lock_sock ( sk ) ;
switch ( optname ) {
default :
err = - ENOPROTOOPT ;
break ;
}
release_sock ( sk ) ;
return err ;
}
2009-06-16 02:01:49 +04:00
static int sco_sock_shutdown ( struct socket * sock , int how )
{
struct sock * sk = sock - > sk ;
int err = 0 ;
BT_DBG ( " sock %p, sk %p " , sock , sk ) ;
if ( ! sk )
return 0 ;
lock_sock ( sk ) ;
if ( ! sk - > sk_shutdown ) {
sk - > sk_shutdown = SHUTDOWN_MASK ;
sco_sock_clear_timer ( sk ) ;
__sco_sock_close ( sk ) ;
if ( sock_flag ( sk , SOCK_LINGER ) & & sk - > sk_lingertime )
err = bt_sock_wait_state ( sk , BT_CLOSED ,
sk - > sk_lingertime ) ;
}
release_sock ( sk ) ;
return err ;
}
2005-04-17 02:20:36 +04:00
static int sco_sock_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
int err = 0 ;
BT_DBG ( " sock %p, sk %p " , sock , sk ) ;
if ( ! sk )
return 0 ;
sco_sock_close ( sk ) ;
if ( sock_flag ( sk , SOCK_LINGER ) & & sk - > sk_lingertime ) {
lock_sock ( sk ) ;
err = bt_sock_wait_state ( sk , BT_CLOSED , sk - > sk_lingertime ) ;
release_sock ( sk ) ;
}
sock_orphan ( sk ) ;
sco_sock_kill ( sk ) ;
return err ;
}
static void __sco_chan_add ( struct sco_conn * conn , struct sock * sk , struct sock * parent )
{
BT_DBG ( " conn %p " , conn ) ;
sco_pi ( sk ) - > conn = conn ;
conn - > sk = sk ;
if ( parent )
bt_accept_enqueue ( parent , sk ) ;
}
2007-02-09 17:24:33 +03:00
/* Delete channel.
2005-04-17 02:20:36 +04:00
* Must be called on the locked socket . */
static void sco_chan_del ( struct sock * sk , int err )
{
struct sco_conn * conn ;
conn = sco_pi ( sk ) - > conn ;
BT_DBG ( " sk %p, conn %p, err %d " , sk , conn , err ) ;
2007-02-09 17:24:33 +03:00
if ( conn ) {
2005-04-17 02:20:36 +04:00
sco_conn_lock ( conn ) ;
conn - > sk = NULL ;
sco_pi ( sk ) - > conn = NULL ;
sco_conn_unlock ( conn ) ;
2011-05-12 12:13:15 +04:00
if ( conn - > hcon )
hci_conn_put ( conn - > hcon ) ;
2005-04-17 02:20:36 +04:00
}
sk - > sk_state = BT_CLOSED ;
sk - > sk_err = err ;
sk - > sk_state_change ( sk ) ;
sock_set_flag ( sk , SOCK_ZAPPED ) ;
}
static void sco_conn_ready ( struct sco_conn * conn )
{
2010-12-01 17:58:22 +03:00
struct sock * parent ;
struct sock * sk = conn - > sk ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " conn %p " , conn ) ;
sco_conn_lock ( conn ) ;
2010-12-01 17:58:22 +03:00
if ( sk ) {
2005-04-17 02:20:36 +04:00
sco_sock_clear_timer ( sk ) ;
bh_lock_sock ( sk ) ;
sk - > sk_state = BT_CONNECTED ;
sk - > sk_state_change ( sk ) ;
bh_unlock_sock ( sk ) ;
} else {
parent = sco_get_sock_listen ( conn - > src ) ;
if ( ! parent )
goto done ;
bh_lock_sock ( parent ) ;
2010-05-01 23:15:35 +04:00
sk = sco_sock_alloc ( sock_net ( parent ) , NULL ,
BTPROTO_SCO , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
if ( ! sk ) {
bh_unlock_sock ( parent ) ;
goto done ;
}
sco_sock_init ( sk , parent ) ;
bacpy ( & bt_sk ( sk ) - > src , conn - > src ) ;
bacpy ( & bt_sk ( sk ) - > dst , conn - > dst ) ;
hci_conn_hold ( conn - > hcon ) ;
__sco_chan_add ( conn , sk , parent ) ;
sk - > sk_state = BT_CONNECTED ;
/* Wake up parent */
parent - > sk_data_ready ( parent , 1 ) ;
bh_unlock_sock ( parent ) ;
}
done :
sco_conn_unlock ( conn ) ;
}
/* ----- SCO interface with lower layer (HCI) ----- */
static int sco_connect_ind ( struct hci_dev * hdev , bdaddr_t * bdaddr , __u8 type )
{
2009-01-15 23:57:02 +03:00
register struct sock * sk ;
struct hlist_node * node ;
int lm = 0 ;
if ( type ! = SCO_LINK & & type ! = ESCO_LINK )
2010-11-01 22:08:50 +03:00
return - EINVAL ;
2009-01-15 23:57:02 +03:00
2005-04-17 02:20:36 +04:00
BT_DBG ( " hdev %s, bdaddr %s " , hdev - > name , batostr ( bdaddr ) ) ;
2009-01-15 23:57:02 +03:00
/* Find listening sockets */
read_lock ( & sco_sk_list . lock ) ;
sk_for_each ( sk , node , & sco_sk_list . head ) {
if ( sk - > sk_state ! = BT_LISTEN )
continue ;
if ( ! bacmp ( & bt_sk ( sk ) - > src , & hdev - > bdaddr ) | |
! bacmp ( & bt_sk ( sk ) - > src , BDADDR_ANY ) ) {
lm | = HCI_LM_ACCEPT ;
break ;
}
}
read_unlock ( & sco_sk_list . lock ) ;
return lm ;
2005-04-17 02:20:36 +04:00
}
static int sco_connect_cfm ( struct hci_conn * hcon , __u8 status )
{
BT_DBG ( " hcon %p bdaddr %s status %d " , hcon , batostr ( & hcon - > dst ) , status ) ;
2007-10-20 16:55:10 +04:00
if ( hcon - > type ! = SCO_LINK & & hcon - > type ! = ESCO_LINK )
2010-11-01 22:08:50 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( ! status ) {
struct sco_conn * conn ;
conn = sco_conn_add ( hcon , status ) ;
if ( conn )
sco_conn_ready ( conn ) ;
2007-02-09 17:24:33 +03:00
} else
2011-06-30 05:18:29 +04:00
sco_conn_del ( hcon , bt_to_errno ( status ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-02-12 16:02:50 +03:00
static int sco_disconn_cfm ( struct hci_conn * hcon , __u8 reason )
2005-04-17 02:20:36 +04:00
{
BT_DBG ( " hcon %p reason %d " , hcon , reason ) ;
2007-10-20 16:55:10 +04:00
if ( hcon - > type ! = SCO_LINK & & hcon - > type ! = ESCO_LINK )
2010-11-01 22:08:50 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2011-06-30 05:18:29 +04:00
sco_conn_del ( hcon , bt_to_errno ( reason ) ) ;
2007-10-20 16:55:10 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int sco_recv_scodata ( struct hci_conn * hcon , struct sk_buff * skb )
{
struct sco_conn * conn = hcon - > sco_data ;
if ( ! conn )
goto drop ;
BT_DBG ( " conn %p len %d " , conn , skb - > len ) ;
if ( skb - > len ) {
sco_recv_frame ( conn , skb ) ;
return 0 ;
}
drop :
2007-02-09 17:24:33 +03:00
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-03-21 07:27:45 +03:00
static int sco_debugfs_show ( struct seq_file * f , void * p )
2005-04-17 02:20:36 +04:00
{
struct sock * sk ;
struct hlist_node * node ;
read_lock_bh ( & sco_sk_list . lock ) ;
2005-11-08 20:57:38 +03:00
sk_for_each ( sk , node , & sco_sk_list . head ) {
2010-03-21 07:27:45 +03:00
seq_printf ( f , " %s %s %d \n " , batostr ( & bt_sk ( sk ) - > src ) ,
batostr ( & bt_sk ( sk ) - > dst ) , sk - > sk_state ) ;
2005-11-08 20:57:38 +03:00
}
2005-04-17 02:20:36 +04:00
read_unlock_bh ( & sco_sk_list . lock ) ;
2010-03-21 07:27:45 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-03-21 07:27:45 +03:00
static int sco_debugfs_open ( struct inode * inode , struct file * file )
{
return single_open ( file , sco_debugfs_show , inode - > i_private ) ;
}
static const struct file_operations sco_debugfs_fops = {
. open = sco_debugfs_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static struct dentry * sco_debugfs ;
2005-04-17 02:20:36 +04:00
2005-12-22 23:49:22 +03:00
static const struct proto_ops sco_sock_ops = {
2005-04-17 02:20:36 +04:00
. family = PF_BLUETOOTH ,
. owner = THIS_MODULE ,
. release = sco_sock_release ,
. bind = sco_sock_bind ,
. connect = sco_sock_connect ,
. listen = sco_sock_listen ,
. accept = sco_sock_accept ,
. getname = sco_sock_getname ,
. sendmsg = sco_sock_sendmsg ,
. recvmsg = bt_sock_recvmsg ,
. poll = bt_sock_poll ,
2008-07-14 22:13:50 +04:00
. ioctl = bt_sock_ioctl ,
2005-04-17 02:20:36 +04:00
. mmap = sock_no_mmap ,
. socketpair = sock_no_socketpair ,
2009-06-16 02:01:49 +04:00
. shutdown = sco_sock_shutdown ,
2005-04-17 02:20:36 +04:00
. setsockopt = sco_sock_setsockopt ,
. getsockopt = sco_sock_getsockopt
} ;
2009-10-05 09:58:39 +04:00
static const struct net_proto_family sco_sock_family_ops = {
2005-04-17 02:20:36 +04:00
. family = PF_BLUETOOTH ,
. owner = THIS_MODULE ,
. create = sco_sock_create ,
} ;
static struct hci_proto sco_hci_proto = {
. name = " SCO " ,
. id = HCI_PROTO_SCO ,
. connect_ind = sco_connect_ind ,
. connect_cfm = sco_connect_cfm ,
2009-02-12 16:02:50 +03:00
. disconn_cfm = sco_disconn_cfm ,
2005-04-17 02:20:36 +04:00
. recv_scodata = sco_recv_scodata
} ;
2011-02-08 01:08:52 +03:00
int __init sco_init ( void )
2005-04-17 02:20:36 +04:00
{
int err ;
err = proto_register ( & sco_proto , 0 ) ;
if ( err < 0 )
return err ;
err = bt_sock_register ( BTPROTO_SCO , & sco_sock_family_ops ) ;
if ( err < 0 ) {
BT_ERR ( " SCO socket registration failed " ) ;
goto error ;
}
err = hci_register_proto ( & sco_hci_proto ) ;
if ( err < 0 ) {
BT_ERR ( " SCO protocol registration failed " ) ;
bt_sock_unregister ( BTPROTO_SCO ) ;
goto error ;
}
2010-03-21 07:27:45 +03:00
if ( bt_debugfs ) {
sco_debugfs = debugfs_create_file ( " sco " , 0444 ,
bt_debugfs , NULL , & sco_debugfs_fops ) ;
if ( ! sco_debugfs )
BT_ERR ( " Failed to create SCO debug file " ) ;
}
2005-04-17 02:20:36 +04:00
BT_INFO ( " SCO socket layer initialized " ) ;
return 0 ;
error :
proto_unregister ( & sco_proto ) ;
return err ;
}
2011-02-08 01:08:52 +03:00
void __exit sco_exit ( void )
2005-04-17 02:20:36 +04:00
{
2010-03-21 07:27:45 +03:00
debugfs_remove ( sco_debugfs ) ;
2005-04-17 02:20:36 +04:00
if ( bt_sock_unregister ( BTPROTO_SCO ) < 0 )
BT_ERR ( " SCO socket unregistration failed " ) ;
if ( hci_unregister_proto ( & sco_hci_proto ) < 0 )
BT_ERR ( " SCO protocol unregistration failed " ) ;
proto_unregister ( & sco_proto ) ;
}
2008-07-14 22:13:53 +04:00
module_param ( disable_esco , bool , 0644 ) ;
MODULE_PARM_DESC ( disable_esco , " Disable eSCO connection creation " ) ;