/*
* 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"
typedef int (*ChannelActionFunc)(RfcommChannelInfo *channel, const void *data);
typedef struct {
RfcommChannelEvent eventId;
ChannelActionFunc fn;
} RfcommChannelEvtAction;
static int RfcommOpenChannel(RfcommChannelInfo *channel, const void *data);
static int RfcommCloseChannel(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvSecurityRslt(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvSabm(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvDisc(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvUa(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvDm(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvPnReq(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvPnRsp(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvRpnCmd(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvRpnReq(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvRpnRsp(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvMscReq(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvMscRsp(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvRlsReq(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvRlsRsp(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvAcceptRsp(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvRejectRsp(RfcommChannelInfo *channel, const void *data);
static int RfcommRecvRevData(RfcommChannelInfo *channel, const void *data);
static int RfcommSendRpnCmd(RfcommChannelInfo *channel, const void *data);
static int RfcommSendRpnReq(RfcommChannelInfo *channel, const void *data);
static int RfcommSendRlsReq(RfcommChannelInfo *channel, const void *data);
static int RfcommSendMscReq(RfcommChannelInfo *channel, const void *data);
static int RfcommWriteData(RfcommChannelInfo *channel, const void *data);
static int RfcommTimeOut(RfcommChannelInfo *channel, const void *data);
static void RfcommSetUihPnParameter(
RfcommFlowControlType fcType, uint8_t reqCl, RfcommPnCmdKind cmdKind, uint8_t *cl, uint8_t *credits);
static RfcommChannelEvtAction g_channelEvtActTbl[EV_CHANNEL_EV_MAX] = {
{EV_CHANNEL_SEND_OPEN_REQ, RfcommOpenChannel},
{EV_CHANNEL_SEND_CLOSE_REQ, RfcommCloseChannel},
{EV_CHANNEL_RECV_SECURITY_RESULT, RfcommRecvSecurityRslt},
{EV_CHANNEL_RECV_SABM, RfcommRecvSabm},
{EV_CHANNEL_RECV_DISC, RfcommRecvDisc},
{EV_CHANNEL_RECV_UA, RfcommRecvUa},
{EV_CHANNEL_RECV_DM, RfcommRecvDm},
{EV_CHANNEL_RECV_PN_REQ, RfcommRecvPnReq},
{EV_CHANNEL_RECV_PN_RSP, RfcommRecvPnRsp},
{EV_CHANNEL_RECV_MSC_REQ, RfcommRecvMscReq},
{EV_CHANNEL_RECV_MSC_RSP, RfcommRecvMscRsp},
{EV_CHANNEL_RECV_RPN_CMD, RfcommRecvRpnCmd},
{EV_CHANNEL_RECV_RPN_REQ, RfcommRecvRpnReq},
{EV_CHANNEL_RECV_RPN_RSP, RfcommRecvRpnRsp},
{EV_CHANNEL_RECV_RLS_REQ, RfcommRecvRlsReq},
{EV_CHANNEL_RECV_RLS_RSP, RfcommRecvRlsRsp},
{EV_CHANNEL_ACCEPT, RfcommRecvAcceptRsp},
{EV_CHANNEL_REJECT, RfcommRecvRejectRsp},
{EV_CHANNEL_RECV_DATA, RfcommRecvRevData},
{EV_CHANNEL_WRITE_DATA, RfcommWriteData},
{EV_CHANNEL_SEND_RPN_CMD, RfcommSendRpnCmd},
{EV_CHANNEL_SEND_RPN_REQ, RfcommSendRpnReq},
{EV_CHANNEL_SEND_RLS_REQ, RfcommSendRlsReq},
{EV_CHANNEL_SEND_MSC_REQ, RfcommSendMscReq},
{EV_CHANNEL_TIMEOUT, RfcommTimeOut}
};
/**
* @brief State machine for handling events related to data link connection.
*
* @param channel The pointer of the channel in the channel 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 RfcommChannelEvtFsm(RfcommChannelInfo *channel, RfcommChannelEvent event, const void *data)
{
LOG_INFO("%{public}s event:%{public}d", __func__, event);
int ret = 0;
for (int cnt = 0; cnt < EV_CHANNEL_EV_MAX; cnt++) {
if (g_channelEvtActTbl[cnt].eventId == event) {
ret = g_channelEvtActTbl[cnt].fn(channel, data);
break;
}
}
return ret;
}
/**
* @brief Processing after receiving connect req event from upper.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommOpenChannel(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
int ret = 0;
uint8_t credits = 0;
uint8_t cl = 0;
uint8_t scn = channel->scn;
RfcommSessionInfo *session = channel->session;
switch (channel->channelState) {
case ST_CHANNEL_CLOSED:
// If session is not connected, connect the session.
if (session->sessionState != ST_SESSION_CONNECTED) {
ret = RfcommSessionEvtFsm(session, EV_SESSION_SEND_OPEN_REQ, &scn);
break;
}
// If session is connected, prepare to connect the DLC.Process PN negotiation firstly.
RfcommSetUihPnParameter(session->fcType, 0, PN_REQ, &cl, &credits);
channel->localCredit = credits;
RfcommSendPnInfo pnInfo;
pnInfo.cl = cl;
pnInfo.credits = credits;
pnInfo.mtu = channel->localMtu;
pnInfo.priority = 0;
ret = RfcommSendUihPn(session, channel->dlci, true, &pnInfo);
channel->channelState = ST_CHANNEL_WAIT_PN_RSP;
RfcommStartChannelTimer(channel, T2_UIH_DLCI0);
break;
case ST_CHANNEL_DISC_REQ_WAIT_UA:
channel->channelState = ST_CHANNEL_WAIT_RESTART;
break;
case ST_CHANNEL_CONNECTED:
LOG_DEBUG("%{public}s:DLC is connected.", __func__);
break;
default:
break;
}
return ret;
}
/**
* @brief Processing after receiving the security check result from GAP.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvSecurityRslt(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
int ret = 0;
uint16_t result = *(uint16_t *)data;
RfcommSessionInfo *session = channel->session;
RfcommConnectedInfo connectedInfo;
switch (channel->channelState) {
case ST_CHANNEL_CLIENT_WAIT_SECURITY_RESULT:
if (result != GAP_SUCCESS) {
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_CONNECT_FAIL, NULL);
// Release channelinfo.
RfcommRemoveChannel(channel);
// If the last channel on the session is closed, close the session.
RfcommCloseInvalidSession(session);
break;
}
ret = RfcommSendSabm(session, channel->dlci);
channel->channelState = ST_CHANNEL_SABM_REQ_WAIT_UA;
RfcommStartChannelTimer(channel, T1_SABM_OPEN_DLC);
break;
case ST_CHANNEL_SERVER_WAIT_SECURITY_RESULT:
// If the security check result is not successful,
// then return a connection rejection response to the peer.
if (result != GAP_SUCCESS) {
ret = RfcommSendDm(session, channel->dlci, true);
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_CONNECT_FAIL, NULL);
// Release channelinfo.
RfcommRemoveChannel(channel);
// If the last channel on the session is closed, close the session.
RfcommCloseInvalidSession(session);
break;
}
// If the result of the security check is successful,
// the connection acceptance response is returned to the peer.
ret = RfcommSendUa(session, channel->dlci);
channel->channelState = ST_CHANNEL_CONNECTED;
// After DLC is established, determine the mtu size of the opposite end.
RfcommDeterminePeerMtu(channel);
(void)memset_s(&connectedInfo, sizeof(RfcommConnectedInfo), 0x00, sizeof(RfcommConnectedInfo));
connectedInfo.sendMTU = channel->peerMtu;
connectedInfo.recvMTU = channel->localMtu;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_CONNECT_SUCCESS, &connectedInfo);
break;
default:
break;
}
return ret;
}
/**
* @brief Processing after receiving the UA response from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvUa(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
int ret = 0;
uint8_t credits = 0;
uint8_t cl = 0;
RfcommSessionInfo *session = channel->session;
RfcommConnectedInfo connectedInfo;
RfcommModemStatusInfo modemStatus;
(void)memset_s(&modemStatus, sizeof(modemStatus), 0x00, sizeof(modemStatus));
switch (channel->channelState) {
case ST_CHANNEL_SABM_REQ_WAIT_UA:
// Stop timer.
RfcommStopChannelTimer(channel);
channel->channelState = ST_CHANNEL_CONNECTED;
// After DLC is established, determine the mtu size of the opposite end.
RfcommDeterminePeerMtu(channel);
// Notify the upper layer that the connection is successful.
(void)memset_s(&connectedInfo, sizeof(RfcommConnectedInfo), 0x00, sizeof(RfcommConnectedInfo));
connectedInfo.sendMTU = channel->peerMtu;
connectedInfo.recvMTU = channel->localMtu;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_CONNECT_SUCCESS, &connectedInfo);
// Send MSC command.
// In any case MSC commands and responses must be exchanged before
// the data transfer may start.Ref-RFCOMM_SPEC_V12(6.3)
modemStatus.signals = MSC_RTC | MSC_RTR | MSC_DV;
ret = RfcommSendUihMsc(session, channel->dlci, true, &modemStatus);
channel->transferReady |= MSC_CMD_SEND;
// Start timer.
RfcommStartChannelTimer(channel, T2_UIH_DLCI0);
break;
case ST_CHANNEL_DISC_REQ_WAIT_UA:
// Stop timer.
RfcommStopChannelTimer(channel);
channel->channelState = ST_CHANNEL_CLOSED;
// Notify the upper layer that the disconnection is successful.
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_DISCONNECT_SUCCESS, NULL);
// Release channelinfo.
RfcommRemoveChannel(channel);
// If the last channel on the session is closed, close the session.
RfcommCloseInvalidSession(session);
break;
case ST_CHANNEL_WAIT_RESTART:
RfcommResetChannelInfo(channel);
// If session is connected, prepare to connect the DLC.Process PN negotiation first.
RfcommSetUihPnParameter(session->fcType, 0, PN_REQ, &cl, &credits);
channel->localCredit = credits;
RfcommSendPnInfo pnInfo;
pnInfo.cl = cl;
pnInfo.credits = credits;
pnInfo.mtu = channel->localMtu;
pnInfo.priority = 0;
ret = RfcommSendUihPn(session, channel->dlci, true, &pnInfo);
channel->channelState = ST_CHANNEL_WAIT_PN_RSP;
RfcommStartChannelTimer(channel, T2_UIH_DLCI0);
break;
default:
break;
}
return ret;
}
/**
* @brief Processing after receiving the DM response from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvDm(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
RfcommSessionInfo *session = channel->session;
if (channel->channelState == ST_CHANNEL_CLOSED) {
LOG_DEBUG("%{public}s Channel is restarting.", __func__);
return RFCOMM_SUCCESS;
}
switch (channel->channelState) {
case ST_CHANNEL_WAIT_PN_RSP:
// fall-through
case ST_CHANNEL_SABM_REQ_WAIT_UA:
// Stop timer.
RfcommStopChannelTimer(channel);
channel->channelState = ST_CHANNEL_CLOSED;
// Notify the upper layer that the connection is fail.
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_CONNECT_FAIL, NULL);
break;
case ST_CHANNEL_DISC_REQ_WAIT_UA:
// Stop timer.
RfcommStopChannelTimer(channel);
channel->channelState = ST_CHANNEL_CLOSED;
// Notify the upper layer that the disconnection is successful.
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_DISCONNECT_SUCCESS, NULL);
break;
case ST_CHANNEL_CONNECTED:
// Stop timer.
RfcommStopChannelTimer(channel);
channel->channelState = ST_CHANNEL_CLOSED;
// Notify the upper layer that the channel is disconnected.
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_DISCONNECTED, NULL);
break;
default:
break;
}
// Release channelinfo.
RfcommRemoveChannel(channel);
// If the last channel on the session is closed, close the session.
RfcommCloseInvalidSession(session);
return RFCOMM_SUCCESS;
}
/**
* @brief Processing after receiving the RPN commad from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvRpnCmd(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
RfcommUihInfo info;
(void)memcpy_s(&info, sizeof(RfcommUihInfo), data, sizeof(RfcommUihInfo));
channel->portConfig.baudrate = info.rpn.rpnInfo.baudrate;
channel->portConfig.data_bits = info.rpn.rpnInfo.data_bits;
channel->portConfig.fc = info.rpn.rpnInfo.fc;
channel->portConfig.parity = info.rpn.rpnInfo.parity;
channel->portConfig.parity_type = info.rpn.rpnInfo.parity_type;
channel->portConfig.stop_bit = info.rpn.rpnInfo.stop_bit;
channel->portConfig.xoff_char = info.rpn.rpnInfo.xoff_char;
channel->portConfig.xon_char = info.rpn.rpnInfo.xon_char;
channel->portConfig.parameter_mask1 = info.rpn.rpnInfo.parameter_mask1;
channel->portConfig.parameter_mask2 = info.rpn.rpnInfo.parameter_mask2;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_REMOTE_PORT_CONFIG, &channel->portConfig);
return RfcommSendUihRpn(channel->session, channel->dlci, false, &channel->portConfig);
}
/**
* @brief Processing after receiving the RPN request from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvRpnReq(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
RfcommRemotePortConfig portConfig;
(void)memcpy_s(&portConfig, sizeof(RfcommRemotePortConfig), &channel->portConfig, sizeof(RfcommRemotePortConfig));
portConfig.parameter_mask1 = 0;
portConfig.parameter_mask2 = 0;
return RfcommSendUihRpn(channel->session, channel->dlci, false, &portConfig);
}
/**
* @brief Processing after receiving the PN request from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvPnReq(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
int ret;
uint8_t credits = 0;
uint8_t cl = 0;
RfcommSessionInfo *session = channel->session;
RfcommUihInfo info;
(void)memcpy_s(&info, sizeof(RfcommUihInfo), data, sizeof(RfcommUihInfo));
RfcommSendPnInfo pnInfo;
(void)memset_s(&pnInfo, sizeof(RfcommSendPnInfo), 0x00, sizeof(RfcommSendPnInfo));
switch (channel->channelState) {
case ST_CHANNEL_CLOSED:
// Set flow control type.
RfcommSetFlowControlType(session, true, info.pn.cl);
// If it is credit-based flow control, then set the initial credits of the peer.
if ((session->fcType == FC_TYPE_CREDIT) && (info.pn.cl == CL_REQ_SUPPORT_CREDIT)) {
channel->peerCredit = info.pn.k;
}
RfcommSetUihPnParameter(session->fcType, info.pn.cl, PN_RSP_BEFORE_CREATE, &cl, &credits);
// For a PN command with N1 value of N1c (c for command),
// a PN response shall have an N1 value N1r (r for response) where N1r <= N1c.
channel->localMtu = (channel->localMtu < info.pn.mtu) ? channel->localMtu : info.pn.mtu;
channel->peerMtu = channel->localMtu;
// Send PN response to peer.
channel->localCredit = credits;
pnInfo.cl = cl;
pnInfo.credits = credits;
pnInfo.mtu = channel->localMtu;
pnInfo.priority = info.pn.priority;
ret = RfcommSendUihPn(session, channel->dlci, false, &pnInfo);
// Waiting peer to send sabm request.
channel->channelState = ST_CHANNEL_WAIT_SABM;
break;
case ST_CHANNEL_DISC_REQ_WAIT_UA:
ret = RfcommSendDm(session, channel->dlci, false);
break;
default:
// When DLC is connected or connecting, PN req is received, only return Pn rsp to Peer.
RfcommSetUihPnParameter(session->fcType, info.pn.cl, PN_RSP_AFTER_CREATE, &cl, &credits);
pnInfo.cl = cl;
pnInfo.credits = credits;
pnInfo.mtu = channel->localMtu;
pnInfo.priority = info.pn.priority;
// Send PN response to peer.
ret = RfcommSendUihPn(session, channel->dlci, false, &pnInfo);
break;
}
return ret;
}
/**
* @brief Processing after receiving PN response from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvPnRsp(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
RfcommUihInfo info;
(void)memcpy_s(&info, sizeof(RfcommUihInfo), data, sizeof(RfcommUihInfo));
if (channel->channelState != ST_CHANNEL_WAIT_PN_RSP) {
LOG_DEBUG("%{public}s:State is not WAIT_RSP.", __func__);
return RFCOMM_FAILED;
}
// Stop timer.
RfcommStopChannelTimer(channel);
// Set flow control type.
RfcommSetFlowControlType(channel->session, false, info.pn.cl);
// If flow control type is based credit, then set amount of credits issued to the peer.
if (channel->session->fcType == FC_TYPE_CREDIT) {
channel->peerCredit = info.pn.k;
}
// A device receiving a PN response may accept N1r and use this value as the
// maximum frame data size.If this connection is established,
// neither side may send a frame with more than N1r bytes of data
channel->localMtu = (channel->localMtu < info.pn.mtu) ? channel->localMtu : info.pn.mtu;
channel->peerMtu = channel->localMtu;
// Perform security checks before initiating a connection request.
channel->channelState = ST_CHANNEL_CLIENT_WAIT_SECURITY_RESULT;
return RfcommCheckChannelSecurity(channel, false);
}
/**
* @brief Processing after receiving the SABM request from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvSabm(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
RfcommIncomingInfo eventInfo;
(void)memset_s(&eventInfo, sizeof(eventInfo), 0x00, sizeof(eventInfo));
switch (channel->channelState) {
case ST_CHANNEL_CLOSED:
// fall-through
case ST_CHANNEL_WAIT_SABM:
(void)memcpy_s(&eventInfo.addr, sizeof(BtAddr), &(channel->session->btAddr), sizeof(BtAddr));
eventInfo.scn = channel->dlci >> RFCOMM_DLCI_SHIFT_SCN;
// After receiving the connection request from the peer, notify the request to the upper layer,
// and wait for the upper layer to accept or reject the connection request response.
channel->channelState = ST_CHANNEL_WAIT_UPPER_RESPONSE;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_CONNECT_INCOMING, &eventInfo);
break;
default:
break;
}
return RFCOMM_SUCCESS;
}
/**
* @brief Processing after receiving the DISC request from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvDisc(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
int ret = 0;
RfcommSessionInfo *session = channel->session;
switch (channel->channelState) {
case ST_CHANNEL_CLOSED:
ret = RfcommSendDm(session, channel->dlci, true);
break;
case ST_CHANNEL_WAIT_PN_RSP:
// Stop timer.
RfcommStopChannelTimer(channel);
ret = RfcommSendDm(session, channel->dlci, true);
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_CONNECT_FAIL, NULL);
break;
case ST_CHANNEL_SABM_REQ_WAIT_UA:
// Stop timer.
RfcommStopChannelTimer(channel);
ret = RfcommSendUa(session, channel->dlci);
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_CONNECT_FAIL, NULL);
break;
case ST_CHANNEL_DISC_REQ_WAIT_UA:
// Stop timer.
RfcommStopChannelTimer(channel);
ret = RfcommSendDm(session, channel->dlci, true);
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_DISCONNECT_SUCCESS, NULL);
// Release channelinfo.
RfcommRemoveChannel(channel);
// If the last channel on the session is closed, close the session.
RfcommCloseInvalidSession(session);
return ret;
case ST_CHANNEL_WAIT_UPPER_RESPONSE:
// fall-through
case ST_CHANNEL_SERVER_WAIT_SECURITY_RESULT:
// fall-through
case ST_CHANNEL_CONNECTED:
ret = RfcommSendUa(session, channel->dlci);
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_DISCONNECTED, NULL);
break;
default:
break;
}
// Release channel resource.
RfcommRemoveChannel(channel);
return ret;
}
/**
* @brief Processing after receiving the acception response of the connection request from upper.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvAcceptRsp(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
if (channel->channelState != ST_CHANNEL_WAIT_UPPER_RESPONSE) {
LOG_DEBUG("%{public}s:State is not WAIT_UPPER_RSP.", __func__);
return RFCOMM_FAILED;
}
// Before returning a response to the connection request, perform security check processing.
channel->channelState = ST_CHANNEL_SERVER_WAIT_SECURITY_RESULT;
return RfcommCheckChannelSecurity(channel, true);
}
/**
* @brief Processing after receiving the rejection response of the connection request from upper.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvRejectRsp(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
int ret = 0;
RfcommSessionInfo *session = channel->session;
if (channel->channelState != ST_CHANNEL_WAIT_UPPER_RESPONSE) {
LOG_DEBUG("%{public}s:State is not WAIT_UPPER_RSP.", __func__);
return ret;
}
ret = RfcommSendDm(session, channel->dlci, true);
// Release channelinfo.
RfcommRemoveChannel(channel);
// If the last channel on the session is closed, close the session.
RfcommCloseInvalidSession(session);
return ret;
}
/**
* @brief Processing after receiving the disconnect channel request from upper.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommCloseChannel(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
int ret = 0;
RfcommSessionInfo *session = channel->session;
switch (channel->channelState) {
case ST_CHANNEL_WAIT_PN_RSP:
// Stop timer.
RfcommStopChannelTimer(channel);
// fall-through
case ST_CHANNEL_CLOSED:
// fall-through
case ST_CHANNEL_CLIENT_WAIT_SECURITY_RESULT:
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_DISCONNECT_SUCCESS, NULL);
// Release channelinfo.
RfcommRemoveChannel(channel);
// If the last channel on the session is closed, close the session.
RfcommCloseInvalidSession(session);
break;
case ST_CHANNEL_WAIT_UPPER_RESPONSE:
// fall-through
case ST_CHANNEL_SERVER_WAIT_SECURITY_RESULT:
ret = RfcommSendDm(session, channel->dlci, true);
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_DISCONNECT_SUCCESS, NULL);
// Release channelinfo.
RfcommRemoveChannel(channel);
// If the last channel on the session is closed, close the session.
RfcommCloseInvalidSession(session);
break;
case ST_CHANNEL_DISC_REQ_WAIT_UA:
LOG_DEBUG("%{public}s:DLCI(%hhu) is Disconnecting.", __func__, channel->dlci);
break;
case ST_CHANNEL_SABM_REQ_WAIT_UA:
// Stop timer.
RfcommStopChannelTimer(channel);
// fall-through
case ST_CHANNEL_CONNECTED:
ret = RfcommSendDisc(session, channel->dlci);
channel->channelState = ST_CHANNEL_DISC_REQ_WAIT_UA;
RfcommStartChannelTimer(channel, T1_SABM_DISC);
break;
default:
break;
}
return ret;
}
/**
* @brief Processing after receiving the timeout notification.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommTimeOut(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
uint32_t eventId = 0;
RfcommSessionInfo *session = channel->session;
switch (channel->channelState) {
case ST_CHANNEL_WAIT_PN_RSP:
// fall-through
case ST_CHANNEL_SABM_REQ_WAIT_UA:
eventId = RFCOMM_CHANNEL_EV_CONNECT_FAIL;
break;
case ST_CHANNEL_DISC_REQ_WAIT_UA:
eventId = RFCOMM_CHANNEL_EV_DISCONNECT_SUCCESS;
break;
case ST_CHANNEL_CONNECTED:
eventId = RFCOMM_CHANNEL_EV_DISCONNECTED;
break;
default:
return RFCOMM_SUCCESS;
}
channel->channelState = ST_CHANNEL_CLOSED;
RfcommNotifyEvtToUpper(channel, eventId, NULL);
RfcommRemoveChannel(channel);
return RfcommSessionEvtFsm(session, EV_SESSION_TIMEOUT, NULL);
}
/**
* @brief Processing after receiving the MSC request from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvMscReq(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
RfcommUihInfo info;
RfcommModemStatusInfo modemSts;
(void)memset_s(&modemSts, sizeof(modemSts), 0x00, sizeof(modemSts));
(void)memcpy_s(&info, sizeof(RfcommUihInfo), data, sizeof(RfcommUihInfo));
modemSts.signals = info.msc.signal;
modemSts.breakSignal = info.msc.breakSignal;
// Send MSC response to the peer.
int ret = RfcommSendUihMsc(channel->session, channel->dlci, false, &modemSts);
uint8_t oldSt = channel->transferReady;
channel->transferReady |= MSC_CMD_RECV;
LOG_DEBUG("%{public}s:transferReady State is (%hhu).", __func__, channel->transferReady);
// Set peer's modem status.
RfcommSetPeerModemStatus(channel, &modemSts);
// If MSC cmd has not been sent, send it.
// In any case MSC commands and responses must be exchanged before
// the data transfer may start.Ref-RFCOMM_SPEC_V12(6.3)
if (!(channel->transferReady & MSC_CMD_SEND)) {
(void)memset_s(&modemSts, sizeof(modemSts), 0x00, sizeof(modemSts));
modemSts.signals = MSC_RTC | MSC_RTR | MSC_DV;
ret = RfcommSendUihMsc(channel->session, channel->dlci, true, &modemSts);
channel->transferReady |= MSC_CMD_SEND;
// Start timer.
RfcommStartChannelTimer(channel, T2_UIH_DLCI0);
} else if (oldSt == (MSC_CMD_SEND | MSC_RSP_RECV)) {
// If the MSC commnd and response have been exchanged,
// check whether there is data in the sending buffer queue,
// and if there is buffered data, send the data.
RfcommSendCachePkt(channel);
RfcommSetFlcToUpper(channel);
}
return ret;
}
/**
* @brief Processing after receiving the MSC response from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvMscRsp(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
if (channel->channelState != ST_CHANNEL_CONNECTED) {
LOG_DEBUG("%{public}s:State is not connected.", __func__);
return RFCOMM_ERR_NOT_CONNECTED;
}
// Stop timer.
RfcommStopChannelTimer(channel);
uint8_t oldSt = channel->transferReady;
channel->transferReady |= MSC_RSP_RECV;
LOG_DEBUG("%{public}s:transferReady State is (%hhu).", __func__, channel->transferReady);
// If the MSC commnd and response have been exchanged,
// check whether there is data in the sending buffer queue,
// and if there is buffered data, send the data.
if (oldSt == (MSC_CMD_SEND | MSC_CMD_RECV)) {
RfcommSendCachePkt(channel);
RfcommSetFlcToUpper(channel);
}
return RFCOMM_SUCCESS;
}
/**
* @brief Processing after receiving the RPN response from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvRpnRsp(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
RfcommStopChannelTimer(channel);
return RFCOMM_SUCCESS;
}
/**
* @brief Processing after receiving the RLS request from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvRlsReq(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
RfcommUihInfo info;
RfcommRemoteLineStatus lineStatus;
(void)memcpy_s(&info, sizeof(RfcommUihInfo), data, sizeof(RfcommUihInfo));
(void)memset_s(&lineStatus, sizeof(RfcommRemoteLineStatus), 0x00, sizeof(RfcommRemoteLineStatus));
lineStatus.overrunErr = info.rls.lineStatus >> RFCOMM_RLS_SHIFT_OVERRUN;
lineStatus.parityErr = info.rls.lineStatus >> RFCOMM_RLS_SHIFT_PARITY;
lineStatus.frameErr = info.rls.lineStatus >> RFCOMM_RLS_SHIFT_FRAMING;
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_REMOTE_LINE_STATUS, &lineStatus);
return RfcommSendUihRls(channel->session, channel->dlci, false, info.rls.lineStatus);
}
/**
* @brief Processing after receiving the RLS response from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvRlsRsp(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
RfcommStopChannelTimer(channel);
return RFCOMM_SUCCESS;
}
/**
* @brief Processing after receiving the transmit information from peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRecvRevData(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
Packet *pkt = NULL;
RfcommUihInfo info;
(void)memcpy_s(&info, sizeof(RfcommUihInfo), data, sizeof(RfcommUihInfo));
if (channel->channelState != ST_CHANNEL_CONNECTED) {
LOG_DEBUG("%{public}s Channel is not connected.", __func__);
return RfcommSendDm(channel->session, channel->dlci, false);
}
if (info.data.size > 0) {
RfcommReadLock();
uint8_t count = (uint8_t)ListGetSize(channel->recvQueue);
if (count < MAX_QUEUE_COUNT) {
pkt = PacketRefMalloc(info.data.payload);
ListAddLast(channel->recvQueue, pkt);
RfcommReadUnlock();
RfcommNotifyEvtToUpper(channel, RFCOMM_CHANNEL_EV_REV_DATA, NULL);
RfcommSetFlcToPeer(channel, false);
} else {
RfcommReadUnlock();
}
}
if (info.data.credits > 0) {
channel->peerCredit += info.data.credits;
RfcommSendCachePkt(channel);
RfcommSetFlcToUpper(channel);
}
return RFCOMM_SUCCESS;
}
/**
* @brief Processing after receiving the write transmit information from upper.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommWriteData(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
uint8_t newCredits = 0;
Packet *refpkt = NULL;
RfcommSessionInfo *session = channel->session;
// If it is currently in the process of disconnection, no data will be sent.
if (channel->channelState == ST_CHANNEL_DISC_REQ_WAIT_UA || channel->channelState == ST_CHANNEL_CLOSED) {
return RFCOMM_ERR_NOT_CONNECTED;
}
// Determine whether the peer can receive the data. If the peer cannot receive the data,
// save the data in the queue to be sent.
if (((session->fcType == FC_TYPE_CREDIT) && (channel->peerCredit == 0)) ||
(channel->transferReady != TRANSFER_READY) || channel->peerChannelFc || (session->peerSessionFc)) {
// Get send list's count
uint8_t count = (uint8_t)ListGetSize(channel->sendQueue);
if (count < MAX_QUEUE_COUNT) {
refpkt = PacketRefMalloc((Packet *)data);
ListAddLast(channel->sendQueue, (void *)refpkt);
return RFCOMM_SUCCESS;
}
channel->localFcToUpper = true;
return RFCOMM_QUEUE_FULL;
}
if (session->fcType == FC_TYPE_CREDIT) {
// The value of the credit octet (0 - 255) signifies a number of frames,
// for which the sender now has buffer space available to receive on the DLC.
newCredits = channel->localCreditMax - channel->localCredit;
channel->localCredit += newCredits;
}
// Add transmite data bytes value.
channel->transmittedBytes += PacketPayloadSize((Packet *)data);
// Send data to peer.
int ret = RfcommSendUihData(session, channel->dlci, newCredits, (Packet *)data);
// Decrease credit count.
if ((session->fcType == FC_TYPE_CREDIT) && (ret == RFCOMM_SUCCESS)) {
if (channel->peerCredit > 0) {
channel->peerCredit--;
}
}
return ret;
}
/**
* @brief Send rpn command to peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommSendRpnCmd(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
int ret = RfcommSendUihRpn(channel->session, channel->dlci, true, (RfcommRemotePortConfig *)data);
// Start timer.
RfcommStartChannelTimer(channel, T2_UIH_DLCI0);
return ret;
}
/**
* @brief Send rpn request to peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommSendRpnReq(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s channel state is:%{public}d.", __func__, channel->channelState);
(void)data;
int ret = RfcommSendUihRpn(channel->session, channel->dlci, true, NULL);
// Start timer.
RfcommStartChannelTimer(channel, T2_UIH_DLCI0);
return ret;
}
/**
* @brief Send rls request to peer.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommSendRlsReq(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s", __func__);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
RfcommRemoteLineStatus *lineStatus = (RfcommRemoteLineStatus *)data;
uint8_t status = lineStatus->overrunErr ? OVERRUN_ERROR : 0;
status |= lineStatus->parityErr ? PARITY_ERROR : 0;
status |= lineStatus->frameErr ? FRAMING_ERROR : 0;
if (status) {
status = (status << 1) | 0x01;
}
int ret = RfcommSendUihRls(channel->session, channel->dlci, true, status);
// Start timer.
RfcommStartChannelTimer(channel, T2_UIH_DLCI0);
return ret;
}
/**
* @brief Send modem status request.
*
* @param channel The pointer of the channel in the channel list.
* @param data Data related to the event.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommSendMscReq(RfcommChannelInfo *channel, const void *data)
{
LOG_INFO("%{public}s", __func__);
if (data == NULL) {
return RFCOMM_ERR_PARAM;
}
RfcommModemStatus *modemSts = (RfcommModemStatus *)data;
RfcommModemStatusInfo status;
(void)memset_s(&status, sizeof(RfcommModemStatusInfo), 0x00, sizeof(RfcommModemStatusInfo));
status.signals = modemSts->fc ? MSC_FC : 0;
status.signals |= modemSts->rtc ? MSC_RTC : 0;
status.signals |= modemSts->rtr ? MSC_RTR : 0;
status.signals |= modemSts->ic ? MSC_IC : 0;
status.signals |= modemSts->dv ? MSC_DV : 0;
status.breakSignal = modemSts->break_signal ? MSC_BREAK : 0;
status.breakSignal |= modemSts->break_length << RFCOMM_MSC_SHIFT_BREAKLEN;
int ret = RfcommSendUihMsc(channel->session, channel->dlci, true, &status);
channel->transferReady |= MSC_CMD_SEND;
// Start timer.
RfcommStartChannelTimer(channel, T2_UIH_DLCI0);
return ret;
}
/**
* @brief Set UIH PN request or response parameter.
*
* @param fcType Flow control type.
* @param reqCl The cl value of the pn request.
* @param cmdKind Pn command kind.
* @param cl The cl value that will be sent to peer.
* @param credits The initial credits that will be sent to peer.
*/
void RfcommSetUihPnParameter(
RfcommFlowControlType fcType, uint8_t reqCl, RfcommPnCmdKind cmdKind, uint8_t *cl, uint8_t *credits)
{
LOG_INFO("%{public}s fcType:%{public}d, req_cl:%hhu, cmdKind:%{public}d.", __func__, fcType, reqCl, cmdKind);
switch (cmdKind) {
case PN_REQ:
// If the flow control type is not set, the credit-based flow control type is supported by default.
// The initiator has to try to turn on the use of credit based flow control.
if ((fcType == FC_TYPE_UNKNOWN) || (fcType == FC_TYPE_CREDIT)) {
*cl = CL_REQ_SUPPORT_CREDIT;
*credits = DEFAULT_CREDITS_VALUE;
} else {
*cl = CL_REQ_UNSUPPORTED_CREDIT;
*credits = 0;
}
break;
case PN_RSP_BEFORE_CREATE:
// 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.
if ((fcType == FC_TYPE_CREDIT) && (reqCl == CL_REQ_SUPPORT_CREDIT)) {
*cl = CL_RSP_SUPPORT_CREDIT;
*credits = DEFAULT_CREDITS_VALUE;
} else {
*cl = CL_RSP_UNSUPPORTED_CREDIT;
*credits = 0;
}
break;
case PN_RSP_AFTER_CREATE:
// After the DLC is established, the responder of a PN request may
// refuse to change any parameters.
// it is not possible to “set initial credits” more than once per DLC activation.
*credits = 0;
if (reqCl == CL_REQ_SUPPORT_CREDIT) {
LOG_DEBUG("%{public}s:Peer's cl value is wrong.", __func__);
*cl = CL_RSP_SUPPORT_CREDIT;
} else {
*cl = CL_RSP_UNSUPPORTED_CREDIT;
}
break;
default:
LOG_ERROR("%{public}s:Error cmd kind.", __func__);
break;
}
}