#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 */
}
/* ---- 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;
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();
}
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);