Răsfoiți Sursa

Bluetooth: Add proper shutdown support to SCO sockets

The SCO sockets for Bluetooth audio setup and streaming are missing the
shutdown implementation. This hasn't been a problem so far, but with a
more deeper integration with PulseAudio it is important to shutdown SCO
sockets properly.

Also the Headset profile 1.2 has more detailed qualification tests that
require that SCO and RFCOMM channels are terminated in the right order. A
proper shutdown function is necessary for this.

Based on a report by Johan Hedberg <johan.hedberg@nokia.com>

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Tested-by: Johan Hedberg <johan.hedberg@nokia.com>
Marcel Holtmann 16 ani în urmă
părinte
comite
fd0b3ff707
1 a modificat fișierele cu 34 adăugiri și 15 ștergeri
  1. 34 15
      net/bluetooth/sco.c

+ 34 - 15
net/bluetooth/sco.c

@@ -359,20 +359,9 @@ static void sco_sock_kill(struct sock *sk)
 	sock_put(sk);
 	sock_put(sk);
 }
 }
 
 
-/* Close socket.
- * Must be called on unlocked socket.
- */
-static void sco_sock_close(struct sock *sk)
+static void __sco_sock_close(struct sock *sk)
 {
 {
-	struct sco_conn *conn;
-
-	sco_sock_clear_timer(sk);
-
-	lock_sock(sk);
-
-	conn = sco_pi(sk)->conn;
-
-	BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, 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) {
 	case BT_LISTEN:
 	case BT_LISTEN:
@@ -390,9 +379,15 @@ static void sco_sock_close(struct sock *sk)
 		sock_set_flag(sk, SOCK_ZAPPED);
 		sock_set_flag(sk, SOCK_ZAPPED);
 		break;
 		break;
 	}
 	}
+}
 
 
+/* 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);
 	release_sock(sk);
 	release_sock(sk);
-
 	sco_sock_kill(sk);
 	sco_sock_kill(sk);
 }
 }
 
 
@@ -748,6 +743,30 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
 	return err;
 	return err;
 }
 }
 
 
+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;
+}
+
 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;
@@ -969,7 +988,7 @@ static const struct proto_ops sco_sock_ops = {
 	.ioctl		= bt_sock_ioctl,
 	.ioctl		= bt_sock_ioctl,
 	.mmap		= sock_no_mmap,
 	.mmap		= sock_no_mmap,
 	.socketpair	= sock_no_socketpair,
 	.socketpair	= sock_no_socketpair,
-	.shutdown	= sock_no_shutdown,
+	.shutdown	= sco_sock_shutdown,
 	.setsockopt	= sco_sock_setsockopt,
 	.setsockopt	= sco_sock_setsockopt,
 	.getsockopt	= sco_sock_getsockopt
 	.getsockopt	= sco_sock_getsockopt
 };
 };