Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

1421
net/bluetooth/6lowpan.c Normal file

File diff suppressed because it is too large Load diff

56
net/bluetooth/Kconfig Normal file
View file

@ -0,0 +1,56 @@
#
# Bluetooth subsystem configuration
#
menuconfig BT
tristate "Bluetooth subsystem support"
depends on NET && !S390
depends on RFKILL || !RFKILL
select CRC16
select CRYPTO
select CRYPTO_BLKCIPHER
select CRYPTO_AES
select CRYPTO_ECB
select CRYPTO_SHA256
help
Bluetooth is low-cost, low-power, short-range wireless technology.
It was designed as a replacement for cables and other short-range
technologies like IrDA. Bluetooth operates in personal area range
that typically extends up to 10 meters. More information about
Bluetooth can be found at <http://www.bluetooth.com/>.
Linux Bluetooth subsystem consist of several layers:
Bluetooth Core
HCI device and connection manager, scheduler
SCO audio links
L2CAP (Logical Link Control and Adaptation Protocol)
SMP (Security Manager Protocol) on LE (Low Energy) links
HCI Device drivers (Interface to the hardware)
RFCOMM Module (RFCOMM Protocol)
BNEP Module (Bluetooth Network Encapsulation Protocol)
CMTP Module (CAPI Message Transport Protocol)
HIDP Module (Human Interface Device Protocol)
Say Y here to compile Bluetooth support into the kernel or say M to
compile it as module (bluetooth).
To use Linux Bluetooth subsystem, you will need several user-space
utilities like hciconfig and bluetoothd. These utilities and updates
to Bluetooth kernel modules are provided in the BlueZ packages. For
more information, see <http://www.bluez.org/>.
config BT_6LOWPAN
tristate "Bluetooth 6LoWPAN support"
depends on BT && 6LOWPAN
help
IPv6 compression over Bluetooth Low Energy.
source "net/bluetooth/rfcomm/Kconfig"
source "net/bluetooth/bnep/Kconfig"
source "net/bluetooth/cmtp/Kconfig"
source "net/bluetooth/hidp/Kconfig"
source "drivers/bluetooth/Kconfig"

18
net/bluetooth/Makefile Normal file
View file

@ -0,0 +1,18 @@
#
# Makefile for the Linux Bluetooth subsystem.
#
obj-$(CONFIG_BT) += bluetooth.o
obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
a2mp.o amp.o
subdir-ccflags-y += -D__CHECK_ENDIAN__

1025
net/bluetooth/a2mp.c Normal file

File diff suppressed because it is too large Load diff

150
net/bluetooth/a2mp.h Normal file
View file

@ -0,0 +1,150 @@
/*
Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
Copyright (c) 2011,2012 Intel Corp.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 and
only version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef __A2MP_H
#define __A2MP_H
#include <net/bluetooth/l2cap.h>
#define A2MP_FEAT_EXT 0x8000
enum amp_mgr_state {
READ_LOC_AMP_INFO,
READ_LOC_AMP_ASSOC,
READ_LOC_AMP_ASSOC_FINAL,
WRITE_REMOTE_AMP_ASSOC,
};
struct amp_mgr {
struct list_head list;
struct l2cap_conn *l2cap_conn;
struct l2cap_chan *a2mp_chan;
struct l2cap_chan *bredr_chan;
struct kref kref;
__u8 ident;
__u8 handle;
unsigned long state;
unsigned long flags;
struct list_head amp_ctrls;
struct mutex amp_ctrls_lock;
};
struct a2mp_cmd {
__u8 code;
__u8 ident;
__le16 len;
__u8 data[0];
} __packed;
/* A2MP command codes */
#define A2MP_COMMAND_REJ 0x01
struct a2mp_cmd_rej {
__le16 reason;
__u8 data[0];
} __packed;
#define A2MP_DISCOVER_REQ 0x02
struct a2mp_discov_req {
__le16 mtu;
__le16 ext_feat;
} __packed;
struct a2mp_cl {
__u8 id;
__u8 type;
__u8 status;
} __packed;
#define A2MP_DISCOVER_RSP 0x03
struct a2mp_discov_rsp {
__le16 mtu;
__le16 ext_feat;
struct a2mp_cl cl[0];
} __packed;
#define A2MP_CHANGE_NOTIFY 0x04
#define A2MP_CHANGE_RSP 0x05
#define A2MP_GETINFO_REQ 0x06
struct a2mp_info_req {
__u8 id;
} __packed;
#define A2MP_GETINFO_RSP 0x07
struct a2mp_info_rsp {
__u8 id;
__u8 status;
__le32 total_bw;
__le32 max_bw;
__le32 min_latency;
__le16 pal_cap;
__le16 assoc_size;
} __packed;
#define A2MP_GETAMPASSOC_REQ 0x08
struct a2mp_amp_assoc_req {
__u8 id;
} __packed;
#define A2MP_GETAMPASSOC_RSP 0x09
struct a2mp_amp_assoc_rsp {
__u8 id;
__u8 status;
__u8 amp_assoc[0];
} __packed;
#define A2MP_CREATEPHYSLINK_REQ 0x0A
#define A2MP_DISCONNPHYSLINK_REQ 0x0C
struct a2mp_physlink_req {
__u8 local_id;
__u8 remote_id;
__u8 amp_assoc[0];
} __packed;
#define A2MP_CREATEPHYSLINK_RSP 0x0B
#define A2MP_DISCONNPHYSLINK_RSP 0x0D
struct a2mp_physlink_rsp {
__u8 local_id;
__u8 remote_id;
__u8 status;
} __packed;
/* A2MP response status */
#define A2MP_STATUS_SUCCESS 0x00
#define A2MP_STATUS_INVALID_CTRL_ID 0x01
#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
#define A2MP_STATUS_COLLISION_OCCURED 0x03
#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
#define A2MP_STATUS_SECURITY_VIOLATION 0x06
extern struct list_head amp_mgr_list;
extern struct mutex amp_mgr_list_lock;
struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
u8 __next_ident(struct amp_mgr *mgr);
struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
struct sk_buff *skb);
struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
void a2mp_discover_amp(struct l2cap_chan *chan);
void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
#endif /* __A2MP_H */

View file

@ -0,0 +1,810 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
/* Bluetooth address family and sockets. */
#include <linux/module.h>
#include <linux/debugfs.h>
#include <asm/ioctls.h>
#include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h>
#define VERSION "2.19"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
static const struct net_proto_family *bt_proto[BT_MAX_PROTO];
static DEFINE_RWLOCK(bt_proto_lock);
static struct lock_class_key bt_lock_key[BT_MAX_PROTO];
static const char *const bt_key_strings[BT_MAX_PROTO] = {
"sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP",
"sk_lock-AF_BLUETOOTH-BTPROTO_HCI",
"sk_lock-AF_BLUETOOTH-BTPROTO_SCO",
"sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM",
"sk_lock-AF_BLUETOOTH-BTPROTO_BNEP",
"sk_lock-AF_BLUETOOTH-BTPROTO_CMTP",
"sk_lock-AF_BLUETOOTH-BTPROTO_HIDP",
"sk_lock-AF_BLUETOOTH-BTPROTO_AVDTP",
};
static struct lock_class_key bt_slock_key[BT_MAX_PROTO];
static const char *const bt_slock_key_strings[BT_MAX_PROTO] = {
"slock-AF_BLUETOOTH-BTPROTO_L2CAP",
"slock-AF_BLUETOOTH-BTPROTO_HCI",
"slock-AF_BLUETOOTH-BTPROTO_SCO",
"slock-AF_BLUETOOTH-BTPROTO_RFCOMM",
"slock-AF_BLUETOOTH-BTPROTO_BNEP",
"slock-AF_BLUETOOTH-BTPROTO_CMTP",
"slock-AF_BLUETOOTH-BTPROTO_HIDP",
"slock-AF_BLUETOOTH-BTPROTO_AVDTP",
};
void bt_sock_reclassify_lock(struct sock *sk, int proto)
{
BUG_ON(!sk);
BUG_ON(sock_owned_by_user(sk));
sock_lock_init_class_and_name(sk,
bt_slock_key_strings[proto], &bt_slock_key[proto],
bt_key_strings[proto], &bt_lock_key[proto]);
}
EXPORT_SYMBOL(bt_sock_reclassify_lock);
int bt_sock_register(int proto, const struct net_proto_family *ops)
{
int err = 0;
if (proto < 0 || proto >= BT_MAX_PROTO)
return -EINVAL;
write_lock(&bt_proto_lock);
if (bt_proto[proto])
err = -EEXIST;
else
bt_proto[proto] = ops;
write_unlock(&bt_proto_lock);
return err;
}
EXPORT_SYMBOL(bt_sock_register);
void bt_sock_unregister(int proto)
{
if (proto < 0 || proto >= BT_MAX_PROTO)
return;
write_lock(&bt_proto_lock);
bt_proto[proto] = NULL;
write_unlock(&bt_proto_lock);
}
EXPORT_SYMBOL(bt_sock_unregister);
#ifdef CONFIG_PARANOID_NETWORK
static inline int current_has_bt_admin(void)
{
return !current_euid();
}
static inline int current_has_bt(void)
{
return current_has_bt_admin();
}
# else
static inline int current_has_bt_admin(void)
{
return 1;
}
static inline int current_has_bt(void)
{
return 1;
}
#endif
static int bt_sock_create(struct net *net, struct socket *sock, int proto,
int kern)
{
int err;
if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO ||
proto == BTPROTO_L2CAP) {
if (!current_has_bt())
return -EPERM;
} else if (!current_has_bt_admin())
return -EPERM;
if (net != &init_net)
return -EAFNOSUPPORT;
if (proto < 0 || proto >= BT_MAX_PROTO)
return -EINVAL;
if (!bt_proto[proto])
request_module("bt-proto-%d", proto);
err = -EPROTONOSUPPORT;
read_lock(&bt_proto_lock);
if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) {
err = bt_proto[proto]->create(net, sock, proto, kern);
if (!err)
bt_sock_reclassify_lock(sock->sk, proto);
module_put(bt_proto[proto]->owner);
}
read_unlock(&bt_proto_lock);
return err;
}
void bt_sock_link(struct bt_sock_list *l, struct sock *sk)
{
write_lock(&l->lock);
sk_add_node(sk, &l->head);
write_unlock(&l->lock);
}
EXPORT_SYMBOL(bt_sock_link);
void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk)
{
write_lock(&l->lock);
sk_del_node_init(sk);
write_unlock(&l->lock);
}
EXPORT_SYMBOL(bt_sock_unlink);
void bt_accept_enqueue(struct sock *parent, struct sock *sk)
{
BT_DBG("parent %p, sk %p", parent, sk);
sock_hold(sk);
list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q);
bt_sk(sk)->parent = parent;
parent->sk_ack_backlog++;
}
EXPORT_SYMBOL(bt_accept_enqueue);
void bt_accept_unlink(struct sock *sk)
{
BT_DBG("sk %p state %d", sk, sk->sk_state);
list_del_init(&bt_sk(sk)->accept_q);
bt_sk(sk)->parent->sk_ack_backlog--;
bt_sk(sk)->parent = NULL;
sock_put(sk);
}
EXPORT_SYMBOL(bt_accept_unlink);
struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
{
struct list_head *p, *n;
struct sock *sk;
BT_DBG("parent %p", parent);
list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
lock_sock(sk);
/* FIXME: Is this check still needed */
if (sk->sk_state == BT_CLOSED) {
release_sock(sk);
bt_accept_unlink(sk);
continue;
}
if (sk->sk_state == BT_CONNECTED || !newsock ||
test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) {
bt_accept_unlink(sk);
if (newsock)
sock_graft(sk, newsock);
release_sock(sk);
return sk;
}
release_sock(sk);
}
return NULL;
}
EXPORT_SYMBOL(bt_accept_dequeue);
int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len, int flags)
{
int noblock = flags & MSG_DONTWAIT;
struct sock *sk = sock->sk;
struct sk_buff *skb;
size_t copied;
int err;
BT_DBG("sock %p sk %p len %zu", sock, sk, len);
if (flags & (MSG_OOB))
return -EOPNOTSUPP;
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb) {
if (sk->sk_shutdown & RCV_SHUTDOWN)
return 0;
return err;
}
copied = skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
skb_reset_transport_header(skb);
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (err == 0) {
sock_recv_ts_and_drops(msg, sk, skb);
if (bt_sk(sk)->skb_msg_name)
bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
&msg->msg_namelen);
}
skb_free_datagram(sk, skb);
return err ? : copied;
}
EXPORT_SYMBOL(bt_sock_recvmsg);
static long bt_sock_data_wait(struct sock *sk, long timeo)
{
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(sk_sleep(sk), &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (!skb_queue_empty(&sk->sk_receive_queue))
break;
if (sk->sk_err || (sk->sk_shutdown & RCV_SHUTDOWN))
break;
if (signal_pending(current) || !timeo)
break;
set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
return timeo;
}
int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
{
struct sock *sk = sock->sk;
int err = 0;
size_t target, copied = 0;
long timeo;
if (flags & MSG_OOB)
return -EOPNOTSUPP;
BT_DBG("sk %p size %zu", sk, size);
lock_sock(sk);
target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
do {
struct sk_buff *skb;
int chunk;
skb = skb_dequeue(&sk->sk_receive_queue);
if (!skb) {
if (copied >= target)
break;
err = sock_error(sk);
if (err)
break;
if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
err = -EAGAIN;
if (!timeo)
break;
timeo = bt_sock_data_wait(sk, timeo);
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
goto out;
}
continue;
}
chunk = min_t(unsigned int, skb->len, size);
if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) {
skb_queue_head(&sk->sk_receive_queue, skb);
if (!copied)
copied = -EFAULT;
break;
}
copied += chunk;
size -= chunk;
sock_recv_ts_and_drops(msg, sk, skb);
if (!(flags & MSG_PEEK)) {
int skb_len = skb_headlen(skb);
if (chunk <= skb_len) {
__skb_pull(skb, chunk);
} else {
struct sk_buff *frag;
__skb_pull(skb, skb_len);
chunk -= skb_len;
skb_walk_frags(skb, frag) {
if (chunk <= frag->len) {
/* Pulling partial data */
skb->len -= chunk;
skb->data_len -= chunk;
__skb_pull(frag, chunk);
break;
} else if (frag->len) {
/* Pulling all frag data */
chunk -= frag->len;
skb->len -= frag->len;
skb->data_len -= frag->len;
__skb_pull(frag, frag->len);
}
}
}
if (skb->len) {
skb_queue_head(&sk->sk_receive_queue, skb);
break;
}
kfree_skb(skb);
} else {
/* put message back and return */
skb_queue_head(&sk->sk_receive_queue, skb);
break;
}
} while (size);
out:
release_sock(sk);
return copied ? : err;
}
EXPORT_SYMBOL(bt_sock_stream_recvmsg);
static inline unsigned int bt_accept_poll(struct sock *parent)
{
struct list_head *p, *n;
struct sock *sk;
list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
if (sk->sk_state == BT_CONNECTED ||
(test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags) &&
sk->sk_state == BT_CONNECT2))
return POLLIN | POLLRDNORM;
}
return 0;
}
unsigned int bt_sock_poll(struct file *file, struct socket *sock,
poll_table *wait)
{
struct sock *sk = sock->sk;
unsigned int mask = 0;
BT_DBG("sock %p, sk %p", sock, sk);
poll_wait(file, sk_sleep(sk), wait);
if (sk->sk_state == BT_LISTEN)
return bt_accept_poll(sk);
if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
mask |= POLLERR |
(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0);
if (sk->sk_shutdown & RCV_SHUTDOWN)
mask |= POLLRDHUP | POLLIN | POLLRDNORM;
if (sk->sk_shutdown == SHUTDOWN_MASK)
mask |= POLLHUP;
if (!skb_queue_empty(&sk->sk_receive_queue))
mask |= POLLIN | POLLRDNORM;
if (sk->sk_state == BT_CLOSED)
mask |= POLLHUP;
if (sk->sk_state == BT_CONNECT ||
sk->sk_state == BT_CONNECT2 ||
sk->sk_state == BT_CONFIG)
return mask;
if (!test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags) && sock_writeable(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
return mask;
}
EXPORT_SYMBOL(bt_sock_poll);
int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
long amount;
int err;
BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg);
switch (cmd) {
case TIOCOUTQ:
if (sk->sk_state == BT_LISTEN)
return -EINVAL;
amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
if (amount < 0)
amount = 0;
err = put_user(amount, (int __user *) arg);
break;
case TIOCINQ:
if (sk->sk_state == BT_LISTEN)
return -EINVAL;
lock_sock(sk);
skb = skb_peek(&sk->sk_receive_queue);
amount = skb ? skb->len : 0;
release_sock(sk);
err = put_user(amount, (int __user *) arg);
break;
case SIOCGSTAMP:
err = sock_get_timestamp(sk, (struct timeval __user *) arg);
break;
case SIOCGSTAMPNS:
err = sock_get_timestampns(sk, (struct timespec __user *) arg);
break;
default:
err = -ENOIOCTLCMD;
break;
}
return err;
}
EXPORT_SYMBOL(bt_sock_ioctl);
/* This function expects the sk lock to be held when called */
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
{
DECLARE_WAITQUEUE(wait, current);
int err = 0;
BT_DBG("sk %p", sk);
add_wait_queue(sk_sleep(sk), &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (sk->sk_state != state) {
if (!timeo) {
err = -EINPROGRESS;
break;
}
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
break;
}
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
set_current_state(TASK_INTERRUPTIBLE);
err = sock_error(sk);
if (err)
break;
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
return err;
}
EXPORT_SYMBOL(bt_sock_wait_state);
/* This function expects the sk lock to be held when called */
int bt_sock_wait_ready(struct sock *sk, unsigned long flags)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long timeo;
int err = 0;
BT_DBG("sk %p", sk);
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
add_wait_queue(sk_sleep(sk), &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags)) {
if (!timeo) {
err = -EAGAIN;
break;
}
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
break;
}
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
set_current_state(TASK_INTERRUPTIBLE);
err = sock_error(sk);
if (err)
break;
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
return err;
}
EXPORT_SYMBOL(bt_sock_wait_ready);
#ifdef CONFIG_PROC_FS
struct bt_seq_state {
struct bt_sock_list *l;
};
static void *bt_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(seq->private->l->lock)
{
struct bt_seq_state *s = seq->private;
struct bt_sock_list *l = s->l;
read_lock(&l->lock);
return seq_hlist_start_head(&l->head, *pos);
}
static void *bt_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct bt_seq_state *s = seq->private;
struct bt_sock_list *l = s->l;
return seq_hlist_next(v, &l->head, pos);
}
static void bt_seq_stop(struct seq_file *seq, void *v)
__releases(seq->private->l->lock)
{
struct bt_seq_state *s = seq->private;
struct bt_sock_list *l = s->l;
read_unlock(&l->lock);
}
static int bt_seq_show(struct seq_file *seq, void *v)
{
struct bt_seq_state *s = seq->private;
struct bt_sock_list *l = s->l;
if (v == SEQ_START_TOKEN) {
seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Parent");
if (l->custom_seq_show) {
seq_putc(seq, ' ');
l->custom_seq_show(seq, v);
}
seq_putc(seq, '\n');
} else {
struct sock *sk = sk_entry(v);
struct bt_sock *bt = bt_sk(sk);
seq_printf(seq,
"%pK %-6d %-6u %-6u %-6u %-6lu %-6lu",
sk,
atomic_read(&sk->sk_refcnt),
sk_rmem_alloc_get(sk),
sk_wmem_alloc_get(sk),
from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
sock_i_ino(sk),
bt->parent? sock_i_ino(bt->parent): 0LU);
if (l->custom_seq_show) {
seq_putc(seq, ' ');
l->custom_seq_show(seq, v);
}
seq_putc(seq, '\n');
}
return 0;
}
static const struct seq_operations bt_seq_ops = {
.start = bt_seq_start,
.next = bt_seq_next,
.stop = bt_seq_stop,
.show = bt_seq_show,
};
static int bt_seq_open(struct inode *inode, struct file *file)
{
struct bt_sock_list *sk_list;
struct bt_seq_state *s;
sk_list = PDE_DATA(inode);
s = __seq_open_private(file, &bt_seq_ops,
sizeof(struct bt_seq_state));
if (!s)
return -ENOMEM;
s->l = sk_list;
return 0;
}
static const struct file_operations bt_fops = {
.open = bt_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private
};
int bt_procfs_init(struct net *net, const char *name,
struct bt_sock_list* sk_list,
int (* seq_show)(struct seq_file *, void *))
{
sk_list->custom_seq_show = seq_show;
if (!proc_create_data(name, 0, net->proc_net, &bt_fops, sk_list))
return -ENOMEM;
return 0;
}
void bt_procfs_cleanup(struct net *net, const char *name)
{
remove_proc_entry(name, net->proc_net);
}
#else
int bt_procfs_init(struct net *net, const char *name,
struct bt_sock_list* sk_list,
int (* seq_show)(struct seq_file *, void *))
{
return 0;
}
void bt_procfs_cleanup(struct net *net, const char *name)
{
}
#endif
EXPORT_SYMBOL(bt_procfs_init);
EXPORT_SYMBOL(bt_procfs_cleanup);
static struct net_proto_family bt_sock_family_ops = {
.owner = THIS_MODULE,
.family = PF_BLUETOOTH,
.create = bt_sock_create,
};
struct dentry *bt_debugfs;
EXPORT_SYMBOL_GPL(bt_debugfs);
static int __init bt_init(void)
{
struct sk_buff *skb;
int err;
BUILD_BUG_ON(sizeof(struct bt_skb_cb) > sizeof(skb->cb));
BT_INFO("Core ver %s", VERSION);
bt_debugfs = debugfs_create_dir("bluetooth", NULL);
err = bt_sysfs_init();
if (err < 0)
return err;
err = sock_register(&bt_sock_family_ops);
if (err < 0) {
bt_sysfs_cleanup();
return err;
}
BT_INFO("HCI device and connection manager initialized");
err = hci_sock_init();
if (err < 0)
goto error;
err = l2cap_init();
if (err < 0)
goto sock_err;
err = sco_init();
if (err < 0) {
l2cap_exit();
goto sock_err;
}
return 0;
sock_err:
hci_sock_cleanup();
error:
sock_unregister(PF_BLUETOOTH);
bt_sysfs_cleanup();
return err;
}
static void __exit bt_exit(void)
{
sco_exit();
l2cap_exit();
hci_sock_cleanup();
sock_unregister(PF_BLUETOOTH);
bt_sysfs_cleanup();
debugfs_remove_recursive(bt_debugfs);
}
subsys_initcall(bt_init);
module_exit(bt_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth Core ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_BLUETOOTH);

