📄 tty.c
字号:
if (!dev) return; BT_DBG("dlc %p dev %p err %d", dlc, dev, err); dev->err = err; wake_up_interruptible(&dev->wait); if (dlc->state == BT_CLOSED) { if (!dev->tty) { if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { rfcomm_dev_hold(dev); rfcomm_dev_del(dev); /* We have to drop DLC lock here, otherwise * rfcomm_dev_put() will dead lock if it's the last refference */ rfcomm_dlc_unlock(dlc); rfcomm_dev_put(dev); rfcomm_dlc_lock(dlc); } } else tty_hangup(dev->tty); }}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); dev->modem_status = ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) | ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) | ((v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0);}/* ---- TTY functions ---- */static void rfcomm_tty_wakeup(unsigned long arg){ struct rfcomm_dev *dev = (void *) arg; struct tty_struct *tty = dev->tty; if (!tty) return; BT_DBG("dev %p tty %p", dev, tty); if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait);#ifdef SERIAL_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait);#endif}static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp){ DECLARE_WAITQUEUE(wait, current); struct rfcomm_dev *dev; struct rfcomm_dlc *dlc; int err, id; id = MINOR(tty->device) - tty->driver.minor_start; BT_DBG("tty %p id %d", tty, id); dev = rfcomm_dev_get(id); if (!dev) return -ENODEV; BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened); if (dev->opened++ != 0) return 0; dlc = dev->dlc; /* Attach TTY and open DLC */ rfcomm_dlc_lock(dlc); tty->driver_data = dev; dev->tty = tty; rfcomm_dlc_unlock(dlc); set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); if (err < 0) return err; /* Wait for DLC to connect */ add_wait_queue(&dev->wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (dlc->state == BT_CLOSED) { err = -dev->err; break; } if (dlc->state == BT_CONNECTED) break; if (signal_pending(current)) { err = -EINTR; break; } schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&dev->wait, &wait); return err;}static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp){ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; if (!dev) return; BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened); if (--dev->opened == 0) { /* Close DLC and dettach TTY */ rfcomm_dlc_close(dev->dlc, 0); clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); tasklet_kill(&dev->wakeup_task); rfcomm_dlc_lock(dev->dlc); tty->driver_data = NULL; dev->tty = NULL; rfcomm_dlc_unlock(dev->dlc); } rfcomm_dev_put(dev);}static int rfcomm_tty_write(struct tty_struct *tty, int from_user, 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); while (count) { size = min_t(uint, count, dlc->mtu); if (from_user) skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL); else skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, 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); if ((err = rfcomm_dlc_send(dlc, skb)) < 0) { kfree_skb(skb); break; } sent += size; count -= size; } return sent ? sent : err;}static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch){ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; struct sk_buff *skb; BT_DBG("tty %p char %x", tty, ch); skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC); if (!skb) return; skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); *(char *)skb_put(skb, 1) = ch; if ((rfcomm_dlc_send(dlc, skb)) < 0) kfree_skb(skb); }static int rfcomm_tty_write_room(struct tty_struct *tty){ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; int room; BT_DBG("tty %p", tty); room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc); if (room < 0) room = 0; return room;}static int rfcomm_tty_set_modem_status(uint cmd, struct rfcomm_dlc *dlc, uint status){ u8 v24_sig, mask; BT_DBG("dlc %p cmd 0x%02x", dlc, cmd); if (cmd == TIOCMSET) v24_sig = 0; else rfcomm_dlc_get_modem_status(dlc, &v24_sig); mask = ((status & TIOCM_DSR) ? RFCOMM_V24_RTC : 0) | ((status & TIOCM_DTR) ? RFCOMM_V24_RTC : 0) | ((status & TIOCM_RTS) ? RFCOMM_V24_RTR : 0) | ((status & TIOCM_CTS) ? RFCOMM_V24_RTR : 0) | ((status & TIOCM_RI) ? RFCOMM_V24_IC : 0) | ((status & TIOCM_CD) ? RFCOMM_V24_DV : 0); if (cmd == TIOCMBIC) v24_sig &= ~mask; else v24_sig |= mask; rfcomm_dlc_set_modem_status(dlc, v24_sig); return 0;}static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg){ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; uint status; int err; BT_DBG("tty %p cmd 0x%02x", tty, cmd); switch (cmd) { case TCGETS: BT_DBG("TCGETS is not supported"); return -ENOIOCTLCMD; case TCSETS: BT_DBG("TCSETS is not supported"); return -ENOIOCTLCMD; case TIOCMGET: BT_DBG("TIOCMGET"); return put_user(dev->modem_status, (unsigned int *)arg); case TIOCMSET: /* Turns on and off the lines as specified by the mask */ case TIOCMBIS: /* Turns on the lines as specified by the mask */ case TIOCMBIC: /* Turns off the lines as specified by the mask */ if ((err = get_user(status, (unsigned int *)arg))) return err; return rfcomm_tty_set_modem_status(cmd, dlc, status); case TIOCMIWAIT: BT_DBG("TIOCMIWAIT"); break; case TIOCGICOUNT: BT_DBG("TIOCGICOUNT"); break; case TIOCGSERIAL: BT_ERR("TIOCGSERIAL is not supported"); return -ENOIOCTLCMD; case TIOCSSERIAL: BT_ERR("TIOCSSERIAL is not supported"); return -ENOIOCTLCMD; case TIOCSERGSTRUCT: BT_ERR("TIOCSERGSTRUCT is not supported"); return -ENOIOCTLCMD; case TIOCSERGETLSR: BT_ERR("TIOCSERGETLSR is not supported"); return -ENOIOCTLCMD; case TIOCSERCONFIG: BT_ERR("TIOCSERCONFIG is not supported"); return -ENOIOCTLCMD; default: return -ENOIOCTLCMD; /* ioctls which we must ignore */ } 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){ BT_DBG("tty %p", tty); if ((tty->termios->c_cflag == old->c_cflag) && (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag))) return; /* handle turning off CRTSCTS */ if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { BT_DBG("turning off CRTSCTS"); }}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);}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; return 0;}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); skb_queue_purge(&dev->dlc->tx_queue); if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) tty->ldisc.write_wakeup(tty);}static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch){ BT_DBG("tty %p ch %c", tty, ch);}static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout){ BT_DBG("tty %p timeout %d", tty, 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); rfcomm_tty_flush_buffer(tty); if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) rfcomm_dev_del(dev);}static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused){ return 0;}/* ---- TTY structure ---- */static int rfcomm_tty_refcount; /* If we manage several devices */static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS];static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS];static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS];static struct tty_driver rfcomm_tty_driver = { magic: TTY_DRIVER_MAGIC, driver_name: "rfcomm",#ifdef CONFIG_DEVFS_FS name: "bluetooth/rfcomm/%d",#else name: "rfcomm",#endif major: RFCOMM_TTY_MAJOR, minor_start: RFCOMM_TTY_MINOR, num: RFCOMM_TTY_PORTS, type: TTY_DRIVER_TYPE_SERIAL, subtype: SERIAL_TYPE_NORMAL, flags: TTY_DRIVER_REAL_RAW, refcount: &rfcomm_tty_refcount, table: rfcomm_tty_table, termios: rfcomm_tty_termios, termios_locked: rfcomm_tty_termios_locked, open: rfcomm_tty_open, close: rfcomm_tty_close, put_char: rfcomm_tty_put_char, write: rfcomm_tty_write, write_room: rfcomm_tty_write_room, chars_in_buffer: rfcomm_tty_chars_in_buffer, flush_buffer: rfcomm_tty_flush_buffer, ioctl: rfcomm_tty_ioctl, throttle: rfcomm_tty_throttle, unthrottle: rfcomm_tty_unthrottle, set_termios: rfcomm_tty_set_termios, send_xchar: rfcomm_tty_send_xchar, stop: NULL, start: NULL, hangup: rfcomm_tty_hangup, wait_until_sent: rfcomm_tty_wait_until_sent, read_proc: rfcomm_tty_read_proc,};int rfcomm_init_ttys(void){ int i; /* Initalize our global data */ for (i = 0; i < RFCOMM_TTY_PORTS; i++) rfcomm_tty_table[i] = NULL; /* Register the TTY driver */ rfcomm_tty_driver.init_termios = tty_std_termios; rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW; if (tty_register_driver(&rfcomm_tty_driver)) { BT_ERR("Can't register RFCOMM TTY driver"); return -1; } return 0;}void rfcomm_cleanup_ttys(void){ tty_unregister_driver(&rfcomm_tty_driver); return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -