Merge git://git.kernel.org/pub/scm/linux/kernel/git/holtmann/bluetooth-2.6

This commit is contained in:
David S. Miller 2007-05-24 16:54:12 -07:00
commit c883f215a2
3 changed files with 94 additions and 74 deletions

View File

@ -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 },

View File

@ -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;

View File

@ -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);
}
BT_DBG("sk %p result %d", sk, result); rsp->scid = cpu_to_le16(pi->dcid);
return result; rsp->result = cpu_to_le16(result);
rsp->flags = cpu_to_le16(0x0000);
return ptr - data;
} }
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); BT_DBG("sk %p", sk);
if (result)
*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);