fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / bluetooth / rfcomm / tty.c
index 9a777b9..eb2b524 100644 (file)
  * $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
 
+#include <linux/capability.h>
 #include <linux/slab.h>
 #include <linux/skbuff.h>
 
 #include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/rfcomm.h>
 
 #ifndef CONFIG_BT_RFCOMM_DEBUG
@@ -77,7 +78,7 @@ struct rfcomm_dev {
 };
 
 static LIST_HEAD(rfcomm_dev_list);
-static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(rfcomm_dev_lock);
 
 static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
 static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
@@ -161,6 +162,22 @@ static inline struct rfcomm_dev *rfcomm_dev_get(int id)
        return dev;
 }
 
+static struct device *rfcomm_get_device(struct rfcomm_dev *dev)
+{
+       struct hci_dev *hdev;
+       struct hci_conn *conn;
+
+       hdev = hci_get_route(&dev->dst, &dev->src);
+       if (!hdev)
+               return NULL;
+
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst);
+
+       hci_dev_put(hdev);
+
+       return conn ? &conn->dev : NULL;
+}
+
 static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
 {
        struct rfcomm_dev *dev;
@@ -169,10 +186,9 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
 
        BT_DBG("id %d channel %d", req->dev_id, req->channel);
        
-       dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
+       dev = kzalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
        if (!dev)
                return -ENOMEM;
-       memset(dev, 0, sizeof(struct rfcomm_dev));
 
        write_lock_bh(&rfcomm_dev_lock);
 
@@ -245,7 +261,7 @@ out:
                return err;
        }
 
-       tty_register_device(rfcomm_tty_driver, dev->id, NULL);
+       tty_register_device(rfcomm_tty_driver, dev->id, rfcomm_get_device(dev));
 
        return dev->id;
 }
@@ -286,7 +302,7 @@ static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *de
        skb->destructor = rfcomm_wfree;
 }
 
-static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority)
+static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, gfp_t priority)
 {
        if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
                struct sk_buff *skb = alloc_skb(size, priority);
@@ -302,13 +318,13 @@ static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size
 
 #define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP))
 
-static int rfcomm_create_dev(struct sock *sk, unsigned long arg)
+static int rfcomm_create_dev(struct sock *sk, void __user *arg)
 {
        struct rfcomm_dev_req req;
        struct rfcomm_dlc *dlc;
        int id;
 
-       if (copy_from_user(&req, (void *) arg, sizeof(req)))
+       if (copy_from_user(&req, arg, sizeof(req)))
                return -EFAULT;
 
        BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags);
@@ -344,12 +360,12 @@ static int rfcomm_create_dev(struct sock *sk, unsigned long arg)
        return id;
 }
 
-static int rfcomm_release_dev(unsigned long arg)
+static int rfcomm_release_dev(void __user *arg)
 {
        struct rfcomm_dev_req req;
        struct rfcomm_dev *dev;
 
-       if (copy_from_user(&req, (void *) arg, sizeof(req)))
+       if (copy_from_user(&req, arg, sizeof(req)))
                return -EFAULT;
 
        BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
@@ -370,7 +386,7 @@ static int rfcomm_release_dev(unsigned long arg)
        return 0;
 }
 
-static int rfcomm_get_dev_list(unsigned long arg)
+static int rfcomm_get_dev_list(void __user *arg)
 {
        struct rfcomm_dev_list_req *dl;
        struct rfcomm_dev_info *di;
@@ -380,7 +396,7 @@ static int rfcomm_get_dev_list(unsigned long arg)
 
        BT_DBG("");
 
-       if (get_user(dev_num, (u16 *) arg))
+       if (get_user(dev_num, (u16 __user *) arg))
                return -EFAULT;
 
        if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
@@ -412,13 +428,13 @@ static int rfcomm_get_dev_list(unsigned long arg)
        dl->dev_num = n;
        size = sizeof(*dl) + n * sizeof(*di);
 
-       err = copy_to_user((void *) arg, dl, size);
+       err = copy_to_user(arg, dl, size);
        kfree(dl);
 
        return err ? -EFAULT : 0;
 }
 
-static int rfcomm_get_dev_info(unsigned long arg)
+static int rfcomm_get_dev_info(void __user *arg)
 {
        struct rfcomm_dev *dev;
        struct rfcomm_dev_info di;
@@ -426,7 +442,7 @@ static int rfcomm_get_dev_info(unsigned long arg)
 
        BT_DBG("");
 
-       if (copy_from_user(&di, (void *)arg, sizeof(di)))
+       if (copy_from_user(&di, arg, sizeof(di)))
                return -EFAULT;
 
        if (!(dev = rfcomm_dev_get(di.id)))
@@ -438,16 +454,16 @@ static int rfcomm_get_dev_info(unsigned long arg)
        bacpy(&di.src, &dev->src);
        bacpy(&di.dst, &dev->dst);
 
-       if (copy_to_user((void *)arg, &di, sizeof(di)))
+       if (copy_to_user(arg, &di, sizeof(di)))
                err = -EFAULT;
 
        rfcomm_dev_put(dev);
        return err;
 }
 
