root/src/add-ons/kernel/network/protocols/l2cap/l2cap.cpp
/*
 * 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_address.h"
#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;

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<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 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<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(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
};