btstack/src/bnep.c

1192 lines
39 KiB
C
Raw Normal View History

/*
* Copyright (C) 2014 by Ole Reinhardt <ole.reinhardt@kernelconcepts.de>
*
* 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. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``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
* COPYRIGHT OWNER OR CONTRIBUTORS 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.
*
* Please inquire about commercial licensing options at btstack@ringwald.ch
*
*/
/*
* bnep.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // memcpy
#include <stdint.h>
#include <btstack/btstack.h>
#include <btstack/hci_cmds.h>
#include <btstack/utils.h>
#include <btstack/utils.h>
#include "btstack_memory.h"
#include "hci.h"
#include "hci_dump.h"
#include "debug.h"
#include "bnep.h"
#include "l2cap.h"
static bnep_service_t * service = NULL;
static linked_list_t bnep_channels = NULL;
static gap_security_level_t bnep_security_level;
static void (*app_packet_handler)(void * connection, uint8_t packet_type,
uint16_t channel, uint8_t *packet, uint16_t size);
/* Emit service registered event */
static void bnep_emit_service_registered(void *connection, uint8_t status)
{
log_info("BNEP_EVENT_SERVICE_REGISTERED status 0x%x channel #%u", status);
uint8_t event[3];
event[0] = BNEP_EVENT_SERVICE_REGISTERED;
event[1] = sizeof(event) - 2;
event[2] = status;
hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
(*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event));
}
/* Send BNEP connection request */
static int bnep_send_command_not_understood(bnep_channel_t *channel, uint8_t control_type)
{
uint8_t *bnep_out_buffer = NULL;
uint16_t pos = 0;
int err = 0;
if (channel->state == BNEP_CHANNEL_STATE_CLOSED) {
return -1; // TODO
}
/* Check for free ACL buffers */
if (!l2cap_can_send_packet_now(channel->l2cap_cid)) {
return BTSTACK_ACL_BUFFERS_FULL;
}
l2cap_reserve_packet_buffer();
bnep_out_buffer = l2cap_get_outgoing_buffer();
/* Setup control packet type */
bnep_out_buffer[pos++] = BNEP_PKT_TYPE_CONTROL;
bnep_out_buffer[pos++] = BNEP_CONTROL_TYPE_COMMAND_NOT_UNDERSTOOD;
/* Add not understood control type */
bnep_out_buffer[pos++] = control_type;
err = l2cap_send_prepared(channel->l2cap_cid, pos);
if (err) {
// TODO: Log error
}
return err;
}
/* Send BNEP connection request */
static int bnep_send_connection_req(bnep_channel_t *channel, uint16_t uuid_source, uint16_t uuid_dest)
{
uint8_t *bnep_out_buffer = NULL;
uint16_t pos = 0;
int err = 0;
if (channel->state == BNEP_CHANNEL_STATE_CLOSED) {
return -1; // TODO
}
/* Check for free ACL buffers */
if (!l2cap_can_send_packet_now(channel->l2cap_cid)) {
return BTSTACK_ACL_BUFFERS_FULL;
}
l2cap_reserve_packet_buffer();
bnep_out_buffer = l2cap_get_outgoing_buffer();
/* Setup control packet type */
bnep_out_buffer[pos++] = BNEP_PKT_TYPE_CONTROL;
bnep_out_buffer[pos++] = BNEP_CONTROL_TYPE_SETUP_CONNECTION_REQUEST;
/* Add UUID Size */
bnep_out_buffer[pos++] = 2;
/* Add dest and source UUID */
bnep_out_buffer[pos++] = (uuid_dest >> 8) & 0xFF;
bnep_out_buffer[pos++] = uuid_dest & 0xFF;
bnep_out_buffer[pos++] = (uuid_source >> 8) & 0xFF;
bnep_out_buffer[pos++] = uuid_source & 0xFF;
err = l2cap_send_prepared(channel->l2cap_cid, pos);
if (err) {
// TODO: Log error
}
return err;
}
/* Send BNEP connection response */
static int bnep_send_connection_resp(bnep_channel_t *channel, uint16_t response_code)
{
uint8_t *bnep_out_buffer = NULL;
uint16_t pos = 0;
int err = 0;
if (channel->state == BNEP_CHANNEL_STATE_CLOSED) {
return -1; // TODO
}
/* Check for free ACL buffers */
if (!l2cap_can_send_packet_now(channel->l2cap_cid)) {
return BTSTACK_ACL_BUFFERS_FULL;
}
l2cap_reserve_packet_buffer();
bnep_out_buffer = l2cap_get_outgoing_buffer();
/* Setup control packet type */
bnep_out_buffer[pos++] = BNEP_PKT_TYPE_CONTROL;
bnep_out_buffer[pos++] = BNEP_CONTROL_TYPE_SETUP_CONNECTION_RESPONSE;
/* Add response code */
bnep_out_buffer[pos++] = (response_code >> 8) & 0xFF;
bnep_out_buffer[pos++] = response_code & 0xFF;
err = l2cap_send_prepared(channel->l2cap_cid, pos);
if (err) {
// TODO: Log error
}
return err;
}
/* Send BNEP filter net type set message */
static int bnep_send_filter_net_type_set(bnep_channel_t *channel, ...)
{
uint8_t *bnep_out_buffer = NULL;
uint16_t pos = 0;
int err = 0;
if (channel->state == BNEP_CHANNEL_STATE_CLOSED) {
return -1; // TODO
}
/* TODO: Not yet implemented */
return -1;
}
/* Send BNEP filter net type response message */
static int bnep_send_filter_net_type_resp(bnep_channel_t *channel, uint16_t response_code)
{
uint8_t *bnep_out_buffer = NULL;
uint16_t pos = 0;
int err = 0;
if (channel->state == BNEP_CHANNEL_STATE_CLOSED) {
return -1; // TODO
}
/* Check for free ACL buffers */
if (!l2cap_can_send_packet_now(channel->l2cap_cid)) {
return BTSTACK_ACL_BUFFERS_FULL;
}
l2cap_reserve_packet_buffer();
bnep_out_buffer = l2cap_get_outgoing_buffer();
/* Setup control packet type */
bnep_out_buffer[pos++] = BNEP_PKT_TYPE_CONTROL;
bnep_out_buffer[pos++] = BNEP_CONTROL_TYPE_FILTER_NET_TYPE_RESPONSE;
/* Add response code */
bnep_out_buffer[pos++] = (response_code >> 8) & 0xFF;
bnep_out_buffer[pos++] = response_code & 0xFF;
err = l2cap_send_prepared(channel->l2cap_cid, pos);
if (err) {
// TODO: Log error
}
return err;
}
/* Send BNEP filter multicast address set message */
static int bnep_send_filter_multi_addr_set(bnep_channel_t *channel, ...)
{
uint8_t *bnep_out_buffer = NULL;
uint16_t pos = 0;
int err = 0;
if (channel->state == BNEP_CHANNEL_STATE_CLOSED) {
return -1; // TODO
}
/* TODO: Not yet implemented */
return -1;
}
/* Send BNEP filter multicast address response message */
static int bnep_send_filter_multi_addr_resp(bnep_channel_t *channel, uint16_t response_code)
{
uint8_t *bnep_out_buffer = NULL;
uint16_t pos = 0;
int err = 0;
if (channel->state == BNEP_CHANNEL_STATE_CLOSED) {
return -1; // TODO
}
/* Check for free ACL buffers */
if (!l2cap_can_send_packet_now(channel->l2cap_cid)) {
return BTSTACK_ACL_BUFFERS_FULL;
}
l2cap_reserve_packet_buffer();
bnep_out_buffer = l2cap_get_outgoing_buffer();
/* Setup control packet type */
bnep_out_buffer[pos++] = BNEP_PKT_TYPE_CONTROL;
bnep_out_buffer[pos++] = BNEP_CONTROL_TYPE_FILTER_MULTI_ADDR_RESPONSE;
/* Add response code */
bnep_out_buffer[pos++] = (response_code >> 8) & 0xFF;
bnep_out_buffer[pos++] = response_code & 0xFF;
err = l2cap_send_prepared(channel->l2cap_cid, pos);
if (err) {
// TODO: Log error
}
return err;
}
/* Send BNEP ethernet packet */
static int bnep_send(bnep_channel_t *channel, uint8_t *src_addr, uint8_t *dest_addr, uint16_t protocol_type, uint8_t *payload, uint16_t len)
{
uint8_t *bnep_out_buffer = NULL;
uint16_t pos = 0;
int err = 0;
int has_src;
int has_dest;
if (channel->state == BNEP_CHANNEL_STATE_CLOSED) {
return -1; // TODO
}
/* Check for free ACL buffers */
if (!l2cap_can_send_packet_now(channel->l2cap_cid)) {
return BTSTACK_ACL_BUFFERS_FULL;
}
l2cap_reserve_packet_buffer();
bnep_out_buffer = l2cap_get_outgoing_buffer();
/* Check if source address is the same as our local address and if the
destination address is the same as the remote addr. Maybe we can use
the compressed data format
*/
has_src = (memcmp(src_addr, channel->local_addr, ETHER_ADDR_LEN) != 0);
has_dest = (memcmp(dest_addr, channel->remote_addr, ETHER_ADDR_LEN) != 0);
/* Fill in the package type depending on the given source and destination address */
if (has_src && has_dest) {
bnep_out_buffer[pos++] = BNEP_GENERAL_ETHERNET;
} else
if (has_src && !has_dest) {
bnep_out_buffer[pos++] = BNEP_PKT_TYPE_COMPRESSED_ETHERNET_SOURCE_ONLY;
} else
if (!has_src && has_dest) {
bnep_out_buffer[pos++] = BNEP_PKT_TYPE_COMPRESSED_ETHERNET_DEST_ONLY;
} else {
bnep_out_buffer[pos++] = BNEP_COMPRESSED_ETHERNET;
}
/* Add the destination address if needed */
if (has_dest) {
memcpy(bnep_out_buffer + pos, dest_addr, ETHER_ADDR_LEN);
pos += ETHER_ADDR_LEN;
}
/* Add the source address if needed */
if (src) {
memcpy(bnep_out_buffer + pos, src_addr, ETHER_ADDR_LEN);
pos += ETHER_ADDR_LEN;
}
/* Add protocol type */
bnep_out_buffer[pos++] = (protocol_type >> 8) & 0xFF;
bnep_out_buffer[pos++] = protocol_type & 0xFF;
/* TODO: Add extension headers, if we may support them at a later stage */
/* Check for MTU limits add the payload and then send out the package */
if (pos + len <= channel->mtu) {
memcpy(bnep_out_buffer + pos, payload, len);
pos += len;
err = l2cap_send_prepared(channel->l2cap_cid, pos);
} else {
// TODO: Error, MTU exceeded
}
if (err) {
// TODO: Log error
}
return err;
}
static uint16_t bnep_max_frame_size_for_l2cap_mtu(uint16_t l2cap_mtu){
/* Assume a standard BNEP header, containing BNEP Type (1 Byte), dest and
source address (6 bytes each) and networking protocol type (2 bytes)
*/
uint16_t max_frame_size = l2cap_mtu - 15; // 15 bytes BNEP header
// single byte can denote len up to 127
if (max_frame_size > 127) {
max_frame_size--;
}
log_info("bnep_max_frame_size_for_l2cap_mtu: %u -> %u", l2cap_mtu, max_frame_size);
return max_frame_size;
}
static bnep_channel_t * bnep_channel_create_for_addr(bd_addr_t *addr)
{
/* Allocate new channel structure */
bnep_channel_t *channel = btstack_memory_benp_channel_get();
if (!channel) {
return NULL;
}
/* Initialize the channel struct */
memset(channel, 0, sizeof(bnep_channel_t));
channel->state = BNEP_CHANNEL_STATE_CLOSED;
channel->max_frame_size = bnep_max_frame_size_for_l2cap_mtu(l2cap_max_mtu());
BD_ADDR_COPY(&channel->remote_addr, addr);
channel->net_filter_count = 0;
channel->multicast_filter_count = 0;
/* Finally add it to the channel list */
linked_list_add(&bnep_channels, (linked_item_t *) channel);
return channel;
}
static bnep_channel_t* bnep_channel_for_addr(bd_addr_t *addr)
{
linked_item_t *it;
for (it = (linked_item_t *) bnep_channels; it ; it = it->next){
bnep_channel_t *channel = ((bnep_channel_t *) it);
if (BD_ADDR_CMP(addr, channel->remote_addr) == 0) {
return channel;
};
}
return NULL;
}
static bnep_channel_t * bnep_channel_for_l2cap_cid(uint16_t l2cap_cid)
{
linked_item_t *it;
for (it = (linked_item_t *) bnep_channels; it ; it = it->next){
bnep_channel_t *channel = ((bnep_channel_t *) it);
if (multiplexer->l2cap_cid == l2cap_cid) {
return channel;
};
}
return NULL;
}
static void bnep_channel_free(bnep_channel_t *channel)
{
linked_list_remove( &bnep_channels, (linked_item_t *) channel);
btstack_memory_bnep_channel_free(channel);
}
static void bnep_channel_finalize(bnep_channel_t *channel)
{
uint16_t l2cap_cid;
/* Inform application about closed channel */
if (channel->state == BNEP_CHANNEL_STATE_OPEN) {
bnep_emit_channel_closed(channel);
}
l2cap_cid = channel->l2cap_cid;
/* Free ressources and then close the l2cap channel */
bnep_channel_free(channel);
l2cap_disconnect_internal(l2cap_cid, 0x13);
}
static int bnep_handle_connection_request(bnep_channel_t *channel, uint8_t *packet, uint16_t size)
{
uint16_t uuid_size;
uint16_t uuid_offset;
uuid_size = packet[2];
uint16_t response_code = BNEP_RESP_SETUP_SUCCESS;
/* Sanity check packet size */
if (size < 1 + 1 + 1 + 2 * uuid_size) {
return 0;
}
if ((channel->state != BNEP_CHANNEL_STATE_WAIT_FOR_CONNECTION_REQUEST) &&
(channel->state != BNEP_CHANNEL_STATE_CONNECTED)) {
/* Ignore a connection request if not waiting for or still connected */
log_info("BNEP_CONNCTION_REQUEST: ignored in state %d, l2cap_cid: %d!", channel->state, channel->l2cap_cid);
return 0;
}
/* Extract source and destination UUID and convert them to UUID16 format */
switch (uuid_size) {
case 2: /* UUID16 */
uuid_offset = 0;
break;
case 4: /* UUID32 */
case 16: /* UUID128 */
uuid_offset = 2;
break;
default:
log_info("BNEP_CONNCTION_REQUEST: Invalid UUID size %d, l2cap_cid: %d!", channel->state, channel->l2cap_cid);
response_code = BNEP_RESP_SETUP_INVALID_SERVICE_UUID_SIZE;
break;
}
/* Check source and destination UUIDs for valid combinations */
if (response_code == BNEP_RESP_SETUP_SUCCESS) {
channel->uuid_dest = READ_BT_16(packet, 3 + uuid_offset);
channel->uuid_source = READ_BT_16(packet, 3 + uuid_offset + uuid_size);
if ((channel->uuid_dest != BNEP_UUID_PANU) &&
(channel->uuid_dest != BNEP_UUID_NAP) &&
(channel->uuid_dest != BNEP_UUID_GN)) {
log_info("BNEP_CONNCTION_REQUEST: Invalid destination service UUID: %04x", channel->uuid_dest);
channel->uuid_dest = 0;
}
if ((channel->uuid_source != BNEP_UUID_PANU) &&
(channel->uuid_source != BNEP_UUID_NAP) &&
(channel->uuid_source != BNEP_UUID_GN)) {
log_info("BNEP_CONNCTION_REQUEST: Invalid source service UUID: %04x", channel->uuid_source);
channel->uuid_source = 0;
}
if (channel->uuid_dest != service->service_uuid) {
response_code = BNEP_RESP_SETUP_INVALID_DEST_UUID;
} else
if ((channel->uuid_source != BNEP_UUID_PANU) && (channel->uuid_dest != BNEP_UUID_PANU)) {
response_code = BNEP_RESP_SETUP_INVALID_SOURCE_UUID;
}
}
bnep_send_connection_resp(channel, response_code);
/* Set the channel state to CONNECTED */
channel->state = BNEP_CHANNEL_STATE_CONNECTED;
// TODO: Emit channel connected event
/* Return the number of processed package bytes = BNEP Type, BNEP Control Type, UUID-Size + 2 * UUID */
return 1 + 1 + 1 + 2 * uuid_size;
}
static int bnep_handle_connection_response(bnep_channel_t *channel, uint8_t *packet, uint16_t size)
{
uint16_t response_code;
/* Sanity check packet size */
if (size < 1 + 1 + 2) {
return 0;
}
if (channel->state != BNEP_CHANNEL_STATE_WAIT_FOR_CONNECTION_RESPONSE) {
/* Ignore a connection response in any state but WAIT_FOR_CONNECTION_RESPONSE */
log_info("BNEP_CONNCTION_RESPONSE: Ignored in channel state %d", channel->state);
return 1 + 1 + 2;
}
response_code = READ_BT_16(packet, 2);
if (response_code == BNEP_RESP_SETUP_SUCCESS) {
log_info("BNEP_CONNCTION_RESPONSE: Channel established to %s", ether_ntoa(channel->remote_addr));
channel->state = BNEP_CHANNEL_STATE_CONNECTED;
} else {
log_info("BNEP_CONNCTION_RESPONSE: Connection to %s failed. Err: %d", ether_ntoa(channel->remote_addr), response_code);
bnep_channel_finalize(channel);
}
return 1 + 1 + 2;
}
static int bnep_handle_filter_net_type_set(bnep_channel_t *channel, uint8_t *packet, uint16_t size)
{
uint16_t list_length;
uint16_t response_code = BNEP_RESP_FILTER_SUCCESS;
/* Sanity check packet size */
if (size < 2) {
return 0;
}
list_length = READ_BT_16(packet, 2);
/* Sanity check packet size again with known package size */
if (size < 2 + list_length) {
return 0;
}
if (channel->state != BNEP_CHANNEL_STATE_CONNECTED) {
/* Ignore filter net type set in any state but CONNECTED */
log_info("BNEP_FILTER_NET_TYPE_SET: Ignored in channel state %d", channel->state);
return 2 + list_length;
}
/* Check if we have enough space for more filters */
if ((list_length / (2*2)) + channel->net_filter_count > MAX_BNEP_NETFILTER) {
log_info("BNEP_FILTER_NET_TYPE_SET: Too many filter");
response_code = BNEP_RESP_FILTER_ERR_TOO_MANY_FILTERS;
} else {
int i;
/* There is still enough space, copy the filters to our filter list */
for (i = 0; i < list_length / (2*2); i ++) {
channel->net_filter[channel->net_filter_count].range_start = READ_BT_16(packet, 4 + i * 4);
channel->net_filter[channel->net_filter_count].range_end = READ_BT_16(packet, 4 + i * 4 + 2);
if (channel->net_filter[channel->net_filter_count].range_start > channel->net_filter[channel->net_filter_count].range_end) {
/* Invalid filter range, ignore this filter rule */
log_info("BNEP_FILTER_NET_TYPE_SET: Invalid filter: start: %d, end: %d",
channel->net_filter[channel->net_filter_count].range_start,
channel->net_filter[channel->net_filter_count].range_end);
response_code = BNEP_RESP_FILTER_ERR_INVALID_RANGE;
} else {
/* Valid filter, increase the filter count */
log_info("BNEP_FILTER_NET_TYPE_SET: Add filter: start: %d, end: %d",
channel->net_filter[channel->net_filter_count].range_start,
channel->net_filter[channel->net_filter_count].range_end);
channel->net_filter_count ++;
}
}
}
// TODO: Should we emit an event?
/* Send out the response code */
bnep_send_filter_net_type_resp(channel, response_code);
return 2 + list_length;
}
static int bnep_handle_filter_net_type_response(bnep_channel_t *channel, uint8_t *packet, uint16_t size)
{
uint16_t response_code;
// TODO: Currently we do not support setting a network filter.
/* Sanity check packet size */
if (size < 1 + 1 + 2) {
return 0;
}
if (channel->state != BNEP_CHANNEL_STATE_CONNECTED) {
/* Ignore a filter net type response in any state but CONNECTED */
log_info("BNEP_FILTER_NET_TYPE_RESPONSE: Ignored in channel state %d", channel->state);
return 1 + 1 + 2;
}
response_code = READ_BT_16(packet, 2);
if (response_code == BNEP_RESP_FILTER_SUCCESS) {
log_info("BNEP_FILTER_NET_TYPE_RESPONSE: Net filter set successfully for %s", ether_ntoa(channel->remote_addr));
} else {
log_info("BNEP_FILTER_NET_TYPE_RESPONSE: Net filter setting for %s failed. Err: %d", ether_ntoa(channel->remote_addr), response_code);
}
return 1 + 1 + 2;
}
static int bnep_handle_multi_addr_set(bnep_channel_t *channel, uint8_t *packet, uint16_t size)
{
uint16_t list_length;
uint16_t response_code = BNEP_RESP_FILTER_SUCCESS;
/* Sanity check packet size */
if (size < 2) {
return 0;
}
list_length = READ_BT_16(packet, 2);
/* Sanity check packet size again with known package size */
if (size < 2 + list_length) {
return 0;
}
if (channel->state != BNEP_CHANNEL_STATE_CONNECTED) {
/* Ignore multicast filter address set in any state but CONNECTED */
log_info("BNEP_MULTI_ADDR_SET: Ignored in channel state %d", channel->state);
return 2 + list_length;
}
/* Check if we have enough space for more filters */
if ((list_length / (2 * ETHER_ADDR_LEN)) + channel->multicast_filter_count > MAX_BNEP_MULTICAST_FILTER) {
log_info("BNEP_MULTI_ADDR_SET: Too many filter");
response_code = BNEP_RESP_FILTER_ERR_TOO_MANY_FILTERS;
} else {
int i;
/* There is still enough space, copy the filters to our filter list */
for (i = 0; i < list_length / (2 * ETHER_ADDR_LEN); i ++) {
memcpy(channel->multicast_filter[channel->multicast_filter_count].addr_start, packet + 4 + i * ETHER_ADDR_LEN * 2);
memcpy(channel->multicast_filter[channel->multicast_filter_count].addr_end, packet + 4 + i * ETHER_ADDR_LEN * 2 + ETHER_ADDR_LEN);
if (memcmp(channel->multicast_filter[channel->multicast_filter_count].addr_start,
channel->multicast_filter[channel->multicast_filter_count].addr_end) > 0) {
/* Invalid filter range, ignore this filter rule */
log_info("BNEP_MULTI_ADDR_SET: Invalid filter: start: %s",
ether_ntoa(channel->multicast_filter[channel->multicast_filter_count].addr_start));
log_info("BNEP_MULTI_ADDR_SET: Invalid filter: end: %s",
ether_ntoa(channel->multicast_filter[channel->multicast_filter_count].addr_end));
response_code = BNEP_RESP_FILTER_ERR_INVALID_RANGE;
} else {
/* Valid filter, increase the filter count */
log_info("BNEP_MULTI_ADDR_SET: Add filter: start: %s",
ether_ntoa(channel->multicast_filter[channel->multicast_filter_count].addr_start));
log_info("BNEP_MULTI_ADDR_SET: Add filter: end: %s",
ether_ntoa(channel->multicast_filter[channel->multicast_filter_count].addr_end));
channel->multicast_filter_count ++;
}
}
}
}
static int bnep_handle_multi_addr_response(bnep_channel_t *channel, uint8_t *packet, uint16_t size)
{
uint16_t response_code;
// TODO: Currently we do not support setting multicast address filter.
/* Sanity check packet size */
if (size < 1 + 1 + 2) {
return 0;
}
if (channel->state != BNEP_CHANNEL_STATE_CONNECTED) {
/* Ignore multicast filter set response in any state but CONNECTED */
log_info("BNEP_MULTI_ADDR_RESPONSE: Ignored in channel state %d", channel->state);
return 1 + 1 + 2;
}
response_code = READ_BT_16(packet, 2);
if (response_code == BNEP_RESP_FILTER_SUCCESS) {
log_info("BNEP_MULTI_ADDR_RESPONSE: Multicast address filter set successfully for %s", ether_ntoa(channel->remote_addr));
} else {
log_info("BNEP_MULTI_ADDR_RESPONSE: Multicast address filter setting for %s failed. Err: %d", ether_ntoa(channel->remote_addr), response_code);
}
return 1 + 1 + 2;
}
static int bnep_handle_control_packet(bnep_channel_t *channel, uint8_t *packet, uint16_t size, int is_extension)
{
int rc = 0;
uint16_t len;
uint8_t bnep_control_type;
bnep_control_type = packet[1];
switch (bnep_control_type) {
case BNEP_CONTROL_TYPE_COMMAND_NOT_UNDERSTOOD:
/* The last command we send was not understood. We should close the connection */
log_info("BNEP_CONTROL: Received COMMAND_NOT_UNDERSTOOD: l2cap_cid: %d, cmd: %d", channel->l2cap_cid, packet[3]);
bnep_channel_finalize(channel);
len = 3; // Length of command not understood packet
rc = 1;
break;
case BNEP_CONTROL_TYPE_SETUP_CONNECTION_REQUEST:
if (is_extension) {
/* Connection requests are not allowed to be send in an extension header */
log_info("BNEP_CONTROL: Received SETUP_CONNECTION_REQUEST in extension header: l2cap_cid: %d", channel->l2cap_cid);
return 0;
}
len = bnep_handle_connection_request(channel, packet, size);
if (len == 0) {
/* If the connection request could not be handled, send a COMMAND_NOT_UNDERSTOOD message */
bnep_send_command_not_understood(channel, bnep_control_type);
}
rc = 1;
break;
case BNEP_CONTROL_TYPE_SETUP_CONNECTION_RESPONSE:
if (is_extension) {
/* Connection requests are not allowed to be send in an extension header */
log_info("BNEP_CONTROL: Received SETUP_CONNECTION_RESPONSE in extension header: l2cap_cid: %d", channel->l2cap_cid);
return 0;
}
rc = bnep_handle_connection_response(channel, packet, size);
if (len == 0) {
/* If the connection request could not be handled, send a COMMAND_NOT_UNDERSTOOD message */
bnep_send_command_not_understood(channel, bnep_control_type);
}
rc = 1;
break;
case BNEP_CONTROL_TYPE_FILTER_NET_TYPE_SET:
len = bnep_handle_filter_net_type_set(channel, packet, size);
return 0;
break;
case BNEP_CONTROL_TYPE_FILTER_NET_TYPE_RESPONSE:
len = bnep_handle_filter_net_type_response(channel, packet, size);
break;
case BNEP_CONTROL_TYPE_FILTER_MULTI_ADDR_SET:
len = bnep_handle_multi_addr_set(channel, packet, size);
break;
case BNEP_CONTROL_TYPE_FILTER_MULTI_ADDR_RESPONSE:
len = bnep_handle_multi_addr_response(channel, packet, size);
break;
default:
log_info("BNEP_CONTROL: Invalid bnep control type: l2cap_cid: %d, cmd: %d", channel->l2cap_cid, bnep_control_type);
len = 0;
rc = 1;
break;
}
if (len == 0) {
/* If the connection request could not be handled, send a COMMAND_NOT_UNDERSTOOD message */
bnep_send_command_not_understood(channel, bnep_control_type);
}
return rc;
}
static size_t
bnep_recv_control(channel_t *chan, uint8_t *ptr, size_t size, bool isext)
{
uint8_t bnep_control_type;
size_t len;
if (size-- < 1)
return 0;
type = *ptr++;
switch (type) {
case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD:
len = bnep_recv_control_command_not_understood(chan, ptr, size);
break;
case BNEP_SETUP_CONNECTION_REQUEST:
if (isext)
return 0; /* not allowed in extension headers */
len = bnep_recv_setup_connection_req(chan, ptr, size);
break;
case BNEP_SETUP_CONNECTION_RESPONSE:
if (isext)
return 0; /* not allowed in extension headers */
len = bnep_recv_setup_connection_rsp(chan, ptr, size);
break;
case BNEP_FILTER_NET_TYPE_SET:
len = bnep_recv_filter_net_type_set(chan, ptr, size);
break;
case BNEP_FILTER_NET_TYPE_RESPONSE:
len = bnep_recv_filter_net_type_rsp(chan, ptr, size);
break;
case BNEP_FILTER_MULTI_ADDR_SET:
len = bnep_recv_filter_multi_addr_set(chan, ptr, size);
break;
case BNEP_FILTER_MULTI_ADDR_RESPONSE:
len = bnep_recv_filter_multi_addr_rsp(chan, ptr, size);
break;
default:
len = 0;
break;
}
if (len == 0)
bnep_send_control(chan, BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD, type);
return len;
}
/**
* @return handled packet
*/
static int bnep_hci_event_handler(uint8_t *packet, uint16_t size)
{
bd_addr_t event_addr;
uint16_t psm;
uint16_t l2cap_cid;
hci_con_handle_t con_handle;
bnep_channel_t *channel = NULL;
uint8_t status;
switch (packet[0]) {
/* Accept an incomming L2CAP connection on PSM_BNEP */
case L2CAP_EVENT_INCOMING_CONNECTION:
/* L2CAP event data: event(8), len(8), address(48), handle (16), psm (16), source cid(16) dest cid(16) */
bt_flip_addr(event_addr, &packet[2]);
con_handle = READ_BT_16(packet, 8);
psm = READ_BT_16(packet, 10);
l2cap_cid = READ_BT_16(packet, 12);
if (psm != PSM_BNEP) break;
channel = bnep_channel_for_addr(&event_addr);
if (channel) {
log_info("INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_BNEP => decline - channel already exists", l2cap_cid);
l2cap_decline_connection_internal(l2cap_cid, 0x04); // no resources available
return 1;
}
/* Create a new BNEP channel instance (incomming) */
channel = bnep_channel_create_for_addr(&event_addr);
if (!channel){
log_info("INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_BNEP => decline - no memory left", l2cap_cid);
l2cap_decline_connection_internal(l2cap_cid, 0x04); // no resources available
return 1;
}
/* Assign connection handle and l2cap cid */
channel->con_handle = con_handle;
channel->l2cap_cid = l2cap_cid;
/* Set channel into accept state */
channel->state = BNEP_CHANNEL_STATE_WAIT_FOR_CONNECTION_REQUEST;
log_info("L2CAP_EVENT_INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_BNEP => accept", l2cap_cid);
l2cap_accept_connection_internal(l2cap_cid);
return 1;
break;
/* Outgoing L2CAP connection has been opened -> store l2cap_cid, remote_addr */
case L2CAP_EVENT_CHANNEL_OPENED:
/* Check if the l2cap channel has been opened for PSM_BNEP */
if (READ_BT_16(packet, 11) != PSM_BNEP) {
break;
}
status = packet[2];
log_info("L2CAP_EVENT_CHANNEL_OPENED for PSM_BNEP, status %u", status);
/* Get the bnep channel fpr remote address */
con_handle = READ_BT_16(packet, 9);
l2cap_cid = READ_BT_16(packet, 13);
bt_flip_addr(event_addr, &packet[3]);
channel = bnep_channel_for_addr(&event_addr);
if (!channel) {
log_error("L2CAP_EVENT_CHANNEL_OPENED but no BNEP channel prepared");
return 1;
}
/* On L2CAP open error discard everything */
if (status) {
/* Emit bnep_channel_opened with status and free channel */
bnep_emit_channel_opened(channel, status);
/* Free BNEP channel mempory */
bnep_channel_free(multiplexer);
return 1;
}
if (channel->state == BNEP_CHANNEL_STATE_CONNECT) {
log_info("L2CAP_EVENT_CHANNEL_OPENED: outgoing connection");
/* Check for the correct remote address */
if (BD_ADDR_CMP(event_addr, channel->remote_addr)) {
break;
}
/* Assign connection handle and l2cap cid */
channel->l2cap_cid = l2cap_cid;
channel->con_handle = con_handle;
// TODO: Should we now send the connect request ?
channel->state = BNEP_CHANNEL_STATE_CONNECTED; // TODO: Is this the right state?
channel->max_frame_size = bnep_max_frame_size_for_l2cap_mtu(READ_BT_16(packet, 17));
} else {
log_info("L2CAP_EVENT_CHANNEL_OPENED: Instalid state: %d", channel->state);
}
return 1;
break;
case DAEMON_EVENT_HCI_PACKET_SENT:
// TODO: What is this event for?
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
// data: event (8), len(8), channel (16)
l2cap_cid = READ_BT_16(packet, 2);
channel = bnep_channel_for_l2cap_cid(l2cap_cid);
log_info("L2CAP_EVENT_CHANNEL_CLOSED cid 0x%0x, channel %p", l2cap_cid, channel);
if (!channel) {
break;
}
log_info("L2CAP_EVENT_CHANNEL_CLOSED state %u", channel->state);
switch (multiplexer->state) {
case BNEP_CHANNEL_STATE_INIT:
case BNEP_CHANNEL_STATE_ACCEPT:
case BNEP_CHANNEL_STATE_CONNECT:
case BNEP_CHANNEL_STATE_CONNECTED:
bnep_channel_finalize(channel);
return 1;
default:
break;
}
break;
default:
break;
}
return 0;
}
static int bnep_l2cap_packet_handler(uint16_t l2cap_cid, uint8_t *packet, uint16_t size)
{
int rc = 0;
uint8_t bnep_type;
uint8_t bnep_header_has_ext;
uint16_t pos = 0;
bnep_channel_t *channel = NULL;
/* Get the bnep channel for this package */
channel = bnep_channe_for_l2cap_cid(l2cap_cid);
if (!channel) {
return rc;
}
/* Sort out short packages */
if (size < 2) {
return rc;
}
bnep_type = BNEP_TYPE(packet[0]);
bnep_header_has_ext = BNEP_HEADER_HAS_EXT(packet[0]);
switch(bnep_type) {
case BNEP_PKT_TYPE_GENERAL_ETHERNET:
break;
case BNEP_PKT_TYPE_COMPRESSED_ETHERNET:
break;
case BNEP_PKT_TYPE_COMPRESSED_ETHERNET_SOURCE_ONLY:
break;
case BNEP_PKT_TYPE_COMPRESSED_ETHERNET_DEST_ONLY:
break;
case BNEP_PKT_TYPE_CONTROL:
rc = bnep_handle_control_packet(channel, packet, size);
break;
default:
break;
}
return rc;
}
void bnep_packet_handler(uint8_t packet_type, uint16_t l2cap_cid, uint8_t *packet, uint16_t size)
{
bnep_channel_t* channel = NULL;
// multiplexer handler
int handled = 0;
switch (packet_type) {
case HCI_EVENT_PACKET:
handled = bnep_hci_event_handler(packet, size);
break;
case L2CAP_DATA_PACKET:
handled = bnep_l2cap_packet_handler(packet, size);
break;
default:
break;
}
if (handled) {
return;
}
/* Forward non l2cap packages to application handler */
if (packet_type != L2CAP_DATA_PACKET) {
(*app_packet_handler)(NULL, packet_type, packet, size);
return;
}
/* Forward l2cap packages to application handler, if no channel has been
registered for this l2cap cid, or if the channel is not opened. */
channel = bnep_channel_for_l2cap_cid(l2cap_cid);
if (!channel || channel->state != BNEP_CHANNEL_OPEN) {
(*app_packet_handler)(NULL, packet_type, l2cap_cid, packet, size);
return;
}
bnep_channel_packet_handler(channel, packet, size);
}
static void bnep_state_machine(bnep_event_t *event){
log_info("bnep_state_machine: state %u, event %u", service->state, event->type);
switch (service->state) {
case BNEP_STATE_CLOSED:
switch (event->type){
case BNEP_EVT_RCVD_CON_REQ:
log_info("-> Inform app");
service->state = BNEP_STATE_CONNECTED;
bnep_emit_connection_request();
break;
default:
break;
}
break;
case BNEP_STATE_CONNECT:
switch (event->type){
case BNEP_EVT_RCVD_CON_RESP:
break;
default:
break;
}
break;
case BNEP_STATE_CONNECTED:
switch (event->type) {
case BNEP_EVT_RCVD_FILTER_SET_TYPE_REQ:
break;
case BNEP_EVT_RCVD_FILTER_SET_TYPE_RESP:
break;
default:
break;
}
break;
default:
break;
}
/* BNEP BTStack API */
void bnep_init(void)
{
bnep_security_level = LEVEL_0;
}
void bnep_set_required_security_level(gap_security_level_t security_level)
{
bnep_security_level = security_level;
}
void bnep_connect2(void * connection, bd_addr_t *addr)
{
log_info("BNEP_CONNECT addr %s", bd_addr_to_str(*addr));
l2cap_create_channel_internal(connection, bnep_packet_handler, *addr, PSM_BNEP, bnep_max_mtu());
// TODO: Completely reworked...
channel->state = BNEP_CHANNEL_STATE_CONNECT;
}
void bnep_connect_internal(void * connection, bd_addr_t *addr)
{
bnep_connect2(connection, addr);
}
void bnep_disconnect_internal(void)
{
log_info("BNEP_DISCONNECT");
if (service) {
service->state = BNEP_SEND_DISC;
}
}
void bnep_register_service(void * connection, uint16_t service_uuid, uint16_t max_frame_size)
{
log_info("BNEP_REGISTER_SERVICE mtu %d", max_frame_size);
/* Check if we already registered a service */
bnep_service_t * bnep_service = NULL;
if (service) {
bnep_emit_service_registered(connection, BNEP_SERVICE_ALREADY_REGISTERED);
return;
}
/* Only alow one the three service types: PANU, NAP, GN */
if ((service_uuid != BNEP_UUID_PANU) &&
(service_uuid != BNEP_UUID_NAP) &&
(service_uuid != BNEP_UUID_GN)) {
log_info("BNEP_REGISTER_SERVICE: Invalid service UUID: %04x", service_uuid);
return;
}
/* Allocate service memory */
bnep_service = (bnep_service_t*) btstack_memory_bnep_service_get();
if (!bnep_service) {
bnep_emit_service_registered(connection, BTSTACK_MEMORY_ALLOC_FAILED, channel);
return;
}
/* register with l2cap if not registered before, max MTU */
if (linked_list_empty(&bnep_services)){
l2cap_register_service_internal(NULL, bnep_packet_handler, PSM_BNEP, 0xffff, bnep_security_level);
}
/* Setup the service struct */
bnep_service->connection = connection;
bnep_service->max_frame_size = max_frame_size;
bnep_service->sevice_uuid = service_uuid;
/* And now assigne the global variable */
service = bnep_service;
/* Inform the application layer */
bnep_emit_service_registered(connection, 0);
}
void bnep_unregister_service(void)
{
log_info("BNEP_UNREGISTER_SERVICE #%u", void);
if (!service) {
return;
}
btstack_memory_bnep_service_free(service);
service = NULL;
l2cap_unregister_service_internal(NULL, PSM_BNEP);
}