]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/bluetooth/rfcomm/core.c
[IPV4]: annotate inetdev.h helpers
[linux-2.6-omap-h63xx.git] / net / bluetooth / rfcomm / core.c
index 35adce6482b6f2d7ed030ecf413e797b23967bb1..332dd8f436ea0a2957df07db7d7b4aedcffa888f 100644 (file)
@@ -27,7 +27,6 @@
  * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
 #include <linux/init.h>
 #include <linux/wait.h>
+#include <linux/device.h>
 #include <linux/net.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
+#include <linux/mutex.h>
+
 #include <net/sock.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/rfcomm.h>
 
-#define VERSION "1.5"
-
 #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"
+
+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 DECLARE_MUTEX(rfcomm_sem);
-#define rfcomm_lock()  down(&rfcomm_sem);
-#define rfcomm_unlock()        up(&rfcomm_sem);
+static DEFINE_MUTEX(rfcomm_mutex);
+#define rfcomm_lock()  mutex_lock(&rfcomm_mutex)
+#define rfcomm_unlock()        mutex_unlock(&rfcomm_mutex)
 
 static unsigned long rfcomm_event;
 
@@ -133,6 +133,49 @@ static inline void rfcomm_session_put(struct rfcomm_session *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]])
 
@@ -231,10 +274,10 @@ static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
 
 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;
@@ -247,6 +290,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio)
        rfcomm_dlc_clear_state(d);
        
        BT_DBG("%p", d);
+
        return d;
 }
 
@@ -480,10 +524,10 @@ int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig)
 /* ---- RFCOMM sessions ---- */
 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);
 
@@ -492,7 +536,7 @@ static 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. */
@@ -585,7 +629,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
        /* 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);
@@ -769,7 +813,10 @@ 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++;
 
@@ -1108,6 +1155,8 @@ static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
 
 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);
@@ -1117,6 +1166,9 @@ static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
        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);
 }
 
@@ -1181,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;
 }
@@ -1718,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);
@@ -1830,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 */
@@ -1958,117 +2022,32 @@ static struct hci_cb rfcomm_cb = {
        .encrypt_cfm    = rfcomm_encrypt_cfm
 };
 
-/* ---- Proc fs support ---- */
-#ifdef CONFIG_PROC_FS
-static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos)
+static ssize_t rfcomm_dlc_sysfs_show(struct class *dev, char *buf)
 {
        struct rfcomm_session *s;
        struct list_head *pp, *p;
-       loff_t l = *pos;
+       char *str = buf;
 
        rfcomm_lock();
 
        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;
-}
+               list_for_each(pp, &s->dlcs) {
+                       struct sock *sk = s->sock->sk;
+                       struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list);
 
-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)++;
-
-       if (p->next != &s->dlcs)
-               return p->next;
-
-       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;
+                       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);
                }
        }
-       return NULL;
-}
 
-static void rfcomm_seq_stop(struct seq_file *seq, void *e)
-{
        rfcomm_unlock();
-}
-
-static int  rfcomm_seq_show(struct seq_file *seq, void *e)
-{
-       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;
+       return (str - buf);
 }
 
-static struct seq_operations rfcomm_seq_ops = {
-       .start  = rfcomm_seq_start,
-       .next   = rfcomm_seq_next,
-       .stop   = rfcomm_seq_stop,
-       .show   = rfcomm_seq_show 
-};
-
-static int rfcomm_seq_open(struct inode *inode, struct file *file)
-{
-       return seq_open(file, &rfcomm_seq_ops);
-}
-
-static struct file_operations rfcomm_seq_fops = {
-       .owner   = THIS_MODULE,
-       .open    = rfcomm_seq_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = seq_release,
-};
-
-static int  __init rfcomm_proc_init(void)
-{
-        struct proc_dir_entry *p;
-
-       proc_bt_rfcomm = proc_mkdir("rfcomm", proc_bt);
-       if (proc_bt_rfcomm) {
-               proc_bt_rfcomm->owner = THIS_MODULE;
-
-               p = create_proc_entry("dlc", S_IRUGO, proc_bt_rfcomm);
-               if (p)
-                       p->proc_fops = &rfcomm_seq_fops;
-       }
-        return 0;
-}
-
-static void __exit rfcomm_proc_cleanup(void)
-{
-        remove_proc_entry("dlc", proc_bt_rfcomm);
-
-       remove_proc_entry("rfcomm", proc_bt);
-}
-
-#else /* CONFIG_PROC_FS */
-
-static int  __init rfcomm_proc_init(void)
-{
-        return 0;
-}
-
-static void __exit rfcomm_proc_cleanup(void)
-{
-        return;
-}
-#endif /* CONFIG_PROC_FS */
+static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
 
 /* ---- Initialization ---- */
 static int __init rfcomm_init(void)
@@ -2079,9 +2058,7 @@ static int __init rfcomm_init(void)
 
        kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
 
-       BT_INFO("RFCOMM ver %s", VERSION);
-
-       rfcomm_proc_init();
+       class_create_file(bt_class, &class_attr_rfcomm_dlc);
 
        rfcomm_init_sockets();
 
@@ -2089,11 +2066,15 @@ 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.
@@ -2110,13 +2091,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 <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION);
 MODULE_VERSION(VERSION);