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 <assert.h>
26 #include <string.h>
27 
28 #include "bt_target.h"
29 #include "gki.h"
30 #include "mca_api.h"
31 #include "mca_defs.h"
32 #include "mca_int.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     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 
149     MCA_TRACE_DEBUG("%s() - cb_idx: %d", __func__, p_tbl->cb_idx);
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     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 
190     MCA_TRACE_DEBUG("%s() - tcid: %d, cb_idx: %d", __func__, p_tbl->tcid, p_tbl->cb_idx);
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         if (p_dcb)
269         {
270             p_opt = &p_dcb->p_chnl_cfg->fcr_opt;
271             fcs   = p_dcb->p_chnl_cfg->fcs;
272         }
273     }
274     memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO));
275     p_cfg->mtu_present = TRUE;
276     p_cfg->mtu = p_tbl->my_mtu;
277     p_cfg->fcr_present = TRUE;
278     memcpy(&p_cfg->fcr, p_opt, sizeof (tL2CAP_FCR_OPTS));
279     if (fcs & MCA_FCS_PRESNT_MASK)
280     {
281         p_cfg->fcs_present = TRUE;
282         p_cfg->fcs = (fcs & MCA_FCS_USE_MASK);
283     }
284 }
285 
286 /*******************************************************************************
287 **
288 ** Function         mca_tc_close_ind
289 **
290 ** Description      This function is called by the L2CAP interface when the
291 **                  L2CAP channel is closed.  It looks up the CCB or DCB for
292 **                  the channel and sends it a close event.  The reason
293 **                  parameter is the same value passed by the L2CAP
294 **                  callback function.
295 **
296 ** Returns          Nothing.
297 **
298 *******************************************************************************/
mca_tc_close_ind(tMCA_TC_TBL * p_tbl,UINT16 reason)299 void mca_tc_close_ind(tMCA_TC_TBL *p_tbl, UINT16 reason)
300 {
301     tMCA_CCB   *p_ccb;
302     tMCA_DCB   *p_dcb;
303     tMCA_CLOSE  close;
304 
305     close.param  = MCA_ACP;
306     close.reason = reason;
307     close.lcid   = p_tbl->lcid;
308 
309     MCA_TRACE_DEBUG("%s() - tcid: %d, cb_idx:%d, old: %d", __func__,
310                      p_tbl->tcid, p_tbl->cb_idx, p_tbl->state);
311 
312     /* Check if the transport channel is in use */
313     if (p_tbl->state == MCA_TC_ST_UNUSED)
314         return;
315 
316     /* clear mca_tc_tbl entry */
317     if (p_tbl->cfg_flags&MCA_L2C_CFG_DISCN_INT)
318         close.param = MCA_INT;
319     p_tbl->cfg_flags = 0;
320     p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
321 
322     /* if control channel, notify ccb that channel close */
323     if (p_tbl->tcid == MCA_CTRL_TCID)
324     {
325         p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
326         mca_ccb_event(p_ccb, MCA_CCB_LL_CLOSE_EVT, (tMCA_CCB_EVT *)&close);
327     }
328     /* notify dcb that channel close */
329     else
330     {
331         /* look up dcb  */
332         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
333         if (p_dcb != NULL)
334         {
335             mca_dcb_event(p_dcb, MCA_DCB_TC_CLOSE_EVT, (tMCA_DCB_EVT *) &close);
336         }
337     }
338     p_tbl->state = MCA_TC_ST_UNUSED;
339 }
340 
341 /*******************************************************************************
342 **
343 ** Function         mca_tc_open_ind
344 **
345 ** Description      This function is called by the L2CAP interface when
346 **                  the L2CAP channel is opened.  It looks up the CCB or DCB
347 **                  for the channel and sends it an open event.
348 **
349 ** Returns          Nothing.
350 **
351 *******************************************************************************/
mca_tc_open_ind(tMCA_TC_TBL * p_tbl)352 void mca_tc_open_ind(tMCA_TC_TBL *p_tbl)
353 {
354     tMCA_CCB   *p_ccb;
355     tMCA_DCB   *p_dcb;
356     tMCA_OPEN  open;
357 
358     MCA_TRACE_DEBUG("mca_tc_open_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx);
359     p_tbl->state = MCA_TC_ST_OPEN;
360 
361     open.peer_mtu = p_tbl->peer_mtu;
362     open.lcid = p_tbl->lcid;
363     /* use param to indicate the role of connection.
364      * MCA_ACP, if ACP */
365     open.param = MCA_INT;
366     if (p_tbl->cfg_flags & MCA_L2C_CFG_CONN_ACP)
367     {
368         open.param = MCA_ACP;
369     }
370 
371     /* if control channel, notify ccb that channel open */
372     if (p_tbl->tcid == MCA_CTRL_TCID)
373     {
374         p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
375 
376         mca_ccb_event(p_ccb, MCA_CCB_LL_OPEN_EVT, (tMCA_CCB_EVT *)&open);
377     }
378     /* must be data channel, notify dcb that channel open */
379     else
380     {
381         /* look up dcb */
382         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
383 
384         /* put lcid in event data */
385         if (p_dcb != NULL)
386         {
387             mca_dcb_event(p_dcb, MCA_DCB_TC_OPEN_EVT, (tMCA_DCB_EVT *) &open);
388         }
389     }
390 }
391 
392 
393 /*******************************************************************************
394 **
395 ** Function         mca_tc_cong_ind
396 **
397 ** Description      This function is called by the L2CAP interface layer when
398 **                  L2CAP calls the congestion callback.  It looks up the CCB
399 **                  or DCB for the channel and sends it a congestion event.
400 **                  The is_congested parameter is the same value passed by
401 **                  the L2CAP callback function.
402 **
403 **
404 ** Returns          Nothing.
405 **
406 *******************************************************************************/
mca_tc_cong_ind(tMCA_TC_TBL * p_tbl,BOOLEAN is_congested)407 void mca_tc_cong_ind(tMCA_TC_TBL *p_tbl, BOOLEAN is_congested)
408 {
409     tMCA_CCB   *p_ccb;
410     tMCA_DCB   *p_dcb;
411 
412     MCA_TRACE_DEBUG("%s() - tcid: %d, cb_idx: %d", __func__, p_tbl->tcid, p_tbl->cb_idx);
413 
414     /* if control channel, notify ccb of congestion */
415     if (p_tbl->tcid == MCA_CTRL_TCID)
416     {
417         p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
418         mca_ccb_event(p_ccb, MCA_CCB_LL_CONG_EVT, (tMCA_CCB_EVT *) &is_congested);
419     }
420     /* notify dcb that channel open */
421     else
422     {
423         /* look up dcb by cb_idx */
424         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
425         if (p_dcb != NULL)
426         {
427             mca_dcb_event(p_dcb, MCA_DCB_TC_CONG_EVT, (tMCA_DCB_EVT *) &is_congested);
428         }
429     }
430 }
431 
432 
433 /*******************************************************************************
434 **
435 ** Function         mca_tc_data_ind
436 **
437 ** Description      This function is called by the L2CAP interface layer when
438 **                  incoming data is received from L2CAP.  It looks up the CCB
439 **                  or DCB for the channel and routes the data accordingly.
440 **
441 ** Returns          Nothing.
442 **
443 *******************************************************************************/
mca_tc_data_ind(tMCA_TC_TBL * p_tbl,BT_HDR * p_buf)444 void mca_tc_data_ind(tMCA_TC_TBL *p_tbl, BT_HDR *p_buf)
445 {
446     tMCA_CCB   *p_ccb;
447     tMCA_DCB   *p_dcb;
448     UINT8       event = MCA_CCB_MSG_RSP_EVT;
449     UINT8       *p;
450     UINT8       rej_rsp_code = MCA_RSP_SUCCESS;
451 
452     MCA_TRACE_DEBUG("%s() - tcid: %d, cb_idx: %d", __func__, p_tbl->tcid, p_tbl->cb_idx);
453 
454     /* if control channel, handle control message */
455     if (p_tbl->tcid == MCA_CTRL_TCID)
456     {
457         p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
458         if (p_ccb)
459         {
460             p = (UINT8*)(p_buf+1) + p_buf->offset;
461             /* all the request opcode has bit 0 set. response code has bit 0 clear */
462             if ((*p) & 0x01)
463                 event = MCA_CCB_MSG_REQ_EVT;
464 
465             if (*p < MCA_NUM_STANDARD_OPCODE)
466             {
467                 if (p_buf->len != mca_std_msg_len[*p])
468                 {
469                     MCA_TRACE_ERROR ("$s() - opcode: %d required len: %d, got len: %d"
470                       , __func__, *p, mca_std_msg_len[*p], p_buf->len);
471                     rej_rsp_code = MCA_RSP_BAD_PARAM;
472                 }
473             }
474             else if ((*p >= MCA_FIRST_SYNC_OP) && (*p <= MCA_LAST_SYNC_OP))
475             {
476                 MCA_TRACE_ERROR ("%s() - unsupported SYNC opcode: %d len:%d"
477                     , __func__, *p, p_buf->len);
478                 /* reject unsupported request */
479                 rej_rsp_code = MCA_RSP_NO_SUPPORT;
480             }
481             else
482             {
483                 MCA_TRACE_ERROR ("%s() - bad opcode: %d len:%d", __func__, *p, p_buf->len);
484                 /* reject unsupported request */
485                 rej_rsp_code = MCA_RSP_BAD_OPCODE;
486             }
487 
488             p_buf->layer_specific = rej_rsp_code;
489             /* forward the request/response to state machine */
490             mca_ccb_event(p_ccb, event, (tMCA_CCB_EVT *) p_buf);
491         } /* got a valid ccb */
492         else
493             GKI_freebuf(p_buf);
494     }
495     /* else send event to dcb */
496     else
497     {
498         p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx);
499         if (p_dcb != NULL)
500         {
501             mca_dcb_event(p_dcb, MCA_DCB_TC_DATA_EVT, (tMCA_DCB_EVT *) p_buf);
502         }
503         else
504             GKI_freebuf(p_buf);
505     }
506 }
507 
508 /*******************************************************************************
509 **
510 ** Function         mca_rcb_alloc
511 **
512 ** Description      This function allocates a registration control block.
513 **                  If no free RCB is available, it returns NULL.
514 **
515 ** Returns          tMCA_RCB *
516 **
517 *******************************************************************************/
mca_rcb_alloc(tMCA_REG * p_reg)518 tMCA_RCB * mca_rcb_alloc(tMCA_REG *p_reg)
519 {
520     int     i;
521     tMCA_RCB *p_rcb = NULL;
522 
523     for (i=0; i<MCA_NUM_REGS; i++)
524     {
525         if (mca_cb.rcb[i].p_cback == NULL)
526         {
527             p_rcb = &mca_cb.rcb[i];
528             memcpy (&p_rcb->reg, p_reg, sizeof(tMCA_REG));
529             break;
530         }
531     }
532     return p_rcb;
533 }
534 
535 /*******************************************************************************
536 **
537 ** Function         mca_rcb_dealloc
538 **
539 ** Description      This function deallocates the RCB with the given handle.
540 **
541 ** Returns          void.
542 **
543 *******************************************************************************/
mca_rcb_dealloc(tMCA_HANDLE handle)544 void mca_rcb_dealloc(tMCA_HANDLE handle)
545 {
546     int      i;
547     BOOLEAN  done = TRUE;
548     tMCA_RCB *p_rcb;
549     tMCA_CCB *p_ccb;
550 
551     if (handle && (handle<=MCA_NUM_REGS))
552     {
553         handle--;
554         p_rcb = &mca_cb.rcb[handle];
555         if (p_rcb->p_cback)
556         {
557             p_ccb = &mca_cb.ccb[handle*MCA_NUM_LINKS];
558             /* check if all associated CCB are disconnected */
559             for (i=0; i<MCA_NUM_LINKS; i++, p_ccb++)
560             {
561                 if (p_ccb->p_rcb)
562                 {
563                     done = FALSE;
564                     mca_ccb_event (p_ccb, MCA_CCB_API_DISCONNECT_EVT, NULL);
565                 }
566             }
567 
568             if (done)
569             {
570                 memset (p_rcb, 0, sizeof(tMCA_RCB));
571                 MCA_TRACE_DEBUG("%s() - reset MCA_RCB index=%d", __func__, handle);
572             }
573         }
574     }
575 }
576 
577 /*******************************************************************************
578 **
579 ** Function         mca_rcb_to_handle
580 **
581 ** Description      This function converts a pointer to an RCB to
582 **                  a handle (tMCA_HANDLE).  It returns the handle.
583 **
584 ** Returns          void.
585 **
586 *******************************************************************************/
mca_rcb_to_handle(tMCA_RCB * p_rcb)587 tMCA_HANDLE mca_rcb_to_handle(tMCA_RCB *p_rcb)
588 {
589     return(UINT8) (p_rcb - mca_cb.rcb + 1);
590 }
591 
592 /*******************************************************************************
593 **
594 ** Function         mca_rcb_by_handle
595 **
596 ** Description      This function finds the RCB for a handle (tMCA_HANDLE).
597 **                  It returns a pointer to the RCB.  If no RCB matches the
598 **                  handle it returns NULL.
599 **
600 ** Returns          tMCA_RCB *
601 **
602 *******************************************************************************/
mca_rcb_by_handle(tMCA_HANDLE handle)603 tMCA_RCB *mca_rcb_by_handle(tMCA_HANDLE handle)
604 {
605     tMCA_RCB *p_rcb = NULL;
606 
607     if (handle && (handle<=MCA_NUM_REGS) && mca_cb.rcb[handle-1].p_cback)
608     {
609         p_rcb = &mca_cb.rcb[handle-1];
610     }
611     return p_rcb;
612 }
613 
614 /*******************************************************************************
615 **
616 ** Function         mca_is_valid_dep_id
617 **
618 ** Description      This function checks if the given dep_id is valid.
619 **
620 ** Returns          TRUE, if this is a valid local dep_id
621 **
622 *******************************************************************************/
mca_is_valid_dep_id(tMCA_RCB * p_rcb,tMCA_DEP dep)623 BOOLEAN mca_is_valid_dep_id(tMCA_RCB *p_rcb, tMCA_DEP dep)
624 {
625     BOOLEAN valid = FALSE;
626     if (dep < MCA_NUM_DEPS && p_rcb->dep[dep].p_data_cback)
627     {
628         valid = TRUE;
629     }
630     return valid;
631 }
632 
633 /*******************************************************************************
634 **
635 ** Function         mca_free_buf
636 **
637 ** Description      free memory for specified GKI packet
638 **
639 ** Returns          void
640 **
641 *******************************************************************************/
mca_free_buf(void ** p_buf)642 void mca_free_buf (void **p_buf)
643 {
644     if (p_buf && *p_buf)
645     {
646         GKI_freebuf(*p_buf);
647         *p_buf = NULL;
648     }
649 }
650