#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/rfcomm.h>
-#define VERSION "1.3"
+#define VERSION "1.5"
#ifndef CONFIG_BT_RFCOMM_DEBUG
#undef BT_DBG
struct proc_dir_entry *proc_bt_rfcomm;
#endif
-struct task_struct *rfcomm_thread;
-DECLARE_MUTEX(rfcomm_sem);
+static struct task_struct *rfcomm_thread;
+
+static DECLARE_MUTEX(rfcomm_sem);
+#define rfcomm_lock() down(&rfcomm_sem);
+#define rfcomm_unlock() up(&rfcomm_sem);
+
unsigned long rfcomm_event;
static LIST_HEAD(session_list);
static void rfcomm_process_connect(struct rfcomm_session *s);
+static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err);
+static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst);
+static void rfcomm_session_del(struct rfcomm_session *s);
+
/* ---- RFCOMM frame parsing macros ---- */
#define __get_dlci(b) ((b & 0xfc) >> 2)
#define __get_channel(b) ((b & 0xf8) >> 3)
#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
#define __get_rpn_parity(line) (((line) >> 3) & 0x3)
+static inline void rfcomm_schedule(uint event)
+{
+ if (!rfcomm_thread)
+ return;
+ //set_bit(event, &rfcomm_event);
+ set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
+ wake_up_process(rfcomm_thread);
+}
+
+static inline void rfcomm_session_put(struct rfcomm_session *s)
+{
+ if (atomic_dec_and_test(&s->refcnt))
+ rfcomm_session_del(s);
+}
+
/* ---- RFCOMM FCS computation ---- */
/* CRC on 2 bytes */
int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
{
- mm_segment_t fs;
int r;
rfcomm_lock();
- fs = get_fs(); set_fs(KERNEL_DS);
r = __rfcomm_dlc_open(d, src, dst, channel);
- set_fs(fs);
rfcomm_unlock();
return r;
int rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
{
- mm_segment_t fs;
int r;
rfcomm_lock();
- fs = get_fs(); set_fs(KERNEL_DS);
r = __rfcomm_dlc_close(d, err);
- set_fs(fs);
rfcomm_unlock();
return r;
}
/* ---- RFCOMM sessions ---- */
-struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
+static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
{
struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s)
return s;
}
-void rfcomm_session_del(struct rfcomm_session *s)
+static void rfcomm_session_del(struct rfcomm_session *s)
{
int state = s->state;
module_put(THIS_MODULE);
}
-struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
+static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
{
struct rfcomm_session *s;
struct list_head *p, *n;
return NULL;
}
-void rfcomm_session_close(struct rfcomm_session *s, int err)
+static void rfcomm_session_close(struct rfcomm_session *s, int err)
{
struct rfcomm_dlc *d;
struct list_head *p, *n;
rfcomm_session_put(s);
}
-struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
+static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
{
struct rfcomm_session *s = NULL;
struct sockaddr_l2 addr;
- struct l2cap_options opts;
struct socket *sock;
- int size;
+ struct sock *sk;
BT_DBG("%s %s", batostr(src), batostr(dst));
goto failed;
/* Set L2CAP options */
- size = sizeof(opts);
- sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
-
- opts.imtu = RFCOMM_MAX_L2CAP_MTU;
- sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
+ sk = sock->sk;
+ lock_sock(sk);
+ l2cap_pi(sk)->imtu = RFCOMM_MAX_L2CAP_MTU;
+ release_sock(sk);
s = rfcomm_session_add(sock, BT_BOUND);
if (!s) {
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
{
struct socket *sock = s->sock;
- struct iovec iv = { data, len };
+ struct kvec iv = { data, len };
struct msghdr msg;
BT_DBG("session %p len %d", s, len);
memset(&msg, 0, sizeof(msg));
- msg.msg_iovlen = 1;
- msg.msg_iov = &iv;
- return sock_sendmsg(sock, &msg, len);
+ return kernel_sendmsg(sock, &msg, &iv, 1, len);
}
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
{
struct socket *sock = s->sock;
- struct iovec iv[3];
+ struct kvec iv[3];
struct msghdr msg;
unsigned char hdr[5], crc[1];
iv[2].iov_len = 1;
memset(&msg, 0, sizeof(msg));
- msg.msg_iovlen = 3;
- msg.msg_iov = iv;
- return sock_sendmsg(sock, &msg, 6 + len);
+ return kernel_sendmsg(sock, &msg, iv, 3, 6 + len);
}
static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
return 0;
}
+static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
+{
+ struct sock *sk = d->session->sock->sk;
+
+ if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
+ if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
+ return 1;
+ } else if (d->link_mode & RFCOMM_LM_AUTH) {
+ if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
+{
+ BT_DBG("dlc %p", d);
+
+ rfcomm_send_ua(d->session, d->dlci);
+
+ rfcomm_dlc_lock(d);
+ d->state = BT_CONNECTED;
+ d->state_change(d, 0);
+ rfcomm_dlc_unlock(d);
+
+ rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
+}
+
static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
{
struct rfcomm_dlc *d;
if (d) {
if (d->state == BT_OPEN) {
/* DLC was previously opened by PN request */
- rfcomm_send_ua(s, dlci);
-
- rfcomm_dlc_lock(d);
- d->state = BT_CONNECTED;
- d->state_change(d, 0);
- rfcomm_dlc_unlock(d);
+ if (rfcomm_check_link_mode(d)) {
+ set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+ rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+ return 0;
+ }
- rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+ rfcomm_dlc_accept(d);
}
return 0;
}
d->addr = __addr(s->initiator, dlci);
rfcomm_dlc_link(s, d);
- rfcomm_send_ua(s, dlci);
-
- rfcomm_dlc_lock(d);
- d->state = BT_CONNECTED;
- d->state_change(d, 0);
- rfcomm_dlc_unlock(d);
+ if (rfcomm_check_link_mode(d)) {
+ set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+ rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+ return 0;
+ }
- rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+ rfcomm_dlc_accept(d);
} else {
rfcomm_send_dm(s, dlci);
}
/* Trim FCS */
skb->len--; skb->tail--;
fcs = *(u8 *) skb->tail;
-
+
if (__check_fcs(skb->data, type, fcs)) {
BT_ERR("bad checksum in packet");
kfree_skb(skb);
skb_pull(skb, 3);
else
skb_pull(skb, 4);
-
+
switch (type) {
case RFCOMM_SABM:
if (__test_pf(hdr->ctrl))
/* Send pending MSC */
if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
-
+
if (d->cfc) {
/* CFC enabled.
* Give them some credits */
list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
+
if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
__rfcomm_dlc_close(d, ETIMEDOUT);
continue;
}
+ if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) {
+ rfcomm_dlc_clear_timer(d);
+ rfcomm_dlc_accept(d);
+ if (d->link_mode & RFCOMM_LM_SECURE) {
+ struct sock *sk = s->sock->sk;
+ hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
+ }
+ continue;
+ } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
+ rfcomm_dlc_clear_timer(d);
+ rfcomm_send_dm(s, d->dlci);
+ __rfcomm_dlc_close(d, ECONNREFUSED);
+ continue;
+ }
+
if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
continue;
rfcomm_session_put(s);
}
-
+
rfcomm_unlock();
}
static int rfcomm_add_listener(bdaddr_t *ba)
{
struct sockaddr_l2 addr;
- struct l2cap_options opts;
struct socket *sock;
+ struct sock *sk;
struct rfcomm_session *s;
- int size, err = 0;
+ int err = 0;
/* Create socket */
err = rfcomm_l2sock_create(&sock);
}
/* Set L2CAP options */
- size = sizeof(opts);
- sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
-
- opts.imtu = RFCOMM_MAX_L2CAP_MTU;
- sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
+ sk = sock->sk;
+ lock_sock(sk);
+ l2cap_pi(sk)->imtu = RFCOMM_MAX_L2CAP_MTU;
+ release_sock(sk);
/* Start listening on the socket */
err = sock->ops->listen(sock, 10);
set_user_nice(current, -10);
current->flags |= PF_NOFREEZE;
- set_fs(KERNEL_DS);
-
BT_DBG("");
rfcomm_add_listener(BDADDR_ANY);
return 0;
}
+static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
+{
+ struct rfcomm_session *s;
+ struct rfcomm_dlc *d;
+ struct list_head *p, *n;
+
+ BT_DBG("conn %p status 0x%02x", conn, status);
+
+ s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
+ if (!s)
+ return;
+
+ rfcomm_session_hold(s);
+
+ list_for_each_safe(p, n, &s->dlcs) {
+ d = list_entry(p, struct rfcomm_dlc, list);
+
+ if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE))
+ continue;
+
+ if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
+ continue;
+
+ if (!status)
+ set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
+ else
+ set_bit(RFCOMM_AUTH_REJECT, &d->flags);
+ }
+
+ rfcomm_session_put(s);
+
+ rfcomm_schedule(RFCOMM_SCHED_AUTH);
+}
+
+static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
+{
+ struct rfcomm_session *s;
+ struct rfcomm_dlc *d;
+ struct list_head *p, *n;
+
+ BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
+
+ s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
+ if (!s)
+ return;
+
+ rfcomm_session_hold(s);
+
+ list_for_each_safe(p, n, &s->dlcs) {
+ d = list_entry(p, struct rfcomm_dlc, list);
+
+ if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
+ continue;
+
+ if (!status && encrypt)
+ set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
+ else
+ set_bit(RFCOMM_AUTH_REJECT, &d->flags);
+ }
+
+ rfcomm_session_put(s);
+
+ rfcomm_schedule(RFCOMM_SCHED_AUTH);
+}
+
+static struct hci_cb rfcomm_cb = {
+ .name = "RFCOMM",
+ .auth_cfm = rfcomm_auth_cfm,
+ .encrypt_cfm = rfcomm_encrypt_cfm
+};
+
/* ---- Proc fs support ---- */
#ifdef CONFIG_PROC_FS
static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos)
{
l2cap_load();
+ hci_register_cb(&rfcomm_cb);
+
kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
BT_INFO("RFCOMM ver %s", VERSION);
static void __exit rfcomm_exit(void)
{
+ hci_unregister_cb(&rfcomm_cb);
+
/* Terminate working thread.
* ie. Set terminate flag and wake it up */
atomic_inc(&terminate);