1/*
2 * Copyright 2007 Oliver Ruiz Dorantes. All rights reserved.
3 * Copyright 2024, Haiku, Inc. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6#include "l2cap_command.h"
7
8#include <NetBufferUtilities.h>
9
10
11net_buffer*
12make_l2cap_command_reject(uint8& code, uint16 reason, uint16 mtu, uint16 scid, uint16 dcid)
13{
14	NetBufferDeleter<> buffer(gBufferModule->create(128));
15	if (!buffer.IsSet())
16		return NULL;
17
18	if (reason == l2cap_command_reject::REJECTED_MTU_EXCEEDED) {
19		NetBufferPrepend<uint16> data(buffer.Get());
20		*data = htole16(mtu);
21	} else if (reason == l2cap_command_reject::REJECTED_INVALID_CID) {
22		NetBufferPrepend<l2cap_command_reject_data> data(buffer.Get());
23		data->invalid_cid.scid = htole16(scid);
24		data->invalid_cid.dcid = htole16(dcid);
25	}
26
27	NetBufferPrepend<l2cap_command_reject> command(buffer.Get());
28	if (command.Status() != B_OK)
29		return NULL;
30
31	code = L2CAP_COMMAND_REJECT_RSP;
32	command->reason = (uint16)htole16(reason);
33
34	return buffer.Detach();
35}
36
37
38net_buffer*
39make_l2cap_connection_req(uint8& code, uint16 psm, uint16 scid)
40{
41	NetBufferDeleter<> buffer(gBufferModule->create(128));
42	if (!buffer.IsSet())
43		return NULL;
44
45	NetBufferPrepend<l2cap_connection_req> command(buffer.Get());
46	if (command.Status() != B_OK)
47		return NULL;
48
49	code = L2CAP_CONNECTION_REQ;
50	command->psm = htole16(psm);
51	command->scid = htole16(scid);
52
53	return buffer.Detach();
54}
55
56
57net_buffer*
58make_l2cap_connection_rsp(uint8& code, uint16 dcid, uint16 scid, uint16 result, uint16 status)
59{
60	NetBufferDeleter<> buffer(gBufferModule->create(128));
61	if (!buffer.IsSet())
62		return NULL;
63
64	NetBufferPrepend<l2cap_connection_rsp> command(buffer.Get());
65	if (command.Status() != B_OK)
66		return NULL;
67
68	code = L2CAP_CONNECTION_RSP;
69	command->dcid = htole16(dcid);
70	command->scid = htole16(scid);
71	command->result = htole16(result);
72	command->status = htole16(status);
73
74	return buffer.Detach();
75}
76
77
78net_buffer*
79make_l2cap_configuration_req(uint8& code, uint16 dcid, uint16 flags,
80	uint16* mtu, uint16* flush_timeout, l2cap_qos* flow)
81{
82	NetBufferDeleter<> buffer(gBufferModule->create(128));
83	if (!buffer.IsSet())
84		return NULL;
85
86	if (mtu != NULL) {
87		struct config_option_mtu {
88			l2cap_configuration_option header;
89			uint16 value;
90		} _PACKED;
91		NetBufferPrepend<config_option_mtu> option(buffer.Get());
92		if (option.Status() != B_OK)
93			return NULL;
94
95		option->header.type = l2cap_configuration_option::OPTION_MTU;
96		option->header.length = sizeof(option->value);
97		option->value = htole16(*mtu);
98	}
99
100	if (flush_timeout != NULL) {
101		struct config_option_flush_timeout {
102			l2cap_configuration_option header;
103			uint16 value;
104		} _PACKED;
105		NetBufferPrepend<config_option_flush_timeout> option(buffer.Get());
106		if (option.Status() != B_OK)
107			return NULL;
108
109		option->header.type = l2cap_configuration_option::OPTION_FLUSH_TIMEOUT;
110		option->header.length = sizeof(option->value);
111		option->value = htole16(*flush_timeout);
112	}
113
114	if (flow != NULL) {
115		struct config_option_flow {
116			l2cap_configuration_option header;
117			l2cap_qos value;
118		} _PACKED;
119		NetBufferPrepend<config_option_flow> option(buffer.Get());
120		if (option.Status() != B_OK)
121			return NULL;
122
123		option->header.type = l2cap_configuration_option::OPTION_QOS;
124		option->header.length = sizeof(option->value);
125		option->value.flags = flow->flags;
126		option->value.service_type = flow->service_type;
127		option->value.token_rate = htole32(flow->token_rate);
128		option->value.token_bucket_size = htole32(flow->token_bucket_size);
129		option->value.peak_bandwidth = htole32(flow->peak_bandwidth);
130		option->value.access_latency = htole32(flow->access_latency);
131		option->value.delay_variation = htole32(flow->delay_variation);
132	}
133
134	NetBufferPrepend<l2cap_configuration_req> command(buffer.Get());
135	if (command.Status() != B_OK)
136		return NULL;
137
138	code = L2CAP_CONFIGURATION_REQ;
139	command->dcid = htole16(dcid);
140	command->flags = htole16(flags);
141
142	return buffer.Detach();
143}
144
145
146net_buffer*
147make_l2cap_configuration_rsp(uint8& code, uint16 scid, uint16 flags,
148	uint16 result, net_buffer* opt)
149{
150	NetBufferDeleter<> buffer(gBufferModule->create(128));
151	if (!buffer.IsSet())
152		return NULL;
153
154	NetBufferPrepend<l2cap_configuration_rsp> command(buffer.Get());
155	if (command.Status() != B_OK)
156		return NULL;
157
158	code = L2CAP_CONFIGURATION_RSP;
159	command->scid = htole16(scid);
160	command->flags = htole16(flags);
161	command->result = htole16(result);
162
163	if (opt != NULL) {
164		if (gBufferModule->append_cloned(buffer.Get(), opt, 0, opt->size) != B_OK)
165			return NULL;
166	}
167
168	return buffer.Detach();
169}
170
171
172net_buffer*
173make_l2cap_disconnection_req(uint8& code, uint16 dcid, uint16 scid)
174{
175	NetBufferDeleter<> buffer(gBufferModule->create(128));
176	if (!buffer.IsSet())
177		return NULL;
178
179	NetBufferPrepend<l2cap_disconnection_req> command(buffer.Get());
180	if (command.Status() != B_OK)
181		return NULL;
182
183	code = L2CAP_DISCONNECTION_REQ;
184	command->dcid = htole16(dcid);
185	command->scid = htole16(scid);
186
187	return buffer.Detach();
188}
189
190
191net_buffer*
192make_l2cap_disconnection_rsp(uint8& code, uint16 dcid, uint16 scid)
193{
194	NetBufferDeleter<> buffer(gBufferModule->create(128));
195	if (!buffer.IsSet())
196		return NULL;
197
198	NetBufferPrepend<l2cap_disconnection_rsp> command(buffer.Get());
199	if (command.Status() != B_OK)
200		return NULL;
201
202	code = L2CAP_DISCONNECTION_RSP;
203	command->dcid = htole16(dcid);
204	command->scid = htole16(scid);
205
206	return buffer.Detach();
207}
208
209
210net_buffer*
211make_l2cap_information_req(uint8& code, uint16 type)
212{
213	NetBufferDeleter<> buffer(gBufferModule->create(128));
214	if (!buffer.IsSet())
215		return NULL;
216
217	NetBufferPrepend<l2cap_information_req> command(buffer.Get());
218	if (command.Status() != B_OK)
219		return NULL;
220
221	code = L2CAP_INFORMATION_REQ;
222
223	command->type = htole16(type);
224
225	return buffer.Detach();
226}
227
228
229net_buffer*
230make_l2cap_information_rsp(uint8& code, uint16 type, uint16 result, uint16 _mtu)
231{
232	NetBufferDeleter<> buffer(gBufferModule->create(128));
233	if (!buffer.IsSet())
234		return NULL;
235
236	NetBufferPrepend<l2cap_information_rsp> command(buffer.Get());
237	if (command.Status() != B_OK)
238		return NULL;
239
240	code = L2CAP_INFORMATION_RSP;
241
242	command->type = htole16(type);
243	command->result = htole16(result);
244
245	if (result == l2cap_information_rsp::RESULT_SUCCESS) {
246		switch (type) {
247		case l2cap_information_req::TYPE_CONNECTIONLESS_MTU: {
248			uint16 mtu = htole16(_mtu);
249			gBufferModule->append(buffer.Get(), &mtu, sizeof(mtu));
250			break;
251		}
252		}
253	}
254
255	return buffer.Detach();
256}
257