/* * Copyright 2007, Oliver Ruiz Dorantes. All rights reserved. * Copyright 2024, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #include #include #include #include #include #include "l2cap_address.h" #include "l2cap_command.h" #include "l2cap_internal.h" #include "l2cap_signal.h" #include "L2capEndpoint.h" #include "L2capEndpointManager.h" #include #include #include 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; static struct net_domain* sDomain; 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(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 sDomain; } 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 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 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_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_STREAM, BLUETOOTH_PROTO_L2CAP, NET_BLUETOOTH_L2CAP_NAME, NULL); if (error != B_OK) return error; error = gStackModule->register_domain(AF_BLUETOOTH, "l2cap", &gL2CAPModule, &gL2capAddressModule, &sDomain); if (error != B_OK) return error; return B_OK; } case B_MODULE_UNINIT: gL2capEndpointManager.~L2capEndpointManager(); gStackModule->unregister_domain(sDomain); 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}, {} }; module_info* modules[] = { (module_info*)&gL2CAPModule, NULL };