39d8477c创建于 2023年6月27日历史提交
/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "rfcomm_defs.h"

static List *g_sessionList;

/**
 * @brief Create session list when RFCOMM initialize.
 *
 */
void RfcommCreateSessionList()
{
    LOG_INFO("%{public}s", __func__);

    g_sessionList = ListCreate(NULL);
}

/**
 * @brief Free session list when RFCOMM finalize.
 *
 */
void RfcommDestroySessionList()
{
    LOG_INFO("%{public}s", __func__);

    ListNode *node = NULL;
    RfcommSessionInfo *session = NULL;

    if (g_sessionList == NULL) {
        LOG_DEBUG("%{public}s Session list is NULL.", __func__);
        return;
    }

    // Release server information.
    node = ListGetFirstNode(g_sessionList);
    while (node != NULL) {
        session = ListGetNodeData(node);
        if (session->timer != NULL) {
            AlarmDelete(session->timer);
            session->timer = NULL;
        }
        free(session);
        node = ListGetNextNode(node);
    }

    // Free server list.
    ListDelete(g_sessionList);
    g_sessionList = NULL;
}

/**
 * @brief The timeout callback function registered to alarm.
 *
 * @param parameter The parametr info registered to alarm.
 */
static void RfcommSessionTimeoutCallback(void *context)
{
    LOG_INFO("%{public}s", __func__);

    if (context == NULL) {
        LOG_ERROR("%{public}s:Session is closed.", __func__);
        return;
    }

    BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_RFCOMM, RfcommSessionTimeout, context);

    return;
}

void RfcommSessionTimeout(void *parameter)
{
    LOG_INFO("%{public}s", __func__);

    bool isSessionValid = RfcommIsSessionValid((RfcommSessionInfo *)parameter);
    if (!isSessionValid) {
        LOG_ERROR("%{public}s:Session is closed.", __func__);
        return;
    }

    RfcommSessionEvtFsm((RfcommSessionInfo *)parameter, EV_SESSION_TIMEOUT, NULL);
}

/**
 * @brief Start session's timer.
 *
 * @param session The pointer of the session in the session list.
 * @param timeout Timeout value.
 */
void RfcommStartSessionTimer(RfcommSessionInfo *session, uint8_t timeout)
{
    LOG_INFO("%{public}s", __func__);

    if (session->timer == NULL) {
        LOG_ERROR("%{public}s Session's timer is NULL.", __func__);
        return;
    }

    AlarmSet(session->timer, timeout * RFCOMM_PER_SEC, RfcommSessionTimeoutCallback, session);
}

/**
 * @brief Stop session's timer.
 *
 * @param session The pointer of the session in the session list.
 */
void RfcommStopSessionTimer(RfcommSessionInfo *session)
{
    LOG_INFO("%{public}s", __func__);

    if (session->timer == NULL) {
        LOG_ERROR("%{public}s Session's timer is NULL.", __func__);
        return;
    }

    AlarmCancel(session->timer);
}

/**
 * @brief This function is used to register session information.
 *
 * @param addr        The peer's BT address.
 * @param lcid        L2CAP channel id.
 * @param id          sequence id(Only used in L2CIF_ConnectRsp).
 * @param isInitiator Whether it is the initiator. true: initiator, false: non-initiator.
 * @return The pointer of the new session in the session list.
 */
RfcommSessionInfo *RfcommCreateSession(const BtAddr *addr, uint16_t lcid, uint8_t id, bool isInitiator)
{
    LOG_INFO("%{public}s", __func__);

    if (g_sessionList == NULL) {
        LOG_ERROR("%{public}s Session list is NULL.", __func__);
        return NULL;
    }

    int32_t listCount = ListGetSize(g_sessionList);
    // If sessionlist's size exceeds 6, there is no resource to establish a new session connection,
    // and NULL is returned
    if (listCount >= MAX_SESSION_COUNT) {
        LOG_ERROR("%{public}s Session is over Max count.", __func__);
        return NULL;
    }
    // Malloc a new session resource.
    RfcommSessionInfo *session = malloc(sizeof(RfcommSessionInfo));
    if (session == NULL) {
        return NULL;
    }
    session->isInitiator = isInitiator;
    (void)memcpy_s(&(session->btAddr), sizeof(BtAddr), addr, sizeof(BtAddr));
    session->l2capId = lcid;
    session->id = id;
    session->pendingL2capId = 0;
    session->pendingId = 0;
    session->fcType = FC_TYPE_UNKNOWN;
    session->sessionState = ST_SESSION_CLOSED;
    session->l2capLocalMtu = RFCOMM_L2CAP_MTU;
    session->l2capPeerMtu = RFCOMM_L2CAP_DEFAUT_MTU;
    session->timer = AlarmCreate(NULL, false);
    session->peerSessionFc = false;
    // Add the new session info to the session list.
    ListAddLast(g_sessionList, session);

    // Return a session pointer.
    return session;
}

/**
 * @brief During the session disconnection process, the upper layer sends a DLC connection request,
 *        reset part of session data.
 *
 * @param session The pointer of the session in the session list.
 */
void RfcommResetSessionInfo(RfcommSessionInfo *session)
{
    LOG_INFO("%{public}s", __func__);

    RfcommStopSessionTimer(session);
    session->isInitiator = false;
    session->l2capId = 0;
    session->id = 0;
    session->pendingL2capId = 0;
    session->pendingId = 0;
    session->peerSessionFc = false;
    session->fcType = FC_TYPE_UNKNOWN;
    session->sessionState = ST_SESSION_CLOSED;
    session->l2capLocalMtu = RFCOMM_L2CAP_MTU;
    session->l2capPeerMtu = RFCOMM_L2CAP_DEFAUT_MTU;
}

/**
 * @brief Set flown control type of the session.
 *
 * @param session The pointer of the session in the session list.
 * @param isPnReq true:Pn request, false:Pn response.
 * @param cl      cl value.
 */
void RfcommSetFlowControlType(RfcommSessionInfo *session, bool isPnReq, uint8_t cl)
{
    LOG_INFO("%{public}s", __func__);

    if (session->fcType != FC_TYPE_UNKNOWN) {
        LOG_INFO("%{public}s flow control type is confirmed.", __func__);
        return;
    }

    // A responding implementation shall set this field in the PN response to 14 (0xE),
    // if (and only if) the value in the PN request was 15.Ref-RFCOMM_SPEC_V12(5.5.3).
    if (isPnReq) {
        if (cl == CL_REQ_SUPPORT_CREDIT) {
            session->fcType = FC_TYPE_CREDIT;
        } else {
            session->fcType = FC_TYPE_NORMAL;
        }
    } else {
        if (cl == CL_RSP_SUPPORT_CREDIT) {
            session->fcType = FC_TYPE_CREDIT;
        } else {
            session->fcType = FC_TYPE_NORMAL;
        }
    }
}

/**
 * @brief Remove session node from session list and free the session resources.
 *
 * @param session The pointer of the session in the session list.
 */
void RfcommRemoveSession(RfcommSessionInfo *session)
{
    LOG_INFO("%{public}s", __func__);

    ListRemoveNode(g_sessionList, session);

    if (session->timer != NULL) {
        AlarmDelete(session->timer);
        session->timer = NULL;
    }

    free(session);
}

/**
 * @brief This function is used to find the specified session using lcid.
 *
 * @param lcid The lcid value.
 * @return The pointer of the session in the session list.
 */
RfcommSessionInfo *RfcommGetSessionByLcid(uint16_t lcid)
{
    LOG_INFO("%{public}s lcid:%hu", __func__, lcid);

    RfcommSessionInfo *session = NULL;
    ListNode *node = NULL;

    if (g_sessionList == NULL) {
        LOG_ERROR("%{public}s Session list is NULL.", __func__);
        return NULL;
    }

    node = ListGetFirstNode(g_sessionList);
    while (node != NULL) {
        session = ListGetNodeData(node);
        if (session->l2capId == lcid) {
            return session;
        }
        node = ListGetNextNode(node);
    }

    return NULL;
}

/**
 * @brief This function is used to find the specified session using BT address.
 *
 * @param addr BT address.
 * @return The pointer of the session in the session list.
 */
RfcommSessionInfo *RfcommGetSessionByAddr(const BtAddr *addr)
{
    LOG_INFO("%{public}s", __func__);

    RfcommSessionInfo *session = NULL;
    ListNode *node = NULL;

    if (g_sessionList == NULL) {
        LOG_ERROR("%{public}s Session list is NULL.", __func__);
        return NULL;
    }

    node = ListGetFirstNode(g_sessionList);
    while (node != NULL) {
        session = ListGetNodeData(node);
        if (!memcmp(&session->btAddr, addr, sizeof(BtAddr))) {
            return session;
        }
        node = ListGetNextNode(node);
    }

    return NULL;
}

/**
 * @brief Check if the session is valid.
 *
 * @param session The pointer of the session.
 * @return True if the session exists,false if the session doesn't exist.
 */
bool RfcommIsSessionValid(RfcommSessionInfo *session)
{
    LOG_INFO("%{public}s", __func__);

    RfcommSessionInfo *sessionInfo = NULL;
    ListNode *node = NULL;

    if (g_sessionList == NULL) {
        LOG_ERROR("%{public}s Session list is NULL.", __func__);
        return NULL;
    }

    // Check if there is a channel on the session.
    node = ListGetFirstNode(g_sessionList);
    while (node != NULL) {
        sessionInfo = ListGetNodeData(node);
        if (sessionInfo == session) {
            LOG_DEBUG("%{public}s The session is valid.", __func__);
            return true;
        }
        node = ListGetNextNode(node);
    }

    return false;
}