📄 l2cap.c
字号:
if (err < 0) 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 l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags){ DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *nsk; 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 (!(nsk = 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", nsk);done: release_sock(sk); return err;}static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer){ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; BT_DBG("sock %p, sk %p", sock, sk); addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_l2); if (peer) bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); else bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); la->l2_psm = l2cap_pi(sk)->psm; return 0;}static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len){ struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb, **frag; int err, hlen, count, sent=0; struct l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, len); /* First fragment (with L2CAP header) */ if (sk->sk_type == SOCK_DGRAM) hlen = L2CAP_HDR_SIZE + 2; else hlen = L2CAP_HDR_SIZE; count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, hlen + count, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return err; /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid); lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); if (sk->sk_type == SOCK_DGRAM) put_unaligned(l2cap_pi(sk)->psm, (u16 *) skb_put(skb, 2)); if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { err = -EFAULT; goto fail; } sent += count; len -= count; /* Continuation fragments (no L2CAP header) */ frag = &skb_shinfo(skb)->frag_list; while (len) { count = min_t(unsigned int, conn->mtu, len); *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); if (!*frag) goto fail; if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) { err = -EFAULT; goto fail; } sent += count; len -= count; frag = &(*frag)->next; } if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0) goto fail; return sent;fail: kfree_skb(skb); return err;}static int l2cap_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; /* Check outgoing MTU */ if (len > l2cap_pi(sk)->omtu) return -EINVAL; lock_sock(sk); if (sk->sk_state == BT_CONNECTED) err = l2cap_do_send(sk, msg, len); else err = -ENOTCONN; release_sock(sk); return err;}static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen){ struct sock *sk = sock->sk; struct l2cap_options opts; int err = 0, len; u32 opt; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { case L2CAP_OPTIONS: len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *)&opts, optval, len)) { err = -EFAULT; break; } l2cap_pi(sk)->imtu = opts.imtu; l2cap_pi(sk)->omtu = opts.omtu; break; case L2CAP_LM: if (get_user(opt, (u32 __user *)optval)) { err = -EFAULT; break; } l2cap_pi(sk)->link_mode = opt; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err;}static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen){ struct sock *sk = sock->sk; struct l2cap_options opts; struct l2cap_conninfo cinfo; int len, err = 0; if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case L2CAP_OPTIONS: opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *)&opts, len)) err = -EFAULT; break; case L2CAP_LM: if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *)optval)) err = -EFAULT; break; case L2CAP_CONNINFO: if (sk->sk_state != BT_CONNECTED) { err = -ENOTCONN; break; } cinfo.hci_handle = l2cap_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 l2cap_sock_shutdown(struct socket *sock, int how){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; lock_sock(sk); if (!sk->sk_shutdown) { sk->sk_shutdown = SHUTDOWN_MASK; l2cap_sock_clear_timer(sk); __l2cap_sock_close(sk, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); } release_sock(sk); return err;}static int l2cap_sock_release(struct socket *sock){ struct sock *sk = sock->sk; int err; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; err = l2cap_sock_shutdown(sock, 2); sock_orphan(sk); l2cap_sock_kill(sk); return err;}/* ---- L2CAP channels ---- */static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid){ struct sock *s; for (s = l->head; s; s = l2cap_pi(s)->next_c) { if (l2cap_pi(s)->dcid == cid) break; } return s;}static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid){ struct sock *s; for (s = l->head; s; s = l2cap_pi(s)->next_c) { if (l2cap_pi(s)->scid == cid) break; } return s;}/* Find channel with given SCID. * Returns locked socket */static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid){ struct sock *s; read_lock(&l->lock); s = __l2cap_get_chan_by_scid(l, cid); if (s) bh_lock_sock(s); read_unlock(&l->lock); return s;}static u16 l2cap_alloc_cid(struct l2cap_chan_list *l){ u16 cid = 0x0040; for (; cid < 0xffff; cid++) { if(!__l2cap_get_chan_by_scid(l, cid)) return cid; } return 0;}static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk){ sock_hold(sk); if (l->head) l2cap_pi(l->head)->prev_c = sk; l2cap_pi(sk)->next_c = l->head; l2cap_pi(sk)->prev_c = NULL; l->head = sk;}static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk){ struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c; write_lock(&l->lock); if (sk == l->head) l->head = next; if (next) l2cap_pi(next)->prev_c = prev; if (prev) l2cap_pi(prev)->next_c = next; write_unlock(&l->lock); __sock_put(sk);}static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent){ struct l2cap_chan_list *l = &conn->chan_list; BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); l2cap_pi(sk)->conn = conn; if (sk->sk_type == SOCK_SEQPACKET) { /* Alloc CID for connection-oriented socket */ l2cap_pi(sk)->scid = l2cap_alloc_cid(l); } else if (sk->sk_type == SOCK_DGRAM) { /* Connectionless socket */ l2cap_pi(sk)->scid = 0x0002; l2cap_pi(sk)->dcid = 0x0002; l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; } else { /* Raw socket can send/recv signalling messages only */ l2cap_pi(sk)->scid = 0x0001; l2cap_pi(sk)->dcid = 0x0001; l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; } __l2cap_chan_link(l, sk); if (parent) bt_accept_enqueue(parent, sk);}/* Delete channel. * Must be called on the locked socket. */static void l2cap_chan_del(struct sock *sk, int err){ struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sock *parent = bt_sk(sk)->parent; l2cap_sock_clear_timer(sk); BT_DBG("sk %p, conn %p, err %d", sk, conn, err); if (conn) { /* Unlink from channel list */ l2cap_chan_unlink(&conn->chan_list, sk); l2cap_pi(sk)->conn = NULL; hci_conn_put(conn->hcon); } sk->sk_state = BT_CLOSED; sk->sk_zapped = 1; if (err) sk->sk_err = err; if (parent) parent->sk_data_ready(parent, 0); else sk->sk_state_change(sk);}static void l2cap_conn_ready(struct l2cap_conn *conn){ struct l2cap_chan_list *l = &conn->chan_list; struct sock *sk; BT_DBG("conn %p", conn); read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); if (sk->sk_type != SOCK_SEQPACKET) { l2cap_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } else if (sk->sk_state == BT_CONNECT) { struct l2cap_conn_req req; req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_send_req(conn, L2CAP_CONN_REQ, sizeof(req), &req); } bh_unlock_sock(sk); } read_unlock(&l->lock);}static void l2cap_chan_ready(struct sock *sk){ struct sock *parent = bt_sk(sk)->parent; BT_DBG("sk %p, parent %p", sk, parent); l2cap_pi(sk)->conf_state = 0; l2cap_sock_clear_timer(sk); if (!parent) { /* Outgoing channel. * Wake up socket sleeping on connect. */ sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } else { /* Incoming channel. * Wake up socket sleeping on accept. */ parent->sk_data_ready(parent, 0); }}/* Copy frame to all raw sockets on that connection */static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb){ struct l2cap_chan_list *l = &conn->chan_list; struct sk_buff *nskb; struct sock * sk; BT_DBG("conn %p", conn); read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { if (sk->sk_type != SOCK_RAW) continue; /* Don't send frame to the socket it came from */ if (skb->sk == sk) continue; if (!(nskb = skb_clone(skb, GFP_ATOMIC))) continue; if (sock_queue_rcv_skb(sk, nskb)) kfree_skb(nskb); } read_unlock(&l->lock);}/* ---- L2CAP signalling commands ---- */static inline u8 l2cap_get_ident(struct l2cap_conn *conn){ u8 id; /* Get next available identificator. * 1 - 199 are used by kernel. * 200 - 254 are used by utilities like l2ping, etc */ spin_lock(&conn->lock); if (++conn->tx_ident > 199) conn->tx_ident = 1; id = conn->tx_ident; spin_unlock(&conn->lock); return id;}static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data){ struct sk_buff *skb, **frag; struct l2cap_cmd_hdr *cmd; struct l2cap_hdr *lh; int len, count; BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen); len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; count = min_t(unsigned int, conn->mtu, len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -