URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/linux-2.4/net/bluetooth/bnep
- from Rev 1275 to Rev 1765
- ↔ Reverse comparison
Rev 1275 → Rev 1765
/sock.c
0,0 → 1,210
/* |
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 Krasnyanskiy <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. |
*/ |
|
/* |
* $Id: sock.c,v 1.1.1.1 2004-04-15 01:17:08 phoenix Exp $ |
*/ |
|
#include <linux/config.h> |
#include <linux/module.h> |
|
#include <linux/types.h> |
#include <linux/errno.h> |
#include <linux/kernel.h> |
#include <linux/major.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 <net/sock.h> |
|
#include <asm/system.h> |
#include <asm/uaccess.h> |
|
#include "bnep.h" |
|
#ifndef CONFIG_BLUEZ_BNEP_DEBUG |
#undef BT_DBG |
#define BT_DBG( A... ) |
#endif |
|
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; |
|
sock_orphan(sk); |
sock_put(sk); |
|
MOD_DEC_USE_COUNT; |
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; |
int err; |
|
BT_DBG("cmd %x arg %lx", cmd, arg); |
|
switch (cmd) { |
case BNEPCONNADD: |
if (!capable(CAP_NET_ADMIN)) |
return -EACCES; |
|
if (copy_from_user(&ca, (void *) arg, sizeof(ca))) |
return -EFAULT; |
|
nsock = sockfd_lookup(ca.sock, &err); |
if (!nsock) |
return err; |
|
if (nsock->sk->state != BT_CONNECTED) { |
fput(nsock->file); |
return -EBADFD; |
} |
|
err = bnep_add_connection(&ca, nsock); |
if (!err) { |
if (copy_to_user((void *) arg, &ca, sizeof(ca))) |
err = -EFAULT; |
} else |
fput(nsock->file); |
|
return err; |
|
case BNEPCONNDEL: |
if (!capable(CAP_NET_ADMIN)) |
return -EACCES; |
|
if (copy_from_user(&cd, (void *) arg, sizeof(cd))) |
return -EFAULT; |
|
return bnep_del_connection(&cd); |
|
case BNEPGETCONNLIST: |
if (copy_from_user(&cl, (void *) arg, sizeof(cl))) |
return -EFAULT; |
|
if (cl.cnum <= 0) |
return -EINVAL; |
|
err = bnep_get_connlist(&cl); |
if (!err && copy_to_user((void *) arg, &cl, sizeof(cl))) |
return -EFAULT; |
|
return err; |
|
case BNEPGETCONNINFO: |
if (copy_from_user(&ci, (void *) arg, sizeof(ci))) |
return -EFAULT; |
|
err = bnep_get_conninfo(&ci); |
if (!err && copy_to_user((void *) arg, &ci, sizeof(ci))) |
return -EFAULT; |
|
return err; |
|
default: |
return -EINVAL; |
} |
|
return 0; |
} |
|
static struct proto_ops bnep_sock_ops = { |
family: PF_BLUETOOTH, |
release: bnep_sock_release, |
ioctl: bnep_sock_ioctl, |
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 int bnep_sock_create(struct socket *sock, int protocol) |
{ |
struct sock *sk; |
|
BT_DBG("sock %p", sock); |
|
if (sock->type != SOCK_RAW) |
return -ESOCKTNOSUPPORT; |
|
sock->ops = &bnep_sock_ops; |
|
if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1))) |
return -ENOMEM; |
|
MOD_INC_USE_COUNT; |
|
sock->state = SS_UNCONNECTED; |
sock_init_data(sock, sk); |
|
sk->destruct = NULL; |
sk->protocol = protocol; |
|
return 0; |
} |
|
static struct net_proto_family bnep_sock_family_ops = { |
family: PF_BLUETOOTH, |
create: bnep_sock_create |
}; |
|
int bnep_sock_init(void) |
{ |
bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops); |
return 0; |
} |
|
int bnep_sock_cleanup(void) |
{ |
if (bluez_sock_unregister(BTPROTO_BNEP)) |
BT_ERR("Can't unregister BNEP socket"); |
return 0; |
} |
/Makefile.lib
0,0 → 1,210
obj-$(CONFIG_BLUEZ_BNEP) += crc32.o |
/netdev.c
0,0 → 1,254
/* |
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 Krasnyanskiy <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. |
*/ |
|
/* |
* $Id: netdev.c,v 1.1.1.1 2004-04-15 01:17:08 phoenix Exp $ |
*/ |
|
#include <linux/config.h> |
#include <linux/module.h> |
|
#include <linux/socket.h> |
#include <linux/netdevice.h> |
#include <linux/etherdevice.h> |
#include <linux/skbuff.h> |
#include <linux/wait.h> |
|
#include <asm/unaligned.h> |
|
#include <net/bluetooth/bluetooth.h> |
#include <net/bluetooth/hci_core.h> |
#include <net/bluetooth/l2cap.h> |
|
#include "bnep.h" |
|
#ifndef CONFIG_BLUEZ_BNEP_DEBUG |
#undef BT_DBG |
#define BT_DBG( A... ) |
#endif |
|
#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 struct net_device_stats *bnep_net_get_stats(struct net_device *dev) |
{ |
struct bnep_session *s = dev->priv; |
return &s->stats; |
} |
|
static void bnep_net_set_mc_list(struct net_device *dev) |
{ |
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER |
struct bnep_session *s = dev->priv; |
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, dev->mc_count); |
|
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 dev_mc_list *dmi = dev->mc_list; |
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. */ |
|
for (i = 0; i < dev->mc_count && i < BNEP_MAX_MULTICAST_FILTERS; i++) { |
memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN); |
memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN); |
dmi = dmi->next; |
} |
r->len = htons(skb->len - len); |
} |
|
skb_queue_tail(&sk->write_queue, skb); |
wake_up_interruptible(sk->sleep); |
#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); |
} |
|
static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
{ |
return -EINVAL; |
} |
|
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER |
static inline 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), &s->mc_filter)) { |
BT_DBG("BNEP: filtered skb %p, dst %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", skb, |
eh->h_dest[0], eh->h_dest[1], eh->h_dest[2], |
eh->h_dest[3], eh->h_dest[4], eh->h_dest[5]); |
return 1; |
} |
return 0; |
} |
#endif |
|
#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER |
/* Determine ether protocol. Based on eth_type_trans. */ |
static inline u16 bnep_net_eth_proto(struct sk_buff *skb) |
{ |
struct ethhdr *eh = (void *) skb->data; |
|
if (ntohs(eh->h_proto) >= 1536) |
return eh->h_proto; |
|
if (get_unaligned((u16 *) skb->data) == 0xFFFF) |
return htons(ETH_P_802_3); |
|
return htons(ETH_P_802_2); |
} |
|
static inline 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 int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) |
{ |
struct bnep_session *s = dev->priv; |
struct sock *sk = s->sock->sk; |
|
BT_DBG("skb %p, dev %p", skb, dev); |
|
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER |
if (bnep_net_mc_filter(skb, s)) { |
kfree_skb(skb); |
return 0; |
} |
#endif |
|
#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER |
if (bnep_net_proto_filter(skb, s)) { |
kfree_skb(skb); |
return 0; |
} |
#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. |
*/ |
dev->trans_start = jiffies; |
skb_queue_tail(&sk->write_queue, skb); |
wake_up_interruptible(sk->sleep); |
|
if (skb_queue_len(&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 0; |
} |
|
int bnep_net_init(struct net_device *dev) |
{ |
struct bnep_session *s = dev->priv; |
|
memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN); |
dev->addr_len = ETH_ALEN; |
|
ether_setup(dev); |
|
dev->open = bnep_net_open; |
dev->stop = bnep_net_close; |
dev->hard_start_xmit = bnep_net_xmit; |
dev->get_stats = bnep_net_get_stats; |
dev->do_ioctl = bnep_net_ioctl; |
dev->set_mac_address = bnep_net_set_mac_addr; |
dev->set_multicast_list = bnep_net_set_mc_list; |
|
dev->watchdog_timeo = HZ * 2; |
dev->tx_timeout = bnep_net_timeout; |
|
return 0; |
} |
/core.c
0,0 → 1,706
/* |
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 Krasnyanskiy <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. |
*/ |
|
/* |
* $Id: core.c,v 1.1.1.1 2004-04-15 01:17:08 phoenix Exp $ |
*/ |
|
#define __KERNEL_SYSCALLS__ |
|
#include <linux/config.h> |
#include <linux/module.h> |
|
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/signal.h> |
#include <linux/init.h> |
#include <linux/wait.h> |
#include <linux/errno.h> |
#include <linux/smp_lock.h> |
#include <linux/net.h> |
#include <net/sock.h> |
|
#include <linux/socket.h> |
#include <linux/file.h> |
|
#include <linux/netdevice.h> |
#include <linux/etherdevice.h> |
#include <linux/skbuff.h> |
|
#include <asm/unaligned.h> |
|
#include <net/bluetooth/bluetooth.h> |
#include <net/bluetooth/l2cap.h> |
|
#include "bnep.h" |
|
#ifndef CONFIG_BLUEZ_BNEP_DEBUG |
#undef BT_DBG |
#define BT_DBG(D...) |
#endif |
|
#define VERSION "1.1" |
|
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; |
struct list_head *p; |
|
BT_DBG(""); |
|
list_for_each(p, &bnep_session_list) { |
s = list_entry(p, struct bnep_session, list); |
if (!memcmp(dst, s->eh.h_source, ETH_ALEN)) |
return s; |
} |
return NULL; |
} |
|
static void __bnep_link_session(struct bnep_session *s) |
{ |
MOD_INC_USE_COUNT; |
list_add(&s->list, &bnep_session_list); |
} |
|
static void __bnep_unlink_session(struct bnep_session *s) |
{ |
list_del(&s->list); |
MOD_DEC_USE_COUNT; |
} |
|
static int bnep_send(struct bnep_session *s, void *data, size_t len) |
{ |
struct socket *sock = s->sock; |
struct iovec iv = { data, len }; |
s->msg.msg_iov = &iv; |
s->msg.msg_iovlen = 1; |
return sock->ops->sendmsg(sock, &s->msg, len, NULL); |
} |
|
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)); |
} |
|
static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len) |
{ |
int n; |
|
if (len < 2) |
return -EILSEQ; |
|
n = ntohs(get_unaligned(data)); |
data++; len -= 2; |
|
if (len < n) |
return -EILSEQ; |
|
BT_DBG("filter len %d", n); |
|
#ifdef CONFIG_BLUEZ_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(data++); |
f[i].end = get_unaligned(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)); |
|
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 = ntohs(get_unaligned((u16 *) data)); |
data += 2; len -= 2; |
|
if (len < n) |
return -EILSEQ; |
|
BT_DBG("filter len %d", n); |
|
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER |
n /= (ETH_ALEN * 2); |
|
if (n > 0) { |
s->mc_filter = 0; |
|
/* Always send broadcast */ |
set_bit(bnep_mc_hash(s->dev.broadcast), &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 %s -> %s", |
batostr((void *) a1), batostr((void *) a2)); |
|
#define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); } |
|
/* Iterate from a1 to a2 */ |
set_bit(bnep_mc_hash(a1), &s->mc_filter); |
while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) { |
INCA(a1); |
set_bit(bnep_mc_hash(a1), &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_REQ: |
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; |
|
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 */ |
}; |
#define BNEP_RX_TYPES (sizeof(__bnep_rx_hlen) - 1) |
|
static inline 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->last_rx = jiffies; |
s->stats.rx_bytes += skb->len; |
|
type = *(u8 *) skb->data; skb_pull(skb, 1); |
|
if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES) |
goto badframe; |
|
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) { |
bnep_rx_control(s, skb->data, skb->len); |
kfree_skb(skb); |
return 0; |
} |
|
skb->mac.raw = skb->data; |
|
/* Verify and pull out header */ |
if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK])) |
goto badframe; |
|
s->eh.h_proto = get_unaligned((u16 *) (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) == 0x8100) { |
if (!skb_pull(skb, 4)) |
goto badframe; |
s->eh.h_proto = get_unaligned((u16 *) (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) { |
s->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.raw, ETH_ALEN); |
put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2)); |
break; |
|
case BNEP_COMPRESSED_DST_ONLY: |
memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, 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.raw, ETH_ALEN * 2); |
put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2)); |
break; |
} |
|
memcpy(__skb_put(nskb, skb->len), skb->data, skb->len); |
kfree_skb(skb); |
|
s->stats.rx_packets++; |
nskb->dev = dev; |
nskb->ip_summed = CHECKSUM_UNNECESSARY; |
nskb->protocol = eth_type_trans(nskb, dev); |
netif_rx_ni(nskb); |
return 0; |
|
badframe: |
s->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 inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb) |
{ |
struct ethhdr *eh = (void *) skb->data; |
struct socket *sock = s->sock; |
struct iovec 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 iovec) { &type, 1 }; |
len++; |
|
if (!memcmp(eh->h_dest, s->eh.h_source, ETH_ALEN)) |
type |= 0x01; |
|
if (!memcmp(eh->h_source, s->eh.h_dest, ETH_ALEN)) |
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 iovec) { eh->h_source, ETH_ALEN }; |
len += ETH_ALEN; |
break; |
|
case BNEP_COMPRESSED_DST_ONLY: |
iv[il++] = (struct iovec) { eh->h_dest, ETH_ALEN }; |
len += ETH_ALEN; |
break; |
} |
|
send: |
iv[il++] = (struct iovec) { skb->data, skb->len }; |
len += skb->len; |
|
/* FIXME: linearize skb */ |
|
s->msg.msg_iov = iv; |
s->msg.msg_iovlen = il; |
len = sock->ops->sendmsg(sock, &s->msg, len, NULL); |
kfree_skb(skb); |
|
if (len > 0) { |
s->stats.tx_bytes += len; |
s->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(""); |
|
daemonize(); reparent_to_init(); |
|
sprintf(current->comm, "kbnepd %s", dev->name); |
|
sigfillset(¤t->blocked); |
flush_signals(current); |
|
current->nice = -15; |
|
set_fs(KERNEL_DS); |
|
init_waitqueue_entry(&wait, current); |
add_wait_queue(sk->sleep, &wait); |
while (!atomic_read(&s->killed)) { |
set_current_state(TASK_INTERRUPTIBLE); |
|
// RX |
while ((skb = skb_dequeue(&sk->receive_queue))) { |
skb_orphan(skb); |
bnep_rx_frame(s, skb); |
} |
|
if (sk->state != BT_CONNECTED) |
break; |
|
// TX |
while ((skb = skb_dequeue(&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, &wait); |
|
/* Cleanup session */ |
down_write(&bnep_session_sem); |
|
/* Delete network device */ |
unregister_netdev(dev); |
|
/* Release the socket */ |
fput(s->sock->file); |
|
__bnep_unlink_session(s); |
|
up_write(&bnep_session_sem); |
kfree(s); |
return 0; |
} |
|
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, &bluez_pi(sock->sk)->dst); |
baswap((void *) src, &bluez_pi(sock->sk)->src); |
|
s = kmalloc(sizeof(struct bnep_session), GFP_KERNEL); |
if (!s) |
return -ENOMEM; |
memset(s, 0, sizeof(struct bnep_session)); |
|
down_write(&bnep_session_sem); |
|
ss = __bnep_get_session(dst); |
if (ss && ss->state == BT_CONNECTED) { |
err = -EEXIST; |
goto failed; |
} |
|
dev = &s->dev; |
|
if (*req->device) |
strcpy(dev->name, req->device); |
else |
strcpy(dev->name, "bnep%d"); |
|
memset(dev->broadcast, 0xff, ETH_ALEN); |
|
/* This is rx header therefor addresses are swaped. |
* ie eh.h_dest is our local address. */ |
memcpy(s->eh.h_dest, &src, ETH_ALEN); |
memcpy(s->eh.h_source, &dst, ETH_ALEN); |
|
s->sock = sock; |
s->role = req->role; |
s->state = BT_CONNECTED; |
|
s->msg.msg_flags = MSG_NOSIGNAL; |
|
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER |
/* Set default mc filter */ |
set_bit(bnep_mc_hash(dev->broadcast), &s->mc_filter); |
#endif |
|
#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER |
/* Set default protocol filter */ |
|
/* (IPv4, ARP) */ |
s->proto_filter[0].start = htons(0x0800); |
s->proto_filter[0].end = htons(0x0806); |
/* (RARP, AppleTalk) */ |
s->proto_filter[1].start = htons(0x8035); |
s->proto_filter[1].end = htons(0x80F3); |
/* (IPX, IPv6) */ |
s->proto_filter[2].start = htons(0x8137); |
s->proto_filter[2].end = htons(0x86DD); |
#endif |
|
dev->init = bnep_net_init; |
dev->priv = s; |
err = register_netdev(dev); |
if (err) { |
goto failed; |
} |
|
__bnep_link_session(s); |
|
err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); |
if (err < 0) { |
/* Session thread start failed, gotta cleanup. */ |
unregister_netdev(dev); |
__bnep_unlink_session(s); |
goto failed; |
} |
|
up_write(&bnep_session_sem); |
strcpy(req->device, dev->name); |
return 0; |
|
failed: |
up_write(&bnep_session_sem); |
kfree(s); |
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) { |
/* Wakeup user-space which is polling for socket errors. |
* This is temporary hack untill we have shutdown in L2CAP */ |
s->sock->sk->err = EUNATCH; |
|
/* Kill session thread */ |
atomic_inc(&s->killed); |
wake_up_interruptible(s->sock->sk->sleep); |
} else |
err = -ENOENT; |
|
up_read(&bnep_session_sem); |
return err; |
} |
|
static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) |
{ |
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 list_head *p; |
int err = 0, n = 0; |
|
down_read(&bnep_session_sem); |
|
list_for_each(p, &bnep_session_list) { |
struct bnep_session *s; |
struct bnep_conninfo ci; |
|
s = list_entry(p, struct bnep_session, list); |
|
__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_module(void) |
{ |
l2cap_load(); |
|
bnep_sock_init(); |
|
BT_INFO("BlueZ BNEP ver %s", VERSION); |
BT_INFO("Copyright (C) 2001,2002 Inventel Systemes"); |
BT_INFO("Written 2001,2002 by Clement Moreau <clement.moreau@inventel.fr>"); |
BT_INFO("Written 2001,2002 by David Libault <david.libault@inventel.fr>"); |
BT_INFO("Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>"); |
|
return 0; |
} |
|
static void __exit bnep_cleanup_module(void) |
{ |
bnep_sock_cleanup(); |
} |
|
module_init(bnep_init_module); |
module_exit(bnep_cleanup_module); |
|
MODULE_DESCRIPTION("BlueZ BNEP ver " VERSION); |
MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>"); |
MODULE_LICENSE("GPL"); |
/bnep.h
0,0 → 1,185
/* |
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, write to the Free Software |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
|
/* |
* $Id: bnep.h,v 1.1.1.1 2004-04-15 01:17:07 phoenix Exp $ |
*/ |
|
#ifndef _BNEP_H |
#define _BNEP_H |
|
#include <linux/types.h> |
#include <net/bluetooth/bluetooth.h> |
|
#include <linux/crc32.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]; |
} __attribute__((packed)); |
|
struct bnep_set_filter_req { |
__u8 type; |
__u8 ctrl; |
__u16 len; |
__u8 list[0]; |
} __attribute__((packed)); |
|
struct bnep_control_rsp { |
__u8 type; |
__u8 ctrl; |
__u16 resp; |
} __attribute__((packed)); |
|
struct bnep_ext_hdr { |
__u8 type; |
__u8 len; |
__u8 data[0]; |
} __attribute__((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 *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 killed; |
|
struct ethhdr eh; |
struct msghdr msg; |
|
struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS]; |
u64 mc_filter; |
|
struct socket *sock; |
struct net_device dev; |
struct net_device_stats stats; |
}; |
|
int bnep_net_init(struct net_device *dev); |
int bnep_sock_init(void); |
int bnep_sock_cleanup(void); |
|
static inline int bnep_mc_hash(__u8 *addr) |
{ |
return (crc32_be(~0, addr, ETH_ALEN) >> 26); |
} |
|
#endif |
/Config.in
0,0 → 1,11
# |
# Bluetooth BNEP layer configuration |
# |
|
dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP |
|
if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then |
bool ' Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER |
bool ' Protocol filter support' CONFIG_BLUEZ_BNEP_PROTO_FILTER |
fi |
|
/Makefile
0,0 → 1,10
# |
# Makefile for the Linux Bluetooth BNEP layer |
# |
|
O_TARGET := bnep.o |
|
obj-y := core.o sock.o netdev.o |
obj-m += $(O_TARGET) |
|
include $(TOPDIR)/Rules.make |