⛏️ index : haiku.git

/*
 * 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();
}