bluetooth pull request for net:
- Fix not setting Dath Path for broadcast sink - Fix not cleaning up on LE Connection failure - SCO: Fix possible circular locking dependency - L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp} - Fix race condition in hidp_session_thread - btbcm: Fix logic error in forming the board name - btbcm: Fix use after free in btsdio_remove -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmQ0RqEZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKQrlEACfcXOFd5OtB2uLT+TTXkWk QS8D4qfQfKkwVxNSBrvg/pSm1PXvmed6OdboQgcrosKppbOydYmXbPy+4k3jzQIG 2fCkgdUd8TUCGp4CiIlOHIZ3iPANa78It1c986L0of3uJ9lZaoZDNioodpFO+xT9 6yrjS7blIU8GpkAGPKLSj7Im59CpkPE2tbh6HGmUAkslngsQb/GicqL1do1XWMX+ 8gtc4J1kHDearzm8ecHwX6csdocFuBfSeFCPIshwPwqeNzGkt43gQeBuylTKa1Ep HbyUekazaLJq2dlVcjMVDQ/ISlYSXannBF2v+Wx7ETdF+DsmidApZxaLDEdwVjok NGxsChfVum5C1bmeiltnc93UW/l8GZYjlf3fEb0xirMmSbZGZ6oR6ul0Q0y6VGa8 S1nw42K+p7Gys/s0fejkmXZAZCTvfA0TJErocKlVPDwigCzUGUIaMNGIdRQT47u5 h3f0aW4qvPkdszmlWvuknXWSqLoOVB97L+fNUCA31sSH/dG83KwnBzQAMcn6ZcWC EPO4WDVToZWxaMdZ0MSBaGXu4j/dD3KT7wz39FTUBNyUiW0bM3DxOPSI4vncyrkl 2uaThul45R+iD3n0Q2eFh5T1fcoSZ/GBMc4w2P+y7V7E2w/wxCyFH7+fnEsbrD0T 7JUlngpbtwbaQu4i4XLRGQ== =Ag/P -----END PGP SIGNATURE----- Merge tag 'for-net-2023-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - Fix not setting Dath Path for broadcast sink - Fix not cleaning up on LE Connection failure - SCO: Fix possible circular locking dependency - L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp} - Fix race condition in hidp_session_thread - btbcm: Fix logic error in forming the board name - btbcm: Fix use after free in btsdio_remove * tag 'for-net-2023-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp} Bluetooth: Set ISO Data Path on broadcast sink Bluetooth: hci_conn: Fix possible UAF Bluetooth: SCO: Fix possible circular locking dependency sco_sock_getsockopt Bluetooth: SCO: Fix possible circular locking dependency on sco_connect_cfm bluetooth: btbcm: Fix logic error in forming the board name. Bluetooth: btsdio: fix use after free bug in btsdio_remove due to race condition Bluetooth: Fix race condition in hidp_session_thread Bluetooth: Fix printing errors if LE Connection times out Bluetooth: hci_conn: Fix not cleaning up on LE Connection failure ==================== Link: https://lore.kernel.org/r/20230410172718.4067798-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Коммит
160c13175e
|
@ -511,7 +511,7 @@ static const char *btbcm_get_board_name(struct device *dev)
|
|||
len = strlen(tmp) + 1;
|
||||
board_type = devm_kzalloc(dev, len, GFP_KERNEL);
|
||||
strscpy(board_type, tmp, len);
|
||||
for (i = 0; i < board_type[i]; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (board_type[i] == '/')
|
||||
board_type[i] = '-';
|
||||
}
|
||||
|
|
|
@ -358,6 +358,7 @@ static void btsdio_remove(struct sdio_func *func)
|
|||
if (!data)
|
||||
return;
|
||||
|
||||
cancel_work_sync(&data->work);
|
||||
hdev = data->hdev;
|
||||
|
||||
sdio_set_drvdata(func, NULL);
|
||||
|
|
|
@ -954,6 +954,7 @@ enum {
|
|||
HCI_CONN_STK_ENCRYPT,
|
||||
HCI_CONN_AUTH_INITIATOR,
|
||||
HCI_CONN_DROP,
|
||||
HCI_CONN_CANCEL,
|
||||
HCI_CONN_PARAM_REMOVAL_PEND,
|
||||
HCI_CONN_NEW_LINK_KEY,
|
||||
HCI_CONN_SCANNING,
|
||||
|
|
|
@ -68,7 +68,7 @@ static const struct sco_param esco_param_msbc[] = {
|
|||
};
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
|
||||
static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
|
||||
{
|
||||
struct hci_conn_params *params;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
@ -88,9 +88,28 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
|
|||
|
||||
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr,
|
||||
bdaddr_type);
|
||||
if (!params || !params->explicit_connect)
|
||||
if (!params)
|
||||
return;
|
||||
|
||||
if (params->conn) {
|
||||
hci_conn_drop(params->conn);
|
||||
hci_conn_put(params->conn);
|
||||
params->conn = NULL;
|
||||
}
|
||||
|
||||
if (!params->explicit_connect)
|
||||
return;
|
||||
|
||||
/* If the status indicates successful cancellation of
|
||||
* the attempt (i.e. Unknown Connection Id) there's no point of
|
||||
* notifying failure since we'll go back to keep trying to
|
||||
* connect. The only exception is explicit connect requests
|
||||
* where a timeout + cancel does indicate an actual failure.
|
||||
*/
|
||||
if (status && status != HCI_ERROR_UNKNOWN_CONN_ID)
|
||||
mgmt_connect_failed(hdev, &conn->dst, conn->type,
|
||||
conn->dst_type, status);
|
||||
|
||||
/* The connection attempt was doing scan for new RPA, and is
|
||||
* in scan phase. If params are not associated with any other
|
||||
* autoconnect action, remove them completely. If they are, just unmark
|
||||
|
@ -178,7 +197,7 @@ static void le_scan_cleanup(struct work_struct *work)
|
|||
rcu_read_unlock();
|
||||
|
||||
if (c == conn) {
|
||||
hci_connect_le_scan_cleanup(conn);
|
||||
hci_connect_le_scan_cleanup(conn, 0x00);
|
||||
hci_conn_cleanup(conn);
|
||||
}
|
||||
|
||||
|
@ -1049,6 +1068,17 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
|||
return conn;
|
||||
}
|
||||
|
||||
static bool hci_conn_unlink(struct hci_conn *conn)
|
||||
{
|
||||
if (!conn->link)
|
||||
return false;
|
||||
|
||||
conn->link->link = NULL;
|
||||
conn->link = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int hci_conn_del(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
@ -1060,15 +1090,16 @@ int hci_conn_del(struct hci_conn *conn)
|
|||
cancel_delayed_work_sync(&conn->idle_work);
|
||||
|
||||
if (conn->type == ACL_LINK) {
|
||||
struct hci_conn *sco = conn->link;
|
||||
if (sco) {
|
||||
sco->link = NULL;
|
||||
struct hci_conn *link = conn->link;
|
||||
|
||||
if (link) {
|
||||
hci_conn_unlink(conn);
|
||||
/* Due to race, SCO connection might be not established
|
||||
* yet at this point. Delete it now, otherwise it is
|
||||
* possible for it to be stuck and can't be deleted.
|
||||
*/
|
||||
if (sco->handle == HCI_CONN_HANDLE_UNSET)
|
||||
hci_conn_del(sco);
|
||||
if (link->handle == HCI_CONN_HANDLE_UNSET)
|
||||
hci_conn_del(link);
|
||||
}
|
||||
|
||||
/* Unacked frames */
|
||||
|
@ -1084,7 +1115,7 @@ int hci_conn_del(struct hci_conn *conn)
|
|||
struct hci_conn *acl = conn->link;
|
||||
|
||||
if (acl) {
|
||||
acl->link = NULL;
|
||||
hci_conn_unlink(conn);
|
||||
hci_conn_drop(acl);
|
||||
}
|
||||
|
||||
|
@ -1179,31 +1210,8 @@ EXPORT_SYMBOL(hci_get_route);
|
|||
static void hci_le_conn_failed(struct hci_conn *conn, u8 status)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_conn_params *params;
|
||||
|
||||
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
|
||||
conn->dst_type);
|
||||
if (params && params->conn) {
|
||||
hci_conn_drop(params->conn);
|
||||
hci_conn_put(params->conn);
|
||||
params->conn = NULL;
|
||||
}
|
||||
|
||||
/* If the status indicates successful cancellation of
|
||||
* the attempt (i.e. Unknown Connection Id) there's no point of
|
||||
* notifying failure since we'll go back to keep trying to
|
||||
* connect. The only exception is explicit connect requests
|
||||
* where a timeout + cancel does indicate an actual failure.
|
||||
*/
|
||||
if (status != HCI_ERROR_UNKNOWN_CONN_ID ||
|
||||
(params && params->explicit_connect))
|
||||
mgmt_connect_failed(hdev, &conn->dst, conn->type,
|
||||
conn->dst_type, status);
|
||||
|
||||
/* Since we may have temporarily stopped the background scanning in
|
||||
* favor of connection establishment, we should restart it.
|
||||
*/
|
||||
hci_update_passive_scan(hdev);
|
||||
hci_connect_le_scan_cleanup(conn, status);
|
||||
|
||||
/* Enable advertising in case this was a failed connection
|
||||
* attempt as a peripheral.
|
||||
|
@ -1237,15 +1245,15 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
|||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!err) {
|
||||
hci_connect_le_scan_cleanup(conn);
|
||||
hci_connect_le_scan_cleanup(conn, 0x00);
|
||||
goto done;
|
||||
}
|
||||
|
||||
bt_dev_err(hdev, "request failed to create LE connection: err %d", err);
|
||||
|
||||
/* Check if connection is still pending */
|
||||
if (conn != hci_lookup_le_connect(hdev))
|
||||
goto done;
|
||||
|
@ -2438,6 +2446,12 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
|
|||
c->state = BT_CLOSED;
|
||||
|
||||
hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
|
||||
|
||||
/* Unlink before deleting otherwise it is possible that
|
||||
* hci_conn_del removes the link which may cause the list to
|
||||
* contain items already freed.
|
||||
*/
|
||||
hci_conn_unlink(c);
|
||||
hci_conn_del(c);
|
||||
}
|
||||
}
|
||||
|
@ -2775,6 +2789,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
|||
{
|
||||
int r = 0;
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
|
||||
return 0;
|
||||
|
||||
switch (conn->state) {
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
|
|
|
@ -2881,16 +2881,6 @@ static void cs_le_create_conn(struct hci_dev *hdev, bdaddr_t *peer_addr,
|
|||
|
||||
conn->resp_addr_type = peer_addr_type;
|
||||
bacpy(&conn->resp_addr, peer_addr);
|
||||
|
||||
/* We don't want the connection attempt to stick around
|
||||
* indefinitely since LE doesn't have a page timeout concept
|
||||
* like BR/EDR. Set a timer for any connection that doesn't use
|
||||
* the accept list for connecting.
|
||||
*/
|
||||
if (filter_policy == HCI_LE_USE_PEER_ADDR)
|
||||
queue_delayed_work(conn->hdev->workqueue,
|
||||
&conn->le_conn_timeout,
|
||||
conn->conn_timeout);
|
||||
}
|
||||
|
||||
static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status)
|
||||
|
@ -5902,6 +5892,12 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
|||
if (status)
|
||||
goto unlock;
|
||||
|
||||
/* Drop the connection if it has been aborted */
|
||||
if (test_bit(HCI_CONN_CANCEL, &conn->flags)) {
|
||||
hci_conn_drop(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (conn->dst_type == ADDR_LE_DEV_PUBLIC)
|
||||
addr_type = BDADDR_LE_PUBLIC;
|
||||
else
|
||||
|
@ -6995,7 +6991,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
|||
bis->iso_qos.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
|
||||
bis->iso_qos.in.sdu = le16_to_cpu(ev->max_pdu);
|
||||
|
||||
hci_connect_cfm(bis, ev->status);
|
||||
hci_iso_setup_path(bis);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
|
@ -246,8 +246,9 @@ int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
|
|||
|
||||
skb = __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout, sk);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "Opcode 0x%4x failed: %ld", opcode,
|
||||
PTR_ERR(skb));
|
||||
if (!event)
|
||||
bt_dev_err(hdev, "Opcode 0x%4x failed: %ld", opcode,
|
||||
PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
|
@ -5126,8 +5127,11 @@ static int hci_le_connect_cancel_sync(struct hci_dev *hdev,
|
|||
if (test_bit(HCI_CONN_SCANNING, &conn->flags))
|
||||
return 0;
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
|
||||
return 0;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_CONN_CANCEL,
|
||||
6, &conn->dst, HCI_CMD_TIMEOUT);
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
|
@ -6102,6 +6106,9 @@ int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
conn->conn_timeout, NULL);
|
||||
|
||||
done:
|
||||
if (err == -ETIMEDOUT)
|
||||
hci_le_connect_cancel_sync(hdev, conn);
|
||||
|
||||
/* Re-enable advertising after the connection attempt is finished. */
|
||||
hci_resume_advertising_sync(hdev);
|
||||
return err;
|
||||
|
|
|
@ -433,7 +433,7 @@ static void hidp_set_timer(struct hidp_session *session)
|
|||
static void hidp_del_timer(struct hidp_session *session)
|
||||
{
|
||||
if (session->idle_to > 0)
|
||||
del_timer(&session->timer);
|
||||
del_timer_sync(&session->timer);
|
||||
}
|
||||
|
||||
static void hidp_process_report(struct hidp_session *session, int type,
|
||||
|
|
|
@ -4652,33 +4652,27 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
|
|||
|
||||
BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
chan = __l2cap_get_chan_by_scid(conn, dcid);
|
||||
chan = l2cap_get_chan_by_scid(conn, dcid);
|
||||
if (!chan) {
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
l2cap_chan_hold(chan);
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
rsp.dcid = cpu_to_le16(chan->scid);
|
||||
rsp.scid = cpu_to_le16(chan->dcid);
|
||||
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
|
||||
|
||||
chan->ops->set_shutdown(chan);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
l2cap_chan_del(chan, ECONNRESET);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
chan->ops->close(chan);
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4698,33 +4692,27 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
|
|||
|
||||
BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
chan = __l2cap_get_chan_by_scid(conn, scid);
|
||||
chan = l2cap_get_chan_by_scid(conn, scid);
|
||||
if (!chan) {
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
l2cap_chan_hold(chan);
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (chan->state != BT_DISCONN) {
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
l2cap_chan_del(chan, 0);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
chan->ops->close(chan);
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -235,27 +235,41 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int sco_connect(struct hci_dev *hdev, struct sock *sk)
|
||||
static int sco_connect(struct sock *sk)
|
||||
{
|
||||
struct sco_conn *conn;
|
||||
struct hci_conn *hcon;
|
||||
struct hci_dev *hdev;
|
||||
int err, type;
|
||||
|
||||
BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
|
||||
|
||||
hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
|
||||
if (!hdev)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (lmp_esco_capable(hdev) && !disable_esco)
|
||||
type = ESCO_LINK;
|
||||
else
|
||||
type = SCO_LINK;
|
||||
|
||||
if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT &&
|
||||
(!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev)))
|
||||
return -EOPNOTSUPP;
|
||||
(!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev))) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
|
||||
sco_pi(sk)->setting, &sco_pi(sk)->codec);
|
||||
if (IS_ERR(hcon))
|
||||
return PTR_ERR(hcon);
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
conn = sco_conn_add(hcon);
|
||||
if (!conn) {
|
||||
|
@ -263,13 +277,15 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Update source addr of the socket */
|
||||
bacpy(&sco_pi(sk)->src, &hcon->src);
|
||||
|
||||
err = sco_chan_add(conn, sk, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
/* Update source addr of the socket */
|
||||
bacpy(&sco_pi(sk)->src, &hcon->src);
|
||||
|
||||
if (hcon->state == BT_CONNECTED) {
|
||||
sco_sock_clear_timer(sk);
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
|
@ -278,6 +294,13 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk)
|
|||
sco_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
return err;
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -565,7 +588,6 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
|
|||
{
|
||||
struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
|
||||
struct sock *sk = sock->sk;
|
||||
struct hci_dev *hdev;
|
||||
int err;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
@ -574,37 +596,26 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
|
|||
addr->sa_family != AF_BLUETOOTH)
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
|
||||
err = -EBADFD;
|
||||
goto done;
|
||||
}
|
||||
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND)
|
||||
return -EBADFD;
|
||||
|
||||
if (sk->sk_type != SOCK_SEQPACKET) {
|
||||
if (sk->sk_type != SOCK_SEQPACKET)
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
hdev = hci_get_route(&sa->sco_bdaddr, &sco_pi(sk)->src, BDADDR_BREDR);
|
||||
if (!hdev) {
|
||||
err = -EHOSTUNREACH;
|
||||
goto done;
|
||||
}
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
lock_sock(sk);
|
||||
/* Set destination address and psm */
|
||||
bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
|
||||
release_sock(sk);
|
||||
|
||||
err = sco_connect(hdev, sk);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
err = sco_connect(sk);
|
||||
if (err)
|
||||
goto done;
|
||||
return err;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
err = bt_sock_wait_state(sk, BT_CONNECTED,
|
||||
sock_sndtimeo(sk, flags & O_NONBLOCK));
|
||||
|
||||
done:
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
@ -1129,6 +1140,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
/* find total buffer size required to copy codec + caps */
|
||||
hci_dev_lock(hdev);
|
||||
list_for_each_entry(c, &hdev->local_codecs, list) {
|
||||
|
@ -1146,15 +1159,13 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||
buf_len += sizeof(struct bt_codecs);
|
||||
if (buf_len > len) {
|
||||
hci_dev_put(hdev);
|
||||
err = -ENOBUFS;
|
||||
break;
|
||||
return -ENOBUFS;
|
||||
}
|
||||
ptr = optval;
|
||||
|
||||
if (put_user(num_codecs, ptr)) {
|
||||
hci_dev_put(hdev);
|
||||
err = -EFAULT;
|
||||
break;
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(num_codecs);
|
||||
|
||||
|
@ -1194,12 +1205,14 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||
ptr += len;
|
||||
}
|
||||
|
||||
if (!err && put_user(buf_len, optlen))
|
||||
err = -EFAULT;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (!err && put_user(buf_len, optlen))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
Загрузка…
Ссылка в новой задаче