/* * Copyright (C) 2021-2022 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" typedef int (*SessionActionFunc)(RfcommSessionInfo *session, const void *data); typedef struct { RfcommSessionEvent eventId; SessionActionFunc fn; } RfcommSessionEvtAction; static int RfcommOpenSession(RfcommSessionInfo *session, const void *data); static int RfcommCloseSession(RfcommSessionInfo *session, const void *data); static int RfcommRecvSessionSecurityRslt(RfcommSessionInfo *session, const void *data); static int RfcommRecvConnectReq(RfcommSessionInfo *session, const void *data); static int RfcommRecvReqConnectRslt(RfcommSessionInfo *session, const void *data); static int RfcommRecvConnectRsp(RfcommSessionInfo *session, const void *data); static int RfcommRecvConfigReq(RfcommSessionInfo *session, const void *data); static int RfcommRecvConfigRsp(RfcommSessionInfo *session, const void *data); static int RfcommRecvDisconnectReq(RfcommSessionInfo *session, const void *data); static int RfcommRecvSabm0(RfcommSessionInfo *session, const void *data); static int RfcommRecvDisc0(RfcommSessionInfo *session, const void *data); static int RfcommRecvUa0(RfcommSessionInfo *session, const void *data); static int RfcommRecvDm0(RfcommSessionInfo *session, const void *data); static int RfcommRecvFconReq(RfcommSessionInfo *session, const void *data); static int RfcommRecvFconRsp(RfcommSessionInfo *session, const void *data); static int RfcommRecvFcoffReq(RfcommSessionInfo *session, const void *data); static int RfcommRecvFcoffRsp(RfcommSessionInfo *session, const void *data); static int RfcommRecvTestReq(RfcommSessionInfo *session, const void *data); static int RfcommRecvTestRsp(RfcommSessionInfo *session, const void *data); static int RfcommRecvNsc(RfcommSessionInfo *session, const void *data); static int RfcommRecvLinkLoss(RfcommSessionInfo *session, const void *data); static int RfcommRecvTimeOut(RfcommSessionInfo *session, const void *data); static int RfcommRestartSession(RfcommSessionInfo *session); static int RfcommAcceptPeerConnectReq(RfcommSessionInfo *session); static RfcommSessionEvtAction g_sessionEvtActTbl[EV_SESSION_EV_MAX] = { {EV_SESSION_SEND_OPEN_REQ, RfcommOpenSession}, {EV_SESSION_SEND_CLOSE_REQ, RfcommCloseSession}, {EV_SESSION_RECV_SECURITY_RESULT, RfcommRecvSessionSecurityRslt}, {EV_SESSION_RECV_REQ_CONNECT_RESULT, RfcommRecvReqConnectRslt}, {EV_SESSION_RECV_CONNECT_REQ, RfcommRecvConnectReq}, {EV_SESSION_RECV_CONNECT_RSP, RfcommRecvConnectRsp}, {EV_SESSION_RECV_CONFIG_REQ, RfcommRecvConfigReq}, {EV_SESSION_RECV_CONFIG_RSP, RfcommRecvConfigRsp}, {EV_SESSION_RECV_DISCONNECT_REQ, RfcommRecvDisconnectReq}, {EV_SESSION_RECV_SABM0, RfcommRecvSabm0}, {EV_SESSION_RECV_DISC0, RfcommRecvDisc0}, {EV_SESSION_RECV_UA0, RfcommRecvUa0}, {EV_SESSION_RECV_DM0, RfcommRecvDm0}, {EV_SESSION_RECV_FCON_REQ, RfcommRecvFconReq}, {EV_SESSION_RECV_FCON_RSP, RfcommRecvFconRsp}, {EV_SESSION_RECV_FCOFF_REQ, RfcommRecvFcoffReq}, {EV_SESSION_RECV_FCOFF_RSP, RfcommRecvFcoffRsp}, {EV_SESSION_RECV_TEST_REQ, RfcommRecvTestReq}, {EV_SESSION_RECV_TEST_RSP, RfcommRecvTestRsp}, {EV_SESSION_RECV_NSC, RfcommRecvNsc}, {EV_SESSION_RECV_LINK_LOSS, RfcommRecvLinkLoss}, {EV_SESSION_TIMEOUT, RfcommRecvTimeOut} }; /** * @brief State machine for handling events related to session. * * @param session The pointer of the session in the session list. * @param event The event id. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommSessionEvtFsm(RfcommSessionInfo *session, RfcommSessionEvent event, const void *data) { LOG_INFO("%{public}s", __func__); int ret = 0; for (int cnt = 0; cnt < EV_SESSION_EV_MAX; cnt++) { if (g_sessionEvtActTbl[cnt].eventId == event) { ret = g_sessionEvtActTbl[cnt].fn(session, data); break; } } return ret; } /** * @brief Processing after receiving connect session request. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommOpenSession(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); if (data == NULL) { return RFCOMM_ERR_PARAM; } int ret = 0; uint8_t scn = *(uint8_t *)data; switch (session->sessionState) { case ST_SESSION_CLOSED: session->sessionState = ST_SESSION_INITIATOR_WAIT_SECURITY_RESULT; ret = RfcommCheckSessionSecurity(session, scn, false); break; case ST_SESSION_DISC0_REQ_WAIT_UA0: // fall-through case ST_SESSION_CLOSING: // If a connection request is received during the closing process, // the status is set to ST_SESSION_WAIT_RESTART, and after the UA response is received, // the reconnection process is performed. session->sessionState = ST_SESSION_WAIT_RESTART; break; default: break; } return ret; } int RfcommRecvReqConnectRslt(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); if (data == NULL) { return RFCOMM_ERR_PARAM; } RfcommReqConnectRslt connectRslt; (void)memcpy_s(&connectRslt, sizeof(RfcommReqConnectRslt), data, sizeof(RfcommReqConnectRslt)); if (session->sessionState != ST_SESSION_WAIT_CONNECT_REQ_RESULT) { return RFCOMM_SUCCESS; } LOG_INFO("%{public}s pendingL2capId :%hu.", __func__, session->pendingL2capId); if (session->pendingL2capId != 0) { RfcommStopSessionTimer(session); if ((connectRslt.result == RFCOMM_SUCCESS) && (connectRslt.lcid != 0)) { RfcommSendDisconnectReq(connectRslt.lcid); } // Accept peer's connection request. return RfcommAcceptPeerConnectReq(session); } if ((connectRslt.result != RFCOMM_SUCCESS) || (connectRslt.lcid == 0)) { // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session from session list. RfcommRemoveSession(session); } else { session->l2capId = connectRslt.lcid; session->sessionState = ST_SESSION_WAIT_CONNECT_RSP; } return RFCOMM_SUCCESS; } /** * @brief Processing after receiving connect response from L2CAP. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvConnectRsp(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); if (data == NULL) { return RFCOMM_ERR_PARAM; } uint16_t result = *(uint16_t *)data; L2capConfigInfo config; (void)memset_s(&config, sizeof(config), 0, sizeof(config)); if (session->sessionState != ST_SESSION_WAIT_CONNECT_RSP) { LOG_DEBUG("%{public}s:Session state is not WAIT_CONNECT_RSP.", __func__); return RFCOMM_FAILED; } LOG_INFO("%{public}s pendingL2capId:%hu.", __func__, session->pendingL2capId); if (result != L2CAP_CONNECTION_SUCCESSFUL) { if (session->pendingL2capId != 0) { RfcommStopSessionTimer(session); // Accept peer's connection request. return RfcommAcceptPeerConnectReq(session); } // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); return RFCOMM_SUCCESS; } if (session->pendingL2capId != 0) { RfcommStopSessionTimer(session); RfcommSendConnectRsp(session->pendingL2capId, session->pendingId, L2CAP_NO_RESOURCES_AVAILABLE, 0); session->pendingL2capId = 0; session->pendingId = 0; } config.rfc.mode = 0; // Basic mode config.flushTimeout = 0xFFFF; config.mtu = session->l2capLocalMtu; config.fcs = 0x01; session->sessionState = ST_SESSION_INITIATOR_WAIT_CONFIG_REQ_AND_RSP; return RfcommSendConfigReq(session->l2capId, &config); } /** * @brief Processing after receiving config response from L2CAP. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvConfigRsp(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); if (data == NULL) { return RFCOMM_ERR_PARAM; } int ret = 0; RfcommConfigRspInfo *configResult = (RfcommConfigRspInfo *)data; if (configResult->result != L2CAP_SUCCESS) { RfcommSendDisconnectReq(session->l2capId); // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); return RFCOMM_SUCCESS; } switch (session->sessionState) { case ST_SESSION_INITIATOR_WAIT_CONFIG_REQ_AND_RSP: session->sessionState = ST_SESSION_INITIATOR_WAIT_CONFIG_REQ; break; case ST_SESSION_INITIATOR_WAIT_CONFIG_RSP: ret = RfcommSendSabm(session, CONTROL_DLCI); session->sessionState = ST_SESSION_SABM0_REQ_WAIT_UA0; RfcommStartSessionTimer(session, T1_SABM_DISC); break; case ST_SESSION_RESPONDER_WAIT_CONFIG_REQ_AND_RSP: session->sessionState = ST_SESSION_RESPONDER_WAIT_CONFIG_REQ; break; case ST_SESSION_RESPONDER_WAIT_CONFIG_RSP: session->sessionState = ST_SESSION_WAIT_SABM0; break; default: break; } return ret; } /** * @brief Processing after receiving config request from L2CAP. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvConfigReq(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); if (data == NULL) { return RFCOMM_ERR_PARAM; } RfcommConfigReqInfo reqInfo; (void)memcpy_s(&reqInfo, sizeof(RfcommConfigReqInfo), (RfcommConfigReqInfo *)data, sizeof(RfcommConfigReqInfo)); session->l2capPeerMtu = reqInfo.cfg.mtu; if (reqInfo.cfg.rfc.mode != 0) { LOG_DEBUG("%{public}s mode is not basic mode, reconfig.", __func__); (void)memset_s(&reqInfo.cfg, sizeof(L2capConfigInfo), 0x00, sizeof(L2capConfigInfo)); reqInfo.cfg.rfc.mode = 0; reqInfo.cfg.flushTimeout = 0xFFFF; reqInfo.cfg.fcs = 0x01; reqInfo.cfg.mtu = session->l2capPeerMtu; return RfcommSendConfigRsp(session->l2capId, reqInfo.id, &reqInfo.cfg, L2CAP_UNACCEPTABLE_PARAMETERS); } int ret = RfcommSendConfigRsp(session->l2capId, reqInfo.id, &reqInfo.cfg, 0); switch (session->sessionState) { case ST_SESSION_INITIATOR_WAIT_CONFIG_REQ_AND_RSP: session->sessionState = ST_SESSION_INITIATOR_WAIT_CONFIG_RSP; break; case ST_SESSION_INITIATOR_WAIT_CONFIG_REQ: ret = RfcommSendSabm(session, CONTROL_DLCI); session->sessionState = ST_SESSION_SABM0_REQ_WAIT_UA0; RfcommStartSessionTimer(session, T1_SABM_DISC); break; case ST_SESSION_RESPONDER_WAIT_CONFIG_REQ_AND_RSP: session->sessionState = ST_SESSION_RESPONDER_WAIT_CONFIG_RSP; break; case ST_SESSION_RESPONDER_WAIT_CONFIG_REQ: session->sessionState = ST_SESSION_WAIT_SABM0; break; default: break; } return ret; } /** * @brief Processing after receiving UA(DLCI0) response from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvUa0(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; int ret = 0; switch (session->sessionState) { case ST_SESSION_SABM0_REQ_WAIT_UA0: session->sessionState = ST_SESSION_CONNECTED; // Stop timer. RfcommStopSessionTimer(session); // After the session is successfully connected, // connect all links under the session. RfcommOpenAllChannelOnSession(session); break; case ST_SESSION_DISC0_REQ_WAIT_UA0: // Stop timer. RfcommStopSessionTimer(session); // L2CAP disconnect. ret = RfcommSendDisconnectReq(session->l2capId); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); break; case ST_SESSION_WAIT_RESTART: // Stop timer. RfcommStopSessionTimer(session); // L2CAP disconnect. RfcommSendDisconnectReq(session->l2capId); // Restart the session, init the session information first. ret = RfcommRestartSession(session); break; default: break; } return ret; } /** * @brief Processing after receiving connect request from L2CAP. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvConnectReq(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); if (data == NULL) { return RFCOMM_ERR_PARAM; } int ret = 0; RfcommRecvConnectedInfo connectedInfo; (void)memcpy_s(&connectedInfo, sizeof(RfcommRecvConnectedInfo), data, sizeof(RfcommRecvConnectedInfo)); switch (session->sessionState) { case ST_SESSION_INITIATOR_WAIT_SECURITY_RESULT: session->id = connectedInfo.id; session->l2capId = connectedInfo.lcid; session->isInitiator = false; RfcommUpdateChannelDirectionBit(session, false); // fall-through case ST_SESSION_CLOSED: session->sessionState = ST_SESSION_RESPONDER_WAIT_SECURITY_RESULT; ret = RfcommCheckSessionSecurity(session, 0, true); break; case ST_SESSION_WAIT_CONNECT_REQ_RESULT: // fall-through case ST_SESSION_WAIT_CONNECT_RSP: session->pendingL2capId = connectedInfo.lcid; session->pendingId = connectedInfo.id; uint32_t randomNum = RandomGenerate(); uint8_t timer = randomNum % RFCOMM_NUM_TEN; RfcommStartSessionTimer(session, timer); break; default: ret = RfcommSendConnectRsp(session->l2capId, session->id, L2CAP_NO_RESOURCES_AVAILABLE, 0); break; } return ret; } /** * @brief Processing after receiving SABM(DLCI0) request from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvSabm0(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; if (session->sessionState != ST_SESSION_WAIT_SABM0) { LOG_DEBUG("%{public}s:Session state is not WAIT_SABM0.", __func__); return RFCOMM_FAILED; } int ret = RfcommSendUa(session, CONTROL_DLCI); session->sessionState = ST_SESSION_CONNECTED; // After the session is successfully connected, // connect all links under the session. RfcommOpenAllChannelOnSession(session); return ret; } /** * @brief Processing after receiving DISC(DLCI0) request from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvDisc0(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; int ret; bool sessionValid = false; switch (session->sessionState) { case ST_SESSION_SABM0_REQ_WAIT_UA0: session->sessionState = ST_SESSION_CLOSING; ret = RfcommSendUa(session, CONTROL_DLCI); // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); break; case ST_SESSION_DISC0_REQ_WAIT_UA0: ret = RfcommSendUa(session, CONTROL_DLCI); break; case ST_SESSION_CONNECTED: session->sessionState = ST_SESSION_CLOSING; ret = RfcommSendUa(session, CONTROL_DLCI); // Delete all channels on the session(except the channel to be connected). RfcommRemoveInvalidChannelOnSession(session); // Check if the session is valid. sessionValid = RfcommCheckSessionValid(session); if (sessionValid) { // If there are channels to be connected, set RESTART of session's state. session->sessionState = ST_SESSION_WAIT_RESTART; RfcommResetAllChannelOnSession(session); } break; default: ret = RfcommSendDm(session, CONTROL_DLCI, true); break; } return ret; } /** * @brief Processing after receiving link loss notification from L2CAP. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvLinkLoss(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); if (data == NULL) { return RFCOMM_ERR_PARAM; } uint8_t reason = *(uint8_t *)data; if (reason == L2CAP_STATE_COLLISION) { LOG_DEBUG("%{public}s Connect request error.Retry!", __func__); return RfcommRestartSession(session); } switch (session->sessionState) { case ST_SESSION_CONNECTED: // Notify all channels on the session that disconnection. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_DISCONNECTED); break; default: // Stop timer. RfcommStopSessionTimer(session); // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); break; } // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); return RFCOMM_SUCCESS; } /** * @brief Processing after receiving the request to disconnect session from L2CAP. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvDisconnectReq(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; if (session->sessionState == ST_SESSION_WAIT_RESTART) { // Restart the session, init the session information first. return RfcommRestartSession(session); } // Notify all channels on the session that disconnection. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_DISCONNECTED); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); return RFCOMM_SUCCESS; } /** * @brief The processing after receiving the request to close the session. * When all channels on the session are disconnected, perform this process. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommCloseSession(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; int ret = 0; switch (session->sessionState) { case ST_SESSION_WAIT_CONNECT_RSP: // fall-through case ST_SESSION_INITIATOR_WAIT_CONFIG_REQ_AND_RSP: // fall-through case ST_SESSION_INITIATOR_WAIT_CONFIG_REQ: // fall-through case ST_SESSION_INITIATOR_WAIT_CONFIG_RSP: // fall-through case ST_SESSION_SABM0_REQ_WAIT_UA0: // Stop timer. RfcommStopSessionTimer(session); ret = RfcommSendDisconnectReq(session->l2capId); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); break; case ST_SESSION_CONNECTED: ret = RfcommSendDisc(session, CONTROL_DLCI); session->sessionState = ST_SESSION_DISC0_REQ_WAIT_UA0; RfcommStartSessionTimer(session, T1_SABM_DISC); break; default: break; } return ret; } /** * @brief The processing after receiving the DM(DLCI0) response from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvDm0(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; int ret = 0; switch (session->sessionState) { case ST_SESSION_SABM0_REQ_WAIT_UA0: // Stop timer. RfcommStopSessionTimer(session); ret = RfcommSendDisconnectReq(session->l2capId); // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); break; case ST_SESSION_DISC0_REQ_WAIT_UA0: // Stop timer. RfcommStopSessionTimer(session); ret = RfcommSendDisconnectReq(session->l2capId); break; case ST_SESSION_WAIT_RESTART: // Stop timer. RfcommStopSessionTimer(session); // L2CAP disconnect. RfcommSendDisconnectReq(session->l2capId); // Restart the session, init the session information first. return RfcommRestartSession(session); default: break; } // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); return ret; } /** * @brief The processing after receiving timeout notification. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvTimeOut(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; int ret = 0; switch (session->sessionState) { case ST_SESSION_SABM0_REQ_WAIT_UA0: ret = RfcommSendDisconnectReq(session->l2capId); // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); break; case ST_SESSION_WAIT_SABM0: break; case ST_SESSION_DISC0_REQ_WAIT_UA0: ret = RfcommSendDisconnectReq(session->l2capId); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); break; case ST_SESSION_WAIT_RESTART: // L2CAP disconnect. RfcommSendDisconnectReq(session->l2capId); // Restart the session, init the session information first. ret = RfcommRestartSession(session); break; case ST_SESSION_CONNECTED: // Notify all channels that DLC is disconnected. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_DISCONNECTED); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // The default action performed on timeouts is to close down the multiplexer session. ret = RfcommSendDisc(session, CONTROL_DLCI); session->sessionState = ST_SESSION_DISC0_REQ_WAIT_UA0; RfcommStartSessionTimer(session, T1_SABM_DISC); break; case ST_SESSION_WAIT_CONNECT_RSP: if (session->pendingL2capId != 0) { // L2CAP disconnect. RfcommSendDisconnectReq(session->l2capId); // Accept peer's connection request. ret = RfcommAcceptPeerConnectReq(session); } break; default: break; } return ret; } /** * @brief The processing after receiving FCON request from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvFconReq(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; if (session->sessionState != ST_SESSION_CONNECTED) { LOG_DEBUG("%{public}s:Session state is not connected.", __func__); return RfcommSendDm(session, CONTROL_DLCI, false); } // Send FCon response. int ret = RfcommSendUihFcon(session, false); // When credit based flow control is being used on a session, // the FCon and FCoff multiplexer control commands shall not be used. if (session->fcType == FC_TYPE_CREDIT) { LOG_DEBUG("%{public}s:Credit based fc is used, ignore FC-bit.", __func__); return ret; } // Set the flow control status, and send buffered data on all channels. if (session->peerSessionFc) { session->peerSessionFc = false; RfcommSendAllCachePktOnSession(session); } return ret; } /** * @brief The processing after receiving FCON response from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvFconRsp(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s", __func__); (void)session; (void)data; return RFCOMM_SUCCESS; } /** * @brief The processing after receiving FCOFF request from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvFcoffReq(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); (void)data; if (session->sessionState != ST_SESSION_CONNECTED) { LOG_DEBUG("%{public}s:Session state is not connected.", __func__); return RfcommSendDm(session, CONTROL_DLCI, false); } // Send FCon response. int ret = RfcommSendUihFcoff(session, false); // When credit based flow control is being used on a session, // the FCon and FCoff multiplexer control commands shall not be used. if (session->fcType == FC_TYPE_CREDIT) { LOG_DEBUG("%{public}s:Credit based fc is used, ignore FC-bit.", __func__); return ret; } // Set the flow control status. session->peerSessionFc = true; return ret; } /** * @brief The processing after receiving FCOFF response from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvFcoffRsp(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s", __func__); (void)session; (void)data; return RFCOMM_SUCCESS; } /** * @brief The processing after receiving test request from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvTestReq(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s", __func__); if (data == NULL) { return RFCOMM_ERR_PARAM; } RfcommUihInfo info; (void)memcpy_s(&info, sizeof(RfcommUihInfo), data, sizeof(RfcommUihInfo)); return RfcommSendUihTest(session, false, info.test.pkt); } /** * @brief The processing after receiving test response from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvTestRsp(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s", __func__); (void)session; (void)data; return RFCOMM_SUCCESS; } /** * @brief The processing after receiving NSC response from peer. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvNsc(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s", __func__); if (data == NULL) { return RFCOMM_ERR_PARAM; } RfcommUihInfo info; (void)memcpy_s(&info, sizeof(RfcommUihInfo), data, sizeof(RfcommUihInfo)); LOG_DEBUG("%{public}s:ea is %hhu.", __func__, info.nsc.ea); LOG_DEBUG("%{public}s:cr is %hhu.", __func__, info.nsc.cr); LOG_DEBUG("%{public}s:type is %hhu.", __func__, info.nsc.type); return RFCOMM_SUCCESS; } /** * @brief The processing after receiving the result of session's security. * * @param session The pointer of the session in the session list. * @param data Data related to the event. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRecvSessionSecurityRslt(RfcommSessionInfo *session, const void *data) { LOG_INFO("%{public}s session state is:%{public}d.", __func__, session->sessionState); if (data == NULL) { return RFCOMM_ERR_PARAM; } int ret = 0; RfcommSessionSecurityRslt securityRslt; L2capConfigInfo config; (void)memset_s(&config, sizeof(config), 0, sizeof(config)); (void)memcpy_s(&securityRslt, sizeof(RfcommSessionSecurityRslt), data, sizeof(RfcommSessionSecurityRslt)); switch (session->sessionState) { case ST_SESSION_INITIATOR_WAIT_SECURITY_RESULT: if (securityRslt.direction != OUTGOING) { LOG_ERROR("%{public}s Direction is error:%{public}d.", __func__, securityRslt.direction); break; } if (securityRslt.result != GAP_SUCCESS) { // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session from session list. RfcommRemoveSession(session); break; } session->sessionState = ST_SESSION_WAIT_CONNECT_REQ_RESULT; ret = RfcommSendConnectReq(&(session->btAddr)); break; case ST_SESSION_RESPONDER_WAIT_SECURITY_RESULT: if (securityRslt.direction != INCOMING) { LOG_ERROR("%{public}s Direction is error:%{public}d.", __func__, securityRslt.direction); break; } if (securityRslt.result != GAP_SUCCESS) { RfcommSendConnectRsp(session->l2capId, session->id, L2CAP_NO_RESOURCES_AVAILABLE, 0); // Notify all channels on the session that connection failed. RfcommNotifyAllChannelEvtOnSession(session, RFCOMM_CHANNEL_EV_CONNECT_FAIL); // Remove all channels on session. RfcommRemoveAllChannelOnSession(session); // Destroy session. RfcommRemoveSession(session); break; } RfcommSendConnectRsp(session->l2capId, session->id, 0, 0); config.rfc.mode = 0; // Basic mode config.flushTimeout = 0xFFFF; config.mtu = session->l2capLocalMtu; config.fcs = 0x01; session->sessionState = ST_SESSION_RESPONDER_WAIT_CONFIG_REQ_AND_RSP; ret = RfcommSendConfigReq(session->l2capId, &config); break; default: break; } return ret; } /** * @brief Restart the session.When a connection request is received during the disconnection process, * the connection is recreated after disconnection. * * @param session The pointer of the session in the session list. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommRestartSession(RfcommSessionInfo *session) { LOG_INFO("%{public}s", __func__); RfcommResetSessionInfo(session); session->isInitiator = true; RfcommUpdateChannelDirectionBit(session, true); // Start to reopen the session. session->sessionState = ST_SESSION_INITIATOR_WAIT_SECURITY_RESULT; RfcommChannelInfo *channel = RfcommGetFirstChannelOnSession(session); if (channel == NULL) { LOG_ERROR("%{public}s No channel on the session.", __func__); return RFCOMM_SUCCESS; } return RfcommCheckSessionSecurity(session, channel->scn, false); } /** * @brief Accept the connection request from the peer device. * * @param session The pointer of the session in the session list. * @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails. */ int RfcommAcceptPeerConnectReq(RfcommSessionInfo *session) { LOG_INFO("%{public}s", __func__); session->l2capId = session->pendingL2capId; session->id = session->pendingId; session->isInitiator = false; RfcommUpdateChannelDirectionBit(session, false); session->pendingL2capId = 0; session->pendingId = 0; session->sessionState = ST_SESSION_RESPONDER_WAIT_SECURITY_RESULT; return RfcommCheckSessionSecurity(session, 0, true); }