467
net/bluetooth/amp.c Normal file
View file

@ -0,0 +1,467 @@
/*
Copyright (c) 2011,2012 Intel Corp.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 and
only version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_core.h>
#include <crypto/hash.h>
#include "a2mp.h"
#include "amp.h"
/* Remote AMP Controllers interface */
void amp_ctrl_get(struct amp_ctrl *ctrl)
{
BT_DBG("ctrl %p orig refcnt %d", ctrl,
atomic_read(&ctrl->kref.refcount));
kref_get(&ctrl->kref);
}
static void amp_ctrl_destroy(struct kref *kref)
{
struct amp_ctrl *ctrl = container_of(kref, struct amp_ctrl, kref);
BT_DBG("ctrl %p", ctrl);
kfree(ctrl->assoc);
kfree(ctrl);
}
int amp_ctrl_put(struct amp_ctrl *ctrl)
{
BT_DBG("ctrl %p orig refcnt %d", ctrl,
atomic_read(&ctrl->kref.refcount));
return kref_put(&ctrl->kref, &amp_ctrl_destroy);
}
struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id)
{
struct amp_ctrl *ctrl;
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return NULL;
kref_init(&ctrl->kref);
ctrl->id = id;
mutex_lock(&mgr->amp_ctrls_lock);
list_add(&ctrl->list, &mgr->amp_ctrls);
mutex_unlock(&mgr->amp_ctrls_lock);
BT_DBG("mgr %p ctrl %p", mgr, ctrl);
return ctrl;
}
void amp_ctrl_list_flush(struct amp_mgr *mgr)
{
struct amp_ctrl *ctrl, *n;
BT_DBG("mgr %p", mgr);
mutex_lock(&mgr->amp_ctrls_lock);
list_for_each_entry_safe(ctrl, n, &mgr->amp_ctrls, list) {
list_del(&ctrl->list);
amp_ctrl_put(ctrl);
}
mutex_unlock(&mgr->amp_ctrls_lock);
}
struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id)
{
struct amp_ctrl *ctrl;
BT_DBG("mgr %p id %d", mgr, id);
mutex_lock(&mgr->amp_ctrls_lock);
list_for_each_entry(ctrl, &mgr->amp_ctrls, list) {
if (ctrl->id == id) {
amp_ctrl_get(ctrl);
mutex_unlock(&mgr->amp_ctrls_lock);
return ctrl;
}
}
mutex_unlock(&mgr->amp_ctrls_lock);
return NULL;
}
/* Physical Link interface */
static u8 __next_handle(struct amp_mgr *mgr)
{
if (++mgr->handle == 0)
mgr->handle = 1;
return mgr->handle;
}
struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
u8 remote_id, bool out)
{
bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst;
struct hci_conn *hcon;
u8 role = out ? HCI_ROLE_MASTER : HCI_ROLE_SLAVE;
hcon = hci_conn_add(hdev, AMP_LINK, dst, role);
if (!hcon)
return NULL;
BT_DBG("hcon %p dst %pMR", hcon, dst);
hcon->state = BT_CONNECT;
hcon->attempt++;
hcon->handle = __next_handle(mgr);
hcon->remote_id = remote_id;
hcon->amp_mgr = amp_mgr_get(mgr);
return hcon;
}
/* AMP crypto key generation interface */
static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
{
struct crypto_shash *tfm;
int ret;
if (!ksize)
return -EINVAL;
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
if (IS_ERR(tfm)) {
BT_DBG("crypto_alloc_ahash failed: err %ld", PTR_ERR(tfm));
return PTR_ERR(tfm);
}
ret = crypto_shash_setkey(tfm, key, ksize);
if (ret) {
BT_DBG("crypto_ahash_setkey failed: err %d", ret);
} else {
char desc[sizeof(struct shash_desc) +
crypto_shash_descsize(tfm)] CRYPTO_MINALIGN_ATTR;
struct shash_desc *shash = (struct shash_desc *)desc;
shash->tfm = tfm;
shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
ret = crypto_shash_digest(shash, plaintext, psize,
output);
}
crypto_free_shash(tfm);
return ret;
}
int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type)
{
struct hci_dev *hdev = conn->hdev;
struct link_key *key;
u8 keybuf[HCI_AMP_LINK_KEY_SIZE];
u8 gamp_key[HCI_AMP_LINK_KEY_SIZE];
int err;
if (!hci_conn_check_link_mode(conn))
return -EACCES;
BT_DBG("conn %p key_type %d", conn, conn->key_type);
/* Legacy key */
if (conn->key_type < 3) {
BT_ERR("Legacy key type %d", conn->key_type);
return -EACCES;
}
*type = conn->key_type;
*len = HCI_AMP_LINK_KEY_SIZE;
key = hci_find_link_key(hdev, &conn->dst);
if (!key) {
BT_DBG("No Link key for conn %p dst %pMR", conn, &conn->dst);
return -EACCES;
}
/* BR/EDR Link Key concatenated together with itself */
memcpy(&keybuf[0], key->val, HCI_LINK_KEY_SIZE);
memcpy(&keybuf[HCI_LINK_KEY_SIZE], key->val, HCI_LINK_KEY_SIZE);
/* Derive Generic AMP Link Key (gamp) */
err = hmac_sha256(keybuf, HCI_AMP_LINK_KEY_SIZE, "gamp", 4, gamp_key);
if (err) {
BT_ERR("Could not derive Generic AMP Key: err %d", err);
return err;
}
if (conn->key_type == HCI_LK_DEBUG_COMBINATION) {
BT_DBG("Use Generic AMP Key (gamp)");
memcpy(data, gamp_key, HCI_AMP_LINK_KEY_SIZE);
return err;
}
/* Derive Dedicated AMP Link Key: "802b" is 802.11 PAL keyID */
return hmac_sha256(gamp_key, HCI_AMP_LINK_KEY_SIZE, "802b", 4, data);
}
void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle)
{
struct hci_cp_read_local_amp_assoc cp;
struct amp_assoc *loc_assoc = &hdev->loc_assoc;
BT_DBG("%s handle %d", hdev->name, phy_handle);
cp.phy_handle = phy_handle;
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
cp.len_so_far = cpu_to_le16(loc_assoc->offset);
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
}
void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
{
struct hci_cp_read_local_amp_assoc cp;
memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc));
memset(&cp, 0, sizeof(cp));
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
set_bit(READ_LOC_AMP_ASSOC, &mgr->state);
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
}
void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
struct hci_conn *hcon)
{
struct hci_cp_read_local_amp_assoc cp;
struct amp_mgr *mgr = hcon->amp_mgr;
cp.phy_handle = hcon->handle;
cp.len_so_far = cpu_to_le16(0);
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
set_bit(READ_LOC_AMP_ASSOC_FINAL, &mgr->state);
/* Read Local AMP Assoc final link information data */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
}
/* Write AMP Assoc data fragments, returns true with last fragment written*/
static bool amp_write_rem_assoc_frag(struct hci_dev *hdev,
struct hci_conn *hcon)
{
struct hci_cp_write_remote_amp_assoc *cp;
struct amp_mgr *mgr = hcon->amp_mgr;
struct amp_ctrl *ctrl;
u16 frag_len, len;
ctrl = amp_ctrl_lookup(mgr, hcon->remote_id);
if (!ctrl)
return false;
if (!ctrl->assoc_rem_len) {
BT_DBG("all fragments are written");
ctrl->assoc_rem_len = ctrl->assoc_len;
ctrl->assoc_len_so_far = 0;
amp_ctrl_put(ctrl);
return true;
}
frag_len = min_t(u16, 248, ctrl->assoc_rem_len);
len = frag_len + sizeof(*cp);
cp = kzalloc(len, GFP_KERNEL);
if (!cp) {
amp_ctrl_put(ctrl);
return false;
}
BT_DBG("hcon %p ctrl %p frag_len %u assoc_len %u rem_len %u",
hcon, ctrl, frag_len, ctrl->assoc_len, ctrl->assoc_rem_len);
cp->phy_handle = hcon->handle;
cp->len_so_far = cpu_to_le16(ctrl->assoc_len_so_far);
cp->rem_len = cpu_to_le16(ctrl->assoc_rem_len);
memcpy(cp->frag, ctrl->assoc, frag_len);
ctrl->assoc_len_so_far += frag_len;
ctrl->assoc_rem_len -= frag_len;
amp_ctrl_put(ctrl);
hci_send_cmd(hdev, HCI_OP_WRITE_REMOTE_AMP_ASSOC, len, cp);
kfree(cp);
return false;
}
void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle)
{
struct hci_conn *hcon;
BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle);
hcon = hci_conn_hash_lookup_handle(hdev, handle);
if (!hcon)
return;
/* Send A2MP create phylink rsp when all fragments are written */
if (amp_write_rem_assoc_frag(hdev, hcon))
a2mp_send_create_phy_link_rsp(hdev, 0);
}
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle)
{
struct hci_conn *hcon;
BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle);
hcon = hci_conn_hash_lookup_handle(hdev, handle);
if (!hcon)
return;
BT_DBG("%s phy handle 0x%2.2x hcon %p", hdev->name, handle, hcon);
amp_write_rem_assoc_frag(hdev, hcon);
}
void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
struct hci_conn *hcon)
{
struct hci_cp_create_phy_link cp;
cp.phy_handle = hcon->handle;
BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon,
hcon->handle);
if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len,
&cp.key_type)) {
BT_DBG("Cannot create link key");
return;
}
hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp);
}
void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
struct hci_conn *hcon)
{
struct hci_cp_accept_phy_link cp;
cp.phy_handle = hcon->handle;
BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon,
hcon->handle);
if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len,
&cp.key_type)) {
BT_DBG("Cannot create link key");
return;
}
hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
}
void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
{
struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
struct amp_mgr *mgr = hs_hcon->amp_mgr;
struct l2cap_chan *bredr_chan;
BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr);
if (!bredr_hdev || !mgr || !mgr->bredr_chan)
return;
bredr_chan = mgr->bredr_chan;
l2cap_chan_lock(bredr_chan);
set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags);
bredr_chan->remote_amp_id = hs_hcon->remote_id;
bredr_chan->local_amp_id = hs_hcon->hdev->id;
bredr_chan->hs_hcon = hs_hcon;
bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu;
__l2cap_physical_cfm(bredr_chan, 0);
l2cap_chan_unlock(bredr_chan);
hci_dev_put(bredr_hdev);
}
void amp_create_logical_link(struct l2cap_chan *chan)
{
struct hci_conn *hs_hcon = chan->hs_hcon;
struct hci_cp_create_accept_logical_link cp;
struct hci_dev *hdev;
BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon,
&chan->conn->hcon->dst);
if (!hs_hcon)
return;
hdev = hci_dev_hold(chan->hs_hcon->hdev);
if (!hdev)
return;
cp.phy_handle = hs_hcon->handle;
cp.tx_flow_spec.id = chan->local_id;
cp.tx_flow_spec.stype = chan->local_stype;
cp.tx_flow_spec.msdu = cpu_to_le16(chan->local_msdu);
cp.tx_flow_spec.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
cp.tx_flow_spec.acc_lat = cpu_to_le32(chan->local_acc_lat);
cp.tx_flow_spec.flush_to = cpu_to_le32(chan->local_flush_to);
cp.rx_flow_spec.id = chan->remote_id;
cp.rx_flow_spec.stype = chan->remote_stype;
cp.rx_flow_spec.msdu = cpu_to_le16(chan->remote_msdu);
cp.rx_flow_spec.sdu_itime = cpu_to_le32(chan->remote_sdu_itime);
cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat);
cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to);
if (hs_hcon->out)
hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
&cp);
else
hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
&cp);
hci_dev_put(hdev);
}
void amp_disconnect_logical_link(struct hci_chan *hchan)
{
struct hci_conn *hcon = hchan->conn;
struct hci_cp_disconn_logical_link cp;
if (hcon->state != BT_CONNECTED) {
BT_DBG("hchan %p not connected", hchan);
return;
}
cp.log_handle = cpu_to_le16(hchan->handle);
hci_send_cmd(hcon->hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp);
}
void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason)
{
BT_DBG("hchan %p", hchan);
hci_chan_del(hchan);
}

