📄 sco.c
字号:
int err = 0; BT_DBG("sk %p", sk); if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_sco)) return -EINVAL; if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) return -EBADFD; if (sk->sk_type != SOCK_SEQPACKET) return -EINVAL; lock_sock(sk); /* Set destination address and psm */ bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr); if ((err = sco_connect(sk))) goto done; err = bt_sock_wait_state(sk, BT_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK));done: release_sock(sk); return err;}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); err = sock_error(sk); if (err) return err; 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; memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); 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); sock_set_flag(sk, SOCK_ZAPPED);}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(parent->sk_net, 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 && hcon->type != ESCO_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 && hcon->type != ESCO_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;}static ssize_t sco_sysfs_show(struct class *dev, char *buf){ struct sock *sk; struct hlist_node *node; char *str = buf; read_lock_bh(&sco_sk_list.lock); sk_for_each(sk, node, &sco_sk_list.head) { str += sprintf(str, "%s %s %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->sk_state); } read_unlock_bh(&sco_sk_list.lock); return (str - buf);}static CLASS_ATTR(sco, S_IRUGO, sco_sysfs_show, NULL);static const 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; err = proto_register(&sco_proto, 0); if (err < 0) return err; err = bt_sock_register(BTPROTO_SCO, &sco_sock_family_ops); if (err < 0) { BT_ERR("SCO socket registration failed"); goto error; } err = hci_register_proto(&sco_hci_proto); if (err < 0) { BT_ERR("SCO protocol registration failed"); bt_sock_unregister(BTPROTO_SCO); goto error; } if (class_create_file(bt_class, &class_attr_sco) < 0) BT_ERR("Failed to create SCO info file"); BT_INFO("SCO (Voice Link) ver %s", VERSION); BT_INFO("SCO socket layer initialized"); return 0;error: proto_unregister(&sco_proto); return err;}static void __exit sco_exit(void){ class_remove_file(bt_class, &class_attr_sco); if (bt_sock_unregister(BTPROTO_SCO) < 0) BT_ERR("SCO socket unregistration failed"); if (hci_unregister_proto(&sco_hci_proto) < 0) BT_ERR("SCO protocol unregistration failed"); proto_unregister(&sco_proto);}module_init(sco_init);module_exit(sco_exit);MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");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 + -