162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci BNEP implementation for Linux Bluetooth stack (BlueZ). 362306a36Sopenharmony_ci Copyright (C) 2001-2002 Inventel Systemes 462306a36Sopenharmony_ci Written 2001-2002 by 562306a36Sopenharmony_ci Clément Moreau <clement.moreau@inventel.fr> 662306a36Sopenharmony_ci David Libault <david.libault@inventel.fr> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci This program is free software; you can redistribute it and/or modify 1162306a36Sopenharmony_ci it under the terms of the GNU General Public License version 2 as 1262306a36Sopenharmony_ci published by the Free Software Foundation; 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1562306a36Sopenharmony_ci OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1662306a36Sopenharmony_ci FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. 1762306a36Sopenharmony_ci IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY 1862306a36Sopenharmony_ci CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 1962306a36Sopenharmony_ci WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 2062306a36Sopenharmony_ci ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 2162306a36Sopenharmony_ci OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 2462306a36Sopenharmony_ci COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 2562306a36Sopenharmony_ci SOFTWARE IS DISCLAIMED. 2662306a36Sopenharmony_ci*/ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/kthread.h> 3062306a36Sopenharmony_ci#include <linux/file.h> 3162306a36Sopenharmony_ci#include <linux/etherdevice.h> 3262306a36Sopenharmony_ci#include <asm/unaligned.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 3562306a36Sopenharmony_ci#include <net/bluetooth/l2cap.h> 3662306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "bnep.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define VERSION "1.3" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic bool compress_src = true; 4362306a36Sopenharmony_cistatic bool compress_dst = true; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic LIST_HEAD(bnep_session_list); 4662306a36Sopenharmony_cistatic DECLARE_RWSEM(bnep_session_sem); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct bnep_session *__bnep_get_session(u8 *dst) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct bnep_session *s; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci BT_DBG(""); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci list_for_each_entry(s, &bnep_session_list, list) 5562306a36Sopenharmony_ci if (ether_addr_equal(dst, s->eh.h_source)) 5662306a36Sopenharmony_ci return s; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return NULL; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void __bnep_link_session(struct bnep_session *s) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci list_add(&s->list, &bnep_session_list); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void __bnep_unlink_session(struct bnep_session *s) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci list_del(&s->list); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int bnep_send(struct bnep_session *s, void *data, size_t len) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct socket *sock = s->sock; 7462306a36Sopenharmony_ci struct kvec iv = { data, len }; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return kernel_sendmsg(sock, &s->msg, &iv, 1, len); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct bnep_control_rsp rsp; 8262306a36Sopenharmony_ci rsp.type = BNEP_CONTROL; 8362306a36Sopenharmony_ci rsp.ctrl = ctrl; 8462306a36Sopenharmony_ci rsp.resp = htons(resp); 8562306a36Sopenharmony_ci return bnep_send(s, &rsp, sizeof(rsp)); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#ifdef CONFIG_BT_BNEP_PROTO_FILTER 8962306a36Sopenharmony_cistatic inline void bnep_set_default_proto_filter(struct bnep_session *s) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci /* (IPv4, ARP) */ 9262306a36Sopenharmony_ci s->proto_filter[0].start = ETH_P_IP; 9362306a36Sopenharmony_ci s->proto_filter[0].end = ETH_P_ARP; 9462306a36Sopenharmony_ci /* (RARP, AppleTalk) */ 9562306a36Sopenharmony_ci s->proto_filter[1].start = ETH_P_RARP; 9662306a36Sopenharmony_ci s->proto_filter[1].end = ETH_P_AARP; 9762306a36Sopenharmony_ci /* (IPX, IPv6) */ 9862306a36Sopenharmony_ci s->proto_filter[2].start = ETH_P_IPX; 9962306a36Sopenharmony_ci s->proto_filter[2].end = ETH_P_IPV6; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci#endif 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int n; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (len < 2) 10862306a36Sopenharmony_ci return -EILSEQ; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci n = get_unaligned_be16(data); 11162306a36Sopenharmony_ci data++; 11262306a36Sopenharmony_ci len -= 2; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (len < n) 11562306a36Sopenharmony_ci return -EILSEQ; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci BT_DBG("filter len %d", n); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#ifdef CONFIG_BT_BNEP_PROTO_FILTER 12062306a36Sopenharmony_ci n /= 4; 12162306a36Sopenharmony_ci if (n <= BNEP_MAX_PROTO_FILTERS) { 12262306a36Sopenharmony_ci struct bnep_proto_filter *f = s->proto_filter; 12362306a36Sopenharmony_ci int i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci for (i = 0; i < n; i++) { 12662306a36Sopenharmony_ci f[i].start = get_unaligned_be16(data++); 12762306a36Sopenharmony_ci f[i].end = get_unaligned_be16(data++); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci BT_DBG("proto filter start %u end %u", 13062306a36Sopenharmony_ci f[i].start, f[i].end); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (i < BNEP_MAX_PROTO_FILTERS) 13462306a36Sopenharmony_ci memset(f + i, 0, sizeof(*f)); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (n == 0) 13762306a36Sopenharmony_ci bnep_set_default_proto_filter(s); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS); 14062306a36Sopenharmony_ci } else { 14162306a36Sopenharmony_ci bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci#else 14462306a36Sopenharmony_ci bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ); 14562306a36Sopenharmony_ci#endif 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int n; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (len < 2) 15462306a36Sopenharmony_ci return -EILSEQ; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci n = get_unaligned_be16(data); 15762306a36Sopenharmony_ci data += 2; 15862306a36Sopenharmony_ci len -= 2; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (len < n) 16162306a36Sopenharmony_ci return -EILSEQ; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci BT_DBG("filter len %d", n); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci#ifdef CONFIG_BT_BNEP_MC_FILTER 16662306a36Sopenharmony_ci n /= (ETH_ALEN * 2); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (n > 0) { 16962306a36Sopenharmony_ci int i; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci s->mc_filter = 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Always send broadcast */ 17462306a36Sopenharmony_ci set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Add address ranges to the multicast hash */ 17762306a36Sopenharmony_ci for (; n > 0; n--) { 17862306a36Sopenharmony_ci u8 a1[6], *a2; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci memcpy(a1, data, ETH_ALEN); 18162306a36Sopenharmony_ci data += ETH_ALEN; 18262306a36Sopenharmony_ci a2 = data; 18362306a36Sopenharmony_ci data += ETH_ALEN; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci BT_DBG("mc filter %pMR -> %pMR", a1, a2); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Iterate from a1 to a2 */ 18862306a36Sopenharmony_ci set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); 18962306a36Sopenharmony_ci while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) { 19062306a36Sopenharmony_ci /* Increment a1 */ 19162306a36Sopenharmony_ci i = 5; 19262306a36Sopenharmony_ci while (i >= 0 && ++a1[i--] == 0) 19362306a36Sopenharmony_ci ; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci BT_DBG("mc filter hash 0x%llx", s->mc_filter); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS); 20362306a36Sopenharmony_ci#else 20462306a36Sopenharmony_ci bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ); 20562306a36Sopenharmony_ci#endif 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int bnep_rx_control(struct bnep_session *s, void *data, int len) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci u8 cmd = *(u8 *)data; 21262306a36Sopenharmony_ci int err = 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci data++; 21562306a36Sopenharmony_ci len--; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci switch (cmd) { 21862306a36Sopenharmony_ci case BNEP_CMD_NOT_UNDERSTOOD: 21962306a36Sopenharmony_ci case BNEP_SETUP_CONN_RSP: 22062306a36Sopenharmony_ci case BNEP_FILTER_NET_TYPE_RSP: 22162306a36Sopenharmony_ci case BNEP_FILTER_MULTI_ADDR_RSP: 22262306a36Sopenharmony_ci /* Ignore these for now */ 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci case BNEP_FILTER_NET_TYPE_SET: 22662306a36Sopenharmony_ci err = bnep_ctrl_set_netfilter(s, data, len); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci case BNEP_FILTER_MULTI_ADDR_SET: 23062306a36Sopenharmony_ci err = bnep_ctrl_set_mcfilter(s, data, len); 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci case BNEP_SETUP_CONN_REQ: 23462306a36Sopenharmony_ci /* Successful response should be sent only once */ 23562306a36Sopenharmony_ci if (test_bit(BNEP_SETUP_RESPONSE, &s->flags) && 23662306a36Sopenharmony_ci !test_and_set_bit(BNEP_SETUP_RSP_SENT, &s->flags)) 23762306a36Sopenharmony_ci err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, 23862306a36Sopenharmony_ci BNEP_SUCCESS); 23962306a36Sopenharmony_ci else 24062306a36Sopenharmony_ci err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, 24162306a36Sopenharmony_ci BNEP_CONN_NOT_ALLOWED); 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci default: { 24562306a36Sopenharmony_ci u8 pkt[3]; 24662306a36Sopenharmony_ci pkt[0] = BNEP_CONTROL; 24762306a36Sopenharmony_ci pkt[1] = BNEP_CMD_NOT_UNDERSTOOD; 24862306a36Sopenharmony_ci pkt[2] = cmd; 24962306a36Sopenharmony_ci err = bnep_send(s, pkt, sizeof(pkt)); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return err; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct bnep_ext_hdr *h; 26062306a36Sopenharmony_ci int err = 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci do { 26362306a36Sopenharmony_ci h = (void *) skb->data; 26462306a36Sopenharmony_ci if (!skb_pull(skb, sizeof(*h))) { 26562306a36Sopenharmony_ci err = -EILSEQ; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci BT_DBG("type 0x%x len %u", h->type, h->len); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci switch (h->type & BNEP_TYPE_MASK) { 27262306a36Sopenharmony_ci case BNEP_EXT_CONTROL: 27362306a36Sopenharmony_ci bnep_rx_control(s, skb->data, skb->len); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci default: 27762306a36Sopenharmony_ci /* Unknown extension, skip it. */ 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (!skb_pull(skb, h->len)) { 28262306a36Sopenharmony_ci err = -EILSEQ; 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci } while (!err && (h->type & BNEP_EXT_HEADER)); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return err; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic u8 __bnep_rx_hlen[] = { 29162306a36Sopenharmony_ci ETH_HLEN, /* BNEP_GENERAL */ 29262306a36Sopenharmony_ci 0, /* BNEP_CONTROL */ 29362306a36Sopenharmony_ci 2, /* BNEP_COMPRESSED */ 29462306a36Sopenharmony_ci ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */ 29562306a36Sopenharmony_ci ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */ 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct net_device *dev = s->dev; 30162306a36Sopenharmony_ci struct sk_buff *nskb; 30262306a36Sopenharmony_ci u8 type, ctrl_type; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci dev->stats.rx_bytes += skb->len; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci type = *(u8 *) skb->data; 30762306a36Sopenharmony_ci skb_pull(skb, 1); 30862306a36Sopenharmony_ci ctrl_type = *(u8 *)skb->data; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen)) 31162306a36Sopenharmony_ci goto badframe; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) { 31462306a36Sopenharmony_ci if (bnep_rx_control(s, skb->data, skb->len) < 0) { 31562306a36Sopenharmony_ci dev->stats.tx_errors++; 31662306a36Sopenharmony_ci kfree_skb(skb); 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!(type & BNEP_EXT_HEADER)) { 32162306a36Sopenharmony_ci kfree_skb(skb); 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Verify and pull ctrl message since it's already processed */ 32662306a36Sopenharmony_ci switch (ctrl_type) { 32762306a36Sopenharmony_ci case BNEP_SETUP_CONN_REQ: 32862306a36Sopenharmony_ci /* Pull: ctrl type (1 b), len (1 b), data (len bytes) */ 32962306a36Sopenharmony_ci if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2)) 33062306a36Sopenharmony_ci goto badframe; 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci case BNEP_FILTER_MULTI_ADDR_SET: 33362306a36Sopenharmony_ci case BNEP_FILTER_NET_TYPE_SET: 33462306a36Sopenharmony_ci /* Pull: ctrl type (1 b), len (2 b), data (len bytes) */ 33562306a36Sopenharmony_ci if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2)) 33662306a36Sopenharmony_ci goto badframe; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci default: 33962306a36Sopenharmony_ci kfree_skb(skb); 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci } else { 34362306a36Sopenharmony_ci skb_reset_mac_header(skb); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Verify and pull out header */ 34662306a36Sopenharmony_ci if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK])) 34762306a36Sopenharmony_ci goto badframe; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2)); 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (type & BNEP_EXT_HEADER) { 35362306a36Sopenharmony_ci if (bnep_rx_extension(s, skb) < 0) 35462306a36Sopenharmony_ci goto badframe; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Strip 802.1p header */ 35862306a36Sopenharmony_ci if (ntohs(s->eh.h_proto) == ETH_P_8021Q) { 35962306a36Sopenharmony_ci if (!skb_pull(skb, 4)) 36062306a36Sopenharmony_ci goto badframe; 36162306a36Sopenharmony_ci s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2)); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* We have to alloc new skb and copy data here :(. Because original skb 36562306a36Sopenharmony_ci * may not be modified and because of the alignment requirements. */ 36662306a36Sopenharmony_ci nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL); 36762306a36Sopenharmony_ci if (!nskb) { 36862306a36Sopenharmony_ci dev->stats.rx_dropped++; 36962306a36Sopenharmony_ci kfree_skb(skb); 37062306a36Sopenharmony_ci return -ENOMEM; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci skb_reserve(nskb, 2); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Decompress header and construct ether frame */ 37562306a36Sopenharmony_ci switch (type & BNEP_TYPE_MASK) { 37662306a36Sopenharmony_ci case BNEP_COMPRESSED: 37762306a36Sopenharmony_ci __skb_put_data(nskb, &s->eh, ETH_HLEN); 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci case BNEP_COMPRESSED_SRC_ONLY: 38162306a36Sopenharmony_ci __skb_put_data(nskb, s->eh.h_dest, ETH_ALEN); 38262306a36Sopenharmony_ci __skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN); 38362306a36Sopenharmony_ci put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci case BNEP_COMPRESSED_DST_ONLY: 38762306a36Sopenharmony_ci __skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN); 38862306a36Sopenharmony_ci __skb_put_data(nskb, s->eh.h_source, ETH_ALEN + 2); 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci case BNEP_GENERAL: 39262306a36Sopenharmony_ci __skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN * 2); 39362306a36Sopenharmony_ci put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len); 39862306a36Sopenharmony_ci kfree_skb(skb); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci dev->stats.rx_packets++; 40162306a36Sopenharmony_ci nskb->ip_summed = CHECKSUM_NONE; 40262306a36Sopenharmony_ci nskb->protocol = eth_type_trans(nskb, dev); 40362306a36Sopenharmony_ci netif_rx(nskb); 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cibadframe: 40762306a36Sopenharmony_ci dev->stats.rx_errors++; 40862306a36Sopenharmony_ci kfree_skb(skb); 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic u8 __bnep_tx_types[] = { 41362306a36Sopenharmony_ci BNEP_GENERAL, 41462306a36Sopenharmony_ci BNEP_COMPRESSED_SRC_ONLY, 41562306a36Sopenharmony_ci BNEP_COMPRESSED_DST_ONLY, 41662306a36Sopenharmony_ci BNEP_COMPRESSED 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct ethhdr *eh = (void *) skb->data; 42262306a36Sopenharmony_ci struct socket *sock = s->sock; 42362306a36Sopenharmony_ci struct kvec iv[3]; 42462306a36Sopenharmony_ci int len = 0, il = 0; 42562306a36Sopenharmony_ci u8 type = 0; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci BT_DBG("skb %p dev %p type %u", skb, skb->dev, skb->pkt_type); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (!skb->dev) { 43062306a36Sopenharmony_ci /* Control frame sent by us */ 43162306a36Sopenharmony_ci goto send; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci iv[il++] = (struct kvec) { &type, 1 }; 43562306a36Sopenharmony_ci len++; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (compress_src && ether_addr_equal(eh->h_dest, s->eh.h_source)) 43862306a36Sopenharmony_ci type |= 0x01; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (compress_dst && ether_addr_equal(eh->h_source, s->eh.h_dest)) 44162306a36Sopenharmony_ci type |= 0x02; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (type) 44462306a36Sopenharmony_ci skb_pull(skb, ETH_ALEN * 2); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci type = __bnep_tx_types[type]; 44762306a36Sopenharmony_ci switch (type) { 44862306a36Sopenharmony_ci case BNEP_COMPRESSED_SRC_ONLY: 44962306a36Sopenharmony_ci iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN }; 45062306a36Sopenharmony_ci len += ETH_ALEN; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci case BNEP_COMPRESSED_DST_ONLY: 45462306a36Sopenharmony_ci iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN }; 45562306a36Sopenharmony_ci len += ETH_ALEN; 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cisend: 46062306a36Sopenharmony_ci iv[il++] = (struct kvec) { skb->data, skb->len }; 46162306a36Sopenharmony_ci len += skb->len; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* FIXME: linearize skb */ 46462306a36Sopenharmony_ci { 46562306a36Sopenharmony_ci len = kernel_sendmsg(sock, &s->msg, iv, il, len); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci kfree_skb(skb); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (len > 0) { 47062306a36Sopenharmony_ci s->dev->stats.tx_bytes += len; 47162306a36Sopenharmony_ci s->dev->stats.tx_packets++; 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return len; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int bnep_session(void *arg) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct bnep_session *s = arg; 48162306a36Sopenharmony_ci struct net_device *dev = s->dev; 48262306a36Sopenharmony_ci struct sock *sk = s->sock->sk; 48362306a36Sopenharmony_ci struct sk_buff *skb; 48462306a36Sopenharmony_ci DEFINE_WAIT_FUNC(wait, woken_wake_function); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci BT_DBG(""); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci set_user_nice(current, -15); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci add_wait_queue(sk_sleep(sk), &wait); 49162306a36Sopenharmony_ci while (1) { 49262306a36Sopenharmony_ci if (atomic_read(&s->terminate)) 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci /* RX */ 49562306a36Sopenharmony_ci while ((skb = skb_dequeue(&sk->sk_receive_queue))) { 49662306a36Sopenharmony_ci skb_orphan(skb); 49762306a36Sopenharmony_ci if (!skb_linearize(skb)) 49862306a36Sopenharmony_ci bnep_rx_frame(s, skb); 49962306a36Sopenharmony_ci else 50062306a36Sopenharmony_ci kfree_skb(skb); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (sk->sk_state != BT_CONNECTED) 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* TX */ 50762306a36Sopenharmony_ci while ((skb = skb_dequeue(&sk->sk_write_queue))) 50862306a36Sopenharmony_ci if (bnep_tx_frame(s, skb)) 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci netif_wake_queue(dev); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* 51362306a36Sopenharmony_ci * wait_woken() performs the necessary memory barriers 51462306a36Sopenharmony_ci * for us; see the header comment for this primitive. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci remove_wait_queue(sk_sleep(sk), &wait); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Cleanup session */ 52162306a36Sopenharmony_ci down_write(&bnep_session_sem); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Delete network device */ 52462306a36Sopenharmony_ci unregister_netdev(dev); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* Wakeup user-space polling for socket errors */ 52762306a36Sopenharmony_ci s->sock->sk->sk_err = EUNATCH; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci wake_up_interruptible(sk_sleep(s->sock->sk)); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Release the socket */ 53262306a36Sopenharmony_ci fput(s->sock->file); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci __bnep_unlink_session(s); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci up_write(&bnep_session_sem); 53762306a36Sopenharmony_ci free_netdev(dev); 53862306a36Sopenharmony_ci module_put_and_kthread_exit(0); 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic struct device *bnep_get_device(struct bnep_session *session) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (!conn || !conn->hcon) 54762306a36Sopenharmony_ci return NULL; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return &conn->hcon->dev; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic struct device_type bnep_type = { 55362306a36Sopenharmony_ci .name = "bluetooth", 55462306a36Sopenharmony_ci}; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ciint bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci u32 valid_flags = BIT(BNEP_SETUP_RESPONSE); 55962306a36Sopenharmony_ci struct net_device *dev; 56062306a36Sopenharmony_ci struct bnep_session *s, *ss; 56162306a36Sopenharmony_ci u8 dst[ETH_ALEN], src[ETH_ALEN]; 56262306a36Sopenharmony_ci int err; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci BT_DBG(""); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!l2cap_is_socket(sock)) 56762306a36Sopenharmony_ci return -EBADFD; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (req->flags & ~valid_flags) 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst); 57362306a36Sopenharmony_ci baswap((void *) src, &l2cap_pi(sock->sk)->chan->src); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* session struct allocated as private part of net_device */ 57662306a36Sopenharmony_ci dev = alloc_netdev(sizeof(struct bnep_session), 57762306a36Sopenharmony_ci (*req->device) ? req->device : "bnep%d", 57862306a36Sopenharmony_ci NET_NAME_UNKNOWN, 57962306a36Sopenharmony_ci bnep_net_setup); 58062306a36Sopenharmony_ci if (!dev) 58162306a36Sopenharmony_ci return -ENOMEM; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci down_write(&bnep_session_sem); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci ss = __bnep_get_session(dst); 58662306a36Sopenharmony_ci if (ss && ss->state == BT_CONNECTED) { 58762306a36Sopenharmony_ci err = -EEXIST; 58862306a36Sopenharmony_ci goto failed; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci s = netdev_priv(dev); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* This is rx header therefore addresses are swapped. 59462306a36Sopenharmony_ci * ie. eh.h_dest is our local address. */ 59562306a36Sopenharmony_ci memcpy(s->eh.h_dest, &src, ETH_ALEN); 59662306a36Sopenharmony_ci memcpy(s->eh.h_source, &dst, ETH_ALEN); 59762306a36Sopenharmony_ci eth_hw_addr_set(dev, s->eh.h_dest); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci s->dev = dev; 60062306a36Sopenharmony_ci s->sock = sock; 60162306a36Sopenharmony_ci s->role = req->role; 60262306a36Sopenharmony_ci s->state = BT_CONNECTED; 60362306a36Sopenharmony_ci s->flags = req->flags; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci s->msg.msg_flags = MSG_NOSIGNAL; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci#ifdef CONFIG_BT_BNEP_MC_FILTER 60862306a36Sopenharmony_ci /* Set default mc filter to not filter out any mc addresses 60962306a36Sopenharmony_ci * as defined in the BNEP specification (revision 0.95a) 61062306a36Sopenharmony_ci * http://grouper.ieee.org/groups/802/15/Bluetooth/BNEP.pdf 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci s->mc_filter = ~0LL; 61362306a36Sopenharmony_ci#endif 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci#ifdef CONFIG_BT_BNEP_PROTO_FILTER 61662306a36Sopenharmony_ci /* Set default protocol filter */ 61762306a36Sopenharmony_ci bnep_set_default_proto_filter(s); 61862306a36Sopenharmony_ci#endif 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci SET_NETDEV_DEV(dev, bnep_get_device(s)); 62162306a36Sopenharmony_ci SET_NETDEV_DEVTYPE(dev, &bnep_type); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci err = register_netdev(dev); 62462306a36Sopenharmony_ci if (err) 62562306a36Sopenharmony_ci goto failed; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci __bnep_link_session(s); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci __module_get(THIS_MODULE); 63062306a36Sopenharmony_ci s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name); 63162306a36Sopenharmony_ci if (IS_ERR(s->task)) { 63262306a36Sopenharmony_ci /* Session thread start failed, gotta cleanup. */ 63362306a36Sopenharmony_ci module_put(THIS_MODULE); 63462306a36Sopenharmony_ci unregister_netdev(dev); 63562306a36Sopenharmony_ci __bnep_unlink_session(s); 63662306a36Sopenharmony_ci err = PTR_ERR(s->task); 63762306a36Sopenharmony_ci goto failed; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci up_write(&bnep_session_sem); 64162306a36Sopenharmony_ci strcpy(req->device, dev->name); 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cifailed: 64562306a36Sopenharmony_ci up_write(&bnep_session_sem); 64662306a36Sopenharmony_ci free_netdev(dev); 64762306a36Sopenharmony_ci return err; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ciint bnep_del_connection(struct bnep_conndel_req *req) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci u32 valid_flags = 0; 65362306a36Sopenharmony_ci struct bnep_session *s; 65462306a36Sopenharmony_ci int err = 0; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci BT_DBG(""); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (req->flags & ~valid_flags) 65962306a36Sopenharmony_ci return -EINVAL; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci down_read(&bnep_session_sem); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci s = __bnep_get_session(req->dst); 66462306a36Sopenharmony_ci if (s) { 66562306a36Sopenharmony_ci atomic_inc(&s->terminate); 66662306a36Sopenharmony_ci wake_up_interruptible(sk_sleep(s->sock->sk)); 66762306a36Sopenharmony_ci } else 66862306a36Sopenharmony_ci err = -ENOENT; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci up_read(&bnep_session_sem); 67162306a36Sopenharmony_ci return err; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci u32 valid_flags = BIT(BNEP_SETUP_RESPONSE); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci memset(ci, 0, sizeof(*ci)); 67962306a36Sopenharmony_ci memcpy(ci->dst, s->eh.h_source, ETH_ALEN); 68062306a36Sopenharmony_ci strcpy(ci->device, s->dev->name); 68162306a36Sopenharmony_ci ci->flags = s->flags & valid_flags; 68262306a36Sopenharmony_ci ci->state = s->state; 68362306a36Sopenharmony_ci ci->role = s->role; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ciint bnep_get_connlist(struct bnep_connlist_req *req) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci struct bnep_session *s; 68962306a36Sopenharmony_ci int err = 0, n = 0; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci down_read(&bnep_session_sem); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci list_for_each_entry(s, &bnep_session_list, list) { 69462306a36Sopenharmony_ci struct bnep_conninfo ci; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci __bnep_copy_ci(&ci, s); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (copy_to_user(req->ci, &ci, sizeof(ci))) { 69962306a36Sopenharmony_ci err = -EFAULT; 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (++n >= req->cnum) 70462306a36Sopenharmony_ci break; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci req->ci++; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci req->cnum = n; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci up_read(&bnep_session_sem); 71162306a36Sopenharmony_ci return err; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ciint bnep_get_conninfo(struct bnep_conninfo *ci) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct bnep_session *s; 71762306a36Sopenharmony_ci int err = 0; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci down_read(&bnep_session_sem); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci s = __bnep_get_session(ci->dst); 72262306a36Sopenharmony_ci if (s) 72362306a36Sopenharmony_ci __bnep_copy_ci(ci, s); 72462306a36Sopenharmony_ci else 72562306a36Sopenharmony_ci err = -ENOENT; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci up_read(&bnep_session_sem); 72862306a36Sopenharmony_ci return err; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int __init bnep_init(void) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci char flt[50] = ""; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci#ifdef CONFIG_BT_BNEP_PROTO_FILTER 73662306a36Sopenharmony_ci strcat(flt, "protocol "); 73762306a36Sopenharmony_ci#endif 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci#ifdef CONFIG_BT_BNEP_MC_FILTER 74062306a36Sopenharmony_ci strcat(flt, "multicast"); 74162306a36Sopenharmony_ci#endif 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION); 74462306a36Sopenharmony_ci if (flt[0]) 74562306a36Sopenharmony_ci BT_INFO("BNEP filters: %s", flt); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci bnep_sock_init(); 74862306a36Sopenharmony_ci return 0; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic void __exit bnep_exit(void) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci bnep_sock_cleanup(); 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cimodule_init(bnep_init); 75762306a36Sopenharmony_cimodule_exit(bnep_exit); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cimodule_param(compress_src, bool, 0644); 76062306a36Sopenharmony_ciMODULE_PARM_DESC(compress_src, "Compress sources headers"); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cimodule_param(compress_dst, bool, 0644); 76362306a36Sopenharmony_ciMODULE_PARM_DESC(compress_dst, "Compress destination headers"); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ciMODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); 76662306a36Sopenharmony_ciMODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION); 76762306a36Sopenharmony_ciMODULE_VERSION(VERSION); 76862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 76962306a36Sopenharmony_ciMODULE_ALIAS("bt-proto-4"); 770