1 /******************************************************************************
3 * Copyright (C) 1999-2012 Broadcom Corporation
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 ******************************************************************************/
19 /******************************************************************************
21 * Port Emulation entity utilities
23 ******************************************************************************/
26 #include "common/bt_target.h"
27 #include "stack/rfcdefs.h"
28 #include "stack/port_api.h"
31 #include "stack/l2cdefs.h"
33 #include "stack/btu.h"
34 #include "osi/mutex.h"
35 #include "osi/allocator.h"
36 #if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
38 static const tPORT_STATE default_port_pars = {
52 /*******************************************************************************
54 ** Function port_allocate_port
56 ** Description Look through the Port Control Blocks for a free one. Note
57 ** that one server can open several ports with the same SCN
58 ** if it can support simulteneous requests from different
61 ** Returns Pointer to the PORT or NULL if not found
63 *******************************************************************************/
64 tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr)
66 tPORT *p_port = &rfc_cb.port.port[0];
69 for (xx = 0, yy = rfc_cb.rfc.last_port + 1; xx < MAX_RFC_PORTS; xx++, yy++) {
70 if (yy >= MAX_RFC_PORTS) {
74 p_port = &rfc_cb.port.port[yy];
75 if (!p_port->in_use) {
76 memset (p_port, 0, sizeof (tPORT));
78 p_port->in_use = TRUE;
82 memcpy (p_port->bd_addr, bd_addr, BD_ADDR_LEN);
84 /* During the open set default state for the port connection */
85 port_set_defaults (p_port);
87 rfc_cb.rfc.last_port = yy;
88 RFCOMM_TRACE_DEBUG("rfc_cb.port.port[%d]:%p allocated, last_port:%d", yy, p_port, rfc_cb.rfc.last_port);
89 RFCOMM_TRACE_DEBUG("port_allocate_port:bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
90 bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
95 /* If here, no free PORT found */
100 /*******************************************************************************
102 ** Function port_set_defaults
104 ** Description Set defualt port parameters
107 *******************************************************************************/
108 void port_set_defaults (tPORT *p_port)
111 p_port->p_callback = NULL;
112 p_port->port_ctrl = 0;
114 p_port->line_status = 0;
115 p_port->rx_flag_ev_pending = FALSE;
116 p_port->peer_mtu = RFCOMM_DEFAULT_MTU;
118 p_port->user_port_pars = default_port_pars;
119 p_port->peer_port_pars = default_port_pars;
121 p_port->credit_tx = 0;
122 p_port->credit_rx = 0;
123 /* p_port->credit_rx_max = PORT_CREDIT_RX_MAX; Determined later */
124 /* p_port->credit_rx_low = PORT_CREDIT_RX_LOW; Determined later */
126 memset (&p_port->local_ctrl, 0, sizeof (p_port->local_ctrl));
127 memset (&p_port->peer_ctrl, 0, sizeof (p_port->peer_ctrl));
128 memset (&p_port->rx, 0, sizeof (p_port->rx));
129 memset (&p_port->tx, 0, sizeof (p_port->tx));
131 p_port->tx.queue = fixed_queue_new(SIZE_MAX);
132 p_port->rx.queue = fixed_queue_new(SIZE_MAX);
135 /*******************************************************************************
137 ** Function port_select_mtu
139 ** Description Select MTU which will best serve connection from our
141 ** If our device is 1.2 or lower we calculate how many DH5s
142 ** fit into 1 RFCOMM buffer.
145 *******************************************************************************/
146 void port_select_mtu (tPORT *p_port)
150 /* Will select MTU only if application did not setup something */
151 if (p_port->mtu == 0) {
152 /* find packet size which connection supports */
153 packet_size = btm_get_max_packet_size (p_port->bd_addr);
154 if (packet_size == 0) {
155 /* something is very wrong */
156 RFCOMM_TRACE_WARNING ("port_select_mtu bad packet size");
157 p_port->mtu = RFCOMM_DEFAULT_MTU;
159 /* We try to negotiate MTU that each packet can be split into whole
160 number of max packets. For example if link is 1.2 max packet size is 339 bytes.
161 At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead.
162 1695, that will be 5 Dh5 packets. Now maximum RFCOMM packet is
163 5 * 339 = 1695. Minus 4 bytes L2CAP header 1691. Minus RFCOMM 6 bytes header overhead 1685
165 For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet
166 1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. Minus RFCOMM 6 bytes header overhead 1017 */
167 if ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) >= packet_size) {
168 p_port->mtu = ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) / packet_size * packet_size) - RFCOMM_DATA_OVERHEAD - L2CAP_PKT_OVERHEAD;
169 RFCOMM_TRACE_DEBUG ("port_select_mtu selected %d based on connection speed", p_port->mtu);
171 p_port->mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD;
172 RFCOMM_TRACE_DEBUG ("port_select_mtu selected %d based on l2cap PDU size", p_port->mtu);
176 RFCOMM_TRACE_DEBUG ("port_select_mtu application selected %d", p_port->mtu);
178 p_port->credit_rx_max = (PORT_RX_HIGH_WM / p_port->mtu);
179 if ( p_port->credit_rx_max > PORT_RX_BUF_HIGH_WM ) {
180 p_port->credit_rx_max = PORT_RX_BUF_HIGH_WM;
182 p_port->credit_rx_low = (PORT_RX_LOW_WM / p_port->mtu);
183 if ( p_port->credit_rx_low > PORT_RX_BUF_LOW_WM ) {
184 p_port->credit_rx_low = PORT_RX_BUF_LOW_WM;
186 p_port->rx_buf_critical = (PORT_RX_CRITICAL_WM / p_port->mtu);
187 if ( p_port->rx_buf_critical > PORT_RX_BUF_CRITICAL_WM ) {
188 p_port->rx_buf_critical = PORT_RX_BUF_CRITICAL_WM;
190 RFCOMM_TRACE_DEBUG ("port_select_mtu credit_rx_max %d, credit_rx_low %d, rx_buf_critical %d",
191 p_port->credit_rx_max, p_port->credit_rx_low, p_port->rx_buf_critical);
195 /*******************************************************************************
197 ** Function port_release_port
199 ** Description Release port infor control block.
201 ** Returns Pointer to the PORT or NULL if not found
203 *******************************************************************************/
204 void port_release_port (tPORT *p_port)
208 tPORT_CALLBACK *p_port_cb;
209 tPORT_STATE user_port_pars;
211 osi_mutex_global_lock();
212 RFCOMM_TRACE_DEBUG("port_release_port, p_port:%p", p_port);
213 while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_port->rx.queue)) != NULL) {
217 p_port->rx.queue_size = 0;
219 while ((p_buf = (BT_HDR *)fixed_queue_try_dequeue(p_port->tx.queue)) != NULL) {
223 p_port->tx.queue_size = 0;
225 osi_mutex_global_unlock();
227 p_port->state = PORT_STATE_CLOSED;
229 if (p_port->rfc.state == RFC_STATE_CLOSED) {
230 RFCOMM_TRACE_DEBUG ("rfc_port_closed DONE");
231 if (p_port->rfc.p_mcb) {
232 p_port->rfc.p_mcb->port_inx[p_port->dlci] = 0;
234 /* If there are no more ports opened on this MCB release it */
235 rfc_check_mcb_active (p_port->rfc.p_mcb);
237 rfc_port_timer_stop (p_port);
238 fixed_queue_free(p_port->tx.queue, NULL);
239 p_port->tx.queue = NULL;
240 fixed_queue_free(p_port->rx.queue, NULL);
241 p_port->rx.queue = NULL;
243 RFCOMM_TRACE_DEBUG ("port_release_port:p_port->keep_port_handle:%d", p_port->keep_port_handle);
244 if ( p_port->keep_port_handle ) {
245 RFCOMM_TRACE_DEBUG ("port_release_port:Initialize handle:%d", p_port->inx);
246 /* save event mask and callback */
247 mask = p_port->ev_mask;
248 p_port_cb = p_port->p_callback;
249 user_port_pars = p_port->user_port_pars;
251 port_set_defaults(p_port);
253 p_port->ev_mask = mask;
254 p_port->p_callback = p_port_cb;
255 p_port->user_port_pars = user_port_pars;
256 p_port->mtu = p_port->keep_mtu;
258 p_port->state = PORT_STATE_OPENING;
259 p_port->rfc.p_mcb = NULL;
260 if (p_port->is_server) {
261 p_port->dlci &= 0xfe;
264 p_port->local_ctrl.modem_signal = p_port->default_signal_state;
265 memcpy (p_port->bd_addr, BT_BD_ANY, BD_ADDR_LEN);
267 RFCOMM_TRACE_DEBUG ("port_release_port:Clean-up handle:%d", p_port->inx);
268 rfc_port_timer_free (p_port);
269 memset (p_port, 0, sizeof (tPORT));
275 /*******************************************************************************
277 ** Function port_find_mcb
279 ** Description This function checks if connection exists to device with
282 *******************************************************************************/
283 tRFC_MCB *port_find_mcb (BD_ADDR bd_addr)
287 for (i = 0; i < MAX_BD_CONNECTIONS; i++) {
288 if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE)
289 && !memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN)) {
290 /* Multiplexer channel found do not change anything */
291 RFCOMM_TRACE_DEBUG("port_find_mcb: found bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
292 bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
293 RFCOMM_TRACE_DEBUG("port_find_mcb: rfc_cb.port.rfc_mcb:index:%d, %p, lcid:%d",
294 i, &rfc_cb.port.rfc_mcb[i], rfc_cb.port.rfc_mcb[i].lcid);
295 return (&rfc_cb.port.rfc_mcb[i]);
298 RFCOMM_TRACE_DEBUG("port_find_mcb: not found, bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
299 bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
304 /*******************************************************************************
306 ** Function port_find_mcb_dlci_port
308 ** Description Find port on the multiplexer channel based on DLCI. If
309 ** this port with DLCI not found try to use even DLCI. This
310 ** is for the case when client is establishing connection on
311 ** none-initiator MCB.
313 ** Returns Pointer to the PORT or NULL if not found
315 *******************************************************************************/
316 tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci)
324 if (dlci > RFCOMM_MAX_DLCI) {
328 inx = p_mcb->port_inx[dlci];
330 RFCOMM_TRACE_DEBUG("port_find_mcb_dlci_port: p_mcb:%p, port_inx[dlci:%d] is 0", p_mcb, dlci);
333 return (&rfc_cb.port.port[inx - 1]);
338 /*******************************************************************************
340 ** Function port_find_dlci_port
342 ** Description Find port with DLCI not assigned to multiplexer channel
344 ** Returns Pointer to the PORT or NULL if not found
346 *******************************************************************************/
347 tPORT *port_find_dlci_port (UINT8 dlci)
352 for (i = 0; i < MAX_RFC_PORTS; i++) {
353 p_port = &rfc_cb.port.port[i];
355 if (p_port->in_use && (p_port->rfc.p_mcb == NULL)) {
356 if (p_port->dlci == dlci) {
358 } else if ((dlci & 0x01) && (p_port->dlci == (dlci - 1))) {
368 /*******************************************************************************
370 ** Function port_find_port
372 ** Description Find port with DLCI, BD_ADDR
374 ** Returns Pointer to the PORT or NULL if not found
376 *******************************************************************************/
377 tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr)
382 for (i = 0; i < MAX_RFC_PORTS; i++) {
383 p_port = &rfc_cb.port.port[i];
385 && (p_port->dlci == dlci)
386 && !memcmp (p_port->bd_addr, bd_addr, BD_ADDR_LEN)) {
394 /*******************************************************************************
396 ** Function port_flow_control_user
398 ** Description Check the current user flow control and if necessary return
399 ** events to be send to the user based on the user's specified
400 ** flow control type.
402 ** Returns event mask to be returned to the application
404 *******************************************************************************/
405 UINT32 port_flow_control_user (tPORT *p_port)
409 /* Flow control to the user can be caused by flow controlling by the peer */
410 /* (FlowInd, or flow control by the peer RFCOMM (Fcon) or internally if */
411 /* tx_queue is full */
412 BOOLEAN fc = p_port->tx.peer_fc
413 || !p_port->rfc.p_mcb
414 || !p_port->rfc.p_mcb->peer_ready
415 || (p_port->tx.queue_size > PORT_TX_HIGH_WM)
416 || (fixed_queue_length(p_port->tx.queue) > PORT_TX_BUF_HIGH_WM);
418 if (p_port->tx.user_fc == fc) {
422 p_port->tx.user_fc = fc;
427 event = PORT_EV_FC | PORT_EV_FCS;
434 /*******************************************************************************
436 ** Function port_get_signal_changes
438 ** Description Check modem signals that has been changed
440 ** Returns event mask to be returned to the application
442 *******************************************************************************/
443 UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal)
445 UINT8 changed_signals = (signal ^ old_signals);
448 if (changed_signals & PORT_DTRDSR_ON) {
449 events |= PORT_EV_DSR;
451 if (signal & PORT_DTRDSR_ON) {
452 events |= PORT_EV_DSRS;
456 if (changed_signals & PORT_CTSRTS_ON) {
457 events |= PORT_EV_CTS;
459 if (signal & PORT_CTSRTS_ON) {
460 events |= PORT_EV_CTSS;
464 if (changed_signals & PORT_RING_ON) {
465 events |= PORT_EV_RING;
468 if (changed_signals & PORT_DCD_ON) {
469 events |= PORT_EV_RLSD;
471 if (signal & PORT_DCD_ON) {
472 events |= PORT_EV_RLSDS;
476 return (p_port->ev_mask & events);
479 /*******************************************************************************
481 ** Function port_flow_control_peer
483 ** Description Send flow control messages to the peer for both enabling
484 ** and disabling flow control, for both credit-based and
485 ** TS 07.10 flow control mechanisms.
489 *******************************************************************************/
490 void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count)
492 if (!p_port->rfc.p_mcb) {
496 /* If using credit based flow control */
497 if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) {
498 /* if want to enable flow from peer */
500 /* update rx credits */
501 if (count > p_port->credit_rx) {
502 p_port->credit_rx = 0;
504 p_port->credit_rx -= count;
507 /* If credit count is less than low credit watermark, and user */
508 /* did not force flow control, send a credit update */
509 /* There might be a special case when we just adjusted rx_max */
510 if ((p_port->credit_rx <= p_port->credit_rx_low)
511 && !p_port->rx.user_fc
512 && (p_port->credit_rx_max > p_port->credit_rx)) {
513 rfc_send_credit(p_port->rfc.p_mcb, p_port->dlci,
514 (UINT8) (p_port->credit_rx_max - p_port->credit_rx));
516 p_port->credit_rx = p_port->credit_rx_max;
518 p_port->rx.peer_fc = FALSE;
521 /* else want to disable flow from peer */
523 /* if client registered data callback, just do what they want */
524 if (p_port->p_data_callback || p_port->p_data_co_callback) {
525 p_port->rx.peer_fc = TRUE;
527 /* if queue count reached credit rx max, set peer fc */
528 else if (fixed_queue_length(p_port->rx.queue) >= p_port->credit_rx_max) {
529 p_port->rx.peer_fc = TRUE;
533 /* else using TS 07.10 flow control */
535 /* if want to enable flow from peer */
537 /* If rfcomm suspended traffic from the peer based on the rx_queue_size */
538 /* check if it can be resumed now */
539 if (p_port->rx.peer_fc
540 && (p_port->rx.queue_size < PORT_RX_LOW_WM)
541 && (fixed_queue_length(p_port->rx.queue) < PORT_RX_BUF_LOW_WM)) {
542 p_port->rx.peer_fc = FALSE;
544 /* If user did not force flow control allow traffic now */
545 if (!p_port->rx.user_fc) {
546 RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, TRUE);
550 /* else want to disable flow from peer */
552 /* if client registered data callback, just do what they want */
553 if (p_port->p_data_callback || p_port->p_data_co_callback) {
554 p_port->rx.peer_fc = TRUE;
555 RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE);
557 /* Check the size of the rx queue. If it exceeds certain */
558 /* level and flow control has not been sent to the peer do it now */
559 else if ( ((p_port->rx.queue_size > PORT_RX_HIGH_WM)
560 || (fixed_queue_length(p_port->rx.queue) > PORT_RX_BUF_HIGH_WM))
561 && !p_port->rx.peer_fc) {
562 RFCOMM_TRACE_EVENT ("PORT_DataInd Data reached HW. Sending FC set.");
564 p_port->rx.peer_fc = TRUE;
565 RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE);
572 #endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)