Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c883f215 authored by David S. Miller's avatar David S. Miller
Browse files
parents 1ff75ed2 5dee9e7c
Loading
Loading
Loading
Loading
+8 −8
Original line number Original line Diff line number Diff line
@@ -115,11 +115,11 @@ static struct usb_device_id blacklist_ids[] = {
	{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = HCI_BCM92035 },
	{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = HCI_BCM92035 },


	/* Broadcom BCM2045 */
	/* Broadcom BCM2045 */
	{ USB_DEVICE(0x0a5c, 0x2101), .driver_info = HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x0a5c, 0x2101), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },


	/* IBM/Lenovo ThinkPad with Broadcom chip */
	/* IBM/Lenovo ThinkPad with Broadcom chip */
	{ USB_DEVICE(0x0a5c, 0x201e), .driver_info = HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x0a5c, 0x201e), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x0a5c, 0x2110), .driver_info = HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x0a5c, 0x2110), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },


	/* Targus ACB10US */
	/* Targus ACB10US */
	{ USB_DEVICE(0x0a5c, 0x2100), .driver_info = HCI_RESET },
	{ USB_DEVICE(0x0a5c, 0x2100), .driver_info = HCI_RESET },
@@ -128,17 +128,17 @@ static struct usb_device_id blacklist_ids[] = {
	{ USB_DEVICE(0x0a5c, 0x2111), .driver_info = HCI_RESET },
	{ USB_DEVICE(0x0a5c, 0x2111), .driver_info = HCI_RESET },


	/* HP laptop with Broadcom chip */
	/* HP laptop with Broadcom chip */
	{ USB_DEVICE(0x03f0, 0x171d), .driver_info = HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x03f0, 0x171d), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },


	/* Dell laptop with Broadcom chip */
	/* Dell laptop with Broadcom chip */
	{ USB_DEVICE(0x413c, 0x8126), .driver_info = HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x413c, 0x8126), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },


	/* Microsoft Wireless Transceiver for Bluetooth 2.0 */
	/* Microsoft Wireless Transceiver for Bluetooth 2.0 */
	{ USB_DEVICE(0x045e, 0x009c), .driver_info = HCI_RESET },
	{ USB_DEVICE(0x045e, 0x009c), .driver_info = HCI_RESET },


	/* Kensington Bluetooth USB adapter */
	/* Kensington Bluetooth USB adapter */
	{ USB_DEVICE(0x047d, 0x105d), .driver_info = HCI_RESET },
	{ USB_DEVICE(0x047d, 0x105d), .driver_info = HCI_RESET },
	{ USB_DEVICE(0x047d, 0x105e), .driver_info = HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x047d, 0x105e), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },


	/* ISSC Bluetooth Adapter v3.1 */
	/* ISSC Bluetooth Adapter v3.1 */
	{ USB_DEVICE(0x1131, 0x1001), .driver_info = HCI_RESET },
	{ USB_DEVICE(0x1131, 0x1001), .driver_info = HCI_RESET },
@@ -148,8 +148,8 @@ static struct usb_device_id blacklist_ids[] = {
	{ USB_DEVICE(0x0400, 0x080a), .driver_info = HCI_BROKEN_ISOC },
	{ USB_DEVICE(0x0400, 0x080a), .driver_info = HCI_BROKEN_ISOC },


	/* Belkin F8T012 and F8T013 devices */
	/* Belkin F8T012 and F8T013 devices */
	{ USB_DEVICE(0x050d, 0x0012), .driver_info = HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x050d, 0x0012), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x050d, 0x0013), .driver_info = HCI_WRONG_SCO_MTU },
	{ USB_DEVICE(0x050d, 0x0013), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },


	/* Digianswer devices */
	/* Digianswer devices */
	{ USB_DEVICE(0x08fd, 0x0001), .driver_info = HCI_DIGIANSWER },
	{ USB_DEVICE(0x08fd, 0x0001), .driver_info = HCI_DIGIANSWER },