54
net/bluetooth/amp.h Normal file
View file

@ -0,0 +1,54 @@
/*
Copyright (c) 2011,2012 Intel Corp.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 and
only version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef __AMP_H
#define __AMP_H
struct amp_ctrl {
struct list_head list;
struct kref kref;
__u8 id;
__u16 assoc_len_so_far;
__u16 assoc_rem_len;
__u16 assoc_len;
__u8 *assoc;
};
int amp_ctrl_put(struct amp_ctrl *ctrl);
void amp_ctrl_get(struct amp_ctrl *ctrl);
struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id);
struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id);
void amp_ctrl_list_flush(struct amp_mgr *mgr);
struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
u8 remote_id, bool out);
int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr);
void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
struct hci_conn *hcon);
void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
struct hci_conn *hcon);
void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
struct hci_conn *hcon);
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
void amp_create_logical_link(struct l2cap_chan *chan);
void amp_disconnect_logical_link(struct hci_chan *hchan);
void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason);
#endif /* __AMP_H */

View file

@ -0,0 +1,24 @@
config BT_BNEP
tristate "BNEP protocol support"
depends on BT
select CRC32
help
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
emulation layer on top of Bluetooth. BNEP is required for
Bluetooth PAN (Personal Area Network).
Say Y here to compile BNEP support into the kernel or say M to
compile it as module (bnep).
config BT_BNEP_MC_FILTER
bool "Multicast filter support"
depends on BT_BNEP
help
This option enables the multicast filter support for BNEP.
config BT_BNEP_PROTO_FILTER
bool "Protocol filter support"
depends on BT_BNEP
help
This option enables the protocol filter support for BNEP.

View file

@ -0,0 +1,7 @@
#
# Makefile for the Linux Bluetooth BNEP layer.
#
obj-$(CONFIG_BT_BNEP) += bnep.o
bnep-objs := core.o sock.o netdev.o

179
net/bluetooth/bnep/bnep.h Normal file
View file

@ -0,0 +1,179 @@
/*
BNEP protocol definition for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
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
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _BNEP_H
#define _BNEP_H
#include <linux/types.h>
#include <linux/crc32.h>
#include <net/bluetooth/bluetooth.h>
/* Limits */
#define BNEP_MAX_PROTO_FILTERS 5
#define BNEP_MAX_MULTICAST_FILTERS 20
/* UUIDs */
#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
#define BNEP_UUID16 0x02
#define BNEP_UUID32 0x04
#define BNEP_UUID128 0x16
#define BNEP_SVC_PANU 0x1115
#define BNEP_SVC_NAP 0x1116
#define BNEP_SVC_GN 0x1117
/* Packet types */
#define BNEP_GENERAL 0x00
#define BNEP_CONTROL 0x01
#define BNEP_COMPRESSED 0x02
#define BNEP_COMPRESSED_SRC_ONLY 0x03
#define BNEP_COMPRESSED_DST_ONLY 0x04
/* Control types */
#define BNEP_CMD_NOT_UNDERSTOOD 0x00
#define BNEP_SETUP_CONN_REQ 0x01
#define BNEP_SETUP_CONN_RSP 0x02
#define BNEP_FILTER_NET_TYPE_SET 0x03
#define BNEP_FILTER_NET_TYPE_RSP 0x04
#define BNEP_FILTER_MULTI_ADDR_SET 0x05
#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
/* Extension types */
#define BNEP_EXT_CONTROL 0x00
/* Response messages */
#define BNEP_SUCCESS 0x00
#define BNEP_CONN_INVALID_DST 0x01
#define BNEP_CONN_INVALID_SRC 0x02
#define BNEP_CONN_INVALID_SVC 0x03
#define BNEP_CONN_NOT_ALLOWED 0x04
#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
#define BNEP_FILTER_INVALID_RANGE 0x02
#define BNEP_FILTER_INVALID_MCADDR 0x02
#define BNEP_FILTER_LIMIT_REACHED 0x03
#define BNEP_FILTER_DENIED_SECURITY 0x04
/* L2CAP settings */
#define BNEP_MTU 1691
#define BNEP_PSM 0x0f
#define BNEP_FLUSH_TO 0xffff
#define BNEP_CONNECT_TO 15
#define BNEP_FILTER_TO 15
/* Headers */
#define BNEP_TYPE_MASK 0x7f
#define BNEP_EXT_HEADER 0x80
struct bnep_setup_conn_req {
__u8 type;
__u8 ctrl;
__u8 uuid_size;
__u8 service[0];
} __packed;
struct bnep_set_filter_req {
__u8 type;
__u8 ctrl;
__be16 len;
__u8 list[0];
} __packed;
struct bnep_control_rsp {
__u8 type;
__u8 ctrl;
__be16 resp;
} __packed;
struct bnep_ext_hdr {
__u8 type;
__u8 len;
__u8 data[0];
} __packed;
/* BNEP ioctl defines */
#define BNEPCONNADD _IOW('B', 200, int)
#define BNEPCONNDEL _IOW('B', 201, int)
#define BNEPGETCONNLIST _IOR('B', 210, int)
#define BNEPGETCONNINFO _IOR('B', 211, int)
struct bnep_connadd_req {
int sock; /* Connected socket */
__u32 flags;
__u16 role;
char device[16]; /* Name of the Ethernet device */
};
struct bnep_conndel_req {
__u32 flags;
__u8 dst[ETH_ALEN];
};
struct bnep_conninfo {
__u32 flags;
__u16 role;
__u16 state;
__u8 dst[ETH_ALEN];
char device[16];
};
struct bnep_connlist_req {
__u32 cnum;
struct bnep_conninfo __user *ci;
};
struct bnep_proto_filter {
__u16 start;
__u16 end;
};
int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock);
int bnep_del_connection(struct bnep_conndel_req *req);
int bnep_get_connlist(struct bnep_connlist_req *req);
int bnep_get_conninfo(struct bnep_conninfo *ci);
/* BNEP sessions */
struct bnep_session {
struct list_head list;
unsigned int role;
unsigned long state;
unsigned long flags;
atomic_t terminate;
struct task_struct *task;
struct ethhdr eh;
struct msghdr msg;
struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS];
unsigned long long mc_filter;
struct socket *sock;
struct net_device *dev;
};
void bnep_net_setup(struct net_device *dev);
int bnep_sock_init(void);
void bnep_sock_cleanup(void);
static inline int bnep_mc_hash(__u8 *addr)
{
return crc32_be(~0, addr, ETH_ALEN) >> 26;
}
#endif

723
net/bluetooth/bnep/core.c Normal file
View file

@ -0,0 +1,723 @@
/*
BNEP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2001-2002 Inventel Systemes
Written 2001-2002 by
Clément Moreau <clement.moreau@inventel.fr>
David Libault <david.libault@inventel.fr>
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/file.h>
#include <linux/etherdevice.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/hci_core.h>
#include "bnep.h"
#define VERSION "1.3"
static bool compress_src = true;
static bool compress_dst = true;
static LIST_HEAD(bnep_session_list);
static DECLARE_RWSEM(bnep_session_sem);
static struct bnep_session *__bnep_get_session(u8 *dst)
{
struct bnep_session *s;
BT_DBG("");
list_for_each_entry(s, &bnep_session_list, list)
if (ether_addr_equal(dst, s->eh.h_source))
return s;
return NULL;
}
static void __bnep_link_session(struct bnep_session *s)
{
list_add(&s->list, &bnep_session_list);
}
static void __bnep_unlink_session(struct bnep_session *s)
{
list_del(&s->list);
}
static int bnep_send(struct bnep_session *s, void *data, size_t len)
{
struct socket *sock = s->sock;
struct kvec iv = { data, len };
return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
}
static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
{
struct bnep_control_rsp rsp;
rsp.type = BNEP_CONTROL;
rsp.ctrl = ctrl;
rsp.resp = htons(resp);
return bnep_send(s, &rsp, sizeof(rsp));
}
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
static inline void bnep_set_default_proto_filter(struct bnep_session *s)
{
/* (IPv4, ARP) */
s->proto_filter[0].start = ETH_P_IP;
s->proto_filter[0].end = ETH_P_ARP;
/* (RARP, AppleTalk) */
s->proto_filter[1].start = ETH_P_RARP;
s->proto_filter[1].end = ETH_P_AARP;
/* (IPX, IPv6) */
s->proto_filter[2].start = ETH_P_IPX;
s->proto_filter[2].end = ETH_P_IPV6;
}
#endif
static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
{
int n;
if (len < 2)
return -EILSEQ;
n = get_unaligned_be16(data);
data++;
len -= 2;
if (len < n)
return -EILSEQ;
BT_DBG("filter len %d", n);
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
n /= 4;
if (n <= BNEP_MAX_PROTO_FILTERS) {
struct bnep_proto_filter *f = s->proto_filter;
int i;
for (i = 0; i < n; i++) {
f[i].start = get_unaligned_be16(data++);
f[i].end = get_unaligned_be16(data++);
BT_DBG("proto filter start %d end %d",
f[i].start, f[i].end);
}
if (i < BNEP_MAX_PROTO_FILTERS)
memset(f + i, 0, sizeof(*f));
if (n == 0)
bnep_set_default_proto_filter(s);
bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
} else {
bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
}
#else
bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
#endif
return 0;
}
static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
{
int n;
if (len < 2)
return -EILSEQ;
n = get_unaligned_be16(data);
data += 2;
len -= 2;
if (len < n)
return -EILSEQ;
BT_DBG("filter len %d", n);
#ifdef CONFIG_BT_BNEP_MC_FILTER
n /= (ETH_ALEN * 2);
if (n > 0) {
int i;
s->mc_filter = 0;
/* Always send broadcast */
set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter);
/* Add address ranges to the multicast hash */
for (; n > 0; n--) {
u8 a1[6], *a2;
memcpy(a1, data, ETH_ALEN);
data += ETH_ALEN;
a2 = data;
data += ETH_ALEN;
BT_DBG("mc filter %pMR -> %pMR", a1, a2);
/* Iterate from a1 to a2 */
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
/* Increment a1 */
i = 5;
while (i >= 0 && ++a1[i--] == 0)
;
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
}
}
}
BT_DBG("mc filter hash 0x%llx", s->mc_filter);
bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
#else
bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
#endif
return 0;
}
static int bnep_rx_control(struct bnep_session *s, void *data, int len)
{
u8 cmd = *(u8 *)data;
int err = 0;
data++;
len--;
switch (cmd) {
case BNEP_CMD_NOT_UNDERSTOOD:
case BNEP_SETUP_CONN_RSP:
case BNEP_FILTER_NET_TYPE_RSP:
case BNEP_FILTER_MULTI_ADDR_RSP:
/* Ignore these for now */
break;
case BNEP_FILTER_NET_TYPE_SET:
err = bnep_ctrl_set_netfilter(s, data, len);
break;
case BNEP_FILTER_MULTI_ADDR_SET:
err = bnep_ctrl_set_mcfilter(s, data, len);
break;
case BNEP_SETUP_CONN_REQ:
err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
break;
default: {
u8 pkt[3];
pkt[0] = BNEP_CONTROL;
pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
pkt[2] = cmd;
bnep_send(s, pkt, sizeof(pkt));
}
break;
}
return err;
}
static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
{
struct bnep_ext_hdr *h;
int err = 0;
do {
h = (void *) skb->data;
if (!skb_pull(skb, sizeof(*h))) {
err = -EILSEQ;
break;
}
BT_DBG("type 0x%x len %d", h->type, h->len);
switch (h->type & BNEP_TYPE_MASK) {
case BNEP_EXT_CONTROL:
bnep_rx_control(s, skb->data, skb->len);
break;
default:
/* Unknown extension, skip it. */
break;
}
if (!skb_pull(skb, h->len)) {
err = -EILSEQ;
break;
}
} while (!err && (h->type & BNEP_EXT_HEADER));
return err;
}
static u8 __bnep_rx_hlen[] = {
ETH_HLEN, /* BNEP_GENERAL */
0, /* BNEP_CONTROL */
2, /* BNEP_COMPRESSED */
ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
};
static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
{
struct net_device *dev = s->dev;
struct sk_buff *nskb;
u8 type;
dev->stats.rx_bytes += skb->len;
type = *(u8 *) skb->data;
skb_pull(skb, 1);
if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
goto badframe;
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
bnep_rx_control(s, skb->data, skb->len);
kfree_skb(skb);
return 0;
}
skb_reset_mac_header(skb);
/* Verify and pull out header */
if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
goto badframe;
s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
if (type & BNEP_EXT_HEADER) {
if (bnep_rx_extension(s, skb) < 0)
goto badframe;
}
/* Strip 802.1p header */
if (ntohs(s->eh.h_proto) == ETH_P_8021Q) {
if (!skb_pull(skb, 4))
goto badframe;
s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
}
/* We have to alloc new skb and copy data here :(. Because original skb
* may not be modified and because of the alignment requirements. */
nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
if (!nskb) {
dev->stats.rx_dropped++;
kfree_skb(skb);
return -ENOMEM;
}
skb_reserve(nskb, 2);
/* Decompress header and construct ether frame */
switch (type & BNEP_TYPE_MASK) {
case BNEP_COMPRESSED:
memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
break;
case BNEP_COMPRESSED_SRC_ONLY:
memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN);
put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
break;
case BNEP_COMPRESSED_DST_ONLY:
memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
ETH_ALEN);
memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
ETH_ALEN + 2);
break;
case BNEP_GENERAL:
memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
ETH_ALEN * 2);
put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
break;
}
skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
kfree_skb(skb);
dev->stats.rx_packets++;
nskb->ip_summed = CHECKSUM_NONE;
nskb->protocol = eth_type_trans(nskb, dev);
netif_rx_ni(nskb);
return 0;
badframe:
dev->stats.rx_errors++;
kfree_skb(skb);
return 0;
}
static u8 __bnep_tx_types[] = {
BNEP_GENERAL,
BNEP_COMPRESSED_SRC_ONLY,
BNEP_COMPRESSED_DST_ONLY,
BNEP_COMPRESSED
};
static int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
{
struct ethhdr *eh = (void *) skb->data;
struct socket *sock = s->sock;
struct kvec iv[3];
int len = 0, il = 0;
u8 type = 0;
BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
if (!skb->dev) {
/* Control frame sent by us */
goto send;
}
iv[il++] = (struct kvec) { &type, 1 };
len++;
if (compress_src && ether_addr_equal(eh->h_dest, s->eh.h_source))
type |= 0x01;
if (compress_dst && ether_addr_equal(eh->h_source, s->eh.h_dest))
type |= 0x02;
if (type)
skb_pull(skb, ETH_ALEN * 2);
type = __bnep_tx_types[type];
switch (type) {
case BNEP_COMPRESSED_SRC_ONLY:
iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
len += ETH_ALEN;
break;
case BNEP_COMPRESSED_DST_ONLY:
iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
len += ETH_ALEN;
break;
}
send:
iv[il++] = (struct kvec) { skb->data, skb->len };
len += skb->len;
/* FIXME: linearize skb */
{
len = kernel_sendmsg(sock, &s->msg, iv, il, len);
}
kfree_skb(skb);
if (len > 0) {
s->dev->stats.tx_bytes += len;
s->dev->stats.tx_packets++;
return 0;
}
return len;
}
static int bnep_session(void *arg)
{
struct bnep_session *s = arg;
struct net_device *dev = s->dev;
struct sock *sk = s->sock->sk;
struct sk_buff *skb;
wait_queue_t wait;
BT_DBG("");
set_user_nice(current, -15);
init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (atomic_read(&s->terminate))
break;
/* RX */
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
skb_orphan(skb);
if (!skb_linearize(skb))
bnep_rx_frame(s, skb);
else
kfree_skb(skb);
}
if (sk->sk_state != BT_CONNECTED)
break;
/* TX */
while ((skb = skb_dequeue(&sk->sk_write_queue)))
if (bnep_tx_frame(s, skb))
break;
netif_wake_queue(dev);
schedule();
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
/* Cleanup session */
down_write(&bnep_session_sem);
/* Delete network device */
unregister_netdev(dev);
/* Wakeup user-space polling for socket errors */
s->sock->sk->sk_err = EUNATCH;
wake_up_interruptible(sk_sleep(s->sock->sk));
/* Release the socket */
fput(s->sock->file);
__bnep_unlink_session(s);
up_write(&bnep_session_sem);
free_netdev(dev);
module_put_and_exit(0);
return 0;
}
static struct device *bnep_get_device(struct bnep_session *session)
{
struct hci_conn *conn;
conn = l2cap_pi(session->sock->sk)->chan->conn->hcon;
if (!conn)
return NULL;
return &conn->dev;
}
static struct device_type bnep_type = {
.name = "bluetooth",
};
int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
{
struct net_device *dev;
struct bnep_session *s, *ss;
u8 dst[ETH_ALEN], src[ETH_ALEN];
int err;
BT_DBG("");
baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
/* session struct allocated as private part of net_device */
dev = alloc_netdev(sizeof(struct bnep_session),
(*req->device) ? req->device : "bnep%d",
NET_NAME_UNKNOWN,
bnep_net_setup);
if (!dev)
return -ENOMEM;
down_write(&bnep_session_sem);
ss = __bnep_get_session(dst);
if (ss && ss->state == BT_CONNECTED) {
err = -EEXIST;
goto failed;
}
s = netdev_priv(dev);
/* This is rx header therefore addresses are swapped.
* ie. eh.h_dest is our local address. */
memcpy(s->eh.h_dest, &src, ETH_ALEN);
memcpy(s->eh.h_source, &dst, ETH_ALEN);
memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
s->dev = dev;
s->sock = sock;
s->role = req->role;
s->state = BT_CONNECTED;
s->msg.msg_flags = MSG_NOSIGNAL;
#ifdef CONFIG_BT_BNEP_MC_FILTER
/* Set default mc filter */
set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
#endif
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
/* Set default protocol filter */
bnep_set_default_proto_filter(s);
#endif
SET_NETDEV_DEV(dev, bnep_get_device(s));
SET_NETDEV_DEVTYPE(dev, &bnep_type);
err = register_netdev(dev);
if (err)
goto failed;
__bnep_link_session(s);
__module_get(THIS_MODULE);
s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
if (IS_ERR(s->task)) {
/* Session thread start failed, gotta cleanup. */
module_put(THIS_MODULE);
unregister_netdev(dev);
__bnep_unlink_session(s);
err = PTR_ERR(s->task);
goto failed;
}
up_write(&bnep_session_sem);
strcpy(req->device, dev->name);
return 0;
failed:
up_write(&bnep_session_sem);
free_netdev(dev);
return err;
}
int bnep_del_connection(struct bnep_conndel_req *req)
{
struct bnep_session *s;
int err = 0;
BT_DBG("");
down_read(&bnep_session_sem);
s = __bnep_get_session(req->dst);
if (s) {
atomic_inc(&s->terminate);
wake_up_process(s->task);
} else
err = -ENOENT;
up_read(&bnep_session_sem);
return err;
}
static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
{
memset(ci, 0, sizeof(*ci));
memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
strcpy(ci->device, s->dev->name);
ci->flags = s->flags;
ci->state = s->state;
ci->role = s->role;
}
int bnep_get_connlist(struct bnep_connlist_req *req)
{
struct bnep_session *s;
int err = 0, n = 0;
down_read(&bnep_session_sem);
list_for_each_entry(s, &bnep_session_list, list) {
struct bnep_conninfo ci;
__bnep_copy_ci(&ci, s);
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
err = -EFAULT;
break;
}
if (++n >= req->cnum)
break;
req->ci++;
}
req->cnum = n;
up_read(&bnep_session_sem);
return err;
}
int bnep_get_conninfo(struct bnep_conninfo *ci)
{
struct bnep_session *s;
int err = 0;
down_read(&bnep_session_sem);
s = __bnep_get_session(ci->dst);
if (s)
__bnep_copy_ci(ci, s);
else
err = -ENOENT;
up_read(&bnep_session_sem);
return err;
}
static int __init bnep_init(void)
{
char flt[50] = "";
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
strcat(flt, "protocol ");
#endif
#ifdef CONFIG_BT_BNEP_MC_FILTER
strcat(flt, "multicast");
#endif
BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
if (flt[0])
BT_INFO("BNEP filters: %s", flt);
bnep_sock_init();
return 0;
}
static void __exit bnep_exit(void)
{
bnep_sock_cleanup();
}
module_init(bnep_init);
module_exit(bnep_exit);
module_param(compress_src, bool, 0644);
MODULE_PARM_DESC(compress_src, "Compress sources headers");
module_param(compress_dst, bool, 0644);
MODULE_PARM_DESC(compress_dst, "Compress destination headers");
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("bt-proto-4");

229
net/bluetooth/bnep/netdev.c Normal file
View file

@ -0,0 +1,229 @@
/*
BNEP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2001-2002 Inventel Systemes
Written 2001-2002 by
Clément Moreau <clement.moreau@inventel.fr>
David Libault <david.libault@inventel.fr>
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/etherdevice.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include "bnep.h"
#define BNEP_TX_QUEUE_LEN 20
static int bnep_net_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
static int bnep_net_close(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
static void bnep_net_set_mc_list(struct net_device *dev)
{
#ifdef CONFIG_BT_BNEP_MC_FILTER
struct bnep_session *s = netdev_priv(dev);
struct sock *sk = s->sock->sk;
struct bnep_set_filter_req *r;
struct sk_buff *skb;
int size;
BT_DBG("%s mc_count %d", dev->name, netdev_mc_count(dev));
size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2;
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {
BT_ERR("%s Multicast list allocation failed", dev->name);
return;
}
r = (void *) skb->data;
__skb_put(skb, sizeof(*r));
r->type = BNEP_CONTROL;
r->ctrl = BNEP_FILTER_MULTI_ADDR_SET;
if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
u8 start[ETH_ALEN] = { 0x01 };
/* Request all addresses */
memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN);
memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
r->len = htons(ETH_ALEN * 2);
} else {
struct netdev_hw_addr *ha;
int i, len = skb->len;
if (dev->flags & IFF_BROADCAST) {
memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
}
/* FIXME: We should group addresses here. */
i = 0;
netdev_for_each_mc_addr(ha, dev) {
if (i == BNEP_MAX_MULTICAST_FILTERS)
break;
memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN);
memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN);
i++;
}
r->len = htons(skb->len - len);
}
skb_queue_tail(&sk->sk_write_queue, skb);
wake_up_interruptible(sk_sleep(sk));
#endif
}
static int bnep_net_set_mac_addr(struct net_device *dev, void *arg)
{
BT_DBG("%s", dev->name);
return 0;
}
static void bnep_net_timeout(struct net_device *dev)
{
BT_DBG("net_timeout");
netif_wake_queue(dev);
}
#ifdef CONFIG_BT_BNEP_MC_FILTER
static int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s)
{
struct ethhdr *eh = (void *) skb->data;
if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), (ulong *) &s->mc_filter))
return 1;
return 0;
}
#endif
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
/* Determine ether protocol. Based on eth_type_trans. */
static u16 bnep_net_eth_proto(struct sk_buff *skb)
{
struct ethhdr *eh = (void *) skb->data;
u16 proto = ntohs(eh->h_proto);
if (proto >= ETH_P_802_3_MIN)
return proto;
if (get_unaligned((__be16 *) skb->data) == htons(0xFFFF))
return ETH_P_802_3;
return ETH_P_802_2;
}
static int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s)
{
u16 proto = bnep_net_eth_proto(skb);
struct bnep_proto_filter *f = s->proto_filter;
int i;
for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) {
if (proto >= f[i].start && proto <= f[i].end)
return 0;
}
BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto);
return 1;
}
#endif
static netdev_tx_t bnep_net_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct bnep_session *s = netdev_priv(dev);
struct sock *sk = s->sock->sk;
BT_DBG("skb %p, dev %p", skb, dev);
#ifdef CONFIG_BT_BNEP_MC_FILTER
if (bnep_net_mc_filter(skb, s)) {
kfree_skb(skb);
return NETDEV_TX_OK;
}
#endif
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
if (bnep_net_proto_filter(skb, s)) {
kfree_skb(skb);
return NETDEV_TX_OK;
}
#endif
/*
* We cannot send L2CAP packets from here as we are potentially in a bh.
* So we have to queue them and wake up session thread which is sleeping
* on the sk_sleep(sk).
*/
dev->trans_start = jiffies;
skb_queue_tail(&sk->sk_write_queue, skb);
wake_up_interruptible(sk_sleep(sk));
if (skb_queue_len(&sk->sk_write_queue) >= BNEP_TX_QUEUE_LEN) {
BT_DBG("tx queue is full");
/* Stop queuing.
* Session thread will do netif_wake_queue() */
netif_stop_queue(dev);
}
return NETDEV_TX_OK;
}
static const struct net_device_ops bnep_netdev_ops = {
.ndo_open = bnep_net_open,
.ndo_stop = bnep_net_close,
.ndo_start_xmit = bnep_net_xmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_rx_mode = bnep_net_set_mc_list,
.ndo_set_mac_address = bnep_net_set_mac_addr,
.ndo_tx_timeout = bnep_net_timeout,
.ndo_change_mtu = eth_change_mtu,
};
void bnep_net_setup(struct net_device *dev)
{
memset(dev->broadcast, 0xff, ETH_ALEN);
dev->addr_len = ETH_ALEN;
ether_setup(dev);
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
dev->netdev_ops = &bnep_netdev_ops;
dev->watchdog_timeo = HZ * 2;
}

