📄 tty.c
字号:
/* RFCOMM implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.*//* * RFCOMM TTY. * * $Id: tty.c,v 1.26 2002/10/18 20:12:12 maxk 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/slab.h>#include <linux/skbuff.h>#include <net/bluetooth/bluetooth.h>#include <net/bluetooth/rfcomm.h>#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG#undef BT_DBG#define BT_DBG(D...)#endif#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */#define RFCOMM_TTY_MINOR 0struct rfcomm_dev { struct list_head list; atomic_t refcnt; char name[12]; int id; unsigned long flags; int opened; int err; bdaddr_t src; bdaddr_t dst; u8 channel; uint modem_status; struct rfcomm_dlc *dlc; struct tty_struct *tty; wait_queue_head_t wait; struct tasklet_struct wakeup_task; atomic_t wmem_alloc;};static LIST_HEAD(rfcomm_dev_list);static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED;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);static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);static void rfcomm_tty_wakeup(unsigned long arg);/* ---- Device functions ---- */static void rfcomm_dev_destruct(struct rfcomm_dev *dev){ struct rfcomm_dlc *dlc = dev->dlc; BT_DBG("dev %p dlc %p", dev, dlc); rfcomm_dlc_lock(dlc); /* Detach DLC if it's owned by this dev */ if (dlc->owner == dev) dlc->owner = NULL; rfcomm_dlc_unlock(dlc); rfcomm_dlc_put(dlc); kfree(dev); MOD_DEC_USE_COUNT;}static inline void rfcomm_dev_hold(struct rfcomm_dev *dev){ atomic_inc(&dev->refcnt);}static inline void rfcomm_dev_put(struct rfcomm_dev *dev){ if (atomic_dec_and_test(&dev->refcnt)) rfcomm_dev_destruct(dev);}static struct rfcomm_dev *__rfcomm_dev_get(int id){ struct rfcomm_dev *dev; struct list_head *p; list_for_each(p, &rfcomm_dev_list) { dev = list_entry(p, struct rfcomm_dev, list); if (dev->id == id) return dev; } return NULL;}static inline struct rfcomm_dev *rfcomm_dev_get(int id){ struct rfcomm_dev *dev; read_lock(&rfcomm_dev_lock); dev = __rfcomm_dev_get(id); read_unlock(&rfcomm_dev_lock); if (dev) rfcomm_dev_hold(dev); return dev;}static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc){ struct rfcomm_dev *dev; struct list_head *head = &rfcomm_dev_list, *p; int err = 0; BT_DBG("id %d channel %d", req->dev_id, req->channel); dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL); if (!dev) return -ENOMEM; memset(dev, 0, sizeof(struct rfcomm_dev)); write_lock_bh(&rfcomm_dev_lock); if (req->dev_id < 0) { dev->id = 0; list_for_each(p, &rfcomm_dev_list) { if (list_entry(p, struct rfcomm_dev, list)->id != dev->id) break; dev->id++; head = p; } } else { dev->id = req->dev_id; list_for_each(p, &rfcomm_dev_list) { struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list); if (entry->id == dev->id) { err = -EADDRINUSE; goto out; } if (entry->id > dev->id - 1) break; head = p; } } if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) { err = -ENFILE; goto out; } sprintf(dev->name, "rfcomm%d", dev->id); list_add(&dev->list, head); atomic_set(&dev->refcnt, 1); bacpy(&dev->src, &req->src); bacpy(&dev->dst, &req->dst); dev->channel = req->channel; dev->flags = req->flags & ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); init_waitqueue_head(&dev->wait); tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); rfcomm_dlc_lock(dlc); dlc->data_ready = rfcomm_dev_data_ready; dlc->state_change = rfcomm_dev_state_change; dlc->modem_status = rfcomm_dev_modem_status; dlc->owner = dev; dev->dlc = dlc; rfcomm_dlc_unlock(dlc); MOD_INC_USE_COUNT; out: write_unlock_bh(&rfcomm_dev_lock); if (err) { kfree(dev); return err; } else return dev->id;}static void rfcomm_dev_del(struct rfcomm_dev *dev){ BT_DBG("dev %p", dev); write_lock_bh(&rfcomm_dev_lock); list_del_init(&dev->list); write_unlock_bh(&rfcomm_dev_lock); rfcomm_dev_put(dev);}/* ---- Send buffer ---- */static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc){ /* We can't let it be zero, because we don't get a callback when tx_credits becomes nonzero, hence we'd never wake up */ return dlc->mtu * (dlc->tx_credits?:1);}static void rfcomm_wfree(struct sk_buff *skb){ struct rfcomm_dev *dev = (void *) skb->sk; atomic_sub(skb->truesize, &dev->wmem_alloc); if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) tasklet_schedule(&dev->wakeup_task); rfcomm_dev_put(dev);}static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev){ rfcomm_dev_hold(dev); atomic_add(skb->truesize, &dev->wmem_alloc); skb->sk = (void *) dev; skb->destructor = rfcomm_wfree;}static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority){ if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { struct sk_buff *skb = alloc_skb(size, priority); if (skb) { rfcomm_set_owner_w(skb, dev); return skb; } } return NULL;}/* ---- Device IOCTLs ---- */#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP))static int rfcomm_create_dev(struct sock *sk, unsigned long arg){ struct rfcomm_dev_req req; struct rfcomm_dlc *dlc; int id; if (copy_from_user(&req, (void *) arg, sizeof(req))) return -EFAULT; BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags); if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) return -EPERM; if (req.flags & (1 << RFCOMM_REUSE_DLC)) { /* Socket must be connected */ if (sk->state != BT_CONNECTED) return -EBADFD; dlc = rfcomm_pi(sk)->dlc; rfcomm_dlc_hold(dlc); } else { dlc = rfcomm_dlc_alloc(GFP_KERNEL); if (!dlc) return -ENOMEM; } id = rfcomm_dev_add(&req, dlc); if (id < 0) { rfcomm_dlc_put(dlc); return id; } if (req.flags & (1 << RFCOMM_REUSE_DLC)) { /* DLC is now used by device. * Socket must be disconnected */ sk->state = BT_CLOSED; } return id;}static int rfcomm_release_dev(unsigned long arg){ struct rfcomm_dev_req req; struct rfcomm_dev *dev; if (copy_from_user(&req, (void *) arg, sizeof(req))) return -EFAULT; BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags); if (!capable(CAP_NET_ADMIN)) return -EPERM; if (!(dev = rfcomm_dev_get(req.dev_id))) return -ENODEV; if (req.flags & (1 << RFCOMM_HANGUP_NOW)) rfcomm_dlc_close(dev->dlc, 0); rfcomm_dev_del(dev); rfcomm_dev_put(dev); return 0;}static int rfcomm_get_dev_list(unsigned long arg){ struct rfcomm_dev_list_req *dl; struct rfcomm_dev_info *di; struct list_head *p; int n = 0, size; u16 dev_num; BT_DBG(""); if (get_user(dev_num, (u16 *) arg)) return -EFAULT; if (!dev_num) return -EINVAL; size = sizeof(*dl) + dev_num * sizeof(*di); if (verify_area(VERIFY_WRITE, (void *)arg, size)) return -EFAULT; if (!(dl = kmalloc(size, GFP_KERNEL))) return -ENOMEM; di = dl->dev_info; read_lock_bh(&rfcomm_dev_lock); list_for_each(p, &rfcomm_dev_list) { struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list); (di + n)->id = dev->id; (di + n)->flags = dev->flags; (di + n)->state = dev->dlc->state; (di + n)->channel = dev->channel; bacpy(&(di + n)->src, &dev->src); bacpy(&(di + n)->dst, &dev->dst); if (++n >= dev_num) break; } read_unlock_bh(&rfcomm_dev_lock); dl->dev_num = n; size = sizeof(*dl) + n * sizeof(*di); copy_to_user((void *) arg, dl, size); kfree(dl); return 0;}static int rfcomm_get_dev_info(unsigned long arg){ struct rfcomm_dev *dev; struct rfcomm_dev_info di; int err = 0; BT_DBG(""); if (copy_from_user(&di, (void *)arg, sizeof(di))) return -EFAULT; if (!(dev = rfcomm_dev_get(di.id))) return -ENODEV; di.flags = dev->flags; di.channel = dev->channel; di.state = dev->dlc->state; bacpy(&di.src, &dev->src); bacpy(&di.dst, &dev->dst); if (copy_to_user((void *)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){ BT_DBG("cmd %d arg %ld", cmd, arg); switch (cmd) { case RFCOMMCREATEDEV: return rfcomm_create_dev(sk, arg); case RFCOMMRELEASEDEV: return rfcomm_release_dev(arg); case RFCOMMGETDEVLIST: return rfcomm_get_dev_list(arg); case RFCOMMGETDEVINFO: return rfcomm_get_dev_info(arg); } return -EINVAL;}/* ---- 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -