1 /******************************************************************************
3 * Copyright (C) 2003-2016 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 * Name: avct_bcb_act.cc
23 * Description: This module contains action functions of the browsing control
26 *****************************************************************************/
32 #include "bt_target.h"
35 #include "osi/include/osi.h"
37 /* action function list */
38 const tAVCT_BCB_ACTION avct_bcb_action[] = {
39 avct_bcb_chnl_open, /* AVCT_LCB_CHNL_OPEN */
40 avct_bcb_chnl_disc, /* AVCT_LCB_CHNL_DISC */
41 avct_bcb_send_msg, /* AVCT_LCB_SEND_MSG */
42 avct_bcb_open_ind, /* AVCT_LCB_OPEN_IND */
43 avct_bcb_open_fail, /* AVCT_LCB_OPEN_FAIL */
44 avct_bcb_close_ind, /* AVCT_LCB_CLOSE_IND */
45 avct_bcb_close_cfm, /* AVCT_LCB_CLOSE_CFM */
46 avct_bcb_msg_ind, /* AVCT_LCB_MSG_IND */
47 avct_bcb_cong_ind, /* AVCT_LCB_CONG_IND */
48 avct_bcb_bind_conn, /* AVCT_LCB_BIND_CONN */
49 avct_bcb_bind_fail, /* AVCT_LCB_BIND_FAIL */
50 avct_bcb_unbind_disc, /* AVCT_LCB_UNBIND_DISC */
51 avct_bcb_chk_disc, /* AVCT_LCB_CHK_DISC */
52 avct_bcb_discard_msg, /* AVCT_LCB_DISCARD_MSG */
53 avct_bcb_dealloc, /* AVCT_LCB_DEALLOC */
54 avct_bcb_free_msg_ind /* AVCT_LCB_FREE_MSG_IND */
57 /*******************************************************************************
59 * Function avct_bcb_msg_asmbl
61 * Description Reassemble incoming message.
64 * Returns Pointer to reassembled message; NULL if no message
67 ******************************************************************************/
68 static BT_HDR* avct_bcb_msg_asmbl(UNUSED_ATTR tAVCT_BCB* p_bcb, BT_HDR* p_buf) {
72 /* parse the message header */
73 p = (uint8_t*)(p_buf + 1) + p_buf->offset;
74 pkt_type = AVCT_PKT_TYPE(p);
76 /* must be single packet - can not fragment */
77 if (pkt_type != AVCT_PKT_TYPE_SINGLE) {
78 osi_free_and_reset((void**)&p_buf);
79 AVCT_TRACE_WARNING("Pkt type=%d - fragmentation not allowed. drop it",
85 /*******************************************************************************
87 * Function avct_bcb_chnl_open
89 * Description Open L2CAP channel to peer
94 ******************************************************************************/
95 void avct_bcb_chnl_open(tAVCT_BCB* p_bcb, UNUSED_ATTR tAVCT_LCB_EVT* p_data) {
96 uint16_t result = AVCT_RESULT_FAIL;
97 tAVCT_LCB* p_lcb = avct_lcb_by_bcb(p_bcb);
98 tL2CAP_ERTM_INFO ertm_info;
100 BTM_SetOutService(p_lcb->peer_addr, BTM_SEC_SERVICE_AVCTP_BROWSE, 0);
102 /* Set the FCR options: Browsing channel mandates ERTM */
103 ertm_info.preferred_mode = avct_l2c_br_fcr_opts_def.mode;
104 ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
105 ertm_info.user_rx_buf_size = BT_DEFAULT_BUFFER_SIZE;
106 ertm_info.user_tx_buf_size = BT_DEFAULT_BUFFER_SIZE;
107 ertm_info.fcr_rx_buf_size = BT_DEFAULT_BUFFER_SIZE;
108 ertm_info.fcr_tx_buf_size = BT_DEFAULT_BUFFER_SIZE;
110 /* call l2cap connect req */
111 p_bcb->ch_state = AVCT_CH_CONN;
113 L2CA_ErtmConnectReq(AVCT_BR_PSM, p_lcb->peer_addr, &ertm_info);
114 if (p_bcb->ch_lcid == 0) {
115 /* if connect req failed, send ourselves close event */
116 tAVCT_LCB_EVT avct_lcb_evt;
117 avct_lcb_evt.result = result;
118 avct_bcb_event(p_bcb, AVCT_LCB_LL_CLOSE_EVT, &avct_lcb_evt);
122 /*******************************************************************************
124 * Function avct_bcb_unbind_disc
126 * Description call callback with disconnect event.
131 ******************************************************************************/
132 void avct_bcb_unbind_disc(UNUSED_ATTR tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
133 p_data->p_ccb->p_bcb = NULL;
134 (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb),
135 AVCT_BROWSE_DISCONN_CFM_EVT, 0, NULL);
138 /*******************************************************************************
140 * Function avct_bcb_open_ind
142 * Description Handle an LL_OPEN event.
143 * For the allocated ccb already bound to the bcb, send a
144 * connect event. For the unbound ccb with a new PID, bind that
145 * ccb to the bcb with the same bd_addr and send a connect
151 ******************************************************************************/
152 void avct_bcb_open_ind(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
153 tAVCT_CCB* p_ccb = &avct_cb.ccb[0];
154 tAVCT_CCB* p_ccb_bind = NULL;
158 for (int idx = 0; idx < AVCT_NUM_CONN; idx++, p_ccb++) {
159 /* if ccb allocated and */
160 if (p_ccb->allocated) {
161 /* if bound to this bcb send connect confirm event */
162 if (p_ccb->p_bcb == p_bcb) {
165 p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_BROWSE_CONN_CFM_EVT,
166 0, &p_ccb->p_lcb->peer_addr);
168 /* if unbound acceptor and lcb allocated and bd_addr are the same for bcb
170 else if ((p_ccb->p_bcb == NULL) && (p_ccb->cc.role == AVCT_ACP) &&
171 (p_ccb->p_lcb != NULL) &&
172 p_bcb->peer_addr == p_ccb->p_lcb->peer_addr) {
173 /* bind bcb to ccb and send connect ind event */
176 p_ccb->p_bcb = p_bcb;
177 p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_BROWSE_CONN_IND_EVT,
178 0, &p_ccb->p_lcb->peer_addr);
183 /* if no ccbs bound to this lcb, disconnect */
185 avct_bcb_event(p_bcb, AVCT_LCB_INT_CLOSE_EVT, p_data);
189 if (!p_bcb->p_tx_msg || !p_ccb_bind) {
193 ul_msg.p_buf = p_bcb->p_tx_msg;
194 ul_msg.p_ccb = p_ccb_bind;
195 ul_msg.label = (uint8_t)(p_bcb->p_tx_msg->layer_specific & 0xFF);
196 ul_msg.cr = (uint8_t)((p_bcb->p_tx_msg->layer_specific & 0xFF00) >> 8);
197 p_bcb->p_tx_msg->layer_specific = AVCT_DATA_BROWSE;
198 p_bcb->p_tx_msg = NULL;
200 /* send msg event to bcb */
201 tAVCT_LCB_EVT avct_lcb_evt;
202 avct_lcb_evt.ul_msg = ul_msg;
203 avct_bcb_event(p_bcb, AVCT_LCB_UL_MSG_EVT, &avct_lcb_evt);
206 /*******************************************************************************
208 * Function avct_bcb_open_fail
210 * Description L2CAP channel open attempt failed. Mark the ccbs
216 ******************************************************************************/
217 void avct_bcb_open_fail(tAVCT_BCB* p_bcb, UNUSED_ATTR tAVCT_LCB_EVT* p_data) {
218 tAVCT_CCB* p_ccb = &avct_cb.ccb[0];
220 for (int idx = 0; idx < AVCT_NUM_CONN; idx++, p_ccb++) {
221 if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) {
227 /*******************************************************************************
229 * Function avct_bcb_close_ind
231 * Description L2CAP channel closed by peer. Deallocate any initiator
232 * ccbs on this lcb and send disconnect ind event.
237 ******************************************************************************/
238 void avct_bcb_close_ind(tAVCT_BCB* p_bcb, UNUSED_ATTR tAVCT_LCB_EVT* p_data) {
239 tAVCT_CCB* p_ccb = &avct_cb.ccb[0];
240 tAVCT_LCB* p_lcb = avct_lcb_by_bcb(p_bcb);
242 for (int idx = 0; idx < AVCT_NUM_CONN; idx++, p_ccb++) {
243 if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) {
244 if (p_ccb->cc.role == AVCT_INT) {
245 (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb),
246 AVCT_BROWSE_DISCONN_CFM_EVT, 0,
249 (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb),
250 AVCT_BROWSE_DISCONN_IND_EVT, 0, NULL);
257 /*******************************************************************************
259 * Function avct_bcb_close_cfm
261 * Description L2CAP channel closed by us. Deallocate any initiator
262 * ccbs on this lcb and send disconnect ind or cfm event.
267 ******************************************************************************/
268 void avct_bcb_close_cfm(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
269 tAVCT_CCB* p_ccb = &avct_cb.ccb[0];
271 /* Whether BCB initiated channel close */
272 bool ch_close = p_bcb->ch_close;
273 tAVCT_CTRL_CBACK* p_cback;
275 p_bcb->ch_close = false;
276 p_bcb->allocated = 0;
277 for (int idx = 0; idx < AVCT_NUM_CONN; idx++, p_ccb++) {
278 if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) {
279 /* if this ccb initiated close send disconnect cfm otherwise ind */
281 event = AVCT_BROWSE_DISCONN_CFM_EVT;
283 event = AVCT_BROWSE_DISCONN_IND_EVT;
286 p_cback = p_ccb->cc.p_ctrl_cback;
288 if (p_ccb->p_lcb == NULL) avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL);
289 (*p_cback)(avct_ccb_to_idx(p_ccb), event, p_data->result,
295 /*******************************************************************************
297 * Function avct_bcb_bind_conn
299 * Description Bind ccb to lcb and send connect cfm event.
304 ******************************************************************************/
305 void avct_bcb_bind_conn(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
306 tAVCT_LCB* p_lcb = avct_lcb_by_bcb(p_bcb);
307 p_data->p_ccb->p_bcb = p_bcb;
308 (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb),
309 AVCT_BROWSE_CONN_CFM_EVT, 0,
313 /*******************************************************************************
315 * Function avct_bcb_chk_disc
317 * Description A ccb wants to close; if it is the last ccb on this lcb,
318 * close channel. Otherwise just deallocate and call
324 ******************************************************************************/
325 void avct_bcb_chk_disc(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
326 p_bcb->ch_close = avct_bcb_get_last_ccb_index(p_bcb, p_data->p_ccb);
327 if (p_bcb->ch_close) {
328 avct_bcb_event(p_bcb, AVCT_LCB_INT_CLOSE_EVT, p_data);
332 avct_bcb_unbind_disc(p_bcb, p_data);
335 /*******************************************************************************
337 * Function avct_bcb_chnl_disc
339 * Description Disconnect L2CAP channel.
344 ******************************************************************************/
345 void avct_bcb_chnl_disc(tAVCT_BCB* p_bcb, UNUSED_ATTR tAVCT_LCB_EVT* p_data) {
346 L2CA_DisconnectReq(p_bcb->ch_lcid);
349 /*******************************************************************************
351 * Function avct_bcb_bind_fail
353 * Description Deallocate ccb and call callback with connect event
354 * with failure result.
359 ******************************************************************************/
360 void avct_bcb_bind_fail(UNUSED_ATTR tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
361 p_data->p_ccb->p_bcb = NULL;
362 (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb),
363 AVCT_BROWSE_CONN_CFM_EVT, AVCT_RESULT_FAIL,
367 /*******************************************************************************
369 * Function avct_bcb_cong_ind
371 * Description Handle congestion indication from L2CAP.
376 ******************************************************************************/
377 void avct_bcb_cong_ind(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
378 tAVCT_CCB* p_ccb = &avct_cb.ccb[0];
380 tAVCT_LCB* p_lcb = avct_lcb_by_bcb(p_bcb);
384 (p_data->cong) ? AVCT_BROWSE_CONG_IND_EVT : AVCT_BROWSE_UNCONG_IND_EVT;
386 /* send event to all ccbs on this lcb */
387 for (int idx = 0; idx < AVCT_NUM_CONN; idx++, p_ccb++) {
388 if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) {
389 (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, 0,
395 /*******************************************************************************
397 * Function avct_bcb_discard_msg
399 * Description Discard a message sent in from the API.
404 ******************************************************************************/
405 void avct_bcb_discard_msg(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
406 osi_free_and_reset((void**)&p_bcb->p_tx_msg);
408 /* if control channel is up, save the message and open the browsing channel */
409 if (p_data->ul_msg.p_ccb->p_lcb == NULL) {
410 osi_free_and_reset((void**)&p_data->ul_msg.p_buf);
413 p_bcb->p_tx_msg = p_data->ul_msg.p_buf;
415 if (p_bcb->p_tx_msg) {
416 p_bcb->p_tx_msg->layer_specific =
417 (p_data->ul_msg.cr << 8) + p_data->ul_msg.label;
419 /* the channel is closed, opening or closing - open it again */
420 AVCT_TRACE_DEBUG("ch_state: %d, allocated:%d->%d", p_bcb->ch_state,
421 p_bcb->allocated, p_data->ul_msg.p_ccb->p_lcb->allocated);
422 p_bcb->allocated = p_data->ul_msg.p_ccb->p_lcb->allocated;
423 avct_bcb_event(p_bcb, AVCT_LCB_UL_BIND_EVT,
424 (tAVCT_LCB_EVT*)p_data->ul_msg.p_ccb);
428 /*******************************************************************************
430 * Function avct_bcb_send_msg
432 * Description Build and send an AVCTP message.
437 ******************************************************************************/
438 void avct_bcb_send_msg(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
439 uint16_t curr_msg_len;
440 uint8_t pkt_type = AVCT_PKT_TYPE_SINGLE;
446 curr_msg_len = p_data->ul_msg.p_buf->len;
448 /* initialize packet type and other stuff */
449 if (curr_msg_len > (p_bcb->peer_mtu - AVCT_HDR_LEN_SINGLE)) {
450 AVCT_TRACE_ERROR("%s msg len (%d) exceeds peer mtu(%d-%d)!!", __func__,
451 curr_msg_len, p_bcb->peer_mtu, AVCT_HDR_LEN_SINGLE);
452 osi_free_and_reset((void**)&p_data->ul_msg.p_buf);
457 hdr_len = avct_lcb_pkt_type_len[pkt_type];
458 p_buf = p_data->ul_msg.p_buf;
460 /* set up to build header */
461 p_buf->len += hdr_len;
462 p_buf->offset -= hdr_len;
463 p = (uint8_t*)(p_buf + 1) + p_buf->offset;
466 AVCT_BUILD_HDR(p, p_data->ul_msg.label, pkt_type, p_data->ul_msg.cr);
467 UINT16_TO_BE_STREAM(p, p_data->ul_msg.p_ccb->cc.pid);
469 p_buf->layer_specific = AVCT_DATA_BROWSE;
471 /* send message to L2CAP */
472 L2CA_DataWrite(p_bcb->ch_lcid, p_buf);
475 /*******************************************************************************
477 * Function avct_bcb_free_msg_ind
479 * Description Discard an incoming AVCTP message.
484 ******************************************************************************/
485 void avct_bcb_free_msg_ind(UNUSED_ATTR tAVCT_BCB* p_bcb,
486 tAVCT_LCB_EVT* p_data) {
487 if (p_data) osi_free_and_reset((void**)&p_data->p_buf);
490 /*******************************************************************************
492 * Function avct_bcb_msg_ind
494 * Description Handle an incoming AVCTP message.
499 ******************************************************************************/
500 void avct_bcb_msg_ind(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* p_data) {
502 uint8_t label, type, cr_ipid;
505 tAVCT_LCB* p_lcb = avct_lcb_by_bcb(p_bcb);
507 if ((p_data == NULL) || (p_data->p_buf == NULL)) {
508 AVCT_TRACE_WARNING("%s p_data is NULL, returning!", __func__);
512 /* this p_buf is to be reported through p_msg_cback. The layer_specific
513 * needs to be set properly to indicate that it is received through
514 * browsing channel */
515 p_data->p_buf->layer_specific = AVCT_DATA_BROWSE;
517 /* reassemble message; if no message available (we received a fragment) return
519 p_data->p_buf = avct_bcb_msg_asmbl(p_bcb, p_data->p_buf);
520 if (p_data->p_buf == NULL) {
524 if (p_data->p_buf->len < AVCT_HDR_LEN_SINGLE) {
525 AVCT_TRACE_WARNING("Invalid AVCTP packet length %d: must be at least %d",
526 p_data->p_buf->len, AVCT_HDR_LEN_SINGLE);
527 osi_free_and_reset((void**)&p_data->p_buf);
528 android_errorWriteLog(0x534e4554, "79944113");
532 p = (uint8_t*)(p_data->p_buf + 1) + p_data->p_buf->offset;
534 /* parse header byte */
535 AVCT_PARSE_HDR(p, label, type, cr_ipid);
537 /* check for invalid cr_ipid */
538 if (cr_ipid == AVCT_CR_IPID_INVALID) {
539 AVCT_TRACE_WARNING("Invalid cr_ipid", cr_ipid);
540 osi_free_and_reset((void**)&p_data->p_buf);
544 /* parse and lookup PID */
545 BE_STREAM_TO_UINT16(pid, p);
546 p_ccb = avct_lcb_has_pid(p_lcb, pid);
548 /* PID found; send msg up, adjust bt hdr and call msg callback */
549 p_data->p_buf->offset += AVCT_HDR_LEN_SINGLE;
550 p_data->p_buf->len -= AVCT_HDR_LEN_SINGLE;
551 (*p_ccb->cc.p_msg_cback)(avct_ccb_to_idx(p_ccb), label, cr_ipid,
556 /* PID not found; drop message */
557 AVCT_TRACE_WARNING("No ccb for PID=%x", pid);
558 osi_free_and_reset((void**)&p_data->p_buf);
560 /* if command send reject */
561 if (cr_ipid == AVCT_CMD) {
562 BT_HDR* p_buf = (BT_HDR*)osi_malloc(AVRC_CMD_BUF_SIZE);
563 p_buf->len = AVCT_HDR_LEN_SINGLE;
564 p_buf->offset = AVCT_MSG_OFFSET - AVCT_HDR_LEN_SINGLE;
565 p = (uint8_t*)(p_buf + 1) + p_buf->offset;
566 AVCT_BUILD_HDR(p, label, AVCT_PKT_TYPE_SINGLE, AVCT_REJ);
567 UINT16_TO_BE_STREAM(p, pid);
568 p_buf->layer_specific = AVCT_DATA_BROWSE;
569 L2CA_DataWrite(p_bcb->ch_lcid, p_buf);
573 /*******************************************************************************
575 * Function avct_bcb_dealloc
577 * Description Deallocate a browse control block.
582 ******************************************************************************/
583 void avct_bcb_dealloc(tAVCT_BCB* p_bcb, UNUSED_ATTR tAVCT_LCB_EVT* p_data) {
584 tAVCT_CCB* p_ccb = &avct_cb.ccb[0];
586 AVCT_TRACE_DEBUG("%s %d", __func__, p_bcb->allocated);
588 for (int idx = 0; idx < AVCT_NUM_CONN; idx++, p_ccb++) {
589 /* if ccb allocated and */
590 if ((p_ccb->allocated) && (p_ccb->p_bcb == p_bcb)) {
592 AVCT_TRACE_DEBUG("%s used by ccb: %d", __func__, idx);
597 /* the browsing channel is down. Check if we have pending messages */
598 osi_free_and_reset((void**)&p_bcb->p_tx_msg);
599 memset(p_bcb, 0, sizeof(tAVCT_BCB));
602 /*******************************************************************************
604 * Function avct_close_bcb
606 * Description this function is called right before LCB disconnects.
611 ******************************************************************************/
612 void avct_close_bcb(tAVCT_LCB* p_lcb, tAVCT_LCB_EVT* p_data) {
613 tAVCT_BCB* p_bcb = avct_bcb_by_lcb(p_lcb);
614 if (p_bcb->allocated) {
615 avct_bcb_event(p_bcb, AVCT_LCB_UL_UNBIND_EVT, p_data);
619 /*******************************************************************************
621 * Function avct_lcb_by_bcb
623 * Description This lookup function finds the lcb for a bcb.
625 * Returns pointer to the lcb.
627 ******************************************************************************/
628 tAVCT_LCB* avct_lcb_by_bcb(tAVCT_BCB* p_bcb) {
629 return &avct_cb.lcb[p_bcb->allocated - 1];
632 /*******************************************************************************
634 * Function avct_bcb_by_lcb
636 * Description This lookup function finds the bcb for a lcb.
638 * Returns pointer to the lcb.
640 ******************************************************************************/
641 tAVCT_BCB* avct_bcb_by_lcb(tAVCT_LCB* p_lcb) {
642 return &avct_cb.bcb[p_lcb->allocated - 1];
645 /*******************************************************************************
647 * Function avct_bcb_get_last_ccb_index
649 * Description See if given ccb is only one on the bcb.
652 * Returns 0, if ccb is last, (ccb index + 1) otherwise.
654 ******************************************************************************/
655 uint8_t avct_bcb_get_last_ccb_index(tAVCT_BCB* p_bcb, tAVCT_CCB* p_ccb_last) {
656 tAVCT_CCB* p_ccb = &avct_cb.ccb[0];
659 for (int i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) {
660 if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) {
661 if (p_ccb != p_ccb_last) return 0;
662 idx = (uint8_t)(i + 1);
668 /*******************************************************************************
670 * Function avct_bcb_by_lcid
672 * Description Find the BCB associated with the L2CAP LCID
675 * Returns pointer to the lcb, or NULL if none found.
677 ******************************************************************************/
678 tAVCT_BCB* avct_bcb_by_lcid(uint16_t lcid) {
679 tAVCT_BCB* p_bcb = &avct_cb.bcb[0];
682 for (idx = 0; idx < AVCT_NUM_LINKS; idx++, p_bcb++) {
683 if (p_bcb->allocated && (p_bcb->ch_lcid == lcid)) {
689 AVCT_TRACE_WARNING("No bcb for lcid %x", lcid);