258
net/bluetooth/bnep/sock.c Normal file
View file

@ -0,0 +1,258 @@
/*
BNEP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2001-2002 Inventel Systemes
Written 2001-2002 by
David Libault <david.libault@inventel.fr>
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/export.h>
#include <linux/file.h>
#include "bnep.h"
static struct bt_sock_list bnep_sk_list = {
.lock = __RW_LOCK_UNLOCKED(bnep_sk_list.lock)
};
static int bnep_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p sk %p", sock, sk);
if (!sk)
return 0;
bt_sock_unlink(&bnep_sk_list, sk);
sock_orphan(sk);
sock_put(sk);
return 0;
}
static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct bnep_connlist_req cl;
struct bnep_connadd_req ca;
struct bnep_conndel_req cd;
struct bnep_conninfo ci;
struct socket *nsock;
void __user *argp = (void __user *)arg;
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
switch (cmd) {
case BNEPCONNADD:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
nsock = sockfd_lookup(ca.sock, &err);
if (!nsock)
return err;
if (nsock->sk->sk_state != BT_CONNECTED) {
sockfd_put(nsock);
return -EBADFD;
}
ca.device[sizeof(ca.device)-1] = 0;
err = bnep_add_connection(&ca, nsock);
if (!err) {
if (copy_to_user(argp, &ca, sizeof(ca)))
err = -EFAULT;
} else
sockfd_put(nsock);
return err;
case BNEPCONNDEL:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
return bnep_del_connection(&cd);
case BNEPGETCONNLIST:
if (copy_from_user(&cl, argp, sizeof(cl)))
return -EFAULT;
if (cl.cnum <= 0)
return -EINVAL;
err = bnep_get_connlist(&cl);
if (!err && copy_to_user(argp, &cl, sizeof(cl)))
return -EFAULT;
return err;
case BNEPGETCONNINFO:
if (copy_from_user(&ci, argp, sizeof(ci)))
return -EFAULT;
err = bnep_get_conninfo(&ci);
if (!err && copy_to_user(argp, &ci, sizeof(ci)))
return -EFAULT;
return err;
default:
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_COMPAT
static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
if (cmd == BNEPGETCONNLIST) {
struct bnep_connlist_req cl;
u32 uci;
int err;
if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
cl.ci = compat_ptr(uci);
if (cl.cnum <= 0)
return -EINVAL;
err = bnep_get_connlist(&cl);
if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
}
return bnep_sock_ioctl(sock, cmd, arg);
}
#endif
static const struct proto_ops bnep_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = bnep_sock_release,
.ioctl = bnep_sock_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = bnep_sock_compat_ioctl,
#endif
.bind = sock_no_bind,
.getname = sock_no_getname,
.sendmsg = sock_no_sendmsg,
.recvmsg = sock_no_recvmsg,
.poll = sock_no_poll,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.getsockopt = sock_no_getsockopt,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
};
static struct proto bnep_proto = {
.name = "BNEP",
.owner = THIS_MODULE,
.obj_size = sizeof(struct bt_sock)
};
static int bnep_sock_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
BT_DBG("sock %p", sock);
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->ops = &bnep_sock_ops;
sock->state = SS_UNCONNECTED;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sk->sk_state = BT_OPEN;
bt_sock_link(&bnep_sk_list, sk);
return 0;
}
static const struct net_proto_family bnep_sock_family_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.create = bnep_sock_create
};
int __init bnep_sock_init(void)
{
int err;
err = proto_register(&bnep_proto, 0);
if (err < 0)
return err;
err = bt_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops);
if (err < 0) {
BT_ERR("Can't register BNEP socket");
goto error;
}
err = bt_procfs_init(&init_net, "bnep", &bnep_sk_list, NULL);
if (err < 0) {
BT_ERR("Failed to create BNEP proc file");
bt_sock_unregister(BTPROTO_BNEP);
goto error;
}
BT_INFO("BNEP socket layer initialized");
return 0;
error:
proto_unregister(&bnep_proto);
return err;
}
void __exit bnep_sock_cleanup(void)
{
bt_procfs_cleanup(&init_net, "bnep");
bt_sock_unregister(BTPROTO_BNEP);
proto_unregister(&bnep_proto);
}

View file

@ -0,0 +1,11 @@
config BT_CMTP
tristate "CMTP protocol support"
depends on BT && ISDN_CAPI
help
CMTP (CAPI Message Transport Protocol) is a transport layer
for CAPI messages. CMTP is required for the Bluetooth Common
ISDN Access Profile.
Say Y here to compile CMTP support into the kernel or say M to
compile it as module (cmtp).

View file

@ -0,0 +1,7 @@
#
# Makefile for the Linux Bluetooth CMTP layer
#
obj-$(CONFIG_BT_CMTP) += cmtp.o
cmtp-objs := core.o sock.o capi.o

618
net/bluetooth/cmtp/capi.c Normal file
View file

@ -0,0 +1,618 @@
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/export.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/wait.h>
#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include <linux/isdn/capicmd.h>
#include <linux/isdn/capiutil.h>
#include "cmtp.h"
#define CAPI_INTEROPERABILITY 0x20
#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
#define CAPI_FUNCTION_REGISTER 0
#define CAPI_FUNCTION_RELEASE 1
#define CAPI_FUNCTION_GET_PROFILE 2
#define CAPI_FUNCTION_GET_MANUFACTURER 3
#define CAPI_FUNCTION_GET_VERSION 4
#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
#define CAPI_FUNCTION_MANUFACTURER 6
#define CAPI_FUNCTION_LOOPBACK 7
#define CMTP_MSGNUM 1
#define CMTP_APPLID 2
#define CMTP_MAPPING 3
static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
{
struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
BT_DBG("session %p application %p appl %d", session, app, appl);
if (!app)
return NULL;
app->state = BT_OPEN;
app->appl = appl;
list_add_tail(&app->list, &session->applications);
return app;
}
static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
{
BT_DBG("session %p application %p", session, app);
if (app) {
list_del(&app->list);
kfree(app);
}
}
static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
{
struct cmtp_application *app;
struct list_head *p, *n;
list_for_each_safe(p, n, &session->applications) {
app = list_entry(p, struct cmtp_application, list);
switch (pattern) {
case CMTP_MSGNUM:
if (app->msgnum == value)
return app;
break;
case CMTP_APPLID:
if (app->appl == value)
return app;
break;
case CMTP_MAPPING:
if (app->mapping == value)
return app;
break;
}
}
return NULL;
}
static int cmtp_msgnum_get(struct cmtp_session *session)
{
session->msgnum++;
if ((session->msgnum & 0xff) > 200)
session->msgnum = CMTP_INITIAL_MSGNUM + 1;
return session->msgnum;
}
static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct cmtp_scb *scb = (void *) skb->cb;
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
scb->id = -1;
scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
skb_queue_tail(&session->transmit, skb);
wake_up_interruptible(sk_sleep(session->sock->sk));
}
static void cmtp_send_interopmsg(struct cmtp_session *session,
__u8 subcmd, __u16 appl, __u16 msgnum,
__u16 function, unsigned char *buf, int len)
{
struct sk_buff *skb;
unsigned char *s;
BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
if (!skb) {
BT_ERR("Can't allocate memory for interoperability packet");
return;
}
s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
capimsg_setu16(s, 2, appl);
capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
capimsg_setu8 (s, 5, subcmd);
capimsg_setu16(s, 6, msgnum);
/* Interoperability selector (Bluetooth Device Management) */
capimsg_setu16(s, 8, 0x0001);
capimsg_setu8 (s, 10, 3 + len);
capimsg_setu16(s, 11, function);
capimsg_setu8 (s, 13, len);
if (len > 0)
memcpy(s + 14, buf, len);
cmtp_send_capimsg(session, skb);
}
static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct capi_ctr *ctrl = &session->ctrl;
struct cmtp_application *application;
__u16 appl, msgnum, func, info;
__u32 controller;
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
switch (CAPIMSG_SUBCOMMAND(skb->data)) {
case CAPI_CONF:
if (skb->len < CAPI_MSG_BASELEN + 10)
break;
func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
switch (func) {
case CAPI_FUNCTION_REGISTER:
msgnum = CAPIMSG_MSGID(skb->data);
application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
if (application) {
application->state = BT_CONNECTED;
application->msgnum = 0;
application->mapping = CAPIMSG_APPID(skb->data);
wake_up_interruptible(&session->wait);
}
break;
case CAPI_FUNCTION_RELEASE:
appl = CAPIMSG_APPID(skb->data);
application = cmtp_application_get(session, CMTP_MAPPING, appl);
if (application) {
application->state = BT_CLOSED;
application->msgnum = 0;
wake_up_interruptible(&session->wait);
}
break;
case CAPI_FUNCTION_GET_PROFILE:
if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
break;
controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
msgnum = CAPIMSG_MSGID(skb->data);
if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
session->ncontroller = controller;
wake_up_interruptible(&session->wait);
break;
}
if (!info && ctrl) {
memcpy(&ctrl->profile,
skb->data + CAPI_MSG_BASELEN + 11,
sizeof(capi_profile));
session->state = BT_CONNECTED;
capi_ctr_ready(ctrl);
}
break;
case CAPI_FUNCTION_GET_MANUFACTURER:
if (skb->len < CAPI_MSG_BASELEN + 15)
break;
controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
if (!info && ctrl) {
int len = min_t(uint, CAPI_MANUFACTURER_LEN,
skb->data[CAPI_MSG_BASELEN + 14]);
memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN);
strncpy(ctrl->manu,
skb->data + CAPI_MSG_BASELEN + 15, len);
}
break;
case CAPI_FUNCTION_GET_VERSION:
if (skb->len < CAPI_MSG_BASELEN + 32)
break;
controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
if (!info && ctrl) {
ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
}
break;
case CAPI_FUNCTION_GET_SERIAL_NUMBER:
if (skb->len < CAPI_MSG_BASELEN + 17)
break;
controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
if (!info && ctrl) {
int len = min_t(uint, CAPI_SERIAL_LEN,
skb->data[CAPI_MSG_BASELEN + 16]);
memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
strncpy(ctrl->serial,
skb->data + CAPI_MSG_BASELEN + 17, len);
}
break;
}
break;
case CAPI_IND:
if (skb->len < CAPI_MSG_BASELEN + 6)
break;
func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
if (func == CAPI_FUNCTION_LOOPBACK) {
int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
skb->data[CAPI_MSG_BASELEN + 5]);
appl = CAPIMSG_APPID(skb->data);
msgnum = CAPIMSG_MSGID(skb->data);
cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
skb->data + CAPI_MSG_BASELEN + 6, len);
}
break;
}
kfree_skb(skb);
}
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct capi_ctr *ctrl = &session->ctrl;
struct cmtp_application *application;
__u16 appl;
__u32 contr;
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
if (skb->len < CAPI_MSG_BASELEN)
return;
if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
cmtp_recv_interopmsg(session, skb);
return;
}
if (session->flags & (1 << CMTP_LOOPBACK)) {
kfree_skb(skb);
return;
}
appl = CAPIMSG_APPID(skb->data);
contr = CAPIMSG_CONTROL(skb->data);
application = cmtp_application_get(session, CMTP_MAPPING, appl);
if (application) {
appl = application->appl;
CAPIMSG_SETAPPID(skb->data, appl);
} else {
BT_ERR("Can't find application with id %d", appl);
kfree_skb(skb);
return;
}
if ((contr & 0x7f) == 0x01) {
contr = (contr & 0xffffff80) | session->num;
CAPIMSG_SETCONTROL(skb->data, contr);
}
capi_ctr_handle_message(ctrl, appl, skb);
}
static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
{
BT_DBG("ctrl %p data %p", ctrl, data);
return 0;
}
static void cmtp_reset_ctr(struct capi_ctr *ctrl)
{
struct cmtp_session *session = ctrl->driverdata;
BT_DBG("ctrl %p", ctrl);
capi_ctr_down(ctrl);
atomic_inc(&session->terminate);
wake_up_process(session->task);
}
static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
{
DECLARE_WAITQUEUE(wait, current);
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
unsigned long timeo = CMTP_INTEROP_TIMEOUT;
unsigned char buf[8];
int err = 0, nconn, want = rp->level3cnt;
BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
application = cmtp_application_add(session, appl);
if (!application) {
BT_ERR("Can't allocate memory for new application");
return;
}
if (want < 0)
nconn = ctrl->profile.nbchannel * -want;
else
nconn = want;
if (nconn == 0)
nconn = ctrl->profile.nbchannel;
capimsg_setu16(buf, 0, nconn);
capimsg_setu16(buf, 2, rp->datablkcnt);
capimsg_setu16(buf, 4, rp->datablklen);
application->state = BT_CONFIG;
application->msgnum = cmtp_msgnum_get(session);
cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
CAPI_FUNCTION_REGISTER, buf, 6);
add_wait_queue(&session->wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (!timeo) {
err = -EAGAIN;
break;
}
if (application->state == BT_CLOSED) {
err = -application->err;
break;
}
if (application->state == BT_CONNECTED)
break;
if (signal_pending(current)) {
err = -EINTR;
break;
}
timeo = schedule_timeout(timeo);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&session->wait, &wait);
if (err) {
cmtp_application_del(session, application);
return;
}
}
static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
{
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
BT_DBG("ctrl %p appl %d", ctrl, appl);
application = cmtp_application_get(session, CMTP_APPLID, appl);
if (!application) {
BT_ERR("Can't find application");
return;
}
application->msgnum = cmtp_msgnum_get(session);
cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
CAPI_FUNCTION_RELEASE, NULL, 0);
wait_event_interruptible_timeout(session->wait,
(application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
cmtp_application_del(session, application);
}
static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
{
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
__u16 appl;
__u32 contr;
BT_DBG("ctrl %p skb %p", ctrl, skb);
appl = CAPIMSG_APPID(skb->data);
contr = CAPIMSG_CONTROL(skb->data);
application = cmtp_application_get(session, CMTP_APPLID, appl);
if ((!application) || (application->state != BT_CONNECTED)) {
BT_ERR("Can't find application with id %d", appl);
return CAPI_ILLAPPNR;
}
CAPIMSG_SETAPPID(skb->data, application->mapping);
if ((contr & 0x7f) == session->num) {
contr = (contr & 0xffffff80) | 0x01;
CAPIMSG_SETCONTROL(skb->data, contr);
}
cmtp_send_capimsg(session, skb);
return CAPI_NOERROR;
}
static char *cmtp_procinfo(struct capi_ctr *ctrl)
{
return "CAPI Message Transport Protocol";
}
static int cmtp_proc_show(struct seq_file *m, void *v)
{
struct capi_ctr *ctrl = m->private;
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *app;
struct list_head *p, *n;
seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
seq_printf(m, "addr %s\n", session->name);
seq_printf(m, "ctrl %d\n", session->num);
list_for_each_safe(p, n, &session->applications) {
app = list_entry(p, struct cmtp_application, list);
seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
}
return 0;
}
static int cmtp_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, cmtp_proc_show, PDE_DATA(inode));
}
static const struct file_operations cmtp_proc_fops = {
.owner = THIS_MODULE,
.open = cmtp_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
int cmtp_attach_device(struct cmtp_session *session)
{
unsigned char buf[4];
long ret;
BT_DBG("session %p", session);
capimsg_setu32(buf, 0, 0);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
CAPI_FUNCTION_GET_PROFILE, buf, 4);
ret = wait_event_interruptible_timeout(session->wait,
session->ncontroller, CMTP_INTEROP_TIMEOUT);
BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
if (!ret)
return -ETIMEDOUT;
if (!session->ncontroller)
return -ENODEV;
if (session->ncontroller > 1)
BT_INFO("Setting up only CAPI controller 1");
session->ctrl.owner = THIS_MODULE;
session->ctrl.driverdata = session;
strcpy(session->ctrl.name, session->name);
session->ctrl.driver_name = "cmtp";
session->ctrl.load_firmware = cmtp_load_firmware;
session->ctrl.reset_ctr = cmtp_reset_ctr;
session->ctrl.register_appl = cmtp_register_appl;
session->ctrl.release_appl = cmtp_release_appl;
session->ctrl.send_message = cmtp_send_message;
session->ctrl.procinfo = cmtp_procinfo;
session->ctrl.proc_fops = &cmtp_proc_fops;
if (attach_capi_ctr(&session->ctrl) < 0) {
BT_ERR("Can't attach new controller");
return -EBUSY;
}
session->num = session->ctrl.cnr;
BT_DBG("session %p num %d", session, session->num);
capimsg_setu32(buf, 0, 1);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_VERSION, buf, 4);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_PROFILE, buf, 4);
return 0;
}
void cmtp_detach_device(struct cmtp_session *session)
{
BT_DBG("session %p", session);
detach_capi_ctr(&session->ctrl);
}

129
net/bluetooth/cmtp/cmtp.h Normal file
View file

@ -0,0 +1,129 @@
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#ifndef __CMTP_H
#define __CMTP_H
#include <linux/types.h>
#include <net/bluetooth/bluetooth.h>
#define BTNAMSIZ 18
/* CMTP ioctl defines */
#define CMTPCONNADD _IOW('C', 200, int)
#define CMTPCONNDEL _IOW('C', 201, int)
#define CMTPGETCONNLIST _IOR('C', 210, int)
#define CMTPGETCONNINFO _IOR('C', 211, int)
#define CMTP_LOOPBACK 0
struct cmtp_connadd_req {
int sock; /* Connected socket */
__u32 flags;
};
struct cmtp_conndel_req {
bdaddr_t bdaddr;
__u32 flags;
};
struct cmtp_conninfo {
bdaddr_t bdaddr;
__u32 flags;
__u16 state;
int num;
};
struct cmtp_connlist_req {
__u32 cnum;
struct cmtp_conninfo __user *ci;
};
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
int cmtp_del_connection(struct cmtp_conndel_req *req);
int cmtp_get_connlist(struct cmtp_connlist_req *req);
int cmtp_get_conninfo(struct cmtp_conninfo *ci);
/* CMTP session defines */
#define CMTP_INTEROP_TIMEOUT (HZ * 5)
#define CMTP_INITIAL_MSGNUM 0xff00
struct cmtp_session {
struct list_head list;
struct socket *sock;
bdaddr_t bdaddr;
unsigned long state;
unsigned long flags;
uint mtu;
char name[BTNAMSIZ];
atomic_t terminate;
struct task_struct *task;
wait_queue_head_t wait;
int ncontroller;
int num;
struct capi_ctr ctrl;
struct list_head applications;
unsigned long blockids;
int msgnum;
struct sk_buff_head transmit;
struct sk_buff *reassembly[16];
};
struct cmtp_application {
struct list_head list;
unsigned long state;
int err;
__u16 appl;
__u16 mapping;
__u16 msgnum;
};
struct cmtp_scb {
int id;
int data;
};
int cmtp_attach_device(struct cmtp_session *session);
void cmtp_detach_device(struct cmtp_session *session);
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
/* CMTP init defines */
int cmtp_init_sockets(void);
void cmtp_cleanup_sockets(void);
#endif /* __CMTP_H */

500
net/bluetooth/cmtp/core.c Normal file
View file

@ -0,0 +1,500 @@
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/freezer.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/l2cap.h>
#include "cmtp.h"
#define VERSION "1.0"
static DECLARE_RWSEM(cmtp_session_sem);
static LIST_HEAD(cmtp_session_list);
static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
{
struct cmtp_session *session;
BT_DBG("");
list_for_each_entry(session, &cmtp_session_list, list)
if (!bacmp(bdaddr, &session->bdaddr))
return session;
return NULL;
}
static void __cmtp_link_session(struct cmtp_session *session)
{
list_add(&session->list, &cmtp_session_list);
}
static void __cmtp_unlink_session(struct cmtp_session *session)
{
list_del(&session->list);
}
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
{
memset(ci, 0, sizeof(*ci));
bacpy(&ci->bdaddr, &session->bdaddr);
ci->flags = session->flags;
ci->state = session->state;
ci->num = session->num;
}
static inline int cmtp_alloc_block_id(struct cmtp_session *session)
{
int i, id = -1;
for (i = 0; i < 16; i++)
if (!test_and_set_bit(i, &session->blockids)) {
id = i;
break;
}
return id;
}
static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
{
clear_bit(id, &session->blockids);
}
static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
{
struct sk_buff *skb = session->reassembly[id], *nskb;
int size;
BT_DBG("session %p buf %p count %d", session, buf, count);
size = (skb) ? skb->len + count : count;
nskb = alloc_skb(size, GFP_ATOMIC);
if (!nskb) {
BT_ERR("Can't allocate memory for CAPI message");
return;
}
if (skb && (skb->len > 0))
skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len);
memcpy(skb_put(nskb, count), buf, count);
session->reassembly[id] = nskb;
kfree_skb(skb);
}
static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
{
__u8 hdr, hdrlen, id;
__u16 len;
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
while (skb->len > 0) {
hdr = skb->data[0];
switch (hdr & 0xc0) {
case 0x40:
hdrlen = 2;
len = skb->data[1];
break;
case 0x80:
hdrlen = 3;
len = skb->data[1] | (skb->data[2] << 8);
break;
default:
hdrlen = 1;
len = 0;
break;
}
id = (hdr & 0x3c) >> 2;
BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
if (hdrlen + len > skb->len) {
BT_ERR("Wrong size or header information in CMTP frame");
break;
}
if (len == 0) {
skb_pull(skb, hdrlen);
continue;
}
switch (hdr & 0x03) {
case 0x00:
cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
cmtp_recv_capimsg(session, session->reassembly[id]);
session->reassembly[id] = NULL;
break;
case 0x01:
cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
break;
default:
if (session->reassembly[id] != NULL)
kfree_skb(session->reassembly[id]);
session->reassembly[id] = NULL;
break;
}
skb_pull(skb, hdrlen + len);
}
kfree_skb(skb);
return 0;
}
static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
{
struct socket *sock = session->sock;
struct kvec iv = { data, len };
struct msghdr msg;
BT_DBG("session %p data %p len %d", session, data, len);
if (!len)
return 0;
memset(&msg, 0, sizeof(msg));
return kernel_sendmsg(sock, &msg, &iv, 1, len);
}
static void cmtp_process_transmit(struct cmtp_session *session)
{
struct sk_buff *skb, *nskb;
unsigned char *hdr;
unsigned int size, tail;
BT_DBG("session %p", session);
nskb = alloc_skb(session->mtu, GFP_ATOMIC);
if (!nskb) {
BT_ERR("Can't allocate memory for new frame");
return;
}
while ((skb = skb_dequeue(&session->transmit))) {
struct cmtp_scb *scb = (void *) skb->cb;
tail = session->mtu - nskb->len;
if (tail < 5) {
cmtp_send_frame(session, nskb->data, nskb->len);
skb_trim(nskb, 0);
tail = session->mtu;
}
size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
if (scb->id < 0) {
scb->id = cmtp_alloc_block_id(session);
if (scb->id < 0) {
skb_queue_head(&session->transmit, skb);
break;
}
}
if (size < 256) {
hdr = skb_put(nskb, 2);
hdr[0] = 0x40
| ((scb->id << 2) & 0x3c)
| ((skb->len == size) ? 0x00 : 0x01);
hdr[1] = size;
} else {
hdr = skb_put(nskb, 3);
hdr[0] = 0x80
| ((scb->id << 2) & 0x3c)
| ((skb->len == size) ? 0x00 : 0x01);
hdr[1] = size & 0xff;
hdr[2] = size >> 8;
}
skb_copy_from_linear_data(skb, skb_put(nskb, size), size);
skb_pull(skb, size);
if (skb->len > 0) {
skb_queue_head(&session->transmit, skb);
} else {
cmtp_free_block_id(session, scb->id);
if (scb->data) {
cmtp_send_frame(session, nskb->data, nskb->len);
skb_trim(nskb, 0);
}
kfree_skb(skb);
}
}
cmtp_send_frame(session, nskb->data, nskb->len);
kfree_skb(nskb);
}
static int cmtp_session(void *arg)
{
struct cmtp_session *session = arg;
struct sock *sk = session->sock->sk;
struct sk_buff *skb;
wait_queue_t wait;
BT_DBG("session %p", session);
set_user_nice(current, -15);
init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (atomic_read(&session->terminate))
break;
if (sk->sk_state != BT_CONNECTED)
break;
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
skb_orphan(skb);
if (!skb_linearize(skb))
cmtp_recv_frame(session, skb);
else
kfree_skb(skb);
}
cmtp_process_transmit(session);
schedule();
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
down_write(&cmtp_session_sem);
if (!(session->flags & (1 << CMTP_LOOPBACK)))
cmtp_detach_device(session);
fput(session->sock->file);
__cmtp_unlink_session(session);
up_write(&cmtp_session_sem);
kfree(session);
module_put_and_exit(0);
return 0;
}
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
{
struct cmtp_session *session, *s;
int i, err;
BT_DBG("");
session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
if (!session)
return -ENOMEM;
down_write(&cmtp_session_sem);
s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst);
if (s && s->state == BT_CONNECTED) {
err = -EEXIST;
goto failed;
}
bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst);
session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
l2cap_pi(sock->sk)->chan->imtu);
BT_DBG("mtu %d", session->mtu);
sprintf(session->name, "%pMR", &session->bdaddr);
session->sock = sock;
session->state = BT_CONFIG;
init_waitqueue_head(&session->wait);
session->msgnum = CMTP_INITIAL_MSGNUM;
INIT_LIST_HEAD(&session->applications);
skb_queue_head_init(&session->transmit);
for (i = 0; i < 16; i++)
session->reassembly[i] = NULL;
session->flags = req->flags;
__cmtp_link_session(session);
__module_get(THIS_MODULE);
session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
session->num);
if (IS_ERR(session->task)) {
module_put(THIS_MODULE);
err = PTR_ERR(session->task);
goto unlink;
}
if (!(session->flags & (1 << CMTP_LOOPBACK))) {
err = cmtp_attach_device(session);
if (err < 0) {
atomic_inc(&session->terminate);
wake_up_process(session->task);
up_write(&cmtp_session_sem);
return err;
}
}
up_write(&cmtp_session_sem);
return 0;
unlink:
__cmtp_unlink_session(session);
failed:
up_write(&cmtp_session_sem);
kfree(session);
return err;
}
int cmtp_del_connection(struct cmtp_conndel_req *req)
{
struct cmtp_session *session;
int err = 0;
BT_DBG("");
down_read(&cmtp_session_sem);
session = __cmtp_get_session(&req->bdaddr);
if (session) {
/* Flush the transmit queue */
skb_queue_purge(&session->transmit);
/* Stop session thread */
atomic_inc(&session->terminate);
wake_up_process(session->task);
} else
err = -ENOENT;
up_read(&cmtp_session_sem);
return err;
}
int cmtp_get_connlist(struct cmtp_connlist_req *req)
{
struct cmtp_session *session;
int err = 0, n = 0;
BT_DBG("");
down_read(&cmtp_session_sem);
list_for_each_entry(session, &cmtp_session_list, list) {
struct cmtp_conninfo ci;
__cmtp_copy_session(session, &ci);
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
err = -EFAULT;
break;
}
if (++n >= req->cnum)
break;
req->ci++;
}
req->cnum = n;
up_read(&cmtp_session_sem);
return err;
}
int cmtp_get_conninfo(struct cmtp_conninfo *ci)
{
struct cmtp_session *session;
int err = 0;
down_read(&cmtp_session_sem);
session = __cmtp_get_session(&ci->bdaddr);
if (session)
__cmtp_copy_session(session, ci);
else
err = -ENOENT;
up_read(&cmtp_session_sem);
return err;
}
static int __init cmtp_init(void)
{
BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
cmtp_init_sockets();
return 0;
}
static void __exit cmtp_exit(void)
{
cmtp_cleanup_sockets();
}
module_init(cmtp_init);
module_exit(cmtp_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("bt-proto-5");

269
net/bluetooth/cmtp/sock.c Normal file
View file

@ -0,0 +1,269 @@
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/export.h>
#include <linux/types.h>
#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/compat.h>
#include <linux/gfp.h>
#include <linux/uaccess.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include "cmtp.h"
static struct bt_sock_list cmtp_sk_list = {
.lock = __RW_LOCK_UNLOCKED(cmtp_sk_list.lock)
};
static int cmtp_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p sk %p", sock, sk);
if (!sk)
return 0;
bt_sock_unlink(&cmtp_sk_list, sk);
sock_orphan(sk);
sock_put(sk);
return 0;
}
static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct cmtp_connadd_req ca;
struct cmtp_conndel_req cd;
struct cmtp_connlist_req cl;
struct cmtp_conninfo ci;
struct socket *nsock;
void __user *argp = (void __user *)arg;
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
switch (cmd) {
case CMTPCONNADD:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
nsock = sockfd_lookup(ca.sock, &err);
if (!nsock)
return err;
if (nsock->sk->sk_state != BT_CONNECTED) {
sockfd_put(nsock);
return -EBADFD;
}
err = cmtp_add_connection(&ca, nsock);
if (!err) {
if (copy_to_user(argp, &ca, sizeof(ca)))
err = -EFAULT;
} else
sockfd_put(nsock);
return err;
case CMTPCONNDEL:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
return cmtp_del_connection(&cd);
case CMTPGETCONNLIST:
if (copy_from_user(&cl, argp, sizeof(cl)))
return -EFAULT;
if (cl.cnum <= 0)
return -EINVAL;
err = cmtp_get_connlist(&cl);
if (!err && copy_to_user(argp, &cl, sizeof(cl)))
return -EFAULT;
return err;
case CMTPGETCONNINFO:
if (copy_from_user(&ci, argp, sizeof(ci)))
return -EFAULT;
err = cmtp_get_conninfo(&ci);
if (!err && copy_to_user(argp, &ci, sizeof(ci)))
return -EFAULT;
return err;
}
return -EINVAL;
}
#ifdef CONFIG_COMPAT
static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
if (cmd == CMTPGETCONNLIST) {
struct cmtp_connlist_req cl;
u32 uci;
int err;
if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
cl.ci = compat_ptr(uci);
if (cl.cnum <= 0)
return -EINVAL;
err = cmtp_get_connlist(&cl);
if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
}
return cmtp_sock_ioctl(sock, cmd, arg);
}
#endif
static const struct proto_ops cmtp_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = cmtp_sock_release,
.ioctl = cmtp_sock_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = cmtp_sock_compat_ioctl,
#endif
.bind = sock_no_bind,
.getname = sock_no_getname,
.sendmsg = sock_no_sendmsg,
.recvmsg = sock_no_recvmsg,
.poll = sock_no_poll,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.getsockopt = sock_no_getsockopt,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
};
static struct proto cmtp_proto = {
.name = "CMTP",
.owner = THIS_MODULE,
.obj_size = sizeof(struct bt_sock)
};
static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
BT_DBG("sock %p", sock);
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->ops = &cmtp_sock_ops;
sock->state = SS_UNCONNECTED;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sk->sk_state = BT_OPEN;
bt_sock_link(&cmtp_sk_list, sk);
return 0;
}
static const struct net_proto_family cmtp_sock_family_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.create = cmtp_sock_create
};
int cmtp_init_sockets(void)
{
int err;
err = proto_register(&cmtp_proto, 0);
if (err < 0)
return err;
err = bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops);
if (err < 0) {
BT_ERR("Can't register CMTP socket");
goto error;
}
err = bt_procfs_init(&init_net, "cmtp", &cmtp_sk_list, NULL);
if (err < 0) {
BT_ERR("Failed to create CMTP proc file");
bt_sock_unregister(BTPROTO_HIDP);
goto error;
}
BT_INFO("CMTP socket layer initialized");
return 0;
error:
proto_unregister(&cmtp_proto);
return err;
}
void cmtp_cleanup_sockets(void)
{
bt_procfs_cleanup(&init_net, "cmtp");
bt_sock_unregister(BTPROTO_CMTP);
proto_unregister(&cmtp_proto);
}

