OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [bluetooth/] [hci_sock.c] - Diff between revs 1275 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1275 Rev 1765
/*
/*
   BlueZ - Bluetooth protocol stack for Linux
   BlueZ - Bluetooth protocol stack for Linux
   Copyright (C) 2000-2001 Qualcomm Incorporated
   Copyright (C) 2000-2001 Qualcomm Incorporated
 
 
   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
 
 
   This program is free software; you can redistribute it and/or modify
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 as
   it under the terms of the GNU General Public License version 2 as
   published by the Free Software Foundation;
   published by the Free Software Foundation;
 
 
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
   SOFTWARE IS DISCLAIMED.
   SOFTWARE IS DISCLAIMED.
*/
*/
 
 
/*
/*
 * BlueZ HCI socket layer.
 * BlueZ HCI socket layer.
 *
 *
 * $Id: hci_sock.c,v 1.1.1.1 2004-04-15 01:17:06 phoenix Exp $
 * $Id: hci_sock.c,v 1.1.1.1 2004-04-15 01:17:06 phoenix Exp $
 */
 */
 
 
#include <linux/config.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/module.h>
 
 
#include <linux/types.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/skbuff.h>
#include <linux/tqueue.h>
#include <linux/tqueue.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/socket.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/ioctl.h>
#include <net/sock.h>
#include <net/sock.h>
 
 
#include <asm/system.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <asm/unaligned.h>
 
 
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci_core.h>
 
 
#ifndef HCI_SOCK_DEBUG
#ifndef HCI_SOCK_DEBUG
#undef  BT_DBG
#undef  BT_DBG
#define BT_DBG( A... )
#define BT_DBG( A... )
#endif
#endif
 
 
/* ----- HCI socket interface ----- */
/* ----- HCI socket interface ----- */
 
 
/* Security filter */
/* Security filter */
static struct hci_sec_filter hci_sec_filter = {
static struct hci_sec_filter hci_sec_filter = {
        /* Packet types */
        /* Packet types */
        0x10,
        0x10,
        /* Events */
        /* Events */
        { 0x1000d9fe, 0x0000300c },
        { 0x1000d9fe, 0x0000300c },
        /* Commands */
        /* Commands */
        {
        {
                { 0x0 },
                { 0x0 },
                /* OGF_LINK_CTL */
                /* OGF_LINK_CTL */
                { 0xbe000006, 0x00000001, 0x0000, 0x00 },
                { 0xbe000006, 0x00000001, 0x0000, 0x00 },
                /* OGF_LINK_POLICY */
                /* OGF_LINK_POLICY */
                { 0x00005200, 0x00000000, 0x0000, 0x00 },
                { 0x00005200, 0x00000000, 0x0000, 0x00 },
                /* OGF_HOST_CTL */
                /* OGF_HOST_CTL */
                { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
                { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
                /* OGF_INFO_PARAM */
                /* OGF_INFO_PARAM */
                { 0x000002be, 0x00000000, 0x0000, 0x00 },
                { 0x000002be, 0x00000000, 0x0000, 0x00 },
                /* OGF_STATUS_PARAM */
                /* OGF_STATUS_PARAM */
                { 0x000000ea, 0x00000000, 0x0000, 0x00 }
                { 0x000000ea, 0x00000000, 0x0000, 0x00 }
        }
        }
};
};
 
 
static struct bluez_sock_list hci_sk_list = {
static struct bluez_sock_list hci_sk_list = {
        lock: RW_LOCK_UNLOCKED
        lock: RW_LOCK_UNLOCKED
};
};
 
 
/* Send frame to RAW socket */
/* Send frame to RAW socket */
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
{
{
        struct sock * sk;
        struct sock * sk;
 
 
        BT_DBG("hdev %p len %d", hdev, skb->len);
        BT_DBG("hdev %p len %d", hdev, skb->len);
 
 
        read_lock(&hci_sk_list.lock);
        read_lock(&hci_sk_list.lock);
        for (sk = hci_sk_list.head; sk; sk = sk->next) {
        for (sk = hci_sk_list.head; sk; sk = sk->next) {
                struct hci_filter *flt;
                struct hci_filter *flt;
                struct sk_buff *nskb;
                struct sk_buff *nskb;
 
 
                if (sk->state != BT_BOUND || hci_pi(sk)->hdev != hdev)
                if (sk->state != BT_BOUND || hci_pi(sk)->hdev != hdev)
                        continue;
                        continue;
 
 
                /* Don't send frame to the socket it came from */
                /* Don't send frame to the socket it came from */
                if (skb->sk == sk)
                if (skb->sk == sk)
                        continue;
                        continue;
 
 
                /* Apply filter */
                /* Apply filter */
                flt = &hci_pi(sk)->filter;
                flt = &hci_pi(sk)->filter;
 
 
                if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
                if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
                        continue;
                        continue;
 
 
                if (skb->pkt_type == HCI_EVENT_PKT) {
                if (skb->pkt_type == HCI_EVENT_PKT) {
                        register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
                        register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
 
 
                        if (!hci_test_bit(evt, &flt->event_mask))
                        if (!hci_test_bit(evt, &flt->event_mask))
                                continue;
                                continue;
 
 
                        if (flt->opcode && ((evt == EVT_CMD_COMPLETE &&
                        if (flt->opcode && ((evt == EVT_CMD_COMPLETE &&
                                        flt->opcode != *(__u16 *)(skb->data + 3)) ||
                                        flt->opcode != *(__u16 *)(skb->data + 3)) ||
                                        (evt == EVT_CMD_STATUS &&
                                        (evt == EVT_CMD_STATUS &&
                                        flt->opcode != *(__u16 *)(skb->data + 4))))
                                        flt->opcode != *(__u16 *)(skb->data + 4))))
                                continue;
                                continue;
                }
                }
 
 
                if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
                if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
                        continue;
                        continue;
 
 
                /* Put type byte before the data */
                /* Put type byte before the data */
                memcpy(skb_push(nskb, 1), &nskb->pkt_type, 1);
                memcpy(skb_push(nskb, 1), &nskb->pkt_type, 1);
 
 
                if (sock_queue_rcv_skb(sk, nskb))
                if (sock_queue_rcv_skb(sk, nskb))
                        kfree_skb(nskb);
                        kfree_skb(nskb);
        }
        }
        read_unlock(&hci_sk_list.lock);
        read_unlock(&hci_sk_list.lock);
}
}
 
 
static int hci_sock_release(struct socket *sock)
static int hci_sock_release(struct socket *sock)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct hci_dev *hdev = hci_pi(sk)->hdev;
        struct hci_dev *hdev = hci_pi(sk)->hdev;
 
 
        BT_DBG("sock %p sk %p", sock, sk);
        BT_DBG("sock %p sk %p", sock, sk);
 
 
        if (!sk)
        if (!sk)
                return 0;
                return 0;
 
 
        bluez_sock_unlink(&hci_sk_list, sk);
        bluez_sock_unlink(&hci_sk_list, sk);
 
 
        if (hdev) {
        if (hdev) {
                atomic_dec(&hdev->promisc);
                atomic_dec(&hdev->promisc);
                hci_dev_put(hdev);
                hci_dev_put(hdev);
        }
        }
 
 
        sock_orphan(sk);
        sock_orphan(sk);
 
 
        skb_queue_purge(&sk->receive_queue);
        skb_queue_purge(&sk->receive_queue);
        skb_queue_purge(&sk->write_queue);
        skb_queue_purge(&sk->write_queue);
 
 
        sock_put(sk);
        sock_put(sk);
 
 
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
        return 0;
        return 0;
}
}
 
 
/* Ioctls that require bound socket */
/* Ioctls that require bound socket */
static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
{
{
        struct hci_dev *hdev = hci_pi(sk)->hdev;
        struct hci_dev *hdev = hci_pi(sk)->hdev;
 
 
        if (!hdev)
        if (!hdev)
                return -EBADFD;
                return -EBADFD;
 
 
        switch (cmd) {
        switch (cmd) {
        case HCISETRAW:
        case HCISETRAW:
                if (!capable(CAP_NET_ADMIN))
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
                        return -EACCES;
 
 
                if (arg)
                if (arg)
                        set_bit(HCI_RAW, &hdev->flags);
                        set_bit(HCI_RAW, &hdev->flags);
                else
                else
                        clear_bit(HCI_RAW, &hdev->flags);
                        clear_bit(HCI_RAW, &hdev->flags);
 
 
                return 0;
                return 0;
 
 
        case HCIGETCONNINFO:
        case HCIGETCONNINFO:
                return hci_get_conn_info(hdev, arg);
                return hci_get_conn_info(hdev, arg);
 
 
        default:
        default:
                if (hdev->ioctl)
                if (hdev->ioctl)
                        return hdev->ioctl(hdev, cmd, arg);
                        return hdev->ioctl(hdev, cmd, arg);
                return -EINVAL;
                return -EINVAL;
        }
        }
}
}
 
 
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        int err;
        int err;
 
 
        BT_DBG("cmd %x arg %lx", cmd, arg);
        BT_DBG("cmd %x arg %lx", cmd, arg);
 
 
        switch (cmd) {
        switch (cmd) {
        case HCIGETDEVLIST:
        case HCIGETDEVLIST:
                return hci_get_dev_list(arg);
                return hci_get_dev_list(arg);
 
 
        case HCIGETDEVINFO:
        case HCIGETDEVINFO:
                return hci_get_dev_info(arg);
                return hci_get_dev_info(arg);
 
 
        case HCIGETCONNLIST:
        case HCIGETCONNLIST:
                return hci_get_conn_list(arg);
                return hci_get_conn_list(arg);
 
 
        case HCIDEVUP:
        case HCIDEVUP:
                if (!capable(CAP_NET_ADMIN))
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
                        return -EACCES;
                return hci_dev_open(arg);
                return hci_dev_open(arg);
 
 
        case HCIDEVDOWN:
        case HCIDEVDOWN:
                if (!capable(CAP_NET_ADMIN))
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
                        return -EACCES;
                return hci_dev_close(arg);
                return hci_dev_close(arg);
 
 
        case HCIDEVRESET:
        case HCIDEVRESET:
                if (!capable(CAP_NET_ADMIN))
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
                        return -EACCES;
                return hci_dev_reset(arg);
                return hci_dev_reset(arg);
 
 
        case HCIDEVRESTAT:
        case HCIDEVRESTAT:
                if (!capable(CAP_NET_ADMIN))
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
                        return -EACCES;
                return hci_dev_reset_stat(arg);
                return hci_dev_reset_stat(arg);
 
 
        case HCISETSCAN:
        case HCISETSCAN:
        case HCISETAUTH:
        case HCISETAUTH:
        case HCISETENCRYPT:
        case HCISETENCRYPT:
        case HCISETPTYPE:
        case HCISETPTYPE:
        case HCISETLINKPOL:
        case HCISETLINKPOL:
        case HCISETLINKMODE:
        case HCISETLINKMODE:
        case HCISETACLMTU:
        case HCISETACLMTU:
        case HCISETSCOMTU:
        case HCISETSCOMTU:
                if (!capable(CAP_NET_ADMIN))
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
                        return -EACCES;
                return hci_dev_cmd(cmd, arg);
                return hci_dev_cmd(cmd, arg);
 
 
        case HCIINQUIRY:
        case HCIINQUIRY:
                return hci_inquiry(arg);
                return hci_inquiry(arg);
 
 
        default:
        default:
                lock_sock(sk);
                lock_sock(sk);
                err = hci_sock_bound_ioctl(sk, cmd, arg);
                err = hci_sock_bound_ioctl(sk, cmd, arg);
                release_sock(sk);
                release_sock(sk);
                return err;
                return err;
        };
        };
}
}
 
 
static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
{
{
        struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
        struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct hci_dev *hdev = NULL;
        struct hci_dev *hdev = NULL;
        int err = 0;
        int err = 0;
 
 
        BT_DBG("sock %p sk %p", sock, sk);
        BT_DBG("sock %p sk %p", sock, sk);
 
 
        if (!haddr || haddr->hci_family != AF_BLUETOOTH)
        if (!haddr || haddr->hci_family != AF_BLUETOOTH)
                return -EINVAL;
                return -EINVAL;
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if (hci_pi(sk)->hdev) {
        if (hci_pi(sk)->hdev) {
                err = -EALREADY;
                err = -EALREADY;
                goto done;
                goto done;
        }
        }
 
 
        if (haddr->hci_dev != HCI_DEV_NONE) {
        if (haddr->hci_dev != HCI_DEV_NONE) {
                if (!(hdev = hci_dev_get(haddr->hci_dev))) {
                if (!(hdev = hci_dev_get(haddr->hci_dev))) {
                        err = -ENODEV;
                        err = -ENODEV;
                        goto done;
                        goto done;
                }
                }
 
 
                atomic_inc(&hdev->promisc);
                atomic_inc(&hdev->promisc);
        }
        }
 
 
        hci_pi(sk)->hdev = hdev;
        hci_pi(sk)->hdev = hdev;
        sk->state = BT_BOUND;
        sk->state = BT_BOUND;
 
 
done:
done:
        release_sock(sk);
        release_sock(sk);
        return err;
        return err;
}
}
 
 
static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer)
static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer)
{
{
        struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
        struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
 
 
        BT_DBG("sock %p sk %p", sock, sk);
        BT_DBG("sock %p sk %p", sock, sk);
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        *addr_len = sizeof(*haddr);
        *addr_len = sizeof(*haddr);
        haddr->hci_family = AF_BLUETOOTH;
        haddr->hci_family = AF_BLUETOOTH;
        haddr->hci_dev    = hci_pi(sk)->hdev->id;
        haddr->hci_dev    = hci_pi(sk)->hdev->id;
 
 
        release_sock(sk);
        release_sock(sk);
        return 0;
        return 0;
}
}
 
 
static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
{
{
        __u32 mask = hci_pi(sk)->cmsg_mask;
        __u32 mask = hci_pi(sk)->cmsg_mask;
 
 
        if (mask & HCI_CMSG_DIR)
        if (mask & HCI_CMSG_DIR)
                put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming);
                put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming);
 
 
        if (mask & HCI_CMSG_TSTAMP)
        if (mask & HCI_CMSG_TSTAMP)
                put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp);
                put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp);
}
}
 
 
static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
{
{
        int noblock = flags & MSG_DONTWAIT;
        int noblock = flags & MSG_DONTWAIT;
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int copied, err;
        int copied, err;
 
 
        BT_DBG("sock %p, sk %p", sock, sk);
        BT_DBG("sock %p, sk %p", sock, sk);
 
 
        if (flags & (MSG_OOB))
        if (flags & (MSG_OOB))
                return -EOPNOTSUPP;
                return -EOPNOTSUPP;
 
 
        if (sk->state == BT_CLOSED)
        if (sk->state == BT_CLOSED)
                return 0;
                return 0;
 
 
        if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
        if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
                return err;
                return err;
 
 
        msg->msg_namelen = 0;
        msg->msg_namelen = 0;
 
 
        copied = skb->len;
        copied = skb->len;
        if (len < copied) {
        if (len < copied) {
                msg->msg_flags |= MSG_TRUNC;
                msg->msg_flags |= MSG_TRUNC;
                copied = len;
                copied = len;
        }
        }
 
 
        skb->h.raw = skb->data;
        skb->h.raw = skb->data;
        err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
        err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
 
        hci_sock_cmsg(sk, msg, skb);
        hci_sock_cmsg(sk, msg, skb);
 
 
        skb_free_datagram(sk, skb);
        skb_free_datagram(sk, skb);
 
 
        return err ? : copied;
        return err ? : copied;
}
}
 
 
static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
                            struct scm_cookie *scm)
                            struct scm_cookie *scm)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct hci_dev *hdev;
        struct hci_dev *hdev;
        struct sk_buff *skb;
        struct sk_buff *skb;
        int err;
        int err;
 
 
        BT_DBG("sock %p sk %p", sock, sk);
        BT_DBG("sock %p sk %p", sock, sk);
 
 
        if (msg->msg_flags & MSG_OOB)
        if (msg->msg_flags & MSG_OOB)
                return -EOPNOTSUPP;
                return -EOPNOTSUPP;
 
 
        if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
        if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
                return -EINVAL;
                return -EINVAL;
 
 
        if (len < 4)
        if (len < 4)
                return -EINVAL;
                return -EINVAL;
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        if (!(hdev = hci_pi(sk)->hdev)) {
        if (!(hdev = hci_pi(sk)->hdev)) {
                err = -EBADFD;
                err = -EBADFD;
                goto done;
                goto done;
        }
        }
 
 
        if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
        if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
                goto done;
                goto done;
 
 
        if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
        if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
                err = -EFAULT;
                err = -EFAULT;
                goto drop;
                goto drop;
        }
        }
 
 
        skb->pkt_type = *((unsigned char *) skb->data);
        skb->pkt_type = *((unsigned char *) skb->data);
        skb_pull(skb, 1);
        skb_pull(skb, 1);
        skb->dev = (void *) hdev;
        skb->dev = (void *) hdev;
 
 
        if (skb->pkt_type == HCI_COMMAND_PKT) {
        if (skb->pkt_type == HCI_COMMAND_PKT) {
                u16 opcode = __le16_to_cpu(get_unaligned((u16 *)skb->data));
                u16 opcode = __le16_to_cpu(get_unaligned((u16 *)skb->data));
                u16 ogf = cmd_opcode_ogf(opcode);
                u16 ogf = cmd_opcode_ogf(opcode);
                u16 ocf = cmd_opcode_ocf(opcode);
                u16 ocf = cmd_opcode_ocf(opcode);
 
 
                if (((ogf > HCI_SFLT_MAX_OGF) ||
                if (((ogf > HCI_SFLT_MAX_OGF) ||
                                !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) &&
                                !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) &&
                                        !capable(CAP_NET_RAW)) {
                                        !capable(CAP_NET_RAW)) {
                        err = -EPERM;
                        err = -EPERM;
                        goto drop;
                        goto drop;
                }
                }
 
 
                if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) {
                if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) {
                        skb_queue_tail(&hdev->raw_q, skb);
                        skb_queue_tail(&hdev->raw_q, skb);
                        hci_sched_tx(hdev);
                        hci_sched_tx(hdev);
                } else {
                } else {
                        skb_queue_tail(&hdev->cmd_q, skb);
                        skb_queue_tail(&hdev->cmd_q, skb);
                        hci_sched_cmd(hdev);
                        hci_sched_cmd(hdev);
                }
                }
        } else {
        } else {
                if (!capable(CAP_NET_RAW)) {
                if (!capable(CAP_NET_RAW)) {
                        err = -EPERM;
                        err = -EPERM;
                        goto drop;
                        goto drop;
                }
                }
 
 
                skb_queue_tail(&hdev->raw_q, skb);
                skb_queue_tail(&hdev->raw_q, skb);
                hci_sched_tx(hdev);
                hci_sched_tx(hdev);
        }
        }
 
 
        err = len;
        err = len;
 
 
