📄 l2cap.c
字号:
} if (!l2cap_pi(sk)->psm) { err = -EINVAL; goto done; } sk->max_ack_backlog = backlog; sk->ack_backlog = 0; sk->state = BT_LISTEN;done: release_sock(sk); return err;}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->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->sleep, &wait); while (!(nsk = bluez_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->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->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, &bluez_pi(sk)->dst); else bacpy(&la->l2_bdaddr, &bluez_pi(sk)->src); la->l2_psm = l2cap_pi(sk)->psm; return 0;}static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (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->state == BT_CONNECTED) err = l2cap_chan_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 *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(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 *)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 *optval, int *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(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 *)optval)) err = -EFAULT; break; case L2CAP_CONNINFO: if (sk->state != BT_CONNECTED) { err = -ENOTCONN; break; } cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; len = MIN(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->shutdown) { sk->shutdown = SHUTDOWN_MASK; l2cap_sock_clear_timer(sk); __l2cap_sock_close(sk, 0); if (sk->linger) err = bluez_sock_wait_state(sk, BT_CLOSED, 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->type == SOCK_SEQPACKET) { /* Alloc CID for connection-oriented socket */ l2cap_pi(sk)->scid = l2cap_alloc_cid(l); } else if (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) bluez_accept_enqueue(parent, sk);}static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent){ struct l2cap_chan_list *l = &conn->chan_list; write_lock(&l->lock); __l2cap_chan_add(conn, sk, parent); write_unlock(&l->lock);}/* 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 = bluez_pi(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->state = BT_CLOSED; sk->zapped = 1; if (err) sk->err = err; if (parent) parent->data_ready(parent, 0); else 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->type != SOCK_SEQPACKET) { l2cap_sock_clear_timer(sk); sk->state = BT_CONNECTED; sk->state_change(sk); } else if (sk->state == BT_CONNECT) { 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, L2CAP_CONN_REQ_SIZE, &req); } bh_unlock_sock(sk); } read_unlock(&l->lock);}/* Notify sockets that we cannot guaranty reliability anymore */static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err){ 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) { if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) sk->err = err; } read_unlock(&l->lock);}static void l2cap_chan_ready(struct sock *sk){ struct sock *parent = bluez_pi(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->state = BT_CONNECTED; sk->state_change(sk); } else { /* Incomming channel. * Wake up socket sleeping on accept. */ parent->data_ready(parent, 0); }}/* Copy frame to all raw sockets on that connection */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->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);}static int l2cap_chan_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; l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, len); /* First fragment (with L2CAP header) */ if (sk->type == SOCK_DGRAM) hlen = L2CAP_HDR_SIZE + 2; else hlen = L2CAP_HDR_SIZE; count = MIN(conn->mtu - hlen, len); skb = bluez_skb_send_alloc(sk, hlen + count, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return err; /* Create L2CAP header */ lh = (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->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(conn->mtu, len); *frag = bluez_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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -