Home | History | Annotate | Download | only in av
      1 /******************************************************************************
      2  *
      3  *  Copyright (C) 2011-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 of the API for the advanced audio/video (AV)
     22  *  subsystem of BTA, Broadcom's Bluetooth application layer for mobile
     23  *  phones.
     24  *
     25  ******************************************************************************/
     26 
     27 #include "bt_target.h"
     28 #if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE)
     29 
     30 #include "bta_api.h"
     31 #include "bta_sys.h"
     32 #include "bta_av_api.h"
     33 #include "bta_av_int.h"
     34 #include "gki.h"
     35 #include <string.h>
     36 
     37 /*****************************************************************************
     38 **  Constants
     39 *****************************************************************************/
     40 
     41 static const tBTA_SYS_REG bta_av_reg =
     42 {
     43     bta_av_hdl_event,
     44     BTA_AvDisable
     45 };
     46 
     47 /*******************************************************************************
     48 **
     49 ** Function         BTA_AvEnable
     50 **
     51 ** Description      Enable the advanced audio/video service. When the enable
     52 **                  operation is complete the callback function will be
     53 **                  called with a BTA_AV_ENABLE_EVT. This function must
     54 **                  be called before other function in the AV API are
     55 **                  called.
     56 **
     57 ** Returns          void
     58 **
     59 *******************************************************************************/
     60 void BTA_AvEnable(tBTA_SEC sec_mask, tBTA_AV_FEAT features, tBTA_AV_CBACK *p_cback)
     61 {
     62     tBTA_AV_API_ENABLE  *p_buf;
     63 
     64     /* register with BTA system manager */
     65     bta_sys_register(BTA_ID_AV, &bta_av_reg);
     66 
     67     if ((p_buf = (tBTA_AV_API_ENABLE *) GKI_getbuf(sizeof(tBTA_AV_API_ENABLE))) != NULL)
     68     {
     69         p_buf->hdr.event = BTA_AV_API_ENABLE_EVT;
     70         p_buf->p_cback  = p_cback;
     71         p_buf->features = features;
     72         p_buf->sec_mask = sec_mask;
     73         bta_sys_sendmsg(p_buf);
     74     }
     75 }
     76 
     77 /*******************************************************************************
     78 **
     79 ** Function         BTA_AvDisable
     80 **
     81 ** Description      Disable the advanced audio/video service.
     82 **
     83 ** Returns          void
     84 **
     85 *******************************************************************************/
     86 void BTA_AvDisable(void)
     87 {
     88     BT_HDR  *p_buf;
     89 
     90     bta_sys_deregister(BTA_ID_AV);
     91     if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
     92     {
     93         p_buf->event = BTA_AV_API_DISABLE_EVT;
     94         bta_sys_sendmsg(p_buf);
     95     }
     96 }
     97 
     98 /*******************************************************************************
     99 **
    100 ** Function         BTA_AvRegister
    101 **
    102 ** Description      Register the audio or video service to stack. When the
    103 **                  operation is complete the callback function will be
    104 **                  called with a BTA_AV_REGISTER_EVT. This function must
    105 **                  be called before AVDT stream is open.
    106 **
    107 **
    108 ** Returns          void
    109 **
    110 *******************************************************************************/
    111 void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id, tBTA_AV_DATA_CBACK  *p_data_cback)
    112 {
    113     tBTA_AV_API_REG  *p_buf;
    114 
    115 
    116     if ((p_buf = (tBTA_AV_API_REG *) GKI_getbuf(sizeof(tBTA_AV_API_REG))) != NULL)
    117     {
    118         p_buf->hdr.layer_specific   = chnl;
    119         p_buf->hdr.event = BTA_AV_API_REGISTER_EVT;
    120         if(p_service_name)
    121         {
    122             BCM_STRNCPY_S(p_buf->p_service_name, sizeof(p_buf->p_service_name), p_service_name, BTA_SERVICE_NAME_LEN);
    123             p_buf->p_service_name[BTA_SERVICE_NAME_LEN-1] = 0;
    124         }
    125         else
    126         {
    127             p_buf->p_service_name[0] = 0;
    128         }
    129         p_buf->app_id = app_id;
    130         p_buf->p_app_data_cback = p_data_cback;
    131         bta_sys_sendmsg(p_buf);
    132     }
    133 }
    134 
    135 /*******************************************************************************
    136 **
    137 ** Function         BTA_AvDeregister
    138 **
    139 ** Description      Deregister the audio or video service
    140 **
    141 ** Returns          void
    142 **
    143 *******************************************************************************/
    144 void BTA_AvDeregister(tBTA_AV_HNDL hndl)
    145 {
    146     BT_HDR  *p_buf;
    147 
    148     if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
    149     {
    150         p_buf->layer_specific   = hndl;
    151         p_buf->event = BTA_AV_API_DEREGISTER_EVT;
    152         bta_sys_sendmsg(p_buf);
    153     }
    154 }
    155 
    156 /*******************************************************************************
    157 **
    158 ** Function         BTA_AvOpen
    159 **
    160 ** Description      Opens an advanced audio/video connection to a peer device.
    161 **                  When connection is open callback function is called
    162 **                  with a BTA_AV_OPEN_EVT.
    163 **
    164 ** Returns          void
    165 **
    166 *******************************************************************************/
    167 void BTA_AvOpen(BD_ADDR bd_addr, tBTA_AV_HNDL handle, BOOLEAN use_rc, tBTA_SEC sec_mask,
    168                                                                              UINT16 uuid)
    169 {
    170     tBTA_AV_API_OPEN  *p_buf;
    171 
    172     if ((p_buf = (tBTA_AV_API_OPEN *) GKI_getbuf(sizeof(tBTA_AV_API_OPEN))) != NULL)
    173     {
    174         p_buf->hdr.event = BTA_AV_API_OPEN_EVT;
    175         p_buf->hdr.layer_specific   = handle;
    176         bdcpy(p_buf->bd_addr, bd_addr);
    177         p_buf->use_rc = use_rc;
    178         p_buf->sec_mask = sec_mask;
    179         p_buf->switch_res = BTA_AV_RS_NONE;
    180         p_buf->uuid = uuid;
    181         bta_sys_sendmsg(p_buf);
    182     }
    183 }
    184 
    185 /*******************************************************************************
    186 **
    187 ** Function         BTA_AvClose
    188 **
    189 ** Description      Close the current streams.
    190 **
    191 ** Returns          void
    192 **
    193 *******************************************************************************/
    194 void BTA_AvClose(tBTA_AV_HNDL handle)
    195 {
    196     BT_HDR  *p_buf;
    197 
    198     if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
    199     {
    200         p_buf->event = BTA_AV_API_CLOSE_EVT;
    201         p_buf->layer_specific   = handle;
    202         bta_sys_sendmsg(p_buf);
    203     }
    204 }
    205 
    206 /*******************************************************************************
    207 **
    208 ** Function         BTA_AvDisconnect
    209 **
    210 ** Description      Close the connection to the address.
    211 **
    212 ** Returns          void
    213 **
    214 *******************************************************************************/
    215 void BTA_AvDisconnect(BD_ADDR bd_addr)
    216 {
    217     tBTA_AV_API_DISCNT  *p_buf;
    218 
    219     if ((p_buf = (tBTA_AV_API_DISCNT *) GKI_getbuf(sizeof(tBTA_AV_API_DISCNT))) != NULL)
    220     {
    221         p_buf->hdr.event = BTA_AV_API_DISCONNECT_EVT;
    222         bdcpy(p_buf->bd_addr, bd_addr);
    223         bta_sys_sendmsg(p_buf);
    224     }
    225 }
    226 
    227 /*******************************************************************************
    228 **
    229 ** Function         BTA_AvStart
    230 **
    231 ** Description      Start audio/video stream data transfer.
    232 **
    233 ** Returns          void
    234 **
    235 *******************************************************************************/
    236 void BTA_AvStart(void)
    237 {
    238     BT_HDR  *p_buf;
    239 
    240     if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
    241     {
    242         p_buf->event = BTA_AV_API_START_EVT;
    243         bta_sys_sendmsg(p_buf);
    244     }
    245 }
    246 
    247 /*******************************************************************************
    248 **
    249 ** Function         BTA_AvEnable_Sink
    250 **
    251 ** Description      Enable/Disable A2DP Sink..
    252 **
    253 ** Returns          void
    254 **
    255 *******************************************************************************/
    256 void BTA_AvEnable_Sink(int enable)
    257 {
    258 #if (BTA_AV_SINK_INCLUDED == TRUE)
    259     BT_HDR  *p_buf;
    260     if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
    261     {
    262         p_buf->event = BTA_AV_API_SINK_ENABLE_EVT;
    263         p_buf->layer_specific = enable;
    264         bta_sys_sendmsg(p_buf);
    265     }
    266 #else
    267     return;
    268 #endif
    269 }
    270 
    271 /*******************************************************************************
    272 **
    273 ** Function         BTA_AvStop
    274 **
    275 ** Description      Stop audio/video stream data transfer.
    276 **                  If suspend is TRUE, this function sends AVDT suspend signal
    277 **                  to the connected peer(s).
    278 **
    279 ** Returns          void
    280 **
    281 *******************************************************************************/
    282 void BTA_AvStop(BOOLEAN suspend)
    283 {
    284     tBTA_AV_API_STOP  *p_buf;
    285 
    286     if ((p_buf = (tBTA_AV_API_STOP *) GKI_getbuf(sizeof(tBTA_AV_API_STOP))) != NULL)
    287     {
    288         p_buf->hdr.event = BTA_AV_API_STOP_EVT;
    289         p_buf->flush   = TRUE;
    290         p_buf->suspend = suspend;
    291         bta_sys_sendmsg(p_buf);
    292     }
    293 }
    294 
    295 /*******************************************************************************
    296 **
    297 ** Function         BTA_AvReconfig
    298 **
    299 ** Description      Reconfigure the audio/video stream.
    300 **                  If suspend is TRUE, this function tries the suspend/reconfigure
    301 **                  procedure first.
    302 **                  If suspend is FALSE or when suspend/reconfigure fails,
    303 **                  this function closes and re-opens the AVDT connection.
    304 **
    305 ** Returns          void
    306 **
    307 *******************************************************************************/
    308 void BTA_AvReconfig(tBTA_AV_HNDL hndl, BOOLEAN suspend, UINT8 sep_info_idx,
    309                     UINT8 *p_codec_info, UINT8 num_protect, UINT8 *p_protect_info)
    310 {
    311     tBTA_AV_API_RCFG  *p_buf;
    312 
    313     if ((p_buf = (tBTA_AV_API_RCFG *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_API_RCFG) + num_protect))) != NULL)
    314     {
    315         p_buf->hdr.layer_specific   = hndl;
    316         p_buf->hdr.event    = BTA_AV_API_RECONFIG_EVT;
    317         p_buf->num_protect  = num_protect;
    318         p_buf->suspend      = suspend;
    319         p_buf->sep_info_idx = sep_info_idx;
    320         p_buf->p_protect_info = (UINT8 *)(p_buf + 1);
    321         memcpy(p_buf->codec_info, p_codec_info, AVDT_CODEC_SIZE);
    322         memcpy(p_buf->p_protect_info, p_protect_info, num_protect);
    323         bta_sys_sendmsg(p_buf);
    324     }
    325 }
    326 
    327 /*******************************************************************************
    328 **
    329 ** Function         BTA_AvProtectReq
    330 **
    331 ** Description      Send a content protection request.  This function can only
    332 **                  be used if AV is enabled with feature BTA_AV_FEAT_PROTECT.
    333 **
    334 ** Returns          void
    335 **
    336 *******************************************************************************/
    337 void BTA_AvProtectReq(tBTA_AV_HNDL hndl, UINT8 *p_data, UINT16 len)
    338 {
    339     tBTA_AV_API_PROTECT_REQ  *p_buf;
    340 
    341     if ((p_buf = (tBTA_AV_API_PROTECT_REQ *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_API_PROTECT_REQ) + len))) != NULL)
    342     {
    343         p_buf->hdr.layer_specific   = hndl;
    344         p_buf->hdr.event = BTA_AV_API_PROTECT_REQ_EVT;
    345         p_buf->len       = len;
    346         if (p_data == NULL)
    347         {
    348             p_buf->p_data = NULL;
    349         }
    350         else
    351         {
    352             p_buf->p_data = (UINT8 *) (p_buf + 1);
    353             memcpy(p_buf->p_data, p_data, len);
    354         }
    355         bta_sys_sendmsg(p_buf);
    356     }
    357 }
    358 
    359 /*******************************************************************************
    360 **
    361 ** Function         BTA_AvProtectRsp
    362 **
    363 ** Description      Send a content protection response.  This function must
    364 **                  be called if a BTA_AV_PROTECT_REQ_EVT is received.
    365 **                  This function can only be used if AV is enabled with
    366 **                  feature BTA_AV_FEAT_PROTECT.
    367 **
    368 ** Returns          void
    369 **
    370 *******************************************************************************/
    371 void BTA_AvProtectRsp(tBTA_AV_HNDL hndl, UINT8 error_code, UINT8 *p_data, UINT16 len)
    372 {
    373     tBTA_AV_API_PROTECT_RSP  *p_buf;
    374 
    375     if ((p_buf = (tBTA_AV_API_PROTECT_RSP *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_API_PROTECT_RSP) + len))) != NULL)
    376     {
    377         p_buf->hdr.layer_specific   = hndl;
    378         p_buf->hdr.event    = BTA_AV_API_PROTECT_RSP_EVT;
    379         p_buf->len          = len;
    380         p_buf->error_code   = error_code;
    381         if (p_data == NULL)
    382         {
    383             p_buf->p_data = NULL;
    384         }
    385         else
    386         {
    387             p_buf->p_data = (UINT8 *) (p_buf + 1);
    388             memcpy(p_buf->p_data, p_data, len);
    389         }
    390         bta_sys_sendmsg(p_buf);
    391     }
    392 }
    393 
    394 /*******************************************************************************
    395 **
    396 ** Function         BTA_AvRemoteCmd
    397 **
    398 ** Description      Send a remote control command.  This function can only
    399 **                  be used if AV is enabled with feature BTA_AV_FEAT_RCCT.
    400 **
    401 ** Returns          void
    402 **
    403 *******************************************************************************/
    404 void BTA_AvRemoteCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_RC rc_id, tBTA_AV_STATE key_state)
    405 {
    406     tBTA_AV_API_REMOTE_CMD  *p_buf;
    407 
    408     if ((p_buf = (tBTA_AV_API_REMOTE_CMD *) GKI_getbuf(sizeof(tBTA_AV_API_REMOTE_CMD))) != NULL)
    409     {
    410         p_buf->hdr.event = BTA_AV_API_REMOTE_CMD_EVT;
    411         p_buf->hdr.layer_specific   = rc_handle;
    412         p_buf->msg.op_id = rc_id;
    413         p_buf->msg.state = key_state;
    414         p_buf->msg.p_pass_data = NULL;
    415         p_buf->msg.pass_len = 0;
    416         p_buf->label = label;
    417         bta_sys_sendmsg(p_buf);
    418     }
    419 }
    420 
    421 /*******************************************************************************
    422 **
    423 ** Function         BTA_AvVendorCmd
    424 **
    425 ** Description      Send a vendor dependent remote control command.  This
    426 **                  function can only be used if AV is enabled with feature
    427 **                  BTA_AV_FEAT_VENDOR.
    428 **
    429 ** Returns          void
    430 **
    431 *******************************************************************************/
    432 void BTA_AvVendorCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE cmd_code, UINT8 *p_data, UINT16 len)
    433 {
    434     tBTA_AV_API_VENDOR  *p_buf;
    435 
    436     if ((p_buf = (tBTA_AV_API_VENDOR *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_API_VENDOR) + len))) != NULL)
    437     {
    438         p_buf->hdr.event = BTA_AV_API_VENDOR_CMD_EVT;
    439         p_buf->hdr.layer_specific   = rc_handle;
    440         p_buf->msg.hdr.ctype = cmd_code;
    441         p_buf->msg.hdr.subunit_type = AVRC_SUB_PANEL;
    442         p_buf->msg.hdr.subunit_id = 0;
    443         p_buf->msg.company_id = p_bta_av_cfg->company_id;
    444         p_buf->label = label;
    445         p_buf->msg.vendor_len = len;
    446         if (p_data == NULL)
    447         {
    448             p_buf->msg.p_vendor_data = NULL;
    449         }
    450         else
    451         {
    452             p_buf->msg.p_vendor_data = (UINT8 *) (p_buf + 1);
    453             memcpy(p_buf->msg.p_vendor_data, p_data, len);
    454         }
    455         bta_sys_sendmsg(p_buf);
    456     }
    457 }
    458 
    459 /*******************************************************************************
    460 **
    461 ** Function         BTA_AvVendorRsp
    462 **
    463 ** Description      Send a vendor dependent remote control response.
    464 **                  This function must be called if a BTA_AV_VENDOR_CMD_EVT
    465 **                  is received. This function can only be used if AV is
    466 **                  enabled with feature BTA_AV_FEAT_VENDOR.
    467 **
    468 ** Returns          void
    469 **
    470 *******************************************************************************/
    471 void BTA_AvVendorRsp(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE rsp_code, UINT8 *p_data, UINT16 len, UINT32 company_id)
    472 {
    473     tBTA_AV_API_VENDOR  *p_buf;
    474 
    475     if ((p_buf = (tBTA_AV_API_VENDOR *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_API_VENDOR) + len))) != NULL)
    476     {
    477         p_buf->hdr.event = BTA_AV_API_VENDOR_RSP_EVT;
    478         p_buf->hdr.layer_specific   = rc_handle;
    479         p_buf->msg.hdr.ctype = rsp_code;
    480         p_buf->msg.hdr.subunit_type = AVRC_SUB_PANEL;
    481         p_buf->msg.hdr.subunit_id = 0;
    482         if(company_id)
    483             p_buf->msg.company_id = company_id;
    484         else
    485             p_buf->msg.company_id = p_bta_av_cfg->company_id;
    486         p_buf->label = label;
    487         p_buf->msg.vendor_len = len;
    488         if (p_data == NULL)
    489         {
    490             p_buf->msg.p_vendor_data = NULL;
    491         }
    492         else
    493         {
    494             p_buf->msg.p_vendor_data = (UINT8 *) (p_buf + 1);
    495             memcpy(p_buf->msg.p_vendor_data, p_data, len);
    496         }
    497         bta_sys_sendmsg(p_buf);
    498     }
    499 }
    500 
    501 /*******************************************************************************
    502 **
    503 ** Function         BTA_AvOpenRc
    504 **
    505 ** Description      Open an AVRCP connection toward the device with the
    506 **                  specified handle
    507 **
    508 ** Returns          void
    509 **
    510 *******************************************************************************/
    511 void BTA_AvOpenRc(tBTA_AV_HNDL handle)
    512 {
    513     tBTA_AV_API_OPEN_RC  *p_buf;
    514 
    515     if ((p_buf = (tBTA_AV_API_OPEN_RC *) GKI_getbuf(sizeof(tBTA_AV_API_OPEN_RC))) != NULL)
    516     {
    517         p_buf->hdr.event = BTA_AV_API_RC_OPEN_EVT;
    518         p_buf->hdr.layer_specific   = handle;
    519         bta_sys_sendmsg(p_buf);
    520     }
    521 }
    522 
    523 /*******************************************************************************
    524 **
    525 ** Function         BTA_AvCloseRc
    526 **
    527 ** Description      Close an AVRCP connection
    528 **
    529 ** Returns          void
    530 **
    531 *******************************************************************************/
    532 void BTA_AvCloseRc(UINT8 rc_handle)
    533 {
    534     tBTA_AV_API_CLOSE_RC  *p_buf;
    535 
    536     if ((p_buf = (tBTA_AV_API_CLOSE_RC *) GKI_getbuf(sizeof(tBTA_AV_API_CLOSE_RC))) != NULL)
    537     {
    538         p_buf->hdr.event = BTA_AV_API_RC_CLOSE_EVT;
    539         p_buf->hdr.layer_specific   = rc_handle;
    540         bta_sys_sendmsg(p_buf);
    541     }
    542 }
    543 
    544 /*******************************************************************************
    545 **
    546 ** Function         BTA_AvMetaRsp
    547 **
    548 ** Description      Send a Metadata/Advanced Control response. The message contained
    549 **                  in p_pkt can be composed with AVRC utility functions.
    550 **                  This function can only be used if AV is enabled with feature
    551 **                  BTA_AV_FEAT_METADATA.
    552 **
    553 ** Returns          void
    554 **
    555 *******************************************************************************/
    556 void BTA_AvMetaRsp(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE rsp_code,
    557                                BT_HDR *p_pkt)
    558 {
    559     tBTA_AV_API_META_RSP  *p_buf;
    560 
    561     if ((p_buf = (tBTA_AV_API_META_RSP *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_API_META_RSP)))) != NULL)
    562     {
    563         p_buf->hdr.event = BTA_AV_API_META_RSP_EVT;
    564         p_buf->hdr.layer_specific   = rc_handle;
    565         p_buf->rsp_code = rsp_code;
    566         p_buf->p_pkt = p_pkt;
    567         p_buf->is_rsp = TRUE;
    568         p_buf->label = label;
    569 
    570         bta_sys_sendmsg(p_buf);
    571     } else if (p_pkt) {
    572         GKI_freebuf(p_pkt);
    573     }
    574 }
    575 
    576 /*******************************************************************************
    577 **
    578 ** Function         BTA_AvMetaCmd
    579 **
    580 ** Description      Send a Metadata/Advanced Control command. The message contained
    581 **                  in p_pkt can be composed with AVRC utility functions.
    582 **                  This function can only be used if AV is enabled with feature
    583 **                  BTA_AV_FEAT_METADATA.
    584 **                  This message is sent only when the peer supports the TG role.
    585 *8                  The only command makes sense right now is the absolute volume command.
    586 **
    587 ** Returns          void
    588 **
    589 *******************************************************************************/
    590 void BTA_AvMetaCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CMD cmd_code, BT_HDR *p_pkt)
    591 {
    592     tBTA_AV_API_META_RSP  *p_buf;
    593 
    594     if ((p_buf = (tBTA_AV_API_META_RSP *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_API_META_RSP)))) != NULL)
    595     {
    596         p_buf->hdr.event = BTA_AV_API_META_RSP_EVT;
    597         p_buf->hdr.layer_specific   = rc_handle;
    598         p_buf->p_pkt = p_pkt;
    599         p_buf->rsp_code = cmd_code;
    600         p_buf->is_rsp = FALSE;
    601         p_buf->label = label;
    602 
    603         bta_sys_sendmsg(p_buf);
    604     }
    605 }
    606 
    607 #endif /* BTA_AV_INCLUDED */
    608