mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 03:10:12 +00:00
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
This commit is contained in:
commit
69b307a48a
13 changed files with 338 additions and 210 deletions
|
@ -43,7 +43,7 @@ static ssize_t btmrvl_hscfgcmd_write(struct file *file,
|
||||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
ret = strict_strtol(buf, 10, &result);
|
ret = kstrtol(buf, 10, &result);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
|
||||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
ret = strict_strtol(buf, 10, &result);
|
ret = kstrtol(buf, 10, &result);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
|
||||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
ret = strict_strtol(buf, 10, &result);
|
ret = kstrtol(buf, 10, &result);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
@ -486,7 +486,7 @@ static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card)
|
||||||
if (firmwarelen - offset < txlen)
|
if (firmwarelen - offset < txlen)
|
||||||
txlen = firmwarelen - offset;
|
txlen = firmwarelen - offset;
|
||||||
|
|
||||||
tx_blocks = (txlen + blksz_dl - 1) / blksz_dl;
|
tx_blocks = DIV_ROUND_UP(txlen, blksz_dl);
|
||||||
|
|
||||||
memcpy(fwbuf, &firmware[offset], txlen);
|
memcpy(fwbuf, &firmware[offset], txlen);
|
||||||
}
|
}
|
||||||
|
@ -873,7 +873,7 @@ static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
|
||||||
}
|
}
|
||||||
|
|
||||||
blksz = SDIO_BLOCK_SIZE;
|
blksz = SDIO_BLOCK_SIZE;
|
||||||
buf_block_len = (nb + blksz - 1) / blksz;
|
buf_block_len = DIV_ROUND_UP(nb, blksz);
|
||||||
|
|
||||||
sdio_claim_host(card->func);
|
sdio_claim_host(card->func);
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,14 @@ struct bt_power {
|
||||||
*/
|
*/
|
||||||
#define BT_CHANNEL_POLICY_AMP_PREFERRED 2
|
#define BT_CHANNEL_POLICY_AMP_PREFERRED 2
|
||||||
|
|
||||||
|
#define BT_VOICE 11
|
||||||
|
struct bt_voice {
|
||||||
|
__u16 setting;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BT_VOICE_TRANSPARENT 0x0003
|
||||||
|
#define BT_VOICE_CVSD_16BIT 0x0060
|
||||||
|
|
||||||
__printf(1, 2)
|
__printf(1, 2)
|
||||||
int bt_info(const char *fmt, ...);
|
int bt_info(const char *fmt, ...);
|
||||||
__printf(1, 2)
|
__printf(1, 2)
|
||||||
|
|
|
@ -238,6 +238,7 @@ enum {
|
||||||
#define LMP_CVSD 0x01
|
#define LMP_CVSD 0x01
|
||||||
#define LMP_PSCHEME 0x02
|
#define LMP_PSCHEME 0x02
|
||||||
#define LMP_PCONTROL 0x04
|
#define LMP_PCONTROL 0x04
|
||||||
|
#define LMP_TRANSPARENT 0x08
|
||||||
|
|
||||||
#define LMP_RSSI_INQ 0x40
|
#define LMP_RSSI_INQ 0x40
|
||||||
#define LMP_ESCO 0x80
|
#define LMP_ESCO 0x80
|
||||||
|
@ -296,6 +297,12 @@ enum {
|
||||||
#define HCI_AT_GENERAL_BONDING 0x04
|
#define HCI_AT_GENERAL_BONDING 0x04
|
||||||
#define HCI_AT_GENERAL_BONDING_MITM 0x05
|
#define HCI_AT_GENERAL_BONDING_MITM 0x05
|
||||||
|
|
||||||
|
/* I/O capabilities */
|
||||||
|
#define HCI_IO_DISPLAY_ONLY 0x00
|
||||||
|
#define HCI_IO_DISPLAY_YESNO 0x01
|
||||||
|
#define HCI_IO_KEYBOARD_ONLY 0x02
|
||||||
|
#define HCI_IO_NO_INPUT_OUTPUT 0x03
|
||||||
|
|
||||||
/* Link Key types */
|
/* Link Key types */
|
||||||
#define HCI_LK_COMBINATION 0x00
|
#define HCI_LK_COMBINATION 0x00
|
||||||
#define HCI_LK_LOCAL_UNIT 0x01
|
#define HCI_LK_LOCAL_UNIT 0x01
|
||||||
|
|
|
@ -320,6 +320,7 @@ struct hci_conn {
|
||||||
__u32 passkey_notify;
|
__u32 passkey_notify;
|
||||||
__u8 passkey_entered;
|
__u8 passkey_entered;
|
||||||
__u16 disc_timeout;
|
__u16 disc_timeout;
|
||||||
|
__u16 setting;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
__u8 remote_cap;
|
__u8 remote_cap;
|
||||||
|
@ -569,7 +570,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
|
||||||
}
|
}
|
||||||
|
|
||||||
void hci_disconnect(struct hci_conn *conn, __u8 reason);
|
void hci_disconnect(struct hci_conn *conn, __u8 reason);
|
||||||
void hci_setup_sync(struct hci_conn *conn, __u16 handle);
|
bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
|
||||||
void hci_sco_setup(struct hci_conn *conn, __u8 status);
|
void hci_sco_setup(struct hci_conn *conn, __u8 status);
|
||||||
|
|
||||||
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
|
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
|
||||||
|
@ -584,6 +585,8 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
|
||||||
|
|
||||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||||
__u8 dst_type, __u8 sec_level, __u8 auth_type);
|
__u8 dst_type, __u8 sec_level, __u8 auth_type);
|
||||||
|
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||||
|
__u16 setting);
|
||||||
int hci_conn_check_link_mode(struct hci_conn *conn);
|
int hci_conn_check_link_mode(struct hci_conn *conn);
|
||||||
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
|
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
|
||||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
|
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
|
||||||
|
@ -797,6 +800,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||||
#define lmp_lsto_capable(dev) ((dev)->features[0][7] & LMP_LSTO)
|
#define lmp_lsto_capable(dev) ((dev)->features[0][7] & LMP_LSTO)
|
||||||
#define lmp_inq_tx_pwr_capable(dev) ((dev)->features[0][7] & LMP_INQ_TX_PWR)
|
#define lmp_inq_tx_pwr_capable(dev) ((dev)->features[0][7] & LMP_INQ_TX_PWR)
|
||||||
#define lmp_ext_feat_capable(dev) ((dev)->features[0][7] & LMP_EXTFEATURES)
|
#define lmp_ext_feat_capable(dev) ((dev)->features[0][7] & LMP_EXTFEATURES)
|
||||||
|
#define lmp_transp_capable(dev) ((dev)->features[0][2] & LMP_TRANSPARENT)
|
||||||
|
|
||||||
/* ----- Extended LMP capabilities ----- */
|
/* ----- Extended LMP capabilities ----- */
|
||||||
#define lmp_host_ssp_capable(dev) ((dev)->features[1][0] & LMP_HOST_SSP)
|
#define lmp_host_ssp_capable(dev) ((dev)->features[1][0] & LMP_HOST_SSP)
|
||||||
|
@ -1213,4 +1217,8 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
||||||
|
|
||||||
u8 bdaddr_to_le(u8 bdaddr_type);
|
u8 bdaddr_to_le(u8 bdaddr_type);
|
||||||
|
|
||||||
|
#define SCO_AIRMODE_MASK 0x0003
|
||||||
|
#define SCO_AIRMODE_CVSD 0x0000
|
||||||
|
#define SCO_AIRMODE_TRANSP 0x0003
|
||||||
|
|
||||||
#endif /* __HCI_CORE_H */
|
#endif /* __HCI_CORE_H */
|
||||||
|
|
|
@ -73,6 +73,7 @@ struct sco_conn {
|
||||||
struct sco_pinfo {
|
struct sco_pinfo {
|
||||||
struct bt_sock bt;
|
struct bt_sock bt;
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
|
__u16 setting;
|
||||||
struct sco_conn *conn;
|
struct sco_conn *conn;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,24 @@
|
||||||
#include <net/bluetooth/a2mp.h>
|
#include <net/bluetooth/a2mp.h>
|
||||||
#include <net/bluetooth/smp.h>
|
#include <net/bluetooth/smp.h>
|
||||||
|
|
||||||
|
struct sco_param {
|
||||||
|
u16 pkt_type;
|
||||||
|
u16 max_latency;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sco_param sco_param_cvsd[] = {
|
||||||
|
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a }, /* S3 */
|
||||||
|
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007 }, /* S2 */
|
||||||
|
{ EDR_ESCO_MASK | ESCO_EV3, 0x0007 }, /* S1 */
|
||||||
|
{ EDR_ESCO_MASK | ESCO_HV3, 0xffff }, /* D1 */
|
||||||
|
{ EDR_ESCO_MASK | ESCO_HV1, 0xffff }, /* D0 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sco_param sco_param_wideband[] = {
|
||||||
|
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d }, /* T2 */
|
||||||
|
{ EDR_ESCO_MASK | ESCO_EV3, 0x0008 }, /* T1 */
|
||||||
|
};
|
||||||
|
|
||||||
static void hci_le_create_connection(struct hci_conn *conn)
|
static void hci_le_create_connection(struct hci_conn *conn)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = conn->hdev;
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
@ -172,10 +190,11 @@ static void hci_add_sco(struct hci_conn *conn, __u16 handle)
|
||||||
hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp);
|
hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = conn->hdev;
|
struct hci_dev *hdev = conn->hdev;
|
||||||
struct hci_cp_setup_sync_conn cp;
|
struct hci_cp_setup_sync_conn cp;
|
||||||
|
const struct sco_param *param;
|
||||||
|
|
||||||
BT_DBG("hcon %p", conn);
|
BT_DBG("hcon %p", conn);
|
||||||
|
|
||||||
|
@ -185,15 +204,35 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
||||||
conn->attempt++;
|
conn->attempt++;
|
||||||
|
|
||||||
cp.handle = cpu_to_le16(handle);
|
cp.handle = cpu_to_le16(handle);
|
||||||
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
|
||||||
|
|
||||||
cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
||||||
cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
||||||
cp.max_latency = __constant_cpu_to_le16(0xffff);
|
cp.voice_setting = cpu_to_le16(conn->setting);
|
||||||
cp.voice_setting = cpu_to_le16(hdev->voice_setting);
|
|
||||||
cp.retrans_effort = 0xff;
|
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp);
|
switch (conn->setting & SCO_AIRMODE_MASK) {
|
||||||
|
case SCO_AIRMODE_TRANSP:
|
||||||
|
if (conn->attempt > ARRAY_SIZE(sco_param_wideband))
|
||||||
|
return false;
|
||||||
|
cp.retrans_effort = 0x02;
|
||||||
|
param = &sco_param_wideband[conn->attempt - 1];
|
||||||
|
break;
|
||||||
|
case SCO_AIRMODE_CVSD:
|
||||||
|
if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
|
||||||
|
return false;
|
||||||
|
cp.retrans_effort = 0x01;
|
||||||
|
param = &sco_param_cvsd[conn->attempt - 1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp.pkt_type = __cpu_to_le16(param->pkt_type);
|
||||||
|
cp.max_latency = __cpu_to_le16(param->max_latency);
|
||||||
|
|
||||||
|
if (hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
||||||
|
@ -560,13 +599,13 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
return acl;
|
return acl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
|
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||||
bdaddr_t *dst, u8 sec_level, u8 auth_type)
|
__u16 setting)
|
||||||
{
|
{
|
||||||
struct hci_conn *acl;
|
struct hci_conn *acl;
|
||||||
struct hci_conn *sco;
|
struct hci_conn *sco;
|
||||||
|
|
||||||
acl = hci_connect_acl(hdev, dst, sec_level, auth_type);
|
acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
|
||||||
if (IS_ERR(acl))
|
if (IS_ERR(acl))
|
||||||
return acl;
|
return acl;
|
||||||
|
|
||||||
|
@ -584,6 +623,8 @@ static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
|
||||||
|
|
||||||
hci_conn_hold(sco);
|
hci_conn_hold(sco);
|
||||||
|
|
||||||
|
sco->setting = setting;
|
||||||
|
|
||||||
if (acl->state == BT_CONNECTED &&
|
if (acl->state == BT_CONNECTED &&
|
||||||
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
|
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
|
||||||
set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
|
set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
|
||||||
|
@ -612,9 +653,6 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||||
return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type);
|
return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type);
|
||||||
case ACL_LINK:
|
case ACL_LINK:
|
||||||
return hci_connect_acl(hdev, dst, sec_level, auth_type);
|
return hci_connect_acl(hdev, dst, sec_level, auth_type);
|
||||||
case SCO_LINK:
|
|
||||||
case ESCO_LINK:
|
|
||||||
return hci_connect_sco(hdev, type, dst, sec_level, auth_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
|
@ -454,6 +454,18 @@ static void hci_setup_event_mask(struct hci_request *req)
|
||||||
events[4] |= 0x04; /* Read Remote Extended Features Complete */
|
events[4] |= 0x04; /* Read Remote Extended Features Complete */
|
||||||
events[5] |= 0x08; /* Synchronous Connection Complete */
|
events[5] |= 0x08; /* Synchronous Connection Complete */
|
||||||
events[5] |= 0x10; /* Synchronous Connection Changed */
|
events[5] |= 0x10; /* Synchronous Connection Changed */
|
||||||
|
} else {
|
||||||
|
/* Use a different default for LE-only devices */
|
||||||
|
memset(events, 0, sizeof(events));
|
||||||
|
events[0] |= 0x10; /* Disconnection Complete */
|
||||||
|
events[0] |= 0x80; /* Encryption Change */
|
||||||
|
events[1] |= 0x08; /* Read Remote Version Information Complete */
|
||||||
|
events[1] |= 0x20; /* Command Complete */
|
||||||
|
events[1] |= 0x40; /* Command Status */
|
||||||
|
events[1] |= 0x80; /* Hardware Error */
|
||||||
|
events[2] |= 0x04; /* Number of Completed Packets */
|
||||||
|
events[3] |= 0x02; /* Data Buffer Overflow */
|
||||||
|
events[5] |= 0x80; /* Encryption Key Refresh Complete */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lmp_inq_rssi_capable(hdev))
|
if (lmp_inq_rssi_capable(hdev))
|
||||||
|
@ -608,7 +620,7 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
|
||||||
* as supported send it. If not supported assume that the controller
|
* as supported send it. If not supported assume that the controller
|
||||||
* does not have actual support for stored link keys which makes this
|
* does not have actual support for stored link keys which makes this
|
||||||
* command redundant anyway.
|
* command redundant anyway.
|
||||||
*/
|
*/
|
||||||
if (hdev->commands[6] & 0x80) {
|
if (hdev->commands[6] & 0x80) {
|
||||||
struct hci_cp_delete_stored_link_key cp;
|
struct hci_cp_delete_stored_link_key cp;
|
||||||
|
|
||||||
|
|
|
@ -2904,15 +2904,16 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
|
||||||
hci_conn_add_sysfs(conn);
|
hci_conn_add_sysfs(conn);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 0x0d: /* Connection Rejected due to Limited Resources */
|
||||||
case 0x11: /* Unsupported Feature or Parameter Value */
|
case 0x11: /* Unsupported Feature or Parameter Value */
|
||||||
case 0x1c: /* SCO interval rejected */
|
case 0x1c: /* SCO interval rejected */
|
||||||
case 0x1a: /* Unsupported Remote Feature */
|
case 0x1a: /* Unsupported Remote Feature */
|
||||||
case 0x1f: /* Unspecified error */
|
case 0x1f: /* Unspecified error */
|
||||||
if (conn->out && conn->attempt < 2) {
|
if (conn->out) {
|
||||||
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
|
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
|
||||||
(hdev->esco_type & EDR_ESCO_MASK);
|
(hdev->esco_type & EDR_ESCO_MASK);
|
||||||
hci_setup_sync(conn, conn->link->handle);
|
if (hci_setup_sync(conn, conn->link->handle))
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
||||||
|
@ -3024,17 +3025,20 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev,
|
||||||
static u8 hci_get_auth_req(struct hci_conn *conn)
|
static u8 hci_get_auth_req(struct hci_conn *conn)
|
||||||
{
|
{
|
||||||
/* If remote requests dedicated bonding follow that lead */
|
/* If remote requests dedicated bonding follow that lead */
|
||||||
if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) {
|
if (conn->remote_auth == HCI_AT_DEDICATED_BONDING ||
|
||||||
|
conn->remote_auth == HCI_AT_DEDICATED_BONDING_MITM) {
|
||||||
/* If both remote and local IO capabilities allow MITM
|
/* If both remote and local IO capabilities allow MITM
|
||||||
* protection then require it, otherwise don't */
|
* protection then require it, otherwise don't */
|
||||||
if (conn->remote_cap == 0x03 || conn->io_capability == 0x03)
|
if (conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT ||
|
||||||
return 0x02;
|
conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)
|
||||||
|
return HCI_AT_DEDICATED_BONDING;
|
||||||
else
|
else
|
||||||
return 0x03;
|
return HCI_AT_DEDICATED_BONDING_MITM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If remote requests no-bonding follow that lead */
|
/* If remote requests no-bonding follow that lead */
|
||||||
if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01)
|
if (conn->remote_auth == HCI_AT_NO_BONDING ||
|
||||||
|
conn->remote_auth == HCI_AT_NO_BONDING_MITM)
|
||||||
return conn->remote_auth | (conn->auth_type & 0x01);
|
return conn->remote_auth | (conn->auth_type & 0x01);
|
||||||
|
|
||||||
return conn->auth_type;
|
return conn->auth_type;
|
||||||
|
@ -3066,7 +3070,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
/* Change the IO capability from KeyboardDisplay
|
/* Change the IO capability from KeyboardDisplay
|
||||||
* to DisplayYesNo as it is not supported by BT spec. */
|
* to DisplayYesNo as it is not supported by BT spec. */
|
||||||
cp.capability = (conn->io_capability == 0x04) ?
|
cp.capability = (conn->io_capability == 0x04) ?
|
||||||
0x01 : conn->io_capability;
|
HCI_IO_DISPLAY_YESNO : conn->io_capability;
|
||||||
conn->auth_type = hci_get_auth_req(conn);
|
conn->auth_type = hci_get_auth_req(conn);
|
||||||
cp.authentication = conn->auth_type;
|
cp.authentication = conn->auth_type;
|
||||||
|
|
||||||
|
@ -3140,7 +3144,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
|
||||||
* request. The only exception is when we're dedicated bonding
|
* request. The only exception is when we're dedicated bonding
|
||||||
* initiators (connect_cfm_cb set) since then we always have the MITM
|
* initiators (connect_cfm_cb set) since then we always have the MITM
|
||||||
* bit set. */
|
* bit set. */
|
||||||
if (!conn->connect_cfm_cb && loc_mitm && conn->remote_cap == 0x03) {
|
if (!conn->connect_cfm_cb && loc_mitm &&
|
||||||
|
conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
|
||||||
BT_DBG("Rejecting request: remote device can't provide MITM");
|
BT_DBG("Rejecting request: remote device can't provide MITM");
|
||||||
hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY,
|
hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY,
|
||||||
sizeof(ev->bdaddr), &ev->bdaddr);
|
sizeof(ev->bdaddr), &ev->bdaddr);
|
||||||
|
@ -3148,8 +3153,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If no side requires MITM protection; auto-accept */
|
/* If no side requires MITM protection; auto-accept */
|
||||||
if ((!loc_mitm || conn->remote_cap == 0x03) &&
|
if ((!loc_mitm || conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) &&
|
||||||
(!rem_mitm || conn->io_capability == 0x03)) {
|
(!rem_mitm || conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)) {
|
||||||
|
|
||||||
/* If we're not the initiators request authorization to
|
/* If we're not the initiators request authorization to
|
||||||
* proceed from user space (mgmt_user_confirm with
|
* proceed from user space (mgmt_user_confirm with
|
||||||
|
|
|
@ -238,6 +238,31 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep
|
||||||
return hidp_send_intr_message(session, hdr, buf, rsize);
|
return hidp_send_intr_message(session, hdr, buf, rsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hidp_hidinput_event(struct input_dev *dev, unsigned int type,
|
||||||
|
unsigned int code, int value)
|
||||||
|
{
|
||||||
|
struct hid_device *hid = input_get_drvdata(dev);
|
||||||
|
struct hidp_session *session = hid->driver_data;
|
||||||
|
struct hid_field *field;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
BT_DBG("session %p type %d code %d value %d",
|
||||||
|
session, type, code, value);
|
||||||
|
|
||||||
|
if (type != EV_LED)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
offset = hidinput_find_field(hid, type, code, &field);
|
||||||
|
if (offset == -1) {
|
||||||
|
hid_warn(dev, "event field not found\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_set_field(field, offset, value);
|
||||||
|
|
||||||
|
return hidp_send_report(session, field->report);
|
||||||
|
}
|
||||||
|
|
||||||
static int hidp_get_raw_report(struct hid_device *hid,
|
static int hidp_get_raw_report(struct hid_device *hid,
|
||||||
unsigned char report_number,
|
unsigned char report_number,
|
||||||
unsigned char *data, size_t count,
|
unsigned char *data, size_t count,
|
||||||
|
@ -678,20 +703,6 @@ static int hidp_parse(struct hid_device *hid)
|
||||||
|
|
||||||
static int hidp_start(struct hid_device *hid)
|
static int hidp_start(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct hidp_session *session = hid->driver_data;
|
|
||||||
struct hid_report *report;
|
|
||||||
|
|
||||||
if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
|
|
||||||
report_list, list)
|
|
||||||
hidp_send_report(session, report);
|
|
||||||
|
|
||||||
list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
|
|
||||||
report_list, list)
|
|
||||||
hidp_send_report(session, report);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,6 +722,7 @@ static struct hid_ll_driver hidp_hid_driver = {
|
||||||
.stop = hidp_stop,
|
.stop = hidp_stop,
|
||||||
.open = hidp_open,
|
.open = hidp_open,
|
||||||
.close = hidp_close,
|
.close = hidp_close,
|
||||||
|
.hidinput_input_event = hidp_hidinput_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This function sets up the hid device. It does not add it
|
/* This function sets up the hid device. It does not add it
|
||||||
|
|
|
@ -1415,8 +1415,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
|
||||||
sk->sk_state_change(sk);
|
sk->sk_state_change(sk);
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
|
|
||||||
} else if (chan->state == BT_CONNECT)
|
} else if (chan->state == BT_CONNECT) {
|
||||||
l2cap_do_start(chan);
|
l2cap_do_start(chan);
|
||||||
|
}
|
||||||
|
|
||||||
l2cap_chan_unlock(chan);
|
l2cap_chan_unlock(chan);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,6 @@ struct rfcomm_dev {
|
||||||
uint modem_status;
|
uint modem_status;
|
||||||
|
|
||||||
struct rfcomm_dlc *dlc;
|
struct rfcomm_dlc *dlc;
|
||||||
wait_queue_head_t wait;
|
|
||||||
|
|
||||||
struct device *tty_dev;
|
struct device *tty_dev;
|
||||||
|
|
||||||
|
@ -76,13 +75,6 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
|
||||||
|
|
||||||
/* ---- Device functions ---- */
|
/* ---- Device functions ---- */
|
||||||
|
|
||||||
/*
|
|
||||||
* The reason this isn't actually a race, as you no doubt have a little voice
|
|
||||||
* screaming at you in your head, is that the refcount should never actually
|
|
||||||
* reach zero unless the device has already been taken off the list, in
|
|
||||||
* rfcomm_dev_del(). And if that's not true, we'll hit the BUG() in
|
|
||||||
* rfcomm_dev_destruct() anyway.
|
|
||||||
*/
|
|
||||||
static void rfcomm_dev_destruct(struct tty_port *port)
|
static void rfcomm_dev_destruct(struct tty_port *port)
|
||||||
{
|
{
|
||||||
struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
|
struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
|
||||||
|
@ -90,10 +82,9 @@ static void rfcomm_dev_destruct(struct tty_port *port)
|
||||||
|
|
||||||
BT_DBG("dev %p dlc %p", dev, dlc);
|
BT_DBG("dev %p dlc %p", dev, dlc);
|
||||||
|
|
||||||
/* Refcount should only hit zero when called from rfcomm_dev_del()
|
spin_lock(&rfcomm_dev_lock);
|
||||||
which will have taken us off the list. Everything else are
|
list_del(&dev->list);
|
||||||
refcounting bugs. */
|
spin_unlock(&rfcomm_dev_lock);
|
||||||
BUG_ON(!list_empty(&dev->list));
|
|
||||||
|
|
||||||
rfcomm_dlc_lock(dlc);
|
rfcomm_dlc_lock(dlc);
|
||||||
/* Detach DLC if it's owned by this dev */
|
/* Detach DLC if it's owned by this dev */
|
||||||
|
@ -112,8 +103,39 @@ static void rfcomm_dev_destruct(struct tty_port *port)
|
||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* device-specific initialization: open the dlc */
|
||||||
|
static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty)
|
||||||
|
{
|
||||||
|
struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
|
||||||
|
|
||||||
|
return rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we block the open until the dlc->state becomes BT_CONNECTED */
|
||||||
|
static int rfcomm_dev_carrier_raised(struct tty_port *port)
|
||||||
|
{
|
||||||
|
struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
|
||||||
|
|
||||||
|
return (dev->dlc->state == BT_CONNECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* device-specific cleanup: close the dlc */
|
||||||
|
static void rfcomm_dev_shutdown(struct tty_port *port)
|
||||||
|
{
|
||||||
|
struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
|
||||||
|
|
||||||
|
if (dev->tty_dev->parent)
|
||||||
|
device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
|
||||||
|
|
||||||
|
/* close the dlc */
|
||||||
|
rfcomm_dlc_close(dev->dlc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct tty_port_operations rfcomm_port_ops = {
|
static const struct tty_port_operations rfcomm_port_ops = {
|
||||||
.destruct = rfcomm_dev_destruct,
|
.destruct = rfcomm_dev_destruct,
|
||||||
|
.activate = rfcomm_dev_activate,
|
||||||
|
.shutdown = rfcomm_dev_shutdown,
|
||||||
|
.carrier_raised = rfcomm_dev_carrier_raised,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct rfcomm_dev *__rfcomm_dev_get(int id)
|
static struct rfcomm_dev *__rfcomm_dev_get(int id)
|
||||||
|
@ -236,7 +258,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
|
||||||
|
|
||||||
tty_port_init(&dev->port);
|
tty_port_init(&dev->port);
|
||||||
dev->port.ops = &rfcomm_port_ops;
|
dev->port.ops = &rfcomm_port_ops;
|
||||||
init_waitqueue_head(&dev->wait);
|
|
||||||
|
|
||||||
skb_queue_head_init(&dev->pending);
|
skb_queue_head_init(&dev->pending);
|
||||||
|
|
||||||
|
@ -282,7 +303,9 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
|
||||||
dev->id, NULL);
|
dev->id, NULL);
|
||||||
if (IS_ERR(dev->tty_dev)) {
|
if (IS_ERR(dev->tty_dev)) {
|
||||||
err = PTR_ERR(dev->tty_dev);
|
err = PTR_ERR(dev->tty_dev);
|
||||||
|
spin_lock(&rfcomm_dev_lock);
|
||||||
list_del(&dev->list);
|
list_del(&dev->list);
|
||||||
|
spin_unlock(&rfcomm_dev_lock);
|
||||||
goto free;
|
goto free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,27 +324,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rfcomm_dev_del(struct rfcomm_dev *dev)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
BT_DBG("dev %p", dev);
|
|
||||||
|
|
||||||
BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags));
|
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->port.lock, flags);
|
|
||||||
if (dev->port.count > 0) {
|
|
||||||
spin_unlock_irqrestore(&dev->port.lock, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&dev->port.lock, flags);
|
|
||||||
|
|
||||||
spin_lock(&rfcomm_dev_lock);
|
|
||||||
list_del_init(&dev->list);
|
|
||||||
spin_unlock(&rfcomm_dev_lock);
|
|
||||||
|
|
||||||
tty_port_put(&dev->port);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Send buffer ---- */
|
/* ---- Send buffer ---- */
|
||||||
static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
|
static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
|
||||||
{
|
{
|
||||||
|
@ -333,10 +335,9 @@ static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
|
||||||
static void rfcomm_wfree(struct sk_buff *skb)
|
static void rfcomm_wfree(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct rfcomm_dev *dev = (void *) skb->sk;
|
struct rfcomm_dev *dev = (void *) skb->sk;
|
||||||
struct tty_struct *tty = dev->port.tty;
|
|
||||||
atomic_sub(skb->truesize, &dev->wmem_alloc);
|
atomic_sub(skb->truesize, &dev->wmem_alloc);
|
||||||
if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags) && tty)
|
if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
|
||||||
tty_wakeup(tty);
|
tty_port_tty_wakeup(&dev->port);
|
||||||
tty_port_put(&dev->port);
|
tty_port_put(&dev->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +411,7 @@ static int rfcomm_release_dev(void __user *arg)
|
||||||
{
|
{
|
||||||
struct rfcomm_dev_req req;
|
struct rfcomm_dev_req req;
|
||||||
struct rfcomm_dev *dev;
|
struct rfcomm_dev *dev;
|
||||||
|
struct tty_struct *tty;
|
||||||
|
|
||||||
if (copy_from_user(&req, arg, sizeof(req)))
|
if (copy_from_user(&req, arg, sizeof(req)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -429,11 +431,15 @@ static int rfcomm_release_dev(void __user *arg)
|
||||||
rfcomm_dlc_close(dev->dlc, 0);
|
rfcomm_dlc_close(dev->dlc, 0);
|
||||||
|
|
||||||
/* Shut down TTY synchronously before freeing rfcomm_dev */
|
/* Shut down TTY synchronously before freeing rfcomm_dev */
|
||||||
if (dev->port.tty)
|
tty = tty_port_tty_get(&dev->port);
|
||||||
tty_vhangup(dev->port.tty);
|
if (tty) {
|
||||||
|
tty_vhangup(tty);
|
||||||
|
tty_kref_put(tty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
|
||||||
|
tty_port_put(&dev->port);
|
||||||
|
|
||||||
if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
|
|
||||||
rfcomm_dev_del(dev);
|
|
||||||
tty_port_put(&dev->port);
|
tty_port_put(&dev->port);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -563,16 +569,21 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
|
||||||
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
|
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
|
||||||
{
|
{
|
||||||
struct rfcomm_dev *dev = dlc->owner;
|
struct rfcomm_dev *dev = dlc->owner;
|
||||||
|
struct tty_struct *tty;
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
|
BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
|
||||||
|
|
||||||
dev->err = err;
|
dev->err = err;
|
||||||
wake_up_interruptible(&dev->wait);
|
if (dlc->state == BT_CONNECTED) {
|
||||||
|
device_move(dev->tty_dev, rfcomm_get_device(dev),
|
||||||
|
DPM_ORDER_DEV_AFTER_PARENT);
|
||||||
|
|
||||||
if (dlc->state == BT_CLOSED) {
|
wake_up_interruptible(&dev->port.open_wait);
|
||||||
if (!dev->port.tty) {
|
} else if (dlc->state == BT_CLOSED) {
|
||||||
|
tty = tty_port_tty_get(&dev->port);
|
||||||
|
if (!tty) {
|
||||||
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
|
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
|
||||||
/* Drop DLC lock here to avoid deadlock
|
/* Drop DLC lock here to avoid deadlock
|
||||||
* 1. rfcomm_dev_get will take rfcomm_dev_lock
|
* 1. rfcomm_dev_get will take rfcomm_dev_lock
|
||||||
|
@ -580,6 +591,9 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
|
||||||
* rfcomm_dev_lock -> dlc lock
|
* rfcomm_dev_lock -> dlc lock
|
||||||
* 2. tty_port_put will deadlock if it's
|
* 2. tty_port_put will deadlock if it's
|
||||||
* the last reference
|
* the last reference
|
||||||
|
*
|
||||||
|
* FIXME: when we release the lock anything
|
||||||
|
* could happen to dev, even its destruction
|
||||||
*/
|
*/
|
||||||
rfcomm_dlc_unlock(dlc);
|
rfcomm_dlc_unlock(dlc);
|
||||||
if (rfcomm_dev_get(dev->id) == NULL) {
|
if (rfcomm_dev_get(dev->id) == NULL) {
|
||||||
|
@ -587,12 +601,17 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rfcomm_dev_del(dev);
|
if (!test_and_set_bit(RFCOMM_TTY_RELEASED,
|
||||||
|
&dev->flags))
|
||||||
|
tty_port_put(&dev->port);
|
||||||
|
|
||||||
tty_port_put(&dev->port);
|
tty_port_put(&dev->port);
|
||||||
rfcomm_dlc_lock(dlc);
|
rfcomm_dlc_lock(dlc);
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
tty_hangup(dev->port.tty);
|
tty_hangup(tty);
|
||||||
|
tty_kref_put(tty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,10 +623,8 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
|
||||||
|
|
||||||
BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
|
BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
|
||||||
|
|
||||||
if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) {
|
if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV))
|
||||||
if (dev->port.tty && !C_CLOCAL(dev->port.tty))
|
tty_port_tty_hangup(&dev->port, true);
|
||||||
tty_hangup(dev->port.tty);
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->modem_status =
|
dev->modem_status =
|
||||||
((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) |
|
((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) |
|
||||||
|
@ -638,124 +655,92 @@ static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev)
|
||||||
tty_flip_buffer_push(&dev->port);
|
tty_flip_buffer_push(&dev->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
|
/* do the reverse of install, clearing the tty fields and releasing the
|
||||||
|
* reference to tty_port
|
||||||
|
*/
|
||||||
|
static void rfcomm_tty_cleanup(struct tty_struct *tty)
|
||||||
|
{
|
||||||
|
struct rfcomm_dev *dev = tty->driver_data;
|
||||||
|
|
||||||
|
clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
|
||||||
|
|
||||||
|
rfcomm_dlc_lock(dev->dlc);
|
||||||
|
tty->driver_data = NULL;
|
||||||
|
rfcomm_dlc_unlock(dev->dlc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* purge the dlc->tx_queue to avoid circular dependencies
|
||||||
|
* between dev and dlc
|
||||||
|
*/
|
||||||
|
skb_queue_purge(&dev->dlc->tx_queue);
|
||||||
|
|
||||||
|
tty_port_put(&dev->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we acquire the tty_port reference since it's here the tty is first used
|
||||||
|
* by setting the termios. We also populate the driver_data field and install
|
||||||
|
* the tty port
|
||||||
|
*/
|
||||||
|
static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
struct rfcomm_dev *dev;
|
struct rfcomm_dev *dev;
|
||||||
struct rfcomm_dlc *dlc;
|
struct rfcomm_dlc *dlc;
|
||||||
unsigned long flags;
|
int err;
|
||||||
int err, id;
|
|
||||||
|
|
||||||
id = tty->index;
|
dev = rfcomm_dev_get(tty->index);
|
||||||
|
|
||||||
BT_DBG("tty %p id %d", tty, id);
|
|
||||||
|
|
||||||
/* We don't leak this refcount. For reasons which are not entirely
|
|
||||||
clear, the TTY layer will call our ->close() method even if the
|
|
||||||
open fails. We decrease the refcount there, and decreasing it
|
|
||||||
here too would cause breakage. */
|
|
||||||
dev = rfcomm_dev_get(id);
|
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
|
|
||||||
dev->channel, dev->port.count);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->port.lock, flags);
|
|
||||||
if (++dev->port.count > 1) {
|
|
||||||
spin_unlock_irqrestore(&dev->port.lock, flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&dev->port.lock, flags);
|
|
||||||
|
|
||||||
dlc = dev->dlc;
|
dlc = dev->dlc;
|
||||||
|
|
||||||
/* Attach TTY and open DLC */
|
/* Attach TTY and open DLC */
|
||||||
|
|
||||||
rfcomm_dlc_lock(dlc);
|
rfcomm_dlc_lock(dlc);
|
||||||
tty->driver_data = dev;
|
tty->driver_data = dev;
|
||||||
dev->port.tty = tty;
|
|
||||||
rfcomm_dlc_unlock(dlc);
|
rfcomm_dlc_unlock(dlc);
|
||||||
set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
|
set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
|
||||||
|
|
||||||
err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
|
/* install the tty_port */
|
||||||
if (err < 0)
|
err = tty_port_install(&dev->port, driver, tty);
|
||||||
|
if (err)
|
||||||
|
rfcomm_tty_cleanup(tty);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||||
|
{
|
||||||
|
struct rfcomm_dev *dev = tty->driver_data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("tty %p id %d", tty, tty->index);
|
||||||
|
|
||||||
|
BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
|
||||||
|
dev->channel, dev->port.count);
|
||||||
|
|
||||||
|
err = tty_port_open(&dev->port, tty, filp);
|
||||||
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* Wait for DLC to connect */
|
/*
|
||||||
add_wait_queue(&dev->wait, &wait);
|
* FIXME: rfcomm should use proper flow control for
|
||||||
while (1) {
|
* received data. This hack will be unnecessary and can
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
* be removed when that's implemented
|
||||||
|
*/
|
||||||
if (dlc->state == BT_CLOSED) {
|
|
||||||
err = -dev->err;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dlc->state == BT_CONNECTED)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (signal_pending(current)) {
|
|
||||||
err = -EINTR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
tty_unlock(tty);
|
|
||||||
schedule();
|
|
||||||
tty_lock(tty);
|
|
||||||
}
|
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
remove_wait_queue(&dev->wait, &wait);
|
|
||||||
|
|
||||||
if (err == 0)
|
|
||||||
device_move(dev->tty_dev, rfcomm_get_device(dev),
|
|
||||||
DPM_ORDER_DEV_AFTER_PARENT);
|
|
||||||
|
|
||||||
rfcomm_tty_copy_pending(dev);
|
rfcomm_tty_copy_pending(dev);
|
||||||
|
|
||||||
rfcomm_dlc_unthrottle(dev->dlc);
|
rfcomm_dlc_unthrottle(dev->dlc);
|
||||||
|
|
||||||
return err;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
|
static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
|
||||||
{
|
{
|
||||||
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
|
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
if (!dev)
|
|
||||||
return;
|
|
||||||
|
|
||||||
BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
|
BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
|
||||||
dev->port.count);
|
dev->port.count);
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->port.lock, flags);
|
tty_port_close(&dev->port, tty, filp);
|
||||||
if (!--dev->port.count) {
|
|
||||||
spin_unlock_irqrestore(&dev->port.lock, flags);
|
|
||||||
if (dev->tty_dev->parent)
|
|
||||||
device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
|
|
||||||
|
|
||||||
/* Close DLC and dettach TTY */
|
|
||||||
rfcomm_dlc_close(dev->dlc, 0);
|
|
||||||
|
|
||||||
clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
|
|
||||||
|
|
||||||
rfcomm_dlc_lock(dev->dlc);
|
|
||||||
tty->driver_data = NULL;
|
|
||||||
dev->port.tty = NULL;
|
|
||||||
rfcomm_dlc_unlock(dev->dlc);
|
|
||||||
|
|
||||||
if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) {
|
|
||||||
spin_lock(&rfcomm_dev_lock);
|
|
||||||
list_del_init(&dev->list);
|
|
||||||
spin_unlock(&rfcomm_dev_lock);
|
|
||||||
|
|
||||||
tty_port_put(&dev->port);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
spin_unlock_irqrestore(&dev->port.lock, flags);
|
|
||||||
|
|
||||||
tty_port_put(&dev->port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||||
|
@ -1055,17 +1040,11 @@ static void rfcomm_tty_hangup(struct tty_struct *tty)
|
||||||
|
|
||||||
BT_DBG("tty %p dev %p", tty, dev);
|
BT_DBG("tty %p dev %p", tty, dev);
|
||||||
|
|
||||||
if (!dev)
|
tty_port_hangup(&dev->port);
|
||||||
return;
|
|
||||||
|
|
||||||
rfcomm_tty_flush_buffer(tty);
|
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
|
||||||
|
!test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
|
||||||
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
|
|
||||||
if (rfcomm_dev_get(dev->id) == NULL)
|
|
||||||
return;
|
|
||||||
rfcomm_dev_del(dev);
|
|
||||||
tty_port_put(&dev->port);
|
tty_port_put(&dev->port);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rfcomm_tty_tiocmget(struct tty_struct *tty)
|
static int rfcomm_tty_tiocmget(struct tty_struct *tty)
|
||||||
|
@ -1128,6 +1107,8 @@ static const struct tty_operations rfcomm_ops = {
|
||||||
.wait_until_sent = rfcomm_tty_wait_until_sent,
|
.wait_until_sent = rfcomm_tty_wait_until_sent,
|
||||||
.tiocmget = rfcomm_tty_tiocmget,
|
.tiocmget = rfcomm_tty_tiocmget,
|
||||||
.tiocmset = rfcomm_tty_tiocmset,
|
.tiocmset = rfcomm_tty_tiocmset,
|
||||||
|
.install = rfcomm_tty_install,
|
||||||
|
.cleanup = rfcomm_tty_cleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
int __init rfcomm_init_ttys(void)
|
int __init rfcomm_init_ttys(void)
|
||||||
|
@ -1146,7 +1127,7 @@ int __init rfcomm_init_ttys(void)
|
||||||
rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||||
rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||||
rfcomm_tty_driver->init_termios = tty_std_termios;
|
rfcomm_tty_driver->init_termios = tty_std_termios;
|
||||||
rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
|
||||||
rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
|
rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
|
||||||
tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
|
tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
|
||||||
|
|
||||||
|
|
|
@ -176,8 +176,13 @@ static int sco_connect(struct sock *sk)
|
||||||
else
|
else
|
||||||
type = SCO_LINK;
|
type = SCO_LINK;
|
||||||
|
|
||||||
hcon = hci_connect(hdev, type, dst, BDADDR_BREDR, BT_SECURITY_LOW,
|
if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT &&
|
||||||
HCI_AT_NO_BONDING);
|
(!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev))) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hcon = hci_connect_sco(hdev, type, dst, sco_pi(sk)->setting);
|
||||||
if (IS_ERR(hcon)) {
|
if (IS_ERR(hcon)) {
|
||||||
err = PTR_ERR(hcon);
|
err = PTR_ERR(hcon);
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -417,6 +422,8 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int pro
|
||||||
sk->sk_protocol = proto;
|
sk->sk_protocol = proto;
|
||||||
sk->sk_state = BT_OPEN;
|
sk->sk_state = BT_OPEN;
|
||||||
|
|
||||||
|
sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
|
||||||
|
|
||||||
setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk);
|
setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk);
|
||||||
|
|
||||||
bt_sock_link(&sco_sk_list, sk);
|
bt_sock_link(&sco_sk_list, sk);
|
||||||
|
@ -652,7 +659,7 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sco_conn_defer_accept(struct hci_conn *conn, int mask)
|
static void sco_conn_defer_accept(struct hci_conn *conn, u16 setting)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = conn->hdev;
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
|
||||||
|
@ -664,11 +671,7 @@ static void sco_conn_defer_accept(struct hci_conn *conn, int mask)
|
||||||
struct hci_cp_accept_conn_req cp;
|
struct hci_cp_accept_conn_req cp;
|
||||||
|
|
||||||
bacpy(&cp.bdaddr, &conn->dst);
|
bacpy(&cp.bdaddr, &conn->dst);
|
||||||
|
cp.role = 0x00; /* Ignored */
|
||||||
if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
|
|
||||||
cp.role = 0x00; /* Become master */
|
|
||||||
else
|
|
||||||
cp.role = 0x01; /* Remain slave */
|
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp);
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp);
|
||||||
} else {
|
} else {
|
||||||
|
@ -679,9 +682,21 @@ static void sco_conn_defer_accept(struct hci_conn *conn, int mask)
|
||||||
|
|
||||||
cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
||||||
cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
|
||||||
cp.max_latency = __constant_cpu_to_le16(0xffff);
|
cp.content_format = cpu_to_le16(setting);
|
||||||
cp.content_format = cpu_to_le16(hdev->voice_setting);
|
|
||||||
cp.retrans_effort = 0xff;
|
switch (setting & SCO_AIRMODE_MASK) {
|
||||||
|
case SCO_AIRMODE_TRANSP:
|
||||||
|
if (conn->pkt_type & ESCO_2EV3)
|
||||||
|
cp.max_latency = __constant_cpu_to_le16(0x0008);
|
||||||
|
else
|
||||||
|
cp.max_latency = __constant_cpu_to_le16(0x000D);
|
||||||
|
cp.retrans_effort = 0x02;
|
||||||
|
break;
|
||||||
|
case SCO_AIRMODE_CVSD:
|
||||||
|
cp.max_latency = __constant_cpu_to_le16(0xffff);
|
||||||
|
cp.retrans_effort = 0xff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
|
||||||
sizeof(cp), &cp);
|
sizeof(cp), &cp);
|
||||||
|
@ -698,7 +713,7 @@ static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
|
|
||||||
if (sk->sk_state == BT_CONNECT2 &&
|
if (sk->sk_state == BT_CONNECT2 &&
|
||||||
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
|
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
|
||||||
sco_conn_defer_accept(pi->conn->hcon, 0);
|
sco_conn_defer_accept(pi->conn->hcon, pi->setting);
|
||||||
sk->sk_state = BT_CONFIG;
|
sk->sk_state = BT_CONFIG;
|
||||||
msg->msg_namelen = 0;
|
msg->msg_namelen = 0;
|
||||||
|
|
||||||
|
@ -714,7 +729,8 @@ static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
|
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
int err = 0;
|
int len, err = 0;
|
||||||
|
struct bt_voice voice;
|
||||||
u32 opt;
|
u32 opt;
|
||||||
|
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
|
@ -740,6 +756,31 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
|
||||||
clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
|
clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BT_VOICE:
|
||||||
|
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND &&
|
||||||
|
sk->sk_state != BT_CONNECT2) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice.setting = sco_pi(sk)->setting;
|
||||||
|
|
||||||
|
len = min_t(unsigned int, sizeof(voice), optlen);
|
||||||
|
if (copy_from_user((char *) &voice, optval, len)) {
|
||||||
|
err = -EFAULT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Explicitly check for these values */
|
||||||
|
if (voice.setting != BT_VOICE_TRANSPARENT &&
|
||||||
|
voice.setting != BT_VOICE_CVSD_16BIT) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sco_pi(sk)->setting = voice.setting;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = -ENOPROTOOPT;
|
err = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
|
@ -765,7 +806,9 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user
|
||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
case SCO_OPTIONS:
|
case SCO_OPTIONS:
|
||||||
if (sk->sk_state != BT_CONNECTED) {
|
if (sk->sk_state != BT_CONNECTED &&
|
||||||
|
!(sk->sk_state == BT_CONNECT2 &&
|
||||||
|
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))) {
|
||||||
err = -ENOTCONN;
|
err = -ENOTCONN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -781,7 +824,9 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCO_CONNINFO:
|
case SCO_CONNINFO:
|
||||||
if (sk->sk_state != BT_CONNECTED) {
|
if (sk->sk_state != BT_CONNECTED &&
|
||||||
|
!(sk->sk_state == BT_CONNECT2 &&
|
||||||
|
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))) {
|
||||||
err = -ENOTCONN;
|
err = -ENOTCONN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -809,6 +854,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
int len, err = 0;
|
int len, err = 0;
|
||||||
|
struct bt_voice voice;
|
||||||
|
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
|
|
||||||
|
@ -834,6 +880,15 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BT_VOICE:
|
||||||
|
voice.setting = sco_pi(sk)->setting;
|
||||||
|
|
||||||
|
len = min_t(unsigned int, len, sizeof(voice));
|
||||||
|
if (copy_to_user(optval, (char *)&voice, len))
|
||||||
|
err = -EFAULT;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = -ENOPROTOOPT;
|
err = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue