Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
24
net/bluetooth/bnep/Kconfig
Normal file
24
net/bluetooth/bnep/Kconfig
Normal file
@@ -0,0 +1,24 @@
|
||||
config BT_BNEP
|
||||
tristate "BNEP protocol support"
|
||||
depends on BT && BT_L2CAP
|
||||
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.
|
||||
|
||||
7
net/bluetooth/bnep/Makefile
Normal file
7
net/bluetooth/bnep/Makefile
Normal 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
|
||||
184
net/bluetooth/bnep/bnep.h
Normal file
184
net/bluetooth/bnep/bnep.h
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
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 2007/06/12 07:27:14 eyryu Exp $
|
||||
*/
|
||||
|
||||
#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];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bnep_set_filter_req {
|
||||
__u8 type;
|
||||
__u8 ctrl;
|
||||
__be16 len;
|
||||
__u8 list[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bnep_control_rsp {
|
||||
__u8 type;
|
||||
__u8 ctrl;
|
||||
__be16 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 __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 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;
|
||||
};
|
||||
|
||||
void bnep_net_setup(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
|
||||
732
net/bluetooth/bnep/core.c
Normal file
732
net/bluetooth/bnep/core.c
Normal file
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
BNEP implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2001-2002 Inventel Systemes
|
||||
Written 2001-2002 by
|
||||
Cl<43>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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id: core.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu Exp $
|
||||
*/
|
||||
|
||||
#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/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
|
||||
#include "bnep.h"
|
||||
|
||||
#ifndef CONFIG_BT_BNEP_DEBUG
|
||||
#undef BT_DBG
|
||||
#define BT_DBG(D...)
|
||||
#endif
|
||||
|
||||
#define VERSION "1.2"
|
||||
|
||||
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 (!compare_ether_addr(dst, s->eh.h_source))
|
||||
return s;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __bnep_link_session(struct bnep_session *s)
|
||||
{
|
||||
/* It's safe to call __module_get() here because sessions are added
|
||||
by the socket layer which has to hold the refference to this module.
|
||||
*/
|
||||
__module_get(THIS_MODULE);
|
||||
list_add(&s->list, &bnep_session_list);
|
||||
}
|
||||
|
||||
static void __bnep_unlink_session(struct bnep_session *s)
|
||||
{
|
||||
list_del(&s->list);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
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 = ntohs(get_unaligned(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 = ntohs(get_unaligned(data++));
|
||||
f[i].end = ntohs(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));
|
||||
|
||||
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 = ntohs(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) {
|
||||
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 %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), (ulong *) &s->mc_filter);
|
||||
while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
|
||||
INCA(a1);
|
||||
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_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((__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) == 0x8100) {
|
||||
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) {
|
||||
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, (__be16 *) __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, (__be16 *) __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_NONE;
|
||||
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 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 (!compare_ether_addr(eh->h_dest, s->eh.h_source))
|
||||
type |= 0x01;
|
||||
|
||||
if (!compare_ether_addr(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->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("kbnepd %s", dev->name);
|
||||
set_user_nice(current, -15);
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(sk->sk_sleep, &wait);
|
||||
while (!atomic_read(&s->killed)) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
// RX
|
||||
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
||||
skb_orphan(skb);
|
||||
bnep_rx_frame(s, 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->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);
|
||||
free_netdev(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device *bnep_get_device(struct bnep_session *session)
|
||||
{
|
||||
bdaddr_t *src = &bt_sk(session->sock->sk)->src;
|
||||
bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
|
||||
struct hci_dev *hdev;
|
||||
struct hci_conn *conn;
|
||||
|
||||
hdev = hci_get_route(dst, src);
|
||||
if (!hdev)
|
||||
return NULL;
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
||||
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return conn ? &conn->dev : NULL;
|
||||
}
|
||||
|
||||
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, &bt_sk(sock->sk)->dst);
|
||||
baswap((void *) src, &bt_sk(sock->sk)->src);
|
||||
|
||||
/* session struct allocated as private part of net_device */
|
||||
dev = alloc_netdev(sizeof(struct bnep_session),
|
||||
(*req->device) ? req->device : "bnep%d",
|
||||
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 = dev->priv;
|
||||
|
||||
/* 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));
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
__bnep_link_session(s);
|
||||
|
||||
err = kernel_thread(bnep_session, s, CLONE_KERNEL);
|
||||
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);
|
||||
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) {
|
||||
/* Wakeup user-space which is polling for socket errors.
|
||||
* This is temporary hack untill we have shutdown in L2CAP */
|
||||
s->sock->sk->sk_err = EUNATCH;
|
||||
|
||||
/* Kill session thread */
|
||||
atomic_inc(&s->killed);
|
||||
wake_up_interruptible(s->sock->sk->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(void)
|
||||
{
|
||||
char flt[50] = "";
|
||||
|
||||
l2cap_load();
|
||||
|
||||
#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_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyansky <maxk@qualcomm.com>");
|
||||
MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("bt-proto-4");
|
||||
247
net/bluetooth/bnep/netdev.c
Normal file
247
net/bluetooth/bnep/netdev.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
BNEP implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2001-2002 Inventel Systemes
|
||||
Written 2001-2002 by
|
||||
Cl<43>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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id: netdev.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu Exp $
|
||||
*/
|
||||
|
||||
#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_BT_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_BT_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->sk_write_queue, skb);
|
||||
wake_up_interruptible(sk->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_BT_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), (ulong *) &s->mc_filter))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_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;
|
||||
u16 proto = ntohs(eh->h_proto);
|
||||
|
||||
if (proto >= 1536)
|
||||
return proto;
|
||||
|
||||
if (get_unaligned((__be16 *) skb->data) == htons(0xFFFF))
|
||||
return ETH_P_802_3;
|
||||
|
||||
return 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_BT_BNEP_MC_FILTER
|
||||
if (bnep_net_mc_filter(skb, s)) {
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_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->sk_sleep.
|
||||
*/
|
||||
dev->trans_start = jiffies;
|
||||
skb_queue_tail(&sk->sk_write_queue, skb);
|
||||
wake_up_interruptible(sk->sk_sleep);
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
void bnep_net_setup(struct net_device *dev)
|
||||
{
|
||||
|
||||
memset(dev->broadcast, 0xff, 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;
|
||||
}
|
||||
268
net/bluetooth/bnep/sock.c
Normal file
268
net/bluetooth/bnep/sock.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id: sock.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.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/init.h>
|
||||
#include <linux/compat.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "bnep.h"
|
||||
|
||||
#ifndef CONFIG_BT_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);
|
||||
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 -EACCES;
|
||||
|
||||
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) {
|
||||
fput(nsock->file);
|
||||
return -EBADFD;
|
||||
}
|
||||
|
||||
err = bnep_add_connection(&ca, nsock);
|
||||
if (!err) {
|
||||
if (copy_to_user(argp, &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, 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;
|
||||
uint32_t uci;
|
||||
int err;
|
||||
|
||||
if (get_user(cl.cnum, (uint32_t __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, (uint32_t __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 socket *sock, int protocol)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
BT_DBG("sock %p", sock);
|
||||
|
||||
if (sock->type != SOCK_RAW)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
sk = sk_alloc(PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto, 1);
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static 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)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
BT_ERR("Can't register BNEP socket");
|
||||
proto_unregister(&bnep_proto);
|
||||
return err;
|
||||
}
|
||||
|
||||
int __exit bnep_sock_cleanup(void)
|
||||
{
|
||||
if (bt_sock_unregister(BTPROTO_BNEP) < 0)
|
||||
BT_ERR("Can't unregister BNEP socket");
|
||||
|
||||
proto_unregister(&bnep_proto);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user