diff --git a/app/Stat/Debug/stat.pch b/app/Stat/Debug/stat.pch deleted file mode 100644 index fa1841c..0000000 Binary files a/app/Stat/Debug/stat.pch and /dev/null differ diff --git a/app/Stat/Release/stat.pch b/app/Stat/Release/stat.pch deleted file mode 100644 index ee10e02..0000000 Binary files a/app/Stat/Release/stat.pch and /dev/null differ diff --git a/app/draw/Debug/Draw.pch b/app/draw/Debug/Draw.pch deleted file mode 100644 index f064e77..0000000 Binary files a/app/draw/Debug/Draw.pch and /dev/null differ diff --git a/app/draw/Release/Draw.pch b/app/draw/Release/Draw.pch deleted file mode 100644 index 9cee77e..0000000 Binary files a/app/draw/Release/Draw.pch and /dev/null differ diff --git "a/doc/~$lloX\351\241\271\347\233\256github\345\215\217\345\220\214\345\274\200\345\217\221\346\214\207\345\215\227.docx" "b/doc/~$lloX\351\241\271\347\233\256github\345\215\217\345\220\214\345\274\200\345\217\221\346\214\207\345\215\227.docx" new file mode 100644 index 0000000..142b672 Binary files /dev/null and "b/doc/~$lloX\351\241\271\347\233\256github\345\215\217\345\220\214\345\274\200\345\217\221\346\214\207\345\215\227.docx" differ diff --git a/doc/~WRL1647.tmp b/doc/~WRL1647.tmp deleted file mode 100644 index d02c931..0000000 Binary files a/doc/~WRL1647.tmp and /dev/null differ diff --git a/kernel/bluetooth/.gitignore b/kernel/bluetooth/.gitignore new file mode 100644 index 0000000..1be8ca9 --- /dev/null +++ b/kernel/bluetooth/.gitignore @@ -0,0 +1,5 @@ +tags +.*.swp +*.o +*.d +*.out diff --git a/kernel/bluetooth/ReadMe.txt b/kernel/bluetooth/ReadMe.txt new file mode 100644 index 0000000..e69de29 diff --git a/kernel/bluetooth/arch/cc.h b/kernel/bluetooth/arch/cc.h new file mode 100644 index 0000000..d85b8e9 --- /dev/null +++ b/kernel/bluetooth/arch/cc.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include + +//system types +typedef uint8_t u8_t; +#define U8_F "d" +typedef int8_t s8_t; +#define S8_F "d" +typedef uint16_t u16_t; +#define U16_F "04x" +typedef int16_t s16_t; +#define S16_F "04x" +typedef uint32_t u32_t; +#define U32_F "08x" +typedef int32_t s32_t; +#define S32_F "08x" +typedef int mem_ptr_t; + +#define PACK_STRUCT_STRUCT __attribute__((__packed__)) + +#define LWIP_PLATFORM_DIAG(x) printf x +#define LWIP_PLATFORM_ASSERT(x) printf(x)//; raise(SIGTRAP) diff --git a/kernel/bluetooth/arch/lwbtopts.h b/kernel/bluetooth/arch/lwbtopts.h new file mode 100644 index 0000000..531adbe --- /dev/null +++ b/kernel/bluetooth/arch/lwbtopts.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __LWBT_LWBTOPTS_H__ +#define __LWBT_LWBTOPTS_H__ + +#define PHYBUSIF_DEBUG LWIP_DBG_ON +#define HCI_DEBUG LWIP_DBG_OFF +#define HCI_EV_DEBUG LWIP_DBG_ON +#define L2CAP_DEBUG LWIP_DBG_OFF +#define SDP_DEBUG LWIP_DBG_ON +#define RFCOMM_DEBUG LWIP_DBG_ON + + +/* ---------- Memory options ---------- */ +#define MAX_NUM_CLIENTS 6 /* Maximum number of connected Bluetooth clients. No more than 6 */ + + /*NAT - Network Address Translation*/ +#define MEMP_NUM_NAT_PCB 8 /* Must be set to 1 or more if NAT is used. Set this to the number of diffrent ports/ids that the application use */ + +#define MEMP_NUM_HCI_PCB 1 /* Always set to one */ +#define MEMP_NUM_HCI_LINK (1 + MAX_NUM_CLIENTS) /* One for DT + One per ACL connection */ +#define MEMP_NUM_HCI_INQ 1 /* One per max number of returned results from an inquiry */ + +/* MEMP_NUM_L2CAP_PCB: the number of simulatenously active L2CAP + connections. */ +#define MEMP_NUM_L2CAP_PCB (2 + MAX_NUM_CLIENTS) /* One for a closing connection + one for DT + one per number of connected Bluetooth clients */ +/* MEMP_NUM_L2CAP_PCB_LISTEN: the number of listening L2CAP + connections. */ +#define MEMP_NUM_L2CAP_PCB_LISTEN 2 /* One per listening PSM */ +/* MEMP_NUM_L2CAP_SIG: the number of simultaneously unresponded + L2CAP signals */ +#define MEMP_NUM_L2CAP_SIG (2 * MAX_NUM_CLIENTS)/* Two per number of connected Bluetooth clients but min 2 */ +#define MEMP_NUM_L2CAP_SEG (2 + 2 * MAX_NUM_CLIENTS) /* One per number of L2CAP connections */ + +#define MEMP_NUM_SDP_PCB MAX_NUM_CLIENTS /* One per number of connected Bluetooth clients */ +#define MEMP_NUM_SDP_RECORD 1 /* One per registered service record */ + +#define MEMP_NUM_RFCOMM_PCB (2 + 2 * MAX_NUM_CLIENTS) /* Two for DT + Two per number of connected Bluetooth clients */ +#define MEMP_NUM_RFCOMM_PCB_LISTEN (2 * MAX_NUM_CLIENTS) /* Two per number of connected Bluetooth clients */ + +#define MEMP_NUM_PPP_PCB (1 + MAX_NUM_CLIENTS) /* One for DT + One per number of connected Bluetooth clients */ +#define MEMP_NUM_PPP_REQ MAX_NUM_CLIENTS /* One per number of connected Bluetooth clients but min 1 */ + +/* ---------- HCI options ---------- */ +/* HCI: Defines if we have lower layers of the Bluetooth stack running on a separate host + controller */ +#define HCI 1 + +#if HCI +/* HCI_HOST_MAX_NUM_ACL: The maximum number of ACL packets that the host can buffer */ +#define HCI_HOST_MAX_NUM_ACL 8 //TODO: Should be equal to PBUF_POOL_SIZE/2??? */ +/* HCI_HOST_ACL_MAX_LEN: The maximum size of an ACL packet that the host can buffer */ +#define HCI_HOST_ACL_MAX_LEN (RFCOMM_N + 14) /* Default: RFCOMM MFS + ACL header size, L2CAP header size, + RFCOMM header size and RFCOMM FCS size */ +/* HCI_PACKET_TYPE: The set of packet types which may be used on the connection. In order to + maximize packet throughput, it is recommended that RFCOMM should make use of the 3 and 5 + slot baseband packets.*/ +#define HCI_PACKET_TYPE 0xCC18 /* Default DM1, DH1, DM3, DH3, DM5, DH5 */ +/* HCI_ALLOW_ROLE_SWITCH: Tells the host controller whether to accept a Master/Slave switch + during establishment of a connection */ +#define HCI_ALLOW_ROLE_SWITCH 1 /* Default 1 */ +/* HCI_FLOW_QUEUEING: Control if a packet should be queued if the host controller is out of + bufferspace for outgoing packets. Only the first packet sent when out of credits will be + queued */ +#define HCI_FLOW_QUEUEING 0 /* Default: 0 */ + +#endif /* HCI */ + +/* ---------- L2CAP options ---------- */ +/* L2CAP_HCI: Option for including HCI to access the Bluetooth baseband capabilities */ +#define L2CAP_HCI 1 //TODO: NEEDED? +/* L2CAP_CFG_QOS: Control if a flow specification similar to RFC 1363 should be used */ +#define L2CAP_CFG_QOS 0 +/* L2CAP_MTU: Maximum transmission unit for L2CAP packet payload (min 48) */ +#define L2CAP_MTU (RFCOMM_N + 6)/* Default for this implementation is RFCOMM MFS + RFCOMM header size and + RFCOMM FCS size while the L2CAP default is 672 */ +/* L2CAP_OUT_FLUSHTO: For some networking protocols, such as many real-time protocols, guaranteed delivery + is undesirable. The flush time-out value SHALL be set to its default value 0xffff for a reliable L2CAP + channel, and MAY be set to other values if guaranteed delivery is not desired. (min 1) */ +#define L2CAP_OUT_FLUSHTO 0xFFFF /* Default: 0xFFFF. Infinite number of retransmissions (reliable channel) + The value of 1 implies no retransmissions at the Baseband level + should be performed since the minimum polling interval is 1.25 ms.*/ +/* L2CAP_RTX: The Responsive Timeout eXpired timer is used to terminate + the channel when the remote endpoint is unresponsive to signalling + requests (min 1s, max 60s) */ +#define L2CAP_RTX 60 +/* L2CAP_ERTX: The Extended Response Timeout eXpired timer is used in + place of the RTC timer when a L2CAP_ConnectRspPnd event is received + (min 60s, max 300s) */ +#define L2CAP_ERTX 300 +/* L2CAP_MAXRTX: Maximum number of Request retransmissions before + terminating the channel identified by the request. The decision + should be based on the flush timeout of the signalling link. If the + flush timeout is infinite, no retransmissions should be performed */ +#define L2CAP_MAXRTX 0 +/* L2CAP_CFG_TO: Amount of time spent arbitrating the channel parameters + before terminating the connection (max 120s) */ +#define L2CAP_CFG_TO 30 + +/* ---------- SDP options ---------- */ + +/* ---------- RFCOMM options ---------- */ +/* RFCOMM_N: Maximum frame size for RFCOMM segments (min 23, max 32767)*/ +#define RFCOMM_N ((1024) + 8) /* Default: Worst case byte stuffed PPP packet size + + non-compressed PPP header size and FCS size */ +/* RFCOMM_K: Initial amount of credits issued to the peer (min 0, max 7) */ +#define RFCOMM_K 0 +/* RFCOMM_TO: Acknowledgement timer (T1) and response timer for multiplexer control channel (T2). + T1 is the timeout for frames sent with the P/F bit set to 1 (SABM and DISC) and T2 is the timeout + for commands sent in UIH frames on DLCI 0 (min 10s, max 60s) */ +#define RFCOMM_TO 20 +/* RFCOMM_FLOW_QUEUEING: Control if a packet should be queued if a channel is out of credits for + outgoing packets. Only the first packet sent when out of credits will be queued */ +#define RFCOMM_FLOW_QUEUEING 0 /* Default: 0 */ + + +#endif /* __LWBTOPTS_H__ */ diff --git a/kernel/bluetooth/arch/lwipopts.h b/kernel/bluetooth/arch/lwipopts.h new file mode 100644 index 0000000..6846769 --- /dev/null +++ b/kernel/bluetooth/arch/lwipopts.h @@ -0,0 +1,26 @@ +#pragma once + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + +#define LWIP_DEBUG 1 +#define NO_SYS 1 +#define MEM_LIBC_MALLOC 1 +#define MEM_ALIGNMENT 4 +#define LWIP_TCP 0 +#define LWIP_UDP 0 +#define LWIP_IGMP 0 +#define LWIP_RAW 0 +#define IP_REASSEMBLY 0 +#define LWIP_NETCONN 0 +#define ARP_QUEUEING 0 +//#define BYTE_ORDER LITTLE_ENDIAN +#define LWIP_PLATFORM_BYTESWAP 0 + +#define MEM_DEBUG LWIP_DBG_ON +#define MEM_STATS 1 diff --git a/kernel/bluetooth/arch/perf.h b/kernel/bluetooth/arch/perf.h new file mode 100644 index 0000000..0d98771 --- /dev/null +++ b/kernel/bluetooth/arch/perf.h @@ -0,0 +1,4 @@ +#pragma once + +#define PERF_START +#define PERF_STOP(x) diff --git a/kernel/bluetooth/arch/sys_arch.h b/kernel/bluetooth/arch/sys_arch.h new file mode 100644 index 0000000..e69de29 diff --git a/kernel/bluetooth/lwbt/fcs.c b/kernel/bluetooth/lwbt/fcs.c new file mode 100644 index 0000000..7e4d751 --- /dev/null +++ b/kernel/bluetooth/lwbt/fcs.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + + + +/* fcs.c + * + * Implementation of the frame check sequence calculation. + * Used by RFCOMM and PPP. + */ + + + +#include "lwbt/fcs.h" + +/* +* FCS lookup table as calculated by the table generator in rfc1662. +*/ +static const u16_t crc16table[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* +* FCS (reversed crc) lookup table as calculated by the table generator in TS 101 369 V6.3.0. +*/ +static const u8_t crc8table[256] = { /* reversed, 8-bit, poly=0x07 */ + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, + 0x7C, 0x09, 0x98, 0xEA, 0x7B, 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, + 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, 0x38, + 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, + 0x31, 0xA0, 0xD2, 0x43, 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, + 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, 0x70, 0xE1, + 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, + 0xE8, 0x9A, 0x0B, 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, + 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, 0x48, 0xD9, 0xAB, + 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, + 0xA2, 0x33, 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, + 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, 0xE0, 0x71, 0x03, 0x92, + 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, + 0x9B, 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, + 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, 0xD8, 0x49, 0x3B, 0xAA, 0xDF, + 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, + 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, + 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, 0x8C, + 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, + 0x85, 0x14, 0x66, 0xF7, 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, + 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, 0xB4, 0x25, + 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, + 0x2C, 0x5E, 0xCF +}; + +#define INITFCS8 0xFF /* Initial FCS value */ +#define GOODFCS8 0xCF /* Good final FCS value */ +#define INITFCS16 0xFFFF /* Initial FCS value */ +#define GOODFCS16 0xF0B8 /* Good final FCS value */ + + +u8_t +fcs8_crc(struct pbuf *p, u8_t len, u8_t fcs) +{ + struct pbuf *q; + u8_t i; + u8_t *payload; + + for(q = p; q != NULL && len > 0; q = q->next) { + payload = q->payload; + for(i = 0; len > 0 && i < q->len; ++i) { + fcs = crc8table[fcs ^ *payload++]; + --len; + } + } + return fcs; +} + + +u8_t +fcs8_crc_check(struct pbuf *p, u8_t len, u8_t check_sum) +{ + u8_t fcs = INITFCS8; + + fcs = fcs8_crc(p, len, fcs); + + fcs = crc8table[fcs ^ check_sum]; + if (fcs == GOODFCS8) { + return 0; /* Valid */ + } else { + return 1; /* Failed */ + } +} + + +u8_t +fcs8_crc_calc(struct pbuf *p, u8_t len) +{ + u8_t fcs = INITFCS8; + + fcs = fcs8_crc(p, len, fcs); + + /* Ones complement */ + return 0xFF-fcs; +} + + +u16_t +fcs16_crc(struct pbuf *p, u16_t len) +{ + struct pbuf *q; + u8_t i; + u8_t *payload; + u16_t fcs = INITFCS16; + + for(q = p; q != NULL && len > 0; q = q->next) { + payload = q->payload; + for(i = 0; len > 0 && i < q->len; ++i) { + fcs = (fcs >> 8) ^ crc16table[(fcs ^ payload[i]) & 0xff]; + --len; + } + } + return fcs; +} + + +u8_t +fcs16_crc_check(struct pbuf *p, u16_t len) +{ + u16_t fcs; + + fcs = fcs16_crc(p, len); + + if (fcs == GOODFCS16) { + return 0; /* Valid */ + } else { + return 1; /* Failed */ + } +} + + +u16_t +fcs16_crc_calc(struct pbuf *p, u16_t len) +{ + u16_t fcs; + + fcs = fcs16_crc(p, len); + + /* Ones complement */ + return (0xFFFF ^ fcs); +} + + diff --git a/kernel/bluetooth/lwbt/hci.c b/kernel/bluetooth/lwbt/hci.c new file mode 100644 index 0000000..9204bf1 --- /dev/null +++ b/kernel/bluetooth/lwbt/hci.c @@ -0,0 +1,1454 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + + +/* hci.c + * + * Implementation of the Host Controller Interface (HCI). A command interface + * to the baseband controller and link manager, and gives access to hardware + * status and control registers. + * + */ + + +#include "lwbt/lwbtopts.h" +#include "lwbt/phybusif.h" +#include "lwbt/hci.h" +#include "lwbt/l2cap.h" +#include "lwbt/lwbt_memp.h" +#include "lwip/debug.h" +#include "lwip/mem.h" + +/* The HCI LINK lists. */ +struct hci_link *hci_active_links; /* List of all active HCI LINKs */ +struct hci_link *hci_tmp_link; + +struct hci_pcb *pcb; + + +/* + * hci_init(): + * + * Initializes the HCI layer. + */ +err_t hci_init(void) +{ + if((pcb = lwbt_memp_malloc(MEMP_HCI_PCB)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_init: Could not allocate memory for pcb\n")); + return ERR_MEM; + } + MEMSET(pcb, 0, sizeof(struct hci_pcb)); + + /* Clear globals */ + hci_active_links = NULL; + hci_tmp_link = NULL; + return ERR_OK; +} + +/* + * hci_new(): + * + * Creates a new HCI link control block + */ +struct hci_link * hci_new(void) +{ + struct hci_link *link; + + link = lwbt_memp_malloc(MEMP_HCI_LINK); + if(link != NULL) { + MEMSET(link, 0, sizeof(struct hci_link)); + return link; + } + LWIP_DEBUGF(HCI_DEBUG, ("hci_new: Could not allocate memory for link\n")); + return NULL; +} + +/* + * hci_close(): + * + * Close the link control block. + */ +err_t hci_close(struct hci_link *link) +{ +#if RFCOMM_FLOW_QUEUEING + if(link->p != NULL) { + pbuf_free(link->p); + } +#endif + HCI_RMV(&(hci_active_links), link); + lwbt_memp_free(MEMP_HCI_LINK, link); + link = NULL; + return ERR_OK; +} + +/* + * hci_reset_all(): + * + * Closes all active link control blocks. + */ +void hci_reset_all(void) +{ + struct hci_link *link, *tlink; + struct hci_inq_res *ires, *tires; + + for(link = hci_active_links; link != NULL;) { + tlink = link->next; + hci_close(link); + link = tlink; + } + hci_active_links = NULL; + + for(ires = pcb->ires; ires != NULL;) { + tires = ires->next; + lwbt_memp_free(MEMP_HCI_INQ, ires); + ires = tires; + } + lwbt_memp_free(MEMP_HCI_PCB, pcb); + + hci_init(); +} + +/* + * hci_arg(): + * + * Used to specify the argument that should be passed callback + * functions. + */ +void hci_arg(void *arg) +{ + pcb->callback_arg = arg; +} + +/* + * hci_cmd_complete(): + * + * Used to specify the function that should be called when HCI has received a + * command complete event. + */ +void hci_cmd_complete(err_t (* cmd_complete)(void *arg, struct hci_pcb *pcb, u8_t ogf, u8_t ocf, u8_t result)) +{ + pcb->cmd_complete = cmd_complete; +} + +/* + * hci_pin_req(): + * + * Used to specify the function that should be called when HCI has received a + * PIN code request event. + */ +void hci_pin_req(err_t (* pin_req)(void *arg, struct bd_addr *bdaddr)) +{ + pcb->pin_req = pin_req; +} + +/* + * hci_link_key_not(): + * + * Used to specify the function that should be called when HCI has received a + * link key notification event. + */ +void hci_link_key_not(err_t (* link_key_not)(void *arg, struct bd_addr *bdaddr, u8_t *key)) +{ + pcb->link_key_not = link_key_not; +} + +/* + * hci_connection_complete(): + * + * Used to specify the function that should be called when HCI has received a + * connection complete event. + */ +void hci_connection_complete(err_t (* conn_complete)(void *arg, struct bd_addr *bdaddr)) +{ + pcb->conn_complete = conn_complete; +} + +/* + * hci_wlp_complete(): + * + * Used to specify the function that should be called when HCI has received a + * successful write link policy complete event. + */ +void hci_wlp_complete(err_t (* wlp_complete)(void *arg, struct bd_addr *bdaddr)) +{ + pcb->wlp_complete = wlp_complete; +} + +/* + * hci_get_link(): + * + * Used to get the link structure for that represents an ACL connection. + */ +struct hci_link * hci_get_link(struct bd_addr *bdaddr) +{ + struct hci_link *link; + + for(link = hci_active_links; link != NULL; link = link->next) { + if(bd_addr_cmp(&(link->bdaddr), bdaddr)) { + break; + } + } + return link; +} + +/* + * hci_acl_input(): + * + * Called by the physical bus interface. Handles host controller to host flow + * control, finds a bluetooth address that correspond to the connection handle + * and forward the packet to the L2CAP layer. + */ +void hci_acl_input(struct pbuf *p) +{ + struct hci_acl_hdr *aclhdr; + struct hci_link *link; + u16_t conhdl; + + pbuf_header(p, HCI_ACL_HDR_LEN); + aclhdr = p->payload; + pbuf_header(p, -HCI_ACL_HDR_LEN); + + conhdl = aclhdr->conhdl_pb_bc & 0x0FFF; /* Get the connection handle from the first + 12 bits */ + if(pcb->flow) { + //TODO: XXX??? DO WE SAVE NUMACL PACKETS COMPLETED IN LINKS LIST?? SHOULD WE CALL + //hci_host_num_comp_packets from the main loop when no data has been received from the + //serial port??? + --pcb->host_num_acl; + if(pcb->host_num_acl == 0) { + hci_host_num_comp_packets(conhdl, HCI_HOST_MAX_NUM_ACL); + pcb->host_num_acl = HCI_HOST_MAX_NUM_ACL; + } + } + + for(link = hci_active_links; link != NULL; link = link->next) { + if(link->conhdl == conhdl) { + break; + } + } + + if(link != NULL) { + if(aclhdr->len) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_acl_input: Forward ACL packet to higher layer p->tot_len = %d\n", p->tot_len)); + l2cap_input(p, &(link->bdaddr)); + } else { + pbuf_free(p); /* If length of ACL packet is zero, we silently discard it */ + } + } else { + pbuf_free(p); /* If no acitve ACL link was found, we silently discard the packet */ + } +} + +#if HCI_EV_DEBUG +char * hci_get_error_code(u8_t code) { + switch(code) { + case HCI_SUCCESS: + return("Success"); + case HCI_UNKNOWN_HCI_COMMAND: + return("Unknown HCI Command"); + case HCI_NO_CONNECTION: + return("No Connection"); + case HCI_HW_FAILURE: + return("Hardware Failure"); + case HCI_PAGE_TIMEOUT: + return("Page Timeout"); + case HCI_AUTHENTICATION_FAILURE: + return("Authentication Failure"); + case HCI_KEY_MISSING: + return("Key Missing"); + case HCI_MEMORY_FULL: + return("Memory Full"); + case HCI_CONN_TIMEOUT: + return("Connection Timeout"); + case HCI_MAX_NUMBER_OF_CONNECTIONS: + return("Max Number Of Connections"); + case HCI_MAX_NUMBER_OF_SCO_CONNECTIONS_TO_DEVICE: + return("Max Number Of SCO Connections To A Device"); + case HCI_ACL_CONNECTION_EXISTS: + return("ACL connection already exists"); + case HCI_COMMAND_DISSALLOWED: + return("Command Disallowed"); + case HCI_HOST_REJECTED_DUE_TO_LIMITED_RESOURCES: + return("Host Rejected due to limited resources"); + case HCI_HOST_REJECTED_DUE_TO_SECURITY_REASONS: + return("Host Rejected due to security reasons"); + case HCI_HOST_REJECTED_DUE_TO_REMOTE_DEVICE_ONLY_PERSONAL_SERVICE: + return("Host Rejected due to remote device is only a personal device"); + case HCI_HOST_TIMEOUT: + return("Host Timeout"); + case HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE: + return("Unsupported Feature or Parameter Value"); + case HCI_INVALID_HCI_COMMAND_PARAMETERS: + return("Invalid HCI Command Parameters"); + case HCI_OTHER_END_TERMINATED_CONN_USER_ENDED: + return("Other End Terminated Connection: User Ended Connection"); + case HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES: + return("Other End Terminated Connection: Low Resources"); + case HCI_OTHER_END_TERMINATED_CONN_ABOUT_TO_POWER_OFF: + return("Other End Terminated Connection: About to Power Off"); + case HCI_CONN_TERMINATED_BY_LOCAL_HOST: + return("Connection Terminated by Local Host"); + case HCI_REPETED_ATTEMPTS: + return("Repeated Attempts"); + case HCI_PAIRING_NOT_ALLOWED: + return("Pairing Not Allowed"); + case HCI_UNKNOWN_LMP_PDU: + return("Unknown LMP PDU"); + case HCI_UNSUPPORTED_REMOTE_FEATURE: + return("Unsupported Remote Feature"); + case HCI_SCO_OFFSET_REJECTED: + return("SCO Offset Rejected"); + case HCI_SCO_INTERVAL_REJECTED: + return("SCO Interval Rejected"); + case HCI_SCO_AIR_MODE_REJECTED: + return("SCO Air Mode Rejected"); + case HCI_INVALID_LMP_PARAMETERS: + return("Invalid LMP Parameters"); + case HCI_UNSPECIFIED_ERROR: + return("Unspecified Error"); + case HCI_UNSUPPORTED_LMP_PARAMETER_VALUE: + return("Unsupported LMP Parameter Value"); + case HCI_ROLE_CHANGE_NOT_ALLOWED: + return("Role Change Not Allowed"); + case HCI_LMP_RESPONSE_TIMEOUT: + return("LMP Response Timeout"); + case HCI_LMP_ERROR_TRANSACTION_COLLISION: + return("LMP Error Transaction Collision"); + case HCI_LMP_PDU_NOT_ALLOWED: + return("LMP PDU Not Allowed"); + case HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE: + return("Encryption Mode Not Acceptable"); + case HCI_UNIT_KEY_USED: + return("Unit Key Used"); + case HCI_QOS_NOT_SUPPORTED: + return("QoS is Not Supported"); + case HCI_INSTANT_PASSED: + return("Instant Passed"); + case HCI_PAIRING_UNIT_KEY_NOT_SUPPORTED: + return("Pairing with Unit Key Not Supported"); + default: + return("Error code unknown"); + } +} +#else +u8_t * hci_get_error_code(u8_t code) +{ + return 0; +} +#endif /* HCI_EV_DEBUG */ + +/* hci_event_input(): + * + * Called by the physical bus interface. Parses the received event packet to + * determine which event occurred and handles it. + */ +void hci_event_input(struct pbuf *p) +{ + struct hci_inq_res *inqres; + struct hci_event_hdr *evhdr; + struct hci_link *link; + u8_t i, j; + struct bd_addr *bdaddr; + u8_t resp_offset; + err_t ret; + u8_t ocf, ogf; + + pbuf_header(p, HCI_EVENT_HDR_LEN); + evhdr = p->payload; + pbuf_header(p, -HCI_EVENT_HDR_LEN); + LWIP_DEBUGF(HCI_EV_DEBUG, ("\n")); + + switch(evhdr->code) { + case HCI_INQUIRY_COMPLETE: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Inquiry complete, 0x%x %s\n", ((u8_t *)p->payload)[0], hci_get_error_code(((u8_t *)p->payload)[0]))); + HCI_EVENT_INQ_COMPLETE(pcb,((u8_t *)p->payload)[0],ret); + break; + case HCI_INQUIRY_RESULT: + for(i=0;i<((u8_t *)p->payload)[0];i++) { + resp_offset = i*14; + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Inquiry result %d\nBD_ADDR: 0x", i)); + for(i = 0; i < BD_ADDR_LEN; i++) { + LWIP_DEBUGF(HCI_EV_DEBUG, ("%x",((u8_t *)p->payload)[1+resp_offset+i])); + } + LWIP_DEBUGF(HCI_EV_DEBUG, ("\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Page_Scan_Rep_Mode: 0x%x\n",((u8_t *)p->payload)[7+resp_offset])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Page_Scan_Per_Mode: 0x%x\n",((u8_t *)p->payload)[8+resp_offset])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Page_Scan_Mode: 0x%x\n",((u8_t *)p->payload)[9+resp_offset])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Class_of_Dev: 0x%x 0x%x 0x%x\n",((u8_t *)p->payload)[10+resp_offset], + ((u8_t *)p->payload)[11+resp_offset], ((u8_t *)p->payload)[12+resp_offset])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Clock_Offset: 0x%x%x\n",((u8_t *)p->payload)[13+resp_offset], + ((u8_t *)p->payload)[14+resp_offset])); + + bdaddr = (void *)(((u8_t *)p->payload)+(1+resp_offset)); + if((inqres = lwbt_memp_malloc(MEMP_HCI_INQ)) != NULL) { + bd_addr_set(&(inqres->bdaddr), bdaddr); + inqres->psrm = ((u8_t *)p->payload)[7+resp_offset]; + inqres->psm = ((u8_t *)p->payload)[9+resp_offset]; + MEMCPY(inqres->cod, ((u8_t *)p->payload)+10+resp_offset, 3); + inqres->co = *((u16_t *)(((u8_t *)p->payload)+13+resp_offset)); + HCI_REG(&(pcb->ires), inqres); + } else { + LWIP_DEBUGF(HCI_DEBUG, ("hci_event_input: Could not allocate memory for inquiry result\n")); + } + } + break; + case HCI_CONNECTION_COMPLETE: + bdaddr = (void *)(((u8_t *)p->payload)+3); /* Get the Bluetooth address */ + link = hci_get_link(bdaddr); + switch(((u8_t *)p->payload)[0]) { + case HCI_SUCCESS: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Conn successfully completed\n")); + if(link == NULL) { + if((link = hci_new()) == NULL) { + /* Could not allocate memory for link. Disconnect */ + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Could not allocate memory for link. Disconnect\n")); + hci_disconnect(bdaddr, HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES); + /* Notify L2CAP */ + lp_disconnect_ind(bdaddr); + break; + } + bd_addr_set(&(link->bdaddr), bdaddr); + link->conhdl = *((u16_t *)(((u8_t *)p->payload)+1)); + HCI_REG(&(hci_active_links), link); + HCI_EVENT_CONN_COMPLETE(pcb,bdaddr,ret); /* Allow applicaton to do optional configuration of link */ + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Calling lp_connect_ind\n")); + lp_connect_ind(&(link->bdaddr)); /* Notify L2CAP */ + } else { + link->conhdl = *((u16_t *)(((u8_t *)p->payload)+1)); + HCI_EVENT_CONN_COMPLETE(pcb,bdaddr,ret); /* Allow applicaton to do optional configuration of link */ + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Calling lp_connect_cfm\n")); + lp_connect_cfm(bdaddr, ((u8_t *)p->payload)[10], ERR_OK); /* Notify L2CAP */ + } + //TODO: MASTER SLAVE SWITCH?? + break; + default: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Conn failed to complete, 0x%x %s\n" + , ((u8_t *)p->payload)[0], hci_get_error_code(((u8_t *)p->payload)[0]))); + if(link != NULL) { + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Link exists. Notify upper layer\n")); + hci_close(link); + lp_connect_cfm(bdaddr, ((u8_t *)p->payload)[10], ERR_CONN); + } else { + /* silently discard */ + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Silently discard. Link does not exist\n")); + } + break; + } /* switch */ + LWIP_DEBUGF(HCI_EV_DEBUG, ("Conn_hdl: 0x%x 0x%x\n", ((u8_t *)p->payload)[1], ((u8_t *)p->payload)[2])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("BD_ADDR: 0x")); + for(j=0;jpayload)[3+j])); + } + LWIP_DEBUGF(HCI_EV_DEBUG, ("\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Link_type: 0x%x\n",((u8_t *)p->payload)[9])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Encryption_Mode: 0x%x\n",((u8_t *)p->payload)[10])); + break; + case HCI_DISCONNECTION_COMPLETE: + switch(((u8_t *)p->payload)[0]) { + case HCI_SUCCESS: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Disconn has occurred\n")); + for(link = hci_active_links; link != NULL; link = link->next) { + if(link->conhdl == *((u16_t *)(((u8_t *)p->payload)+1))) { + break; /* Found */ + } + } + if(link != NULL) { + lp_disconnect_ind(&(link->bdaddr)); /* Notify upper layer */ + hci_close(link); + } + /* else silently discard */ + break; + default: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Disconn failed to complete, 0x%x %s\n" + , ((u8_t *)p->payload)[0], hci_get_error_code(((u8_t *)p->payload)[0]))); + return; + } + LWIP_DEBUGF(HCI_EV_DEBUG, ("Conn_hdl: 0x%x%x\n", ((u8_t *)p->payload)[1], ((u8_t *)p->payload)[2])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Reason: 0x%x %s\n", ((u8_t *)p->payload)[3], hci_get_error_code(((u8_t *)p->payload)[3]))); + break; + case HCI_ENCRYPTION_CHANGE: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Encryption changed. Status = 0x%x, Encryption enable = 0x%x\n", ((u8_t *)p->payload)[0], ((u8_t *)p->payload)[3])); + break; + case HCI_QOS_SETUP_COMPLETE: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: QOS setup complete result = 0x%x\n", ((u8_t *)p->payload)[0])); + break; + case HCI_COMMAND_COMPLETE: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Command Complete\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Num_HCI_Command_Packets: 0x%x\n", ((u8_t *)p->payload)[0])); + + pcb->numcmd += ((u8_t *)p->payload)[0]; /* Add number of completed command packets to the + number of command packets that the BT module + can buffer */ + pbuf_header(p, -1); /* Adjust payload pointer not to cover + Num_HCI_Command_Packets parameter */ + ocf = *((u16_t *)p->payload) & 0x03FF; + ogf = *((u16_t *)p->payload) >> 10; + + LWIP_DEBUGF(HCI_EV_DEBUG, ("OCF == 0x%x OGF == 0x%x\n", ocf, ogf)); + + pbuf_header(p, -2); /* Adjust payload pointer not to cover Command_Opcode + parameter */ + if(ogf == HCI_INFO_PARAM) { + if(ocf == HCI_READ_BUFFER_SIZE) { + if(((u8_t *)p->payload)[0] == HCI_SUCCESS) { + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Read_Buffer_Size command succeeded\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("HC_ACL_Data_Packet_Length: 0x%x%x\n", ((u8_t *)p->payload)[1], ((u8_t *)p->payload)[2])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("HC_SCO_Data_Packet_Length: 0x%x\n", ((u8_t *)p->payload)[3])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("HC_Total_Num_ACL_Data_Packets: %d\n", *((u16_t *)(((u8_t *)p->payload)+4)))); + pcb->maxsize = *((u16_t *)(((u8_t *)p->payload)+1)); /* Maximum size of an ACL packet + that the BT module is able to + accept */ + pcb->hc_num_acl = *((u16_t *)(((u8_t *)p->payload)+4)); /* Number of ACL packets that the + BT module can buffer */ + LWIP_DEBUGF(HCI_EV_DEBUG, ("HC_Total_Num_SCO_Data_Packets: 0x%x%x\n", ((u8_t *)p->payload)[6], ((u8_t *)p->payload)[7])); + } else { + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Read_Buffer_Size command failed, 0x%x %s\n", ((u8_t *)p->payload)[0], hci_get_error_code(((u8_t *)p->payload)[0]))); + return; + } + } + if(ocf == HCI_READ_BD_ADDR) { + if(((u8_t *)p->payload)[0] == HCI_SUCCESS) { + bdaddr = (void *)(((u8_t *)p->payload) + 1); /* Get the Bluetooth address */ + HCI_EVENT_RBD_COMPLETE(pcb, bdaddr, ret); /* Notify application.*/ + } + } + } + if(ogf == HCI_HOST_C_N_BB && ocf == HCI_SET_HC_TO_H_FC) { + if(((u8_t *)p->payload)[0] == HCI_SUCCESS) { + pcb->flow = 1; + } + } + if(ogf == HCI_LINK_POLICY) { + if(ocf == HCI_W_LINK_POLICY) { + if(((u8_t *)p->payload)[0] == HCI_SUCCESS) { + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Successful HCI_W_LINK_POLICY.\n")); + for(link = hci_active_links; link != NULL; link = link->next) { + if(link->conhdl == *((u16_t *)(((u8_t *)p->payload)+1))) { + break; + } + } + if(link == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_event_input: Connection does not exist\n")); + return; /* Connection does not exist */ + } + HCI_EVENT_WLP_COMPLETE(pcb, &link->bdaddr, ret); /* Notify application.*/ + } else { + LWIP_DEBUGF(HCI_EV_DEBUG, ("Unsuccessful HCI_W_LINK_POLICY.\n")); + return; + } + } + } + + HCI_EVENT_CMD_COMPLETE(pcb,ogf,ocf,((u8_t *)p->payload)[0],ret); + break; + case HCI_COMMAND_STATUS: + switch(((u8_t *)p->payload)[0]) { + case HCI_SUCCESS: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Command Status\n")); + break; + default: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Command failed, %s\n", hci_get_error_code(((u8_t *)p->payload)[0]))); + pbuf_header(p, -2); /* Adjust payload pointer not to cover + Num_HCI_Command_Packets and status + parameter */ + ocf = *((u16_t *)p->payload) & 0x03FF; + ogf = *((u16_t *)p->payload) >> 10; + pbuf_header(p, -2); /* Adjust payload pointer not to cover + Command_Opcode parameter */ + HCI_EVENT_CMD_COMPLETE(pcb,ogf,ocf,((u8_t *)p->payload)[0],ret); + pbuf_header(p, 4); + break; + } + LWIP_DEBUGF(HCI_EV_DEBUG, ("Num_HCI_Command_Packets: 0x%x\n", ((u8_t *)p->payload)[1])); + /* Add number of completed command packets to the number of command + * packets that the BT module can buffer */ + pcb->numcmd += ((u8_t *)p->payload)[1]; + LWIP_DEBUGF(HCI_EV_DEBUG, ("Command_Opcode: 0x%x 0x%x\n", ((u8_t *)p->payload)[2], ((u8_t *)p->payload)[3])); + break; + case HCI_HARDWARE_ERROR: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Hardware Error\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Hardware_code: 0x%x\n\n", ((u8_t *)p->payload)[0])); + hci_reset(); + //TODO: IS THIS FATAL?? + break; + case HCI_ROLE_CHANGE: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Role change\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Status: 0x%x\n", ((u8_t *)p->payload)[0])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("New Role: 0x%x\n", ((u8_t *)p->payload)[7])); + break; + case HCI_NBR_OF_COMPLETED_PACKETS: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Number Of Completed Packets\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Number_of_Handles: 0x%x\n", ((u8_t *)p->payload)[0])); + for(i=0;i<((u8_t *)p->payload)[0];i++) { + resp_offset = i*4; + LWIP_DEBUGF(HCI_EV_DEBUG, ("Conn_hdl: 0x%x%x\n", ((u8_t *)p->payload)[1+resp_offset], ((u8_t *)p->payload)[2+resp_offset])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("HC_Num_Of_Completed_Packets: 0x%x\n",*((u16_t *)(((u8_t *)p->payload)+3+resp_offset)))); + /* Add number of completed ACL packets to the number of ACL packets that the + BT module can buffer */ + pcb->hc_num_acl += *((u16_t *)(((u8_t *)p->payload) + 3 + resp_offset)); +#if HCI_FLOW_QUEUEING + { + u16_t conhdl = *((u16_t *)(((u8_t *)p->payload) + 1 + resp_offset)); + struct pbuf *q; + for(link = hci_active_links; link != NULL; link = link->next) { + if(link->conhdl == conhdl) { + break; + } + } + q = link->p; + /* Queued packet present? */ + if (q != NULL) { + /* NULL attached buffer immediately */ + link->p = NULL; + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Sending queued packet.\n")); + /* Send the queued packet */ + lp_acl_write(&link->bdaddr, q, link->len, link->pb); + /* Free the queued packet */ + pbuf_free(q); + } + } +#endif /* RFCOMM_FLOW_QUEUEING */ + } + break; + case HCI_MODE_CHANGE: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Mode change\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Status: 0x%x\n", ((u8_t *)p->payload)[0])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Conn_hdl: 0x%x\n", ((u16_t *)(((u8_t *)p->payload) + 1))[0])); + break; + case HCI_DATA_BUFFER_OVERFLOW: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Data Buffer Overflow\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Link_Type: 0x%x\n", ((u8_t *)p->payload)[0])); + //TODO: IS THIS FATAL???? + break; + case HCI_MAX_SLOTS_CHANGE: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Max slots changed\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("Conn_hdl: 0x%x\n", ((u16_t *)p->payload)[0])); + LWIP_DEBUGF(HCI_EV_DEBUG, ("LMP max slots: 0x%x\n", ((u8_t *)p->payload)[2])); + break; + case HCI_PIN_CODE_REQUEST: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Pin request\n")); + bdaddr = (void *)((u8_t *)p->payload); /* Get the Bluetooth address */ + HCI_EVENT_PIN_REQ(pcb, bdaddr, ret); /* Notify application. If event is not registered, + send a negative reply */ + break; + case HCI_LINK_KEY_NOTIFICATION: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Link key notification\n")); + bdaddr = (void *)((u8_t *)p->payload); /* Get the Bluetooth address */ + LWIP_DEBUGF(HCI_EV_DEBUG, ("bdaddr: %02x:%02x:%02x:%02x:%02x:%02x\n", + ((u8_t *)p->payload)[5], ((u8_t *)p->payload)[4], ((u8_t *)p->payload)[3], + ((u8_t *)p->payload)[2], ((u8_t *)p->payload)[1], ((u8_t *)p->payload)[0] + )); + LWIP_DEBUGF(HCI_EV_DEBUG, ("key_type: %d\n", ((u8_t *)p->payload)[16+6])); + + HCI_EVENT_LINK_KEY_NOT(pcb, bdaddr, ((u8_t *)p->payload) + 6, ret); /* Notify application.*/ + break; + case HCI_PAGE_SCAN_REP_MODE_CHANGE: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Page Scan Repetition Mode changed\n")); + LWIP_DEBUGF(HCI_EV_DEBUG, ("bdaddr: %02x:%02x:%02x:%02x:%02x:%02x\n", + ((u8_t *)p->payload)[5], ((u8_t *)p->payload)[4], ((u8_t *)p->payload)[3], + ((u8_t *)p->payload)[2], ((u8_t *)p->payload)[1], ((u8_t *)p->payload)[0] + )); + LWIP_DEBUGF(HCI_EV_DEBUG, ("rep mode: %d\n", ((u8_t *)p->payload)[6])); + break; + default: + LWIP_DEBUGF(HCI_EV_DEBUG, ("hci_event_input: Undefined event code 0x%x\n", evhdr->code)); + break; + }/* switch */ +} + +/* HCI Commands */ + + +/* hci_cmd_ass(): + * + * Assemble the command header. + */ +struct pbuf * hci_cmd_ass(struct pbuf *p, u8_t ocf, u8_t ogf, u8_t len) +{ + ((u8_t *)p->payload)[0] = HCI_COMMAND_DATA_PACKET; /* cmd packet type */ + ((u8_t *)p->payload)[1] = (ocf & 0xff); /* OCF & OGF */ + ((u8_t *)p->payload)[2] = (ocf >> 8)|(ogf << 2); + ((u8_t *)p->payload)[3] = len-HCI_CMD_HDR_LEN-1; /* Param len = plen - cmd hdr - ptype */ + if(pcb->numcmd != 0) { + --pcb->numcmd; /* Reduce number of cmd packets that the host controller can buffer */ + } + return p; +} + +/* hci_inquiry(): + * + * Cause the Host contoller to enter inquiry mode to discovery other nearby + * Bluetooth devices. + */ +err_t hci_inquiry(u32_t lap, u8_t inq_len, u8_t num_resp, err_t (* inq_complete)(void *arg, struct hci_pcb *pcb, struct hci_inq_res *ires, u16_t result)) +{ + struct pbuf *p; + struct hci_inq_res *tmpres; + + /* Free any previous inquiry result list */ + while(pcb->ires != NULL) { + tmpres = pcb->ires; + pcb->ires = pcb->ires->next; + lwbt_memp_free(MEMP_HCI_INQ, tmpres); + } + + pcb->inq_complete = inq_complete; + + if((p = pbuf_alloc(PBUF_RAW, HCI_INQUIRY_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_inquiry: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_INQUIRY_OCF, HCI_LINK_CTRL_OGF, HCI_INQUIRY_PLEN); + /* Assembling cmd prameters */ + ((u8_t *)p->payload)[4] = lap & 0xFF; + ((u8_t *)p->payload)[5] = lap >> 8; + ((u8_t *)p->payload)[6] = lap >> 16; + //MEMCPY(((u8_t *)p->payload)+4, inqres->cod, 3); + ((u8_t *)p->payload)[7] = inq_len; + ((u8_t *)p->payload)[8] = num_resp; + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_disconnect(): + * + * Used to terminate an existing connection. + */ +err_t hci_disconnect(struct bd_addr *bdaddr, u8_t reason) +{ + struct pbuf *p; + struct hci_link *link; + + link = hci_get_link(bdaddr); + + if(link == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_disconnect: Connection does not exist\n")); + return ERR_CONN; /* Connection does not exist */ + } + if((p = pbuf_alloc(PBUF_RAW, HCI_DISCONN_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_disconnect: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_DISCONN_OCF, HCI_LINK_CTRL_OGF, HCI_DISCONN_PLEN); + + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = link->conhdl; + ((u8_t *)p->payload)[6] = reason; + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_reject_connection_request(): + * + * Used to decline a new incoming connection request. + */ +err_t hci_reject_connection_request(struct bd_addr *bdaddr, u8_t reason) +{ + struct pbuf *p; + LWIP_DEBUGF(HCI_DEBUG, ("hci_reject_connection_request: reject from %02x:%02x:%02x:%02x:%02x:%02x\n", + bdaddr->addr[5], bdaddr->addr[4], bdaddr->addr[3], + bdaddr->addr[2], bdaddr->addr[1], bdaddr->addr[0] )); + if((p = pbuf_alloc(PBUF_RAW, HCI_REJECT_CONN_REQ_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_reject_connection_request: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_REJECT_CONN_REQ_OCF, HCI_LINK_CTRL_OGF, HCI_REJECT_CONN_REQ_PLEN); + /* Assembling cmd prameters */ + MEMCPY(((u8_t *)p->payload) + 4, bdaddr->addr, 6); + ((u8_t *)p->payload)[10] = reason; + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_pin_code_request_reply(): + * + * Used to reply to a PIN Code Request event from the Host Controller and + * specifies the PIN code to use for a connection. + */ +err_t hci_pin_code_request_reply(struct bd_addr *bdaddr, u8_t pinlen, u8_t *pincode) +{ + struct pbuf *p; + + if((p = pbuf_alloc(PBUF_RAW, HCI_PIN_CODE_REQ_REP_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_pin_code_request_reply: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Reset buffer content just to make sure */ + MEMSET((u8_t *)p->payload, 0, HCI_PIN_CODE_REQ_REP_PLEN); + + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_PIN_CODE_REQ_REP, HCI_LINK_CTRL_OGF, HCI_PIN_CODE_REQ_REP_PLEN); + /* Assembling cmd prameters */ + MEMCPY(((u8_t *)p->payload) + 4, bdaddr->addr, 6); + ((u8_t *)p->payload)[10] = pinlen; + MEMCPY(((u8_t *)p->payload) + 11, pincode, pinlen); + + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_pin_code_request_neg_reply(): + * + * Used to reply to a PIN Code Request event from the Host Controller when the + * Host cannot specify a PIN code to use for a connection. + */ +err_t hci_pin_code_request_neg_reply(struct bd_addr *bdaddr) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_PIN_CODE_REQ_NEG_REP_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_pin_code_request_neg_reply: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_PIN_CODE_REQ_NEG_REP, HCI_LINK_CTRL_OGF, HCI_PIN_CODE_REQ_NEG_REP_PLEN); + /* Assembling cmd prameters */ + MEMCPY(((u8_t *)p->payload)+4, bdaddr->addr, 6); + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_sniff_mode(): + * + * Sets an ACL connection to low power Sniff mode. + */ +err_t hci_sniff_mode(struct bd_addr *bdaddr, u16_t max_interval, u16_t min_interval, u16_t attempt, u16_t timeout) +{ + struct pbuf *p; + struct hci_link *link; + + /* Check if an ACL connection exists */ + link = hci_get_link(bdaddr); + + if(link == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_sniff_mode: ACL connection does not exist\n")); + return ERR_CONN; + } + + if((p = pbuf_alloc(PBUF_TRANSPORT, HCI_SNIFF_PLEN, PBUF_RAM)) == NULL) { /* Alloc len of packet */ + LWIP_DEBUGF(HCI_DEBUG, ("hci_sniff_mode: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_SNIFF_MODE, HCI_LINK_POLICY, HCI_SNIFF_PLEN); + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = link->conhdl; + ((u16_t *)p->payload)[3] = max_interval; + ((u16_t *)p->payload)[4] = min_interval; + ((u16_t *)p->payload)[5] = attempt; + ((u16_t *)p->payload)[6] = timeout; + + phybusif_output(p, p->tot_len); + pbuf_free(p); + return ERR_OK; +} + +/* hci_write_link_policy_settings(): + * + * Control the modes (park, sniff, hold) that an ACL connection can take. + * + */ +err_t hci_write_link_policy_settings(struct bd_addr *bdaddr, u16_t link_policy) +{ + struct pbuf *p; + struct hci_link *link; + + /* Check if an ACL connection exists */ + link = hci_get_link(bdaddr); + + if(link == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_write_link_policy_settings: ACL connection does not exist\n")); + return ERR_CONN; + } + + if( (p = pbuf_alloc(PBUF_TRANSPORT, HCI_W_LINK_POLICY_PLEN, PBUF_RAM)) == NULL) { /* Alloc len of packet */ + LWIP_DEBUGF(HCI_DEBUG, ("hci_write_link_policy_settings: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_W_LINK_POLICY, HCI_LINK_POLICY, HCI_W_LINK_POLICY_PLEN); + + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = link->conhdl; + ((u16_t *)p->payload)[3] = link_policy; + phybusif_output(p, p->tot_len); + pbuf_free(p); + return ERR_OK; +} + +/* hci_reset(): + * + * Reset the Bluetooth host controller, link manager, and radio module. + */ +err_t hci_reset(void) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_RESET_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_reset: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_RESET_OCF, HCI_HC_BB_OGF, HCI_RESET_PLEN); + /* Assembling cmd prameters */ + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_set_event_filter(): + * + * Used by the host to specify different event filters. + */ +err_t hci_set_event_filter(u8_t filter_type, u8_t filter_cond_type, u8_t* cond) +{ + u8_t cond_len = 0x00; + struct pbuf *p; + LWIP_DEBUGF(HCI_DEBUG, ("hci_set_event_filter: filter_type=%d cond_type=%d\n", filter_type, filter_cond_type)); + switch(filter_type) { + case HCI_SET_EV_FILTER_CLEAR: + LWIP_DEBUGF(HCI_DEBUG, ("hci_set_event_filter: Clearing all filters\n")); + cond_len = 0x00; + break; + case HCI_SET_EV_FILTER_INQUIRY: + switch(filter_cond_type) { + case HCI_SET_EV_FILTER_ALLDEV: + cond_len = 0x00; + break; + case HCI_SET_EV_FILTER_COD: + cond_len = 0x06; + break; + case HCI_SET_EV_FILTER_BDADDR: + cond_len = 0x06; + break; + default: + LWIP_DEBUGF(HCI_DEBUG, ("hci_set_event_filter: Entered an unspecified filter condition type!\n")); + break; + } + break; + case HCI_SET_EV_FILTER_CONNECTION: + switch(filter_cond_type) { + case HCI_SET_EV_FILTER_ALLDEV: + cond_len = 0x01; + break; + case HCI_SET_EV_FILTER_COD: + cond_len = 0x07; + break; + case HCI_SET_EV_FILTER_BDADDR: + cond_len = 0x07; + break; + default: + LWIP_DEBUGF(HCI_DEBUG, ("hci_set_event_filter: Entered an unspecified filter condition type!\n")); + break; + } + break; + default: + LWIP_DEBUGF(HCI_DEBUG, ("hci_set_event_filter: Entered an unspecified filter type!\n")); + break; + } + if((p = pbuf_alloc(PBUF_RAW, HCI_SET_EV_FILTER_PLEN+cond_len, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_set_event_filter: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_SET_EV_FILTER_OCF, HCI_HC_BB_OGF, HCI_SET_EV_FILTER_PLEN+cond_len); + ((u8_t *)p->payload)[4] = filter_type; + ((u8_t *)p->payload)[5] = filter_cond_type; + /* Assembling cmd prameters */ + if(cond_len) { + MEMCPY(((u8_t *)p->payload)+6, cond, cond_len); + } + phybusif_output(p, p->tot_len); + pbuf_free(p); + return ERR_OK; +} + +/* hci_write_stored_link_key(): + * + * Writes a link key to be stored in the Bluetooth host controller. + */ +err_t hci_write_stored_link_key(struct bd_addr *bdaddr, u8_t *link) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_WRITE_STORED_LINK_KEY_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_write_stored_link_key: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_WRITE_STORED_LINK_KEY, HCI_HC_BB_OGF, HCI_WRITE_STORED_LINK_KEY_PLEN); + /* Assembling cmd prameters */ + ((u8_t *)p->payload)[4] = 0x01; + MEMCPY(((u8_t *)p->payload) + 5, bdaddr->addr, 6); + MEMCPY(((u8_t *)p->payload) + 11, link, 16); + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_change_local_name(): + * + * Writes a link key to be stored in the Bluetooth host controller. + */ +err_t hci_change_local_name(u8_t *name, u8_t len) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_CHANGE_LOCAL_NAME_PLEN + len, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_change_local_name: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_CHANGE_LOCAL_NAME, HCI_HC_BB_OGF, HCI_CHANGE_LOCAL_NAME_PLEN + len); + /* Assembling cmd prameters */ + MEMCPY(((u8_t *)p->payload) + 4, name, len); + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_write_page_timeout(): + * + * Define the amount of time a connection request will wait for the remote device + * to respond before the local device returns a connection failure. + */ +err_t hci_write_page_timeout(u16_t page_timeout) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_W_PAGE_TIMEOUT_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_write_page_timeout: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_W_PAGE_TIMEOUT_OCF, HCI_HC_BB_OGF, HCI_W_PAGE_TIMEOUT_PLEN); + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = page_timeout; + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_write_scan_enable(): + * + * Controls whether or not the Bluetooth device will periodically scan for page + * attempts and/or inquiry requests from other Bluetooth devices. + */ +err_t hci_write_scan_enable(u8_t scan_enable) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_W_SCAN_EN_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_write_scan_enable: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_W_SCAN_EN_OCF, HCI_HC_BB_OGF, HCI_W_SCAN_EN_PLEN); + /* Assembling cmd prameters */ + ((u8_t *)p->payload)[4] = scan_enable; + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_write_cod(): + * + * Write the value for the Class_of_Device parameter, which is used to indicate + * its capabilities to other devices. + */ +err_t hci_write_cod(u8_t *cod) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_W_COD_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_write_cod: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_W_COD_OCF, HCI_HC_BB_OGF, HCI_W_COD_PLEN); + /* Assembling cmd prameters */ + MEMCPY(((u8_t *)p->payload)+4, cod, 3); + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_set_hc_to_h_fc(): + * + * Used by the Host to turn flow control on or off in the direction from the Host + * Controller to the Host. + */ +err_t hci_set_hc_to_h_fc(void) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_SET_HC_TO_H_FC_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_set_hc_to_h_fc: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_SET_HC_TO_H_FC_OCF, HCI_HC_BB_OGF, HCI_SET_HC_TO_H_FC_PLEN); + /* Assembling cmd prameters */ + ((u8_t *)p->payload)[4] = 0x01; /* Flow control on for HCI ACL Data Packets and off for HCI + SCO Data Packets in direction from Host Controller to + Host */ + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_host_buffer_size(): + * + * Used by the Host to notify the Host Controller about the maximum size of the + * data portion of HCI ACL Data Packets sent from the Host Controller to the Host. + */ +err_t hci_host_buffer_size(void) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_H_BUF_SIZE_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_host_buffer_size: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_H_BUF_SIZE_OCF, HCI_HC_BB_OGF, HCI_H_BUF_SIZE_PLEN); + ((u16_t *)p->payload)[2] = HCI_HOST_ACL_MAX_LEN; /* Host ACL data packet maximum length */ + ((u8_t *)p->payload)[6] = 255; /* Host SCO Data Packet Length */ + *((u16_t *)(((u8_t *)p->payload)+7)) = HCI_HOST_MAX_NUM_ACL; /* Host max total num ACL data packets */ + ((u16_t *)p->payload)[4] = 1; /* Host Total Num SCO Data Packets */ + phybusif_output(p, p->tot_len); + pbuf_free(p); + + pcb->host_num_acl = HCI_HOST_MAX_NUM_ACL; + + return ERR_OK; +} + +/* hci_host_num_comp_packets(): + * + * Used by the Host to indicate to the Host Controller the number of HCI Data + * Packets that have been completed for each Connection Handle since the previous + * Host_Number_Of_Completed_Packets command was sent to the Host Controller. + */ +err_t hci_host_num_comp_packets(u16_t conhdl, u16_t num_complete) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_H_NUM_COMPL_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_host_num_comp_packets: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_H_NUM_COMPL_OCF, HCI_HC_BB_OGF, HCI_H_NUM_COMPL_PLEN); + ((u16_t *)p->payload)[2] = conhdl; + ((u16_t *)p->payload)[3] = num_complete; /* Number of completed acl packets */ + + phybusif_output(p, p->tot_len); + pbuf_free(p); + + pcb->host_num_acl += num_complete; + + return ERR_OK; +} + +/* hci_read_buffer_size(): + * + * Used to read the maximum size of the data portion of HCI ACL packets sent from the + * Host to the Host Controller. + */ +err_t hci_read_buffer_size(void) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_R_BUF_SIZE_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_read_buffer_size: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_R_BUF_SIZE_OCF, HCI_INFO_PARAM_OGF, HCI_R_BUF_SIZE_PLEN); + /* Assembling cmd prameters */ + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_read_local_features() + * + * Read the features of the connected module + */ +err_t hci_read_local_features(void) +{ + struct pbuf *p; + if((p = pbuf_alloc(PBUF_RAW, HCI_R_BUF_SIZE_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_read_buffer_size: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_R_SUPPORTED_LOCAL_FEATURES_OCF, HCI_INFO_PARAM_OGF, HCI_R_SUPPORTED_LOCAL_FEATURES_PLEN); + /* Assembling cmd prameters */ + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* hci_read_bd_addr(): + * + * Used to retreive the Bluetooth address of the host controller. + */ +err_t hci_read_bd_addr(err_t (* rbd_complete)(void *arg, struct bd_addr *bdaddr)) +{ + struct pbuf *p; + + pcb->rbd_complete = rbd_complete; + + if((p = pbuf_alloc(PBUF_RAW, HCI_R_BD_ADDR_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("hci_read_buffer_size: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_READ_BD_ADDR, HCI_INFO_PARAM_OGF, HCI_R_BD_ADDR_PLEN); + /* Assembling cmd prameters */ + phybusif_output(p, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/* lp_write_flush_timeout(): + * + * Called by L2CAP to set the flush timeout for the ACL link. + */ +err_t lp_write_flush_timeout(struct bd_addr *bdaddr, u16_t flushto) +{ + struct hci_link *link; + struct pbuf *p; + + /* Check if an ACL connection exists */ + link = hci_get_link(bdaddr); + + if(link == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("lp_write_flush_timeout: ACL connection does not exist\n")); + return ERR_CONN; + } + + if((p = pbuf_alloc(PBUF_TRANSPORT, HCI_W_FLUSHTO_PLEN, PBUF_RAM)) == NULL) { /* Alloc len of packet */ + LWIP_DEBUGF(HCI_DEBUG, ("lp_write_flush_timeout: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_W_FLUSHTO, HCI_HC_BB_OGF, HCI_W_FLUSHTO_PLEN); + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = link->conhdl; + ((u16_t *)p->payload)[3] = flushto; + + phybusif_output(p, p->tot_len); + pbuf_free(p); + return ERR_OK; +} + +/* lp_connect_req(): + * + * Called by L2CAP to cause the Link Manager to create a connection to the + * Bluetooth device with the BD_ADDR specified by the command parameters. + */ +err_t lp_connect_req(struct bd_addr *bdaddr, u8_t allow_role_switch) +{ + u8_t page_scan_repetition_mode, page_scan_mode; + u16_t clock_offset; + struct pbuf *p; + struct hci_link *link = hci_new(); + struct hci_inq_res *inqres; + + if(link == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("lp_connect_req: Could not allocate memory for link\n")); + return ERR_MEM; /* Could not allocate memory for link */ + } + + bd_addr_set(&(link->bdaddr), bdaddr); + HCI_REG(&(hci_active_links), link); + + + /* Check if module has been discovered in a recent inquiry */ + for(inqres = pcb->ires; inqres != NULL; inqres = inqres->next) { + if(bd_addr_cmp(&inqres->bdaddr, bdaddr)) { + page_scan_repetition_mode = inqres->psrm; + page_scan_mode = inqres->psm; + clock_offset = inqres->co; + break; + } + } + if(inqres == NULL) { + /* No information on parameters from an inquiry. Using default values */ + page_scan_repetition_mode = 0x01; /* Assuming worst case: time between + successive page scans starting + <= 2.56s */ + page_scan_mode = 0x00; /* Assumes the device uses mandatory scanning, most + devices use this. If no conn is established, try + again w this parm set to optional page scanning */ + clock_offset = 0x00; /* If the device was not found in a recent inquiry + this information is irrelevant */ + } + + if((p = pbuf_alloc(PBUF_RAW, HCI_CREATE_CONN_PLEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("lp_connect_req: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_CREATE_CONN_OCF, HCI_LINK_CTRL_OGF, HCI_CREATE_CONN_PLEN); + /* Assembling cmd prameters */ + MEMCPY(((u8_t *)p->payload)+4, bdaddr->addr, 6); + + ((u16_t *)p->payload)[5] = HCI_PACKET_TYPE; + ((u8_t *)p->payload)[12] = page_scan_repetition_mode; + ((u8_t *)p->payload)[13] = page_scan_mode; + ((u16_t *)p->payload)[7] = clock_offset; + ((u8_t *)p->payload)[16] = allow_role_switch; + phybusif_output(p, p->tot_len); + pbuf_free(p); + return ERR_OK; +} + +/* lp_acl_write(): + * + * Called by L2CAP to send data to the Host Controller that will be transfered over + * the ACL link from there. + */ +err_t lp_acl_write(struct bd_addr *bdaddr, struct pbuf *p, u16_t len, u8_t pb) +{ + struct hci_link *link; + static struct hci_acl_hdr *aclhdr; + struct pbuf *q; + + /* Check if an ACL connection exists */ + link = hci_get_link(bdaddr); + + if(link == NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("lp_acl_write: ACL connection does not exist\n")); + return ERR_CONN; + } + + LWIP_DEBUGF(HCI_DEBUG, ("lp_acl_write: HC num ACL %d\n", pcb->hc_num_acl)); + if(pcb->hc_num_acl == 0) { + LWIP_DEBUGF(HCI_DEBUG, ("lp_acl_write: HC out of buffer space\n")); +#if HCI_FLOW_QUEUEING + if(p != NULL) { + /* Packet can be queued? */ + if(link->p != NULL) { + LWIP_DEBUGF(HCI_DEBUG, ("lp_acl_write: Host buffer full. Dropped packet\n")); + return ERR_OK; /* Drop packet */ + } else { + /* Copy PBUF_REF referenced payloads into PBUF_RAM */ + p = pbuf_take(p); + /* Remember pbuf to queue, if any */ + link->p = p; + link->len = len; + link->pb = pb; + /* Pbufs are queued, increase the reference count */ + pbuf_ref(p); + LWIP_DEBUGF(HCI_DEBUG, ("lp_acl_write: Host queued packet %p\n", (void *)p)); + } + } +#else + LWIP_DEBUGF(HCI_DEBUG, ("lp_acl_write: Dropped packet\n")); +#endif + return ERR_OK; + } + + if((q = pbuf_alloc(PBUF_RAW, 1+HCI_ACL_HDR_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(HCI_DEBUG, ("lp_acl_write: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + pbuf_chain(q, p); + + ((u8_t*)q->payload)[0] = HCI_ACL_DATA_PACKET; + aclhdr = (void *)(((u8_t*)q->payload)+1); + aclhdr->conhdl_pb_bc = link->conhdl; /* Received from connection complete event */ + aclhdr->conhdl_pb_bc |= pb << 12; /* Packet boundary flag */ + aclhdr->conhdl_pb_bc &= 0x3FFF; /* Point-to-point */ + aclhdr->len = len; + + LWIP_DEBUGF(HCI_DEBUG, ("lp_acl_write: q->tot_len = %d aclhdr->len + q->len = %d\n", q->tot_len, aclhdr->len + q->len)); + + phybusif_output(q, aclhdr->len + q->len); + + --pcb->hc_num_acl; + + /* Free ACL header. Upper layers will handle rest of packet */ + p = pbuf_dechain(q); + pbuf_free(q); + return ERR_OK; +} + +/* lp_is_connected(): + * + * Called by L2CAP to check if an active ACL connection exists for the specified + * Bluetooth address. + */ +u8_t lp_is_connected(struct bd_addr *bdaddr) +{ + struct hci_link *link; + + link = hci_get_link(bdaddr); + + if(link == NULL) { + return 0; + } + return 1; +} + +/* lp_pdu_maxsize(): + * + * Called by L2CAP to check the maxsize of the PDU. In this case it is the largest + * ACL packet that the Host Controller can buffer. + */ +u16_t lp_pdu_maxsize(void) +{ + return pcb->maxsize; +} + + diff --git a/kernel/bluetooth/lwbt/l2cap.c b/kernel/bluetooth/lwbt/l2cap.c new file mode 100644 index 0000000..3c97f8d --- /dev/null +++ b/kernel/bluetooth/lwbt/l2cap.c @@ -0,0 +1,1573 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + + +/* l2cap.c + * + * Implementation of the logical link control and adaption protocol (L2CAP). Supports + * higher level protocol multiplexing, packet segmentation and reassembly, and the + * conveying of quality of service information. + */ + + +#include "lwbt/l2cap.h" +#include "lwbt/lwbt_memp.h" +#include "lwbt/lwbtopts.h" +#include "lwip/debug.h" + +/* Next Identifier to be sent */ +u8_t sigid_nxt; + +/* The L2CAP PCB lists. */ +struct l2cap_pcb_listen *l2cap_listen_pcbs; /* List of all L2CAP PCBs in CLOSED state + but awaiting an incoming conn req */ +struct l2cap_pcb *l2cap_active_pcbs; /* List of all L2CAP PCBs that are in a + state in which they accept or send + data */ +struct l2cap_pcb *l2cap_tmp_pcb; + +/* Temp signal */ +struct l2cap_sig *l2cap_tmp_sig; + +/* Global variable involved in input processing of l2cap data segements */ +struct l2cap_seg *l2cap_insegs; +struct l2cap_seg *l2cap_tmp_inseg; + +/* Forward declarations */ +static u16_t l2cap_cid_alloc(void); + + +/* + * l2cap_init(): + * + * Initializes the L2CAP layer. + */ + + void +l2cap_init(void) +{ + /* Clear globals */ + l2cap_listen_pcbs = NULL; + l2cap_active_pcbs = NULL; + l2cap_tmp_pcb = NULL; + l2cap_tmp_sig = NULL; + l2cap_insegs = NULL; + l2cap_tmp_inseg = NULL; + + /* Initialize the signal identifier (0x00 shall never be used) */ + sigid_nxt = 0x00; +} + +/* + * l2cap_tmr(): + * + * Called every 1s and implements the retransmission timer that + * removes a channel if it has been waiting for a request enough + * time. It also includes a configuration timer. + */ + + void +l2cap_tmr(void) +{ + struct l2cap_sig *sig; + struct l2cap_pcb *pcb; + err_t ret; + + /* Step through all of the active pcbs */ + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + /* Step through any unresponded signals */ + for(sig = pcb->unrsp_sigs; sig != NULL; sig = sig->next) { + /* Check if channel is not reliable */ + if(pcb->cfg.outflushto < 0xFFFF) { + /* Check if rtx is active. Otherwise ertx is active */ + if(sig->rtx > 0) { + /* Adjust rtx timer */ + --sig->rtx; + /* Check if rtx has expired */ + if(sig->rtx == 0) { + if(sig->nrtx == 0) { + /* Move pcb to closed state */ + pcb->state = L2CAP_CLOSED; + /* Indicate disconnect to upper layer */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_tmr: Max number of retransmissions (rtx) has expired\n")); + L2CA_ACTION_DISCONN_IND(pcb,ERR_OK,ret); + } else { + --sig->nrtx; + /* Indicate timeout to upper layer */ + L2CA_ACTION_TO_IND(pcb,ERR_OK,ret); + /* Retransmitt signal w timeout doubled */ + sig->rtx += sig->rtx; + ret = l2cap_rexmit_signal(pcb, sig); + } + } /* if */ + } else { + /* Adjust ertx timer */ + --sig->ertx; + /* Check if ertx has expired */ + if(sig->ertx == 0) { + if(sig->nrtx == 0) { + /* Move pcb to closed state */ + pcb->state = L2CAP_CLOSED; + /* Indicate disconnect to upper layer */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_tmr: Max number of retransmissions (ertx) has expired\n")); + L2CA_ACTION_DISCONN_IND(pcb,ERR_OK,ret); + } else { + --sig->nrtx; + /* Indicate timeout to upper layer */ + L2CA_ACTION_TO_IND(pcb,ERR_OK,ret); + /* Disable ertx, activate rtx and retransmitt signal */ + sig->ertx = 0; + sig->rtx = L2CAP_RTX; + ret = l2cap_rexmit_signal(pcb, sig); + } + } /* if */ + } /* else */ + } /* if */ + } /* for */ + + /* Check configuration timer */ + if(pcb->state == L2CAP_CONFIG) { + /* Check if configuration timer is active */ + if(pcb->cfg.cfgto > 0) { + --pcb->cfg.cfgto; + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_tmr: Configuration timer = %d\n", pcb->cfg.cfgto)); + /* Check if config timer has expired */ + if(pcb->cfg.cfgto == 0) { + /* Connection attempt failed. Disconnect */ + l2ca_disconnect_req(pcb, NULL); + /* Notify the application that the connection attempt failed */ + if(pcb->cfg.l2capcfg & L2CAP_CFG_IR) { + L2CA_ACTION_CONN_CFM(pcb, L2CAP_CONN_CFG_TO, 0x0000, ret); + } else { + L2CA_ACTION_CONN_IND(pcb, ERR_OK, ret); + } + pcb->cfg.cfgto = L2CAP_CFG_TO; /* Reset timer */ + } + } + } + } /* for */ +} + +/* + * l2cap_write(): + * + * Output L2CAP data to the lower layers. Segments the packet in to PDUs. + */ + + err_t +l2cap_write(struct bd_addr *bdaddr, struct pbuf *p, u16_t len) +{ + u8_t pb = L2CAP_ACL_START; + u16_t maxsize; + u16_t outsize; + err_t ret = ERR_OK; + struct pbuf *q; + u16_t i = 0; + + /*u16_t i; + struct pbuf *q; + for(q = p; q != NULL; q = q->next) { + for(i = 0; i < q->len; ++i) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: 0x%x\n", ((u8_t *)q->payload)[i])); + } + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: *\n")); + } + */ + + maxsize = lp_pdu_maxsize(); + q = p; + + while(len && ret == ERR_OK) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: len %d maxsize %d p->len %d\n", len, maxsize, p->len)); + if(len > maxsize) { + ret = lp_acl_write(bdaddr, q, maxsize, pb); + len -= maxsize; + outsize = maxsize; + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: Outsize before %d\n", outsize)); + while(q->len < outsize) { + outsize -= q->len; + q = q->next; + } + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: Outsize after %d\n", outsize)); + if(outsize) { + pbuf_header(q, -outsize); + i += outsize; + } + pb = L2CAP_ACL_CONT; + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: FRAG\n")); + } else { + ret = lp_acl_write(bdaddr, q, len, pb); + len = 0; + } + } + pbuf_header(q, i); + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: DONE\n")); + return ret; +} + +/* + * l2cap_process_sig(): + * + * Parses the received message handles it. + */ + + void +l2cap_process_sig(struct pbuf *q, struct l2cap_hdr *l2caphdr, struct bd_addr *bdaddr) +{ + struct l2cap_sig_hdr *sighdr; + struct l2cap_sig *sig = NULL; + struct l2cap_pcb *pcb = NULL; + struct l2cap_pcb_listen *lpcb; + struct l2cap_cfgopt_hdr *opthdr; + u16_t result, status, flags, psm, dcid, scid; + u16_t len; + u16_t siglen; + struct pbuf *p, *r = NULL, *s = NULL, *data; + err_t ret; + u8_t i; + u16_t rspstate = L2CAP_CFG_SUCCESS; + + if(q->len != q->tot_len) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Fragmented packet received. Reassemble into one buffer\n")); + if((p = pbuf_alloc(PBUF_RAW, q->tot_len, PBUF_RAM)) != NULL) { + i = 0; + for(r = q; r != NULL; r = r->next) { + memcpy(((u8_t *)p->payload) + i, r->payload, r->len); + i += r->len; + } + } else { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Could not allocate buffer for fragmented packet\n")); + return; + } + } else { + p = q; + } + + len = l2caphdr->len; + + while(len > 0) { + /* Set up signal header */ + sighdr = p->payload; + pbuf_header(p, -L2CAP_SIGHDR_LEN); + + /* Check if this is a response/reject signal, and if so, find the matching request */ + if(sighdr->code % 2) { /* if odd this is a resp/rej signal */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Response/reject signal received id = %d code = %d\n", + sighdr->id, sighdr->code)); + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + for(sig = pcb->unrsp_sigs; sig != NULL; sig = sig->next) { + if(sig->sigid == sighdr->id) { + break; /* found */ + } + } + if(sig != NULL) { + break; + } + } + } else { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Request signal received id = %d code = %d\n", + sighdr->id, sighdr->code)); + } + + /* Reject packet if length exceeds MTU */ + if(l2caphdr->len > L2CAP_MTU) { + /* Alloc size of reason in cmd rej + MTU */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE+2, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = L2CAP_MTU_EXCEEDED; + ((u16_t *)data->payload)[1] = L2CAP_MTU; + + l2cap_signal(NULL, L2CAP_CMD_REJ, sighdr->id, bdaddr, data); + } + break; + } + + switch(sighdr->code) { + case L2CAP_CMD_REJ: + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + pbuf_free(sig->p); + lwbt_memp_free(MEMP_L2CAP_SIG, sig); + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Our command was rejected so we disconnect\n")); + l2ca_disconnect_req(pcb, NULL); + break; + case L2CAP_CONN_REQ: + psm = ((u16_t *)p->payload)[0]; + /* Search for a listening pcb */ + for(lpcb = l2cap_listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if(lpcb->psm == psm) { + /* Found a listening pcb with the correct PSM */ + break; + } + } + /* If no matching pcb was found, send a connection rsp neg (PSM) */ + if(lpcb == NULL) { + /* Alloc size of data in conn rsp signal */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CONN_RSP_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = L2CAP_CONN_REF_PSM; + ((u16_t *)data->payload)[1] = 0; /* No further info available */ + ret = l2cap_signal(pcb, L2CAP_CONN_RSP, sighdr->id, &(pcb->remote_bdaddr), data); + } + } else { + /* Initiate a new active pcb */ + pcb = l2cap_new(); + if(pcb == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: could not allocate PCB\n")); + /* Send a connection rsp neg (no resources available) and alloc size of data in conn rsp + signal */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CONN_RSP_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = L2CAP_CONN_REF_RES; + ((u16_t *)data->payload)[1] = 0; /* No further info available */ + ret = l2cap_signal(pcb, L2CAP_CONN_RSP, sighdr->id, &(pcb->remote_bdaddr), data); + } + } + bd_addr_set(&(pcb->remote_bdaddr),bdaddr); + + pcb->scid = l2cap_cid_alloc(); + pcb->dcid = ((u16_t *)p->payload)[1]; + pcb->psm = psm; + pcb->callback_arg = lpcb->callback_arg; + pcb->l2ca_connect_ind = lpcb->l2ca_connect_ind; + + pcb->state = L2CAP_CONFIG; + L2CAP_REG(&l2cap_active_pcbs, pcb); + + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: A connection request was received. Send a response\n")); + data = pbuf_alloc(PBUF_RAW, L2CAP_CONN_RSP_SIZE, PBUF_RAM); + if(data == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_connect_rsp: Could not allocate memory for pbuf\n")); + break; + } + ((u16_t *)data->payload)[0] = pcb->scid; + ((u16_t *)data->payload)[1] = pcb->dcid; + ((u16_t *)data->payload)[2] = L2CAP_CONN_SUCCESS; + ((u16_t *)data->payload)[3] = 0x0000; /* No further information available */ + + /* Send the response */ + ret = l2cap_signal(pcb, L2CAP_CONN_RSP, sighdr->id, &(pcb->remote_bdaddr), data); + } + break; + case L2CAP_CONN_RSP: + if(pcb == NULL) { + /* A response without a matching request is silently discarded */ + break; + } + LWIP_ASSERT("l2cap_process_sig: conn rsp, active pcb->state == W4_L2CAP_CONNECT_RSP\n", + pcb->state == W4_L2CAP_CONNECT_RSP); + result = ((u16_t *)p->payload)[2]; + status = ((u16_t *)p->payload)[3]; + switch(result) { + case L2CAP_CONN_SUCCESS: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Conn_rsp_sucess, status %d\n", status)); + + LWIP_ASSERT("l2cap_process_sig: conn rsp success, pcb->scid == ((u16_t *)p->payload)[1]\n", + pcb->scid == ((u16_t *)p->payload)[1]); + + /* Set destination connection id */ + pcb->dcid = ((u16_t *)p->payload)[0]; + + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + pbuf_free(sig->p); + lwbt_memp_free(MEMP_L2CAP_SIG, sig); + + /* Configure connection */ + pcb->state = L2CAP_CONFIG; + + /* If initiator send a configuration request */ + if(pcb->cfg.l2capcfg & L2CAP_CFG_IR) { + l2ca_config_req(pcb); + pcb->cfg.l2capcfg |= L2CAP_CFG_OUT_REQ; + } + break; + case L2CAP_CONN_PND: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Conn_rsp_pnd, status %d\n", status)); + + /* Disable rtx and enable ertx */ + sig->rtx = 0; + sig->ertx = L2CAP_ERTX; + break; + default: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Conn_rsp_neg, result %d\n", result)); + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + pbuf_free(sig->p); + lwbt_memp_free(MEMP_L2CAP_SIG, sig); + + L2CA_ACTION_CONN_CFM(pcb,result,status,ret); + break; + } + break; + case L2CAP_CFG_REQ: + siglen = sighdr->len; + dcid = ((u16_t *)p->payload)[0]; + flags = ((u16_t *)p->payload)[1]; + siglen -= 4; + pbuf_header(p, -4); + + + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Congfiguration request, flags = %d\n", flags)); + + /* Find PCB with matching cid */ + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: dcid = 0x%x, pcb->scid = 0x%x, pcb->dcid = 0x%x\n\n", dcid, pcb->scid, pcb->dcid)); + if(pcb->scid == dcid) { + /* Matching cid found */ + break; + } + } + /* If no matching cid was found, send a cmd reject (Invalid cid) */ + if(pcb == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Cfg req: no matching cid was found\n")); + /* Alloc size of reason in cmd rej + data (dcid + scid) */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE+4, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = L2CAP_INVALID_CID; + ((u16_t *)data->payload)[1] = dcid; /* Requested local cid */ + ((u16_t *)data->payload)[2] = L2CAP_NULL_CID; /* Remote cid not known */ + + ret = l2cap_signal(NULL, L2CAP_CMD_REJ, sighdr->id, bdaddr, data); + } + } else { /* Handle config request */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Handle configuration request\n")); + pcb->ursp_id = sighdr->id; /* Set id of request to respond to */ + + /* Parse options and add to pcb */ + while(siglen > 0) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Siglen = %d\n", siglen)); + opthdr = p->payload; + /* Check if type of action bit indicates a non-hint. Hints are ignored */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Type of action bit = %d\n", L2CAP_OPTH_TOA(opthdr))); + if(L2CAP_OPTH_TOA(opthdr) == 0) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Type = %d\n", L2CAP_OPTH_TYPE(opthdr))); + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Length = %d\n", opthdr->len)); + switch(L2CAP_OPTH_TYPE(opthdr)) { + case L2CAP_CFG_MTU: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Out MTU = %d\n", ((u16_t *)p->payload)[1])); + pcb->cfg.outmtu = ((u16_t *)p->payload)[1]; + break; + case L2CAP_FLUSHTO: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: In flush timeout = %d\n", ((u16_t *)p->payload)[1])); + pcb->cfg.influshto = ((u16_t *)p->payload)[1]; + break; + case L2CAP_QOS: + /* If service type is Best Effort or No Traffic the remainder fields will be ignored */ + if(((u8_t *)p->payload)[3] == L2CAP_QOS_GUARANTEED) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: This implementation does not support the guaranteed QOS service type")); + if(rspstate == L2CAP_CFG_SUCCESS) { + rspstate = L2CAP_CFG_UNACCEPT; + if(pcb->cfg.opt != NULL) { + pbuf_free(pcb->cfg.opt); + pcb->cfg.opt = NULL; + } + } + s = pbuf_alloc(PBUF_RAW, L2CAP_CFGOPTHDR_LEN + opthdr->len, PBUF_RAM); + memcpy((u8_t *)s->payload, (u8_t *)p->payload, L2CAP_CFGOPTHDR_LEN + opthdr->len); + if(pcb->cfg.opt == NULL) { + pcb->cfg.opt = s; + } else { + pbuf_chain(pcb->cfg.opt, s); + pbuf_free(s); + } + } + break; + default: + if(rspstate != L2CAP_CFG_REJ) { + /* Unknown option. Add to unknown option type buffer */ + if(rspstate != L2CAP_CFG_UNKNOWN) { + rspstate = L2CAP_CFG_UNKNOWN; + if(pcb->cfg.opt != NULL) { + pbuf_free(pcb->cfg.opt); + pcb->cfg.opt = NULL; + } + } + s = pbuf_alloc(PBUF_RAW, L2CAP_CFGOPTHDR_LEN + opthdr->len, PBUF_RAM); + memcpy((u8_t *)s->payload, (u8_t *)p->payload, L2CAP_CFGOPTHDR_LEN + opthdr->len); + if(pcb->cfg.opt == NULL) { + pcb->cfg.opt = s; + } else { + pbuf_chain(pcb->cfg.opt, s); + pbuf_free(s); + } + } + break; + } /* switch */ + } /* if(L2CAP_OPTH_TOA(opthdr) == 0) */ + pbuf_header(p, -(L2CAP_CFGOPTHDR_LEN + opthdr->len)); + siglen -= L2CAP_CFGOPTHDR_LEN + opthdr->len; + } /* while */ + + /* If continuation flag is set we don't send the final response just yet */ + if((flags & 0x0001) == 1) { + /* Send success result with no options until the full request has been received */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CFG_RSP_SIZE, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Could not allocate memory for pbuf\n")); + break; + } + ((u16_t *)data->payload)[0] = pcb->dcid; + ((u16_t *)data->payload)[1] = 0; + ((u16_t *)data->payload)[2] = L2CAP_CFG_SUCCESS; + ret = l2cap_signal(pcb, L2CAP_CFG_RSP, pcb->ursp_id, &(pcb->remote_bdaddr), data); + break; + } + + /* Send a configure request for outgoing link if it hasnt been configured */ + if(!(pcb->cfg.l2capcfg & L2CAP_CFG_IR) && !(pcb->cfg.l2capcfg & L2CAP_CFG_OUT_REQ)) { + l2ca_config_req(pcb); + pcb->cfg.l2capcfg |= L2CAP_CFG_OUT_REQ; + } + + /* Send response to configuration request */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Send response to configuration request\n")); + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CFG_RSP_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = pcb->dcid; + ((u16_t *)data->payload)[1] = 0; /* Flags (No continuation) */ + ((u16_t *)data->payload)[2] = rspstate; /* Result */ + if(pcb->cfg.opt != NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: pcb->cfg.opt->len = %d\n", pcb->cfg.opt->len)); + pbuf_chain(data, pcb->cfg.opt); /* Add option type buffer to data buffer */ + pbuf_free(pcb->cfg.opt); + pcb->cfg.opt = NULL; + } + ret = l2cap_signal(pcb, L2CAP_CFG_RSP, pcb->ursp_id, &(pcb->remote_bdaddr), data); + } + + if(rspstate == L2CAP_CFG_SUCCESS) { + pcb->cfg.l2capcfg |= L2CAP_CFG_OUT_SUCCESS; + /* L2CAP connection established if a successful configuration response has been sent */ + if(pcb->cfg.l2capcfg & L2CAP_CFG_IN_SUCCESS) { + /* IPCP connection established, notify upper layer that connection is open */ + pcb->state = L2CAP_OPEN; + if(pcb->cfg.l2capcfg & L2CAP_CFG_IR) { + L2CA_ACTION_CONN_CFM(pcb, L2CAP_CONN_SUCCESS, 0x0000, ret); + } else { + L2CA_ACTION_CONN_IND(pcb, ERR_OK, ret); + } + } + } + } /* else */ + break; + case L2CAP_CFG_RSP: + if(pcb == NULL) { + /* A response without a matching request is silently discarded */ + break; + } + + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + pbuf_free(sig->p); + lwbt_memp_free(MEMP_L2CAP_SIG, sig); + + LWIP_ASSERT(("l2cap_process_sig: cfg rsp, active pcb->state == L2CAP_CONFIG\n"), + pcb->state == L2CAP_CONFIG); + + siglen = sighdr->len; + scid = ((u16_t *)p->payload)[0]; + flags = ((u16_t *)p->payload)[1]; + result = ((u16_t *)p->payload)[2]; + siglen -= 6; + pbuf_header(p, -6); + + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Outgoing configuration result == %d continuation flag == %d\n", result, flags)); + + /* Handle config request */ + switch(result) { + case L2CAP_CFG_SUCCESS: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Successfull outgoing configuration\n")); + pcb->cfg.l2capcfg |= L2CAP_CFG_IN_SUCCESS; /* Local side of the connection + has been configured for outgoing data */ + pcb->cfg.cfgto = L2CAP_CFG_TO; /* Reset configuration timeout */ + + if(pcb->cfg.outflushto != L2CAP_CFG_DEFAULT_OUTFLUSHTO) { + lp_write_flush_timeout(&pcb->remote_bdaddr, pcb->cfg.outflushto); + } + + /* L2CAP connection established if a successful configuration response has been sent */ + if(pcb->cfg.l2capcfg & L2CAP_CFG_OUT_SUCCESS) { + pcb->state = L2CAP_OPEN; + if(pcb->cfg.l2capcfg & L2CAP_CFG_IR) { + L2CA_ACTION_CONN_CFM(pcb, L2CAP_CONN_SUCCESS, 0x0000, ret); + } else { + L2CA_ACTION_CONN_IND(pcb, ERR_OK, ret); + } + } + break; + case L2CAP_CFG_UNACCEPT: + /* Parse and add options to pcb */ + while(siglen > 0) { + opthdr = p->payload; + /* Check if type of action bit indicates a non-hint. Hints are ignored */ + if(L2CAP_OPTH_TOA(opthdr) == 0) { + switch(L2CAP_OPTH_TYPE(opthdr)) { + case L2CAP_CFG_MTU: + if(L2CAP_MTU > ((u16_t *)p->payload)[1]) { + pcb->cfg.outmtu = ((u16_t *)p->payload)[1]; + } else { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Configuration of MTU failed\n")); + l2ca_disconnect_req(pcb, NULL); + return; + } + break; + case L2CAP_FLUSHTO: + pcb->cfg.influshto = ((u16_t *)p->payload)[1]; + break; + case L2CAP_QOS: + /* If service type Best Effort is not accepted we will close the connection */ + if(((u8_t *)p->payload)[3] != L2CAP_QOS_BEST_EFFORT) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Unsupported service type\n")); + l2ca_disconnect_req(pcb, NULL); + return; + } + break; + default: + /* Should not happen, skip option */ + break; + } /* switch */ + } /* if(L2CAP_OPTH_TOA(opthdr) == 0) */ + pbuf_header(p, -(L2CAP_CFGOPTHDR_LEN + opthdr->len)); + siglen -= L2CAP_CFGOPTHDR_LEN + opthdr->len; + } /* while */ + + /* Send out a new configuration request if the continuation flag isn't set */ + if((flags & 0x0001) == 0) { + l2ca_config_req(pcb); + } + break; + case L2CAP_CFG_REJ: + /* Fallthrough */ + case L2CAP_CFG_UNKNOWN: + /* Fallthrough */ + default: + if((flags & 0x0001) == 0) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Configuration failed\n")); + l2ca_disconnect_req(pcb, NULL); + return; + } + break; + } /* switch(result) */ + + /* If continuation flag is set we must send a NULL configuration request */ + if((flags & 0x0001) == 1) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Continuation flag is set. Send empty (default) config request signal\n")); + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CFG_REQ_SIZE, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Could not allocate memory for pbuf\n")); + return; + } + /* Assemble config request packet */ + ((u16_t *)data->payload)[0] = pcb->scid; + ((u16_t *)data->payload)[2] = 0; + l2cap_signal(pcb, L2CAP_CFG_REQ, 0, &(pcb->remote_bdaddr), data); + } + break; + case L2CAP_DISCONN_REQ: + siglen = sighdr->len; + dcid = ((u16_t *)p->payload)[0]; + siglen = siglen - 2; + flags = ((u16_t *)p->payload)[1]; + siglen = siglen - 2; + pbuf_header(p, -4); + + /* Find PCB with matching cid */ + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->scid == dcid) { + /* Matching cid found */ + break; + } + } + /* If no matching cid was found, send a cmd reject (Invalid cid) */ + if(pcb == NULL) { + /* Alloc size of reason in cmd rej + data (dcid + scid) */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE+4, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = L2CAP_INVALID_CID; + ((u16_t *)data->payload)[1] = dcid; /* Requested local cid */ + ((u16_t *)data->payload)[2] = L2CAP_NULL_CID; /* Remote cid not known */ + + ret = l2cap_signal(NULL, L2CAP_CMD_REJ, sighdr->id, bdaddr, data); + } + } else { /* Handle disconnection request */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_DISCONN_RSP_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = pcb->scid; + ((u16_t *)data->payload)[1] = pcb->dcid; + ret = l2cap_signal(pcb, L2CAP_DISCONN_RSP, sighdr->id, &(pcb->remote_bdaddr), data); + + /* Give upper layer indication */ + pcb->state = L2CAP_CLOSED; + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_process_sig: Disconnection request\n")); + L2CA_ACTION_DISCONN_IND(pcb,ERR_OK,ret); + } + } + break; + case L2CAP_DISCONN_RSP: + if(pcb == NULL) { + /* A response without a matching request is silently discarded */ + break; + } + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + pbuf_free(sig->p); + lwbt_memp_free(MEMP_L2CAP_SIG, sig); + + L2CA_ACTION_DISCONN_CFM(pcb,ret); /* NOTE: Application should + now close the connection */ + break; + case L2CAP_ECHO_REQ: + if( pcb != NULL) + { + pcb->ursp_id = sighdr->id; + ret = l2cap_signal(pcb, L2CAP_ECHO_RSP, sighdr->id, &(pcb->remote_bdaddr), p); + } else { + ret = l2cap_signal(NULL, L2CAP_ECHO_RSP, sighdr->id, bdaddr, p); + } + pbuf_free(p); + break; + case L2CAP_ECHO_RSP: + if(pcb == NULL) { + /* A response without a matching request is silently discarded */ + break; + } + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + pbuf_free(sig->p); + lwbt_memp_free(MEMP_L2CAP_SIG, sig); + + /* Remove temporary pcb from active list */ + L2CAP_RMV(&l2cap_active_pcbs, pcb); + L2CA_ACTION_PING_CFM(pcb,L2CAP_ECHO_RCVD,ret); + break; + default: + /* Alloc size of reason in cmd rej */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = L2CAP_CMD_NOT_UNDERSTOOD; + + ret = l2cap_signal(NULL, L2CAP_CMD_REJ, sighdr->id, bdaddr, data); + } + break; + } /* switch */ + len = len - (sighdr->len + L2CAP_SIGHDR_LEN); + pbuf_header(p, -(sighdr->len)); + } /* while */ +} + +/* + * l2cap_input(): + * + * Called by the lower layer. Reassembles the packet, parses the header and forward + * it to the upper layer or the signal handler. + */ + + void +l2cap_input(struct pbuf *p, struct bd_addr *bdaddr) +{ + struct l2cap_seg *inseg; + struct hci_acl_hdr *aclhdr; + struct pbuf *data; + err_t ret; + + pbuf_header(p, HCI_ACL_HDR_LEN); + aclhdr = p->payload; + pbuf_header(p, -HCI_ACL_HDR_LEN); + + pbuf_realloc(p, aclhdr->len); + + for(inseg = l2cap_insegs; inseg != NULL; inseg = inseg->next) { + if(bd_addr_cmp(bdaddr, &(inseg->bdaddr))) { + break; + } + } + + /* Reassembly procedures */ + /* Check if continuing fragment or start of L2CAP packet */ + if(((aclhdr->conhdl_pb_bc >> 12) & 0x03)== L2CAP_ACL_CONT) { /* Continuing fragment */ + if(inseg == NULL) { + /* Discard packet */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_input: Continuing fragment. Discard packet\n")); + pbuf_free(p); + return; + } else if(inseg->p->tot_len + p->tot_len > inseg->len) { /* Check if length of + segment exceeds + l2cap header length */ + /* Discard packet */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_input: Continuing fragment. Length exceeds L2CAP hdr length. Discard packet\n")); + pbuf_free(inseg->p); + L2CAP_SEG_RMV(&(l2cap_insegs), inseg); + lwbt_memp_free(MEMP_L2CAP_SEG, inseg); + + pbuf_free(p); + return; + } + /* Add pbuf to segement */ + pbuf_chain(inseg->p, p); + pbuf_free(p); + + } else if(((aclhdr->conhdl_pb_bc >> 12) & 0x03) == L2CAP_ACL_START) { /* Start of L2CAP packet */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_input: Start of L2CAP packet p->len = %d, p->tot_len = %d\n", + p->len, p->tot_len)); + if(inseg != NULL) { /* Check if there are segments missing in a previous packet */ + /* Discard previous packet */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_input: Start of L2CAP packet. Discard previous packet\n")); + pbuf_free(inseg->p); + } else { + inseg = lwbt_memp_malloc(MEMP_L2CAP_SEG); + bd_addr_set(&(inseg->bdaddr), bdaddr); + L2CAP_SEG_REG(&(l2cap_insegs), inseg); + } + inseg->p = p; + inseg->l2caphdr = p->payload; + inseg->len = inseg->l2caphdr->len + L2CAP_HDR_LEN; + for(inseg->pcb = l2cap_active_pcbs; inseg->pcb != NULL; inseg->pcb = inseg->pcb->next) { + if(inseg->pcb->scid == inseg->l2caphdr->cid) { + break; /* found */ + } + } + } else { + /* Discard packet */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_input: Discard packet\n")); + pbuf_free(inseg->p); + L2CAP_SEG_RMV(&(l2cap_insegs), inseg); + lwbt_memp_free(MEMP_L2CAP_SEG, inseg); + + pbuf_free(p); + return; + } + if(inseg->p->tot_len < inseg->len) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_input: Get continuing segments\n")); + return; /* Get continuing segments */ + } + + /* Handle packet */ + switch(inseg->l2caphdr->cid) { + case L2CAP_NULL_CID: + /* Illegal */ + LWIP_DEBUGF(L2CAP_DEBUG,("l2cap_input: Illegal null cid\n")); + pbuf_free(inseg->p); + break; + case L2CAP_SIG_CID: + pbuf_header(inseg->p, -L2CAP_HDR_LEN); + l2cap_process_sig(inseg->p, inseg->l2caphdr, bdaddr); + pbuf_free(inseg->p); + break; + case L2CAP_CONNLESS_CID: + /* Not needed by PAN, LAN access or DUN profiles */ + pbuf_free(inseg->p); + break; + default: + if(inseg->l2caphdr->cid < 0x0040 || inseg->pcb == NULL) { + /* Reserved for specific L2CAP functions or channel does not exist */ + /* Alloc size of reason in cmd rej */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE+4, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = L2CAP_INVALID_CID; + ((u16_t *)data->payload)[1] = inseg->l2caphdr->cid; + ((u16_t *)data->payload)[2] = L2CAP_NULL_CID; + + ret = l2cap_signal(NULL, L2CAP_CMD_REJ, l2cap_next_sigid(), bdaddr, data); + } + pbuf_free(inseg->p); + break; + } + + pbuf_header(inseg->p, -L2CAP_HDR_LEN); + + /* Forward packet to higher layer */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_input: Forward packet to higher layer\n")); + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_input: Remote BD address: %02x:%02x:%02x:%02x:%02x:%02x\n", + inseg->pcb->remote_bdaddr.addr[5], + inseg->pcb->remote_bdaddr.addr[4], + inseg->pcb->remote_bdaddr.addr[3], + inseg->pcb->remote_bdaddr.addr[2], + inseg->pcb->remote_bdaddr.addr[1], + inseg->pcb->remote_bdaddr.addr[0])); + + L2CA_ACTION_RECV(inseg->pcb,inseg->p,ERR_OK,ret); + break; + } + + /* Remove input segment */ + L2CAP_SEG_RMV(&(l2cap_insegs), inseg); + lwbt_memp_free(MEMP_L2CAP_SEG, inseg); + +} + +/* + * l2cap_cid_alloc(): + * + * Allocates a channel identifier (CID). They are local names representing a logical + * channel endpoint on the device. + */ + + static u16_t +l2cap_cid_alloc(void) +{ + u16_t cid; + struct l2cap_pcb *pcb; + + for (cid = L2CAP_MIN_CID; cid < L2CAP_MAX_CID; ++cid) { + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->scid == cid) { + break; + } + } + if(pcb == NULL) { + return cid; + } + } + return 0; +} + +/* + * l2cap_new(): + * + * Creates a new L2CAP protocol control block but doesn't place it on + * any of the L2CAP PCB lists. + */ + + struct l2cap_pcb * +l2cap_new(void) +{ + struct l2cap_pcb *pcb; + + pcb = lwbt_memp_malloc(MEMP_L2CAP_PCB); + if(pcb != NULL) { + memset(pcb, 0, sizeof(struct l2cap_pcb)); + pcb->state = L2CAP_CLOSED; + + /* Initialize configuration parameter options with default values */ + + /* Maximum Transmission Unit */ + pcb->cfg.inmtu = L2CAP_MTU; /* The MTU that this implementation support */ + pcb->cfg.outmtu = 672; /* Default MTU. Two Baseband DH5 packets minus the Baseband ACL headers and + L2CAP header. This can be set here since we will never send any signals + larger than the L2CAP sig MTU (48 bytes) before L2CAP has been configured + */ + + /* Flush Timeout */ + pcb->cfg.influshto = 0xFFFF; + pcb->cfg.outflushto = 0xFFFF; + + pcb->cfg.cfgto = L2CAP_CFG_TO; /* Maximum time before terminating a negotiation. + Cfg shall not last more than 120s */ + pcb->cfg.opt = NULL; + return pcb; + } + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_new: Could not allocate memory for pcb\n")); + return NULL; +} + +/* + * l2cap_close(): + * + * Closes the L2CAP protocol control block. + */ + + err_t +l2cap_close(struct l2cap_pcb *pcb) +{ + struct l2cap_sig *tmpsig; + + if(pcb->state == L2CAP_LISTEN) { + L2CAP_RMV((struct l2cap_pcb **)&l2cap_listen_pcbs, pcb); + lwbt_memp_free(MEMP_L2CAP_PCB_LISTEN, pcb); + } else { + L2CAP_RMV(&l2cap_active_pcbs, pcb); + /* Free any unresponded signals */ + while(pcb->unrsp_sigs != NULL) { + tmpsig = pcb->unrsp_sigs; + pcb->unrsp_sigs = pcb->unrsp_sigs->next; + lwbt_memp_free(MEMP_L2CAP_SIG, tmpsig); + } + + lwbt_memp_free(MEMP_L2CAP_PCB, pcb); + } + pcb = NULL; + return ERR_OK; +} + +/* + * l2cap_reset_all(): + * + * Closes all active and listening L2CAP protocol control blocks. + */ + + void +l2cap_reset_all(void) +{ + struct l2cap_pcb *pcb, *tpcb; + struct l2cap_pcb_listen *lpcb, *tlpcb; + struct l2cap_seg *seg, *tseg; + + for(pcb = l2cap_active_pcbs; pcb != NULL;) { + tpcb = pcb->next; + l2cap_close(pcb); + pcb = tpcb; + } + + for(lpcb = l2cap_listen_pcbs; lpcb != NULL;) { + tlpcb = lpcb->next; + l2cap_close((struct l2cap_pcb *)lpcb); + lpcb = tlpcb; + } + + for(seg = l2cap_insegs; seg != NULL;) { + tseg = seg->next; + L2CAP_SEG_RMV(&(l2cap_insegs), seg); + lwbt_memp_free(MEMP_L2CAP_SEG, seg); + seg = tseg; + } + + l2cap_init(); +} + +/* L2CAP to L2CAP signalling events +*/ + + +/* + * l2cap_signal(): + * + * Assembles the signalling packet and passes it to the lower layer. + */ + + err_t +l2cap_signal(struct l2cap_pcb *pcb, u8_t code, u16_t ursp_id, struct bd_addr *remote_bdaddr, + struct pbuf *data) +{ + struct l2cap_sig *sig; + struct l2cap_sig_hdr *sighdr; + struct l2cap_hdr *hdr; + err_t ret; + + /* Alloc a new signal */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_signal: Allocate memory for l2cap_sig. Code = 0x%x\n", code)); + if((sig = lwbt_memp_malloc(MEMP_L2CAP_SIG)) == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_signal: could not allocate memory for l2cap_sig\n")); + return ERR_MEM; + } + + /* Alloc a pbuf for signal */ + if((sig->p = pbuf_alloc(PBUF_RAW, L2CAP_HDR_LEN+L2CAP_SIGHDR_LEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_signal: could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Setup signal header and leave room for l2cap hdr */ + sighdr = (struct l2cap_sig_hdr *)(((u8_t *)sig->p->payload)+L2CAP_HDR_LEN); + + /* Chain data to signal and set length of signal data */ + if(data == NULL) { + sighdr->len = 0; + } else { + pbuf_chain(sig->p, data); + pbuf_free(data); + sighdr->len = data->tot_len; + } + + sighdr->code = code; + + if(sighdr->code % 2) { /* If odd this is a resp/rej signal */ + sig->sigid = ursp_id; /* Get id */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_signal: Sending response/reject signal with id = %d code = %d\n", + sig->sigid, sighdr->code)); + } else { + sig->sigid = l2cap_next_sigid(); /* Alloc id */ + sig->rtx = L2CAP_RTX; /* Set Response Timeout Expired timer (in seconds) + should be at least as large as the BB flush timeout */ + sig->nrtx = L2CAP_MAXRTX; /* Set max number of retransmissions */ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_signal: Sending request signal with id = %d code = %d\n", + sig->sigid, sighdr->code)); + } + sighdr->id = sig->sigid; /* Set id */ + + /* Set up L2CAP hdr */ + hdr = sig->p->payload; + hdr->len = sig->p->tot_len - L2CAP_HDR_LEN; + hdr->cid = L2CAP_SIG_CID; /* 0x0001 */ + + ret = l2cap_write(remote_bdaddr, sig->p, sig->p->tot_len); /* Send peer L2CAP signal */ + + /* Put signal on unresponded list if it's a request signal, else deallocate it */ + if(ret == ERR_OK && (sighdr->code % 2) == 0) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_signal: Registering sent request signal with id = %d code = %d\n", + sig->sigid, sighdr->code)); + L2CAP_SIG_REG(&(pcb->unrsp_sigs), sig); + } else { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_signal: Deallocating sent response/reject signal with id = %d code = %d\n", + sig->sigid, sighdr->code)); + pbuf_free(sig->p); + sig->p = NULL; + lwbt_memp_free(MEMP_L2CAP_SIG, sig); + } + + return ret; +} + +/* + * l2cap_rexmit_signal(): + * + * Called by the l2cap timer. Retransmitts a signal. + */ + + err_t +l2cap_rexmit_signal(struct l2cap_pcb *pcb, struct l2cap_sig *sig) +{ + err_t ret; + + /* Set up L2CAP hdr */ + ret = l2cap_write(&(pcb->remote_bdaddr), sig->p, sig->p->tot_len); /* Send peer L2CAP signal */ + + return ret; +} + +/* Upper-Layer to L2CAP signaling events +*/ + + +/* + * l2ca_connect_req(): + * + * Initiates the sending of a connect request message. Requests the creation of a + * channel representing a logicalconnection to a physical address. Input parameters + * are the target protocol(PSM) and remote devices 48-bit address (BD_ADDR). Also + * specify the function to be called when a confirm has been received. + */ + + err_t +l2ca_connect_req(struct l2cap_pcb *pcb, struct bd_addr *bdaddr, u16_t psm, + u8_t role_switch, err_t (* l2ca_connect_cfm)(void *arg, struct l2cap_pcb *lpcb, + u16_t result, u16_t status)) +{ + err_t ret; + struct pbuf *data; + + if(bdaddr != NULL) { + bd_addr_set(&(pcb->remote_bdaddr),bdaddr); + } else { + return ERR_VAL; + } + + pcb->psm = psm; + pcb->l2ca_connect_cfm = l2ca_connect_cfm; + pcb->scid = l2cap_cid_alloc(); + LWIP_ASSERT("l2ca_connect_req: out of CIDs\n", pcb->scid != 0); + + pcb->cfg.l2capcfg |= L2CAP_CFG_IR; /* We are the initiator of this connection */ + + if(!lp_is_connected(bdaddr)) { + ret = lp_connect_req(bdaddr, role_switch); /* Create ACL link w pcb state == CLOSED */ + } else { + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CONN_REQ_SIZE, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_connect_req: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + ((u16_t *)data->payload)[0] = psm; + ((u16_t *)data->payload)[1] = pcb->scid; + ret = l2cap_signal(pcb, L2CAP_CONN_REQ, 0, &(pcb->remote_bdaddr), data); /* Send l2cap_conn_req signal */ + + pcb->state = W4_L2CAP_CONNECT_RSP; + } + + L2CAP_REG(&l2cap_active_pcbs, pcb); + + return ret; +} + +/* + * l2ca_config_req(): + * + * Requests the initial configuration (or reconfiguration) of a channel to a new set + * of channel parameters. Input parameters are the local CID endpoint, new incoming + * receivable MTU (InMTU), new outgoing flow specification, and flush and link + * timeouts. Also specify the function to be called when a confirm has been received. + */ + + err_t +l2ca_config_req(struct l2cap_pcb *pcb) +{ + struct pbuf *p, *q; + struct l2cap_cfgopt_hdr *opthdr; + err_t ret; + + switch(pcb->state) { + case L2CAP_OPEN: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_config_req: state = L2CAP_OPEN. Suspend transmission\n")); + /* Note: Application should have suspended data transmission, otherwise outgoing data will be + dropped */ + pcb->state = L2CAP_CONFIG; + /* Fallthrough */ + case L2CAP_CONFIG: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_config_req: state = L2CAP_CONFIG\n")); + + if((p = pbuf_alloc(PBUF_RAW, L2CAP_CFG_REQ_SIZE, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_config_req: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Assemble config request packet. Only options that has to be changed will be + sent */ + ((u16_t *)p->payload)[0] = pcb->dcid; + /* In this implementation we do not send multiple cmds in one + signal packet. Therefore we will never send a config_req packet + that will cause the signal to be larger than the minimum L2CAP MTU + 48 bytes. Hence, this flag will always be cleared */ + ((u16_t *)p->payload)[1] = 0; + + /* Add MTU and out flush timeout to cfg packet if not default value. QoS (Best effort) is always + set to default and can be skipped */ + if(pcb->cfg.inmtu != L2CAP_CFG_DEFAULT_INMTU) { + if((q = pbuf_alloc(PBUF_RAW, L2CAP_CFGOPTHDR_LEN + L2CAP_MTU_LEN, PBUF_RAM)) == NULL) { + pbuf_free(p); + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_config_req: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + opthdr = q->payload; + opthdr->type = L2CAP_CFG_MTU; + opthdr->len = L2CAP_MTU_LEN; + ((u16_t *)q->payload)[1] = pcb->cfg.inmtu; + pbuf_chain(p, q); + pbuf_free(q); + } + + if(L2CAP_OUT_FLUSHTO != L2CAP_CFG_DEFAULT_OUTFLUSHTO) { + if((q = pbuf_alloc(PBUF_RAW, L2CAP_CFGOPTHDR_LEN + L2CAP_FLUSHTO_LEN, PBUF_RAM)) == NULL) { + pbuf_free(p); + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_config_req: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + opthdr = q->payload; + opthdr->type = L2CAP_FLUSHTO; + opthdr->len = L2CAP_FLUSHTO_LEN; + ((u16_t *)q->payload)[1] = pcb->cfg.outflushto = L2CAP_OUT_FLUSHTO; + pbuf_chain(p, q); + pbuf_free(q); + } + + /* Send config request signal */ + ret = l2cap_signal(pcb, L2CAP_CFG_REQ, 0, &(pcb->remote_bdaddr), p); + break; + default: + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_config_req: state = L2CAP_?. Invalid state\n")); + return ERR_CONN; /* Invalid state. Connection is not in OPEN or CONFIG state */ + } + return ret; +} + +/* + * l2ca_disconnect_req(): + * + * Requests the disconnection of the channel. Also specify the function to be called + * when a confirm is received + */ + + err_t +l2ca_disconnect_req(struct l2cap_pcb *pcb, err_t (* l2ca_disconnect_cfm)(void *arg, struct l2cap_pcb *pcb)) +{ + struct pbuf *data; + err_t ret; + + if(pcb->state == L2CAP_OPEN || pcb->state == L2CAP_CONFIG) { + if((data = pbuf_alloc(PBUF_RAW, L2CAP_DISCONN_REQ_SIZE, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_disconnect_req: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + pcb->l2ca_disconnect_cfm = l2ca_disconnect_cfm; + + ((u16_t *)data->payload)[0] = pcb->dcid; + ((u16_t *)data->payload)[1] = pcb->scid; + + ret = l2cap_signal(pcb, L2CAP_DISCONN_REQ, 0, &(pcb->remote_bdaddr), data); + + if(ret == ERR_OK) { + pcb->state = W4_L2CAP_DISCONNECT_RSP; + } + } else { + return ERR_CONN; /* Signal not supported in this state */ + } + + return ret; +} + +/* + * l2ca_datawrite(): + * + * Transfers data across the channel. + */ + + err_t +l2ca_datawrite(struct l2cap_pcb *pcb, struct pbuf *p) +{ + err_t ret; + struct l2cap_hdr *l2caphdr; + struct pbuf *q; + + if(pcb->state != L2CAP_OPEN) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_datawrite: State != L2CAP_OPEN. Dropping data\n")); + return ERR_CONN; + } + + /* Build L2CAP header */ + if((q = pbuf_alloc(PBUF_RAW, L2CAP_HDR_LEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_datawrite: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + pbuf_chain(q, p); + + l2caphdr = q->payload; + l2caphdr->cid = pcb->dcid; + + /* If length of the data exceeds the OutMTU then only the first OutMTU bytes are sent */ + if(p->tot_len > pcb->cfg.outmtu) { + /* Send peer L2CAP data */ + l2caphdr->len = pcb->cfg.outmtu; + if((ret = l2cap_write(&(pcb->remote_bdaddr), q, pcb->cfg.outmtu + L2CAP_HDR_LEN)) == ERR_OK) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_datawrite: Length of data exceeds the OutMTU p->tot_len = %d\n", p->tot_len)); + ret = ERR_BUF; /* Length of data exceeds the OutMTU */ + } + } else { + /* Send peer L2CAP data */ + l2caphdr->len = p->tot_len; + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_datawrite: q->tot_len = %d\n", q->tot_len)); + ret = l2cap_write(&(pcb->remote_bdaddr), q, q->tot_len); + } + + /* Free L2CAP header. Higher layers will handle rest of packet */ + p = pbuf_dechain(q); + pbuf_free(q); + + return ret; +} + +/* + * l2ca_ping(): + * + * Sends an empty L2CAP echo request message. Also specify the function that should + * be called when a L2CAP echo reply has been received. + */ + + err_t +l2ca_ping(struct bd_addr *bdaddr, struct l2cap_pcb *tpcb, + err_t (* l2ca_pong)(void *arg, struct l2cap_pcb *pcb, u8_t result)) +{ + err_t ret; + + if(!lp_is_connected(bdaddr)) { + return ERR_CONN; + } + + bd_addr_set(&(tpcb->remote_bdaddr), bdaddr); + tpcb->l2ca_pong = l2ca_pong; + + L2CAP_REG(&l2cap_active_pcbs, tpcb); + + ret = l2cap_signal(tpcb, L2CAP_ECHO_REQ, 0, &(tpcb->remote_bdaddr), NULL); /* Send l2cap_echo_req signal */ + + return ret; +} + +/* Lower-Layer to L2CAP signaling events +*/ + + +/* + * lp_connect_cfm(): + * + * Confirms the request to establish a lower layer (Baseband) connection. + */ + + void +lp_connect_cfm(struct bd_addr *bdaddr, u8_t encrypt_mode, err_t err) +{ + struct l2cap_pcb *pcb; + struct pbuf *data; + err_t ret; + + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(bd_addr_cmp(&(pcb->remote_bdaddr), bdaddr)) { + break; + } + } + if(pcb == NULL) { + /* Silently discard */ + LWIP_DEBUGF(L2CAP_DEBUG, ("lp_connect_cfm: Silently discard\n")); + } else { + if(err == ERR_OK) { + pcb->encrypt = encrypt_mode; + /* Send l2cap_conn_req signal if no error */ + if((data = pbuf_alloc(PBUF_RAW, L2CAP_CONN_REQ_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = pcb->psm; + ((u16_t *)data->payload)[1] = pcb->scid; + if((ret = l2cap_signal(pcb, L2CAP_CONN_REQ, 0, &(pcb->remote_bdaddr), data)) == ERR_OK) { + pcb->state = W4_L2CAP_CONNECT_RSP; + } else { + L2CA_ACTION_CONN_CFM(pcb,L2CAP_CONN_REF_RES,0x0000,ret); /* No resources available? */ + } + LWIP_DEBUGF(L2CAP_DEBUG, ("lp_connect_cfm: l2cap_conn_req signal sent. err = %d\nPSM = 0x%x\nscid = 0x%x\nencrypt mode = 0x%x\n", err, pcb->psm, pcb->scid, pcb->encrypt)); + } else { + LWIP_DEBUGF(L2CAP_DEBUG, ("lp_connect_cfm: No resources available\n")); + L2CA_ACTION_CONN_CFM(pcb,L2CAP_CONN_REF_RES,0x0000,ret); /* No resources available */ + } + } else { + LWIP_DEBUGF(L2CAP_DEBUG, ("lp_connect_cfm: Connection falied\n")); + L2CA_ACTION_CONN_CFM(pcb,L2CAP_CONN_REF_RES,0x0000,ret); /* No resources available */ + } + } +} + +/* + * lp_connect_ind(): + * + * Indicates the lower protocol has successfully established a connection. + */ + + void +lp_connect_ind(struct bd_addr *bdaddr) +{ + LWIP_DEBUGF(L2CAP_DEBUG, ("lp_connect_ind\n")); +} + +/* + * lp_disconnect_ind(): + * + * Indicates the lower protocol (Baseband) has been shut down by LMP commands or a + * timeout event.. + */ + + void +lp_disconnect_ind(struct bd_addr *bdaddr) +{ + struct l2cap_pcb *pcb, *tpcb; + err_t ret; + + for(pcb = l2cap_active_pcbs; pcb != NULL;) { + tpcb = pcb->next; + LWIP_DEBUGF(L2CAP_DEBUG, ("lp_disconnect_ind: Find a pcb with a matching Bluetooth address\n")); + /* All PCBs with matching Bluetooth address have been disconnected */ + if(bd_addr_cmp(&(pcb->remote_bdaddr), bdaddr)) {// && pcb->state != L2CAP_CLOSED) { + pcb->state = L2CAP_CLOSED; + LWIP_DEBUGF(L2CAP_DEBUG, ("lp_disconnect_ind: Notify application\n")); + L2CA_ACTION_DISCONN_IND(pcb,ERR_OK,ret); + } + pcb = tpcb; + } + } + + /* + * l2cap_next_sigid(): + * + * Issues a signal identifier that helps matching a request with the reply. + */ + + u8_t + l2cap_next_sigid(void) + { + ++sigid_nxt; + if(sigid_nxt == 0) { + sigid_nxt = 1; + } + return sigid_nxt; + } + + /* + * l2cap_arg(): + * + * Used to specify the argument that should be passed callback functions. + */ + + void + l2cap_arg(struct l2cap_pcb *pcb, void *arg) + { + pcb->callback_arg = arg; + } + + /* + * l2cap_connect_ind(): + * + * Set the state of the connection to be LISTEN, which means that it is able to + * accept incoming connections. The protocol control block is reallocated in + * order to consume less memory. Setting the connection to LISTEN is an + * irreversible process. Also specify the function that should be called when + * the channel has received a connection request. + */ + err_t + l2cap_connect_ind(struct l2cap_pcb *npcb, u8_t psm, + err_t (* l2ca_connect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err)) + { + struct l2cap_pcb_listen *lpcb; + + lpcb = lwbt_memp_malloc(MEMP_L2CAP_PCB_LISTEN); + if(lpcb == NULL) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_connect_ind: Could not allocate memory for lpcb\n")); + return ERR_MEM; + } + lpcb->psm = psm; + lpcb->l2ca_connect_ind = l2ca_connect_ind; + lpcb->state = L2CAP_LISTEN; + lpcb->callback_arg = npcb->callback_arg; + lwbt_memp_free(MEMP_L2CAP_PCB, npcb); + L2CAP_REG((struct l2cap_pcb **)&l2cap_listen_pcbs, (struct l2cap_pcb *)lpcb); + return ERR_OK; + } + + /* + * l2cap_disconnect_ind(): + * + * Used to specify the a function to be called when a disconnection request has been + * received from a remote device or the remote device has been disconnected because it + * has failed to respond to a signalling request. + */ + void + l2cap_disconnect_ind(struct l2cap_pcb *pcb, + err_t (* l2ca_disconnect_ind)(void *arg, struct l2cap_pcb *newpcb, err_t err)) + { + pcb->l2ca_disconnect_ind = l2ca_disconnect_ind; + } + + /* + * l2cap_timeout_ind(): + * + * Used to specify the function to be called when RTX or ERTX timer has expired. + */ + void + l2cap_timeout_ind(struct l2cap_pcb *pcb, + err_t (* l2ca_timeout_ind)(void *arg, struct l2cap_pcb *newpcb, err_t err)) + { + pcb->l2ca_timeout_ind = l2ca_timeout_ind; + } + + /* + * l2cap_recv(): + * + * Used to specify the function that should be called when a L2CAP connection receives + * data. + */ + void + l2cap_recv(struct l2cap_pcb *pcb, + err_t (* l2ca_recv)(void *arg, struct l2cap_pcb *pcb, struct pbuf *p, err_t err)) + { + pcb->l2ca_recv = l2ca_recv; + } + diff --git a/kernel/bluetooth/lwbt/lwbt_memp.c b/kernel/bluetooth/lwbt/lwbt_memp.c new file mode 100644 index 0000000..0dfdc83 --- /dev/null +++ b/kernel/bluetooth/lwbt/lwbt_memp.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#include "lwbt/lwbtopts.h" +#include "lwbt/lwbt_memp.h" +#include "lwbt/hci.h" +#include "lwbt/l2cap.h" +#include "lwbt/sdp.h" +#include "lwbt/rfcomm.h" +#include "lwip/mem.h" +#include "lwip/opt.h" + +struct memp { + struct memp *next; +}; + +static struct memp *memp_tab[MEMP_LWBT_MAX]; + + static const u16_t memp_sizes[MEMP_LWBT_MAX] = { + sizeof(struct hci_pcb), + sizeof(struct hci_link), + sizeof(struct hci_inq_res), + sizeof(struct l2cap_pcb), + sizeof(struct l2cap_pcb_listen), + sizeof(struct l2cap_sig), + sizeof(struct l2cap_seg), + sizeof(struct sdp_pcb), + sizeof(struct sdp_record), + sizeof(struct rfcomm_pcb), + sizeof(struct rfcomm_pcb_listen), + }; + +static const u16_t memp_num[MEMP_LWBT_MAX] = { + MEMP_NUM_HCI_PCB, + MEMP_NUM_HCI_LINK, + MEMP_NUM_HCI_INQ, + MEMP_NUM_L2CAP_PCB, + MEMP_NUM_L2CAP_PCB_LISTEN, + MEMP_NUM_L2CAP_SIG, + MEMP_NUM_L2CAP_SEG, + MEMP_NUM_SDP_PCB, + MEMP_NUM_SDP_RECORD, + MEMP_NUM_RFCOMM_PCB, + MEMP_NUM_RFCOMM_PCB_LISTEN, +}; + +static u8_t memp_memory[(MEMP_NUM_HCI_PCB * + LWIP_MEM_ALIGN_SIZE(sizeof(struct hci_pcb) + + sizeof(struct memp)) + + MEMP_NUM_HCI_LINK * + LWIP_MEM_ALIGN_SIZE(sizeof(struct hci_link) + + sizeof(struct memp)) + + MEMP_NUM_HCI_INQ * + LWIP_MEM_ALIGN_SIZE(sizeof(struct hci_inq_res) + + sizeof(struct memp)) + + MEMP_NUM_L2CAP_PCB * + LWIP_MEM_ALIGN_SIZE(sizeof(struct l2cap_pcb) + + sizeof(struct memp)) + + MEMP_NUM_L2CAP_PCB_LISTEN * + LWIP_MEM_ALIGN_SIZE(sizeof(struct l2cap_pcb_listen) + + sizeof(struct memp)) + + MEMP_NUM_L2CAP_SIG * + LWIP_MEM_ALIGN_SIZE(sizeof(struct l2cap_sig) + + sizeof(struct memp)) + + MEMP_NUM_L2CAP_SEG * + LWIP_MEM_ALIGN_SIZE(sizeof(struct l2cap_seg) + + sizeof(struct memp)) + + MEMP_NUM_SDP_PCB * + LWIP_MEM_ALIGN_SIZE(sizeof(struct sdp_pcb) + + sizeof(struct memp)) + + MEMP_NUM_SDP_RECORD * + LWIP_MEM_ALIGN_SIZE(sizeof(struct sdp_record) + + sizeof(struct memp)) + + MEMP_NUM_RFCOMM_PCB * + LWIP_MEM_ALIGN_SIZE(sizeof(struct rfcomm_pcb) + + sizeof(struct memp)) + + MEMP_NUM_RFCOMM_PCB_LISTEN * + LWIP_MEM_ALIGN_SIZE(sizeof(struct rfcomm_pcb_listen) + + sizeof(struct memp)))]; + + + +void lwbt_memp_init(void) +{ + struct memp *m, *memp; + u16_t i, j; + u16_t size; + + memp = (struct memp *)&memp_memory[0]; + for(i = 0; i < MEMP_LWBT_MAX; ++i) { + size = LWIP_MEM_ALIGN_SIZE(memp_sizes[i] + sizeof(struct memp)); + if(memp_num[i] > 0) { + memp_tab[i] = memp; + m = memp; + + for(j = 0; j < memp_num[i]; ++j) { + m->next = (struct memp *)LWIP_MEM_ALIGN((u8_t *)m + size); + memp = m; + m = m->next; + } + memp->next = NULL; + memp = m; + } else { + memp_tab[i] = NULL; + } + } +} + + +void * lwbt_memp_malloc(lwbt_memp_t type) +{ + struct memp *memp; + void *mem; + + memp = memp_tab[type]; + + if(memp != NULL) { + memp_tab[type] = memp->next; + memp->next = NULL; + + mem = LWIP_MEM_ALIGN((u8_t *)memp + sizeof(struct memp)); + /* initialize memp memory with zeroes */ + MEMSET(mem, 0, memp_sizes[type]); + return mem; + } else { + return NULL; + } +} + + +void lwbt_memp_free(lwbt_memp_t type, void *mem) +{ + struct memp *memp; + + if(mem == NULL) { + return; + } + memp = (struct memp *)((u8_t *)mem - sizeof(struct memp)); + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + + return; +} + + diff --git a/kernel/bluetooth/lwbt/rfcomm.c b/kernel/bluetooth/lwbt/rfcomm.c new file mode 100644 index 0000000..e4f387f --- /dev/null +++ b/kernel/bluetooth/lwbt/rfcomm.c @@ -0,0 +1,1441 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + + +/* rfcomm.c + * + * Implementation of the RFCOMM protocol. A subset of the ETSI TS 07.10 standard with + * some Bluetooth-specific adaptations. + */ + + +#include "lwbt/l2cap.h" +#include "lwbt/rfcomm.h" +#include "lwbt/lwbt_memp.h" +#include "lwbt/fcs.h" +#include "lwbt/lwbtopts.h" +#include "lwip/debug.h" + +struct rfcomm_pcb_listen *rfcomm_listen_pcbs; /* List of all RFCOMM PCBs listening for + an incomming connection on a specific + server channel */ +struct rfcomm_pcb *rfcomm_active_pcbs; /* List of all active RFCOMM PCBs */ +struct rfcomm_pcb *rfcomm_tmp_pcb; + +/* Forward declarations */ +struct rfcomm_pcb *rfcomm_get_active_pcb(u8_t cn, struct bd_addr *bdaddr); + + +/* + * rfcomm_init(): + * + * Initializes the rfcomm layer. + */ +void rfcomm_init(void) +{ + /* Clear globals */ + rfcomm_listen_pcbs = NULL; + rfcomm_active_pcbs = NULL; + rfcomm_tmp_pcb = NULL; +} + +/* + * rfcomm_tmr(): + * + * Called every 1s and implements the command timer that + * removes a DLC if it has been waiting for a response enough + * time. + */ +void rfcomm_tmr(void) +{ + struct rfcomm_pcb *pcb, *tpcb; + err_t ret; + + /* Step through all of the active pcbs */ + for(pcb = rfcomm_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->to != 0) { + --pcb->to; + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_tmr: %d\n", pcb->to)); + if(pcb->to == 0) { + /* Timeout */ + if(pcb->cn == 0) { + /* If DLC 0 timed out, disconnect all other DLCs on this multiplexer session first */ + for(tpcb = rfcomm_active_pcbs; tpcb != NULL; tpcb = tpcb->next) { + if(tpcb->cn != 0 && bd_addr_cmp(&(tpcb->l2cappcb->remote_bdaddr), &(pcb->l2cappcb->remote_bdaddr))) { + //RFCOMM_RMV(&rfcomm_active_pcbs, tpcb); /* Remove pcb from active list */ + tpcb->state = RFCOMM_CLOSED; + RFCOMM_EVENT_DISCONNECTED(tpcb,ERR_OK,ret); /* Notify upper layer */ + } + } + } + /* Disconnect this DLC */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_tmr: Timeout! Disconnect this DLC. State = %d\n", pcb->state)); + //RFCOMM_RMV(&rfcomm_active_pcbs, pcb); /* Remove pcb from active list */ + pcb->state = RFCOMM_CLOSED; + RFCOMM_EVENT_DISCONNECTED(pcb,ERR_OK,ret); /* Notify upper layer */ + } + } + } +} + +/* + * rfcomm_lp_disconnected(): + * + * Called by the application to indicate that the lower protocol disconnected. Closes + * any active PCBs in the lists + */ +err_t rfcomm_lp_disconnected(struct l2cap_pcb *l2cappcb) +{ + struct rfcomm_pcb *pcb, *tpcb; + err_t ret = ERR_OK; + + pcb = rfcomm_active_pcbs; + while(pcb != NULL) { + tpcb = pcb->next; + if(bd_addr_cmp(&(l2cappcb->remote_bdaddr), &(pcb->l2cappcb->remote_bdaddr))) { + pcb->state = RFCOMM_CLOSED; + RFCOMM_EVENT_DISCONNECTED(pcb,ERR_OK,ret); /* Notify upper layer */ + } + pcb = tpcb; + } + + return ret; +} + +/* + * rfcomm_new(): + * + * Creates a new RFCOMM protocol control block but doesn't place it on + * any of the RFCOMM PCB lists. + */ +struct rfcomm_pcb * rfcomm_new(struct l2cap_pcb *l2cappcb) +{ + struct rfcomm_pcb *pcb; + + pcb = lwbt_memp_malloc(MEMP_RFCOMM_PCB); + if(pcb != NULL) { + memset(pcb, 0, sizeof(struct rfcomm_pcb)); + pcb->l2cappcb = l2cappcb; + + pcb->cl = RFCOMM_CL; /* Default convergence layer */ + pcb->n = RFCOMM_N; /* Default maximum frame size */ + + pcb->state = RFCOMM_CLOSED; + return pcb; + } + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_new: Could not allocate a new pcb\n")); + return NULL; +} + +/* + * rfcomm_close(): + * + * Closes the RFCOMM protocol control block. + */ +void rfcomm_close(struct rfcomm_pcb *pcb) +{ +#if RFCOMM_FLOW_QUEUEING + if(pcb->buf != NULL) { + pbuf_free(pcb->buf); + } +#endif + if(pcb->state == RFCOMM_LISTEN) { + RFCOMM_RMV((struct rfcomm_pcb **)&rfcomm_listen_pcbs, pcb); + lwbt_memp_free(MEMP_RFCOMM_PCB_LISTEN, pcb); + } else { + RFCOMM_RMV(&rfcomm_active_pcbs, pcb); + lwbt_memp_free(MEMP_RFCOMM_PCB, pcb); + } + pcb = NULL; +} + +/* + * rfcomm_reset_all(): + * + * Closes all active and listening RFCOMM protocol control blocks. + */ +void rfcomm_reset_all(void) +{ + struct rfcomm_pcb *pcb, *tpcb; + struct rfcomm_pcb_listen *lpcb, *tlpcb; + + for(pcb = rfcomm_active_pcbs; pcb != NULL;) { + tpcb = pcb->next; + rfcomm_close(pcb); + pcb = tpcb; + } + + for(lpcb = rfcomm_listen_pcbs; lpcb != NULL;) { + tlpcb = lpcb->next; + rfcomm_close((struct rfcomm_pcb *)lpcb); + lpcb = tlpcb; + } + + rfcomm_init(); +} + +/* + * rfcomm_get_active_pcb(): + * + * Return the active PCB with the matching Bluetooth address and channel number. + */ +struct rfcomm_pcb * rfcomm_get_active_pcb(u8_t cn, struct bd_addr *bdaddr) +{ + struct rfcomm_pcb *pcb; + for(pcb = rfcomm_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->cn == cn && bd_addr_cmp(&(pcb->l2cappcb->remote_bdaddr), + bdaddr)) { + break; + } + } + return pcb; +} + +/* + * rfcomm_dm(): + * + * Sends a RFCOMM disconnected mode frame in response to a command when disconnected. + */ +static err_t rfcomm_dm(struct l2cap_pcb *pcb, struct rfcomm_hdr *hdr) +{ + struct pbuf *p; + struct rfcomm_hdr *rfcommhdr; + err_t ret; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_dm\n")); + + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_DM_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_dm: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + rfcommhdr = p->payload; + rfcommhdr->addr = hdr->addr & 0xFB; /* Set direction bit to 0 for the response */ + rfcommhdr->ctrl = RFCOMM_DM; + rfcommhdr->len = 1; /* EA bit set to 1 to indicate a 7 bit length field */ + ((u8_t *)p->payload)[RFCOMM_HDR_LEN_1] = fcs8_crc_calc(p, RFCOMM_CRC_CHECK_LEN); + + ret = l2ca_datawrite(pcb, p); + pbuf_free(p); + return ret; +} + +/* + * rfcomm_connect(): + * + * Sends a RFCOMM start asynchronous balanced mode frame to startup the channel. Also + * specify the function to be called when the channel has been connected. + */ +err_t rfcomm_connect(struct rfcomm_pcb *pcb, u8_t cn, + err_t (* connected)(void *arg, struct rfcomm_pcb *tpcb, err_t err)) +{ + struct rfcomm_hdr *hdr; + struct pbuf *p; + err_t ret; + struct rfcomm_pcb *tpcb; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_connect\n")); + + pcb->connected = connected; + pcb->cn = cn; + pcb->rfcommcfg |= RFCOMM_CFG_IR; /* Set role to initiator */ + + /* Create multiplexer session if one does not already exist */ + if(cn != 0) { + tpcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr); + + if(tpcb == NULL) { + pcb->state = W4_RFCOMM_MULTIPLEXER; + RFCOMM_REG(&rfcomm_active_pcbs, pcb); + pcb = rfcomm_new(pcb->l2cappcb); + pcb->rfcommcfg |= RFCOMM_CFG_IR; /* Set role to initiator */ + } + } + + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_SABM_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_connect: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + hdr = p->payload; + hdr->addr = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (((pcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 2) | (pcb->cn << 3); + hdr->ctrl = RFCOMM_SABM; + hdr->len = (1 << 0) | (0 << 1); + ((u8_t *)p->payload)[RFCOMM_HDR_LEN_1] = fcs8_crc_calc(p, RFCOMM_CRC_CHECK_LEN); + + if((ret = l2ca_datawrite(pcb->l2cappcb, p)) == ERR_OK) { + pcb->state = W4_RFCOMM_SABM_RSP; + pcb->to = 5*RFCOMM_TO; /* Set acknowledgement timer, 50-300s (5*10-60s) */ + } + + if((tpcb = rfcomm_get_active_pcb(pcb->cn, &pcb->l2cappcb->remote_bdaddr)) == NULL) { + RFCOMM_REG(&rfcomm_active_pcbs, pcb); + } + + pbuf_free(p); + return ret; +} + +/* + * rfcomm_disconnect(): + * + * Sends a RFCOMM disconnect frame to close the channel. + */ +err_t rfcomm_disconnect(struct rfcomm_pcb *pcb) +{ + struct rfcomm_hdr *hdr; + struct pbuf *p; + err_t ret; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_disconnect\n")); + + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_DISC_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_disconnect: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + p = pbuf_alloc(PBUF_RAW, RFCOMM_DISC_LEN, PBUF_RAM); + hdr = p->payload; + hdr->addr = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (((pcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 2) | (pcb->cn << 3); + hdr->ctrl = RFCOMM_DISC; + hdr->len = (1 << 0) | (0 << 1); + ((u8_t *)p->payload)[RFCOMM_HDR_LEN_1] = fcs8_crc_calc(p, RFCOMM_CRC_CHECK_LEN); + pcb->state = W4_RFCOMM_DISC_RSP; + + if((ret = l2ca_datawrite(pcb->l2cappcb, p)) == ERR_OK) { + pcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */ + } + pbuf_free(p); + return ret; +} + +/* + * rfcomm_ua(): + * + * Sends a RFCOMM unnumbered acknowledgement to respond to a connection request. + */ +static err_t rfcomm_ua(struct l2cap_pcb *pcb, struct rfcomm_hdr *hdr) + //RESPONDER +{ + struct pbuf *p; + struct rfcomm_hdr *rfcommhdr; + err_t ret; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_ua\n")); + + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UA_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_ua: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + rfcommhdr = p->payload; + rfcommhdr->addr = hdr->addr & 0xFB; /* Set direction bit to 0 for the response */ + rfcommhdr->ctrl = RFCOMM_UA; + rfcommhdr->len = 1; /* EA bit set to 1 to indicate a 7 bit length field */ + ((u8_t *)p->payload)[RFCOMM_HDR_LEN_1] = fcs8_crc_calc(p, RFCOMM_CRC_CHECK_LEN); + + ret = l2ca_datawrite(pcb, p); + pbuf_free(p); + return ret; +} + +/* + * rfcomm_pn(): + * + * Sends a RFCOMM parameter negotiation multiplexer frame to negotiate the parameters + * of a data link connection. Also specify the function to be called when a PN + * response is received + */ +err_t rfcomm_pn(struct rfcomm_pcb *pcb, + err_t (* pn_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err)) +{ + struct pbuf *p; + struct rfcomm_msg_hdr *cmdhdr; + struct rfcomm_pn_msg *pnmsg; + err_t ret; + struct rfcomm_pcb *opcb; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_pn\n")); + + opcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr); + + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN + RFCOMM_PNMSG_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_pn: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Set multiplexer parameter negotiation command header */ + cmdhdr = p->payload; + cmdhdr->type = RFCOMM_PN_CMD; + cmdhdr->len = 1 | (RFCOMM_PNMSG_LEN << 1); + + /* Set multiplexer parameter negotiation command paramenters */ + pnmsg = (void *)(((u8_t *)p->payload) + RFCOMM_MSGHDR_LEN); + pnmsg->dlci = (((opcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 0) | (pcb->cn << 1); + pnmsg->i_cl = 0 | (RFCOMM_CL << 4); + pnmsg->p = 0; + pnmsg->t = 0; + pnmsg->n = RFCOMM_N; + pnmsg->na = 0; + pnmsg->k = RFCOMM_K; + + if((ret = rfcomm_uih(opcb, 0, p)) == ERR_OK) { + pcb->pn_rsp = pn_rsp; + opcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */ + } + pbuf_free(p); + return ret; +} + +/* + * rfcomm_test(): + * + * . + */ +err_t rfcomm_test(struct rfcomm_pcb *pcb, err_t (* test_rsp)(void *arg, struct rfcomm_pcb *tpcb, err_t err)) +{ + struct pbuf *p; + struct rfcomm_msg_hdr *cmdhdr; + err_t ret; + struct rfcomm_pcb *opcb; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_test\n")); + + opcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr); + + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_test: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Set multiplexer modem status command header */ + cmdhdr = p->payload; + cmdhdr->type = RFCOMM_TEST_CMD; + cmdhdr->len = 1 | (0 << 1); + + if((ret = rfcomm_uih(opcb, 0, p)) == ERR_OK) { + opcb->test_rsp = test_rsp; + opcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */ + } + pbuf_free(p); + return ret; +} + +/* + * rfcomm_msc(): + * + * Sends a RFCOMM modem status multiplexer frame. Also specify the function to be + * called when a MSC response is received. + */ +err_t rfcomm_msc(struct rfcomm_pcb *pcb, u8_t fc, + err_t (* msc_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err)) +{ + struct pbuf *p; + struct rfcomm_msg_hdr *cmdhdr; + struct rfcomm_msc_msg *mscmsg; + err_t ret; + struct rfcomm_pcb *opcb; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_msc\n")); + + opcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr); + + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN + RFCOMM_MSCMSG_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_msc: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Set multiplexer modem status command header */ + cmdhdr = p->payload; + cmdhdr->type = RFCOMM_MSC_CMD; + cmdhdr->len = 1 | (RFCOMM_MSCMSG_LEN << 1); + + /* Set multiplexer parameter negotiation command paramenters */ + mscmsg = (void *)(((u8_t *)p->payload) + RFCOMM_MSGHDR_LEN); + // mscmsg->dlci = (1 << 0) | (1 << 1) | (((pcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 2) | (pcb->cn << 3); + mscmsg->dlci = (1 << 0) | (1 << 1) | (0 << 2) | (pcb->cn << 3); + mscmsg->rs232 = (1 << 0) | (fc << 1) | (0x23 << 2); + + if((ret = rfcomm_uih(opcb, 0, p)) == ERR_OK) { + pcb->msc_rsp = msc_rsp; + opcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */ + } + pbuf_free(p); + return ret; +} + +/* + * rfcomm_rpn(): + * + * Sends a RFCOMM remote port negotiation multiplexer frame to set communication + * settings at the remote end of the data link connection. + */ +err_t rfcomm_rpn(struct rfcomm_pcb *pcb, u8_t br, + err_t (* rpn_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err)) + //INITIATOR +{ + struct pbuf *p; + struct rfcomm_msg_hdr *cmdhdr; + struct rfcomm_rpn_msg *rpnmsg; + err_t ret; + struct rfcomm_pcb *opcb; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_rpn\n")); + + opcb = rfcomm_get_active_pcb(0, &pcb->l2cappcb->remote_bdaddr); + + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN + RFCOMM_RPNMSG_LEN, PBUF_RAM)) == NULL) { + /* Could not allocate memory for pbuf */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_rpn: Could not allocate memory for pbuf\n")); + return ERR_MEM; + } + + /* Set remote port negotiation command header */ + cmdhdr = p->payload; + cmdhdr->type = RFCOMM_RPN_CMD; + cmdhdr->len = 1 | (RFCOMM_RPNMSG_LEN << 1); + + /* Set remote port negotiation command paramenters */ + rpnmsg = (void *)(((u8_t *)p->payload) + RFCOMM_MSGHDR_LEN); + rpnmsg->dlci = (1 << 0) | (1 << 1) | (((opcb->rfcommcfg & RFCOMM_CFG_IR) ^ 1) << 2) | (pcb->cn << 3); + rpnmsg->br = br; + rpnmsg->mask = 1; + + if((ret = rfcomm_uih(opcb, 0, p)) == ERR_OK) { + pcb->rpn_rsp = rpn_rsp; + opcb->to = RFCOMM_TO; /* Set acknowledgement timer, 10-60s */ + + } + pbuf_free(p); + return ret; +} + +/* + * rfcomm_uih(): + * + * Sends a RFCOMM unnumbered information frame with header check. + */ +err_t rfcomm_uih(struct rfcomm_pcb *pcb, u8_t cn, struct pbuf *q) + //RESPONDER & INITIATOR +{ + struct pbuf *p, *r; + err_t ret; + u16_t tot_len = 0; + + /* Decrease local credits */ + if(pcb->cl == 0xF && pcb->state == RFCOMM_OPEN && pcb->cn != 0) { + if(pcb->k != 0) { + --pcb->k; + } else { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Out of local credits\n")); +#if RFCOMM_FLOW_QUEUEING + if(q != NULL) { + /* Packet can be queued? */ + if(pcb->buf != NULL) { + return ERR_OK; /* Drop packet */ + } else { + /* Copy PBUF_REF referenced payloads into PBUF_RAM */ + q = pbuf_take(q); + /* Remember pbuf to queue, if any */ + pcb->buf = q; + /* Pbufs are queued, increase the reference count */ + pbuf_ref(q); + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Queued packet %p on channel %d\n", (void *)q, pcb->cn)); + } + } +#else + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Dropped packet.\n")); + return ERR_OK; /* Drop packet */ +#endif /* RFCOMM_FLOW_QUEUEING */ + } + } + + if(q != NULL) { + tot_len = q->tot_len; + } + + /* Size of information must be less than maximum frame size */ + if(tot_len > pcb->n) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Size of information must be less than maximum frame size\n")); + return ERR_MEM; + } + + if(tot_len < 127) { + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UIH_LEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + } else { + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UIH_LEN+1, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + } + + /* Setup RFCOMM header */ + if(cn == 0) { + ((u8_t *)p->payload)[0] = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (0 << 2) | (0 << 3); + } else { + ((u8_t *)p->payload)[0] = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (0 << 2) | (cn << 3); + } + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + if(q != NULL) { + if(q->tot_len < 127) { + ((u8_t *)p->payload)[2] = (1 << 0) | (q->tot_len << 1); + } else { + ((u16_t *)p->payload)[1] = (0 << 0) | (q->tot_len << 1); + } + /* Add information data to pbuf */ + pbuf_chain(p, q); + } else { + ((u8_t *)p->payload)[2] = (1 << 0) | (0 << 1); /* Empty UIH frame */ + } + /* Add information FCS to pbuf */ + if((r = pbuf_alloc(PBUF_RAW, 1, PBUF_RAM)) == NULL) { + pbuf_free(p); + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + + if(cn == 0) { + ((u8_t *)r->payload)[0] = pcb->uih0_out_fcs; + } else { + ((u8_t *)r->payload)[0] = pcb->uih_out_fcs; + } + + pbuf_chain(p, r); + pbuf_free(r); + + ret = l2ca_datawrite(pcb->l2cappcb, p); + + /* Dealloc the RFCOMM header. Lower layers will handle rest of packet */ + pbuf_free(p); + if(q) { + //pbuf_dechain(p); /* Have q point to information + FCS */ + pbuf_realloc(q, q->tot_len-1); /* Remove FCS from packet */ + } + + return ret; +} + +/* + * rfcomm_uih_credits(): + * + * Sends a RFCOMM unnumbered information frame with header check and credit based + * flow control. + */ +err_t rfcomm_uih_credits(struct rfcomm_pcb *pcb, u8_t credits, struct pbuf *q) + //RESPONDER & INITIATOR +{ + struct pbuf *p, *r; + err_t ret; + u16_t tot_len = 0; + + /* Decrease local credits */ + if(pcb->cl == 0xF && pcb->state == RFCOMM_OPEN && pcb->cn != 0) { + if(pcb->k != 0) { + --pcb->k; + } else { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Out of local credits\n")); +#if RFCOMM_FLOW_QUEUEING + if(q != NULL) { + /* Packet can be queued? */ + if(pcb->buf != NULL) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Buffer full. Dropped packet\n")); + return ERR_OK; /* Drop packet */ + } else { + /* Copy PBUF_REF referenced payloads into PBUF_RAM */ + q = pbuf_take(q); + /* Remember pbuf to queue, if any */ + pcb->buf = q; + /* Pbufs are queued, increase the reference count */ + pbuf_ref(q); + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Queued packet %p on channel %d\n", (void *)q, pcb->cn)); + } + } +#else + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Dropped packet\n")); +#endif /* RFCOMM_FLOW_QUEUEING */ + return ERR_OK; + } + } + + if(q != NULL) { + tot_len = q->tot_len; + } + + /* Size of information must be less than maximum frame size */ + if(tot_len > pcb->n) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Size of information must be less than maximum frame size = %d Packet lenght = %d\n", pcb->n, q->tot_len)); + return ERR_MEM; + } + + if(tot_len < 127) { + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UIHCRED_LEN, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + } else { + if((p = pbuf_alloc(PBUF_RAW, RFCOMM_UIHCRED_LEN+1, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + } + + /* Setup RFCOMM header */ + ((u8_t *)p->payload)[0] = (1 << 0) | ((pcb->rfcommcfg & RFCOMM_CFG_IR) << 1) | (0 << 2) | (pcb->cn << 3); + ((u8_t *)p->payload)[1] = RFCOMM_UIH_PF; + if(q != NULL) { + if(q->tot_len < 127) { + ((u8_t *)p->payload)[2] = (1 << 0) | (q->tot_len << 1); + ((u8_t *)p->payload)[3] = credits; + } else { + ((u16_t *)p->payload)[1] = (0 << 0) | (q->tot_len << 1); + ((u8_t *)p->payload)[4] = credits; + } + /* Add information data to pbuf */ + pbuf_chain(p, q); + } else { + /* Credit only UIH frame */ + ((u8_t *)p->payload)[2] = (1 << 0) | (0 << 1); + } + + /* Add information FCS to pbuf */ + if((r = pbuf_alloc(PBUF_RAW, 1, PBUF_RAM)) == NULL) { + pbuf_free(p); + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: Could not allocate memory for pbuf\n")); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + + ((u8_t *)r->payload)[0] = pcb->uihpf_out_fcs; + pbuf_chain(p, r); + pbuf_free(r); + + /* Increase remote credits */ + pcb->rk += credits; + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_uih_credits: p->tot_len = %d pcb->k = %d pcb->rk = %d\n", p->tot_len, pcb->k, pcb->rk)); + + ret = l2ca_datawrite(pcb->l2cappcb, p); + + /* Free RFCOMM header. Higher layers will handle rest of packet */ + pbuf_free(p); + if(q) { + //pbuf_dechain(p); + pbuf_realloc(q, q->tot_len-1); /* Remove FCS from packet */ + } + + return ret; +} + +/* + * rfcomm_process_msg(): + * + * Parses the received RFCOMM message and handles it. + */ +void rfcomm_process_msg(struct rfcomm_pcb *pcb, struct rfcomm_hdr *rfcommhdr, struct l2cap_pcb *l2cappcb, struct pbuf *p) +{ + struct rfcomm_msg_hdr *cmdhdr, *rsphdr; + struct rfcomm_pn_msg *pnreq; + struct rfcomm_msc_msg *mscreq; + struct rfcomm_rpn_msg *rpnreq; + struct rfcomm_pcb *tpcb; /* Temp pcb */ + struct rfcomm_pcb_listen *lpcb; /* Listen pcb */ + struct pbuf *q; + err_t ret; + + cmdhdr = p->payload; + pbuf_header(p, -RFCOMM_MSGHDR_LEN); + + switch(cmdhdr->type) { + case RFCOMM_PN_CMD: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM PN command\n")); + pnreq = p->payload; + + /* Check if the DLC is already established */ + tpcb = rfcomm_get_active_pcb((pnreq->dlci >> 1), &pcb->l2cappcb->remote_bdaddr); + + if(tpcb == NULL) { + /* Check if the server channel exists */ + for(lpcb = rfcomm_listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if(lpcb->cn == (pnreq->dlci >> 1)) { + break; + } + } + if(lpcb != NULL) { + /* Found a listening pcb with a matching server channel number, now initiate a new PCB + with default configuration */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: Allocate RFCOMM PCB for CN %d******************************\n", lpcb->cn)); + if((tpcb = rfcomm_new(pcb->l2cappcb)) == NULL) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: could not allocate PCB\n")); + return; + } + tpcb->cn = lpcb->cn; + tpcb->callback_arg = lpcb->callback_arg; + tpcb->accept = lpcb->accept; + tpcb->state = RFCOMM_CFG; + + RFCOMM_REG(&rfcomm_active_pcbs, tpcb); + } else { + /* Channel does not exist, refuse connection with DM frame */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: Channel does not exist, refuse connection with DM frame CN %d\n", (pnreq->dlci >> 1))); + rfcomm_dm(pcb->l2cappcb, rfcommhdr); + break; + } + } + /* Get suggested parameters */ + tpcb->cl = pnreq->i_cl >> 4; + tpcb->p = pnreq->p; + if(tpcb->n > pnreq->n) { + tpcb->n = pnreq->n; + } + tpcb->k = pnreq->k; + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_PN_CMD. tpcb->k = %d\n", tpcb->k)); + + /* Send PN response */ + cmdhdr->type = cmdhdr->type & 0xFD; /* Set C/R to response */ + if(tpcb->cl == 0xF) { + pnreq->i_cl = 0 | (0xE << 4); /* Credit based flow control */ + } else { + pnreq->i_cl = 0; /* Remote device conforms to bluetooth version 1.0B. No flow control */ + } + pnreq->p = tpcb->p; + pnreq->n = tpcb->n; + pnreq->k = tpcb->k; + pbuf_header(p, RFCOMM_MSGHDR_LEN); + + rfcomm_uih(pcb, 0, p); + break; + case RFCOMM_PN_RSP: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM PN response\n")); + pcb->to = 0; /* Reset response timer */ + pnreq = p->payload; + /* Find PCB with matching server channel number and bluetooth address */ + tpcb = rfcomm_get_active_pcb((pnreq->dlci >> 1), &pcb->l2cappcb->remote_bdaddr); + + if(tpcb != NULL) { + /* Use negotiated settings that may have changed from the default ones */ + if((pnreq->i_cl >> 4) == 0xE) { + tpcb->cl = 0xF; /* Credit based flow control */ + tpcb->k = pnreq->k==0?7:pnreq->k; /* Inital credit value */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_PN_RSP. tpcb->k = %d\n", tpcb->k)); + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: Credit based flow control is used for outgoing packets 0x%x %d %d\n", (pnreq->i_cl >> 4), pnreq->k, pnreq->n)); + } else { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: No flow control used for outgoing packets 0x%x\n", (pnreq->i_cl >> 4))); + tpcb->cl = 0; /* Remote device conform to bluetooth version 1.0B. No flow control */ + } + tpcb->n = pnreq->n; /* Maximum frame size */ + + if(tpcb->state == W4_RFCOMM_MULTIPLEXER) { + rfcomm_connect(tpcb, tpcb->cn, tpcb->connected); /* Create a connection for a channel that + waits for the multiplexer connection to + be established */ + } + + pcb->state = RFCOMM_OPEN; + RFCOMM_EVENT_PN_RSP(tpcb,ERR_OK,ret); + } /* else silently discard */ + break; + case RFCOMM_TEST_CMD: + /* Send TEST response */ + cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */ + pbuf_header(p, RFCOMM_MSGHDR_LEN); + + rfcomm_uih(pcb, 0, p); + break; + case RFCOMM_TEST_RSP: + pcb->to = 0; /* Reset response timer */ + RFCOMM_EVENT_TEST(pcb,ERR_OK,ret); + break; + case RFCOMM_FCON_CMD: + /* Enable transmission of data on all channels in session except cn 0 */ + for(tpcb = rfcomm_active_pcbs; tpcb != NULL; tpcb = tpcb->next) { + if(bd_addr_cmp(&(tpcb->l2cappcb->remote_bdaddr), &(l2cappcb->remote_bdaddr)) && + tpcb->cn != 0) { + tpcb->rfcommcfg |= RFCOMM_CFG_FC; + } + } + /* Send FC_ON response */ + cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */ + pbuf_header(p, RFCOMM_MSGHDR_LEN); + + rfcomm_uih(pcb, 0, p); + break; + case RFCOMM_FCON_RSP: + break; + case RFCOMM_FCOFF_CMD: + /* Disable transmission of data on all channels in session except cn 0 */ + for(tpcb = rfcomm_active_pcbs; tpcb != NULL; tpcb = tpcb->next) { + if(bd_addr_cmp(&(tpcb->l2cappcb->remote_bdaddr), &(l2cappcb->remote_bdaddr)) && + tpcb->cn != 0) { + tpcb->rfcommcfg &= ~RFCOMM_CFG_FC; + } + } + /* Send FC_OFF response */ + cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */ + pbuf_header(p, RFCOMM_MSGHDR_LEN); + + rfcomm_uih(pcb, 0, p); + break; + case RFCOMM_FCOFF_RSP: + break; + case RFCOMM_MSC_CMD: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_MSC_CMD\n")); + mscreq = p->payload; + /* Find DLC */ + tpcb = rfcomm_get_active_pcb((mscreq->dlci >> 3), &pcb->l2cappcb->remote_bdaddr); + + if(tpcb != NULL) { + /* Set flow control bit. Ignore remaining fields in the MSC since this is a type 1 + device */ + if((mscreq->rs232 >> 1) & 0x01) { + tpcb->rfcommcfg |= RFCOMM_CFG_FC; + } else { + tpcb->rfcommcfg &= ~RFCOMM_CFG_FC; + } + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcommm_process_msg: fc bit = %d\n", (mscreq->rs232 >> 1) & 0x01)); + + /* Send MSC response */ + cmdhdr->type = cmdhdr->type & 0xFD; /* Set C/R to response */ + pbuf_header(p, RFCOMM_MSGHDR_LEN); + + if(!(tpcb->rfcommcfg & RFCOMM_CFG_IR) && !(tpcb->rfcommcfg & RFCOMM_CFG_MSC_IN)) { /* We are the responder and should send a MSC command before responding to one */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcommm_process_msg: We are the responder and should send a MSC command before responding to one\n")); + rfcomm_msc(tpcb, 0, NULL); + } + + rfcomm_uih(pcb, 0, p); + + tpcb->rfcommcfg |= RFCOMM_CFG_MSC_OUT; + + if(tpcb->rfcommcfg & RFCOMM_CFG_MSC_IN && tpcb->state != RFCOMM_OPEN) { + tpcb->state = RFCOMM_OPEN; + if(tpcb->rfcommcfg & RFCOMM_CFG_IR) { + RFCOMM_EVENT_CONNECTED(tpcb,ERR_OK,ret); + } else { + RFCOMM_EVENT_ACCEPT(tpcb,ERR_OK,ret); + } + } + } /* else silently discard */ + break; + case RFCOMM_MSC_RSP: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_MSC_RSP\n")); + /* Information received in response is only a copy of the signals that where sent in the command */ + pcb->to = 0; /* Reset response timer */ + + mscreq = p->payload; + + /* Find PCB with matching server channel number and Bluetooth address */ + tpcb = rfcomm_get_active_pcb((mscreq->dlci >> 3), &pcb->l2cappcb->remote_bdaddr); + + if(tpcb != NULL) { + + if(tpcb->rfcommcfg & RFCOMM_CFG_MSC_IN) { + RFCOMM_EVENT_MSC(tpcb,ERR_OK,ret); /* We have sent a MSC after initial configuration of + the connection was done */ + } else { + tpcb->rfcommcfg |= RFCOMM_CFG_MSC_IN; + if(tpcb->rfcommcfg & RFCOMM_CFG_MSC_OUT) { + tpcb->state = RFCOMM_OPEN; + if(tpcb->rfcommcfg & RFCOMM_CFG_IR) { + RFCOMM_EVENT_CONNECTED(tpcb,ERR_OK,ret); + } else { + RFCOMM_EVENT_ACCEPT(tpcb,ERR_OK,ret); + } + } + } + } /* else silently discard */ + break; + case RFCOMM_RPN_CMD: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_RPN_CMD\n")); + /* Send RPN response */ + if(cmdhdr->len == 8) { + /* RPN command was a request to set up the link's parameters */ + /* All parameters accepted since this is a type 1 device */ + cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */ + pbuf_header(p, RFCOMM_MSGHDR_LEN); + //rfcomm_uih(pcb->l2cappcb, rfcommhdr, p); + rfcomm_uih(pcb, 0, p); + } else if(cmdhdr->len == 1) { + /* RPN command was a request for the link's parameters */ + if((q = pbuf_alloc(PBUF_RAW, RFCOMM_RPNMSG_LEN+RFCOMM_MSGHDR_LEN, PBUF_RAM)) == NULL) + { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: Could not allocate memory at line: %d\n", __LINE__ - 1)); + break; + } + rsphdr = q->payload; + rsphdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */ + rsphdr->len = RFCOMM_RPNMSG_LEN; + pbuf_header(q, -RFCOMM_MSGHDR_LEN); + rpnreq = q->payload; + rpnreq->dlci = ((u8_t *)p->payload)[0]; /* Copy DLCI from command to response */ + rpnreq->br = RFCOMM_COM_BR; /* Default baud rate */ + rpnreq->cfg = RFCOMM_COM_CFG; /* Default data bits, stop bits, parity and parity type */ + rpnreq->fc = RFCOMM_COM_FC; /* Default flow control */ + rpnreq->xon = RFCOMM_COM_XON; /* Default */ + rpnreq->xoff = RFCOMM_COM_XOFF; /* Default */ + rpnreq->mask = 0xFFFF; /* All parameters are valid */ + pbuf_header(q, RFCOMM_MSGHDR_LEN); + + rfcomm_uih(pcb, 0, q); + pbuf_free(q); + } else { + //SHOULD NOT HAPPEN. LENGTH SHOULD ALWAYS BE 1 OR 8 + } + break; + case RFCOMM_RPN_RSP: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_RPN_CMD\n")); + pcb->to = 0; /* Reset response timer */ + rpnreq = p->payload; + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_msc: rpn response received 0x%x\n", rpnreq->br)); + + /* Find PCB with matching server channel number and bluetooth address */ + tpcb = rfcomm_get_active_pcb((rpnreq->dlci >> 3), &pcb->l2cappcb->remote_bdaddr); + + if(tpcb != NULL) { + RFCOMM_EVENT_RPN(tpcb,ERR_OK,ret); + } + break; + case RFCOMM_RLS_CMD: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: RFCOMM_RLS_CMD\n")); + /* Send RLS response */ + cmdhdr->type = cmdhdr->type & 0xBF; /* Set C/R to response */ + pbuf_header(p, RFCOMM_MSGHDR_LEN); + + rfcomm_uih(pcb, 0, p); + break; + case RFCOMM_RLS_RSP: + break; + case RFCOMM_NSC_RSP: + break; + default: + /* Send NSC response */ + if ((q = pbuf_alloc(PBUF_RAW, RFCOMM_MSGHDR_LEN, PBUF_RAM)) == NULL) + { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_process_msg: Could not allocate memory at line: %d\n", __LINE__ - 1)); + break; + } + rsphdr = q->payload; + rsphdr->type = ((cmdhdr->type & 0x03) << 0) | (RFCOMM_NSC_RSP << 2); + rsphdr->len = 0; + + rfcomm_uih(pcb, 0, q); + pbuf_free(q); + break; + } +} + +/* + * rfcomm_input(): + * + * Called by the lower layer. Does a frame check, parses the header and forward it to + * the upper layer or handle the command frame. + */ +err_t rfcomm_input(void *arg, struct l2cap_pcb *l2cappcb, struct pbuf *p, err_t err) +{ + struct rfcomm_hdr rfcommhdr; + struct rfcomm_pcb *pcb, *tpcb; + struct rfcomm_pcb_listen *lpcb; + + s16_t len = 0; + u8_t hdrlen; + u8_t fcs; + + struct pbuf *q; + err_t ret; + + rfcommhdr.addr = *((u8_t *)p->payload); + rfcommhdr.ctrl = ((u8_t *)p->payload)[1]; + + /* Find PCB with matching server channel number and bluetooth address */ + pcb = rfcomm_get_active_pcb((rfcommhdr.addr >> 3), &l2cappcb->remote_bdaddr); + + if(pcb == NULL && rfcommhdr.ctrl != RFCOMM_SABM) { + /* Channel does not exist */ + if(rfcommhdr.ctrl != RFCOMM_DM_PF && rfcommhdr.ctrl != RFCOMM_DM) { + /* Send a DM response */ + LWIP_DEBUGF(RFCOMM_DEBUG,("Send a DM response to CN %d rfcomm.ctrl == 0x%x\n", (rfcommhdr.addr >> 3), rfcommhdr.ctrl)); + rfcomm_dm(l2cappcb, &rfcommhdr); + } /* else silently discard packet */ + pbuf_free(p); + return ERR_OK; + } + + /* Check if length field is 1 or 2 bytes long and remove EA bit */ + if((((u8_t *)p->payload)[2] & 0x01) == 1) { + hdrlen = RFCOMM_HDR_LEN_1; + rfcommhdr.len = (((u8_t *)p->payload)[2] >> 1) & 0x007F; + } else { + hdrlen = RFCOMM_HDR_LEN_2; + rfcommhdr.len = (((u16_t *)p->payload)[1] >> 1) & 0x7FFF; + } + + if(rfcommhdr.ctrl == RFCOMM_UIH_PF) { + if(pcb->cl == 0xF) { + rfcommhdr.k = ((u8_t *)p->payload)[hdrlen++]; + } + } + + /* Frame check */ + for(q = p; q != NULL; q = q->next) { + len += q->len; + if(len > (rfcommhdr.len + hdrlen)) { + len -= q->len; + len = rfcommhdr.len - len; + len += hdrlen; + break; + } + } + + fcs = ((u8_t *)q->payload)[len]; + if(rfcommhdr.ctrl == RFCOMM_UIH) { + if(pcb->cn == 0) { + if(fcs != pcb->uih0_in_fcs) { /* Check against the precalculated fcs */ + /*if(fcs8_crc_check(p, RFCOMM_UIHCRC_CHECK_LEN, fcs) != 0) */ + /* Packet discarded due to failing frame check sequence */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: UIH packet discarded due to failing frame check sequence\n")); + pbuf_free(p); + return ERR_OK; + } + } + } else if(rfcommhdr.ctrl == RFCOMM_UIH) { + if(fcs != pcb->uih_in_fcs) { /* Check against the precalculated fcs */ + /*if(fcs8_crc_check(p, RFCOMM_UIHCRC_CHECK_LEN, fcs) != 0) */ + /* Packet discarded due to failing frame check sequence */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: UIH packet discarded due to failing frame check sequence\n")); + pbuf_free(p); + return ERR_OK; + } + } else if(rfcommhdr.ctrl == RFCOMM_UIH_PF) { + if(fcs != pcb->uihpf_in_fcs) { /* Check against the precalculated fcs */ + /*if(fcs8_crc_check(p, RFCOMM_UIHCRC_CHECK_LEN, fcs) != 0) */ + /* Packet discarded due to failing frame check sequence */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: UIH_PF packet discarded due to failing frame check sequence RFCS = 0x%x LFCS = 0x%x\n", + fcs, pcb->uihpf_in_fcs)); + pbuf_free(p); + return ERR_OK; + } + } else { + if(fcs8_crc_check(p, hdrlen, fcs) != 0) { + /* Packet discarded due to failing frame check sequence */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Packet discarded due to failing frame check sequence\n")); + pbuf_free(p); + return ERR_OK; + } + } + + pbuf_header(p, -hdrlen); /* Adjust information pointer */ + pbuf_realloc(p, rfcommhdr.len); /* Remove fcs from packet */ + + switch(rfcommhdr.ctrl) { + case RFCOMM_SABM: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_SABM\n")); + if(pcb == NULL) { + /* Check if the server channel exists */ + lpcb = NULL; + if(rfcommhdr.addr >> 3 == 0) { /* Only the multiplexer channel can be connected without first + configuring it */ + for(lpcb = rfcomm_listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if(lpcb->cn == (rfcommhdr.addr >> 3)) { + break; + } + } + } + if(lpcb != NULL) { + /* Found a listening pcb with a matching server channel number, now initiate a + * new active PCB with default configuration */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Allocate RFCOMM PCB for CN %d******************************\n", lpcb->cn)); + if((pcb = rfcomm_new(l2cappcb)) == NULL) { + /* No memory to allocate PCB. Refuse connection attempt */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: No memory to allocate PCB. Refuse connection attempt CN %d******************************\n", lpcb->cn)); + rfcomm_dm(l2cappcb, &rfcommhdr); + pbuf_free(p); + return ERR_OK; + } + pcb->cn = lpcb->cn; + pcb->callback_arg = lpcb->callback_arg; + pcb->accept = lpcb->accept; + + RFCOMM_REG(&rfcomm_active_pcbs, pcb); + } else { + /* Channel does not exist or multiplexer is not connected, refuse connection with DM frame */ + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Channel does not exist, refuse connection with DM frame CN %d\n", (rfcommhdr.addr >> 3))); + rfcomm_dm(l2cappcb, &rfcommhdr); + pbuf_free(p); + break; + } + } + /* Set role to responder */ + pcb->rfcommcfg &= ~RFCOMM_CFG_IR; + + /* Send UA frame as response to SABM frame */ + rfcomm_ua(l2cappcb, &rfcommhdr); + + /* FCS precalculation for UIH frames */ + pbuf_header(p, hdrlen); /* Reuse the buffer for the current header */ + + /* Change header values to refelct an UIH frame sent to the initiator */ + *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0. We are the responder */ + *((u8_t *)p->payload) &= 0xFD; /* Set C/R bit to 0. We are the responder */ + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + pcb->uih_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + ((u8_t *)p->payload)[1] = RFCOMM_UIH_PF; + pcb->uihpf_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + + /* Change header values to refelct an UIH frame from to the initiator */ + //*((u8_t *)p->payload) |= 0x04; /* Set direction bit to 1. We are the responder */ + *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0. We are the responder */ + *((u8_t *)p->payload) |= 0x02; /* Set C/R bit to 1. We are the responder */ + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + pcb->uih_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + ((u8_t *)p->payload)[1] = RFCOMM_UIH_PF; + pcb->uihpf_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + + /* UIH frame received on the control channel */ + *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0 */ + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + pcb->uih0_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + + /* Change header values to reflect an UIH frame sent on the control channel */ + *((u8_t *)p->payload) &= 0xF9; /* Set C/R bit and direction bit to 0 */ + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + pcb->uih0_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + + pbuf_free(p); + break; + case RFCOMM_UA: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_UA\n")); + pcb->to = 0; + if(pcb->state == W4_RFCOMM_SABM_RSP) { + pcb->state = RFCOMM_CFG; + /* FCS precalculation for UIH frames */ + pbuf_header(p, hdrlen); /* Reuse the buffer for the current header */ + /* Change header values to refelct an UIH frame sent to the responder */ + *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0. We are the initiator */ + *((u8_t *)p->payload) |= 0x02; /* Set C/R bit to 1. We are the intitiator */ + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + pcb->uih_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + ((u8_t *)p->payload)[1]= RFCOMM_UIH_PF; + pcb->uihpf_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + + /* Change header values to reflect an UIH frame sent to the responder */ + *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0. We are the intitiator */ + *((u8_t *)p->payload) &= 0xFD; /* Set C/R bit to 0. We are the initiator */ + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + pcb->uih_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + ((u8_t *)p->payload)[1] = RFCOMM_UIH_PF; + pcb->uihpf_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + + /* UIH frame sent on the control channel */ + *((u8_t *)p->payload) &= 0xFB; /* Set direction bit to 0 */ + *((u8_t *)p->payload) |= 0x02; /* Set C/R bit to 1. We are the intitiator */ + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + pcb->uih0_out_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + + /* Change header values to reflect an UIH frame received on the control channel */ + *((u8_t *)p->payload) &= 0xF9; /* Set C/R bit and direction bit to 0 */ + ((u8_t *)p->payload)[1] = RFCOMM_UIH; + pcb->uih0_in_fcs = fcs8_crc_calc(p, RFCOMM_UIHCRC_CHECK_LEN); + + if(pcb->cn == 0) { + for(tpcb = rfcomm_active_pcbs; tpcb != NULL; tpcb = tpcb->next) { + if(bd_addr_cmp(&(tpcb->l2cappcb->remote_bdaddr), &(pcb->l2cappcb->remote_bdaddr)) && + tpcb->state == W4_RFCOMM_MULTIPLEXER) { + rfcomm_pn(tpcb, NULL); /* Send a parameter negotiation command to negotiate the + connection settings for a channel that waits for the + multiplexer connection to be established */ + break; + } + } + } else { + rfcomm_msc(pcb, 0, NULL); /* Send a modem status command to set V.24 control signals for + the RFCOMM connection */ + } + } else if (pcb->state == W4_RFCOMM_DISC_RSP) { + //RFCOMM_RMV(&rfcomm_active_pcbs, pcb); + pcb->state = RFCOMM_CLOSED; + RFCOMM_EVENT_DISCONNECTED(pcb,ERR_OK,ret); + } else { + /* A response without an outstanding request is silently discarded */ + } + pbuf_free(p); + break; + case RFCOMM_DM_PF: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_DM_PF\n")); + case RFCOMM_DM: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_DM\n")); + pcb->to = 0; + //RFCOMM_RMV(&rfcomm_active_pcbs, pcb); + if(pcb->state == W4_RFCOMM_SABM_RSP) { + pcb->state = RFCOMM_CLOSED; + RFCOMM_EVENT_CONNECTED(pcb,ERR_CONN,ret); + } else { + pcb->state = RFCOMM_CLOSED; + RFCOMM_EVENT_DISCONNECTED(pcb,ERR_CONN,ret); + } + pbuf_free(p); + break; + case RFCOMM_DISC: + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: RFCOMM_DISC\n")); + //RFCOMM_RMV(&rfcomm_active_pcbs, pcb); + /* Send UA frame as response to DISC frame */ + ret = rfcomm_ua(l2cappcb, &rfcommhdr); + pcb->state = RFCOMM_CLOSED; + RFCOMM_EVENT_DISCONNECTED(pcb,ERR_OK,ret); + pbuf_free(p); + break; + case RFCOMM_UIH_PF: + if((rfcommhdr.addr >> 3) == 0) { + /* Process multiplexer command/response */ + rfcomm_process_msg(pcb, &rfcommhdr, l2cappcb, p); + pbuf_free(p); + } else if(pcb->cl == 0xF) { + /* Process credit based frame */ + if(pcb->rk != 0) { + --pcb->rk; /* Decrease remote credits */ + } + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Received local credits: %d Existing local credits: %d\n", rfcommhdr.k, pcb->k)); + if((pcb->k + rfcommhdr.k) < 255) { + pcb->k += rfcommhdr.k; /* Increase local credits */ +#if RFCOMM_FLOW_QUEUEING + q = pcb->buf; + /* Queued packet present? */ + if (q != NULL) { + /* NULL attached buffer immediately */ + pcb->buf = NULL; + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: sending queued packet.\n")); + /* Send the queued packet */ + rfcomm_uih(pcb, pcb->cn, q); + /* Free the queued packet */ + pbuf_free(q); + } +#endif /* RFCOMM_FLOW_QUEUEING */ + } else { + pcb->k = 255; + } + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Forward RFCOMM_UIH_PF credit packet to higher layer\n")); + RFCOMM_EVENT_RECV(pcb,ERR_OK,p,ret); /* Process information. Application must free pbuf */ + } else { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Forward RFCOMM_UIH_PF non credit packet to higher layer\n")); + RFCOMM_EVENT_RECV(pcb,ERR_OK,p,ret); /* Process information. Application must free pbuf */ + } + break; + case RFCOMM_UIH: + if((rfcommhdr.addr >> 3) == 0) { + /* Process multiplexer command/response */ + rfcomm_process_msg(pcb, &rfcommhdr, l2cappcb, p); + pbuf_free(p); + } else { + if(pcb->rk != 0) { + --pcb->rk; /* Decrease remote credits */ + } + + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_input: Forward RFCOMM_UIH packet to higher layer\n")); + RFCOMM_EVENT_RECV(pcb,ERR_OK,p,ret); /* Process information. Application must free pbuf */ + } + break; + default: + /* Unknown or illegal frame type. Throw it away! */ + pbuf_free(p); + break; + } + return ERR_OK; +} + +/* + * rfcomm_arg(): + * + * Used to specify the argument that should be passed callback functions. + */ +void rfcomm_arg(struct rfcomm_pcb *pcb, void *arg) +{ + pcb->callback_arg = arg; +} + +/* + * rfcomm_recv(): + * + * Used to specify the function that should be called when a RFCOMM connection + * receives data. + */ +void rfcomm_recv(struct rfcomm_pcb *pcb, + err_t (* recv)(void *arg, struct rfcomm_pcb *pcb, struct pbuf *p, err_t err)) +{ + pcb->recv = recv; +} + +/* + * rfcomm_disc(): + * + * Used to specify the function that should be called when a RFCOMM channel is + * disconnected + */ +void rfcomm_disc(struct rfcomm_pcb *pcb, + err_t (* disc)(void *arg, struct rfcomm_pcb *pcb, err_t err)) +{ + pcb->disconnected = disc; +} + +/* + * rfcomm_listen(): + * + * Set the state of the connection to be LISTEN, which means that it is + * able to accept incoming connections. The protocol control block is + * reallocated in order to consume less memory. Setting the connection to + * LISTEN is an irreversible process. Also specify the function that should + * be called when the channel has been connected. + */ +err_t rfcomm_listen(struct rfcomm_pcb *npcb, u8_t cn, + err_t (* accept)(void *arg, struct rfcomm_pcb *pcb, err_t err)) +{ + struct rfcomm_pcb_listen *lpcb; + + if((lpcb = lwbt_memp_malloc(MEMP_RFCOMM_PCB_LISTEN)) == NULL) { + LWIP_DEBUGF(RFCOMM_DEBUG, ("rfcomm_listen: Could not allocate memory for pcb\n")); + return ERR_MEM; + } + lpcb->cn = cn; + lpcb->callback_arg = npcb->callback_arg; + lpcb->accept = accept; + lpcb->state = RFCOMM_LISTEN; + + lwbt_memp_free(MEMP_RFCOMM_PCB, npcb); + RFCOMM_REG((struct rfcomm_pcb **)&rfcomm_listen_pcbs, (struct rfcomm_pcb *)lpcb); + return ERR_OK; +} diff --git a/kernel/bluetooth/lwbt/sdp.c b/kernel/bluetooth/lwbt/sdp.c new file mode 100644 index 0000000..8f7c668 --- /dev/null +++ b/kernel/bluetooth/lwbt/sdp.c @@ -0,0 +1,954 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + + +/* sdp.c + * + * Implementation of the service discovery protocol (SDP) + */ + + +#include "lwip/opt.h" +#include "lwbt/sdp.h" +#include "lwbt/lwbt_memp.h" + +#include "lwip/err.h" +#include "lwip/pbuf.h" + +#include "lwip/debug.h" +#include "lwip/inet.h" + +/* Next service record handle to be used */ +u32_t rhdl_next; + +/* Next transaction id to be used */ +u16_t tid_next; + +/* The SDP PCB lists */ +struct sdp_pcb *sdp_pcbs; +struct sdp_pcb *sdp_tmp_pcb; + +/* List of all active service records in the SDP server */ +struct sdp_record *sdp_server_records; +struct sdp_record *sdp_tmp_record; /* Only used for temp storage */ + + +/* + * sdp_init(): + * + * Initializes the SDP layer. + */ +void sdp_init(void) +{ + /* Clear globals */ + sdp_server_records = NULL; + sdp_tmp_record = NULL; + + /* Inialize service record handles */ + rhdl_next = 0x0000FFFF; + + /* Initialize transaction ids */ + tid_next = 0x0000; +} + +/* Server API +*/ + + +/* + * sdp_next_rhdl(): + * + * Issues a service record handler. + */ +u32_t sdp_next_rhdl(void) +{ + ++rhdl_next; + if(rhdl_next == 0) { + rhdl_next = 0x0000FFFF; + } + return rhdl_next; +} + +/* + * sdp_record_new(): + * + * Creates a new service record. + */ +struct sdp_record * sdp_record_new(u8_t *record_de_list, u16_t rlen) +{ + struct sdp_record *record; + + record = lwbt_memp_malloc(MEMP_SDP_RECORD); + if(record != NULL) { + record->hdl = sdp_next_rhdl(); + record->record_de_list = record_de_list; + record->len = rlen; + return record; + } + return NULL; +} + +void sdp_record_free(struct sdp_record *record) +{ + lwbt_memp_free(MEMP_SDP_RECORD, record); +} + +/* + * sdp_register_service(): + * + * Add a record to the list of records in the service record database, making it + * available to clients. + */ +err_t sdp_register_service(struct sdp_record *record) +{ + if(record == NULL) { + return ERR_ARG; + } + SDP_RECORD_REG(&sdp_server_records, record); + return ERR_OK; +} + +/* + * sdp_unregister_service(): + * + * Remove a record from the list of records in the service record database, making it + * unavailable to clients. + */ +void sdp_unregister_service(struct sdp_record *record) +{ + SDP_RECORD_RMV(&sdp_server_records, record); +} + +/* + * sdp_next_transid(): + * + * Issues a transaction identifier that helps matching a request with the reply. + */ +u16_t sdp_next_transid(void) +{ + ++tid_next; + return tid_next; +} + +/* + * sdp_pattern_search(): + * + * Check if the given service search pattern matches the record. + */ +u8_t sdp_pattern_search(struct sdp_record *record, u8_t size, struct pbuf *p) +{ + u8_t i, j; + u8_t *payload = (u8_t *)p->payload; + + //TODO actually parse the request instead of going over each byte + for(i = 0; i < size; ++i) { + if(SDP_DE_TYPE(payload[i]) == SDP_DE_TYPE_UUID) { + switch(SDP_DE_SIZE(payload[i])) { + case SDP_DE_SIZE_16: + for(j = 0; j < record->len; ++j) { + if(SDP_DE_TYPE(record->record_de_list[j]) == SDP_DE_TYPE_UUID) { + if(*((u16_t *)(payload + i + 1)) == *((u16_t *)(record->record_de_list + j + 1))) { + return 1; /* Found a matching UUID in record */ + } + ++j; + } + } + i += 2; + break; + case SDP_DE_SIZE_32: + i += 4; + break; + case SDP_DE_SIZE_128: + LWIP_DEBUGF(SDP_DEBUG, ("TODO: add support for 128-bit UUID\n")); + i+= 16; + break; + default: + break; + } + } + } + return 1; //TODO change back to 0 +} + +/* + * sdp_attribute_search(): + * + * Searches a record for attributes and add them to a given packet buffer. + */ +struct pbuf * sdp_attribute_search(u16_t max_attribl_bc, struct pbuf *p, struct sdp_record *record) +{ + struct pbuf *q = NULL; + struct pbuf *r; + struct pbuf *s = NULL; + u8_t *payload = (u8_t *)p->payload; + u8_t size; + u8_t i = 0, j; + u16_t attr_id = 0, attr_id2 = 0; + + u16_t attribl_bc = 0; /* Byte count of the sevice attributes */ + u32_t hdl = htonl(record->hdl); + + if(SDP_DE_TYPE(payload[0]) == SDP_DE_TYPE_DES && + SDP_DE_SIZE(payload[0]) == SDP_DE_SIZE_N1) { + /* Get size of attribute ID list */ + size = payload[1]; //TODO: correct to assume only one size byte in remote request? probably + + while(i < size) { + /* Check if this is an attribute ID or a range of attribute IDs */ + if(payload[2+i] == (SDP_DE_TYPE_UINT | SDP_DE_SIZE_16)) { + attr_id = ntohs(*((u16_t *)(payload+3+i))); + attr_id2 = attr_id; /* For the range to cover this attribute ID only */ + i += 3; + } else if(payload[2+i] == (SDP_DE_TYPE_UINT | SDP_DE_SIZE_32)) { + attr_id = ntohs(*((u16_t *)(payload+3+i))); + attr_id2 = ntohs(*((u16_t *)(payload+5+i))); + i += 5; + } else { + /* ERROR: Invalid req syntax */ + LWIP_DEBUGF(SDP_DEBUG, ("sdp_attribute_search: Invalid req syntax\n")); + } + + LWIP_DEBUGF(SDP_DEBUG, ("sdp_attribute_search: looking for %04x-%04x\n", attr_id, attr_id2)); + + for(j = 0; j < record->len; ++j) { + if(SDP_DE_TYPE(record->record_de_list[j]) == SDP_DE_TYPE_DES) { + if(record->record_de_list[j + 2] == (SDP_DE_TYPE_UINT | SDP_DE_SIZE_16)) { + u16_t rec_id = ntohs(*((u16_t *)(record->record_de_list + j + 3))); + + LWIP_DEBUGF(SDP_DEBUG, ("sdp_attribute_search: looking at rec %04x\n", rec_id)); + + if(rec_id >= attr_id && rec_id <= attr_id2) { + if(attribl_bc + record->record_de_list[j + 1] + 2 > max_attribl_bc) { + /* Abort attribute search since attribute list byte count must not + exceed max attribute byte count in req */ + break; + } + /* Allocate a pbuf for the service attribute */ + ; + if((r = pbuf_alloc(PBUF_RAW, record->record_de_list[j + 1], PBUF_RAM)) == NULL) + { + LWIP_DEBUGF(SDP_DEBUG, ("couldn't alloc pbuf\n")); + return NULL; + } + + memcpy((u8_t *)r->payload, record->record_de_list + j + 2, r->len); + attribl_bc += r->len; + + /* If request included a service record handle attribute id, add the correct + * id to the response */ + if(rec_id == 0x0000) { + memcpy(((u8_t *)r->payload) + 4, &hdl, 4); + } + + /* Add the attribute to the service attribute list */ + if(s == NULL) { + s = r; + } else { + pbuf_chain(s, r); + pbuf_free(r); + } + } + } + } + } /* for */ + } /* while */ + } else { + /* ERROR: Invalid req syntax */ + LWIP_DEBUGF(SDP_DEBUG, ("sdp_attribute_search: Req not a data element list <255 bytes")); + } + /* Return service attribute list */ + if(s != NULL) { + q = pbuf_alloc(PBUF_RAW, 2, PBUF_RAM); + ((u8_t *)q->payload)[0] = SDP_DE_TYPE_DES | SDP_DE_SIZE_N1; + ((u8_t *)q->payload)[1] = s->tot_len; + pbuf_chain(q, s); + pbuf_free(s); + } + + return q; +} + +/* + * SDP CLIENT API. + */ + + +/* + * sdp_new(): + * + * Creates a new SDP protocol control block but doesn't place it on + * any of the SDP PCB lists. + */ +struct sdp_pcb * sdp_new(struct l2cap_pcb *l2cappcb) +{ + struct sdp_pcb *pcb; + + pcb = lwbt_memp_malloc(MEMP_SDP_PCB); + if(pcb != NULL) { + memset(pcb, 0, sizeof(struct sdp_pcb)); + pcb->l2cappcb = l2cappcb; + return pcb; + } + return NULL; +} + +/* + * sdp_free(): + * + * Free the SDP protocol control block. + */ +void sdp_free(struct sdp_pcb *pcb) +{ + lwbt_memp_free(MEMP_SDP_PCB, pcb); + pcb = NULL; +} + +/* + * sdp_reset_all(): + * + * Free all SDP protocol control blocks and registered records. + */ +void sdp_reset_all(void) +{ + struct sdp_pcb *pcb, *tpcb; + struct sdp_record *record, *trecord; + + for(pcb = sdp_pcbs; pcb != NULL;) { + tpcb = pcb->next; + SDP_RMV(&sdp_pcbs, pcb); + sdp_free(pcb); + pcb = tpcb; + } + + for(record = sdp_server_records; record != NULL;) { + trecord = record->next; + sdp_unregister_service(record); + sdp_record_free(record); + record = trecord; + } + + sdp_init(); +} + +/* + * sdp_arg(): + * + * Used to specify the argument that should be passed callback functions. + */ +void sdp_arg(struct sdp_pcb *pcb, void *arg) +{ + pcb->callback_arg = arg; +} + +/* + * sdp_lp_disconnected(): + * + * Called by the application to indicate that the lower protocol disconnected. + */ +void sdp_lp_disconnected(struct l2cap_pcb *l2cappcb) +{ + struct sdp_pcb *pcb, *tpcb; + + pcb = sdp_pcbs; + while(pcb != NULL) { + tpcb = pcb->next; + if(bd_addr_cmp(&(l2cappcb->remote_bdaddr), &(pcb->l2cappcb->remote_bdaddr))) { + /* We do not need to notify upper layer, free PCB */ + sdp_free(pcb); + } + pcb = tpcb; + } +} + +/* + * sdp_service_search_req(): + * + * Sends a request to a SDP server to locate service records that match the service + * search pattern. + */ +err_t sdp_service_search_req(struct sdp_pcb *pcb, u8_t *ssp, u8_t ssplen, + u16_t max_src, + void (* service_searched)(void *arg, struct sdp_pcb *pcb, u16_t tot_src, + u16_t curr_src, u32_t *rhdls)) +{ + struct pbuf *p; + struct sdp_hdr *sdphdr; + + /* Update PCB */ + pcb->tid = sdp_next_transid(); /* Set transaction id */ + + /* Allocate packet for PDU hdr + service search pattern + max service record count + + continuation state */ + p = pbuf_alloc(PBUF_RAW, SDP_PDUHDR_LEN+ssplen+2+1, PBUF_RAM); + sdphdr = p->payload; + /* Add PDU header to packet */ + sdphdr->pdu = SDP_SS_PDU; + sdphdr->id = htons(pcb->tid); + sdphdr->len = htons(ssplen + 3); /* Seq descr + ServiceSearchPattern + MaxServiceRecCount + ContState */ + + /* Add service search pattern to packet */ + memcpy(((u8_t *)p->payload) + SDP_PDUHDR_LEN, ssp, ssplen); + + /* Add maximum service record count to packet */ + *((u16_t *)(((u8_t *)p->payload) + ssplen + SDP_PDUHDR_LEN)) = htons(max_src); + + ((u8_t *)p->payload)[SDP_PDUHDR_LEN+ssplen+2] = 0; /* No continuation */ + + /* Update PCB */ + pcb->service_searched = service_searched; /* Set callback */ + SDP_REG(&sdp_pcbs, pcb); /* Register request */ + + return l2ca_datawrite(pcb->l2cappcb, p); +} + +/* + * sdp_service_attrib_req(): + * + * Retrieves specified attribute values from a specific service record. + */ +err_t sdp_service_attrib_req(struct sdp_pcb *pcb, u32_t srhdl, u16_t max_abc, + u8_t *attrids, u8_t attrlen, + void (* attributes_recv)(void *arg, struct sdp_pcb *pcb, + u16_t attribl_bc, struct pbuf *p)) +{ + struct sdp_hdr *sdphdr; + u8_t *payload; + struct pbuf *p; + + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_attrib_req")); + + /* Allocate packet for PDU hdr + service rec hdl + max attribute byte count + + attribute id data element sequense lenght + continuation state */ + p = pbuf_alloc(PBUF_RAW, SDP_PDUHDR_LEN + attrlen + 7, PBUF_RAM); + + /* Update PCB */ + pcb->tid = sdp_next_transid(); /* Set transaction id */ + + /* Add PDU header to packet */ + sdphdr = p->payload; + sdphdr->pdu = SDP_SA_PDU; + sdphdr->id = htons(pcb->tid); + sdphdr->len = htons((attrlen + 7)); /* Service rec hdl + Max attrib B count + Seq descr + Attribute sequence + ContState */ + + payload = p->payload; + + /* Add service record handle to packet */ + *((u32_t *)(payload + SDP_PDUHDR_LEN)) = htonl(srhdl); + + /* Add maximum attribute count to packet */ + *((u16_t *)(payload + SDP_PDUHDR_LEN + 4)) = htons(max_abc); + + /* Add attribute id data element sequence to packet */ + memcpy(payload + SDP_PDUHDR_LEN + 6, attrids, attrlen); + + payload[SDP_PDUHDR_LEN + 6 + attrlen] = 0x00; /* No continuation */ + + /* Update PCB */ + pcb->attributes_recv = attributes_recv; /* Set callback */ + SDP_REG(&sdp_pcbs, pcb); /* Register request */ + + return l2ca_datawrite(pcb->l2cappcb, p); +} + +/* + * sdp_service_search_attrib_req(): + * + * Combines the capabilities of the SDP_ServiceSearchRequest and the + * SDP_ServiceAttributeRequest into a single request. Contains both a service search + * pattern and a list of attributes to be retrieved from service records that match + * the service search pattern. + */ +err_t sdp_service_search_attrib_req(struct sdp_pcb *pcb, u16_t max_abc, + u8_t *ssp, u8_t ssplen, u8_t *attrids, u8_t attrlen, + void (* attributes_searched)(void *arg, struct sdp_pcb *pcb, + u16_t attribl_bc, struct pbuf *p)) +{ + struct sdp_hdr *sdphdr; + + struct pbuf *p; + u8_t *payload; + u16_t pbuf_bc; + + /* Allocate packet for PDU hdr + service search pattern + max attribute byte count + + attribute id list + continuation state */ + p = pbuf_alloc(PBUF_RAW, SDP_PDUHDR_LEN+ssplen+2+attrlen+1, PBUF_RAM); + + /* Update PCB */ + pcb->tid = sdp_next_transid(); /* Set transaction id */ + + /* Add PDU header to packet */ + sdphdr = p->payload; + sdphdr->pdu = SDP_SSA_PDU; + sdphdr->id = htons(pcb->tid); + sdphdr->len = htons(ssplen + 2 + attrlen + 1); + + pbuf_bc = SDP_PDUHDR_LEN; + payload = (u8_t *)p->payload; + + /* Add service search pattern to packet */ + memcpy(((u8_t *)p->payload) + SDP_PDUHDR_LEN, ssp, ssplen); + + /* Add maximum attribute count to packet */ + *((u16_t *)(payload + SDP_PDUHDR_LEN + ssplen)) = htons(max_abc); + + /* Add attribute id data element sequence to packet */ + memcpy(payload + SDP_PDUHDR_LEN + ssplen + 2, attrids, attrlen); + + payload[SDP_PDUHDR_LEN + ssplen + 2 + attrlen] = 0x00; /* No continuation */ + + pcb->attributes_searched = attributes_searched; /* Set callback */ + SDP_REG(&sdp_pcbs, pcb); /* Register request */ + + return l2ca_datawrite(pcb->l2cappcb, p); +} + +/* + * SDP SERVER API. + */ + + +/* + * sdp_service_search_rsp(): + * + * The SDP server sends a list of service record handles for service records that + * match the service search pattern given in the request. + */ +err_t sdp_service_search_rsp(struct l2cap_pcb *pcb, struct pbuf *p, struct sdp_hdr *reqhdr) +{ + struct sdp_record *record; + struct sdp_hdr *rsphdr; + + struct pbuf *q; /* response packet */ + struct pbuf *r; /* tmp buffer */ + + u16_t max_src = 0; + u16_t curr_src = 0; + u16_t tot_src = 0; + + u8_t size = 0; + + err_t ret; + + if(SDP_DE_TYPE(((u8_t *)p->payload)[0]) == SDP_DE_TYPE_DES && + SDP_DE_SIZE(((u8_t *)p->payload)[0]) == SDP_DE_SIZE_N1) { + /* Size of the search pattern must be in the next byte since only + 12 UUIDs are allowed in one pattern */ + size = ((u8_t *)p->payload)[1]; + + /* Get maximum service record count that follows the service search pattern */ + max_src = ntohs(*((u16_t *)(((u8_t *)p->payload)+(2+size)))); + + pbuf_header(p, -2); + } else { + //TODO: INVALID SYNTAX ERROR + } + + /* Allocate header + Total service rec count + Current service rec count */ + q = pbuf_alloc(PBUF_RAW, SDP_PDUHDR_LEN+4, PBUF_RAM); + + rsphdr = q->payload; + rsphdr->pdu = SDP_SSR_PDU; + rsphdr->id = reqhdr->id; + + for(record = sdp_server_records; record != NULL; record = record->next) { + /* Check if service search pattern matches record */ + if(sdp_pattern_search(record, size, p)) { + if(max_src > 0) { + /* Add service record handle to packet */ + r = pbuf_alloc(PBUF_RAW, 4, PBUF_RAM); + *((u32_t *)r->payload) = htonl(record->hdl); + pbuf_chain(q, r); + pbuf_free(r); + --max_src; + ++curr_src; + } + ++tot_src; + } + } + + /* Add continuation state to packet */ + r = pbuf_alloc(PBUF_RAW, 1, PBUF_RAM); + ((u8_t *)r->payload)[0] = 0x00; + pbuf_chain(q, r); + pbuf_free(r); + + /* Add paramenter length to header */ + rsphdr->len = htons(q->tot_len - SDP_PDUHDR_LEN); + + /* Add total service record count to packet */ + *((u16_t *)(((u8_t *)q->payload) + SDP_PDUHDR_LEN)) = htons(tot_src); + + /* Add current service record count to packet */ + *((u16_t *)(((u8_t *)q->payload) + SDP_PDUHDR_LEN + 2)) = htons(curr_src); + + + { + u16_t i; + for(r = q; r != NULL; r = r->next) { + for(i = 0; i < r->len; ++i) { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_search_rsp: 0x%x\n", ((u8_t *)r->payload)[i])); + } + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_search_rsp: STOP\n")); + } + } + + ret = l2ca_datawrite(pcb, q); + pbuf_free(q); + return ret; +} + +/* + * sdp_service_attrib_rsp(): + * + * Sends a response that contains a list of attributes (both attribute ID and + * attribute value) from the requested service record. + */ +err_t sdp_service_attrib_rsp(struct l2cap_pcb *pcb, struct pbuf *p, struct sdp_hdr *reqhdr) +{ + struct sdp_record *record; + struct sdp_hdr *rsphdr; + + struct pbuf *q; + struct pbuf *r; + + u16_t max_attribl_bc = 0; /* Maximum attribute list byte count */ + + err_t ret; + + /* Find record */ + for(record = sdp_server_records; record != NULL; record = record->next) { + if(record->hdl == ntohl(*((u32_t *)p->payload))) { + break; + } + } + if(record != NULL) { + /* Get maximum attribute byte count */ + max_attribl_bc = ntohs(((u16_t *)p->payload)[2]); + + /* Allocate rsp packet header + Attribute list count */ + q = pbuf_alloc(PBUF_RAW, SDP_PDUHDR_LEN+2, PBUF_RAM); + rsphdr = q->payload; + rsphdr->pdu = SDP_SAR_PDU; + rsphdr->id = reqhdr->id; + + /* Search for attributes and add them to a pbuf */ + pbuf_header(p, -6); + r = sdp_attribute_search(max_attribl_bc, p, record); + if(r != NULL) { + /* Add attribute list byte count length to header */ + *((u16_t *)(((u8_t *)q->payload) + SDP_PDUHDR_LEN)) = htons(r->tot_len); + pbuf_chain(q, r); /* Chain attribute id list for service to response packet */ + pbuf_free(r); + } else { + *((u16_t *)(((u8_t *)q->payload) + SDP_PDUHDR_LEN)) = 0; + } + + /* Add continuation state to packet */ + r = pbuf_alloc(PBUF_RAW, 1, PBUF_RAM); + ((u8_t *)r->payload)[0] = 0x00; //TODO: Is this correct? + pbuf_chain(q, r); + pbuf_free(r); + + /* Add paramenter length to header */ + rsphdr->len = htons(q->tot_len - SDP_PDUHDR_LEN); + + { + u16_t i; + for(r = q; r != NULL; r = r->next) { + for(i = 0; i < r->len; ++i) { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_attrib_rsp: 0x%02x\n", ((u8_t *)r->payload)[i])); + } + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_attrib_rsp: STOP\n")); + } + } + + ret = l2ca_datawrite(pcb, q); + pbuf_free(q); + + return ret; + } + //TODO: ERROR NO SERVICE RECORD MATCHING HANDLE FOUND + return ERR_OK; +} + +/* + * sdp_service_search_attrib_rsp(): + * + * Sends a response that contains a list of attributes (both attribute ID and + * attribute value) from the service records that match the requested service + * search pattern. + */ +err_t sdp_service_search_attrib_rsp(struct l2cap_pcb *pcb, struct pbuf *p, struct sdp_hdr *reqhdr) +{ + struct sdp_record *record; + struct sdp_hdr *rsphdr; + + struct pbuf *q; /* response packet */ + struct pbuf *r = NULL; /* tmp buffer */ + struct pbuf *s = NULL; /* tmp buffer */ + + err_t ret; + + u16_t max_attribl_bc = 0; + u8_t size = 0; + + /* Get size of service search pattern */ + if(SDP_DE_TYPE(((u8_t *)p->payload)[0]) == SDP_DE_TYPE_DES && + SDP_DE_SIZE(((u8_t *)p->payload)[0]) == SDP_DE_SIZE_N1) { + /* Size of the search pattern must be in the next byte since only + 12 UUIDs are allowed in one pattern */ + size = ((u8_t *)p->payload)[1]; + + /* Get maximum attribute byte count that follows the service search pattern */ + max_attribl_bc = ntohs(*((u16_t *)(((u8_t *)p->payload)+(2+size)))); + + pbuf_header(p, -2); + } else { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_search_attrib_rsp: invalid syntax error\n")); + //TODO: INVALID SYNTAX ERROR + } + + /* Allocate header + attribute list count */ + q = pbuf_alloc(PBUF_RAW, SDP_PDUHDR_LEN + 2, PBUF_RAM); + + rsphdr = q->payload; + rsphdr->pdu = SDP_SSAR_PDU; + rsphdr->id = reqhdr->id; + + for(record = sdp_server_records; record != NULL; record = record->next) { + /* Check if service search pattern matches record */ + if(sdp_pattern_search(record, size, p)) { + /* Search for attributes and add them to a pbuf */ + pbuf_header(p, -(size + 2)); + r = sdp_attribute_search(max_attribl_bc, p, record); + if(r != NULL) { + if(q->next == NULL) { + if((s = pbuf_alloc(PBUF_RAW, 2, PBUF_RAM)) == NULL) + { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_search_attrib_rsp: couldn't alloc pbuf")); + return ERR_MEM; + } + /* Chain attribute id list for service to response packet */ + pbuf_chain(q, s); + pbuf_free(s); + } + /* Calculate remaining number of bytes of attribute data the + * server is to return in response to the request */ + max_attribl_bc -= r->tot_len; + /* Chain attribute id list for service to response packet */ + pbuf_chain(q, r); + pbuf_free(r); + } + pbuf_header(p, size + 2); + } + } + + /* Add attribute list byte count length and length of all attribute lists + * in this PDU to packet */ + if(q->next != NULL ) { + *((u16_t *)(((u8_t *)q->payload) + SDP_PDUHDR_LEN)) = htons(q->tot_len - SDP_PDUHDR_LEN - 2); + + ((u8_t *)q->next->payload)[0] = SDP_DES_SIZE8; + ((u8_t *)q->next->payload)[1] = q->tot_len - SDP_PDUHDR_LEN - 4; + } else { + *((u16_t *)(((u8_t *)q->payload) + SDP_PDUHDR_LEN)) = 0; + } + + /* Add continuation state to packet */ + if((r = pbuf_alloc(PBUF_RAW, 1, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_search_attrib_rsp: error allocating new pbuf\n")); + return ERR_MEM; + //TODO: ERROR + } else { + ((u8_t *)r->payload)[0] = 0x00; //TODO: Is this correct? + pbuf_chain(q, r); + pbuf_free(r); + } + + /* Add paramenter length to header */ + rsphdr->len = htons(q->tot_len - SDP_PDUHDR_LEN); + +#if SDP_DEBUG == LWIP_DBG_ON + for(r = q; r != NULL; r = r->next) { + u8_t i; + for(i = 0; i < r->len; ++i) { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_search_attrib_rsp: 0x%02x\n", ((u8_t *)r->payload)[i])); + } + LWIP_DEBUGF(SDP_DEBUG, ("sdp_service_search_attrib_rsp: next pbuf\n")); + } +#endif + + ret = l2ca_datawrite(pcb, q); + pbuf_free(q); + return ret; +} + +/* + * sdp_recv(): + * + * Called by the lower layer. Parses the header and handle the SDP message. + */ +err_t sdp_recv(void *arg, struct l2cap_pcb *pcb, struct pbuf *s, err_t err) +{ + struct sdp_hdr *sdphdr; + struct sdp_pcb *sdppcb; + err_t ret = ERR_OK; + u16_t i; + struct pbuf *p, *q, *r; + + if(s->len != s->tot_len) { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Fragmented packet received. Reassemble into one buffer\n")); + if((p = pbuf_alloc(PBUF_RAW, s->tot_len, PBUF_RAM)) != NULL) { + i = 0; + for(r = s; r != NULL; r = r->next) { + memcpy(((u8_t *)p->payload) + i, r->payload, r->len); + i += r->len; + } + pbuf_free(s); + } else { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Could not allocate buffer for fragmented packet\n")); + pbuf_free(s); + return ERR_MEM; + } + } else { + p = s; + } + + for(r = p; r != NULL; r = r->next) { + for(i = 0; i < r->len; ++i) { + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: 0x%02x\n", ((u8_t *)r->payload)[i])); + } + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: STOP\n")); + } + + sdphdr = p->payload; + pbuf_header(p, -SDP_PDUHDR_LEN); + + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: pdu=%02x\n", sdphdr->pdu)); + + switch(sdphdr->pdu) { + case SDP_ERR_PDU: + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Error response 0x%x\n", ntohs(*((u16_t *)p->payload)))); + pbuf_free(p); + break; + case SDP_SS_PDU: /* Client request */ + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Service search request\n")); + ret = sdp_service_search_rsp(pcb, p, sdphdr); + pbuf_free(p); + break; + case SDP_SSR_PDU: /* Server response */ + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Service search response\n")); + /* Find the original request */ + for(sdppcb = sdp_pcbs; sdppcb != NULL; sdppcb = sdppcb->next) { + if(sdppcb->tid == ntohs(sdphdr->id)) { + break; /* Found */ + } /* if */ + } /* for */ + if(sdppcb != NULL) { + /* Unregister the request */ + SDP_RMV(&sdp_pcbs, sdppcb); + /* Callback function for a service search response */ + SDP_ACTION_SERVICE_SEARCHED(sdppcb, ntohs(((u16_t *)p->payload)[0]), ntohs(((u16_t *)p->payload)[1]), ((u32_t *)p->payload) + 1); + } + pbuf_free(p); + break; + case SDP_SA_PDU: + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Service attribute request\n")); + ret = sdp_service_attrib_rsp(pcb, p, sdphdr); + pbuf_free(p); + break; + case SDP_SAR_PDU: + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Service attribute response\n")); + /* Find the original request */ + for(sdppcb = sdp_pcbs; sdppcb != NULL; sdppcb = sdppcb->next) { + if(sdppcb->tid == ntohs(sdphdr->id)) { + /* Unregister the request */ + SDP_RMV(&sdp_pcbs, sdppcb); + /* If packet is divided into several pbufs we need to merge them */ + if(p->next != NULL) { + r = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + i = 0; + for(q = p; q != NULL; q = q->next) { + memcpy(((u8_t *)r->payload)+i, q->payload, q->len); + i += q->len; + } + pbuf_free(p); + p = r; + } + i = *((u16_t *)p->payload); + pbuf_header(p, -2); + /* Callback function for a service attribute response */ + SDP_ACTION_ATTRIB_RECV(sdppcb, i, p); + } /* if */ + } /* for */ + pbuf_free(p); + break; + case SDP_SSA_PDU: + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Service search attribute request\n")); + ret = sdp_service_search_attrib_rsp(pcb, p, sdphdr); + pbuf_free(p); + break; + case SDP_SSAR_PDU: + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: Service search attribute response\n")); + /* Find the original request */ + for(sdppcb = sdp_pcbs; sdppcb != NULL; sdppcb = sdppcb->next) { + if(sdppcb->tid == ntohs(sdphdr->id)) { + /* Unregister the request */ + SDP_RMV(&sdp_pcbs, sdppcb); + /* If packet is divided into several pbufs we need to merge them */ + if(p->next != NULL) { + r = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + i = 0; + for(q = p; q != NULL; q = q->next) { + memcpy(((u8_t *)r->payload)+i, q->payload, q->len); + i += q->len; + } + pbuf_free(p); + p = r; + } + i = *((u16_t *)p->payload); + pbuf_header(p, -2); + /* Callback function for a service search attribute response */ + SDP_ACTION_ATTRIB_SEARCHED(sdppcb, i, p); + break; /* Abort request search */ + } /* if */ + } /* for */ + pbuf_free(p); + break; + default: + LWIP_DEBUGF(SDP_DEBUG, ("sdp_recv: syntax error\n")); + ret = ERR_VAL; + break; + } + return ret; +} + diff --git a/kernel/bluetooth/lwbt/uartif.c b/kernel/bluetooth/lwbt/uartif.c new file mode 100644 index 0000000..d5c96ec --- /dev/null +++ b/kernel/bluetooth/lwbt/uartif.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#include "lwbt/lwbtopts.h" +#include "lwbt/phybusif.h" +#include "lwbt/hci.h" +#include "lwip/debug.h" +#include "lwip/mem.h" +#include + +#define BAUDRATE B115200 //baudarte set +#define _POSIX_SOURCE 1 // POSIX compliant source + +int fd; + +// Initializes the physical bus interface +int set_speed(int fd, struct termios *ti, int speed) +{ + return 0; +} + +void phybusif_init(const char * port) +{ +} + +err_t phybusif_reset(struct phybusif_cb *cb) +{ + return ERR_OK; +} + +//Bluetooth protocol stack data reception callback function +err_t phybusif_input(struct phybusif_cb *cb) +{ + return ERR_OK; +} +//Bluetooth protocol stack data transmission callback function +void phybusif_output(struct pbuf *p, u16_t len) +{ +} + diff --git a/kernel/include/lwbt/bd_addr.h b/kernel/include/lwbt/bd_addr.h new file mode 100644 index 0000000..e873abf --- /dev/null +++ b/kernel/include/lwbt/bd_addr.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __LWBT_BD_ADDR_H__ +#define __LWBT_BD_ADDR_H__ + +struct bd_addr { + u8_t addr[6]; +}; + +#define BD_ADDR_LEN 6 + +#define BD_ADDR(bdaddr, a, b, c, d, e, f) do{ \ + bdaddr->addr[0] = a; \ + bdaddr ->addr[1] = b; \ + bdaddr->addr[2] = c; \ + bdaddr->addr[3] = d; \ + bdaddr->addr[4] = e; \ + bdaddr->addr[5] = f; }while(0) +//TODO: USE memcmp???? +#define bd_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3]) && \ + ((addr1)->addr[4] == (addr2)->addr[4]) && \ + ((addr1)->addr[5] == (addr2)->addr[5])) +//TODO: USE memcpy???? +#define bd_addr_set(addr1, addr2) do { \ + (addr1)->addr[0] = (addr2)->addr[0]; \ + (addr1)->addr[1] = (addr2)->addr[1]; \ + (addr1)->addr[2] = (addr2)->addr[2]; \ + (addr1)->addr[3] = (addr2)->addr[3]; \ + (addr1)->addr[4] = (addr2)->addr[4]; \ + (addr1)->addr[5] = (addr2)->addr[5]; }while(0) + +#define bd_addr_debug_print(bdaddr) LWIP_DEBUGF(LWBT_DEBUG, ("%02x:%02x:%02x:%02x:%02x:%02x\n", \ + (bdaddr)->addr[0], \ + (bdaddr)->addr[1], \ + (bdaddr)->addr[2], \ + (bdaddr)->addr[3], \ + (bdaddr)->addr[4], \ + (bdaddr)->addr[5])) + + +#endif /* __LWBT_BD_ADDR_H__ */ diff --git a/kernel/include/lwbt/fcs.h b/kernel/include/lwbt/fcs.h new file mode 100644 index 0000000..92463ce --- /dev/null +++ b/kernel/include/lwbt/fcs.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __LWBT_FCS_H__ +#define __LWBT_FCS_H__ +#include "arch/cc.h" +#include "lwip/pbuf.h" + +u8_t fcs8_crc_check(struct pbuf *p, u8_t len, u8_t check_sum); +u8_t fcs8_crc_calc(struct pbuf *p, u8_t len); +u8_t fcs16_crc_check(struct pbuf *p, u16_t len); +u16_t fcs16_crc_calc(struct pbuf *p, u16_t len); + +#endif /* __LWBT_FCS_H__ */ diff --git a/kernel/include/lwbt/hci.h b/kernel/include/lwbt/hci.h new file mode 100644 index 0000000..115f7c9 --- /dev/null +++ b/kernel/include/lwbt/hci.h @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __BLUETOOTHIF_H__ +#define __BLUETOOTHIF_H__ + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/err.h" + +#include "bd_addr.h" + +struct hci_pcb; +struct hci_link; +struct hci_inq_res; + +/* Functions for interfacing with HCI */ +err_t hci_init(void); /* Must be called first to initialize HCI */ + +/* Application program's interface: */ +err_t hci_close(struct hci_link *link); +void hci_reset_all(void); +void hci_arg(void *arg); +void hci_cmd_complete(err_t (* cmd_complete)(void *arg, struct hci_pcb *pcb, + u8_t ogf, u8_t ocf, u8_t result)); +void hci_pin_req(err_t (* pin_req)(void *arg, struct bd_addr *bdaddr)); +void hci_link_key_not(err_t (* link_key_not)(void *arg, struct bd_addr *bdaddr, u8_t *key)); +void hci_wlp_complete(err_t (* wlp_complete)(void *arg, struct bd_addr *bdaddr)); +void hci_connection_complete(err_t (* conn_complete)(void *arg, struct bd_addr *bdaddr)); +#define hci_num_cmd(pcb) ((pcb)->numcmd) +#define hci_num_acl(pcb) ((pcb)->hc_num_acl) +#define hci_maxsize(pcb) ((pcb)->maxsize) +err_t hci_inquiry(u32_t lap, u8_t inq_len, u8_t num_resp, err_t (* inq_complete)(void *arg, struct hci_pcb *pcb, struct hci_inq_res *ires, u16_t result)); +err_t hci_disconnect(struct bd_addr *bdaddr, u8_t reason); +err_t hci_pin_code_request_reply(struct bd_addr *bdaddr, u8_t pinlen, u8_t *pincode); +err_t hci_pin_code_request_neg_reply(struct bd_addr *bdaddr); +err_t hci_write_stored_link_key(struct bd_addr *bdaddr, u8_t *key); +err_t hci_change_local_name(u8_t *name, u8_t len); +err_t hci_sniff_mode(struct bd_addr *bdaddr, u16_t max_interval, u16_t min_interval, u16_t attempt, u16_t timeout); +err_t hci_write_link_policy_settings(struct bd_addr *bdaddr, u16_t link_policy); +err_t hci_reset(void); +err_t hci_set_event_filter(u8_t filter_type, u8_t filter_cond_type, u8_t* cond); +err_t hci_write_page_timeout(u16_t page_timeout); +err_t hci_write_scan_enable(u8_t scan_enable); +err_t hci_write_cod(u8_t *cod); +err_t hci_set_hc_to_h_fc(void); +err_t hci_host_buffer_size(void); +err_t hci_host_num_comp_packets(u16_t conhdl, u16_t num_complete); +err_t hci_read_buffer_size(void); +err_t hci_read_local_features(void); +err_t hci_read_bd_addr(err_t (* rbd_complete)(void *arg, struct bd_addr *bdaddr)); + +/* Lower layers interface */ +void hci_acl_input(struct pbuf *p); +void hci_event_input(struct pbuf *p); + +/* L2CAP interface */ +err_t lp_write_flush_timeout(struct bd_addr *bdaddr, u16_t flushto); +err_t lp_acl_write(struct bd_addr *bdaddr, struct pbuf *p, u16_t len, u8_t pb); +err_t lp_connect_req(struct bd_addr *bdaddr, u8_t allow_role_switch); +u8_t lp_is_connected(struct bd_addr *bdaddr); +u16_t lp_pdu_maxsize(void); + +/* HCI packet indicators */ +#define HCI_COMMAND_DATA_PACKET 0x01 +#define HCI_ACL_DATA_PACKET 0x02 +#define HCI_SCO_DATA_PACKET 0x03 +#define HCI_EVENT_PACKET 0x04 + +#define HCI_EVENT_HDR_LEN 2 +#define HCI_ACL_HDR_LEN 4 +#define HCI_SCO_HDR_LEN 3 +#define HCI_CMD_HDR_LEN 3 + +/* Opcode Group Field (OGF) values */ +#define HCI_LINK_CONTROL 0x01 /* Link Control Commands */ +#define HCI_LINK_POLICY 0x02 /* Link Policy Commands */ +#define HCI_HOST_C_N_BB 0x03 /* Host Controller & Baseband Commands */ +#define HCI_INFO_PARAM 0x04 /* Informational Parameters */ +#define HCI_STATUS_PARAM 0x05 /* Status Parameters */ +#define HCI_TESTING 0x06 /* Testing Commands */ + +/* Opcode Command Field (OCF) values */ + +/* Link control commands */ +#define HCI_INQUIRY 0x01 +#define HCI_CREATE_CONNECTION 0x05 +#define HCI_REJECT_CONNECTION_REQUEST 0x0A +#define HCI_DISCONNECT 0x06 +#define HCI_PIN_CODE_REQ_REP 0x0D +#define HCI_PIN_CODE_REQ_NEG_REP 0x0E +#define HCI_SET_CONN_ENCRYPT 0x13 + +/* Link Policy commands */ +#define HCI_HOLD_MODE 0x01 +#define HCI_SNIFF_MODE 0x03 +#define HCI_EXIT_SNIFF_MODE 0x04 +#define HCI_PARK_MODE 0x05 +#define HCI_EXIT_PARK_MODE 0x06 +#define HCI_W_LINK_POLICY 0x0D + +/* Host-Controller and Baseband Commands */ +#define HCI_SET_EVENT_MASK 0x01 +#define HCI_RESET 0x03 +#define HCI_SET_EVENT_FILTER 0x05 +#define HCI_WRITE_STORED_LINK_KEY 0x11 +#define HCI_ROLE_CHANGE 0x12 +#define HCI_CHANGE_LOCAL_NAME 0x13 + +#define HCI_WRITE_PAGE_TIMEOUT 0x18 +#define HCI_WRITE_SCAN_ENABLE 0x1A +#define HCI_WRITE_COD 0x24 +#define HCI_W_FLUSHTO 0x28 +#define HCI_SET_HC_TO_H_FC 0x31 + +/* Informational Parameters */ +#define HCI_READ_BUFFER_SIZE 0x05 +#define HCI_READ_BD_ADDR 0x09 + +/* Status Parameters */ +#define HCI_READ_FAILED_CONTACT_COUNTER 0x01 +#define HCI_RESET_FAILED_CONTACT_COUNTER 0x02 +#define HCI_GET_LINK_QUALITY 0x03 +#define HCI_READ_RSSI 0x05 + +/* Testing commands */ + +/* Possible event codes */ +#define HCI_INQUIRY_COMPLETE 0x01 +#define HCI_INQUIRY_RESULT 0x02 +#define HCI_CONNECTION_COMPLETE 0x03 +#define HCI_CONNECTION_REQUEST 0x04 +#define HCI_DISCONNECTION_COMPLETE 0x05 +#define HCI_ENCRYPTION_CHANGE 0x08 +#define HCI_QOS_SETUP_COMPLETE 0x0D +#define HCI_COMMAND_COMPLETE 0x0E +#define HCI_COMMAND_STATUS 0x0F +#define HCI_HARDWARE_ERROR 0x10 +#define HCI_ROLE_CHANGE 0x12 +#define HCI_NBR_OF_COMPLETED_PACKETS 0x13 +#define HCI_MODE_CHANGE 0x14 +#define HCI_PIN_CODE_REQUEST 0x16 +#define HCI_LINK_KEY_NOTIFICATION 0x18 +#define HCI_DATA_BUFFER_OVERFLOW 0x1A +#define HCI_MAX_SLOTS_CHANGE 0x1B +#define HCI_PAGE_SCAN_REP_MODE_CHANGE 0x20 + +/* Success code */ +#define HCI_SUCCESS 0x00 +/* Possible error codes */ +#define HCI_UNKNOWN_HCI_COMMAND 0x01 +#define HCI_NO_CONNECTION 0x02 +#define HCI_HW_FAILURE 0x03 +#define HCI_PAGE_TIMEOUT 0x04 +#define HCI_AUTHENTICATION_FAILURE 0x05 +#define HCI_KEY_MISSING 0x06 +#define HCI_MEMORY_FULL 0x07 +#define HCI_CONN_TIMEOUT 0x08 +#define HCI_MAX_NUMBER_OF_CONNECTIONS 0x09 +#define HCI_MAX_NUMBER_OF_SCO_CONNECTIONS_TO_DEVICE 0x0A +#define HCI_ACL_CONNECTION_EXISTS 0x0B +#define HCI_COMMAND_DISSALLOWED 0x0C +#define HCI_HOST_REJECTED_DUE_TO_LIMITED_RESOURCES 0x0D +#define HCI_HOST_REJECTED_DUE_TO_SECURITY_REASONS 0x0E +#define HCI_HOST_REJECTED_DUE_TO_REMOTE_DEVICE_ONLY_PERSONAL_SERVICE 0x0F +#define HCI_HOST_TIMEOUT 0x10 +#define HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE 0x11 +#define HCI_INVALID_HCI_COMMAND_PARAMETERS 0x12 +#define HCI_OTHER_END_TERMINATED_CONN_USER_ENDED 0x13 +#define HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES 0x14 +#define HCI_OTHER_END_TERMINATED_CONN_ABOUT_TO_POWER_OFF 0x15 +#define HCI_CONN_TERMINATED_BY_LOCAL_HOST 0x16 +#define HCI_REPETED_ATTEMPTS 0x17 +#define HCI_PAIRING_NOT_ALLOWED 0x18 +#define HCI_UNKNOWN_LMP_PDU 0x19 +#define HCI_UNSUPPORTED_REMOTE_FEATURE 0x1A +#define HCI_SCO_OFFSET_REJECTED 0x1B +#define HCI_SCO_INTERVAL_REJECTED 0x1C +#define HCI_SCO_AIR_MODE_REJECTED 0x1D +#define HCI_INVALID_LMP_PARAMETERS 0x1E +#define HCI_UNSPECIFIED_ERROR 0x1F +#define HCI_UNSUPPORTED_LMP_PARAMETER_VALUE 0x20 +#define HCI_ROLE_CHANGE_NOT_ALLOWED 0x21 +#define HCI_LMP_RESPONSE_TIMEOUT 0x22 +#define HCI_LMP_ERROR_TRANSACTION_COLLISION 0x23 +#define HCI_LMP_PDU_NOT_ALLOWED 0x24 +#define HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE 0x25 +#define HCI_UNIT_KEY_USED 0x26 +#define HCI_QOS_NOT_SUPPORTED 0x27 +#define HCI_INSTANT_PASSED 0x28 +#define HCI_PAIRING_UNIT_KEY_NOT_SUPPORTED 0x29 + +/* Specification specific parameters */ +#define HCI_BD_ADDR_LEN 6 +#define HCI_LMP_FEATURES_LEN 8 +#define HCI_LINK_KEY_LEN 16 +#define HCI_LMP_FEAT_LEN 8 + +/* Command OGF */ +#define HCI_LINK_CTRL_OGF 0x01 /* Link ctrl cmds */ +#define HCI_HC_BB_OGF 0x03 /* Host controller and baseband commands */ +#define HCI_INFO_PARAM_OGF 0x04 /* Informational parameters */ + +/* Command OCF */ +#define HCI_R_LOCAL_VERSION_INFO_OCF 0x01 +#define HCI_R_SUPPORTED_LOCAL_FEATURES_OCF 0x03 +#define HCI_INQUIRY_OCF 0x01 +#define HCI_CREATE_CONN_OCF 0x05 +#define HCI_DISCONN_OCF 0x06 +#define HCI_REJECT_CONN_REQ_OCF 0x0A +#define HCI_SET_EV_MASK_OCF 0x01 +#define HCI_RESET_OCF 0x03 +#define HCI_SET_EV_FILTER_OCF 0x05 +#define HCI_W_PAGE_TIMEOUT_OCF 0x18 +#define HCI_W_SCAN_EN_OCF 0x1A +#define HCI_R_COD_OCF 0x23 +#define HCI_W_COD_OCF 0x24 +#define HCI_SET_HC_TO_H_FC_OCF 0x31 +#define HCI_H_BUF_SIZE_OCF 0x33 +#define HCI_H_NUM_COMPL_OCF 0x35 +#define HCI_R_BUF_SIZE_OCF 0x05 +#define HCI_R_BD_ADDR_OCF 0x09 + +/* Command packet length (including ACL header)*/ +#define HCI_INQUIRY_PLEN 9 +#define HCI_CREATE_CONN_PLEN 17 +#define HCI_DISCONN_PLEN 7 +#define HCI_REJECT_CONN_REQ_PLEN 11 +#define HCI_PIN_CODE_REQ_REP_PLEN 27 +#define HCI_PIN_CODE_REQ_NEG_REP_PLEN 10 +#define HCI_SET_CONN_ENCRYPT_PLEN 7 +#define HCI_WRITE_STORED_LINK_KEY_PLEN 27 +#define HCI_CHANGE_LOCAL_NAME_PLEN 4 +#define HCI_SET_EV_MASK_PLEN 12 +#define HCI_SNIFF_PLEN 14 +#define HCI_W_LINK_POLICY_PLEN 8 +#define HCI_RESET_PLEN 4 +#define HCI_SET_EV_FILTER_PLEN 6 +#define HCI_W_PAGE_TIMEOUT_PLEN 6 +#define HCI_W_SCAN_EN_PLEN 5 +#define HCI_R_COD_PLEN 4 +#define HCI_W_COD_PLEN 7 +#define HCI_W_FLUSHTO_PLEN 7 +#define HCI_SET_HC_TO_H_FC_PLEN 5 +#define HCI_H_BUF_SIZE_PLEN 7 +#define HCI_H_NUM_COMPL_PLEN 7 +#define HCI_R_BUF_SIZE_PLEN 4 +#define HCI_R_BD_ADDR_PLEN 4 +#define HCI_R_SUPPORTED_LOCAL_FEATURES_PLEN 4 + +/* Set Event Filter params */ +#define HCI_SET_EV_FILTER_CLEAR 0 +#define HCI_SET_EV_FILTER_INQUIRY 1 +#define HCI_SET_EV_FILTER_CONNECTION 2 + +#define HCI_SET_EV_FILTER_ALLDEV 0 +#define HCI_SET_EV_FILTER_COD 1 +#define HCI_SET_EV_FILTER_BDADDR 2 + +#define HCI_SET_EV_FILTER_AUTOACC_OFF 1 +#define HCI_SET_EV_FILTER_AUTOACC_NOROLESW 2 +#define HCI_SET_EV_FILTER_AUTOACC_ROLESW 3 + +/* Write Scan Enable params */ +#define HCI_SCAN_EN_INQUIRY 1 +#define HCI_SCAN_EN_PAGE 2 + +struct hci_event_hdr { + u8_t code; /* Event code */ + u8_t len; /* Parameter total length */ +}; + +struct hci_acl_hdr { + u16_t conhdl_pb_bc; /* Connection handle, packet boundary and broadcast flag + flag */ + u16_t len; /* length of data */ +}; + +struct hci_inq_res { + struct hci_inq_res *next; /* For the linked list */ + + struct bd_addr bdaddr; /* Bluetooth address of a device found in an inquiry */ + u8_t cod[3]; /* Class of the remote device */ + u8_t psrm; /* Page scan repetition mode */ + u8_t psm; /* Page scan mode */ + u16_t co; /* Clock offset */ +}; + +struct hci_link { + struct hci_link *next; /* For the linked list */ + + struct bd_addr bdaddr; /* The remote peers Bluetooth address for this connection */ + u16_t conhdl; /* Connection handle */ +#if HCI_FLOW_QUEUEING + struct pbuf *p; + u16_t len; + u8_t pb; +#endif +}; + +struct hci_pcb { + void *callback_arg; + + /* Host to host controller flow control */ + u8_t numcmd; /* Number of command packets that the host controller (Bluetooth + module) can buffer */ + u16_t maxsize; /* Maximum length of the data portion of each HCI ACL data + packet that the Host Controller is able to accept */ + u16_t hc_num_acl; /* Number of ACL packets that the Bluetooth module can buffer */ + + /* Host controller to host flow control */ + u8_t flow; /* Indicates if host controller to host flow control is on */ + u16_t host_num_acl; /* Number of ACL packets that we (the host) can buffer */ + + struct hci_inq_res *ires; /* Results of an inquiry */ + + err_t (* pin_req)(void *arg, struct bd_addr *bdaddr); + err_t (* inq_complete)(void *arg, struct hci_pcb *pcb, struct hci_inq_res *ires, u16_t result); + err_t (* rbd_complete)(void *arg, struct bd_addr *bdaddr); + err_t (* link_key_not)(void *arg, struct bd_addr *bdaddr, u8_t *key); + err_t (* wlp_complete)(void *arg, struct bd_addr *bdaddr); + err_t (* conn_complete)(void *arg, struct bd_addr *bdaddr); + err_t (* cmd_complete)(void *arg, struct hci_pcb *pcb, u8_t ogf, u8_t ocf, u8_t result); +}; + +#define HCI_EVENT_PIN_REQ(pcb,bdaddr,ret) \ + if((pcb)->pin_req != NULL) { \ + (ret = (pcb)->pin_req((pcb)->callback_arg,(bdaddr))); \ + } else { \ + ret = hci_pin_code_request_neg_reply(bdaddr); \ + } +#define HCI_EVENT_LINK_KEY_NOT(pcb,bdaddr,key,ret) \ + if((pcb)->link_key_not != NULL) { \ + (ret = (pcb)->link_key_not((pcb)->callback_arg,(bdaddr),(key))); \ + } +#define HCI_EVENT_INQ_COMPLETE(pcb,result,ret) \ + if((pcb)->inq_complete != NULL) \ + (ret = (pcb)->inq_complete((pcb)->callback_arg,(pcb),(pcb)->ires,(result))) +#define HCI_EVENT_RBD_COMPLETE(pcb,bdaddr,ret) \ + if((pcb)->rbd_complete != NULL) \ + (ret = (pcb)->rbd_complete((pcb)->callback_arg,(bdaddr))); +#define HCI_EVENT_WLP_COMPLETE(pcb,bdaddr,ret) \ + if((pcb)->wlp_complete != NULL) \ + (ret = (pcb)->wlp_complete((pcb)->callback_arg,(bdaddr))); +#define HCI_EVENT_CONN_COMPLETE(pcb,bdaddr,ret) \ + if((pcb)->conn_complete != NULL) \ + (ret = (pcb)->conn_complete((pcb)->callback_arg,(bdaddr))); +#define HCI_EVENT_CMD_COMPLETE(pcb,ogf,ocf,result,ret) \ + if((pcb)->cmd_complete != NULL) \ + (ret = (pcb)->cmd_complete((pcb)->callback_arg,(pcb),(ogf),(ocf),(result))) + +/* The HCI LINK lists. */ +extern struct hci_link *hci_active_links; /* List of all active HCI LINKs */ +extern struct hci_link *hci_tmp_link; /* Only used for temporary storage. */ + +#define HCI_REG(links, nlink) do { \ + nlink->next = *links; \ + *links = nlink; \ +} while(0) +#define HCI_RMV(links, nlink) do { \ + if(*links == nlink) { \ + *links = (*links)->next; \ + } else for(hci_tmp_link = *links; hci_tmp_link != NULL; hci_tmp_link = hci_tmp_link->next) { \ + if(hci_tmp_link->next != NULL && hci_tmp_link->next == nlink) { \ + hci_tmp_link->next = nlink->next; \ + break; \ + } \ + } \ + nlink->next = NULL; \ +} while(0) +#endif /* __HCI_H__ */ diff --git a/kernel/include/lwbt/l2cap.h b/kernel/include/lwbt/l2cap.h new file mode 100644 index 0000000..894707a --- /dev/null +++ b/kernel/include/lwbt/l2cap.h @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __LWBT_L2CAP_H__ +#define __LWBT_L2CAP_H__ + +#include "lwbtopts.h" + +#if L2CAP_HCI +#include "lwbt/hci.h" +#else +//#include "netif/lwbt/bb.h" +//#include "netif/lwbt/lmp.h" +#endif /* L2CAP_HCI_UART */ + +struct l2cap_pcb; +struct l2cap_cfg; +struct l2cap_sig; + +/* Functions for interfacing with L2CAP */ +void l2cap_init(void); /* Must be called first to initialize L2CAP */ +void l2cap_tmr(void); /* Must be called every 1s */ + +/* Higher layers interface */ +struct l2cap_pcb *l2cap_new(void); +err_t l2cap_close(struct l2cap_pcb *pcb); +void l2cap_reset_all(void); +void l2cap_arg(struct l2cap_pcb *pcb, void *arg); +#define l2cap_psm(pcb) ((pcb)->psm) +err_t l2cap_connect_ind(struct l2cap_pcb *lpcb, u8_t psm, + err_t (* l2ca_connect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err)); +void l2cap_disconnect_ind(struct l2cap_pcb *pcb, + err_t (* l2ca_disconnect_ind)(void *arg, struct l2cap_pcb *newpcb, + err_t err)); +void l2cap_timeout_ind(struct l2cap_pcb *pcb, + err_t (* l2ca_timeout_ind)(void *arg, struct l2cap_pcb *newpcb, + err_t err)); +void l2cap_recv(struct l2cap_pcb *pcb, + err_t (* l2ca_recv)(void *arg, struct l2cap_pcb *pcb, struct pbuf *p, err_t err)); + +err_t l2ca_connect_req(struct l2cap_pcb *pcb, struct bd_addr *bdaddr, u16_t psm, u8_t role_switch, + err_t (* l2ca_connect_cfm)(void *arg, struct l2cap_pcb *lpcb, + u16_t result, u16_t status)); +err_t l2ca_config_req(struct l2cap_pcb *pcb); +err_t l2ca_disconnect_req(struct l2cap_pcb *pcb, + err_t (* l2ca_disconnect_cfm)(void *arg, struct l2cap_pcb *pcb)); +err_t l2ca_datawrite(struct l2cap_pcb *pcb, struct pbuf *p); +err_t l2ca_ping(struct bd_addr *bdaddr, struct l2cap_pcb *tpcb, + err_t (* l2ca_pong)(void *arg, struct l2cap_pcb *pcb, u8_t result)); + +/* Only used by lower layers to pass a L2CAP segment to L2CAP */ +void l2cap_input(struct pbuf *p, struct bd_addr *bdaddr); + +/* Used by lower layers to signal events to L2CAP */ +void lp_connect_cfm(struct bd_addr *bdaddr, u8_t encrypt_mode, err_t err); +void lp_connect_ind(struct bd_addr *bdaddr); +void lp_disconnect_ind(struct bd_addr *bdaddr); + +/* Used within L2CAP code only */ +err_t l2cap_signal(struct l2cap_pcb *pcb, u8_t code, u16_t ursp_id, struct bd_addr *remote_bdaddr, + struct pbuf *data); +err_t l2cap_rexmit_signal(struct l2cap_pcb *pcb, struct l2cap_sig *sig); +u8_t l2cap_next_sigid(void); + +/* Protocol and service multiplexor */ +#define SDP_PSM 0x0001 +#define RFCOMM_PSM 0x0003 +#define BNEP_PSM 0x000F + +/* Packet header lengths */ +#define L2CAP_HDR_LEN 4 +#define L2CAP_SIGHDR_LEN 4 +#define L2CAP_CFGOPTHDR_LEN 2 + +/* Signals sizes */ +#define L2CAP_CONN_REQ_SIZE 4 +#define L2CAP_CONN_RSP_SIZE 8 +#define L2CAP_CFG_RSP_SIZE 6 +#define L2CAP_DISCONN_RSP_SIZE 4 + +#define L2CAP_CFG_REQ_SIZE 4 + +#define L2CAP_DISCONN_REQ_SIZE 4 +#define L2CAP_CMD_REJ_SIZE 2 + +/* Signal codes */ +#define L2CAP_CMD_REJ 0x01 +#define L2CAP_CONN_REQ 0x02 +#define L2CAP_CONN_RSP 0x03 +#define L2CAP_CFG_REQ 0x04 +#define L2CAP_CFG_RSP 0x05 +#define L2CAP_DISCONN_REQ 0x06 +#define L2CAP_DISCONN_RSP 0x07 +#define L2CAP_ECHO_REQ 0x08 +#define L2CAP_ECHO_RSP 0x09 +#define L2CAP_INFO_REQ 0x0A +#define L2CAP_INFO_RSP 0x0B + +/* Permanent channel identifiers */ +#define L2CAP_NULL_CID 0x0000 +#define L2CAP_SIG_CID 0x0001 +#define L2CAP_CONNLESS_CID 0x0002 + +/* Channel identifiers values */ +#define L2CAP_MIN_CID 0x0040 +#define L2CAP_MAX_CID 0xFFFF + +/* Configuration types */ +#define L2CAP_CFG_MTU 0x01 +#define L2CAP_FLUSHTO 0x02 +#define L2CAP_QOS 0x03 + +/* Configuration types length */ +#define L2CAP_MTU_LEN 2 +#define L2CAP_FLUSHTO_LEN 2 +#define L2CAP_QOS_LEN 22 + +/* Configuration response types */ +#define L2CAP_CFG_SUCCESS 0x0000 +#define L2CAP_CFG_UNACCEPT 0x0001 +#define L2CAP_CFG_REJ 0x0002 +#define L2CAP_CFG_UNKNOWN 0x0003 +#define L2CAP_CFG_TIMEOUT 0xEEEE + +/* QoS types */ +#define L2CAP_QOS_NO_TRAFFIC 0x00 +#define L2CAP_QOS_BEST_EFFORT 0x01 +#define L2CAP_QOS_GUARANTEED 0x02 + +/* Command reject reasons */ +#define L2CAP_CMD_NOT_UNDERSTOOD 0x0000 +#define L2CAP_MTU_EXCEEDED 0x0001 +#define L2CAP_INVALID_CID 0x0002 + +/* Connection response results */ +#define L2CAP_CONN_SUCCESS 0x0000 +#define L2CAP_CONN_PND 0x0001 +#define L2CAP_CONN_REF_PSM 0x0002 +#define L2CAP_CONN_REF_SEC 0x0003 +#define L2CAP_CONN_REF_RES 0x0004 +#define L2CAP_CONN_CFG_TO 0x0005 /* Implementation specific result */ + +/* Echo response results */ +#define L2CAP_ECHO_RCVD 0x00 +#define L2CAP_ECHO_TO 0x01 + +/* L2CAP segmentation */ +#define L2CAP_ACL_START 0x02 +#define L2CAP_ACL_CONT 0x01 + +/* L2CAP config default parameters */ +#define L2CAP_CFG_DEFAULT_INMTU 672 /* Two Baseband DH5 packets (2*341=682) minus the Baseband ACL + headers (2*2=4) and L2CAP header (6) */ +#define L2CAP_CFG_DEFAULT_OUTFLUSHTO 0xFFFF + +/* L2CAP configuration parameter masks */ +#define L2CAP_CFG_IR 0x01 +#define L2CAP_CFG_IN_SUCCESS 0x02 +#define L2CAP_CFG_OUT_SUCCESS 0x04 +#define L2CAP_CFG_OUT_REQ 0x08 + +struct l2cap_hdr { + u16_t len; + u16_t cid; +}; + +struct l2cap_sig_hdr { + u8_t code; + u8_t id; + u16_t len; +}; + +struct l2cap_cfgopt_hdr { + u8_t type; + u8_t len; +}; + +enum l2cap_state { + L2CAP_CLOSED, L2CAP_LISTEN, W4_L2CAP_CONNECT_RSP, W4_L2CA_CONNECT_RSP, L2CAP_CONFIG, + L2CAP_OPEN, W4_L2CAP_DISCONNECT_RSP, W4_L2CA_DISCONNECT_RSP +}; + +struct l2cap_acl_link { + struct l2cap_acl_link *next; + struct bd_addr bdaddr; +}; + +/* This structure is used to represent L2CAP signals. */ +struct l2cap_sig { + struct l2cap_sig *next; /* for the linked list, used when putting signals + on a queue */ + struct pbuf *p; /* buffer containing data + L2CAP header */ + u16_t sigid; /* Identification */ + u16_t ertx; /* extended response timeout expired */ + u8_t rtx; /* response timeout expired */ + u8_t nrtx; /* number of retransmissions */ +}; + +struct l2cap_cfg { + u16_t inmtu; /* Maximum transmission unit this channel can accept */ + u16_t outmtu; /* Maximum transmission unit that can be sent on this channel */ + u16_t influshto; /* In flush timeout */ + u16_t outflushto; /* Out flush timeout */ + + struct pbuf *opt; /* Any received non-hint unknown option(s) or option(s) with unacceptable parameters + will be temporarily stored here */ + + u8_t cfgto; /* Configuration timeout */ + u8_t l2capcfg; /* Bit 1 indicates if we are the initiator of this connection + * Bit 2 indicates if a successful configuration response has been received + * Bit 3 indicates if a successful configuration response has been sent + * Bit 4 indicates if an initial configuration request has been sent + */ +}; + +struct l2cap_seg { + struct l2cap_seg *next; /* For the linked list */ + + struct bd_addr bdaddr; + + struct pbuf *p; /* Buffer containing data + L2CAP header */ + u16_t len; /* The L2CAP length of this segment */ + struct l2cap_hdr *l2caphdr; /* The L2CAP header */ + struct l2cap_pcb *pcb; /* The L2CAP Protocol Control Block */ +}; + +struct l2cap_pcb { + struct l2cap_pcb *next; /* For the linked list */ + + enum l2cap_state state; /* L2CAP state */ + + void *callback_arg; + + u16_t scid; /* Source CID */ + u16_t dcid; /* Destination CID */ + + u16_t psm; /* Protocol/Service Multiplexer */ + + u16_t ursp_id; /* Signal id to respond to */ + u8_t encrypt; /* encryption mode */ + + struct l2cap_sig *unrsp_sigs; /* List of sent but unresponded signals */ + + struct bd_addr remote_bdaddr; + + struct l2cap_cfg cfg; /* Configuration parameters */ + + /* Upper layer to L2CAP confirmation functions */ + + /* Function to be called when a connection has been set up */ + err_t (* l2ca_connect_cfm)(void *arg, struct l2cap_pcb *pcb, u16_t result, u16_t status); + /* Function to be called when a connection has been closed */ + err_t (* l2ca_disconnect_cfm)(void *arg, struct l2cap_pcb *pcb); + /* Function to be called when a echo reply has been received */ + err_t (* l2ca_pong)(void *arg, struct l2cap_pcb *pcb, u8_t result); + + /* L2CAP to upper layer indication functions */ + + /* Function to be called when a connection indication event occurs */ + err_t (* l2ca_connect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err); + /* Function to be called when a disconnection indication event occurs */ + err_t (* l2ca_disconnect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err); + /* Function to be called when a timeout indication event occurs */ + err_t (* l2ca_timeout_ind)(void *arg, struct l2cap_pcb *newpcb, err_t err); + /* Function to be called when a L2CAP connection receives data */ + err_t (* l2ca_recv)(void *arg, struct l2cap_pcb *pcb, struct pbuf *p, err_t err); +}; + +struct l2cap_pcb_listen { + struct l2cap_pcb_listen *next; /* for the linked list */ + + enum l2cap_state state; /* L2CAP state */ + + void *callback_arg; + + u16_t psm; /* Protocol/Service Multiplexer */ + + /* Function to call when a connection request has been received + from a remote device. */ + err_t (* l2ca_connect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err); +}; + +/* Internal functions and global variables */ +#define L2CA_ACTION_CONN_CFM(pcb,result,status,ret) if((pcb)->l2ca_connect_cfm != NULL) (ret = (pcb)->l2ca_connect_cfm((pcb)->callback_arg,(pcb),(result),(status))) +#define L2CA_ACTION_DISCONN_CFM(pcb,ret) if((pcb)->l2ca_disconnect_cfm != NULL) (ret = (pcb)->l2ca_disconnect_cfm((pcb)->callback_arg,(pcb))) +#define L2CA_ACTION_PING_CFM(pcb,result,ret) if((pcb)->l2ca_pong != NULL) (ret = (pcb)->l2ca_pong((pcb)->callback_arg,(pcb),(result))) + +#define L2CA_ACTION_CONN_IND(pcb,err,ret) if((pcb)->l2ca_connect_ind != NULL) (ret = (pcb)->l2ca_connect_ind((pcb)->callback_arg,(pcb),(err))) +#define L2CA_ACTION_DISCONN_IND(pcb,err,ret) \ + if((pcb)->l2ca_disconnect_ind != NULL) { \ + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_disconnect_ind called\n")); \ + (ret = (pcb)->l2ca_disconnect_ind((pcb)->callback_arg,(pcb),(err))); \ + } else { \ + l2cap_close(pcb); \ + } +#define L2CA_ACTION_TO_IND(pcb,err,ret) if((pcb)->l2ca_timeout_ind != NULL) (ret = (pcb)->l2ca_timeout_ind((pcb)->callback_arg,(pcb),(err))) +#define L2CA_ACTION_RECV(pcb,p,err,ret) \ + if((pcb)->l2ca_recv != NULL) { \ + (ret = (pcb)->l2ca_recv((pcb)->callback_arg,(pcb),(p),(err))); \ + } else { \ + pbuf_free(p); \ + } + +#define L2CAP_OPTH_TYPE(hdr) (((hdr)->type) & 0x7f) +#define L2CAP_OPTH_TOA(hdr) (((hdr)->type) >> 7) + +/* The L2CAP PCB lists. */ +extern struct l2cap_pcb_listen *l2cap_listen_pcbs; /* List of all L2CAP PCBs in CLOSED state + but awaing an incoming conn req. */ +extern struct l2cap_pcb *l2cap_active_pcbs; /* List of all L2CAP PCBs that has + established or is about to establish + an ACL link */ + +extern struct l2cap_pcb *l2cap_tmp_pcb; /* Only used for temporary storage. */ + +/* Axoims about the above list: + 1) A PCB is only in one of the lists. + 2) All PCBs in the l2cap_active_pcbs list has established or is about to + establish an ACL link. +*/ + +/* Define two macros, L2CAP_REG and L2CAP_RMV that registers a L2CAP PCB + with a PCB list or removes a PCB from a list, respectively. */ +#ifdef LWBT_DEBUG +#define L2CAP_REG(pcbs, npcb) \ + do { \ + for(l2cap_tmp_pcb = *(pcbs); \ + l2cap_tmp_pcb != NULL; \ + l2cap_tmp_pcb = l2cap_tmp_pcb->next) { \ + LWIP_ASSERT("L2CAP_REG: already registered\n", l2cap_tmp_pcb != (npcb)); \ + } \ + (npcb)->next = *(pcbs); \ + LWIP_ASSERT("L2CAP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \ + *(pcbs) = (npcb); \ + } while(0) + +#define L2CAP_RMV(pcbs, npcb) \ + do { \ + LWIP_ASSERT("L2CAP_RMV: pcbs != NULL", *(pcbs) != NULL); \ + LWIP_DEBUGF(L2CAP_DEBUG, ("L2CAP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \ + if(*(pcbs) == (npcb)) { \ + *(pcbs) = (*(pcbs))->next; \ + } else for(l2cap_tmp_pcb = *(pcbs); l2cap_tmp_pcb != NULL; l2cap_tmp_pcb = l2cap_tmp_pcb->next) { \ + if(l2cap_tmp_pcb->next != NULL && l2cap_tmp_pcb->next == (npcb)) { \ + l2cap_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + (npcb)->next = NULL; \ + LWIP_DEBUGF(L2CAP_DEBUG, ("L2CAP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \ + } while(0) + +#else /* LWBT_DEBUG */ +#define L2CAP_REG(pcbs, npcb) \ + do { \ + (npcb)->next = *(pcbs); \ + *(pcbs) = (npcb); \ + } while(0) + +#define L2CAP_RMV(pcbs, npcb) \ + do { \ + if(*(pcbs) == (npcb)) { \ + *(pcbs) = (*pcbs)->next; \ + } else for(l2cap_tmp_pcb = *(pcbs); l2cap_tmp_pcb != NULL; l2cap_tmp_pcb = l2cap_tmp_pcb->next) { \ + if(l2cap_tmp_pcb->next != NULL && l2cap_tmp_pcb->next == (npcb)) { \ + l2cap_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + (npcb)->next = NULL; \ + } while(0) + +#endif /* LWBT_DEBUG */ + +/* The L2CAP SIG list macros */ +extern struct l2cap_sig *l2cap_tmp_sig; /* Only used for temporary storage. */ + +#define L2CAP_SIG_REG(ursp_sigs, nsig) do { \ + nsig->next = *ursp_sigs; \ + *ursp_sigs = nsig; \ + } while(0) +#define L2CAP_SIG_RMV(ursp_sigs, nsig) do { \ + if(*ursp_sigs == nsig) { \ + *ursp_sigs = (*ursp_sigs)->next; \ + } else for(l2cap_tmp_sig = *ursp_sigs; l2cap_tmp_sig != NULL; l2cap_tmp_sig = l2cap_tmp_sig->next) { \ + if(l2cap_tmp_sig->next != NULL && l2cap_tmp_sig->next == nsig) { \ + l2cap_tmp_sig->next = nsig->next; \ + break; \ + } \ + } \ + nsig->next = NULL; \ + } while(0) + +/* The L2CAP incoming segments list macros */ +extern struct l2cap_seg *l2cap_tmp_inseg; /* Only used for temporary storage. */ + +#define L2CAP_SEG_REG(segs, nseg) do { \ + nseg->next = *segs; \ + *segs = nseg; \ + } while(0) +#define L2CAP_SEG_RMV(segs, nseg) do { \ + if(*segs == nseg) { \ + *segs = (*segs)->next; \ + } else for(l2cap_tmp_inseg = *segs; l2cap_tmp_inseg != NULL; l2cap_tmp_inseg = l2cap_tmp_inseg->next) { \ + if(l2cap_tmp_inseg->next != NULL && l2cap_tmp_inseg->next == nseg) { \ + l2cap_tmp_inseg->next = nseg->next; \ + break; \ + } \ + } \ + nseg->next = NULL; \ + } while(0) +#endif /* __LWBT_L2CAP_H__ */ + + diff --git a/kernel/include/lwbt/lwbt_memp.h b/kernel/include/lwbt/lwbt_memp.h new file mode 100644 index 0000000..51e8336 --- /dev/null +++ b/kernel/include/lwbt/lwbt_memp.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Conny Ohult + */ + +#ifndef __LWBT_MEMP_H__ +#define __LWBT_MEMP_H__ + +typedef enum { + MEMP_HCI_PCB, + MEMP_HCI_LINK, + MEMP_HCI_INQ, + MEMP_L2CAP_PCB, + MEMP_L2CAP_PCB_LISTEN, + MEMP_L2CAP_SIG, + MEMP_L2CAP_SEG, + MEMP_SDP_PCB, + MEMP_SDP_RECORD, + MEMP_RFCOMM_PCB, + MEMP_RFCOMM_PCB_LISTEN, + MEMP_PPP_PCB, + MEMP_PPP_REQ, + + MEMP_LWBT_MAX +} lwbt_memp_t; + +void lwbt_memp_init(void); + +void *lwbt_memp_malloc(lwbt_memp_t type); +void lwbt_memp_free(lwbt_memp_t type, void *mem); + +#endif /* __LWBT_MEMP_H__ */ diff --git a/kernel/include/lwbt/lwbtopts.h b/kernel/include/lwbt/lwbtopts.h new file mode 100644 index 0000000..531adbe --- /dev/null +++ b/kernel/include/lwbt/lwbtopts.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __LWBT_LWBTOPTS_H__ +#define __LWBT_LWBTOPTS_H__ + +#define PHYBUSIF_DEBUG LWIP_DBG_ON +#define HCI_DEBUG LWIP_DBG_OFF +#define HCI_EV_DEBUG LWIP_DBG_ON +#define L2CAP_DEBUG LWIP_DBG_OFF +#define SDP_DEBUG LWIP_DBG_ON +#define RFCOMM_DEBUG LWIP_DBG_ON + + +/* ---------- Memory options ---------- */ +#define MAX_NUM_CLIENTS 6 /* Maximum number of connected Bluetooth clients. No more than 6 */ + + /*NAT - Network Address Translation*/ +#define MEMP_NUM_NAT_PCB 8 /* Must be set to 1 or more if NAT is used. Set this to the number of diffrent ports/ids that the application use */ + +#define MEMP_NUM_HCI_PCB 1 /* Always set to one */ +#define MEMP_NUM_HCI_LINK (1 + MAX_NUM_CLIENTS) /* One for DT + One per ACL connection */ +#define MEMP_NUM_HCI_INQ 1 /* One per max number of returned results from an inquiry */ + +/* MEMP_NUM_L2CAP_PCB: the number of simulatenously active L2CAP + connections. */ +#define MEMP_NUM_L2CAP_PCB (2 + MAX_NUM_CLIENTS) /* One for a closing connection + one for DT + one per number of connected Bluetooth clients */ +/* MEMP_NUM_L2CAP_PCB_LISTEN: the number of listening L2CAP + connections. */ +#define MEMP_NUM_L2CAP_PCB_LISTEN 2 /* One per listening PSM */ +/* MEMP_NUM_L2CAP_SIG: the number of simultaneously unresponded + L2CAP signals */ +#define MEMP_NUM_L2CAP_SIG (2 * MAX_NUM_CLIENTS)/* Two per number of connected Bluetooth clients but min 2 */ +#define MEMP_NUM_L2CAP_SEG (2 + 2 * MAX_NUM_CLIENTS) /* One per number of L2CAP connections */ + +#define MEMP_NUM_SDP_PCB MAX_NUM_CLIENTS /* One per number of connected Bluetooth clients */ +#define MEMP_NUM_SDP_RECORD 1 /* One per registered service record */ + +#define MEMP_NUM_RFCOMM_PCB (2 + 2 * MAX_NUM_CLIENTS) /* Two for DT + Two per number of connected Bluetooth clients */ +#define MEMP_NUM_RFCOMM_PCB_LISTEN (2 * MAX_NUM_CLIENTS) /* Two per number of connected Bluetooth clients */ + +#define MEMP_NUM_PPP_PCB (1 + MAX_NUM_CLIENTS) /* One for DT + One per number of connected Bluetooth clients */ +#define MEMP_NUM_PPP_REQ MAX_NUM_CLIENTS /* One per number of connected Bluetooth clients but min 1 */ + +/* ---------- HCI options ---------- */ +/* HCI: Defines if we have lower layers of the Bluetooth stack running on a separate host + controller */ +#define HCI 1 + +#if HCI +/* HCI_HOST_MAX_NUM_ACL: The maximum number of ACL packets that the host can buffer */ +#define HCI_HOST_MAX_NUM_ACL 8 //TODO: Should be equal to PBUF_POOL_SIZE/2??? */ +/* HCI_HOST_ACL_MAX_LEN: The maximum size of an ACL packet that the host can buffer */ +#define HCI_HOST_ACL_MAX_LEN (RFCOMM_N + 14) /* Default: RFCOMM MFS + ACL header size, L2CAP header size, + RFCOMM header size and RFCOMM FCS size */ +/* HCI_PACKET_TYPE: The set of packet types which may be used on the connection. In order to + maximize packet throughput, it is recommended that RFCOMM should make use of the 3 and 5 + slot baseband packets.*/ +#define HCI_PACKET_TYPE 0xCC18 /* Default DM1, DH1, DM3, DH3, DM5, DH5 */ +/* HCI_ALLOW_ROLE_SWITCH: Tells the host controller whether to accept a Master/Slave switch + during establishment of a connection */ +#define HCI_ALLOW_ROLE_SWITCH 1 /* Default 1 */ +/* HCI_FLOW_QUEUEING: Control if a packet should be queued if the host controller is out of + bufferspace for outgoing packets. Only the first packet sent when out of credits will be + queued */ +#define HCI_FLOW_QUEUEING 0 /* Default: 0 */ + +#endif /* HCI */ + +/* ---------- L2CAP options ---------- */ +/* L2CAP_HCI: Option for including HCI to access the Bluetooth baseband capabilities */ +#define L2CAP_HCI 1 //TODO: NEEDED? +/* L2CAP_CFG_QOS: Control if a flow specification similar to RFC 1363 should be used */ +#define L2CAP_CFG_QOS 0 +/* L2CAP_MTU: Maximum transmission unit for L2CAP packet payload (min 48) */ +#define L2CAP_MTU (RFCOMM_N + 6)/* Default for this implementation is RFCOMM MFS + RFCOMM header size and + RFCOMM FCS size while the L2CAP default is 672 */ +/* L2CAP_OUT_FLUSHTO: For some networking protocols, such as many real-time protocols, guaranteed delivery + is undesirable. The flush time-out value SHALL be set to its default value 0xffff for a reliable L2CAP + channel, and MAY be set to other values if guaranteed delivery is not desired. (min 1) */ +#define L2CAP_OUT_FLUSHTO 0xFFFF /* Default: 0xFFFF. Infinite number of retransmissions (reliable channel) + The value of 1 implies no retransmissions at the Baseband level + should be performed since the minimum polling interval is 1.25 ms.*/ +/* L2CAP_RTX: The Responsive Timeout eXpired timer is used to terminate + the channel when the remote endpoint is unresponsive to signalling + requests (min 1s, max 60s) */ +#define L2CAP_RTX 60 +/* L2CAP_ERTX: The Extended Response Timeout eXpired timer is used in + place of the RTC timer when a L2CAP_ConnectRspPnd event is received + (min 60s, max 300s) */ +#define L2CAP_ERTX 300 +/* L2CAP_MAXRTX: Maximum number of Request retransmissions before + terminating the channel identified by the request. The decision + should be based on the flush timeout of the signalling link. If the + flush timeout is infinite, no retransmissions should be performed */ +#define L2CAP_MAXRTX 0 +/* L2CAP_CFG_TO: Amount of time spent arbitrating the channel parameters + before terminating the connection (max 120s) */ +#define L2CAP_CFG_TO 30 + +/* ---------- SDP options ---------- */ + +/* ---------- RFCOMM options ---------- */ +/* RFCOMM_N: Maximum frame size for RFCOMM segments (min 23, max 32767)*/ +#define RFCOMM_N ((1024) + 8) /* Default: Worst case byte stuffed PPP packet size + + non-compressed PPP header size and FCS size */ +/* RFCOMM_K: Initial amount of credits issued to the peer (min 0, max 7) */ +#define RFCOMM_K 0 +/* RFCOMM_TO: Acknowledgement timer (T1) and response timer for multiplexer control channel (T2). + T1 is the timeout for frames sent with the P/F bit set to 1 (SABM and DISC) and T2 is the timeout + for commands sent in UIH frames on DLCI 0 (min 10s, max 60s) */ +#define RFCOMM_TO 20 +/* RFCOMM_FLOW_QUEUEING: Control if a packet should be queued if a channel is out of credits for + outgoing packets. Only the first packet sent when out of credits will be queued */ +#define RFCOMM_FLOW_QUEUEING 0 /* Default: 0 */ + + +#endif /* __LWBTOPTS_H__ */ diff --git a/kernel/include/lwbt/phybusif.h b/kernel/include/lwbt/phybusif.h new file mode 100644 index 0000000..9963189 --- /dev/null +++ b/kernel/include/lwbt/phybusif.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __LWBT_PHYBUSIF_H__ +#define __LWBT_PHYBUSIF_H__ + +#include "lwip/pbuf.h" +#include "lwip/err.h" + +struct phybusif_cb; + +/* Application program's interface: */ +void phybusif_init(const char * port); /* Must be called first to initialize the physical bus interface */ +err_t phybusif_reset(struct phybusif_cb *cb); +err_t phybusif_input(struct phybusif_cb *cb); + +/* Upper layer interface: */ +void phybusif_output(struct pbuf *p, u16_t len); + +enum phybusif_state { + W4_PACKET_TYPE, W4_EVENT_HDR, W4_EVENT_PARAM, W4_ACL_HDR, W4_ACL_DATA +}; + +/* The physical bus interface control block */ +struct phybusif_cb { + enum phybusif_state state; + + struct pbuf *p; + struct pbuf *q; + + struct hci_event_hdr *evhdr; + struct hci_acl_hdr *aclhdr; + + unsigned int tot_recvd; + unsigned int recvd; +}; + +#endif /* __LWBT_PHYBUSIF_H__ */ diff --git a/kernel/include/lwbt/rfcomm.h b/kernel/include/lwbt/rfcomm.h new file mode 100644 index 0000000..f00f465 --- /dev/null +++ b/kernel/include/lwbt/rfcomm.h @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __LWBT_RFCOMM_H__ +#define __LWBT_RFCOMM_H__ + +#include "lwbt/l2cap.h" + + +//struct rfcomm_pcb; + +/* Functions for interfacing with RFCOMM: */ + +/* Lower layer interface to RFCOMM: */ +void rfcomm_init(void); /* Must be called first to initialize RFCOMM */ +void rfcomm_tmr(void); /* Must be called every 1s interval */ + +/* Application program's interface: */ +struct rfcomm_pcb *rfcomm_new(struct l2cap_pcb *pcb); +void rfcomm_close(struct rfcomm_pcb *pcb); +void rfcomm_reset_all(void); +void rfcomm_arg(struct rfcomm_pcb *pcb, void *arg); +void rfcomm_recv(struct rfcomm_pcb *pcb, + err_t (* recv)(void *arg, struct rfcomm_pcb *pcb, struct pbuf *p, err_t err)); +void rfcomm_disc(struct rfcomm_pcb *pcb, + err_t (* disc)(void *arg, struct rfcomm_pcb *pcb, err_t err)); + +#define rfcomm_cn(pcb) ((pcb)->cn) +#define rfcomm_cl(pcb) ((pcb)->cl) +#define rfcomm_local_credits(pcb) ((pcb)->k) +#define rfcomm_remote_credits(pcb) ((pcb)->rk) +#define rfcomm_fc(pcb) ((pcb)->fc) +#define rfcomm_mfs(pcb) ((pcb)->n) + +err_t rfcomm_input(void *arg, struct l2cap_pcb *l2cappcb, struct pbuf *p, err_t err); + +err_t rfcomm_connect(struct rfcomm_pcb *pcb, u8_t cn, + err_t (* connected)(void *arg, struct rfcomm_pcb *tpcb, err_t err)); +err_t rfcomm_disconnect(struct rfcomm_pcb *pcb); +err_t rfcomm_listen(struct rfcomm_pcb *npcb, u8_t cn, + err_t (* accept)(void *arg, struct rfcomm_pcb *pcb, err_t err)); +err_t rfcomm_pn(struct rfcomm_pcb *pcb, + err_t (* pn_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err)); +err_t rfcomm_test(struct rfcomm_pcb *pcb, + err_t (* test_rsp)(void *arg, struct rfcomm_pcb *tpcb, err_t err)); +err_t rfcomm_msc(struct rfcomm_pcb *pcb, u8_t fc, + err_t (* msc_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err)); +err_t rfcomm_rpn(struct rfcomm_pcb *pcb, u8_t br, + err_t (* rpn_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err)); +err_t rfcomm_uih(struct rfcomm_pcb *pcb, u8_t cn, struct pbuf *q); +err_t rfcomm_uih_credits(struct rfcomm_pcb *pcb, u8_t credits, struct pbuf *q); + +err_t rfcomm_lp_disconnected(struct l2cap_pcb *pcb); + + +/* Control field values */ +#define RFCOMM_SABM 0x3F +#define RFCOMM_UA 0x73 +#define RFCOMM_DM 0x0F +#define RFCOMM_DM_PF 0x1F +#define RFCOMM_DISC 0x53 +#define RFCOMM_UIH 0xEF +#define RFCOMM_UIH_PF 0xFF + +/* Multiplexer message types */ +#define RFCOMM_PN_CMD 0x83 +#define RFCOMM_PN_RSP 0x81 +#define RFCOMM_TEST_CMD 0x23 +#define RFCOMM_TEST_RSP 0x21 +#define RFCOMM_FCON_CMD 0xA3 +#define RFCOMM_FCON_RSP 0xA1 +#define RFCOMM_FCOFF_CMD 0x63 +#define RFCOMM_FCOFF_RSP 0x61 +#define RFCOMM_MSC_CMD 0xE3 +#define RFCOMM_MSC_RSP 0xE1 +#define RFCOMM_RPN_CMD 0x93 +#define RFCOMM_RPN_RSP 0x91 +#define RFCOMM_RLS_CMD 0x53 +#define RFCOMM_RLS_RSP 0x51 +#define RFCOMM_NSC_RSP 0x11 + +/* Length of RFCOMM hdr with 1 or 2 byte lengh field excluding credit */ +#define RFCOMM_HDR_LEN_1 3 +#define RFCOMM_HDR_LEN_2 4 + +/* Length of a multiplexer message */ +#define RFCOMM_MSGHDR_LEN 2 +#define RFCOMM_PNMSG_LEN 8 +#define RFCOMM_MSCMSG_LEN 2 +#define RFCOMM_RPNMSG_LEN 8 +#define RFCOMM_NCMSG_LEN 1 + +/* Length of a frame */ +#define RFCOMM_DM_LEN 4 +#define RFCOMM_SABM_LEN 4 +#define RFCOMM_DISC_LEN 4 +#define RFCOMM_UA_LEN 4 +#define RFCOMM_UIH_LEN 3 +#define RFCOMM_UIHCRED_LEN 4 + +/* Default convergence layer */ +#define RFCOMM_CL 0xF /* Credit based flow control */ + +/* Default port settings for a communication link */ +#define RFCOMM_COM_BR 0x03 /* Baud rate (9600 bit/s)*/ +#define RFCOMM_COM_CFG 0x03 /* Data bits (8 bits), stop bits (1), parity (no parity) + and parity type */ +#define RFCOMM_COM_FC 0x00 /* Flow control (no flow ctrl) */ +#define RFCOMM_COM_XON 0x00 /* No flow control (default DC1) */ +#define RFCOMM_COM_XOFF 0x00 /* No flow control (default DC3) */ + +/* FCS calc */ +#define RFCOMM_CODE_WORD 0xE0 /* pol = x8+x2+x1+1 */ +#define RFCOMM_CRC_CHECK_LEN 3 +#define RFCOMM_UIHCRC_CHECK_LEN 2 + +/* RFCOMM configuration parameter masks */ +#define RFCOMM_CFG_IR 0x01 +#define RFCOMM_CFG_FC 0x02 +#define RFCOMM_CFG_MSC_OUT 0x04 +#define RFCOMM_CFG_MSC_IN 0x08 + +enum rfcomm_state { + RFCOMM_CLOSED, RFCOMM_LISTEN, W4_RFCOMM_MULTIPLEXER, W4_RFCOMM_SABM_RSP, RFCOMM_CFG, RFCOMM_OPEN, + W4_RFCOMM_DISC_RSP +}; + +/* The RFCOMM frame header */ +struct rfcomm_hdr { + u8_t addr; + u8_t ctrl; + u16_t len; + u8_t k; +}; + +struct rfcomm_msg_hdr { + u8_t type; + u8_t len; +}; + +struct rfcomm_pn_msg { + u8_t dlci; /* Data link connection id */ + u8_t i_cl; /* Type frame for information and Convergece layer to use */ + u8_t p; /* Priority */ + u8_t t; /* Value of acknowledgement timer */ + u16_t n; /* Maximum frame size */ + u8_t na; /* Maximum number of retransmissions */ + u8_t k; /* Initial credit value */ +}; + +struct rfcomm_msc_msg { + u8_t dlci; /* Data link connection id */ + u8_t rs232; /* RS232 control signals */ +}; + +struct rfcomm_rpn_msg { + u8_t dlci; /* Data link connection id */ + u8_t br; /* Baud Rate */ + u8_t cfg; /* Data bits, Stop bits, Parity, Parity type */ + u8_t fc; /* Flow control */ + u8_t xon; + u8_t xoff; + u16_t mask; +}; + +/* The RFCOMM protocol control block */ +struct rfcomm_pcb { + struct rfcomm_pcb *next; /* For the linked list */ + + enum rfcomm_state state; /* RFCOMM state */ + + struct l2cap_pcb *l2cappcb; /* The L2CAP connection */ + + u8_t cn; /* Channel number */ + + u8_t cl; /* Convergence layer */ + u8_t p; /* Connection priority */ + u16_t n; /* Maximum frame size */ + u8_t k; /* No of local credits */ + + u8_t rk; /* No of remote credits */ + + u8_t rfcommcfg; /* Bit 1 indicates if we are the initiator of this connection + * Bit 2 indicates if the flow control bit is set so that we are allowed to send data + * Bit 3 indicates if modem status has been configured for the incoming direction + * Bit 4 indicates if modem status has been configured for the outgoing direction + */ + + u16_t to; /* Frame and cmd timeout */ + + u8_t uih_in_fcs; /* Frame check sequence for uih frames (P/F bit = 0) */ + u8_t uihpf_in_fcs; /* Frame check sequence for uih frames (P/F bit = 1) */ + u8_t uih_out_fcs; /* Frame check sequence for uih frames (P/F bit = 0) */ + u8_t uihpf_out_fcs; /* Frame check sequence for uih frames (P/F bit = 1) */ + + u8_t uih0_in_fcs; /* Frame check sequence for uih frames on the control channel (P/F bit = 0) */ + u8_t uih0_out_fcs; /* Frame check sequence for uih frames on the control channel (P/F bit = 0) */ + +#if RFCOMM_FLOW_QUEUEING + struct pbuf *buf; +#endif + void *callback_arg; + + /* RFCOMM Frame commands and responses */ + err_t (* connected)(void *arg, struct rfcomm_pcb *pcb, err_t err); + err_t (* accept)(void *arg, struct rfcomm_pcb *pcb, err_t err); + err_t (* disconnected)(void *arg, struct rfcomm_pcb *pcb, err_t err); + + /* RFCOMM Multiplexer responses */ + err_t (* pn_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err); + err_t (* test_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err); + err_t (* msc_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err); + err_t (* rpn_rsp)(void *arg, struct rfcomm_pcb *pcb, err_t err); + + err_t (* recv)(void *arg, struct rfcomm_pcb *pcb, struct pbuf *p, err_t err); +}; + +/* The server channel */ +struct rfcomm_pcb_listen { + struct rfcomm_pcb_listen *next; /* For the linked list */ + + enum rfcomm_state state; /* RFCOMM state */ + + u8_t cn; /* Channel number */ + + void *callback_arg; + + err_t (* accept)(void *arg, struct rfcomm_pcb *pcb, err_t err); +}; + +#define RFCOMM_EVENT_CONNECTED(pcb,err,ret) \ + if((pcb)->connected != NULL) \ + (ret = (pcb)->connected((pcb)->callback_arg,(pcb),(err))) +#define RFCOMM_EVENT_ACCEPT(pcb,err,ret) \ + if((pcb)->accept != NULL) \ + (ret = (pcb)->accept((pcb)->callback_arg,(pcb),(err))) +#define RFCOMM_EVENT_DISCONNECTED(pcb,err,ret) \ + if((pcb)->disconnected != NULL) { \ + (ret = (pcb)->disconnected((pcb)->callback_arg,(pcb),(err))); \ + } else { \ + rfcomm_close(pcb); \ + } +#define RFCOMM_EVENT_PN_RSP(pcb,err,ret) \ + if((pcb)->pn_rsp != NULL) \ + (ret = (pcb)->pn_rsp((pcb)->callback_arg,(pcb),(err))) +#define RFCOMM_EVENT_TEST(pcb,err,ret) \ + if((pcb)->test_rsp != NULL) \ + (ret = (pcb)->test_rsp((pcb)->callback_arg,(pcb),(err))) +#define RFCOMM_EVENT_MSC(pcb,err,ret) \ + if((pcb)->msc_rsp != NULL) \ + (ret = (pcb)->msc_rsp((pcb)->callback_arg,(pcb),(err))) +#define RFCOMM_EVENT_RPN(pcb,err,ret) \ + if((pcb)->rpn_rsp != NULL) \ + (ret = (pcb)->rpn_rsp((pcb)->callback_arg,(pcb),(err))) +#define RFCOMM_EVENT_RECV(pcb,err,p,ret) \ + if((pcb)->recv != NULL) { \ + (ret = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err))); \ + } else { \ + pbuf_free(p); \ + } + + +/* The RFCOMM PCB lists. */ +extern struct rfcomm_pcb_listen *rfcomm_listen_pcbs; /* List of all RFCOMM PCBs listening for + an incomming connection on a specific + server channel */ +extern struct rfcomm_pcb *rfcomm_active_pcbs; /* List of all active RFCOMM PCBs */ + +extern struct rfcomm_pcb *rfcomm_tmp_pcb; /* Only used for temporary storage. */ + +/* Define two macros, RFCOMM_REG and RFCOMM_RMV that registers a RFCOMM PCB + with a PCB list or removes a PCB from a list, respectively. */ + +#define RFCOMM_REG(pcbs, npcb) \ + do { \ + (npcb)->next = *(pcbs); \ + *(pcbs) = (npcb); \ + } while(0) + +#define RFCOMM_RMV(pcbs, npcb) \ + do { \ + if(*(pcbs) == (npcb)) { \ + *(pcbs) = (*(pcbs))->next; \ + } else for(rfcomm_tmp_pcb = *(pcbs); rfcomm_tmp_pcb != NULL; rfcomm_tmp_pcb = rfcomm_tmp_pcb->next) { \ + if(rfcomm_tmp_pcb->next != NULL && rfcomm_tmp_pcb->next == (npcb)) { \ + rfcomm_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + (npcb)->next = NULL; \ + } while(0) + +#endif /* __RFCOMM_H__ */ diff --git a/kernel/include/lwbt/sdp.h b/kernel/include/lwbt/sdp.h new file mode 100644 index 0000000..34d8daa --- /dev/null +++ b/kernel/include/lwbt/sdp.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __LWBT_SDP_H__ +#define __LWBT_SDP_H__ + +#include "lwbt/l2cap.h" + +struct sdp_pcb; +struct sdp_record; +struct sdp_attribute; +struct sdp_element; + +/* Functions for interfacing with SDP: */ +void sdp_init(void); + +/* Client API */ +struct sdp_pcb *sdp_new(struct l2cap_pcb *l2cappcb); +void sdp_free(struct sdp_pcb *pcb); +void sdp_reset_all(void); +void sdp_arg(struct sdp_pcb *pcb, void *arg); +err_t sdp_service_search_req(struct sdp_pcb *pcb, u8_t *ssp, u8_t ssplen, u16_t max_src, + void (* service_searched)(void *arg, struct sdp_pcb *pcb, u16_t tot_src, + u16_t curr_src, u32_t *rhdls)); +err_t sdp_service_attrib_req(struct sdp_pcb *pcb, u32_t srhdl, u16_t max_abc, u8_t *attrids, u8_t attrlen, + void (* attributes_recv)(void *arg, struct sdp_pcb *pcb, u16_t attribl_bc, struct pbuf *p)); +err_t sdp_service_search_attrib_req(struct sdp_pcb *pcb, u16_t max_abc, u8_t *ssp, u8_t ssplen, u8_t *attrids, + u8_t attrlen, void (* attributes_searched)(void *arg, struct sdp_pcb *pcb, + u16_t attribl_bc, + struct pbuf *p)); + +/* Server API */ +/* Functions to be used when adding and removing service records to and from the SDDB */ +struct sdp_record *sdp_record_new(u8_t *record_de_list, u16_t rlen); +void sdp_record_free(struct sdp_record *record); +err_t sdp_register_service(struct sdp_record *record); +void sdp_unregister_service(struct sdp_record *record); + +/* Lower layer API */ +void sdp_lp_disconnected(struct l2cap_pcb *l2cappcb); +err_t sdp_recv(void *arg, struct l2cap_pcb *pcb, struct pbuf *p, err_t err); + +/* Type is constructed by ORing a type and size bitmask. + Size is ignored for String, URL and sequence types. + For String, URL types, the given value must be a char*, + from which the size is calculated. + For a sequence type the size is calculated directly from the + list of elements added into the sequence. + For integer types greater than 32 bit, and for 128 bit UUID + types, the value is given as a byte array. +*/ + +#define SDP_DE_TYPE_NIL (0<<3) /* Nil, the null type */ +#define SDP_DE_TYPE_UINT (1<<3) /* Unsigned Integer */ +#define SDP_DE_TYPE_STCI (2<<3) /* Signed, twos-complement integer */ +#define SDP_DE_TYPE_UUID (3<<3) /* UUID, a universally unique identifier */ +#define SDP_DE_TYPE_STR (4<<3) /* Text string */ +#define SDP_DE_TYPE_BOOL (5<<3) /* Boolean */ +#define SDP_DE_TYPE_DES (6<<3) /* Data Element Sequence */ +#define SDP_DE_TYPE_DEA (7<<3) /* Data Element Alternative */ +#define SDP_DE_TYPE_URL (8<<3) /* URL, a uniform resource locator */ + +#define SDP_DE_SIZE_8 0x0 /* 8 bit integer value */ +#define SDP_DE_SIZE_16 0x1 /* 16 bit integer value */ +#define SDP_DE_SIZE_32 0x2 /* 32 bit integer value */ +#define SDP_DE_SIZE_64 0x3 /* 64 bit integer value */ +#define SDP_DE_SIZE_128 0x4 /* 128 bit integer value */ +#define SDP_DE_SIZE_N1 0x5 /* Data size is in next 1 byte */ +#define SDP_DE_SIZE_N2 0x6 /* Data size is in next 2 bytes */ +#define SDP_DE_SIZE_N4 0x7 /* Data size is in next 4 bytes */ + +#define SDP_DES_SIZE8 (SDP_DE_TYPE_DES | SDP_DE_SIZE_N1) +#define SDP_DES_SIZE16 (SDP_DE_TYPE_DES | SDP_DE_SIZE_N2) +#define SDP_UINT8 (SDP_DE_TYPE_UINT | SDP_DE_SIZE_8) +#define SDP_UINT16 (SDP_DE_TYPE_UINT | SDP_DE_SIZE_16) +#define SDP_UINT32 (SDP_DE_TYPE_UINT | SDP_DE_SIZE_32) +#define SDP_UUID16 (SDP_DE_TYPE_UUID | SDP_DE_SIZE_16) +#define SDP_UUID128 (SDP_DE_TYPE_UUID | SDP_DE_SIZE_128) + +/* PDU identifiers */ +#define SDP_ERR_PDU 0x01 +#define SDP_SS_PDU 0x02 +#define SDP_SSR_PDU 0x03 +#define SDP_SA_PDU 0x04 +#define SDP_SAR_PDU 0x05 +#define SDP_SSA_PDU 0x06 +#define SDP_SSAR_PDU 0x07 + +/* Response lengths and sizes */ +#define SDP_PDUHDR_LEN 5 +#define SDP_ATTRIBIDHDR_LEN 3 +#define SDP_SSR_LEN 4 +#define SDP_SRHDL_SIZE 4 /* Size of a service record handle */ + +/**/ +#define SDP_MAX_SRHDLS 12 + +struct sdp_hdr { + u8_t pdu; + u16_t id; + u16_t len; +} PACK_STRUCT_STRUCT; + +struct sdp_record { + struct sdp_record *next; /* For the linked list */ + + u32_t hdl; /* Service Record Handle */ + u8_t *record_de_list; + u16_t len; +}; + +/* The SDP protocol control block */ +struct sdp_pcb { + struct sdp_pcb *next; /* For the linked list */ + + struct l2cap_pcb *l2cappcb; /* The L2CAP connection */ + + u16_t tid; /* Transaction ID */ + + void *callback_arg; + + void (* service_searched)(void *arg, struct sdp_pcb *pcb, u16_t tot_src, u16_t curr_src, u32_t *rhdls); + void (* attributes_recv)(void *arg, struct sdp_pcb *pcb, u16_t attribl_bc,struct pbuf *p); + void (* attributes_searched)(void *arg, struct sdp_pcb *pcb, u16_t attribl_bc, struct pbuf *p); +}; + +#define SDP_DE_TYPE(type_size) ((type_size) & 0xF8) +#define SDP_DE_SIZE(type_size) ((type_size) & 0x07) + +#define SDP_ACTION_SERVICE_SEARCHED(pcb,tot_src,curr_src,rhdls) if((pcb)->service_searched != NULL) ((pcb)->service_searched((pcb)->callback_arg,(pcb),(tot_src),(curr_src),(rhdls))) +#define SDP_ACTION_ATTRIB_RECV(pcb,attribl_bc,p) if((pcb)->attributes_recv != NULL) ((pcb)->attributes_recv((pcb)->callback_arg,(pcb),(attribl_bc),(p))) +#define SDP_ACTION_ATTRIB_SEARCHED(pcb,attribl_bc,p) if((pcb)-> attributes_searched != NULL) ((pcb)->attributes_searched((pcb)->callback_arg,(pcb),(attribl_bc),(p))) + +extern struct sdp_pcb *sdp_pcbs; /* List of all SDP PCBs awaiting incoming response to + a request */ +extern struct sdp_pcb *sdp_tmp_pcb; /* Only used for temporary storage. */ + +extern struct sdp_record *sdp_server_records; /* List of all active service records in the + SDP server */ +extern struct sdp_record *sdp_tmp_record; /* Only used for temporary storage. */ + +#define SDP_REG(pcbs, npcb) do { \ + npcb->next = *pcbs; \ + *pcbs = npcb; \ + } while(0) +#define SDP_RMV(pcbs, npcb) do { \ + if(*pcbs == npcb) { \ + *pcbs = (*pcbs)->next; \ + } else for(sdp_tmp_pcb = *pcbs; sdp_tmp_pcb != NULL; sdp_tmp_pcb = sdp_tmp_pcb->next) { \ + if(sdp_tmp_pcb->next != NULL && sdp_tmp_pcb->next == npcb) { \ + sdp_tmp_pcb->next = npcb->next; \ + break; \ + } \ + } \ + npcb->next = NULL; \ + } while(0) +#define SDP_RECORD_REG(records, record) do { \ + record->next = *records; \ + *records = record; \ + } while(0) +#define SDP_RECORD_RMV(records, record) do { \ + if(*records == record) { \ + *records = (*records)->next; \ + } else for(sdp_tmp_record = *records; sdp_tmp_record != NULL; sdp_tmp_record = sdp_tmp_record->next) { \ + if(sdp_tmp_record->next != NULL && sdp_tmp_record->next == record) { \ + sdp_tmp_record->next = record->next; \ + break; \ + } \ + } \ + record->next = NULL; \ + } while(0) + +#endif /* __LWBT_SDP_H__ */ diff --git a/kernel/include/lwip/opt.h b/kernel/include/lwip/opt.h index a1b8765..b3a00b5 100644 --- a/kernel/include/lwip/opt.h +++ b/kernel/include/lwip/opt.h @@ -92,6 +92,15 @@ #define SMEMCPY(dst,src,len) memcpy(dst,src,len) #endif + +/** +* MEMCPY: override this if you have a faster implementation at hand than the +* one included in your C library +*/ +#ifndef MEMSET +#define MEMSET(dst,val,len) memset(dst,val,len) +#endif + /* ------------------------------------ ---------- Memory options ---------- diff --git a/kernel/master.vcxproj b/kernel/master.vcxproj index acc2738..da790fd 100644 --- a/kernel/master.vcxproj +++ b/kernel/master.vcxproj @@ -153,6 +153,13 @@ + + + + + + + @@ -312,7 +319,6 @@ - @@ -331,6 +337,15 @@ + + + + + + + + + diff --git a/kernel/master.vcxproj.filters b/kernel/master.vcxproj.filters index 94c08de..2efeb7d 100644 --- a/kernel/master.vcxproj.filters +++ b/kernel/master.vcxproj.filters @@ -23,10 +23,6 @@ {1ce50db2-8163-4f93-a138-53819420cac5} - - {2ab20da3-fb06-4503-964c-7ae0e770da37} - .cpp - {75a7017c-537e-49b1-b808-4bc8c3deaaec} @@ -87,6 +83,16 @@ {7a17f3ee-f237-47d6-8aaa-a729083628f8} + + {2ab20da3-fb06-4503-964c-7ae0e770da37} + .cpp + + + {e1c4960e-738e-47b4-9a42-cac0b0b567be} + + + {aa470ad4-363a-499f-b24d-6ed061b87daf} + @@ -557,11 +563,29 @@ Source Files\jvm + + Source Files\bluttooth + + + Source Files\bluttooth + + + Source Files\bluttooth + + + Source Files\bluttooth + + + Source Files\bluttooth + + + Source Files\bluttooth + + + Source Files\bluttooth + - - Header Files\include - Header Files\include @@ -910,6 +934,33 @@ Source Files\lib\sys + + Header Files\bt_hdr + + + Header Files\bt_hdr + + + Header Files\bt_hdr + + + Header Files\bt_hdr + + + Header Files\bt_hdr + + + Header Files\bt_hdr + + + Header Files\bt_hdr + + + Header Files\bt_hdr + + + Header Files\bt_hdr + diff --git a/sdk/vc60/sdklib/Debug/sdklib.pch b/sdk/vc60/sdklib/Debug/sdklib.pch deleted file mode 100644 index 0fb9e59..0000000 Binary files a/sdk/vc60/sdklib/Debug/sdklib.pch and /dev/null differ diff --git a/sdk/vc60/sdklib/Release/sdklib.pch b/sdk/vc60/sdklib/Release/sdklib.pch deleted file mode 100644 index 7187915..0000000 Binary files a/sdk/vc60/sdklib/Release/sdklib.pch and /dev/null differ diff --git a/tools/append/Release/APPEND.pch b/tools/append/Release/APPEND.pch deleted file mode 100644 index 2795b12..0000000 Binary files a/tools/append/Release/APPEND.pch and /dev/null differ diff --git a/tools/dumpf32/Release/DUMPF32.pch b/tools/dumpf32/Release/DUMPF32.pch deleted file mode 100644 index 11bb352..0000000 Binary files a/tools/dumpf32/Release/DUMPF32.pch and /dev/null differ diff --git a/tools/hcxbuild/Release/hcxbuild.pch b/tools/hcxbuild/Release/hcxbuild.pch deleted file mode 100644 index 81f9873..0000000 Binary files a/tools/hcxbuild/Release/hcxbuild.pch and /dev/null differ diff --git a/tools/mkntfsbs/Release/MKNTFSBS.pch b/tools/mkntfsbs/Release/MKNTFSBS.pch deleted file mode 100644 index 3c5cf41..0000000 Binary files a/tools/mkntfsbs/Release/MKNTFSBS.pch and /dev/null differ diff --git a/tools/process/Release/process.pch b/tools/process/Release/process.pch deleted file mode 100644 index 833bf08..0000000 Binary files a/tools/process/Release/process.pch and /dev/null differ