-int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
+int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
 {
-       BT_DBG("cmd %d arg %ld", cmd, arg);
+       BT_DBG("cmd %d arg %p", cmd, arg);
 
        switch (cmd) {
        case RFCOMMCREATEDEV:
@@ -479,17 +495,8 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
 
        BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
 
-       if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
-               register int i;
-               for (i = 0; i < skb->len; i++) {
-                       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                               tty_flip_buffer_push(tty);
-
-                       tty_insert_flip_char(tty, skb->data[i], 0);
-               }
-               tty_flip_buffer_push(tty);
-       } else
-               tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len);
+       tty_insert_flip_string(tty, skb->data, skb->len);
+       tty_flip_buffer_push(tty);
 
        kfree_skb(skb);
 }
@@ -528,9 +535,14 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
        struct rfcomm_dev *dev = dlc->owner;
        if (!dev)
                return;
-       
+
        BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
 
+       if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) {
+               if (dev->tty && !C_CLOCAL(dev->tty))
+                       tty_hangup(dev->tty);
+       }
+
        dev->modem_status = 
                ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) |
                ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) |
@@ -645,32 +657,26 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
        rfcomm_dev_put(dev);
 }
 
-static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
 {
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
        struct rfcomm_dlc *dlc = dev->dlc;
        struct sk_buff *skb;
        int err = 0, sent = 0, size;
 
-       BT_DBG("tty %p from_user %d count %d", tty, from_user, count);
+       BT_DBG("tty %p count %d", tty, count);
 
        while (count) {
                size = min_t(uint, count, dlc->mtu);
 
-               if (from_user)
-                       skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL);
-               else
-                       skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
+               skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
                
                if (!skb)
                        break;
 
                skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
 
-               if (from_user)
-                       copy_from_user(skb_put(skb, size), buf + sent, size);
-               else
-                       memcpy(skb_put(skb, size), buf + sent, size);
+               memcpy(skb_put(skb, size), buf + sent, size);
 
                if ((err = rfcomm_dlc_send(dlc, skb)) < 0) {
                        kfree_skb(skb);
@@ -691,9 +697,13 @@ static int rfcomm_tty_write_room(struct tty_struct *tty)
 
        BT_DBG("tty %p", tty);
 
+       if (!dev || !dev->dlc)
+               return 0;
+
        room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc);
        if (room < 0)
                room = 0;
+
        return room;
 }
 
@@ -746,20 +756,146 @@ static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned
        return -ENOIOCTLCMD;
 }
 
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
-static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old)
+static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 {
-       BT_DBG("tty %p", tty);
+       struct ktermios *new = tty->termios;
+       int old_baud_rate = tty_termios_baud_rate(old);
+       int new_baud_rate = tty_termios_baud_rate(new);
+
+       u8 baud, data_bits, stop_bits, parity, x_on, x_off;
+       u16 changes = 0;
+
+       struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+
+       BT_DBG("tty %p termios %p", tty, old);
 
-       if ((tty->termios->c_cflag == old->c_cflag) &&
-               (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag)))
+       if (!dev || !dev->dlc || !dev->dlc->session)
                return;
 
