This commits splits the address handling into bluetooth protocol and does the rest in l2cap. This is to allow for a different struct for sco and l2cap. Change-Id: I4c577691118613cd3be75eb7ef191fd2f07d742d Reviewed-on: https://review.haiku-os.org/c/haiku/+/11181 Reviewed-by: waddlesplash <[email protected]>
420 lines
8.4 KiB
C++
420 lines
8.4 KiB
C++
/*
|
|
* Copyright 2007, Oliver Ruiz Dorantes. All rights reserved.
|
|
* Copyright 2024, Haiku, Inc. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
#include <net_datalink.h>
|
|
#include <net_protocol.h>
|
|
#include <net_stack.h>
|
|
#include <NetBufferUtilities.h>
|
|
|
|
#include <new>
|
|
|
|
#include "l2cap_command.h"
|
|
#include "l2cap_internal.h"
|
|
#include "l2cap_signal.h"
|
|
#include "L2capEndpoint.h"
|
|
#include "L2capEndpointManager.h"
|
|
|
|
#include <bluetooth/HCI/btHCI_acl.h>
|
|
#include <btModules.h>
|
|
#include <btDebug.h>
|
|
|
|
|
|
extern net_protocol_module_info gL2CAPModule;
|
|
|
|
// module references
|
|
bluetooth_core_data_module_info* btCoreData;
|
|
bt_hci_module_info* btDevices;
|
|
|
|
net_buffer_module_info* gBufferModule;
|
|
net_stack_module_info* gStackModule;
|
|
net_socket_module_info* gSocketModule;
|
|
module_info* gBluetoothModuleInfo;
|
|
|
|
|
|
net_protocol*
|
|
l2cap_init_protocol(net_socket* socket)
|
|
{
|
|
CALLED();
|
|
|
|
L2capEndpoint* protocol = new(std::nothrow) L2capEndpoint(socket);
|
|
if (protocol == NULL)
|
|
return NULL;
|
|
|
|
return protocol;
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_uninit_protocol(net_protocol* protocol)
|
|
{
|
|
CALLED();
|
|
|
|
L2capEndpoint* endpoint = static_cast<L2capEndpoint*>(protocol);
|
|
delete endpoint;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_open(net_protocol* protocol)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Open();
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_close(net_protocol* protocol)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Close();
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_free(net_protocol* protocol)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Free();
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_connect(net_protocol* protocol, const struct sockaddr* address)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Connect(address);
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_accept(net_protocol* protocol, struct net_socket** _acceptedSocket)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Accept(_acceptedSocket);
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_control(net_protocol* protocol, int level, int option, void* value,
|
|
size_t* _length)
|
|
{
|
|
CALLED();
|
|
return EOPNOTSUPP;
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_getsockopt(net_protocol* protocol, int level, int option,
|
|
void* value, int* _length)
|
|
{
|
|
CALLED();
|
|
return gSocketModule->get_option(protocol->socket, level, option, value,
|
|
_length);
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_setsockopt(net_protocol* protocol, int level, int option,
|
|
const void* _value, int length)
|
|
{
|
|
CALLED();
|
|
return gSocketModule->set_option(protocol->socket, level, option,
|
|
_value, length);
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_bind(net_protocol* protocol, const struct sockaddr* address)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Bind(address);
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_unbind(net_protocol* protocol, struct sockaddr* address)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Unbind();
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_listen(net_protocol* protocol, int count)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Listen(count);
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_shutdown(net_protocol* protocol, int direction)
|
|
{
|
|
if (direction != SHUT_RDWR)
|
|
return EOPNOTSUPP;
|
|
return ((L2capEndpoint*)protocol)->Shutdown();
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_send_data(net_protocol* protocol, net_buffer* buffer)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->SendData(buffer);
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_send_routed_data(net_protocol* protocol, struct net_route* route,
|
|
net_buffer* buffer)
|
|
{
|
|
CALLED();
|
|
return EOPNOTSUPP;
|
|
}
|
|
|
|
|
|
ssize_t
|
|
l2cap_send_avail(net_protocol* protocol)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Sendable();
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_read_data(net_protocol* protocol, size_t numBytes, uint32 flags,
|
|
net_buffer** _buffer)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->ReadData(numBytes, flags, _buffer);
|
|
}
|
|
|
|
|
|
ssize_t
|
|
l2cap_read_avail(net_protocol* protocol)
|
|
{
|
|
return ((L2capEndpoint*)protocol)->Receivable();
|
|
}
|
|
|
|
|
|
struct net_domain*
|
|
l2cap_get_domain(net_protocol* protocol)
|
|
{
|
|
return gStackModule->get_domain(AF_BLUETOOTH);
|
|
}
|
|
|
|
|
|
size_t
|
|
l2cap_get_mtu(net_protocol* protocol, const struct sockaddr* address)
|
|
{
|
|
CALLED();
|
|
return protocol->next->module->get_mtu(protocol->next, address);
|
|
}
|
|
|
|
|
|
static HciConnection*
|
|
connection_for(net_buffer* buffer)
|
|
{
|
|
const sockaddr_l2cap* l2capAddr = (sockaddr_l2cap*)buffer->source;
|
|
const sockaddr_dl* interfaceAddr = (sockaddr_dl*)buffer->interface_address->local;
|
|
struct HciConnection* connection = btCoreData->ConnectionByDestination(
|
|
l2capAddr->l2cap_bdaddr, interfaceAddr->sdl_index);
|
|
buffer->interface_address = NULL;
|
|
// This isn't a real interface_address; it could confuse the buffer module.
|
|
// FIXME: We probably should have an alternate interface for passing along data.
|
|
return connection;
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_receive_data(net_buffer* buffer)
|
|
{
|
|
if (buffer->size < sizeof(l2cap_basic_header)) {
|
|
ERROR("%s: invalid L2CAP packet: too small. len=%" B_PRIu32 "\n",
|
|
__func__, buffer->size);
|
|
gBufferModule->free(buffer);
|
|
return EMSGSIZE;
|
|
}
|
|
|
|
NetBufferHeaderReader<l2cap_basic_header> bufferHeader(buffer);
|
|
if (bufferHeader.Status() != B_OK)
|
|
return ENOBUFS;
|
|
|
|
uint16 length = le16toh(bufferHeader->length);
|
|
uint16 dcid = le16toh(bufferHeader->dcid);
|
|
|
|
TRACE("%s: len=%d cid=%x\n", __func__, length, dcid);
|
|
|
|
bufferHeader.Remove();
|
|
|
|
if (length != buffer->size) {
|
|
ERROR("l2cap: payload length mismatch, packetlen=%d, bufferlen=%" B_PRIu32 "\n",
|
|
length, buffer->size);
|
|
return EMSGSIZE;
|
|
}
|
|
|
|
status_t status = B_ERROR;
|
|
switch (dcid) {
|
|
case L2CAP_SIGNALING_CID:
|
|
{
|
|
// We need to find the connection this packet is associated with.
|
|
struct HciConnection* connection = connection_for(buffer);
|
|
if (connection == NULL) {
|
|
panic("no connection for received L2CAP command");
|
|
return ENOTCONN;
|
|
}
|
|
|
|
status = l2cap_handle_signaling_command(connection, buffer);
|
|
break;
|
|
}
|
|
|
|
case L2CAP_CONNECTIONLESS_CID:
|
|
{
|
|
NetBufferHeaderReader<l2cap_connectionless_header> connlessHeader(buffer);
|
|
const uint16 psm = le16toh(connlessHeader->psm);
|
|
L2capEndpoint* endpoint = gL2capEndpointManager.ForPSM(psm);
|
|
if (endpoint == NULL)
|
|
return ECONNRESET;
|
|
|
|
connlessHeader.Remove();
|
|
buffer->interface_address = NULL;
|
|
|
|
status = endpoint->ReceiveData(buffer);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
L2capEndpoint* endpoint = gL2capEndpointManager.ForChannel(dcid);
|
|
if (endpoint == NULL)
|
|
return ECONNRESET;
|
|
|
|
buffer->interface_address = NULL;
|
|
status = endpoint->ReceiveData(buffer);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_error_received(net_error error, net_error_data* errorData, net_buffer* data)
|
|
{
|
|
CALLED();
|
|
|
|
if (error == B_NET_ERROR_UNREACH_HOST) {
|
|
struct HciConnection* connection = connection_for(data);
|
|
if (connection == NULL)
|
|
return ENOTCONN;
|
|
|
|
// Disconnect all connections with this HciConnection.
|
|
gL2capEndpointManager.Disconnected(connection);
|
|
|
|
gBufferModule->free(data);
|
|
return B_OK;
|
|
}
|
|
|
|
return B_ERROR;
|
|
}
|
|
|
|
|
|
status_t
|
|
l2cap_error_reply(net_protocol* protocol, net_buffer* cause, net_error error,
|
|
net_error_data* errorData)
|
|
{
|
|
CALLED();
|
|
return B_ERROR;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
static status_t
|
|
l2cap_std_ops(int32 op, ...)
|
|
{
|
|
status_t error;
|
|
|
|
CALLED();
|
|
|
|
switch (op) {
|
|
case B_MODULE_INIT:
|
|
{
|
|
new(&gL2capEndpointManager) L2capEndpointManager;
|
|
|
|
error = gStackModule->register_domain_protocols(AF_BLUETOOTH,
|
|
SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP,
|
|
NET_BLUETOOTH_L2CAP_NAME,
|
|
NULL);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
error = gStackModule->register_domain_receiving_protocol(AF_BLUETOOTH,
|
|
BLUETOOTH_PROTO_L2CAP, NET_BLUETOOTH_L2CAP_NAME);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
case B_MODULE_UNINIT:
|
|
gL2capEndpointManager.~L2capEndpointManager();
|
|
return B_OK;
|
|
|
|
default:
|
|
return B_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
net_protocol_module_info gL2CAPModule = {
|
|
{
|
|
NET_BLUETOOTH_L2CAP_NAME,
|
|
0,
|
|
l2cap_std_ops
|
|
},
|
|
NET_PROTOCOL_ATOMIC_MESSAGES,
|
|
|
|
l2cap_init_protocol,
|
|
l2cap_uninit_protocol,
|
|
l2cap_open,
|
|
l2cap_close,
|
|
l2cap_free,
|
|
l2cap_connect,
|
|
l2cap_accept,
|
|
l2cap_control,
|
|
l2cap_getsockopt,
|
|
l2cap_setsockopt,
|
|
l2cap_bind,
|
|
l2cap_unbind,
|
|
l2cap_listen,
|
|
l2cap_shutdown,
|
|
l2cap_send_data,
|
|
l2cap_send_routed_data,
|
|
l2cap_send_avail,
|
|
l2cap_read_data,
|
|
l2cap_read_avail,
|
|
l2cap_get_domain,
|
|
l2cap_get_mtu,
|
|
l2cap_receive_data,
|
|
NULL, // deliver_data()
|
|
l2cap_error_received,
|
|
l2cap_error_reply,
|
|
NULL, // add_ancillary_data()
|
|
NULL, // process_ancillary_data()
|
|
NULL, // process_ancillary_data_no_container()
|
|
NULL, // send_data_no_buffer()
|
|
NULL // read_data_no_buffer()
|
|
};
|
|
|
|
module_dependency module_dependencies[] = {
|
|
{NET_STACK_MODULE_NAME, (module_info**)&gStackModule},
|
|
{NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule},
|
|
{NET_SOCKET_MODULE_NAME, (module_info**)&gSocketModule},
|
|
{BT_CORE_DATA_MODULE_NAME, (module_info**)&btCoreData},
|
|
{BT_HCI_MODULE_NAME, (module_info**)&btDevices},
|
|
{NET_BLUETOOTH_CORE_NAME, &gBluetoothModuleInfo},
|
|
{}
|
|
};
|
|
|
|
module_info* modules[] = {
|
|
(module_info*)&gL2CAPModule,
|
|
NULL
|
|
};
|