+6 −2
Original line number Original line Diff line number Diff line
@@ -129,8 +129,10 @@ struct l2cap_conf_rsp {
	__u8       data[0];
	__u8       data[0];
} __attribute__ ((packed));
} __attribute__ ((packed));


#define L2CAP_CONF_SUCCESS	0x00
#define L2CAP_CONF_SUCCESS	0x0000
#define L2CAP_CONF_UNACCEPT	0x01
#define L2CAP_CONF_UNACCEPT	0x0001
#define L2CAP_CONF_REJECT	0x0002
#define L2CAP_CONF_UNKNOWN	0x0003


struct l2cap_conf_opt {
struct l2cap_conf_opt {
	__u8       type;
	__u8       type;
@@ -215,6 +217,8 @@ struct l2cap_pinfo {


	__u32		link_mode;
	__u32		link_mode;


	__u8		conf_req[64];
	__u8		conf_len;
	__u8		conf_state;
	__u8		conf_state;
	__u8		conf_retry;
	__u8		conf_retry;
	__u16		conf_mtu;
	__u16		conf_mtu;
+80 −64
Original line number Original line Diff line number Diff line
@@ -507,6 +507,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
	}
	}


	/* Default config options */
	/* Default config options */
	pi->conf_len = 0;
	pi->conf_mtu = L2CAP_DEFAULT_MTU;
	pi->conf_mtu = L2CAP_DEFAULT_MTU;
	pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
	pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
}
}
@@ -1271,42 +1272,6 @@ static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned
	return len;
	return len;
}
}


static inline void l2cap_parse_conf_req(struct sock *sk, void *data, int len)
{
	int type, hint, olen;
	unsigned long val;
	void *ptr = data;

	BT_DBG("sk %p len %d", sk, len);

	while (len >= L2CAP_CONF_OPT_SIZE) {
		len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);

		hint  = type & 0x80;
		type &= 0x7f;

		switch (type) {
		case L2CAP_CONF_MTU:
			l2cap_pi(sk)->conf_mtu = val;
			break;

		case L2CAP_CONF_FLUSH_TO:
			l2cap_pi(sk)->flush_to = val;
			break;

		case L2CAP_CONF_QOS:
			break;

		default:
			if (hint)
				break;

			/* FIXME: Reject unknown option */
			break;
		}
	}
}

static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
{
{
	struct l2cap_conf_opt *opt = *ptr;
	struct l2cap_conf_opt *opt = *ptr;
@@ -1358,39 +1323,75 @@ static int l2cap_build_conf_req(struct sock *sk, void *data)
	return ptr - data;
	return ptr - data;
}
}


static inline int l2cap_conf_output(struct sock *sk, void **ptr)
static int l2cap_parse_conf_req(struct sock *sk, void *data)
{
{
	struct l2cap_pinfo *pi = l2cap_pi(sk);
	struct l2cap_pinfo *pi = l2cap_pi(sk);
	int result = 0;
	struct l2cap_conf_rsp *rsp = data;
	void *ptr = rsp->data;
	void *req = pi->conf_req;
	int len = pi->conf_len;
	int type, hint, olen;
	unsigned long val;
	u16 result = L2CAP_CONF_SUCCESS;

	BT_DBG("sk %p", sk);

	while (len >= L2CAP_CONF_OPT_SIZE) {
		len -= l2cap_get_conf_opt(&req, &type, &olen, &val);


		hint  = type & 0x80;
		type &= 0x7f;

		switch (type) {
		case L2CAP_CONF_MTU:
			pi->conf_mtu = val;
			break;

		case L2CAP_CONF_FLUSH_TO:
			pi->flush_to = val;
			break;

		case L2CAP_CONF_QOS:
			break;

		default:
			if (hint)
				break;

			result = L2CAP_CONF_UNKNOWN;
			*((u8 *) ptr++) = type;
			break;
		}
	}

	if (result == L2CAP_CONF_SUCCESS) {
		/* Configure output options and let the other side know
		/* Configure output options and let the other side know
		 * which ones we don't like. */
		 * which ones we don't like. */

		if (pi->conf_mtu < pi->omtu)
		if (pi->conf_mtu < pi->omtu)
			result = L2CAP_CONF_UNACCEPT;
			result = L2CAP_CONF_UNACCEPT;
		else
		else
			pi->omtu = pi->conf_mtu;
			pi->omtu = pi->conf_mtu;


	l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu);
		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
	}

	rsp->scid   = cpu_to_le16(pi->dcid);
	rsp->result = cpu_to_le16(result);
	rsp->flags  = cpu_to_le16(0x0000);


	BT_DBG("sk %p result %d", sk, result);
	return ptr - data;
	return result;
}
}