-       /* handle turning off CRTSCTS */
-       if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
-               BT_DBG("turning off CRTSCTS");
+       /* Handle turning off CRTSCTS */
+       if ((old->c_cflag & CRTSCTS) && !(new->c_cflag & CRTSCTS)) 
+               BT_DBG("Turning off CRTSCTS unsupported");
+
+       /* Parity on/off and when on, odd/even */
+       if (((old->c_cflag & PARENB) != (new->c_cflag & PARENB)) ||
+                       ((old->c_cflag & PARODD) != (new->c_cflag & PARODD)) ) {
+               changes |= RFCOMM_RPN_PM_PARITY;
+               BT_DBG("Parity change detected.");
+       }
+
+       /* Mark and space parity are not supported! */
+       if (new->c_cflag & PARENB) {
+               if (new->c_cflag & PARODD) {
+                       BT_DBG("Parity is ODD");
+                       parity = RFCOMM_RPN_PARITY_ODD;
+               } else {
+                       BT_DBG("Parity is EVEN");
+                       parity = RFCOMM_RPN_PARITY_EVEN;
+               }
+       } else {
+               BT_DBG("Parity is OFF");
+               parity = RFCOMM_RPN_PARITY_NONE;
+       }
+
+       /* Setting the x_on / x_off characters */
+       if (old->c_cc[VSTOP] != new->c_cc[VSTOP]) {
+               BT_DBG("XOFF custom");
+               x_on = new->c_cc[VSTOP];
+               changes |= RFCOMM_RPN_PM_XON;
+       } else {
+               BT_DBG("XOFF default");
+               x_on = RFCOMM_RPN_XON_CHAR;
+       }
+
+       if (old->c_cc[VSTART] != new->c_cc[VSTART]) {
+               BT_DBG("XON custom");
+               x_off = new->c_cc[VSTART];
+               changes |= RFCOMM_RPN_PM_XOFF;
+       } else {
+               BT_DBG("XON default");
+               x_off = RFCOMM_RPN_XOFF_CHAR;
+       }
+
+       /* Handle setting of stop bits */
+       if ((old->c_cflag & CSTOPB) != (new->c_cflag & CSTOPB))
+               changes |= RFCOMM_RPN_PM_STOP;
+
+       /* POSIX does not support 1.5 stop bits and RFCOMM does not
+        * support 2 stop bits. So a request for 2 stop bits gets
+        * translated to 1.5 stop bits */
+       if (new->c_cflag & CSTOPB) {
+               stop_bits = RFCOMM_RPN_STOP_15;
+       } else {
+               stop_bits = RFCOMM_RPN_STOP_1;
+       }
+
+       /* Handle number of data bits [5-8] */
+       if ((old->c_cflag & CSIZE) != (new->c_cflag & CSIZE)) 
+               changes |= RFCOMM_RPN_PM_DATA;
+
+       switch (new->c_cflag & CSIZE) {
+       case CS5:
+               data_bits = RFCOMM_RPN_DATA_5;
+               break;
+       case CS6:
+               data_bits = RFCOMM_RPN_DATA_6;
+               break;
+       case CS7:
+               data_bits = RFCOMM_RPN_DATA_7;
+               break;
+       case CS8:
+               data_bits = RFCOMM_RPN_DATA_8;
+               break;
+       default:
+               data_bits = RFCOMM_RPN_DATA_8;
+               break;
+       }
+
+       /* Handle baudrate settings */
+       if (old_baud_rate != new_baud_rate)
+               changes |= RFCOMM_RPN_PM_BITRATE;
+
+       switch (new_baud_rate) {
+       case 2400:
+               baud = RFCOMM_RPN_BR_2400;
+               break;
+       case 4800:
+               baud = RFCOMM_RPN_BR_4800;
+               break;
+       case 7200:
+               baud = RFCOMM_RPN_BR_7200;
+               break;
+       case 9600:
+               baud = RFCOMM_RPN_BR_9600;
+               break;
+       case 19200: 
+               baud = RFCOMM_RPN_BR_19200;
+               break;
+       case 38400:
+               baud = RFCOMM_RPN_BR_38400;
+               break;
+       case 57600:
+               baud = RFCOMM_RPN_BR_57600;
+               break;
+       case 115200:
+               baud = RFCOMM_RPN_BR_115200;
+               break;
+       case 230400:
+               baud = RFCOMM_RPN_BR_230400;
+               break;
+       default:
+               /* 9600 is standard accordinag to the RFCOMM specification */
+               baud = RFCOMM_RPN_BR_9600;
+               break;
+       
        }
+
+       if (changes)
+               rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud,
+                               data_bits, stop_bits, parity,
+                               RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes);
+
+       return;
 }
 
 static void rfcomm_tty_throttle(struct tty_struct *tty)
@@ -767,7 +903,7 @@ static void rfcomm_tty_throttle(struct tty_struct *tty)
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
 
        BT_DBG("tty %p dev %p", tty, dev);
-       
+
        rfcomm_dlc_throttle(dev->dlc);
 }
 
@@ -776,19 +912,21 @@ static void rfcomm_tty_unthrottle(struct tty_struct *tty)
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
 
        BT_DBG("tty %p dev %p", tty, dev);
-       
+
        rfcomm_dlc_unthrottle(dev->dlc);
 }
 
 static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty)
 {
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
-       struct rfcomm_dlc *dlc = dev->dlc;
 
        BT_DBG("tty %p dev %p", tty, dev);
 
-       if (skb_queue_len(&dlc->tx_queue))
-               return dlc->mtu;
+       if (!dev || !dev->dlc)
+               return 0;
+
+       if (!skb_queue_empty(&dev->dlc->tx_queue))
+               return dev->dlc->mtu;
 
        return 0;
 }
