Commit 037f7f39 authored by Maksim Krasnyanskiy's avatar Maksim Krasnyanskiy

[Bluetooth] Add support for SO_LINGER to L2CAP, RFCOMM and SCO sockets.

This is required to pass qualification testing.
parent 8c473eea
...@@ -130,7 +130,7 @@ void bt_sock_link(struct bt_sock_list *l, struct sock *s); ...@@ -130,7 +130,7 @@ void bt_sock_link(struct bt_sock_list *l, struct sock *s);
void bt_sock_unlink(struct bt_sock_list *l, struct sock *s); void bt_sock_unlink(struct bt_sock_list *l, struct sock *s);
int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, int len, int flags); int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, int len, int flags);
uint bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait); uint bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait);
int bt_sock_w4_connect(struct sock *sk, int flags); int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
void bt_accept_enqueue(struct sock *parent, struct sock *sk); void bt_accept_enqueue(struct sock *parent, struct sock *sk);
struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock);
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* *
* $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $ * $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $
*/ */
#define VERSION "2.2" #define VERSION "2.3"
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -282,39 +282,35 @@ unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *w ...@@ -282,39 +282,35 @@ unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *w
return mask; return mask;
} }
int bt_sock_w4_connect(struct sock *sk, int flags) int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
{ {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
int err = 0; int err = 0;
BT_DBG("sk %p", sk); BT_DBG("sk %p", sk);
add_wait_queue(sk->sk_sleep, &wait); add_wait_queue(sk->sk_sleep, &wait);
while (sk->sk_state != BT_CONNECTED) { while (sk->sk_state != state) {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if (!timeo) { if (!timeo) {
err = -EAGAIN; err = -EAGAIN;
break; break;
} }
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
break;
}
release_sock(sk); release_sock(sk);
timeo = schedule_timeout(timeo); timeo = schedule_timeout(timeo);
lock_sock(sk); lock_sock(sk);
err = 0;
if (sk->sk_state == BT_CONNECTED)
break;
if (sk->sk_err) { if (sk->sk_err) {
err = sock_error(sk); err = sock_error(sk);
break; break;
} }
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
break;
}
} }
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sk_sleep, &wait); remove_wait_queue(sk->sk_sleep, &wait);
......
...@@ -287,7 +287,7 @@ static void __l2cap_sock_close(struct sock *sk, int reason) ...@@ -287,7 +287,7 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
struct l2cap_disconn_req req; struct l2cap_disconn_req req;
sk->sk_state = BT_DISCONN; sk->sk_state = BT_DISCONN;
l2cap_sock_set_timer(sk, HZ * 5); l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
...@@ -312,11 +312,9 @@ static void __l2cap_sock_close(struct sock *sk, int reason) ...@@ -312,11 +312,9 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
static void l2cap_sock_close(struct sock *sk) static void l2cap_sock_close(struct sock *sk)
{ {
l2cap_sock_clear_timer(sk); l2cap_sock_clear_timer(sk);
lock_sock(sk); lock_sock(sk);
__l2cap_sock_close(sk, ECONNRESET); __l2cap_sock_close(sk, ECONNRESET);
release_sock(sk); release_sock(sk);
l2cap_sock_kill(sk); l2cap_sock_kill(sk);
} }
...@@ -528,8 +526,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al ...@@ -528,8 +526,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
goto done; goto done;
wait: wait:
err = bt_sock_w4_connect(sk, flags); err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
done: done:
release_sock(sk); release_sock(sk);
return err; return err;
...@@ -829,32 +827,39 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch ...@@ -829,32 +827,39 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
static int l2cap_sock_shutdown(struct socket *sock, int how) static int l2cap_sock_shutdown(struct socket *sock, int how)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk); BT_DBG("sock %p, sk %p", sock, sk);
if (!sk) return 0; if (!sk) return 0;
l2cap_sock_clear_timer(sk);
lock_sock(sk); lock_sock(sk);
if (!sk->sk_shutdown) {
sk->sk_shutdown = SHUTDOWN_MASK; sk->sk_shutdown = SHUTDOWN_MASK;
__l2cap_sock_close(sk, ECONNRESET); l2cap_sock_clear_timer(sk);
release_sock(sk); __l2cap_sock_close(sk, 0);
return 0; 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;
} }
static int l2cap_sock_release(struct socket *sock) static int l2cap_sock_release(struct socket *sock)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int err;
BT_DBG("sock %p, sk %p", sock, sk); BT_DBG("sock %p, sk %p", sock, sk);
if (!sk) return 0; if (!sk) return 0;
err = l2cap_sock_shutdown(sock, 2);
sock_orphan(sk); sock_orphan(sk);
l2cap_sock_close(sk); l2cap_sock_kill(sk);
return 0; return err;
} }
/* ---- L2CAP channels ---- */ /* ---- L2CAP channels ---- */
...@@ -979,9 +984,11 @@ static void l2cap_chan_del(struct sock *sk, int err) ...@@ -979,9 +984,11 @@ static void l2cap_chan_del(struct sock *sk, int err)
} }
sk->sk_state = BT_CLOSED; sk->sk_state = BT_CLOSED;
sk->sk_err = err;
sk->sk_zapped = 1; sk->sk_zapped = 1;
if (err)
sk->sk_err = err;
if (parent) if (parent)
parent->sk_data_ready(parent, 0); parent->sk_data_ready(parent, 0);
else else
...@@ -1588,7 +1595,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -1588,7 +1595,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
return 0; return 0;
l2cap_chan_del(sk, ECONNABORTED); l2cap_chan_del(sk, 0);
bh_unlock_sock(sk); bh_unlock_sock(sk);
l2cap_sock_kill(sk); l2cap_sock_kill(sk);
......
...@@ -191,8 +191,10 @@ static void rfcomm_sock_cleanup_listen(struct sock *parent) ...@@ -191,8 +191,10 @@ static void rfcomm_sock_cleanup_listen(struct sock *parent)
BT_DBG("parent %p", parent); BT_DBG("parent %p", parent);
/* Close not yet accepted dlcs */ /* Close not yet accepted dlcs */
while ((sk = bt_accept_dequeue(parent, NULL))) while ((sk = bt_accept_dequeue(parent, NULL))) {
rfcomm_sock_close(sk); rfcomm_sock_close(sk);
rfcomm_sock_kill(sk);
}
parent->sk_state = BT_CLOSED; parent->sk_state = BT_CLOSED;
parent->sk_zapped = 1; parent->sk_zapped = 1;
...@@ -214,15 +216,10 @@ static void rfcomm_sock_kill(struct sock *sk) ...@@ -214,15 +216,10 @@ static void rfcomm_sock_kill(struct sock *sk)
sock_put(sk); sock_put(sk);
} }
/* Close socket. static void __rfcomm_sock_close(struct sock *sk)
* Must be called on unlocked socket.
*/
static void rfcomm_sock_close(struct sock *sk)
{ {
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
lock_sock(sk);
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
switch (sk->sk_state) { switch (sk->sk_state) {
...@@ -239,11 +236,17 @@ static void rfcomm_sock_close(struct sock *sk) ...@@ -239,11 +236,17 @@ static void rfcomm_sock_close(struct sock *sk)
default: default:
sk->sk_zapped = 1; sk->sk_zapped = 1;
break; break;
}; }
}
/* Close socket.
* Must be called on unlocked socket.
*/
static void rfcomm_sock_close(struct sock *sk)
{
lock_sock(sk);
__rfcomm_sock_close(sk);
release_sock(sk); release_sock(sk);
rfcomm_sock_kill(sk);
} }
static void rfcomm_sock_init(struct sock *sk, struct sock *parent) static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
...@@ -373,7 +376,8 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a ...@@ -373,7 +376,8 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
if (!err) if (!err)
err = bt_sock_w4_connect(sk, flags); err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
release_sock(sk); release_sock(sk);
return err; return err;
...@@ -557,9 +561,6 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -557,9 +561,6 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
int target, err = 0, copied = 0; int target, err = 0, copied = 0;
long timeo; long timeo;
if (sk->sk_state != BT_CONNECTED)
return -EINVAL;
if (flags & MSG_OOB) if (flags & MSG_OOB)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -634,23 +635,6 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -634,23 +635,6 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
return copied ? : err; return copied ? : err;
} }
static int rfcomm_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p, sk %p", sock, sk);
if (!sk) return 0;
lock_sock(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
if (sk->sk_state == BT_CONNECTED)
rfcomm_dlc_close(rfcomm_pi(sk)->dlc, 0);
release_sock(sk);
return 0;
}
static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
...@@ -709,19 +693,42 @@ static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned lon ...@@ -709,19 +693,42 @@ static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned lon
return err; return err;
} }
static int rfcomm_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;
__rfcomm_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;
}
static int rfcomm_sock_release(struct socket *sock) static int rfcomm_sock_release(struct socket *sock)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int err;
BT_DBG("sock %p, sk %p", sock, sk); BT_DBG("sock %p, sk %p", sock, sk);
if (!sk) if (!sk)
return 0; return 0;
sock_orphan(sk); err = rfcomm_sock_shutdown(sock, 2);
rfcomm_sock_close(sk);
return 0; sock_orphan(sk);
rfcomm_sock_kill(sk);
return err;
} }
/* ---- RFCOMM core layer callbacks ---- /* ---- RFCOMM core layer callbacks ----
......
...@@ -874,8 +874,6 @@ static struct tty_operations rfcomm_ops = { ...@@ -874,8 +874,6 @@ static struct tty_operations rfcomm_ops = {
int rfcomm_init_ttys(void) int rfcomm_init_ttys(void)
{ {
int i;
rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS);
if (!rfcomm_tty_driver) if (!rfcomm_tty_driver)
return -1; return -1;
......
...@@ -352,8 +352,10 @@ static void sco_sock_cleanup_listen(struct sock *parent) ...@@ -352,8 +352,10 @@ static void sco_sock_cleanup_listen(struct sock *parent)
BT_DBG("parent %p", parent); BT_DBG("parent %p", parent);
/* Close not yet accepted channels */ /* Close not yet accepted channels */
while ((sk = bt_accept_dequeue(parent, NULL))) while ((sk = bt_accept_dequeue(parent, NULL))) {
sco_sock_close(sk); sco_sock_close(sk);
sco_sock_kill(sk);
}
parent->sk_state = BT_CLOSED; parent->sk_state = BT_CLOSED;
parent->sk_zapped = 1; parent->sk_zapped = 1;
...@@ -522,7 +524,8 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen ...@@ -522,7 +524,8 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
if ((err = sco_connect(sk))) if ((err = sco_connect(sk)))
goto done; goto done;
err = bt_sock_w4_connect(sk, flags); err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
done: done:
release_sock(sk); release_sock(sk);
...@@ -726,16 +729,24 @@ int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optva ...@@ -726,16 +729,24 @@ int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optva
static int sco_sock_release(struct socket *sock) static int sco_sock_release(struct socket *sock)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk); BT_DBG("sock %p, sk %p", sock, sk);
if (!sk) if (!sk)
return 0; return 0;
sock_orphan(sk);
sco_sock_close(sk); sco_sock_close(sk);
return 0; 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) static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
......
...@@ -77,6 +77,6 @@ EXPORT_SYMBOL(bt_sock_recvmsg); ...@@ -77,6 +77,6 @@ EXPORT_SYMBOL(bt_sock_recvmsg);
EXPORT_SYMBOL(bt_sock_poll); EXPORT_SYMBOL(bt_sock_poll);
EXPORT_SYMBOL(bt_accept_enqueue); EXPORT_SYMBOL(bt_accept_enqueue);
EXPORT_SYMBOL(bt_accept_dequeue); EXPORT_SYMBOL(bt_accept_dequeue);
EXPORT_SYMBOL(bt_sock_w4_connect); EXPORT_SYMBOL(bt_sock_wait_state);
EXPORT_SYMBOL(proc_bt); EXPORT_SYMBOL(proc_bt);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment