• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright (C) 2009-2012 Broadcom Corporation
4  *
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:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  ******************************************************************************/
18 
19 /******************************************************************************
20  *
21  *  This is the implementation file for the MCAP Main Control Block and
22  *  Utility functions.
23  *
24  ******************************************************************************/
25 #include <string.h>
26 
27 #include "bt_target.h"
28 #include "gki.h"
29 #include "mca_api.h"
30 #include "mca_defs.h"
31 #include "mca_int.h"
32 #include "wcassert.h"
33 #include "l2c_api.h"
34 
35 /* Main Control block for MCA */
36 #if MCA_DYNAMIC_MEMORY == FALSE
37 tMCA_CB mca_cb;
38 #endif
39 
40 /*****************************************************************************
41 ** constants
42 *****************************************************************************/
43 
44 /* table of standard opcode message size */
45 const UINT8 mca_std_msg_len[MCA_NUM_STANDARD_OPCODE] = {
46     4,          /* MCA_OP_ERROR_RSP         */
47     5,          /* MCA_OP_MDL_CREATE_REQ    */
48     5,          /* MCA_OP_MDL_CREATE_RSP    */
49     3,          /* MCA_OP_MDL_RECONNECT_REQ */
50     4,          /* MCA_OP_MDL_RECONNECT_RSP */
51     3,          /* MCA_OP_MDL_ABORT_REQ     */
52     4,          /* MCA_OP_MDL_ABORT_RSP     */
53     3,          /* MCA_OP_MDL_DELETE_REQ    */
54     4           /* MCA_OP_MDL_DELETE_RSP    */
55 };
56 
57 
58 /*******************************************************************************
59 **
60 ** Function         mca_handle_by_cpsm
61 **
62 ** Description      This function returns the handle for the given control
63 **                  channel PSM. 0, if not found.
64 **
65 ** Returns          the MCA handle.
66 **
67 *******************************************************************************/
mca_handle_by_cpsm(UINT16 psm)68 tMCA_HANDLE mca_handle_by_cpsm(UINT16 psm)
69 {
70     int     i;
71     tMCA_HANDLE handle = 0;
72     tMCA_RCB *p_rcb = &mca_cb.rcb[0];
73 
74     for (i=0; i<MCA_NUM_REGS; i++, p_rcb++)
75     {
76         if (p_rcb->p_cback && p_rcb->reg.ctrl_psm == psm)
77         {
78             handle = i+1;
79             break;
80         }
81     }
82     return handle;
83 }
84 
85 /*******************************************************************************
86 **
87 ** Function         mca_handle_by_dpsm
88 **
89 ** Description      This function returns the handle for the given data
90 **                  channel PSM. 0, if not found.
91 **
92 ** Returns          the MCA handle.
93 **
94 *******************************************************************************/
mca_handle_by_dpsm(UINT16 psm)95 tMCA_HANDLE mca_handle_by_dpsm(UINT16 psm)
96 {
97     int     i;
98     tMCA_HANDLE handle = 0;
99     tMCA_RCB *p_rcb = &mca_cb.rcb[0];
100 
101     for (i=0; i<MCA_NUM_REGS; i++, p_rcb++)
102     {
103         if (p_rcb->p_cback && p_rcb->reg.data_psm == psm)
104         {
105             handle = i+1;
106             break;
107         }
108     }
109     return handle;
110 }
111 
112 /*******************************************************************************
113 **
114 ** Function         mca_tc_tbl_calloc
115 **
116 ** Description      This function allocates a transport table for the given
117 **                  control channel.
118 **
119 ** Returns          The tranport table.
120 **
121 *******************************************************************************/
mca_tc_tbl_calloc(tMCA_CCB * p_ccb)122 tMCA_TC_TBL * mca_tc_tbl_calloc(tMCA_CCB *p_ccb)
123 {
124     tMCA_TC_TBL *p_tbl = mca_cb.tc.tc_tbl;
125     int             i;
126 
127     /* find next free entry in tc table */
128     for (i = 0; i < MCA_NUM_TC_TBL; i++, p_tbl++)
129     {
130         if (p_tbl->state == MCA_TC_ST_UNUSED)
131         {
132             break;
133         }
134     }
135 
136     /* sanity check */
137     WC_ASSERT(i != MCA_NUM_TC_TBL);
138 
139     /* initialize entry */
140     p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
141     p_tbl->cfg_flags= 0;
142     p_tbl->cb_idx   = mca_ccb_to_hdl(p_ccb);
143     p_tbl->tcid     = MCA_CTRL_TCID;
144     p_tbl->my_mtu   = MCA_CTRL_MTU;
145     p_tbl->state    = MCA_TC_ST_IDLE;
146     p_tbl->lcid     = p_ccb->lcid;
147     mca_cb.tc.lcid_tbl[p_ccb->lcid - L2CAP_BASE_APPL_CID] = i;
148     MCA_TRACE_DEBUG("mca_tc_tbl_calloc cb_idx: %d", p_tbl->cb_idx);
149 
150     return p_tbl;
151 }
152 
153 /*******************************************************************************
154 **
155 ** Function         mca_tc_tbl_dalloc
156 **
157 ** Description      This function allocates a transport table for the given
158 **                  data channel.
159 **
160 ** Returns          The tranport table.
161 **
162 *******************************************************************************/
mca_tc_tbl_dalloc(tMCA_DCB * p_dcb)163 tMCA_TC_TBL * mca_tc_tbl_dalloc(tMCA_DCB *p_dcb)
164 {
165     tMCA_TC_TBL *p_tbl = mca_cb.tc.tc_tbl;
166     int             i;
167 
168     /* find next free entry in tc table */
169     for (i = 0; i < MCA_NUM_TC_TBL; i++, p_tbl++)
170     {
171         if (p_tbl->state == MCA_TC_ST_UNUSED)
172         {
173             break;
174         }
175     }
176 
177     /* sanity check */
178     WC_ASSERT(i != MCA_NUM_TC_TBL);
179 
180     /* initialize entry */
181     p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
182     p_tbl->cfg_flags= 0;
183     p_tbl->cb_idx   = mca_dcb_to_hdl(p_dcb);
184     p_tbl->tcid     = p_dcb->p_cs->type + 1;
185     p_tbl->my_mtu   = p_dcb->p_chnl_cfg->data_mtu;
186     p_tbl->state    = MCA_TC_ST_IDLE;
187     p_tbl->lcid     = p_dcb->lcid;
188     mca_cb.tc.lcid_tbl[p_dcb->lcid - L2CAP_BASE_APPL_CID] = i;
189     MCA_TRACE_DEBUG("mca_tc_tbl_dalloc tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx);
190 
191     return p_tbl;
192 }
193 
194 /*******************************************************************************
195 **
196 ** Function         mca_tc_tbl_by_lcid
197 **
198 ** Description      Find the transport channel table entry by LCID.
199 **
200 **
201 ** Returns          The tranport table.
202 **
203 *******************************************************************************/
mca_tc_tbl_by_lcid(UINT16 lcid)204 tMCA_TC_TBL *mca_tc_tbl_by_lcid(UINT16 lcid)
205 {
206     UINT8 idx;
207 
208     if (lcid)
209     {
210         idx = mca_cb.tc.lcid_tbl[lcid - L2CAP_BASE_APPL_CID];
211 
212         if (idx < MCA_NUM_TC_TBL)
213         {
214             return &mca_cb.tc.tc_tbl[idx];
215         }
216     }
217     return NULL;
218 }
219 
220 /*******************************************************************************
221 **
222 ** Function         mca_free_tc_tbl_by_lcid
223 **
224 ** Description      Find the  transport table entry by LCID
225 **                  and free the tc_tbl
226 **
227 ** Returns          void.
228 **
229 *******************************************************************************/
mca_free_tc_tbl_by_lcid(UINT16 lcid)230 void mca_free_tc_tbl_by_lcid(UINT16 lcid)
231 {
232     UINT8 idx;
233 
234     if (lcid)
235     {
236         idx = mca_cb.tc.lcid_tbl[lcid - L2CAP_BASE_APPL_CID];
237 
238         if (idx < MCA_NUM_TC_TBL)
239         {
240             mca_cb.tc.tc_tbl[idx].state = MCA_TC_ST_UNUSED;
241         }
242     }
243 }
244 
245 
246 /*******************************************************************************
247 **
248 ** Function         mca_set_cfg_by_tbl
249 **
250 ** Description      Set the L2CAP configuration information
251 **
252 ** Returns          none.
253 **
254 *******************************************************************************/
mca_set_cfg_by_tbl(tL2CAP_CFG_INFO * p_cfg,tMCA_TC_TBL * p_tbl)255 void mca_set_cfg_by_tbl(tL2CAP_CFG_INFO *p_cfg, tMCA_TC_TBL *p_tbl)
256 {
257     tMCA_DCB   *p_dcb;
258     const tL2CAP_FCR_OPTS *p_opt;
259     tMCA_FCS_OPT    fcs = MCA_FCS_NONE;
260 
261     if (p_tbl->tcid == MCA_CTRL_TCID)
262     {
263         p_opt = &mca_l2c_fcr_opts_def;
264     }
265     else
266     {
267         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
268         p_opt = &p_dcb->p_chnl_cfg->fcr_opt;
269         fcs   = p_dcb->p_chnl_cfg->fcs;
270     }
271     memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO));
272     p_cfg->mtu_present = TRUE;
273     p_cfg->mtu = p_tbl->my_mtu;
274     p_cfg->fcr_present = TRUE;
275     memcpy(&p_cfg->fcr, p_opt, sizeof (tL2CAP_FCR_OPTS));
276     if (fcs & MCA_FCS_PRESNT_MASK)
277     {
278         p_cfg->fcs_present = TRUE;
279         p_cfg->fcs = (fcs & MCA_FCS_USE_MASK);
280     }
281 }
282 
283 /*******************************************************************************
284 **
285 ** Function         mca_tc_close_ind
286 **
287 ** Description      This function is called by the L2CAP interface when the
288 **                  L2CAP channel is closed.  It looks up the CCB or DCB for
289 **                  the channel and sends it a close event.  The reason
290 **                  parameter is the same value passed by the L2CAP
291 **                  callback function.
292 **
293 ** Returns          Nothing.
294 **
295 *******************************************************************************/
mca_tc_close_ind(tMCA_TC_TBL * p_tbl,UINT16 reason)296 void mca_tc_close_ind(tMCA_TC_TBL *p_tbl, UINT16 reason)
297 {
298     tMCA_CCB   *p_ccb;
299     tMCA_DCB   *p_dcb;
300     tMCA_CLOSE  close;
301 
302     close.param  = MCA_ACP;
303     close.reason = reason;
304     close.lcid   = p_tbl->lcid;
305 
306     MCA_TRACE_DEBUG("mca_tc_close_ind tcid: %d, cb_idx:%d, old: %d",
307                      p_tbl->tcid, p_tbl->cb_idx, p_tbl->state);
308 
309     /* Check if the transport channel is in use */
310     if (p_tbl->state == MCA_TC_ST_UNUSED)
311         return;
312 
313     /* clear mca_tc_tbl entry */
314     if (p_tbl->cfg_flags&MCA_L2C_CFG_DISCN_INT)
315         close.param = MCA_INT;
316     p_tbl->cfg_flags = 0;
317     p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
318 
319     /* if control channel, notify ccb that channel close */
320     if (p_tbl->tcid == MCA_CTRL_TCID)
321     {
322         p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
323         mca_ccb_event(p_ccb, MCA_CCB_LL_CLOSE_EVT, (tMCA_CCB_EVT *)&close);
324     }
325     /* notify dcb that channel close */
326     else
327     {
328         /* look up dcb  */
329         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
330         if (p_dcb != NULL)
331         {
332             mca_dcb_event(p_dcb, MCA_DCB_TC_CLOSE_EVT, (tMCA_DCB_EVT *) &close);
333         }
334     }
335     p_tbl->state = MCA_TC_ST_UNUSED;
336 }
337 
338 /*******************************************************************************
339 **
340 ** Function         mca_tc_open_ind
341 **
342 ** Description      This function is called by the L2CAP interface when
343 **                  the L2CAP channel is opened.  It looks up the CCB or DCB
344 **                  for the channel and sends it an open event.
345 **
346 ** Returns          Nothing.
347 **
348 *******************************************************************************/
mca_tc_open_ind(tMCA_TC_TBL * p_tbl)349 void mca_tc_open_ind(tMCA_TC_TBL *p_tbl)
350 {
351     tMCA_CCB   *p_ccb;
352     tMCA_DCB   *p_dcb;
353     tMCA_OPEN  open;
354 
355     MCA_TRACE_DEBUG("mca_tc_open_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx);
356     p_tbl->state = MCA_TC_ST_OPEN;
357 
358     open.peer_mtu = p_tbl->peer_mtu;
359     open.lcid = p_tbl->lcid;
360     /* use param to indicate the role of connection.
361      * MCA_ACP, if ACP */
362     open.param = MCA_INT;
363     if (p_tbl->cfg_flags & MCA_L2C_CFG_CONN_ACP)
364     {
365         open.param = MCA_ACP;
366     }
367 
368     /* if control channel, notify ccb that channel open */
369     if (p_tbl->tcid == MCA_CTRL_TCID)
370     {
371         p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
372 
373         mca_ccb_event(p_ccb, MCA_CCB_LL_OPEN_EVT, (tMCA_CCB_EVT *)&open);
374     }
375     /* must be data channel, notify dcb that channel open */
376     else
377     {
378         /* look up dcb */
379         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
380 
381         /* put lcid in event data */
382         if (p_dcb != NULL)
383         {
384             mca_dcb_event(p_dcb, MCA_DCB_TC_OPEN_EVT, (tMCA_DCB_EVT *) &open);
385         }
386     }
387 }
388 
389 
390 /*******************************************************************************
391 **
392 ** Function         mca_tc_cong_ind
393 **
394 ** Description      This function is called by the L2CAP interface layer when
395 **                  L2CAP calls the congestion callback.  It looks up the CCB
396 **                  or DCB for the channel and sends it a congestion event.
397 **                  The is_congested parameter is the same value passed by
398 **                  the L2CAP callback function.
399 **
400 **
401 ** Returns          Nothing.
402 **
403 *******************************************************************************/
mca_tc_cong_ind(tMCA_TC_TBL * p_tbl,BOOLEAN is_congested)404 void mca_tc_cong_ind(tMCA_TC_TBL *p_tbl, BOOLEAN is_congested)
405 {
406     tMCA_CCB   *p_ccb;
407     tMCA_DCB   *p_dcb;
408 
409     MCA_TRACE_DEBUG("mca_tc_cong_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx);
410     /* if control channel, notify ccb of congestion */
411     if (p_tbl->tcid == MCA_CTRL_TCID)
412     {
413         p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
414         mca_ccb_event(p_ccb, MCA_CCB_LL_CONG_EVT, (tMCA_CCB_EVT *) &is_congested);
415     }
416     /* notify dcb that channel open */
417     else
418     {
419         /* look up dcb by cb_idx */
420         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
421         if (p_dcb != NULL)
422         {
423             mca_dcb_event(p_dcb, MCA_DCB_TC_CONG_EVT, (tMCA_DCB_EVT *) &is_congested);
424         }
425     }
426 }
427 
428 
429 /*******************************************************************************
430 **
431 ** Function         mca_tc_data_ind
432 **
433 ** Description      This function is called by the L2CAP interface layer when
434 **                  incoming data is received from L2CAP.  It looks up the CCB
435 **                  or DCB for the channel and routes the data accordingly.
436 **
437 ** Returns          Nothing.
438 **
439 *******************************************************************************/
mca_tc_data_ind(tMCA_TC_TBL * p_tbl,BT_HDR * p_buf)440 void mca_tc_data_ind(tMCA_TC_TBL *p_tbl, BT_HDR *p_buf)
441 {
442     tMCA_CCB   *p_ccb;
443     tMCA_DCB   *p_dcb;
444     UINT8       event = MCA_CCB_MSG_RSP_EVT;
445     UINT8       *p;
446     UINT8       rej_rsp_code = MCA_RSP_SUCCESS;
447 
448     MCA_TRACE_DEBUG("mca_tc_data_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx);
449 
450 
451     /* if control channel, handle control message */
452     if (p_tbl->tcid == MCA_CTRL_TCID)
453     {
454         p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
455         if (p_ccb)
456         {
457             p = (UINT8*)(p_buf+1) + p_buf->offset;
458             /* all the request opcode has bit 0 set. response code has bit 0 clear */
459             if ((*p) & 0x01)
460                 event = MCA_CCB_MSG_REQ_EVT;
461 
462             if (*p < MCA_NUM_STANDARD_OPCODE)
463             {
464                 if (p_buf->len != mca_std_msg_len[*p])
465                 {
466                     MCA_TRACE_ERROR ("opcode: %d required len:%d, got len:%d", *p, mca_std_msg_len[*p], p_buf->len);
467                     rej_rsp_code = MCA_RSP_BAD_PARAM;
468                 }
469             }
470             else if ((*p >= MCA_FIRST_SYNC_OP) && (*p <= MCA_LAST_SYNC_OP))
471             {
472                 MCA_TRACE_ERROR ("unsupported SYNC opcode: %d len:%d", *p, p_buf->len);
473                 /* reject unsupported request */
474                 rej_rsp_code = MCA_RSP_NO_SUPPORT;
475             }
476             else
477             {
478                 MCA_TRACE_ERROR ("bad opcode: %d len:%d", *p, p_buf->len);
479                 /* reject unsupported request */
480                 rej_rsp_code = MCA_RSP_BAD_OPCODE;
481             }
482 
483             p_buf->layer_specific = rej_rsp_code;
484             /* forward the request/response to state machine */
485             mca_ccb_event(p_ccb, event, (tMCA_CCB_EVT *) p_buf);
486         } /* got a valid ccb */
487         else
488             GKI_freebuf(p_buf);
489     }
490     /* else send event to dcb */
491     else
492     {
493         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
494         if (p_dcb != NULL)
495         {
496             mca_dcb_event(p_dcb, MCA_DCB_TC_DATA_EVT, (tMCA_DCB_EVT *) p_buf);
497         }
498         else
499             GKI_freebuf(p_buf);
500     }
501 }
502 
503 /*******************************************************************************
504 **
505 ** Function         mca_rcb_alloc
506 **
507 ** Description      This function allocates a registration control block.
508 **                  If no free RCB is available, it returns NULL.
509 **
510 ** Returns          tMCA_RCB *
511 **
512 *******************************************************************************/
mca_rcb_alloc(tMCA_REG * p_reg)513 tMCA_RCB * mca_rcb_alloc(tMCA_REG *p_reg)
514 {
515     int     i;
516     tMCA_RCB *p_rcb = NULL;
517 
518     for (i=0; i<MCA_NUM_REGS; i++)
519     {
520         if (mca_cb.rcb[i].p_cback == NULL)
521         {
522             p_rcb = &mca_cb.rcb[i];
523             memcpy (&p_rcb->reg, p_reg, sizeof(tMCA_REG));
524             break;
525         }
526     }
527     return p_rcb;
528 }
529 
530 /*******************************************************************************
531 **
532 ** Function         mca_rcb_dealloc
533 **
534 ** Description      This function deallocates the RCB with the given handle.
535 **
536 ** Returns          void.
537 **
538 *******************************************************************************/
mca_rcb_dealloc(tMCA_HANDLE handle)539 void mca_rcb_dealloc(tMCA_HANDLE handle)
540 {
541     int      i;
542     BOOLEAN  done = TRUE;
543     tMCA_RCB *p_rcb;
544     tMCA_CCB *p_ccb;
545 
546     if (handle && (handle<=MCA_NUM_REGS))
547     {
548         handle--;
549         p_rcb = &mca_cb.rcb[handle];
550         if (p_rcb->p_cback)
551         {
552             p_ccb = &mca_cb.ccb[handle*MCA_NUM_LINKS];
553             /* check if all associated CCB are disconnected */
554             for (i=0; i<MCA_NUM_LINKS; i++, p_ccb++)
555             {
556                 if (p_ccb->p_rcb)
557                 {
558                     done = FALSE;
559                     mca_ccb_event (p_ccb, MCA_CCB_API_DISCONNECT_EVT, NULL);
560                 }
561             }
562 
563             if (done)
564             {
565                 memset (p_rcb, 0, sizeof(tMCA_RCB));
566                 MCA_TRACE_DEBUG("Reset MCA_RCB index=%d",handle);
567             }
568         }
569     }
570 }
571 
572 /*******************************************************************************
573 **
574 ** Function         mca_rcb_to_handle
575 **
576 ** Description      This function converts a pointer to an RCB to
577 **                  a handle (tMCA_HANDLE).  It returns the handle.
578 **
579 ** Returns          void.
580 **
581 *******************************************************************************/
mca_rcb_to_handle(tMCA_RCB * p_rcb)582 tMCA_HANDLE mca_rcb_to_handle(tMCA_RCB *p_rcb)
583 {
584     return(UINT8) (p_rcb - mca_cb.rcb + 1);
585 }
586 
587 /*******************************************************************************
588 **
589 ** Function         mca_rcb_by_handle
590 **
591 ** Description      This function finds the RCB for a handle (tMCA_HANDLE).
592 **                  It returns a pointer to the RCB.  If no RCB matches the
593 **                  handle it returns NULL.
594 **
595 ** Returns          tMCA_RCB *
596 **
597 *******************************************************************************/
mca_rcb_by_handle(tMCA_HANDLE handle)598 tMCA_RCB *mca_rcb_by_handle(tMCA_HANDLE handle)
599 {
600     tMCA_RCB *p_rcb = NULL;
601 
602     if (handle && (handle<=MCA_NUM_REGS) && mca_cb.rcb[handle-1].p_cback)
603     {
604         p_rcb = &mca_cb.rcb[handle-1];
605     }
606     return p_rcb;
607 }
608 
609 /*******************************************************************************
610 **
611 ** Function         mca_is_valid_dep_id
612 **
613 ** Description      This function checks if the given dep_id is valid.
614 **
615 ** Returns          TRUE, if this is a valid local dep_id
616 **
617 *******************************************************************************/
mca_is_valid_dep_id(tMCA_RCB * p_rcb,tMCA_DEP dep)618 BOOLEAN mca_is_valid_dep_id(tMCA_RCB *p_rcb, tMCA_DEP dep)
619 {
620     BOOLEAN valid = FALSE;
621     if (dep < MCA_NUM_DEPS && p_rcb->dep[dep].p_data_cback)
622     {
623         valid = TRUE;
624     }
625     return valid;
626 }
627 
628 /*******************************************************************************
629 **
630 ** Function         mca_free_buf
631 **
632 ** Description      free memory for specified GKI packet
633 **
634 ** Returns          void
635 **
636 *******************************************************************************/
mca_free_buf(void ** p_buf)637 void mca_free_buf (void **p_buf)
638 {
639     if (p_buf && *p_buf)
640     {
641         GKI_freebuf(*p_buf);
642         *p_buf = NULL;
643     }
644 }
645