📄 tty.c
字号:
}/* ---- DLC callbacks ---- */static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb){ struct rfcomm_dev *dev = dlc->owner; struct tty_struct *tty; if (!dev || !(tty = dev->tty)) { kfree_skb(skb); return; } 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); kfree_skb(skb);}static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err){ struct rfcomm_dev *dev = dlc->owner; 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 reference. */ 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 = tty->index; BT_DBG("tty %p id %d", tty, id); /* We don't leak this refcount. For reasons which are not entirely clear, the TTY layer will call our ->close() method even if the open fails. We decrease the refcount there, and decreasing it here too would cause breakage. */ 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, 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 count %d", tty, count); while (count) { size = min_t(uint, count, dlc->mtu); skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC); if (!skb) break; skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); 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 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_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg){ 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 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;}static int rfcomm_tty_tiocmget(struct tty_struct *tty, struct file *filp){ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p dev %p", tty, dev); return dev->modem_status;}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; BT_DBG("tty %p dev %p set 0x%02x clear 0x%02x", tty, dev, set, clear); 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 (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); return 0;}/* ---- TTY structure ---- */static struct tty_operations rfcomm_ops = { .open = rfcomm_tty_open, .close = rfcomm_tty_close, .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, .hangup = rfcomm_tty_hangup, .wait_until_sent = rfcomm_tty_wait_until_sent, .read_proc = rfcomm_tty_read_proc, .tiocmget = rfcomm_tty_tiocmget, .tiocmset = rfcomm_tty_tiocmset,};int rfcomm_init_ttys(void){ rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); if (!rfcomm_tty_driver) return -1; 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->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); if (tty_register_driver(rfcomm_tty_driver)) { BT_ERR("Can't register RFCOMM TTY driver"); put_tty_driver(rfcomm_tty_driver); return -1; } BT_INFO("RFCOMM TTY layer initialized"); return 0;}void rfcomm_cleanup_ttys(void){ tty_unregister_driver(rfcomm_tty_driver); put_tty_driver(rfcomm_tty_driver);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -