* 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;
}
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);
}
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 (listCount >= MAX_SESSION_COUNT) {
LOG_ERROR("%{public}s Session is over Max count.", __func__);
return NULL;
}
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;
ListAddLast(g_sessionList, session);
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;
}
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;
}
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;
}