📄 sco.c
字号:
static int sco_sock_listen(struct socket *sock, int backlog){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p backlog %d", sk, backlog); lock_sock(sk); if (sk->sk_state != BT_BOUND || sock->type != SOCK_SEQPACKET) { err = -EBADFD; goto done; } sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; sk->sk_state = BT_LISTEN;done: release_sock(sk); return err;}static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags){ DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *ch; long timeo; int err = 0; lock_sock(sk); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; goto done; } timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk->sk_sleep, &wait); while (!(ch = bt_accept_dequeue(sk, newsock))) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) { err = -EAGAIN; break; } release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; break; } if (signal_pending(current)) { err = sock_intr_errno(timeo); break; } } set_current_state(TASK_RUNNING); remove_wait_queue(sk->sk_sleep, &wait); if (err) goto done; newsock->state = SS_CONNECTED; BT_DBG("new socket %p", ch);done: release_sock(sk); return err;}static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer){ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; struct sock *sk = sock->sk; BT_DBG("sock %p, sk %p", sock, sk); addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_sco); if (peer) bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst); else bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src); return 0;}static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (sk->sk_err) return sock_error(sk); if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; lock_sock(sk); if (sk->sk_state == BT_CONNECTED) err = sco_send_frame(sk, msg, len); else err = -ENOTCONN; release_sock(sk); return err;}static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { default: err = -ENOPROTOOPT; break; } release_sock(sk); return err;}static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen){ struct sock *sk = sock->sk; struct sco_options opts; struct sco_conninfo cinfo; int len, err = 0; BT_DBG("sk %p", sk); if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case SCO_OPTIONS: if (sk->sk_state != BT_CONNECTED) { err = -ENOTCONN; break; } opts.mtu = sco_pi(sk)->conn->mtu; BT_DBG("mtu %d", opts.mtu); len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *)&opts, len)) err = -EFAULT; break; case SCO_CONNINFO: if (sk->sk_state != BT_CONNECTED) { err = -ENOTCONN; break; } cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *)&cinfo, len)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err;}static int sco_sock_release(struct socket *sock){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; sco_sock_close(sk); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) { lock_sock(sk); err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); release_sock(sk); } sock_orphan(sk); sco_sock_kill(sk); return err;}static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent){ BT_DBG("conn %p", conn); sco_pi(sk)->conn = conn; conn->sk = sk; if (parent) bt_accept_enqueue(parent, sk);}/* Delete channel. * Must be called on the locked socket. */static void sco_chan_del(struct sock *sk, int err){ struct sco_conn *conn; conn = sco_pi(sk)->conn; BT_DBG("sk %p, conn %p, err %d", sk, conn, err); if (conn) { sco_conn_lock(conn); conn->sk = NULL; sco_pi(sk)->conn = NULL; sco_conn_unlock(conn); hci_conn_put(conn->hcon); } sk->sk_state = BT_CLOSED; sk->sk_err = err; sk->sk_state_change(sk); sk->sk_zapped = 1;}static void sco_conn_ready(struct sco_conn *conn){ struct sock *parent, *sk; BT_DBG("conn %p", conn); sco_conn_lock(conn); if ((sk = conn->sk)) { sco_sock_clear_timer(sk); bh_lock_sock(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); bh_unlock_sock(sk); } else { parent = sco_get_sock_listen(conn->src); if (!parent) goto done; bh_lock_sock(parent); sk = sco_sock_alloc(NULL, BTPROTO_SCO, GFP_ATOMIC); if (!sk) { bh_unlock_sock(parent); goto done; } sco_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); hci_conn_hold(conn->hcon); __sco_chan_add(conn, sk, parent); sk->sk_state = BT_CONNECTED; /* Wake up parent */ parent->sk_data_ready(parent, 1); bh_unlock_sock(parent); }done: sco_conn_unlock(conn);}/* ----- SCO interface with lower layer (HCI) ----- */static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type){ BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); /* Always accept connection */ return HCI_LM_ACCEPT;}static int sco_connect_cfm(struct hci_conn *hcon, __u8 status){ BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); if (hcon->type != SCO_LINK) return 0; if (!status) { struct sco_conn *conn; conn = sco_conn_add(hcon, status); if (conn) sco_conn_ready(conn); } else sco_conn_del(hcon, bt_err(status)); return 0;}static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason){ BT_DBG("hcon %p reason %d", hcon, reason); if (hcon->type != SCO_LINK) return 0; sco_conn_del(hcon, bt_err(reason)); return 0;}static int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb){ struct sco_conn *conn = hcon->sco_data; if (!conn) goto drop; BT_DBG("conn %p len %d", conn, skb->len); if (skb->len) { sco_recv_frame(conn, skb); return 0; }drop: kfree_skb(skb); return 0;}/* ---- Proc fs support ---- */#ifdef CONFIG_PROC_FSstatic void *sco_seq_start(struct seq_file *seq, loff_t *pos){ struct sock *sk; struct hlist_node *node; loff_t l = *pos; read_lock_bh(&sco_sk_list.lock); sk_for_each(sk, node, &sco_sk_list.head) if (!l--) goto found; sk = NULL;found: return sk;}static void *sco_seq_next(struct seq_file *seq, void *e, loff_t *pos){ struct sock *sk = e; (*pos)++; return sk_next(sk);}static void sco_seq_stop(struct seq_file *seq, void *e){ read_unlock_bh(&sco_sk_list.lock);}static int sco_seq_show(struct seq_file *seq, void *e){ struct sock *sk = e; seq_printf(seq, "%s %s %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->sk_state); return 0;}static struct seq_operations sco_seq_ops = { .start = sco_seq_start, .next = sco_seq_next, .stop = sco_seq_stop, .show = sco_seq_show };static int sco_seq_open(struct inode *inode, struct file *file){ return seq_open(file, &sco_seq_ops);}static struct file_operations sco_seq_fops = { .owner = THIS_MODULE, .open = sco_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release,};static int __init sco_proc_init(void){ struct proc_dir_entry *p = create_proc_entry("sco", S_IRUGO, proc_bt); if (!p) return -ENOMEM; p->owner = THIS_MODULE; p->proc_fops = &sco_seq_fops; return 0;}static void __exit sco_proc_cleanup(void){ remove_proc_entry("sco", proc_bt);}#else /* CONFIG_PROC_FS */static int __init sco_proc_init(void){ return 0;}static void __exit sco_proc_cleanup(void){ return;}#endif /* CONFIG_PROC_FS */static struct proto_ops sco_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = sco_sock_release, .bind = sco_sock_bind, .connect = sco_sock_connect, .listen = sco_sock_listen, .accept = sco_sock_accept, .getname = sco_sock_getname, .sendmsg = sco_sock_sendmsg, .recvmsg = bt_sock_recvmsg, .poll = bt_sock_poll, .ioctl = sock_no_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, .shutdown = sock_no_shutdown, .setsockopt = sco_sock_setsockopt, .getsockopt = sco_sock_getsockopt};static struct net_proto_family sco_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = sco_sock_create,};static struct hci_proto sco_hci_proto = { .name = "SCO", .id = HCI_PROTO_SCO, .connect_ind = sco_connect_ind, .connect_cfm = sco_connect_cfm, .disconn_ind = sco_disconn_ind, .recv_scodata = sco_recv_scodata};static int __init sco_init(void){ int err; if ((err = bt_sock_register(BTPROTO_SCO, &sco_sock_family_ops))) { BT_ERR("SCO socket registration failed"); return err; } if ((err = hci_register_proto(&sco_hci_proto))) { BT_ERR("SCO protocol registration failed"); return err; } sco_proc_init(); BT_INFO("SCO (Voice Link) ver %s", VERSION); BT_INFO("SCO socket layer initialized"); return 0;}static void __exit sco_exit(void){ int err; sco_proc_cleanup(); /* Unregister socket, protocol and notifier */ if ((err = bt_sock_unregister(BTPROTO_SCO))) BT_ERR("SCO socket unregistration failed. %d", err); if ((err = hci_unregister_proto(&sco_hci_proto))) BT_ERR("SCO protocol unregistration failed. %d", err);}module_init(sco_init);module_exit(sco_exit);MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);MODULE_VERSION(VERSION);MODULE_LICENSE("GPL");MODULE_ALIAS("bt-proto-2");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -