mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-28 14:58:52 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
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
|
||||
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
|
||||
179
net/bluetooth/bnep/bnep.h
Normal file
179
net/bluetooth/bnep/bnep.h
Normal 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
723
net/bluetooth/bnep/core.c
Normal 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
229
net/bluetooth/bnep/netdev.c
Normal 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
258
net/bluetooth/bnep/sock.c
Normal 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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue