/*
* 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 bool g_serverNum[MAX_SERVER_COUNT] = {false};
/**
* @brief This function is used to initialize rfcomm resources and
* register the callback function to L2CAP.
*
*/
void RfcommInitialize()
{
LOG_INFO("%{public}s", __func__);
for (uint8_t cnt = 0; cnt < MAX_SERVER_COUNT; cnt++) {
g_serverNum[cnt] = false;
}
// Create list.
RfcommCreateSessionList();
RfcommCreateChannelList();
RfcommCreateServerList();
// Register security to GAP.
RfcommRegisterSecurity();
// Register to L2CAP.
RfcommRegisterL2cap();
}
/**
* @brief This function is used to release rfcomm internal resources and
* deregister the callback function to L2CAP.
*
*/
void RfcommFinalize()
{
LOG_INFO("%{public}s", __func__);
for (uint8_t cnt = 0; cnt < MAX_SERVER_COUNT; cnt++) {
g_serverNum[cnt] = false;
}
// Deregister from L2CAP.
RfcommDeregisterL2cap();
// Deregister security from GAP.
RfcommDeregisterSecurity();
// Free list.
RfcommDestroySessionList();
RfcommDestroyChannelList();
RfcommDestroyServerList();
}
/**
* @brief The function is used to assign server numbers to individual servers.
* Server number is used to register with the RFCOMM service interface, range is 1~30.
* When the return value is 0, it means that there is no available server number.
* This Server number shall be registered in the Service Discovery Database;
* RFCOMM_SPEC_V12 #5.4
*
* @return Server number.0(unavailable number),1~30(available number)
*/
uint8_t RfcommAssignServerNum()
{
LOG_INFO("%{public}s", __func__);
uint8_t scn = 0;
for (uint8_t index = 0; index < MAX_SERVER_COUNT; index++) {
if (g_serverNum[index] == true) {
continue;
}
g_serverNum[index] = true;
scn = index + 1;
break;
}
return scn;
}
/**
* @brief After close the server, free the server number.
*
* @param scn Server number.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommFreeServerNum(uint8_t scn)
{
LOG_INFO("%{public}s scn:%hhu", __func__, scn);
if ((scn < 1) || (scn > MAX_SERVER_COUNT)) {
return RFCOMM_ERR_PARAM;
}
g_serverNum[scn - 1] = false;
return RFCOMM_SUCCESS;
}
/**
* @brief The function is used by the client to establish a connection of the channel.
*
* @param reqInfo Connection request information.
* @param handle The handle of the channel created by rfcomm
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommConnectChannel(const RfcommConnectReqInfo *reqInfo, uint16_t *handle)
{
LOG_INFO("%{public}s", __func__);
// The valid range of server num is 1~30.
// If the incoming parameter scn is greater than 30, an exception will be returned.
if ((reqInfo->scn == 0) || (reqInfo->scn > MAX_SERVER_COUNT)) {
return RFCOMM_ERR_PARAM;
}
// Find session by BT address.If the session does not exist, create the session.
RfcommSessionInfo *session = RfcommGetSessionByAddr(&(reqInfo->addr));
if (session == NULL) {
// If the session does not exist, create a session.
session = RfcommCreateSession(&(reqInfo->addr), 0, 0, true);
if (session == NULL) {
return RFCOMM_ERR_NO_RESOURCES;
}
}
// Server applications on the noninitiating device are reachable on DLCIs 2,4,6,…,60;
// and server applications on the initiating device are reachable on DLCIs 3,5,7,…,61.
uint8_t dlci = session->isInitiator ? (reqInfo->scn << 1) : ((reqInfo->scn << 1) + 1);
// Determine whether the channel has been created.
RfcommChannelInfo *channel = RfcommGetChannelByDlci(session, dlci);
if (channel != NULL) {
// Channel already exists.
*handle = channel->handle;
return RFCOMM_ALREADY_EXIST;
}
RfcommCreateChannelInfo createChannelInfo;
createChannelInfo.session = session;
createChannelInfo.isServer = false;
createChannelInfo.dlci = dlci;
createChannelInfo.mtu = reqInfo->mtu;
createChannelInfo.eventMask = reqInfo->eventMask;
createChannelInfo.callback = reqInfo->callback;
createChannelInfo.context = reqInfo->context;
// If the channel does not exist, create a channel.
channel = RfcommCreateChannel(&createChannelInfo);
if (channel == NULL) {
return RFCOMM_ERR_NO_RESOURCES;
}
*handle = channel->handle;
return RfcommChannelEvtFsm(channel, EV_CHANNEL_SEND_OPEN_REQ, NULL);
}
/**
* @brief The function is used for the server to register with RFCOMM and
* wait for the client to connect.
*
* @param scn The server's number.
* @param mtu The maximum size of data received at a time.
* @param eventMask The collection of events followed by upper layers.
* @param callback The callback function used by rfcomm to notify uppers of data or events.
* @param context The content passed in from the upper layer.
* It will be brought back to the upper layer when callback is called.
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRegisterServer(uint8_t scn, uint16_t mtu, uint32_t eventMask,
RFCOMM_EventCallback callback, void *context)
{
LOG_INFO("%{public}s scn:%hhu", __func__, scn);
RfcommServerInfo *server = NULL;
// The valid range of server num is 1~30.
// If the incoming parameter scn is greater than 30, an exception will be returned.
if ((scn == 0) || (scn > MAX_SERVER_COUNT)) {
return RFCOMM_ERR_PARAM;
}
// Determine whether the server has been registered.
server = RfcommGetServerByScn(scn);
if (server != NULL) {
return RFCOMM_ALREADY_EXIST;
}
// If it is not registered, proceed with the registration process.
server = RfcommCreateServer(scn, mtu, eventMask, callback, context);
if (server == NULL) {
return RFCOMM_ERR_NO_RESOURCES;
}
return RFCOMM_SUCCESS;
}
/**
* @brief The function is used to tell RFCOMM to accept the connection request when the server
* receives the connection notification(eventId is RFCOMM_EVENT_CONNECT_INCOMING).
* After receiving the response from the upper layer, RFCOMM notifies the client
* of the peer device to accept the connection request.
*
* @param handle The channel(DLC)'s handle number
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommAcceptConnection(uint16_t handle)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
return RfcommChannelEvtFsm(channel, EV_CHANNEL_ACCEPT, NULL);
}
/**
* @brief This function is used to tell RFCOMM to reject the connection request when the server
* receives the connection notification(eventId is RFCOMM_EVENT_CONNECT_INCOMING).
* After receiving the response from the upper layer, RFCOMM notifies the client
* of the peer device to reject the connection request.
*
* @param handle The channel(DLC)'s handle number
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRejectConnection(uint16_t handle)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
return RfcommChannelEvtFsm(channel, EV_CHANNEL_REJECT, NULL);
}
/**
* @brief The function is used to disconnect the specified channel.
* RFCOMM_SPEC_V12 #5.2
*
* @param handle The channel(DLC)'s handle number
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommDisconnectChannel(uint16_t handle)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
return RfcommChannelEvtFsm(channel, EV_CHANNEL_SEND_CLOSE_REQ, NULL);
}
/**
* @brief When the server is shut down, call this function to
* release the resources about the server held in RFCOMM.
*
* @param scn The server's number
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommDeregisterServer(uint8_t scn, bool isRemoveCallback)
{
LOG_INFO("%{public}s scn:%hhu", __func__, scn);
RfcommServerInfo *server = RfcommGetServerByScn(scn);
if (server != NULL) {
RfcommRemoveServer(server);
}
// Remove callback from channel list and disconnect the channel.
if (isRemoveCallback) {
RfcommRemoveChannelCallback(scn);
}
return RFCOMM_SUCCESS;
}
/**
* @brief The function is used for set the remote port communication settings.
* The command may be used before a new DLC is opened and shall be used
* whenever the port settings change.
* RFCOMM_SPEC_V12 #5.5.1
*
* @param handle The channel(DLC)'s handle number
* @param config Remote port negotiation parameters
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommSetPortConfig(uint16_t handle, const RfcommRemotePortConfig *config)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
return RfcommChannelEvtFsm(channel, EV_CHANNEL_SEND_RPN_CMD, (void *)config);
}
/**
* @brief The function is used to obtain the remote port negotiation information
* of the peer device.
*
* @param handle The channel(DLC)'s handle number
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommReqPortConfig(uint16_t handle)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
return RfcommChannelEvtFsm(channel, EV_CHANNEL_SEND_RPN_REQ, NULL);
}
/**
* @brief The function is used for indication of remote port line status.
* RFCOMM_SPEC_V12 #5.5.2
*
* @param handle The channel(DLC)'s handle number
* @param lineStatus Remote line status
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommSendRemoteLineStatus(uint16_t handle, const RfcommRemoteLineStatus *lineStatus)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
return RfcommChannelEvtFsm(channel, EV_CHANNEL_SEND_RLS_REQ, (void *)lineStatus);
}
/**
* @brief The function is used to convey the RS-232 control signals and the break signal.
* RFCOMM_SPEC_V12 #2.2
*
* @param handle The channel(DLC)'s handle number
* @param modemStatus Control signals and the break signal
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommSetModemStatus(uint16_t handle, const RfcommModemStatus *modemStatus)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
return RfcommChannelEvtFsm(channel, EV_CHANNEL_SEND_MSC_REQ, (void *)modemStatus);
}
/**
* @brief This function is used to obtain port related information.
* Currently, the amount of data sent and received by the port can be obtained.
*
* @param handle The channel(DLC)'s handle number
* @param state The port's information
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommGetPortState(uint16_t handle, RfcommPortState *state)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
state->receivedBytes = channel->receivedBytes;
state->transmittedBytes = channel->transmittedBytes;
return RFCOMM_SUCCESS;
}
/**
* @brief This function is used to get the payload packet sent by the peer from RFCOMM.
* After the caller finishes using this interface, it creates a packet reference or
* reads the payload buffer as needed, and must free the packet obtained from RFCOMM.
*
* @param handle The channel(DLC)'s handle number
* @param pkt The packet point for receiving data
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommRead(uint16_t handle, Packet **pkt)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
ListNode *node = NULL;
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
return RFCOMM_ERR_PARAM;
}
node = ListGetFirstNode(channel->recvQueue);
if (node == NULL) {
*pkt = NULL;
return RFCOMM_NO_DATA;
}
*pkt = (Packet *)ListGetNodeData(node);
// Add received data bytes value.
channel->receivedBytes += PacketPayloadSize(*pkt);
// Remove first node.
ListRemoveFirst(channel->recvQueue);
// Local can receive more data, send flow control to peer.
RfcommSetFlcToPeer(channel, true);
return RFCOMM_SUCCESS;
}
/**
* @brief This function is used to write the data to be transmitted to the opposite end to RFCOMM.
*
* @param handle The channel(DLC)'s handle number
* @param pkt The packet for sending data
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommWrite(uint16_t handle, Packet *pkt)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
size_t len = PacketPayloadSize(pkt);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
LOG_ERROR("%{public}s:Channel does not exist.", __func__);
return RFCOMM_ERR_PARAM;
}
if (len > channel->peerMtu) {
LOG_ERROR("%{public}s:Packet length(%zu) is over MTU.", __func__, len);
return RFCOMM_OVERRUN;
}
return RfcommChannelEvtFsm(channel, EV_CHANNEL_WRITE_DATA, pkt);
}
/**
* @brief This function is used to send Test Command to the peer.
*
* @param handle The channel(DLC)'s handle number
* @param pkt The packet for sending data
* @return Returns RFCOMM_SUCCESS if the operation is successful, otherwise the operation fails.
*/
int RfcommSendTestCmd(uint16_t handle, Packet *pkt)
{
LOG_INFO("%{public}s handle:%hu", __func__, handle);
size_t len = PacketPayloadSize(pkt);
RfcommChannelInfo *channel = RfcommGetChannelByHandle(handle);
if (channel == NULL) {
LOG_ERROR("%{public}s:The channel does not exist.", __func__);
return RFCOMM_ERR_PARAM;
}
if (len > channel->peerMtu) {
LOG_ERROR("%{public}s:Test cmd packet length(%zu) is over MTU.", __func__, len);
return RFCOMM_OVERRUN;
}
return RfcommSendUihTest(channel->session, true, pkt);
}