@@ -796,11 +934,12 @@ static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty)
 static void rfcomm_tty_flush_buffer(struct tty_struct *tty)
 {
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
-       if (!dev)
-               return;
 
        BT_DBG("tty %p dev %p", tty, dev);
 
+       if (!dev || !dev->dlc)
+               return;
+
        skb_queue_purge(&dev->dlc->tx_queue);
 
        if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
@@ -820,11 +959,12 @@ static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
 static void rfcomm_tty_hangup(struct tty_struct *tty)
 {
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
-       if (!dev)
-               return;
 
        BT_DBG("tty %p dev %p", tty, dev);
 
+       if (!dev)
+               return;
+
        rfcomm_tty_flush_buffer(tty);
 
        if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
@@ -847,40 +987,40 @@ static int rfcomm_tty_tiocmget(struct tty_struct *tty, struct file *filp)
 
 static int rfcomm_tty_tiocmset(struct tty_struct *tty, struct file *filp, unsigned int set, unsigned int clear)
 {
-       struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
-       struct rfcomm_dlc *dlc = dev->dlc;
-       u8 v24_sig;
+       struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+       struct rfcomm_dlc *dlc = dev->dlc;
+       u8 v24_sig;
 
        BT_DBG("tty %p dev %p set 0x%02x clear 0x%02x", tty, dev, set, clear);
 
-       rfcomm_dlc_get_modem_status(dlc, &v24_sig);
+       rfcomm_dlc_get_modem_status(dlc, &v24_sig);
 
-       if (set & TIOCM_DSR || set & TIOCM_DTR)
-               v24_sig |= RFCOMM_V24_RTC;
-       if (set & TIOCM_RTS || set & TIOCM_CTS)
-               v24_sig |= RFCOMM_V24_RTR;
-       if (set & TIOCM_RI)
-               v24_sig |= RFCOMM_V24_IC;
-       if (set & TIOCM_CD)
-               v24_sig |= RFCOMM_V24_DV;
+       if (set & TIOCM_DSR || set & TIOCM_DTR)
+               v24_sig |= RFCOMM_V24_RTC;
+       if (set & TIOCM_RTS || set & TIOCM_CTS)
+               v24_sig |= RFCOMM_V24_RTR;
+       if (set & TIOCM_RI)
+               v24_sig |= RFCOMM_V24_IC;
+       if (set & TIOCM_CD)
+               v24_sig |= RFCOMM_V24_DV;
 
-       if (clear & TIOCM_DSR || clear & TIOCM_DTR)
-               v24_sig &= ~RFCOMM_V24_RTC;
-       if (clear & TIOCM_RTS || clear & TIOCM_CTS)
-               v24_sig &= ~RFCOMM_V24_RTR;
-       if (clear & TIOCM_RI)
-               v24_sig &= ~RFCOMM_V24_IC;
-       if (clear & TIOCM_CD)
-               v24_sig &= ~RFCOMM_V24_DV;
+       if (clear & TIOCM_DSR || clear & TIOCM_DTR)
+               v24_sig &= ~RFCOMM_V24_RTC;
+       if (clear & TIOCM_RTS || clear & TIOCM_CTS)
+               v24_sig &= ~RFCOMM_V24_RTR;
+       if (clear & TIOCM_RI)
+               v24_sig &= ~RFCOMM_V24_IC;
+       if (clear & TIOCM_CD)
+               v24_sig &= ~RFCOMM_V24_DV;
 
-       rfcomm_dlc_set_modem_status(dlc, v24_sig);
+       rfcomm_dlc_set_modem_status(dlc, v24_sig);
 
-       return 0;
+       return 0;
 }
 
 /* ---- TTY structure ---- */
 
-static struct tty_operations rfcomm_ops = {
+static const struct tty_operations rfcomm_ops = {
        .open                   = rfcomm_tty_open,
        .close                  = rfcomm_tty_close,
        .write                  = rfcomm_tty_write,
@@ -907,13 +1047,12 @@ int rfcomm_init_ttys(void)
 
        rfcomm_tty_driver->owner        = THIS_MODULE;
        rfcomm_tty_driver->driver_name  = "rfcomm";
-       rfcomm_tty_driver->devfs_name   = "bluetooth/rfcomm/";
        rfcomm_tty_driver->name         = "rfcomm";
        rfcomm_tty_driver->major        = RFCOMM_TTY_MAJOR;
        rfcomm_tty_driver->minor_start  = RFCOMM_TTY_MINOR;
        rfcomm_tty_driver->type         = TTY_DRIVER_TYPE_SERIAL;
        rfcomm_tty_driver->subtype      = SERIAL_TYPE_NORMAL;
-       rfcomm_tty_driver->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+       rfcomm_tty_driver->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
        rfcomm_tty_driver->init_termios = tty_std_termios;
        rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
        tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);