xref: /haiku/src/add-ons/kernel/network/protocols/l2cap/l2cap_command.cpp (revision 47c05920fde47c2618efccd24bd82f1e79cdf05a)
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 
11 net_buffer*
12 make_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 
38 net_buffer*
39 make_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 
57 net_buffer*
58 make_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 
78 net_buffer*
79 make_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 
146 net_buffer*
147 make_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 
172 net_buffer*
173 make_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 
191 net_buffer*
192 make_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 
210 net_buffer*
211 make_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 
229 net_buffer*
230 make_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