static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result)
static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags)
{
{
	struct l2cap_conf_rsp *rsp = data;
	struct l2cap_conf_rsp *rsp = data;
	void *ptr = rsp->data;
	void *ptr = rsp->data;
	u16 flags = 0;

	BT_DBG("sk %p complete %d", sk, result ? 1 : 0);


	if (result)
	BT_DBG("sk %p", sk);
		*result = l2cap_conf_output(sk, &ptr);
	else
		flags = 0x0001;


	rsp->scid   = cpu_to_le16(l2cap_pi(sk)->dcid);
	rsp->scid   = cpu_to_le16(l2cap_pi(sk)->dcid);
	rsp->result = cpu_to_le16(result ? *result : 0);
	rsp->result = cpu_to_le16(result);
	rsp->flags  = cpu_to_le16(flags);
	rsp->flags  = cpu_to_le16(flags);


	return ptr - data;
	return ptr - data;
@@ -1535,7 +1536,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
	u16 dcid, flags;
	u16 dcid, flags;
	u8 rsp[64];
	u8 rsp[64];
	struct sock *sk;
	struct sock *sk;
	int result;
	int len;


	dcid  = __le16_to_cpu(req->dcid);
	dcid  = __le16_to_cpu(req->dcid);
	flags = __le16_to_cpu(req->flags);
	flags = __le16_to_cpu(req->flags);
@@ -1548,25 +1549,40 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
	if (sk->sk_state == BT_DISCONN)
	if (sk->sk_state == BT_DISCONN)
		goto unlock;
		goto unlock;


	l2cap_parse_conf_req(sk, req->data, cmd->len - sizeof(*req));
	/* Reject if config buffer is too small. */
	len = cmd->len - sizeof(*req);
	if (l2cap_pi(sk)->conf_len + len > sizeof(l2cap_pi(sk)->conf_req)) {
		l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
				l2cap_build_conf_rsp(sk, rsp,
					L2CAP_CONF_REJECT, flags), rsp);
		goto unlock;
	}

	/* Store config. */
	memcpy(l2cap_pi(sk)->conf_req + l2cap_pi(sk)->conf_len, req->data, len);
	l2cap_pi(sk)->conf_len += len;


	if (flags & 0x0001) {
	if (flags & 0x0001) {
		/* Incomplete config. Send empty response. */
		/* Incomplete config. Send empty response. */
		l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
		l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
				l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
				l2cap_build_conf_rsp(sk, rsp,
					L2CAP_CONF_SUCCESS, 0x0001), rsp);
		goto unlock;
		goto unlock;
	}
	}


	/* Complete config. */
	/* Complete config. */
	l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
	len = l2cap_parse_conf_req(sk, rsp);
			l2cap_build_conf_rsp(sk, rsp, &result), rsp);
	if (len < 0)

	if (result)
		goto unlock;
		goto unlock;


	/* Output config done */
	l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);

	/* Output config done. */
	l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
	l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;


	/* Reset config buffer. */
	l2cap_pi(sk)->conf_len = 0;

	if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
	if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
		sk->sk_state = BT_CONNECTED;
		sk->sk_state = BT_CONNECTED;
		l2cap_chan_ready(sk);
		l2cap_chan_ready(sk);