mirror of https://github.com/FOME-Tech/fome-fw.git
287 lines
6.7 KiB
C++
287 lines
6.7 KiB
C++
#include "pch.h"
|
|
#include "wifi_socket.h"
|
|
#include "thread_controller.h"
|
|
|
|
#include "driver/include/m2m_wifi.h"
|
|
#include "socket/include/socket.h"
|
|
|
|
static chibios_rt::BinarySemaphore isrSemaphore(/* taken =*/ true);
|
|
|
|
/*static*/ ServerSocket* ServerSocket::s_serverList = nullptr;
|
|
|
|
ServerSocket::ServerSocket() {
|
|
// Add server to linked list
|
|
m_nextServer = s_serverList;
|
|
s_serverList = this;
|
|
|
|
// Set up queue
|
|
iqObjectInit(&m_recvQueue, m_recvQueueBuffer, sizeof(m_recvQueueBuffer), nullptr, nullptr);
|
|
}
|
|
|
|
void ServerSocket::startListening(const sockaddr_in& addr) {
|
|
m_listenerSocket = socket(AF_INET, SOCK_STREAM, SOCKET_CONFIG_SSL_OFF);
|
|
bind(m_listenerSocket, (sockaddr*)&addr, sizeof(addr));
|
|
}
|
|
|
|
void ServerSocket::onAccept(int connectedSocket) {
|
|
m_connectedSocket = connectedSocket;
|
|
|
|
recv(m_connectedSocket, &m_recvBuf, 1, 0);
|
|
}
|
|
|
|
void ServerSocket::onClose() {
|
|
close(m_connectedSocket);
|
|
|
|
m_connectedSocket = -1;
|
|
|
|
{
|
|
chibios_rt::CriticalSectionLocker csl;
|
|
iqResetI(&m_recvQueue);
|
|
}
|
|
}
|
|
|
|
void ServerSocket::onRecv(uint8_t* buffer, size_t recvSize, size_t remaining) {
|
|
{
|
|
chibios_rt::CriticalSectionLocker csl;
|
|
|
|
for (size_t i = 0; i < recvSize; i++) {
|
|
iqPutI(&m_recvQueue, buffer[i]);
|
|
}
|
|
}
|
|
|
|
size_t nextRecv;
|
|
if (remaining < 1) {
|
|
// Always try to read at least 1 byte
|
|
nextRecv = 1;
|
|
} else if (remaining > sizeof(m_recvBuf)) {
|
|
// Remaining is too big for the buffer, so just read one buffer worth
|
|
nextRecv = sizeof(m_recvBuf);
|
|
} else {
|
|
// The full thing will fit, try to read it
|
|
nextRecv = remaining;
|
|
}
|
|
|
|
// start the next recv
|
|
recv(m_connectedSocket, &m_recvBuf, nextRecv, 0);
|
|
}
|
|
|
|
bool ServerSocket::hasConnectedSocket() const {
|
|
return m_connectedSocket != -1;
|
|
}
|
|
|
|
/*static*/ bool ServerSocket::checkSend() {
|
|
bool result = false;
|
|
|
|
auto current = s_serverList;
|
|
|
|
while (current) {
|
|
result |= current->trySendImpl();
|
|
current = current->m_nextServer;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void ServerSocket::send(uint8_t* buffer, size_t size) {
|
|
m_sendBuffer = buffer;
|
|
m_sendSize = size;
|
|
m_sendRequest = true;
|
|
|
|
// Wake the driver to perform the actual send
|
|
isrSemaphore.signal();
|
|
|
|
// Wait for this chunk to complete
|
|
m_sendDoneSemaphore.wait();
|
|
}
|
|
|
|
void ServerSocket::onSendDone() {
|
|
// Send completed, notify caller!
|
|
chibios_rt::CriticalSectionLocker csl;
|
|
m_sendDoneSemaphore.signalI();
|
|
}
|
|
|
|
size_t ServerSocket::recvTimeout(uint8_t* buffer, size_t size, int timeout) {
|
|
return iqReadTimeout(&m_recvQueue, buffer, size, timeout);
|
|
}
|
|
|
|
/*static*/ ServerSocket* ServerSocket::findListener(int sock) {
|
|
auto current = s_serverList;
|
|
|
|
while (current) {
|
|
if (current->m_listenerSocket == sock) {
|
|
break;
|
|
}
|
|
|
|
current = current->m_nextServer;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
/*static*/ ServerSocket* ServerSocket::findConnected(int sock) {
|
|
auto current = s_serverList;
|
|
|
|
while (current) {
|
|
if (current->m_connectedSocket == sock) {
|
|
break;
|
|
}
|
|
|
|
current = current->m_nextServer;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
bool ServerSocket::trySendImpl() {
|
|
if ((m_connectedSocket != -1) && m_sendRequest) {
|
|
::send(m_connectedSocket, (void*)m_sendBuffer, m_sendSize, 0);
|
|
m_sendRequest = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void os_hook_isr() {
|
|
isrSemaphore.signalI();
|
|
}
|
|
|
|
static void wifiCallback(uint8 u8MsgType, void* pvMsg) {
|
|
switch (u8MsgType) {
|
|
case M2M_WIFI_REQ_DHCP_CONF: {
|
|
auto& dhcpInfo = *reinterpret_cast<tstrM2MIPConfig*>(pvMsg);
|
|
uint8_t* addr = reinterpret_cast<uint8_t*>(&dhcpInfo.u32StaticIP);
|
|
efiPrintf("WiFi client connected DHCP IP is %d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
|
|
} break;
|
|
default:
|
|
efiPrintf("WifiCallback: %d", (int)u8MsgType);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void socketCallback(SOCKET sock, uint8_t u8Msg, void* pvMsg) {
|
|
switch (u8Msg) {
|
|
case SOCKET_MSG_BIND: {
|
|
auto bindMsg = reinterpret_cast<tstrSocketBindMsg*>(pvMsg);
|
|
if (bindMsg && bindMsg->status == 0) {
|
|
// Socket bind complete, now listen!
|
|
listen(sock, 1);
|
|
}
|
|
} break;
|
|
case SOCKET_MSG_LISTEN: {
|
|
auto listenMsg = reinterpret_cast<tstrSocketListenMsg*>(pvMsg);
|
|
if (listenMsg && listenMsg->status == 0) {
|
|
// Listening, now accept a connection
|
|
accept(sock, nullptr, nullptr);
|
|
}
|
|
} break;
|
|
case SOCKET_MSG_ACCEPT: {
|
|
auto acceptMsg = reinterpret_cast<tstrSocketAcceptMsg*>(pvMsg);
|
|
if (acceptMsg && (acceptMsg->sock >= 0)) {
|
|
if (auto server = ServerSocket::findListener(sock)) {
|
|
server->onAccept(acceptMsg->sock);
|
|
}
|
|
}
|
|
} break;
|
|
case SOCKET_MSG_RECV: {
|
|
auto recvMsg = reinterpret_cast<tstrSocketRecvMsg*>(pvMsg);
|
|
if (recvMsg && (recvMsg->s16BufferSize > 0)) {
|
|
if (auto server = ServerSocket::findConnected(sock)) {
|
|
server->onRecv(recvMsg->pu8Buffer, recvMsg->s16BufferSize, recvMsg->u16RemainingSize);
|
|
}
|
|
} else {
|
|
if (auto server = ServerSocket::findConnected(sock)) {
|
|
server->onClose();
|
|
}
|
|
}
|
|
} break;
|
|
case SOCKET_MSG_SEND: {
|
|
if (auto server = ServerSocket::findConnected(sock)) {
|
|
server->onSendDone();
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
class WifiHelperThread : public ThreadController<4096> {
|
|
public:
|
|
WifiHelperThread() : ThreadController("WiFi", WIFI_THREAD_PRIORITY) {}
|
|
void ThreadTask() override {
|
|
if (!initWifi()) {
|
|
return;
|
|
}
|
|
|
|
m_initDone = true;
|
|
|
|
while (true)
|
|
{
|
|
m2m_wifi_handle_events(nullptr);
|
|
|
|
if (!ServerSocket::checkSend()) {
|
|
isrSemaphore.wait(TIME_MS2I(1));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool initDone() const {
|
|
return m_initDone;
|
|
}
|
|
|
|
private:
|
|
bool initWifi() {
|
|
// Initialize the WiFi module
|
|
static tstrWifiInitParam param;
|
|
param.pfAppWifiCb = wifiCallback;
|
|
if (auto ret = m2m_wifi_init(¶m); M2M_SUCCESS != ret) {
|
|
efiPrintf("Wifi init failed with: %d", ret);
|
|
return false;
|
|
}
|
|
|
|
static tstrM2MAPConfig apConfig;
|
|
strncpy(apConfig.au8SSID, config->wifiAccessPointSsid, std::min(sizeof(apConfig.au8SSID), sizeof(config->wifiAccessPointSsid)));
|
|
apConfig.u8ListenChannel = 1;
|
|
apConfig.u8SsidHide = 0;
|
|
|
|
size_t keyLength = strlen(config->wifiAccessPointPassword);
|
|
if (keyLength > 0) {
|
|
apConfig.u8SecType = M2M_WIFI_SEC_WPA_PSK;
|
|
apConfig.u8KeySz = keyLength;
|
|
strncpy((char*)apConfig.au8Key, config->wifiAccessPointPassword, std::min(sizeof(apConfig.au8Key), sizeof(config->wifiAccessPointPassword)));
|
|
} else {
|
|
apConfig.u8SecType = M2M_WIFI_SEC_OPEN;
|
|
}
|
|
|
|
// IP Address
|
|
apConfig.au8DHCPServerIP[0] = 192;
|
|
apConfig.au8DHCPServerIP[1] = 168;
|
|
apConfig.au8DHCPServerIP[2] = 10;
|
|
apConfig.au8DHCPServerIP[3] = 1;
|
|
|
|
// Trigger AP
|
|
if (M2M_SUCCESS != m2m_wifi_enable_ap(&apConfig)) {
|
|
return false;
|
|
}
|
|
|
|
// Set up the socket APIs
|
|
socketInit();
|
|
registerSocketCallback(socketCallback, nullptr);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool m_initDone = false;
|
|
};
|
|
|
|
static NO_CACHE WifiHelperThread wifiHelper;
|
|
|
|
void initWifi() {
|
|
wifiHelper.start();
|
|
}
|
|
|
|
void waitForWifiInit() {
|
|
while (!wifiHelper.initDone()) {
|
|
chThdSleepMilliseconds(10);
|
|
}
|
|
}
|