/* * 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 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 data(buffer.Get()); *data = htole16(mtu); } else if (reason == l2cap_command_reject::REJECTED_INVALID_CID) { NetBufferPrepend data(buffer.Get()); data->invalid_cid.scid = htole16(scid); data->invalid_cid.dcid = htole16(dcid); } NetBufferPrepend 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 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 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 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 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 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 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 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 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 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 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 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(); }