X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fbluetooth%2Frfcomm%2Fcore.c;h=278c8676906a3d345c00498256973b4b91533c57;hb=refs%2Fheads%2Fvserver;hp=f53a8b040ffbb03ebe241885feaca3fdda975bf7;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index f53a8b040..278c86769 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -21,17 +21,12 @@ SOFTWARE IS DISCLAIMED. */ -/* - RPN support - Dirk Husemann -*/ - /* * Bluetooth RFCOMM core. * * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $ */ -#include #include #include #include @@ -39,31 +34,37 @@ #include #include #include +#include #include -#include -#include +#include + #include #include #include #include +#include #include #include -#define VERSION "1.3" - #ifndef CONFIG_BT_RFCOMM_DEBUG #undef BT_DBG #define BT_DBG(D...) #endif -#ifdef CONFIG_PROC_FS -struct proc_dir_entry *proc_bt_rfcomm; -#endif +#define VERSION "1.8" -struct task_struct *rfcomm_thread; -DECLARE_MUTEX(rfcomm_sem); -unsigned long rfcomm_event; +static int disable_cfc = 0; +static int channel_mtu = -1; +static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU; + +static struct task_struct *rfcomm_thread; + +static DEFINE_MUTEX(rfcomm_mutex); +#define rfcomm_lock() mutex_lock(&rfcomm_mutex) +#define rfcomm_unlock() mutex_unlock(&rfcomm_mutex) + +static unsigned long rfcomm_event; static LIST_HEAD(session_list); static atomic_t terminate, running; @@ -81,6 +82,10 @@ static void rfcomm_make_uih(struct sk_buff *skb, u8 addr); 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) @@ -106,13 +111,71 @@ static void rfcomm_process_connect(struct rfcomm_session *s); #define __get_mcc_len(b) ((b & 0xfe) >> 1) /* RPN macros */ -#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3)) +#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x7) << 3)) #define __get_rpn_data_bits(line) ((line) & 0x3) #define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1) -#define __get_rpn_parity(line) (((line) >> 3) & 0x3) +#define __get_rpn_parity(line) (((line) >> 3) & 0x7) + +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 ---- */ +/* reversed, 8-bit, poly=0x07 */ +static unsigned char rfcomm_crc_table[256] = { + 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, + 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, + 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, + 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, + + 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, + 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, + 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, + 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, + + 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, + 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, + 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, + 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, + + 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, + 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, + 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, + 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, + + 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, + 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, + 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, + 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, + + 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, + 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, + 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, + 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, + + 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, + 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, + 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, + 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, + + 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, + 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, + 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, + 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf +}; + /* CRC on 2 bytes */ #define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]]) @@ -209,12 +272,12 @@ static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d) d->rx_credits = RFCOMM_DEFAULT_CREDITS; } -struct rfcomm_dlc *rfcomm_dlc_alloc(int prio) +struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio) { - struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio); + struct rfcomm_dlc *d = kzalloc(sizeof(*d), prio); + if (!d) return NULL; - memset(d, 0, sizeof(*d)); init_timer(&d->timer); d->timer.function = rfcomm_dlc_timeout; @@ -227,6 +290,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(int prio) rfcomm_dlc_clear_state(d); BT_DBG("%p", d); + return d; } @@ -458,12 +522,12 @@ int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig) } /* ---- 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); + struct rfcomm_session *s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) return NULL; - memset(s, 0, sizeof(*s)); BT_DBG("session %p sock %p", s, sock); @@ -472,7 +536,7 @@ struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) s->sock = sock; s->mtu = RFCOMM_DEFAULT_MTU; - s->cfc = RFCOMM_CFC_UNKNOWN; + s->cfc = disable_cfc ? RFCOMM_CFC_DISABLED : RFCOMM_CFC_UNKNOWN; /* Do not increment module usage count for listening sessions. * Otherwise we won't be able to unload the module. */ @@ -487,7 +551,7 @@ struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) return s; } -void rfcomm_session_del(struct rfcomm_session *s) +static void rfcomm_session_del(struct rfcomm_session *s) { int state = s->state; @@ -505,7 +569,7 @@ void rfcomm_session_del(struct rfcomm_session *s) 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; @@ -521,7 +585,7 @@ struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) 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; @@ -542,7 +606,7 @@ void rfcomm_session_close(struct rfcomm_session *s, int err) 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; @@ -565,7 +629,7 @@ struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int * /* Set L2CAP options */ sk = sock->sk; lock_sock(sk); - l2cap_pi(sk)->imtu = RFCOMM_MAX_L2CAP_MTU; + l2cap_pi(sk)->imtu = l2cap_mtu; release_sock(sk); s = rfcomm_session_add(sock, BT_BOUND); @@ -580,7 +644,7 @@ struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int * addr.l2_family = AF_BLUETOOTH; addr.l2_psm = htobs(RFCOMM_PSM); *err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); - if (*err == 0 || *err == -EAGAIN) + if (*err == 0 || *err == -EINPROGRESS) return s; rfcomm_session_del(s); @@ -749,17 +813,20 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d pn->credits = 0; } - pn->mtu = htobs(d->mtu); + if (cr && channel_mtu >= 0) + pn->mtu = htobs(channel_mtu); + else + pn->mtu = htobs(d->mtu); *ptr = __fcs(buf); ptr++; return rfcomm_send_frame(s, buf, ptr - buf); } -static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, - u8 bit_rate, u8 data_bits, u8 stop_bits, - u8 parity, u8 flow_ctrl_settings, - u8 xon_char, u8 xoff_char, u16 param_mask) +int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, + u8 bit_rate, u8 data_bits, u8 stop_bits, + u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask) { struct rfcomm_hdr *hdr; struct rfcomm_mcc *mcc; @@ -767,9 +834,9 @@ static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, u8 buf[16], *ptr = buf; BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x" - "flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", - s, cr, dlci, bit_rate, data_bits, stop_bits, parity, - flow_ctrl_settings, xon_char, xoff_char, param_mask); + " flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", + s, cr, dlci, bit_rate, data_bits, stop_bits, parity, + flow_ctrl_settings, xon_char, xoff_char, param_mask); hdr = (void *) ptr; ptr += sizeof(*hdr); hdr->addr = __addr(s->initiator, 0); @@ -787,7 +854,7 @@ static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, rpn->flow_ctrl = flow_ctrl_settings; rpn->xon_char = xon_char; rpn->xoff_char = xoff_char; - rpn->param_mask = param_mask; + rpn->param_mask = cpu_to_le16(param_mask); *ptr = __fcs(buf); ptr++; @@ -951,7 +1018,7 @@ static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) if (len > 127) { hdr = (void *) skb_push(skb, 4); - put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len); + put_unaligned(htobs(__len16(len)), (__le16 *) &hdr->len); } else { hdr = (void *) skb_push(skb, 3); hdr->len = __len8(len); @@ -1071,6 +1138,40 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) 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) +{ + struct sock *sk = d->session->sock->sk; + + 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); + + if (d->link_mode & RFCOMM_LM_MASTER) + hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00); + + 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; @@ -1093,14 +1194,13 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) 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; } @@ -1112,14 +1212,13 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) 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); } @@ -1134,17 +1233,24 @@ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn) BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits); - if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) { - d->cfc = s->cfc = RFCOMM_CFC_ENABLED; + if ((pn->flow_ctrl == 0xf0 && s->cfc != RFCOMM_CFC_DISABLED) || + pn->flow_ctrl == 0xe0) { + d->cfc = RFCOMM_CFC_ENABLED; d->tx_credits = pn->credits; } else { - d->cfc = s->cfc = RFCOMM_CFC_DISABLED; + d->cfc = RFCOMM_CFC_DISABLED; set_bit(RFCOMM_TX_THROTTLED, &d->flags); } + if (s->cfc == RFCOMM_CFC_UNKNOWN) + s->cfc = d->cfc; + d->priority = pn->priority; - d->mtu = s->mtu = btohs(pn->mtu); + d->mtu = btohs(pn->mtu); + + if (cr && d->mtu > s->mtu) + d->mtu = s->mtu; return 0; } @@ -1214,16 +1320,16 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ u8 xon_char = 0; u8 xoff_char = 0; u16 rpn_mask = RFCOMM_RPN_PM_ALL; - - BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", - dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, - rpn->xon_char, rpn->xoff_char, rpn->param_mask); - - if (!cr) + + BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", + dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, + rpn->xon_char, rpn->xoff_char, rpn->param_mask); + + if (!cr) return 0; - + if (len == 1) { - /* request: return default setting */ + /* This is a request, return default settings */ bit_rate = RFCOMM_RPN_BR_115200; data_bits = RFCOMM_RPN_DATA_8; stop_bits = RFCOMM_RPN_STOP_1; @@ -1231,12 +1337,13 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ flow_ctrl = RFCOMM_RPN_FLOW_NONE; xon_char = RFCOMM_RPN_XON_CHAR; xoff_char = RFCOMM_RPN_XOFF_CHAR; - goto rpn_out; } - /* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity, - no flow control lines, normal XON/XOFF chars */ - if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) { + + /* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit, + * no parity, no flow control lines, normal XON/XOFF chars */ + + if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_BITRATE)) { bit_rate = rpn->bit_rate; if (bit_rate != RFCOMM_RPN_BR_115200) { BT_DBG("RPN bit rate mismatch 0x%x", bit_rate); @@ -1244,7 +1351,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ rpn_mask ^= RFCOMM_RPN_PM_BITRATE; } } - if (rpn->param_mask & RFCOMM_RPN_PM_DATA) { + + if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_DATA)) { data_bits = __get_rpn_data_bits(rpn->line_settings); if (data_bits != RFCOMM_RPN_DATA_8) { BT_DBG("RPN data bits mismatch 0x%x", data_bits); @@ -1252,7 +1360,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ rpn_mask ^= RFCOMM_RPN_PM_DATA; } } - if (rpn->param_mask & RFCOMM_RPN_PM_STOP) { + + if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_STOP)) { stop_bits = __get_rpn_stop_bits(rpn->line_settings); if (stop_bits != RFCOMM_RPN_STOP_1) { BT_DBG("RPN stop bits mismatch 0x%x", stop_bits); @@ -1260,7 +1369,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ rpn_mask ^= RFCOMM_RPN_PM_STOP; } } - if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) { + + if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_PARITY)) { parity = __get_rpn_parity(rpn->line_settings); if (parity != RFCOMM_RPN_PARITY_NONE) { BT_DBG("RPN parity mismatch 0x%x", parity); @@ -1268,7 +1378,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ rpn_mask ^= RFCOMM_RPN_PM_PARITY; } } - if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) { + + if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_FLOW)) { flow_ctrl = rpn->flow_ctrl; if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) { BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl); @@ -1276,7 +1387,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ rpn_mask ^= RFCOMM_RPN_PM_FLOW; } } - if (rpn->param_mask & RFCOMM_RPN_PM_XON) { + + if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XON)) { xon_char = rpn->xon_char; if (xon_char != RFCOMM_RPN_XON_CHAR) { BT_DBG("RPN XON char mismatch 0x%x", xon_char); @@ -1284,7 +1396,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ rpn_mask ^= RFCOMM_RPN_PM_XON; } } - if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) { + + if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XOFF)) { xoff_char = rpn->xoff_char; if (xoff_char != RFCOMM_RPN_XOFF_CHAR) { BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char); @@ -1294,9 +1407,8 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ } rpn_out: - rfcomm_send_rpn(s, 0, dlci, - bit_rate, data_bits, stop_bits, parity, flow_ctrl, - xon_char, xoff_char, rpn_mask); + rfcomm_send_rpn(s, 0, dlci, bit_rate, data_bits, stop_bits, + parity, flow_ctrl, xon_char, xoff_char, rpn_mask); return 0; } @@ -1307,14 +1419,13 @@ static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb u8 dlci = __get_dlci(rls->dlci); BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status); - + if (!cr) return 0; - /* FIXME: We should probably do something with this - information here. But for now it's sufficient just - to reply -- Bluetooth 1.1 says it's mandatory to - recognise and respond to RLS */ + /* We should probably do something with this information here. But + * for now it's sufficient just to reply -- Bluetooth 1.1 says it's + * mandatory to recognise and respond to RLS */ rfcomm_send_rls(s, 0, dlci, rls->status); @@ -1330,7 +1441,7 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); d = rfcomm_dlc_get(s, dlci); - if (!d) + if (!d) return 0; if (cr) { @@ -1338,7 +1449,7 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb set_bit(RFCOMM_TX_THROTTLED, &d->flags); else clear_bit(RFCOMM_TX_THROTTLED, &d->flags); - + rfcomm_dlc_lock(d); if (d->modem_status) d->modem_status(d, msc->v24_sig); @@ -1347,7 +1458,7 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb rfcomm_send_msc(s, 0, dlci, msc->v24_sig); d->mscex |= RFCOMM_MSCEX_RX; - } else + } else d->mscex |= RFCOMM_MSCEX_TX; return 0; @@ -1457,7 +1568,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) /* 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); @@ -1468,7 +1579,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) skb_pull(skb, 3); else skb_pull(skb, 4); - + switch (type) { case RFCOMM_SABM: if (__test_pf(hdr->ctrl)) @@ -1536,7 +1647,7 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d) /* 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 */ @@ -1582,11 +1693,27 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) 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; @@ -1650,6 +1777,11 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s) s = rfcomm_session_add(nsock, BT_OPEN); if (s) { rfcomm_session_hold(s); + + /* We should adjust MTU on incoming sessions. + * L2CAP MTU minus UIH header and FCS. */ + s->mtu = min(l2cap_pi(nsock->sk)->omtu, l2cap_pi(nsock->sk)->imtu) - 5; + rfcomm_schedule(RFCOMM_SCHED_RX); } else sock_release(nsock); @@ -1710,7 +1842,7 @@ static inline void rfcomm_process_sessions(void) rfcomm_session_put(s); } - + rfcomm_unlock(); } @@ -1762,7 +1894,7 @@ static int rfcomm_add_listener(bdaddr_t *ba) /* Set L2CAP options */ sk = sock->sk; lock_sock(sk); - l2cap_pi(sk)->imtu = RFCOMM_MAX_L2CAP_MTU; + l2cap_pi(sk)->imtu = l2cap_mtu; release_sock(sk); /* Start listening on the socket */ @@ -1819,128 +1951,115 @@ static int rfcomm_run(void *unused) return 0; } -/* ---- Proc fs support ---- */ -#ifdef CONFIG_PROC_FS -static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos) +static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status) { struct rfcomm_session *s; - struct list_head *pp, *p; - loff_t l = *pos; + struct rfcomm_dlc *d; + struct list_head *p, *n; - rfcomm_lock(); + BT_DBG("conn %p status 0x%02x", conn, status); - list_for_each(p, &session_list) { - s = list_entry(p, struct rfcomm_session, list); - list_for_each(pp, &s->dlcs) - if (!l--) { - seq->private = s; - return pp; - } - } - return NULL; -} + s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); + if (!s) + return; -static void *rfcomm_seq_next(struct seq_file *seq, void *e, loff_t *pos) -{ - struct rfcomm_session *s = seq->private; - struct list_head *pp, *p = e; - (*pos)++; + rfcomm_session_hold(s); - if (p->next != &s->dlcs) - return p->next; + list_for_each_safe(p, n, &s->dlcs) { + d = list_entry(p, struct rfcomm_dlc, list); - list_for_each(p, &session_list) { - s = list_entry(p, struct rfcomm_session, list); - __list_for_each(pp, &s->dlcs) { - seq->private = s; - return pp; - } + 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); } - return NULL; -} -static void rfcomm_seq_stop(struct seq_file *seq, void *e) -{ - rfcomm_unlock(); + rfcomm_session_put(s); + + rfcomm_schedule(RFCOMM_SCHED_AUTH); } -static int rfcomm_seq_show(struct seq_file *seq, void *e) +static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) { - struct rfcomm_session *s = seq->private; - struct sock *sk = s->sock->sk; - struct rfcomm_dlc *d = list_entry(e, struct rfcomm_dlc, list); - - seq_printf(seq, "%s %s %ld %d %d %d %d\n", - batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), - d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits); - return 0; -} + struct rfcomm_session *s; + struct rfcomm_dlc *d; + struct list_head *p, *n; -static struct seq_operations rfcomm_seq_ops = { - .start = rfcomm_seq_start, - .next = rfcomm_seq_next, - .stop = rfcomm_seq_stop, - .show = rfcomm_seq_show -}; + BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt); -static int rfcomm_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &rfcomm_seq_ops); -} + s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); + if (!s) + return; -static struct file_operations rfcomm_seq_fops = { - .owner = THIS_MODULE, - .open = rfcomm_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; + rfcomm_session_hold(s); -static int __init rfcomm_proc_init(void) -{ - struct proc_dir_entry *p; + list_for_each_safe(p, n, &s->dlcs) { + d = list_entry(p, struct rfcomm_dlc, list); - proc_bt_rfcomm = proc_mkdir("rfcomm", proc_bt); - if (proc_bt_rfcomm) { - proc_bt_rfcomm->owner = THIS_MODULE; + if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) + continue; - p = create_proc_entry("dlc", S_IRUGO, proc_bt_rfcomm); - if (p) - p->proc_fops = &rfcomm_seq_fops; + if (!status && encrypt) + set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); + else + set_bit(RFCOMM_AUTH_REJECT, &d->flags); } - return 0; -} -static void __exit rfcomm_proc_cleanup(void) -{ - remove_proc_entry("dlc", proc_bt_rfcomm); + rfcomm_session_put(s); - remove_proc_entry("rfcomm", proc_bt); + rfcomm_schedule(RFCOMM_SCHED_AUTH); } -#else /* CONFIG_PROC_FS */ +static struct hci_cb rfcomm_cb = { + .name = "RFCOMM", + .auth_cfm = rfcomm_auth_cfm, + .encrypt_cfm = rfcomm_encrypt_cfm +}; -static int __init rfcomm_proc_init(void) +static ssize_t rfcomm_dlc_sysfs_show(struct class *dev, char *buf) { - return 0; -} + struct rfcomm_session *s; + struct list_head *pp, *p; + char *str = buf; -static void __exit rfcomm_proc_cleanup(void) -{ - return; + rfcomm_lock(); + + list_for_each(p, &session_list) { + s = list_entry(p, struct rfcomm_session, list); + list_for_each(pp, &s->dlcs) { + struct sock *sk = s->sock->sk; + struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list); + + str += sprintf(str, "%s %s %ld %d %d %d %d\n", + batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), + d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits); + } + } + + rfcomm_unlock(); + + return (str - buf); } -#endif /* CONFIG_PROC_FS */ + +static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL); /* ---- Initialization ---- */ static int __init rfcomm_init(void) { l2cap_load(); - kernel_thread(rfcomm_run, NULL, CLONE_KERNEL); + hci_register_cb(&rfcomm_cb); - BT_INFO("RFCOMM ver %s", VERSION); + kernel_thread(rfcomm_run, NULL, CLONE_KERNEL); - rfcomm_proc_init(); + if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0) + BT_ERR("Failed to create RFCOMM info file"); rfcomm_init_sockets(); @@ -1948,11 +2067,17 @@ static int __init rfcomm_init(void) rfcomm_init_ttys(); #endif + BT_INFO("RFCOMM ver %s", VERSION); + return 0; } static void __exit rfcomm_exit(void) { + class_remove_file(bt_class, &class_attr_rfcomm_dlc); + + hci_unregister_cb(&rfcomm_cb); + /* Terminate working thread. * ie. Set terminate flag and wake it up */ atomic_inc(&terminate); @@ -1967,13 +2092,20 @@ static void __exit rfcomm_exit(void) #endif rfcomm_cleanup_sockets(); - - rfcomm_proc_cleanup(); } module_init(rfcomm_init); module_exit(rfcomm_exit); +module_param(disable_cfc, bool, 0644); +MODULE_PARM_DESC(disable_cfc, "Disable credit based flow control"); + +module_param(channel_mtu, int, 0644); +MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel"); + +module_param(l2cap_mtu, uint, 0644); +MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection"); + MODULE_AUTHOR("Maxim Krasnyansky , Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION); MODULE_VERSION(VERSION);