done:
done:
        release_sock(sk);
        release_sock(sk);
        return err;
        return err;
 
 
drop:
drop:
        kfree_skb(skb);
        kfree_skb(skb);
        goto done;
        goto done;
}
}
 
 
int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        struct hci_filter flt = { opcode: 0 };
        struct hci_filter flt = { opcode: 0 };
        int err = 0, opt = 0;
        int err = 0, opt = 0;
 
 
        BT_DBG("sk %p, opt %d", sk, optname);
        BT_DBG("sk %p, opt %d", sk, optname);
 
 
        lock_sock(sk);
        lock_sock(sk);
 
 
        switch (optname) {
        switch (optname) {
        case HCI_DATA_DIR:
        case HCI_DATA_DIR:
                if (get_user(opt, (int *)optval)) {
                if (get_user(opt, (int *)optval)) {
                        err = -EFAULT;
                        err = -EFAULT;
                        break;
                        break;
                }
                }
 
 
                if (opt)
                if (opt)
                        hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR;
                        hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR;
                else
                else
                        hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR;
                        hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR;
                break;
                break;
 
 
        case HCI_TIME_STAMP:
        case HCI_TIME_STAMP:
                if (get_user(opt, (int *)optval)) {
                if (get_user(opt, (int *)optval)) {
                        err = -EFAULT;
                        err = -EFAULT;
                        break;
                        break;
                }
                }
 
 
                if (opt)
                if (opt)
                        hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP;
                        hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP;
                else
                else
                        hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP;
                        hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP;
                break;
                break;
 
 
        case HCI_FILTER:
        case HCI_FILTER:
                len = MIN(len, sizeof(struct hci_filter));
                len = MIN(len, sizeof(struct hci_filter));
                if (copy_from_user(&flt, optval, len)) {
                if (copy_from_user(&flt, optval, len)) {
                        err = -EFAULT;
                        err = -EFAULT;
                        break;
                        break;
                }
                }
 
 
                if (!capable(CAP_NET_RAW)) {
                if (!capable(CAP_NET_RAW)) {
                        flt.type_mask     &= hci_sec_filter.type_mask;
                        flt.type_mask     &= hci_sec_filter.type_mask;
                        flt.event_mask[0] &= hci_sec_filter.event_mask[0];
                        flt.event_mask[0] &= hci_sec_filter.event_mask[0];
                        flt.event_mask[1] &= hci_sec_filter.event_mask[1];
                        flt.event_mask[1] &= hci_sec_filter.event_mask[1];
                }
                }
 
 
                memcpy(&hci_pi(sk)->filter, &flt, len);
                memcpy(&hci_pi(sk)->filter, &flt, len);
                break;
                break;
 
 
        default:
        default:
                err = -ENOPROTOOPT;
                err = -ENOPROTOOPT;
                break;
                break;
        };
        };
 
 
        release_sock(sk);
        release_sock(sk);
        return err;
        return err;
}
}
 
 
int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
{
        struct sock *sk = sock->sk;
        struct sock *sk = sock->sk;
        int len, opt;
        int len, opt;
 
 
        if (get_user(len, optlen))
        if (get_user(len, optlen))
                return -EFAULT;
                return -EFAULT;
 
 
        switch (optname) {
        switch (optname) {
        case HCI_DATA_DIR:
        case HCI_DATA_DIR:
                if (hci_pi(sk)->cmsg_mask & HCI_CMSG_DIR)
                if (hci_pi(sk)->cmsg_mask & HCI_CMSG_DIR)
                        opt = 1;
                        opt = 1;
                else
                else
                        opt = 0;
                        opt = 0;
 
 
                if (put_user(opt, optval))
                if (put_user(opt, optval))
                        return -EFAULT;
                        return -EFAULT;
                break;
                break;
 
 
        case HCI_TIME_STAMP:
        case HCI_TIME_STAMP:
                if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP)
                if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP)
                        opt = 1;
                        opt = 1;
                else
                else
                        opt = 0;
                        opt = 0;
 
 
                if (put_user(opt, optval))
                if (put_user(opt, optval))
                        return -EFAULT;
                        return -EFAULT;
                break;
                break;
 
 
        case HCI_FILTER:
        case HCI_FILTER:
                len = MIN(len, sizeof(struct hci_filter));
                len = MIN(len, sizeof(struct hci_filter));
                if (copy_to_user(optval, &hci_pi(sk)->filter, len))
                if (copy_to_user(optval, &hci_pi(sk)->filter, len))
                        return -EFAULT;
                        return -EFAULT;
                break;
                break;
 
 
        default:
        default:
                return -ENOPROTOOPT;
                return -ENOPROTOOPT;
                break;
                break;
        };
        };
 
 
        return 0;
        return 0;
}
}
 
 
struct proto_ops hci_sock_ops = {
struct proto_ops hci_sock_ops = {
        family:         PF_BLUETOOTH,
        family:         PF_BLUETOOTH,
        release:        hci_sock_release,
        release:        hci_sock_release,
        bind:           hci_sock_bind,
        bind:           hci_sock_bind,
        getname:        hci_sock_getname,
        getname:        hci_sock_getname,
        sendmsg:        hci_sock_sendmsg,
        sendmsg:        hci_sock_sendmsg,
        recvmsg:        hci_sock_recvmsg,
        recvmsg:        hci_sock_recvmsg,
        ioctl:          hci_sock_ioctl,
        ioctl:          hci_sock_ioctl,
        poll:           datagram_poll,
        poll:           datagram_poll,
        listen:         sock_no_listen,
        listen:         sock_no_listen,
        shutdown:       sock_no_shutdown,
        shutdown:       sock_no_shutdown,
        setsockopt:     hci_sock_setsockopt,
        setsockopt:     hci_sock_setsockopt,
        getsockopt:     hci_sock_getsockopt,
        getsockopt:     hci_sock_getsockopt,
        connect:        sock_no_connect,
        connect:        sock_no_connect,
        socketpair:     sock_no_socketpair,
        socketpair:     sock_no_socketpair,
        accept:         sock_no_accept,
        accept:         sock_no_accept,
        mmap:           sock_no_mmap
        mmap:           sock_no_mmap
};
};
 
 
static int hci_sock_create(struct socket *sock, int protocol)
static int hci_sock_create(struct socket *sock, int protocol)
{
{
        struct sock *sk;
        struct sock *sk;
 
 
        BT_DBG("sock %p", sock);
        BT_DBG("sock %p", sock);
 
 
        if (sock->type != SOCK_RAW)
        if (sock->type != SOCK_RAW)
                return -ESOCKTNOSUPPORT;
                return -ESOCKTNOSUPPORT;
 
 
        sock->ops = &hci_sock_ops;
        sock->ops = &hci_sock_ops;
 
 
        if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
        if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
                return -ENOMEM;
                return -ENOMEM;
 
 
        sock->state = SS_UNCONNECTED;
        sock->state = SS_UNCONNECTED;
        sock_init_data(sock, sk);
        sock_init_data(sock, sk);
 
 
        memset(&sk->protinfo, 0, sizeof(struct hci_pinfo));
        memset(&sk->protinfo, 0, sizeof(struct hci_pinfo));
        sk->destruct = NULL;
        sk->destruct = NULL;
        sk->protocol = protocol;
        sk->protocol = protocol;
        sk->state    = BT_OPEN;
        sk->state    = BT_OPEN;
 
 
        bluez_sock_link(&hci_sk_list, sk);
        bluez_sock_link(&hci_sk_list, sk);
 
 
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
        return 0;
        return 0;
}
}
 
 
static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
{
        struct hci_dev *hdev = (struct hci_dev *) ptr;
        struct hci_dev *hdev = (struct hci_dev *) ptr;
        evt_si_device sd;
        evt_si_device sd;
 
 
        BT_DBG("hdev %s event %ld", hdev->name, event);
        BT_DBG("hdev %s event %ld", hdev->name, event);
 
 
        /* Send event to sockets */
        /* Send event to sockets */
        sd.event  = event;
        sd.event  = event;
        sd.dev_id = hdev->id;
        sd.dev_id = hdev->id;
        hci_si_event(NULL, EVT_SI_DEVICE, EVT_SI_DEVICE_SIZE, &sd);
        hci_si_event(NULL, EVT_SI_DEVICE, EVT_SI_DEVICE_SIZE, &sd);
 
 
        if (event == HCI_DEV_UNREG) {
        if (event == HCI_DEV_UNREG) {
                struct sock *sk;
                struct sock *sk;
 
 
                /* Detach sockets from device */
                /* Detach sockets from device */
                read_lock(&hci_sk_list.lock);
                read_lock(&hci_sk_list.lock);
                for (sk = hci_sk_list.head; sk; sk = sk->next) {
                for (sk = hci_sk_list.head; sk; sk = sk->next) {
                        bh_lock_sock(sk);
                        bh_lock_sock(sk);
                        if (hci_pi(sk)->hdev == hdev) {
                        if (hci_pi(sk)->hdev == hdev) {
                                hci_pi(sk)->hdev = NULL;
                                hci_pi(sk)->hdev = NULL;
                                sk->err = EPIPE;
                                sk->err = EPIPE;
                                sk->state = BT_OPEN;
                                sk->state = BT_OPEN;
                                sk->state_change(sk);
                                sk->state_change(sk);
 
 
                                hci_dev_put(hdev);
                                hci_dev_put(hdev);
                        }
                        }
                        bh_unlock_sock(sk);
                        bh_unlock_sock(sk);
                }
                }
                read_unlock(&hci_sk_list.lock);
                read_unlock(&hci_sk_list.lock);
        }
        }
 
 
        return NOTIFY_DONE;
        return NOTIFY_DONE;
}
}
 
 
struct net_proto_family hci_sock_family_ops = {
struct net_proto_family hci_sock_family_ops = {
        family: PF_BLUETOOTH,
        family: PF_BLUETOOTH,
        create: hci_sock_create
        create: hci_sock_create
};
};
 
 
struct notifier_block hci_sock_nblock = {
struct notifier_block hci_sock_nblock = {
        notifier_call: hci_sock_dev_event
        notifier_call: hci_sock_dev_event
};
};
 
 
int hci_sock_init(void)
int hci_sock_init(void)
{
{
        if (bluez_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) {
        if (bluez_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) {
                BT_ERR("Can't register HCI socket");
                BT_ERR("Can't register HCI socket");
                return -EPROTO;
                return -EPROTO;
        }
        }
 
 
        hci_register_notifier(&hci_sock_nblock);
        hci_register_notifier(&hci_sock_nblock);
        return 0;
        return 0;
}
}
 
 
int hci_sock_cleanup(void)
int hci_sock_cleanup(void)
{
{
        if (bluez_sock_unregister(BTPROTO_HCI))
        if (bluez_sock_unregister(BTPROTO_HCI))
                BT_ERR("Can't unregister HCI socket");
                BT_ERR("Can't unregister HCI socket");
 
 
        hci_unregister_notifier(&hci_sock_nblock);
        hci_unregister_notifier(&hci_sock_nblock);
        return 0;
        return 0;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.