int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; struct sco_options opts; struct sco_conninfo cinfo; int len, err = 0; //BT_DBG printf("%s: sk %p\n", __FUNCTION__, sk); if (get_user(&len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case SCO_OPTIONS: if (sk->state != BT_CONNECTED) { err = -ENOTCONN; break; } opts.mtu = sco_pi(sk)->conn->mtu; BT_DBG("mtu %d", opts.mtu); len = MIN(len, sizeof(opts)); if (copy_to_user(optval, (char *)&opts, len)) err = -EFAULT; break; case SCO_CONNINFO: if (sk->state != BT_CONNECTED) { err = -ENOTCONN; break; } cinfo.hci_handle = sco_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; }
/* Close socket. * Must be called on unlocked socket. */ static void sco_sock_close(struct sock *sk) { struct sco_conn *conn; sco_sock_clear_timer(sk); lock_sock(sk); conn = sco_pi(sk)->conn; BT_DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket); switch (sk->state) { case BT_LISTEN: sco_sock_cleanup_listen(sk); break; case BT_CONNECTED: case BT_CONFIG: case BT_CONNECT: case BT_DISCONN: sco_chan_del(sk, ECONNRESET); break; default: sk->zapped = 1; break; }; release_sock(sk); }
static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) { struct sco_conn *conn = sco_pi(sk)->conn; struct sk_buff *skb; int err, count; /* Check outgoing MTU */ if (len > conn->mtu) return -EINVAL; BT_DBG("sk %p len %d", sk, len); count = MIN(conn->mtu, len); if (!(skb = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err))) return err; if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { err = -EFAULT; goto fail; } if ((err = hci_send_sco(conn->hcon, skb)) < 0) goto fail; return count; fail: kfree_skb(skb); 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) bluez_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->state = BT_CLOSED; sk->err = err; sk->state_change(sk); sk->zapped = 1; }
/* ----- Proc fs support ------ */ static int sco_sock_dump(char *buf, struct bluez_sock_list *list) { struct sco_pinfo *pi; struct sock *sk; char *ptr = buf; write_lock_bh(&list->lock); for (sk = list->head; sk; sk = sk->next) { pi = sco_pi(sk); ptr += sprintf(ptr, "%s %s %d\n", batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst), sk->state); } write_unlock_bh(&list->lock); ptr += sprintf(ptr, "\n"); return ptr - buf; }