1377
net/bluetooth/hci_conn.c Normal file

File diff suppressed because it is too large Load diff

5716
net/bluetooth/hci_core.c Normal file

File diff suppressed because it is too large Load diff

4875
net/bluetooth/hci_event.c Normal file

File diff suppressed because it is too large Load diff

1264
net/bluetooth/hci_sock.c Normal file

File diff suppressed because it is too large Load diff

214
net/bluetooth/hci_sysfs.c Normal file
View file

@ -0,0 +1,214 @@
/* Bluetooth HCI driver model support. */
#include <linux/module.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
static struct class *bt_class;
static inline char *link_typetostr(int type)
{
switch (type) {
case ACL_LINK:
return "ACL";
case SCO_LINK:
return "SCO";
case ESCO_LINK:
return "eSCO";
case LE_LINK:
return "LE";
default:
return "UNKNOWN";
}
}
static ssize_t show_link_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hci_conn *conn = to_hci_conn(dev);
return sprintf(buf, "%s\n", link_typetostr(conn->type));
}
static ssize_t show_link_address(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hci_conn *conn = to_hci_conn(dev);
return sprintf(buf, "%pMR\n", &conn->dst);
}
#define LINK_ATTR(_name, _mode, _show, _store) \
struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store)
static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
static struct attribute *bt_link_attrs[] = {
&link_attr_type.attr,
&link_attr_address.attr,
NULL
};
ATTRIBUTE_GROUPS(bt_link);
static void bt_link_release(struct device *dev)
{
struct hci_conn *conn = to_hci_conn(dev);
kfree(conn);
}
static struct device_type bt_link = {
.name = "link",
.groups = bt_link_groups,
.release = bt_link_release,
};
/*
* The rfcomm tty device will possibly retain even when conn
* is down, and sysfs doesn't support move zombie device,
* so we should move the device before conn device is destroyed.
*/
static int __match_tty(struct device *dev, void *data)
{
return !strncmp(dev_name(dev), "rfcomm", 6);
}
void hci_conn_init_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p", conn);
conn->dev.type = &bt_link;
conn->dev.class = bt_class;
conn->dev.parent = &hdev->dev;
device_initialize(&conn->dev);
}
void hci_conn_add_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p", conn);
dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle);
if (device_add(&conn->dev) < 0) {
BT_ERR("Failed to register connection device");
return;
}
hci_dev_hold(hdev);
}
void hci_conn_del_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
if (!device_is_registered(&conn->dev))
return;
while (1) {
struct device *dev;
dev = device_find_child(&conn->dev, NULL, __match_tty);
if (!dev)
break;
device_move(dev, NULL, DPM_ORDER_DEV_LAST);
put_device(dev);
}
device_del(&conn->dev);
hci_dev_put(hdev);
}
static inline char *host_typetostr(int type)
{
switch (type) {
case HCI_BREDR:
return "BR/EDR";
case HCI_AMP:
return "AMP";
default:
return "UNKNOWN";
}
}
static ssize_t show_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hci_dev *hdev = to_hci_dev(dev);
return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type));
}
static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hci_dev *hdev = to_hci_dev(dev);
char name[HCI_MAX_NAME_LENGTH + 1];
int i;
for (i = 0; i < HCI_MAX_NAME_LENGTH; i++)
name[i] = hdev->dev_name[i];
name[HCI_MAX_NAME_LENGTH] = '\0';
return sprintf(buf, "%s\n", name);
}
static ssize_t show_address(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hci_dev *hdev = to_hci_dev(dev);
return sprintf(buf, "%pMR\n", &hdev->bdaddr);
}
static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
static struct attribute *bt_host_attrs[] = {
&dev_attr_type.attr,
&dev_attr_name.attr,
&dev_attr_address.attr,
NULL
};
ATTRIBUTE_GROUPS(bt_host);
static void bt_host_release(struct device *dev)
{
struct hci_dev *hdev = to_hci_dev(dev);
kfree(hdev);
module_put(THIS_MODULE);
}
static struct device_type bt_host = {
.name = "host",
.groups = bt_host_groups,
.release = bt_host_release,
};
void hci_init_sysfs(struct hci_dev *hdev)
{
struct device *dev = &hdev->dev;
dev->type = &bt_host;
dev->class = bt_class;
__module_get(THIS_MODULE);
device_initialize(dev);
}
int __init bt_sysfs_init(void)
{
bt_class = class_create(THIS_MODULE, "bluetooth");
return PTR_ERR_OR_ZERO(bt_class);
}
void bt_sysfs_cleanup(void)
{
class_destroy(bt_class);
}

View file

@ -0,0 +1,12 @@
config BT_HIDP
tristate "HIDP protocol support"
depends on BT && INPUT
select HID
help
HIDP (Human Interface Device Protocol) is a transport layer
for HID reports. HIDP is required for the Bluetooth Human
Interface Device Profile.
Say Y here to compile HIDP support into the kernel or say M to
compile it as module (hidp).

View file

@ -0,0 +1,7 @@
#
# Makefile for the Linux Bluetooth HIDP layer
#
obj-$(CONFIG_BT_HIDP) += hidp.o
hidp-objs := core.o sock.o

1439
net/bluetooth/hidp/core.c Normal file

File diff suppressed because it is too large Load diff

192
net/bluetooth/hidp/hidp.h Normal file
View file

@ -0,0 +1,192 @@
/*
HIDP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#ifndef __HIDP_H
#define __HIDP_H
#include <linux/types.h>
#include <linux/hid.h>
#include <linux/kref.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/l2cap.h>
/* HIDP header masks */
#define HIDP_HEADER_TRANS_MASK 0xf0
#define HIDP_HEADER_PARAM_MASK 0x0f
/* HIDP transaction types */
#define HIDP_TRANS_HANDSHAKE 0x00
#define HIDP_TRANS_HID_CONTROL 0x10
#define HIDP_TRANS_GET_REPORT 0x40
#define HIDP_TRANS_SET_REPORT 0x50
#define HIDP_TRANS_GET_PROTOCOL 0x60
#define HIDP_TRANS_SET_PROTOCOL 0x70
#define HIDP_TRANS_GET_IDLE 0x80
#define HIDP_TRANS_SET_IDLE 0x90
#define HIDP_TRANS_DATA 0xa0
#define HIDP_TRANS_DATC 0xb0
/* HIDP handshake results */
#define HIDP_HSHK_SUCCESSFUL 0x00
#define HIDP_HSHK_NOT_READY 0x01
#define HIDP_HSHK_ERR_INVALID_REPORT_ID 0x02
#define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST 0x03
#define HIDP_HSHK_ERR_INVALID_PARAMETER 0x04
#define HIDP_HSHK_ERR_UNKNOWN 0x0e
#define HIDP_HSHK_ERR_FATAL 0x0f
/* HIDP control operation parameters */
#define HIDP_CTRL_NOP 0x00
#define HIDP_CTRL_HARD_RESET 0x01
#define HIDP_CTRL_SOFT_RESET 0x02
#define HIDP_CTRL_SUSPEND 0x03
#define HIDP_CTRL_EXIT_SUSPEND 0x04
#define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG 0x05
/* HIDP data transaction headers */
#define HIDP_DATA_RTYPE_MASK 0x03
#define HIDP_DATA_RSRVD_MASK 0x0c
#define HIDP_DATA_RTYPE_OTHER 0x00
#define HIDP_DATA_RTYPE_INPUT 0x01
#define HIDP_DATA_RTYPE_OUPUT 0x02
#define HIDP_DATA_RTYPE_FEATURE 0x03
/* HIDP protocol header parameters */
#define HIDP_PROTO_BOOT 0x00
#define HIDP_PROTO_REPORT 0x01
/* HIDP ioctl defines */
#define HIDPCONNADD _IOW('H', 200, int)
#define HIDPCONNDEL _IOW('H', 201, int)
#define HIDPGETCONNLIST _IOR('H', 210, int)
#define HIDPGETCONNINFO _IOR('H', 211, int)
#define HIDP_VIRTUAL_CABLE_UNPLUG 0
#define HIDP_BOOT_PROTOCOL_MODE 1
#define HIDP_BLUETOOTH_VENDOR_ID 9
#define HIDP_WAITING_FOR_RETURN 10
#define HIDP_WAITING_FOR_SEND_ACK 11
struct hidp_connadd_req {
int ctrl_sock; /* Connected control socket */
int intr_sock; /* Connected interrupt socket */
__u16 parser;
__u16 rd_size;
__u8 __user *rd_data;
__u8 country;
__u8 subclass;
__u16 vendor;
__u16 product;
__u16 version;
__u32 flags;
__u32 idle_to;
char name[128];
};
struct hidp_conndel_req {
bdaddr_t bdaddr;
__u32 flags;
};
struct hidp_conninfo {
bdaddr_t bdaddr;
__u32 flags;
__u16 state;
__u16 vendor;
__u16 product;
__u16 version;
char name[128];
};
struct hidp_connlist_req {
__u32 cnum;
struct hidp_conninfo __user *ci;
};
int hidp_connection_add(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock);
int hidp_connection_del(struct hidp_conndel_req *req);
int hidp_get_connlist(struct hidp_connlist_req *req);
int hidp_get_conninfo(struct hidp_conninfo *ci);
enum hidp_session_state {
HIDP_SESSION_IDLING,
HIDP_SESSION_PREPARING,
HIDP_SESSION_RUNNING,
};
/* HIDP session defines */
struct hidp_session {
struct list_head list;
struct kref ref;
/* runtime management */
atomic_t state;
wait_queue_head_t state_queue;
atomic_t terminate;
struct task_struct *task;
unsigned long flags;
/* connection management */
bdaddr_t bdaddr;
struct l2cap_conn *conn;
struct l2cap_user user;
struct socket *ctrl_sock;
struct socket *intr_sock;
struct sk_buff_head ctrl_transmit;
struct sk_buff_head intr_transmit;
uint ctrl_mtu;
uint intr_mtu;
unsigned long idle_to;
/* device management */
struct work_struct dev_init;
struct input_dev *input;
struct hid_device *hid;
struct timer_list timer;
/* Report descriptor */
__u8 *rd_data;
uint rd_size;
/* session data */
unsigned char keys[8];
unsigned char leds;
/* Used in hidp_get_raw_report() */
int waiting_report_type; /* HIDP_DATA_RTYPE_* */
int waiting_report_number; /* -1 for not numbered */
struct mutex report_mutex;
struct sk_buff *report_return;
wait_queue_head_t report_queue;
/* Used in hidp_output_raw_report() */
int output_report_success; /* boolean */
/* temporary input buffer */
u8 input_buf[HID_MAX_BUFFER_SIZE];
};
/* HIDP init defines */
int __init hidp_init_sockets(void);
void __exit hidp_cleanup_sockets(void);
#endif /* __HIDP_H */

