root/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.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 "l2cap_command.h"

#include <NetBufferUtilities.h>


net_buffer*
make_l2cap_command_reject(uint8& code, uint16 reason, uint16 mtu, uint16 scid, uint16 dcid)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        if (reason == l2cap_command_reject::REJECTED_MTU_EXCEEDED) {
                NetBufferPrepend<uint16> data(buffer.Get());
                *data = htole16(mtu);
        } else if (reason == l2cap_command_reject::REJECTED_INVALID_CID) {
                NetBufferPrepend<l2cap_command_reject_data> data(buffer.Get());
                data->invalid_cid.scid = htole16(scid);
                data->invalid_cid.dcid = htole16(dcid);
        }

        NetBufferPrepend<l2cap_command_reject> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_COMMAND_REJECT_RSP;
        command->reason = (uint16)htole16(reason);

        return buffer.Detach();
}


net_buffer*
make_l2cap_connection_req(uint8& code, uint16 psm, uint16 scid)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        NetBufferPrepend<l2cap_connection_req> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_CONNECTION_REQ;
        command->psm = htole16(psm);
        command->scid = htole16(scid);

        return buffer.Detach();
}


net_buffer*
make_l2cap_connection_rsp(uint8& code, uint16 dcid, uint16 scid, uint16 result, uint16 status)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        NetBufferPrepend<l2cap_connection_rsp> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_CONNECTION_RSP;
        command->dcid = htole16(dcid);
        command->scid = htole16(scid);
        command->result = htole16(result);
        command->status = htole16(status);

        return buffer.Detach();
}


net_buffer*
make_l2cap_configuration_req(uint8& code, uint16 dcid, uint16 flags,
        uint16* mtu, uint16* flush_timeout, l2cap_qos* flow)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        if (mtu != NULL) {
                struct config_option_mtu {
                        l2cap_configuration_option header;
                        uint16 value;
                } _PACKED;
                NetBufferPrepend<config_option_mtu> option(buffer.Get());
                if (option.Status() != B_OK)
                        return NULL;

                option->header.type = l2cap_configuration_option::OPTION_MTU;
                option->header.length = sizeof(option->value);
                option->value = htole16(*mtu);
        }

        if (flush_timeout != NULL) {
                struct config_option_flush_timeout {
                        l2cap_configuration_option header;
                        uint16 value;
                } _PACKED;
                NetBufferPrepend<config_option_flush_timeout> option(buffer.Get());
                if (option.Status() != B_OK)
                        return NULL;

                option->header.type = l2cap_configuration_option::OPTION_FLUSH_TIMEOUT;
                option->header.length = sizeof(option->value);
                option->value = htole16(*flush_timeout);
        }

        if (flow != NULL) {
                struct config_option_flow {
                        l2cap_configuration_option header;
                        l2cap_qos value;
                } _PACKED;
                NetBufferPrepend<config_option_flow> option(buffer.Get());
                if (option.Status() != B_OK)
                        return NULL;

                option->header.type = l2cap_configuration_option::OPTION_QOS;
                option->header.length = sizeof(option->value);
                option->value.flags = flow->flags;
                option->value.service_type = flow->service_type;
                option->value.token_rate = htole32(flow->token_rate);
                option->value.token_bucket_size = htole32(flow->token_bucket_size);
                option->value.peak_bandwidth = htole32(flow->peak_bandwidth);
                option->value.access_latency = htole32(flow->access_latency);
                option->value.delay_variation = htole32(flow->delay_variation);
        }

        NetBufferPrepend<l2cap_configuration_req> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_CONFIGURATION_REQ;
        command->dcid = htole16(dcid);
        command->flags = htole16(flags);

        return buffer.Detach();
}


net_buffer*
make_l2cap_configuration_rsp(uint8& code, uint16 scid, uint16 flags,
        uint16 result, net_buffer* opt)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        NetBufferPrepend<l2cap_configuration_rsp> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_CONFIGURATION_RSP;
        command->scid = htole16(scid);
        command->flags = htole16(flags);
        command->result = htole16(result);

        if (opt != NULL) {
                if (gBufferModule->append_cloned(buffer.Get(), opt, 0, opt->size) != B_OK)
                        return NULL;
        }

        return buffer.Detach();
}


net_buffer*
make_l2cap_disconnection_req(uint8& code, uint16 dcid, uint16 scid)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        NetBufferPrepend<l2cap_disconnection_req> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_DISCONNECTION_REQ;
        command->dcid = htole16(dcid);
        command->scid = htole16(scid);

        return buffer.Detach();
}


net_buffer*
make_l2cap_disconnection_rsp(uint8& code, uint16 dcid, uint16 scid)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        NetBufferPrepend<l2cap_disconnection_rsp> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_DISCONNECTION_RSP;
        command->dcid = htole16(dcid);
        command->scid = htole16(scid);

        return buffer.Detach();
}


net_buffer*
make_l2cap_information_req(uint8& code, uint16 type)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        NetBufferPrepend<l2cap_information_req> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_INFORMATION_REQ;

        command->type = htole16(type);

        return buffer.Detach();
}


net_buffer*
make_l2cap_information_rsp(uint8& code, uint16 type, uint16 result, uint16 _mtu)
{
        NetBufferDeleter<> buffer(gBufferModule->create(128));
        if (!buffer.IsSet())
                return NULL;

        NetBufferPrepend<l2cap_information_rsp> command(buffer.Get());
        if (command.Status() != B_OK)
                return NULL;

        code = L2CAP_INFORMATION_RSP;

        command->type = htole16(type);
        command->result = htole16(result);

        if (result == l2cap_information_rsp::RESULT_SUCCESS) {
                switch (type) {
                case l2cap_information_req::TYPE_CONNECTIONLESS_MTU: {
                        uint16 mtu = htole16(_mtu);
                        gBufferModule->append(buffer.Get(), &mtu, sizeof(mtu));
                        break;
                }
                }
        }

        return buffer.Detach();
}