299
net/bluetooth/hidp/sock.c Normal file
View file

@ -0,0 +1,299 @@
/*
HIDP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/export.h>
#include <linux/file.h>
#include "hidp.h"
static struct bt_sock_list hidp_sk_list = {
.lock = __RW_LOCK_UNLOCKED(hidp_sk_list.lock)
};
static int hidp_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p sk %p", sock, sk);
if (!sk)
return 0;
bt_sock_unlink(&hidp_sk_list, sk);
sock_orphan(sk);
sock_put(sk);
return 0;
}
static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *) arg;
struct hidp_connadd_req ca;
struct hidp_conndel_req cd;
struct hidp_connlist_req cl;
struct hidp_conninfo ci;
struct socket *csock;
struct socket *isock;
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
switch (cmd) {
case HIDPCONNADD:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
csock = sockfd_lookup(ca.ctrl_sock, &err);
if (!csock)
return err;
isock = sockfd_lookup(ca.intr_sock, &err);
if (!isock) {
sockfd_put(csock);
return err;
}
err = hidp_connection_add(&ca, csock, isock);
if (!err && copy_to_user(argp, &ca, sizeof(ca)))
err = -EFAULT;
sockfd_put(csock);
sockfd_put(isock);
return err;
case HIDPCONNDEL:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
return hidp_connection_del(&cd);
case HIDPGETCONNLIST:
if (copy_from_user(&cl, argp, sizeof(cl)))
return -EFAULT;
if (cl.cnum <= 0)
return -EINVAL;
err = hidp_get_connlist(&cl);
if (!err && copy_to_user(argp, &cl, sizeof(cl)))
return -EFAULT;
return err;
case HIDPGETCONNINFO:
if (copy_from_user(&ci, argp, sizeof(ci)))
return -EFAULT;
err = hidp_get_conninfo(&ci);
if (!err && copy_to_user(argp, &ci, sizeof(ci)))
return -EFAULT;
return err;
}
return -EINVAL;
}
#ifdef CONFIG_COMPAT
struct compat_hidp_connadd_req {
int ctrl_sock; /* Connected control socket */
int intr_sock; /* Connected interrupt socket */
__u16 parser;
__u16 rd_size;
compat_uptr_t rd_data;
__u8 country;
__u8 subclass;
__u16 vendor;
__u16 product;
__u16 version;
__u32 flags;
__u32 idle_to;
char name[128];
};
static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
if (cmd == HIDPGETCONNLIST) {
struct hidp_connlist_req cl;
u32 uci;
int err;
if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
cl.ci = compat_ptr(uci);
if (cl.cnum <= 0)
return -EINVAL;
err = hidp_get_connlist(&cl);
if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
} else if (cmd == HIDPCONNADD) {
struct compat_hidp_connadd_req ca;
struct hidp_connadd_req __user *uca;
uca = compat_alloc_user_space(sizeof(*uca));
if (copy_from_user(&ca, (void __user *) arg, sizeof(ca)))
return -EFAULT;
if (put_user(ca.ctrl_sock, &uca->ctrl_sock) ||
put_user(ca.intr_sock, &uca->intr_sock) ||
put_user(ca.parser, &uca->parser) ||
put_user(ca.rd_size, &uca->rd_size) ||
put_user(compat_ptr(ca.rd_data), &uca->rd_data) ||
put_user(ca.country, &uca->country) ||
put_user(ca.subclass, &uca->subclass) ||
put_user(ca.vendor, &uca->vendor) ||
put_user(ca.product, &uca->product) ||
put_user(ca.version, &uca->version) ||
put_user(ca.flags, &uca->flags) ||
put_user(ca.idle_to, &uca->idle_to) ||
copy_to_user(&uca->name[0], &ca.name[0], 128))
return -EFAULT;
arg = (unsigned long) uca;
/* Fall through. We don't actually write back any _changes_
to the structure anyway, so there's no need to copy back
into the original compat version */
}
return hidp_sock_ioctl(sock, cmd, arg);
}
#endif
static const struct proto_ops hidp_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = hidp_sock_release,
.ioctl = hidp_sock_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = hidp_sock_compat_ioctl,
#endif
.bind = sock_no_bind,
.getname = sock_no_getname,
.sendmsg = sock_no_sendmsg,
.recvmsg = sock_no_recvmsg,
.poll = sock_no_poll,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.getsockopt = sock_no_getsockopt,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
};
static struct proto hidp_proto = {
.name = "HIDP",
.owner = THIS_MODULE,
.obj_size = sizeof(struct bt_sock)
};
static int hidp_sock_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
BT_DBG("sock %p", sock);
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->ops = &hidp_sock_ops;
sock->state = SS_UNCONNECTED;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sk->sk_state = BT_OPEN;
bt_sock_link(&hidp_sk_list, sk);
return 0;
}
static const struct net_proto_family hidp_sock_family_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.create = hidp_sock_create
};
int __init hidp_init_sockets(void)
{
int err;
err = proto_register(&hidp_proto, 0);
if (err < 0)
return err;
err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops);
if (err < 0) {
BT_ERR("Can't register HIDP socket");
goto error;
}
err = bt_procfs_init(&init_net, "hidp", &hidp_sk_list, NULL);
if (err < 0) {
BT_ERR("Failed to create HIDP proc file");
bt_sock_unregister(BTPROTO_HIDP);
goto error;
}
BT_INFO("HIDP socket layer initialized");
return 0;
error:
proto_unregister(&hidp_proto);
return err;
}
void __exit hidp_cleanup_sockets(void)
{
bt_procfs_cleanup(&init_net, "hidp");
bt_sock_unregister(BTPROTO_HIDP);
proto_unregister(&hidp_proto);
}

7548
net/bluetooth/l2cap_core.c Normal file

File diff suppressed because it is too large Load diff

1636
net/bluetooth/l2cap_sock.c Normal file

File diff suppressed because it is too large Load diff

168
net/bluetooth/lib.c Normal file
View file

@ -0,0 +1,168 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
/* Bluetooth kernel library. */
#define pr_fmt(fmt) "Bluetooth: " fmt
#include <linux/export.h>
#include <net/bluetooth/bluetooth.h>
void baswap(bdaddr_t *dst, bdaddr_t *src)
{
unsigned char *d = (unsigned char *) dst;
unsigned char *s = (unsigned char *) src;
unsigned int i;
for (i = 0; i < 6; i++)
d[i] = s[5 - i];
}
EXPORT_SYMBOL(baswap);
/* Bluetooth error codes to Unix errno mapping */
int bt_to_errno(__u16 code)
{
switch (code) {
case 0:
return 0;
case 0x01:
return EBADRQC;
case 0x02:
return ENOTCONN;
case 0x03:
return EIO;
case 0x04:
case 0x3c:
return EHOSTDOWN;
case 0x05:
return EACCES;
case 0x06:
return EBADE;
case 0x07:
return ENOMEM;
case 0x08:
return ETIMEDOUT;
case 0x09:
return EMLINK;
case 0x0a:
return EMLINK;
case 0x0b:
return EALREADY;
case 0x0c:
return EBUSY;
case 0x0d:
case 0x0e:
case 0x0f:
return ECONNREFUSED;
case 0x10:
return ETIMEDOUT;
case 0x11:
case 0x27:
case 0x29:
case 0x20:
return EOPNOTSUPP;
case 0x12:
return EINVAL;
case 0x13:
case 0x14:
case 0x15:
return ECONNRESET;
case 0x16:
return ECONNABORTED;
case 0x17:
return ELOOP;
case 0x18:
return EACCES;
case 0x1a:
return EPROTONOSUPPORT;
case 0x1b:
return ECONNREFUSED;
case 0x19:
case 0x1e:
case 0x23:
case 0x24:
case 0x25:
return EPROTO;
default:
return ENOSYS;
}
}
EXPORT_SYMBOL(bt_to_errno);
void bt_info(const char *format, ...)
{
struct va_format vaf;
va_list args;
va_start(args, format);
vaf.fmt = format;
vaf.va = &args;
pr_info("%pV", &vaf);
va_end(args);
}
EXPORT_SYMBOL(bt_info);
void bt_err(const char *format, ...)
{
struct va_format vaf;
va_list args;
va_start(args, format);
vaf.fmt = format;
vaf.va = &args;
pr_err("%pV", &vaf);
va_end(args);
}
EXPORT_SYMBOL(bt_err);

6855
net/bluetooth/mgmt.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,18 @@
config BT_RFCOMM
tristate "RFCOMM protocol support"
depends on BT
help
RFCOMM provides connection oriented stream transport. RFCOMM
support is required for Dialup Networking, OBEX and other Bluetooth
applications.
Say Y here to compile RFCOMM support into the kernel or say M to
compile it as module (rfcomm).
config BT_RFCOMM_TTY
bool "RFCOMM TTY support"
depends on BT_RFCOMM
depends on TTY
help
This option enables TTY emulation support for RFCOMM channels.

View file

@ -0,0 +1,8 @@
#
# Makefile for the Linux Bluetooth RFCOMM layer.
#
obj-$(CONFIG_BT_RFCOMM) += rfcomm.o
rfcomm-y := core.o sock.o
rfcomm-$(CONFIG_BT_RFCOMM_TTY) += tty.o

2277
net/bluetooth/rfcomm/core.c Normal file

File diff suppressed because it is too large Load diff

1103
net/bluetooth/rfcomm/sock.c Normal file

File diff suppressed because it is too large Load diff

1174
net/bluetooth/rfcomm/tty.c Normal file

File diff suppressed because it is too large Load diff

1231
net/bluetooth/sco.c Normal file

File diff suppressed because it is too large Load diff

1755
net/bluetooth/smp.c Normal file

File diff suppressed because it is too large Load diff

147
net/bluetooth/smp.h Normal file
View file

@ -0,0 +1,147 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
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
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#ifndef __SMP_H
#define __SMP_H
struct smp_command_hdr {
__u8 code;
} __packed;
#define SMP_CMD_PAIRING_REQ 0x01
#define SMP_CMD_PAIRING_RSP 0x02
struct smp_cmd_pairing {
__u8 io_capability;
__u8 oob_flag;
__u8 auth_req;
__u8 max_key_size;
__u8 init_key_dist;
__u8 resp_key_dist;
} __packed;
#define SMP_IO_DISPLAY_ONLY 0x00
#define SMP_IO_DISPLAY_YESNO 0x01
#define SMP_IO_KEYBOARD_ONLY 0x02
#define SMP_IO_NO_INPUT_OUTPUT 0x03
#define SMP_IO_KEYBOARD_DISPLAY 0x04
#define SMP_OOB_NOT_PRESENT 0x00
#define SMP_OOB_PRESENT 0x01
#define SMP_DIST_ENC_KEY 0x01
#define SMP_DIST_ID_KEY 0x02
#define SMP_DIST_SIGN 0x04
#define SMP_AUTH_NONE 0x00
#define SMP_AUTH_BONDING 0x01
#define SMP_AUTH_MITM 0x04
#define SMP_CMD_PAIRING_CONFIRM 0x03
struct smp_cmd_pairing_confirm {
__u8 confirm_val[16];
} __packed;
#define SMP_CMD_PAIRING_RANDOM 0x04
struct smp_cmd_pairing_random {
__u8 rand_val[16];
} __packed;
#define SMP_CMD_PAIRING_FAIL 0x05
struct smp_cmd_pairing_fail {
__u8 reason;
} __packed;
#define SMP_CMD_ENCRYPT_INFO 0x06
struct smp_cmd_encrypt_info {
__u8 ltk[16];
} __packed;
#define SMP_CMD_MASTER_IDENT 0x07
struct smp_cmd_master_ident {
__le16 ediv;
__le64 rand;
} __packed;
#define SMP_CMD_IDENT_INFO 0x08
struct smp_cmd_ident_info {
__u8 irk[16];
} __packed;
#define SMP_CMD_IDENT_ADDR_INFO 0x09
struct smp_cmd_ident_addr_info {
__u8 addr_type;
bdaddr_t bdaddr;
} __packed;
#define SMP_CMD_SIGN_INFO 0x0a
struct smp_cmd_sign_info {
__u8 csrk[16];
} __packed;
#define SMP_CMD_SECURITY_REQ 0x0b
struct smp_cmd_security_req {
__u8 auth_req;
} __packed;
#define SMP_CMD_MAX 0x0b
#define SMP_PASSKEY_ENTRY_FAILED 0x01
#define SMP_OOB_NOT_AVAIL 0x02
#define SMP_AUTH_REQUIREMENTS 0x03
#define SMP_CONFIRM_FAILED 0x04
#define SMP_PAIRING_NOTSUPP 0x05
#define SMP_ENC_KEY_SIZE 0x06
#define SMP_CMD_NOTSUPP 0x07
#define SMP_UNSPECIFIED 0x08
#define SMP_REPEATED_ATTEMPTS 0x09
#define SMP_INVALID_PARAMS 0x0a
#define SMP_MIN_ENC_KEY_SIZE 7
#define SMP_MAX_ENC_KEY_SIZE 16
/* LTK types used in internal storage (struct smp_ltk) */
enum {
SMP_STK,
SMP_LTK,
SMP_LTK_SLAVE,
};
static inline u8 smp_ltk_sec_level(struct smp_ltk *key)
{
if (key->authenticated)
return BT_SECURITY_HIGH;
return BT_SECURITY_MEDIUM;
}
/* SMP Commands */
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr);
int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa);
int smp_register(struct hci_dev *hdev);
void smp_unregister(struct hci_dev *hdev);
#endif /* __SMP_H */