diff --git a/inc/config.h b/inc/config.h index 9aad9d35..9780dc29 100644 --- a/inc/config.h +++ b/inc/config.h @@ -160,7 +160,7 @@ * Set it to 1 if you want to enable the TTY text console module (written by Andreas Dietrich).
* It consume about 34 bytes of memory when enabled. */ -#define MODULE_CONSOLE 1 +#define MODULE_CONSOLE 0 #endif // _CONFIG_ diff --git a/inc/ext/mw/16c550.h b/inc/ext/mw/16c550.h index 6e5754a1..dd066bc1 100644 --- a/inc/ext/mw/16c550.h +++ b/inc/ext/mw/16c550.h @@ -1,184 +1,193 @@ -/************************************************************************//** - * \brief Simple 16C550 UART chip driver. - * - * \author Jesus Alonso (doragasu) - * \date 2016 - * \defgroup 16C550 16c550 - * \{ - ****************************************************************************/ - -#ifndef _16C550_H_ -#define _16C550_H_ - -#include "types.h" - -#if (MODULE_MEGAWIFI != 0) - -/// 16C550 UART base address -#define UART_BASE 0xA130C1 - -/// Clock applied to 16C550 chip. Currently using 24 MHz crystal -#define UART_CLK 24000000LU - -/// Desired baud rate. Maximum achievable baudrate with 24 MHz crystal -/// is 24000000/16 = 1.5 Mbps -#define UART_BR 1500000LU -//#define UART_BR 500000LU -//#define UART_BR 750000LU -//#define UART_BR 115200 - -/// Length of the TX FIFO in bytes -#define UART_TX_FIFO_LEN 16 - -/// Division with one bit rounding, useful for divisor calculations. -#define DivWithRounding(dividend, divisor) ((((dividend)*2/(divisor))+1)/2) -/// Value to load on the UART divisor, high byte -#define UART_DLM_VAL (DivWithRounding(UART_CLK, 16 * UART_BR)>>8) -//#define UART_DLM_VAL ((UART_CLK/16/UART_BR)>>8) -/// Value to load on the UART divisor, low byte -#define UART_DLL_VAL (DivWithRounding(UART_CLK, 16 * UART_BR) & 0xFF) -//#define UART_DLL_VAL ((UART_CLK/16/UART_BR)&0xFF) - -/** \addtogroup UartRegs UartRegs - * \brief 16C550 UART registers - * \note Do NOT access IER, FCR, LCR and MCR directly, use Set/Get functions. - * Remaining registers can be directly accessed, but meeting the - * read only/write only restrictions. - * \{ - */ -/// Receiver holding register. Read only. -#define UART_RHR (*((volatile uint8_t*)(UART_BASE + 0))) -/// Transmit holding register. Write only. -#define UART_THR (*((volatile uint8_t*)(UART_BASE + 0))) -/// Interrupt enable register. Write only. -#define UART_IER (*((volatile uint8_t*)(UART_BASE + 2))) -/// FIFO control register. Write only. -#define UART_FCR (*((volatile uint8_t*)(UART_BASE + 4))) -/// Interrupt status register. Read only. -#define UART_ISR (*((volatile uint8_t*)(UART_BASE + 4))) -/// Line control register. Write only. -#define UART_LCR (*((volatile uint8_t*)(UART_BASE + 6))) -/// Modem control register. Write only. -#define UART_MCR (*((volatile uint8_t*)(UART_BASE + 8))) -/// Line status register. Read only. -#define UART_LSR (*((volatile uint8_t*)(UART_BASE + 10))) -/// Modem status register. Read only. -#define UART_MSR (*((volatile uint8_t*)(UART_BASE + 12))) -/// Scratchpad register. -#define UART_SPR (*((volatile uint8_t*)(UART_BASE + 14))) -/// Divisor latch LSB. Acessed only when LCR[7] = 1. -#define UART_DLL (*((volatile uint8_t*)(UART_BASE + 0))) -/// Divisor latch MSB. Acessed only when LCR[7] = 1. -#define UART_DLM (*((volatile uint8_t*)(UART_BASE + 2))) -/** \} */ - -/// Structure with the shadow registers. -typedef struct { - uint8_t IER; ///< Interrupt Enable Register - uint8_t FCR; ///< FIFO Control Register - uint8_t LCR; ///< Line Control Register - uint8_t MCR; ///< Modem Control Register -} UartShadow; - -/// Uart shadow registers. Do NOT access directly! -extern UartShadow sh; - -/** \addtogroup UartOuts UartOuts - * \brief Output pins controlled by the MCR UART - * register. - * \{ */ -#define UART_MCR__DTR 0x01 ///< Data Terminal Ready. -#define UART_MCR__RTS 0x02 ///< Request To Send. -#define UART_MCR__OUT1 0x04 ///< GPIO pin 1. -#define UART_MCR__OUT2 0x08 ///< GPIO pin 2. -/** \} */ - -/** \addtogroup UartIns UartIns - * \brief Input pins readed in the MSR UART register. - * \{ */ -#define UART_MSR__DSR 0x20 ///< Data Set Ready -/** \} */ - -/************************************************************************//** - * \brief Initializes the driver. The baud rate is set to UART_BR, and the - * UART FIFOs are enabled. This function must be called before using - * any other API call. - ****************************************************************************/ -void uart_init(void); - -/************************************************************************//** - * \brief Checks if UART transmit register/FIFO is ready. In FIFO mode, up to - * 16 characters can be loaded each time transmitter is ready. - * - * \return TRUE if transmitter is ready, FALSE otherwise. - ****************************************************************************/ -#define uart_tx_ready() (UART_LSR & 0x20) - -/************************************************************************//** - * \brief Checks if UART receive register/FIFO has data available. - * - * \return TRUE if at least 1 byte is available, FALSE otherwise. - ****************************************************************************/ -#define uart_rx_ready() (UART_LSR & 0x01) - -/************************************************************************//** - * \brief Sends a character. Please make sure there is room in the transmit - * register/FIFO by calling uart_rx_ready() before using this function. - * - * \return Received character. - ****************************************************************************/ -#define uart_putc(c) do{UART_RHR = (c);}while(0); - -/************************************************************************//** - * \brief Returns a received character. Please make sure data is available by - * calling uart_rx_ready() before using this function. - * - * \return Received character. - ****************************************************************************/ -#define uart_getc() (UART_RHR) - -/************************************************************************//** - * \brief Sets a value in IER, FCR, LCR or MCR register. - * - * \param[in] reg Register to modify (IER, FCR, LCR or MCR). - * \param[in] val Value to set in IER, FCR, LCR or MCR register. - ****************************************************************************/ -#define uart_set(reg, val) do{sh.reg = (val);UART_##reg = (val);}while(0) - -/************************************************************************//** - * \brief Gets value of IER, FCR, LCR or MCR register. - * - * \param[in] reg Register to read (IER, FCR, LCR or MCR). - * \return The value of the requested register. - ****************************************************************************/ -#define uart_get(reg) (sh.reg) - -/************************************************************************//** - * \brief Sets bits in IER, FCR, LCR or MCR register. - * - * \param[in] reg Register to modify (IER, FCR, LCR or MCR). - * \param[in] val Bits set in val, will be set in reg register. - ****************************************************************************/ -#define uart_set_bits(reg, val) do{sh.reg |= (val); \ - UART_##reg = sh.reg;}while(0) - -/************************************************************************//** - * \brief Clears bits in IER, FCR, LCR or MCR register. - * - * \param[in] reg Register to modify (IER, FCR, LCR or MCR). - * \param[in] val Bits set in val, will be cleared in reg register. - ****************************************************************************/ -#define uart_clr_bits(reg, val) do{sh.reg &= ~(val); \ - UART_##reg = sh.reg;}while(0) - -/************************************************************************//** - * \brief Reset TX and RX FIFOs. - ****************************************************************************/ -#define uart_reset_fifos() uart_set_bits(FCR, 0x07) - -#endif // MODULE_MEGAWIFI - -#endif /*_16C550_H_*/ - -/** \} */ - +/************************************************************************//** + * \brief Simple 16C550 UART chip driver. + * + * \author Jesus Alonso (doragasu) + * \date 2016 + * \defgroup 16C550 16c550 + * \{ + ****************************************************************************/ + +#ifndef _16C550_H_ +#define _16C550_H_ + +#include "types.h" + +#if (MODULE_MEGAWIFI == 1 && MODULE_EVERDRIVE == 0) + +/// 16C550 UART base address +#define UART_BASE 0xA130C1 + +/// Clock applied to 16C550 chip. Currently using 24 MHz crystal +#define UART_CLK 24000000LU + +/// Desired baud rate. Maximum achievable baudrate with 24 MHz crystal +/// is 24000000/16 = 1.5 Mbps +#define UART_BR 1500000LU +//#define UART_BR 500000LU +//#define UART_BR 750000LU +//#define UART_BR 115200 + +/// Length of the TX FIFO in bytes +#define UART_TX_FIFO_LEN 16 + +/// Division with one bit rounding, useful for divisor calculations. +#define DivWithRounding(dividend, divisor) ((((dividend)*2/(divisor))+1)/2) +/// Value to load on the UART divisor, high byte +#define UART_DLM_VAL (DivWithRounding(UART_CLK, 16 * UART_BR)>>8) +//#define UART_DLM_VAL ((UART_CLK/16/UART_BR)>>8) +/// Value to load on the UART divisor, low byte +#define UART_DLL_VAL (DivWithRounding(UART_CLK, 16 * UART_BR) & 0xFF) +//#define UART_DLL_VAL ((UART_CLK/16/UART_BR)&0xFF) + +/** \addtogroup UartRegs UartRegs + * \brief 16C550 UART registers + * \note Do NOT access IER, FCR, LCR and MCR directly, use Set/Get functions. + * Remaining registers can be directly accessed, but meeting the + * read only/write only restrictions. + * \{ + */ +/// Receiver holding register. Read only. +#define UART_RHR (*((volatile uint8_t*)(UART_BASE + 0))) +/// Transmit holding register. Write only. +#define UART_THR (*((volatile uint8_t*)(UART_BASE + 0))) +/// Interrupt enable register. Write only. +#define UART_IER (*((volatile uint8_t*)(UART_BASE + 2))) +/// FIFO control register. Write only. +#define UART_FCR (*((volatile uint8_t*)(UART_BASE + 4))) +/// Interrupt status register. Read only. +#define UART_ISR (*((volatile uint8_t*)(UART_BASE + 4))) +/// Line control register. Write only. +#define UART_LCR (*((volatile uint8_t*)(UART_BASE + 6))) +/// Modem control register. Write only. +#define UART_MCR (*((volatile uint8_t*)(UART_BASE + 8))) +/// Line status register. Read only. +#define UART_LSR (*((volatile uint8_t*)(UART_BASE + 10))) +/// Modem status register. Read only. +#define UART_MSR (*((volatile uint8_t*)(UART_BASE + 12))) +/// Scratchpad register. +#define UART_SPR (*((volatile uint8_t*)(UART_BASE + 14))) +/// Divisor latch LSB. Acessed only when LCR[7] = 1. +#define UART_DLL (*((volatile uint8_t*)(UART_BASE + 0))) +/// Divisor latch MSB. Acessed only when LCR[7] = 1. +#define UART_DLM (*((volatile uint8_t*)(UART_BASE + 2))) +/** \} */ + +/// Structure with the shadow registers. +typedef struct { + uint8_t IER; ///< Interrupt Enable Register + uint8_t FCR; ///< FIFO Control Register + uint8_t LCR; ///< Line Control Register + uint8_t MCR; ///< Modem Control Register +} UartShadow; + +/// Uart shadow registers. Do NOT access directly! +extern UartShadow sh; + +/** \addtogroup UartOuts UartOuts + * \brief Output pins controlled by the MCR UART + * register. + * \{ */ +#define UART_MCR__DTR 0x01 ///< Data Terminal Ready. +#define UART_MCR__RTS 0x02 ///< Request To Send. +#define UART_MCR__OUT1 0x04 ///< GPIO pin 1. +#define UART_MCR__OUT2 0x08 ///< GPIO pin 2. +/** \} */ + +/** \addtogroup UartIns UartIns + * \brief Input pins readed in the MSR UART register. + * \{ */ +#define UART_MSR__DSR 0x20 ///< Data Set Ready +/** \} */ + +/************************************************************************//** + * \brief Initializes the driver. The baud rate is set to UART_BR, and the + * UART FIFOs are enabled. This function must be called before using + * any other API call. + ****************************************************************************/ +void uart_init(void); + +/************************************************************************//** + * \brief Checks if UART transmit register/FIFO is ready. In FIFO mode, up to + * 16 characters can be loaded each time transmitter is ready. + * + * \return TRUE if transmitter is ready, FALSE otherwise. + ****************************************************************************/ +#define uart_tx_ready() (UART_LSR & 0x20) + +/************************************************************************//** + * \brief Checks if UART receive register/FIFO has data available. + * + * \return TRUE if at least 1 byte is available, FALSE otherwise. + ****************************************************************************/ +#define uart_rx_ready() (UART_LSR & 0x01) + +/************************************************************************//** + * \brief Sends a character. Please make sure there is room in the transmit + * register/FIFO by calling uart_rx_ready() before using this function. + * + * \return Received character. + ****************************************************************************/ +#define uart_putc(c) do{UART_RHR = (c);}while(0); + +/************************************************************************//** + * \brief Returns a received character. Please make sure data is available by + * calling uart_rx_ready() before using this function. + * + * \return Received character. + ****************************************************************************/ +#define uart_getc() (UART_RHR) + +/************************************************************************//** + * \brief Sets a value in IER, FCR, LCR or MCR register. + * + * \param[in] reg Register to modify (IER, FCR, LCR or MCR). + * \param[in] val Value to set in IER, FCR, LCR or MCR register. + ****************************************************************************/ +#define uart_set(reg, val) do{sh.reg = (val);UART_##reg = (val);}while(0) + +/************************************************************************//** + * \brief Gets value of IER, FCR, LCR or MCR register. + * + * \param[in] reg Register to read (IER, FCR, LCR or MCR). + * \return The value of the requested register. + ****************************************************************************/ +#define uart_get(reg) (sh.reg) + +/************************************************************************//** + * \brief Sets bits in IER, FCR, LCR or MCR register. + * + * \param[in] reg Register to modify (IER, FCR, LCR or MCR). + * \param[in] val Bits set in val, will be set in reg register. + ****************************************************************************/ +#define uart_set_bits(reg, val) do{sh.reg |= (val); \ + UART_##reg = sh.reg;}while(0) + +/************************************************************************//** + * \brief Clears bits in IER, FCR, LCR or MCR register. + * + * \param[in] reg Register to modify (IER, FCR, LCR or MCR). + * \param[in] val Bits set in val, will be cleared in reg register. + ****************************************************************************/ +#define uart_clr_bits(reg, val) do{sh.reg &= ~(val); \ + UART_##reg = sh.reg;}while(0) + +/************************************************************************//** + * \brief Reset TX and RX FIFOs. + ****************************************************************************/ +#define uart_reset_fifos() uart_set_bits(FCR, 0x07) + +/************************************************************************//** + * \brief Test Connection with registers + * + * \param[in] reg Register to modify + * \param[in] val Bits set in val, will be readed from reg register. + ****************************************************************************/ +#define uart_test(reg, val) reg = val; \ + if (reg != val) return MW_ERR + +#endif // MODULE_MEGAWIFI + +#endif /*_16C550_H_*/ + +/** \} */ + diff --git a/inc/ext/mw/lsd.h b/inc/ext/mw/lsd.h index f876746f..fa635192 100644 --- a/inc/ext/mw/lsd.h +++ b/inc/ext/mw/lsd.h @@ -1,181 +1,186 @@ -/************************************************************************//** - * \brief Local Symmetric Data-link. Implements an extremely simple - * protocol to link two full-duplex devices, multiplexing the - * data link. - * - * The multiplexing facility allows having up to LSD_MAX_CH simultaneous - * channels on the serial link. - * - * The module has synchronous functions to send/receive data (easy to use, but - * due to polling hang the console until transfer is complete) and their - * asyncronous counterparts. The asynchronous functions return immediately, - * but require calling frequently lsd_process() to actually send/receive data. - * Once the asynchronous functions complete sending/receiving data, the - * specified callback is run. - * - * \author Jesus Alonso (doragasu) - * \date 2019 - * \note Unfortunately the Megadrive does have neither an interrupt pin nor - * DMA threshold pins in the cartridge slot, so polling is the only - * way. So you have - * Megadrive does not have an interrupt pin on the cart, implementing - * more efficient data transmission techniques will be tricky. - * \warning The syncrhonous API is easier to use, but a lot less reliable: - * * It polls, using all the CPU until the send/recv operation completes. - * * A lsd_recv_sync() can freeze the machine if no frame is received. USE IT - * WITH CARE! - * - * \defgroup lsd lsd - * \{ - ****************************************************************************/ - -/* - * Frame format is: - * - * STX : CH-LENH : LENL : DATA : ETX - * - * - STX and ETX are the start/end of transmission characters (1 byte each). - * - CH-LENH is the channel number (first 4 bits) and the 4 high bits of the - * data length. - * - LENL is the low 8 bits of the data length. - * - DATA is the payload, of the previously specified length. - */ -#ifndef _LSD_H_ -#define _LSD_H_ - -#include "16c550.h" -#include "mw-msg.h" - -#if (MODULE_MEGAWIFI != 0) - -/// LSD frame overhead in bytes -#define LSD_OVERHEAD 4 - -/// Maximum number of available simultaneous channels -#define LSD_MAX_CH 4 - -/// Maximum data payload length -#define LSD_MAX_LEN 4095 - -/// Number of buffer frames available -#define LSD_BUF_FRAMES 2 - -/// Return status codes for LSD functions -enum lsd_status { - LSD_STAT_ERR_FRAMING = -5, ///< Frame format error - LSD_STAT_ERR_INVALID_CH = -4, ///< Invalid channel - LSD_STAT_ERR_FRAME_TOO_LONG = -3, ///< Frame is too long - LSD_STAT_ERR_IN_PROGRESS = -2, ///< Operation in progress - LSD_STAT_ERROR = -1, ///< General error - LSD_STAT_COMPLETE = 0, ///< No error - LSD_STAT_BUSY = 1 ///< Doing requested operation -}; - -/// Callback for the asynchronous lsd_send() function. -typedef void (*lsd_send_cb)(enum lsd_status stat, void *ctx); -/// Callback for the asynchronous lsd_recv() function. -typedef void (*lsd_recv_cb)(enum lsd_status stat, uint8_t ch, - char *data, uint16_t len, void *ctx); - -/************************************************************************//** - * \brief Module initialization. - ****************************************************************************/ -void lsd_init(void); - -/************************************************************************//** - * \brief Enables a channel to start reception and be able to send data. - * - * \param[in] ch Channel number. - * - * \return LSD_OK on success, LSD_ERROR otherwise. - ****************************************************************************/ -int lsd_ch_enable(uint8_t ch); - -/************************************************************************//** - * \brief Disables a channel to stop reception and prohibit sending data. - * - * \param[in] ch Channel number. - * - * \return LSD_OK on success, LSD_ERROR otherwise. - ****************************************************************************/ -int lsd_ch_disable(uint8_t ch); - - -/************************************************************************//** - * \brief Asynchronously sends data through a previously enabled channel. - * - * \param[in] ch Channel number to use. - * \param[in] data Buffer to send. - * \param[in] len Length of the buffer to send. - * \param[in] ctx Context for the send callback function. - * \param[in] send_cb Callback to run when send completes or errors. - * - * \return Status of the send procedure. Usually LSD_STAT_BUSY is returned, - * and the send procedure is then performed in background. - * \note Calling this function while there is a send procedure in progress, - * will cause the function call to fail with LSD_STAT_SEND_ERR_IN_PROGRESS. - ****************************************************************************/ -enum lsd_status lsd_send(uint8_t ch, const char *data, int16_t len, - void *ctx, lsd_send_cb send_cb); - -/************************************************************************//** - * \brief Synchronously sends data through a previously enabled channel. - * - * \param[in] ch Channel number to use. - * \param[in] data Buffer to send. - * \param[in] len Length of the buffer to send. - * - * \return Status of the send procedure. - * \warning This function polls until the procedure is complete (or errors). - ****************************************************************************/ -enum lsd_status lsd_send_sync(uint8_t ch, const char *data, int16_t len); - -/************************************************************************//** - * \brief Asyncrhonously Receives a frame using LSD protocol. - * - * \param[in] buf Buffer for reception. - * \param[in] len Buffer length. - * \param[in] ctx Context for the receive callback function. - * \param[in] recv_cb Callback to run when receive completes or errors. - * - * \return Status of the receive procedure. - ****************************************************************************/ -enum lsd_status lsd_recv(char *buf, int16_t len, void *ctx, - lsd_recv_cb recv_cb); - -/************************************************************************//** - * \brief Syncrhonously Receives a frame using LSD protocol. - * - * \param[out] buf Buffer for received data. - * \param[inout] len On input: buffer length. On output: received frame length. - * \param[out] ch Channel on which the data has been received. - * - * \warning This function polls until the reception is complete, or a reception - * error occurs. - * \warning If no frame is received when this function is called, the machine - * will lock. - ****************************************************************************/ -enum lsd_status lsd_recv_sync(char *buf, uint16_t *len, uint8_t *ch); - -/************************************************************************//** - * \brief Processes sends/receives pending data. - * - * Call this function as much as possible when using the asynchronous - * lsd_send() and lsd_receive() functions. - ****************************************************************************/ -void lsd_process(void); - -/************************************************************************//** - * \brief Sends syncrhonization frame. - * - * This function sends a chunk of 0x55 bytes to help physical layer to - * synchronize. It is usually not necessary to use this function, but might - * help some UART chips to compute an accurate clock. - ****************************************************************************/ -void lsd_line_sync(void); - -#endif // MODULE_MEGAWIFI - -#endif //_LSD_H_ - -/** \} */ +/************************************************************************//** + * \brief Local Symmetric Data-link. Implements an extremely simple + * protocol to link two full-duplex devices, multiplexing the + * data link. + * + * The multiplexing facility allows having up to LSD_MAX_CH simultaneous + * channels on the serial link. + * + * The module has synchronous functions to send/receive data (easy to use, but + * due to polling hang the console until transfer is complete) and their + * asyncronous counterparts. The asynchronous functions return immediately, + * but require calling frequently lsd_process() to actually send/receive data. + * Once the asynchronous functions complete sending/receiving data, the + * specified callback is run. + * + * \author Jesus Alonso (doragasu) + * \author Juan Antonio (PaCHoN) + * \date 2019~2025 + * \note Unfortunately the Megadrive does have neither an interrupt pin nor + * DMA threshold pins in the cartridge slot, so polling is the only + * way. So you have + * Megadrive does not have an interrupt pin on the cart, implementing + * more efficient data transmission techniques will be tricky. + * \warning The syncrhonous API is easier to use, but a lot less reliable: + * * It polls, using all the CPU until the send/recv operation completes. + * * A lsd_recv_sync() can freeze the machine if no frame is received. USE IT + * WITH CARE! + * + * \defgroup lsd lsd + * \{ + ****************************************************************************/ + +/* + * Frame format is: + * + * STX : CH-LENH : LENL : DATA : ETX + * + * - STX and ETX are the start/end of transmission characters (1 byte each). + * - CH-LENH is the channel number (first 4 bits) and the 4 high bits of the + * data length. + * - LENL is the low 8 bits of the data length. + * - DATA is the payload, of the previously specified length. + */ +#ifndef _LSD_H_ +#define _LSD_H_ + +#if (MODULE_MEGAWIFI == 1 && MODULE_EVERDRIVE == 0) + #include "16c550.h" +#elif (MODULE_MEGAWIFI == 1 && MODULE_EVERDRIVE == 1) + #include "ssf.h" +#endif +#include "mw-msg.h" + +#if (MODULE_MEGAWIFI == 1) + +/// LSD frame overhead in bytes +#define LSD_OVERHEAD 4 + +/// Maximum number of available simultaneous channels +#define LSD_MAX_CH 4 + +/// Maximum data payload length +#define LSD_MAX_LEN 4095 + +/// Number of buffer frames available +#define LSD_BUF_FRAMES 2 + +/// Return status codes for LSD functions +enum lsd_status { + LSD_STAT_ERR_FRAMING = -5, ///< Frame format error + LSD_STAT_ERR_INVALID_CH = -4, ///< Invalid channel + LSD_STAT_ERR_FRAME_TOO_LONG = -3, ///< Frame is too long + LSD_STAT_ERR_IN_PROGRESS = -2, ///< Operation in progress + LSD_STAT_ERROR = -1, ///< General error + LSD_STAT_COMPLETE = 0, ///< No error + LSD_STAT_BUSY = 1 ///< Doing requested operation +}; + +/// Callback for the asynchronous lsd_send() function. +typedef void (*lsd_send_cb)(enum lsd_status stat, void *ctx); +/// Callback for the asynchronous lsd_recv() function. +typedef void (*lsd_recv_cb)(enum lsd_status stat, uint8_t ch, + char *data, uint16_t len, void *ctx); + +/************************************************************************//** + * \brief Module initialization. + ****************************************************************************/ +void lsd_init(void); + +/************************************************************************//** + * \brief Enables a channel to start reception and be able to send data. + * + * \param[in] ch Channel number. + * + * \return LSD_OK on success, LSD_ERROR otherwise. + ****************************************************************************/ +int lsd_ch_enable(uint8_t ch); + +/************************************************************************//** + * \brief Disables a channel to stop reception and prohibit sending data. + * + * \param[in] ch Channel number. + * + * \return LSD_OK on success, LSD_ERROR otherwise. + ****************************************************************************/ +int lsd_ch_disable(uint8_t ch); + + +/************************************************************************//** + * \brief Asynchronously sends data through a previously enabled channel. + * + * \param[in] ch Channel number to use. + * \param[in] data Buffer to send. + * \param[in] len Length of the buffer to send. + * \param[in] ctx Context for the send callback function. + * \param[in] send_cb Callback to run when send completes or errors. + * + * \return Status of the send procedure. Usually LSD_STAT_BUSY is returned, + * and the send procedure is then performed in background. + * \note Calling this function while there is a send procedure in progress, + * will cause the function call to fail with LSD_STAT_SEND_ERR_IN_PROGRESS. + ****************************************************************************/ +enum lsd_status lsd_send(uint8_t ch, const char *data, int16_t len, + void *ctx, lsd_send_cb send_cb); + +/************************************************************************//** + * \brief Synchronously sends data through a previously enabled channel. + * + * \param[in] ch Channel number to use. + * \param[in] data Buffer to send. + * \param[in] len Length of the buffer to send. + * + * \return Status of the send procedure. + * \warning This function polls until the procedure is complete (or errors). + ****************************************************************************/ +enum lsd_status lsd_send_sync(uint8_t ch, const char *data, int16_t len); + +/************************************************************************//** + * \brief Asyncrhonously Receives a frame using LSD protocol. + * + * \param[in] buf Buffer for reception. + * \param[in] len Buffer length. + * \param[in] ctx Context for the receive callback function. + * \param[in] recv_cb Callback to run when receive completes or errors. + * + * \return Status of the receive procedure. + ****************************************************************************/ +enum lsd_status lsd_recv(char *buf, int16_t len, void *ctx, + lsd_recv_cb recv_cb); + +/************************************************************************//** + * \brief Syncrhonously Receives a frame using LSD protocol. + * + * \param[out] buf Buffer for received data. + * \param[inout] len On input: buffer length. On output: received frame length. + * \param[out] ch Channel on which the data has been received. + * + * \warning This function polls until the reception is complete, or a reception + * error occurs. + * \warning If no frame is received when this function is called, the machine + * will lock. + ****************************************************************************/ +enum lsd_status lsd_recv_sync(char *buf, uint16_t *len, uint8_t *ch); + +/************************************************************************//** + * \brief Processes sends/receives pending data. + * + * Call this function as much as possible when using the asynchronous + * lsd_send() and lsd_receive() functions. + ****************************************************************************/ +void lsd_process(void); + +/************************************************************************//** + * \brief Sends syncrhonization frame. + * + * This function sends a chunk of 0x55 bytes to help physical layer to + * synchronize. It is usually not necessary to use this function, but might + * help some UART chips to compute an accurate clock. + ****************************************************************************/ +void lsd_line_sync(void); + +#endif // MODULE_MEGAWIFI + +#endif //_LSD_H_ + +/** \} */ diff --git a/inc/ext/mw/megawifi.h b/inc/ext/mw/megawifi.h index 147e5732..e2286525 100644 --- a/inc/ext/mw/megawifi.h +++ b/inc/ext/mw/megawifi.h @@ -1,979 +1,1015 @@ -/************************************************************************//** - * \file - * - * \brief MegaWiFi API implementation. - * - * \defgroup megawifi megawifi - * \{ - * - * \brief MegaWiFi API implementation. - * - * API to communicate with the wifi module and the Internet. API calls are - * documented and most of them are self explanatory. Mostly the only weird - * thing about the API is UDP reuse mode. If you enable reuse mode (setting - * the dst_addr and/or dst_port to NULL in the mw_udp_set() call), received - * data will prepend the IP and port of the peer (using mw_reuse_payload data - * structure), and data to be sent also requires the IP and port to be - * prepended to the payload. - * - * \author Jesus Alonso (doragasu) - * \date 2015 - * - * \note This module requires setting MODULE_MEGAWIFI to 1 in config.h and - * rebuilding the library (if you had to change them). - ****************************************************************************/ - -#ifndef _MEGAWIFI_H_ -#define _MEGAWIFI_H_ - -#include "16c550.h" -#include "mw-msg.h" -#include "lsd.h" - -#if (MODULE_MEGAWIFI != 0) - - -/// API version implemented, major number -#define MW_API_VERSION_MAJOR 1 - -/// API version implemented, minor number -#define MW_API_VERSION_MINOR 5 - -/// Timeout for standard commands in milliseconds -#define MW_COMMAND_TOUT_MS 1000 -/// Timeout for TCP connections -#define MW_CONNECT_TOUT_MS 10000 -/// Timeout for HTTP open command in milliseconds -#define MW_HTTP_OPEN_TOUT_MS 10000 -/// Timeout for the AP scan command in milliseconds -#define MW_SCAN_TOUT_MS 10000 -/// Timeout for the AP associate command in milliseconds -#define MW_ASSOC_TOUT_MS 20000 -/// Time to sleep before waiting for assoc in milliseconds -#define MW_ASSOC_WAIT_SLEEP_MS 5000 -/// Timeout for upgrade command in milliseconds -#define MW_UPGRADE_TOUT_MS 180000 -/// Milliseconds between status polls while in wm_ap_assoc_wait() -#define MW_STAT_POLL_MS 250 - -/// Error codes for MegaWiFi API functions -enum mw_err { - MW_ERR_NONE = 0, ///< No error (success) - MW_ERR, ///< General error - MW_ERR_NOT_READY, ///< Not ready to run command - MW_ERR_BUFFER_TOO_SHORT, ///< Command buffer is too small - MW_ERR_PARAM, ///< Input parameter out of range - MW_ERR_SEND, ///< Error sending data - MW_ERR_RECV ///< Error receiving data -}; - -/// Supported HTTP methods -enum mw_http_method { - MW_HTTP_METHOD_GET = 0, ///< HTTP GET Method - MW_HTTP_METHOD_POST, ///< HTTP POST Method - MW_HTTP_METHOD_PUT, ///< HTTP PUT Method - MW_HTTP_METHOD_PATCH, ///< HTTP PATCH Method - MW_HTTP_METHOD_DELETE, ///< HTTP DELETE Method - MW_HTTP_METHOD_HEAD, ///< HTTP HEAD Method - MW_HTTP_METHOD_NOTIFY, ///< HTTP NOTIFY Method - MW_HTTP_METHOD_SUBSCRIBE, ///< HTTP SUBSCRIBE Method - MW_HTTP_METHOD_UNSUBSCRIBE,///< HTTP UNSUBSCRIBE - MW_HTTP_METHOD_OPTIONS, ///< HTTP OPTIONS - MW_HTTP_METHOD_MAX, -}; - -/** \addtogroup mw_ctrl_pins mw_ctrl_pins - * \brief Pins used to control WiFi module. - * \{ */ -#define MW__RESET UART_MCR__OUT1 ///< Reset out. -#define MW__PRG UART_MCR__OUT2 ///< Program out. -#define MW__PD UART_MCR__DTR ///< Power Down out. -#define MW__DAT UART_MSR__DSR ///< Data request in. -/** \} */ - -/// Maximum SSID length (including '\0'). -#define MW_SSID_MAXLEN 32 -/// Maximum password length (including '\0'). -#define MW_PASS_MAXLEN 64 -/// Maximum length of an NTP pool URI (including '\0'). -#define MW_NTP_POOL_MAXLEN 80 -/// Number of AP configurations stored to nvflash. -#define MW_NUM_CFG_SLOTS 3 -/// Number of DSN servers supported per AP configuration. -#define MW_NUM_DNS_SERVERS 2 -/// Length of the FSM queue -#define MW_FSM_QUEUE_LEN 8 -/// Maximum number of simultaneous TCP connections -#define MW_MAX_SOCK 3 -/// Control channel used for LSD protocol -#define MW_CTRL_CH 0 -/// Channel used for HTTP requests and cert sets -#define MW_HTTP_CH LSD_MAX_CH - 1 - -/// Minimum command buffer length to be able to send all available commands -/// with minimum data payload. This length might not guarantee that commands -/// like mw_sntp_cfg_set() can be sent if payload length is big enough). -#define MW_CMD_MIN_BUFLEN 168 - -/// Access Point data. -struct mw_ap_data { - enum mw_security auth; ///< Security type - uint8_t channel; ///< WiFi channel. - int8_t rssi; ///< Signal strength. - uint8_t ssid_len; ///< Length of ssid field. - char *ssid; ///< SSID string (not NULL terminated). -}; - -/// Interface type for the mw_bssid_get() function. -enum mw_if_type { - MW_IF_STATION = 0, ///< Station interface - MW_IF_SOFTAP, ///< Access Point interface - MW_IF_MAX ///< Number of supported interface types -}; - -/************************************************************************//** - * \brief Module initialization. Must be called once before using any - * other function. It also initializes de UART. - * - * \param[in] cmd_buf Pointer to the buffer used to send and receive commands. - * \param[in] buf_len Length of cmdBuf in bytes. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -int16_t mw_init(uint16_t *cmd_buf, uint16_t buf_len); - -/************************************************************************//** - * \brief Processes sends/receives pending data. - * - * Call this function as much as possible to process incoming/outgoing data. - * - * \warning No data will be sent/received if this function is not frequently - * invoked. - ****************************************************************************/ -static inline void mw_process(void) {lsd_process();} - -/************************************************************************//** - * \brief Sets the callback function to be run when network data is received - * while waiting for a command reply. - * - * \param[in] cmd_recv_cb Callback to be run when data is received while - * waiting for a command reply. - * - * \warning If this callback is not set, data received while waiting for a - * command reply will be silently discarded. - ****************************************************************************/ -void mw_cmd_data_cb_set(lsd_recv_cb cmd_recv_cb); - -/************************************************************************//** - * \brief Performs the startup sequence for the WiFi module, and tries - * detecting it by requesting the version data. - * - * \param[out] major Major version number. - * \param[out] minor Minor version number. - * \param[out] variant String with firmware variant ("std" for standard). - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_detect(uint8_t *major, uint8_t *minor, char **variant); - -/************************************************************************//** - * \brief Obtain module version numbers and string - * - * \param[out] version Version numbers (major, minor, micro) in order. - * \param[out] variant String with firmware variant ("std" for standard). - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_version_get(uint8_t version[3], char **variant); - -/************************************************************************//** - * \brief Gets the module BSSID (the MAC address) for the specified interface. - * - * \param[in] interface_type Type of the interface to obtain BSSID from. - * - * \return The requested BSSID (6 byte binary data), or NULL on error. - ****************************************************************************/ -uint8_t *mw_bssid_get(enum mw_if_type interface_type); - -/************************************************************************//** - * \brief Set default module configuration (AKA factory settings). - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note For this command to take effect, it must be followed by a module - * reset. - ****************************************************************************/ -enum mw_err mw_default_cfg_set(void); - -/************************************************************************//** - * \brief Set access point configuration (SSID and password). - * - * \param[in] slot Configuration slot to use. - * \param[in] ssid String with the AP SSID to set. - * \param[in] pass String with the AP SSID to set. - * \param[in] phy_type Bitmask with the PHY type configuration. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note Strings must be NULL terminated. Maximum SSID length is 32 bytes, - * maximum pass length is 64 bytes. - * \note After a successful invocation, call mw_cfg_save() for changes to - * be persistent - ****************************************************************************/ -enum mw_err mw_ap_cfg_set(uint8_t slot, const char *ssid, const char *pass, - enum mw_phy_type phy_type); - -/************************************************************************//** - * \brief Gets access point configuration (SSID and password). - * - * \param[in] slot Configuration slot to use. - * \param[out] ssid String with the AP SSID got. - * \param[out] pass String with the AP SSID got. - * \param[out] phy_type Bitmask with the PHY type configuration. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \warning ssid is zero padded up to 32 bytes, and pass is zero padded up - * to 64 bytes. If ssid is 32 bytes, it will NOT be NULL terminated. - * Also if pass is 64 bytes, it will NOT be NULL terminated. - ****************************************************************************/ -enum mw_err mw_ap_cfg_get(uint8_t slot, char **ssid, char **pass, - enum mw_phy_type *phy_type); - -/************************************************************************//** - * \brief Set IPv4 configuration. - * - * \param[in] slot Configuration slot to use. - * \param[in] ip Pointer to the mw_ip_cfg structure, with IP configuration. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note After a successful invocation, call mw_cfg_save() for changes to - * be persistent - ****************************************************************************/ -enum mw_err mw_ip_cfg_set(uint8_t slot, const struct mw_ip_cfg *ip); - -/************************************************************************//** - * \brief Get IPv4 configuration. - * - * \param[in] slot Configuration slot to use. - * \param[out] ip Double pointer to mw_ip_cfg structure, with IP conf. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_ip_cfg_get(uint8_t slot, struct mw_ip_cfg **ip); - -/************************************************************************//** - * \brief Set advanced WiFi configuration. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \warning This function is dangerous. Changing these parameters is rarely - * needed, and setting incorrect values, may render the connection unstable - * and/or crash the WiFi module. Invalid configurations can even cause the - * module to crash in a bootloop, requiring a programmer to unbrick it. - * Make sure you thoroughly test the values you allow users to set here. - * \note If you want to change WiFi parameters, the recommendation is to get - * the current configuration via mw_wifi_adv_cfg_get(), and from it change - * only the required parameters. - * \note These parameters will not take effect until saved to non-volatile - * storage (with mw_cfg_save()) and issuing a module reboot. - ****************************************************************************/ -enum mw_err mw_wifi_adv_cfg_set(const struct mw_wifi_adv_cfg *wifi); - -/************************************************************************//** - * \brief Get advanced WiFi configuration. - * - * \return Pointer to the advanced WiFi configuration, or NULL on error. - ****************************************************************************/ -struct mw_wifi_adv_cfg *mw_wifi_adv_cfg_get(void); - -/************************************************************************//** - * \brief Saves changed configuration parameters to non-volatile memory. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_cfg_save(void); - -/************************************************************************//** - * \brief Get current IP configuration, of the joined AP. - * - * \param[out] ip Double pointer to mw_ip_cfg structure, with IP conf. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_ip_current(struct mw_ip_cfg **ip); - -/************************************************************************//** - * \brief Scan for access points. - * - * \param[in] phy_type Bitmask with the PHY type configuration. - * \param[out] ap_data Data of the found access points. Each entry has the - * format specified on the mw_ap_data structure. - * \param[out] aps Number of found access points. - * - * \return Length in bytes of the output data if operation completes - * successfully, or -1 if scan fails. - ****************************************************************************/ -int16_t mw_ap_scan(enum mw_phy_type phy_type, char **ap_data, uint8_t *aps); - -/************************************************************************//** - * \brief Parses received AP data and fills information of the AP at "pos". - * Useful to extract AP information from the data obtained by - * calling mw_ap_scan() function. - * - * \param[in] ap_data Access point data obtained from mw_ap_scan(). - * \param[in] pos Position at which to extract data. - * \param[out] apd Pointer to the extracted data from an AP. - * \param[in] data_len Lenght of apData. - * - * \return Position of the next AP entry in apData, 0 if no more APs - * available or MW_ERROR if ap data/pos combination is not valid. - * - * \note This functions executes locally, does not communicate with the - * WiFi module. - ****************************************************************************/ -int16_t mw_ap_fill_next(const char *ap_data, uint16_t pos, - struct mw_ap_data *apd, uint16_t data_len); - -/************************************************************************//** - * \brief Tries associating to an AP. If successful, also configures IPv4. - * - * \param[in] slot Configuration slot to use. - * - * \return MW_ERR_NONE if AP join operation has been successfully started, - ****************************************************************************/ -enum mw_err mw_ap_assoc(uint8_t slot); - -/************************************************************************//** - * \brief Polls the module status until it reports device is associated to - * AP or timeout occurs. - * - * \param[in] tout_frames Maximun number of frames to wait for association. - * Set to TSK_PEND_FOREVER for an infinite wait. - * - * \return MW_ERR_NONE if device is associated to AP. MW_ERR_NOT_READY if - * the timeout has expired. - ****************************************************************************/ -enum mw_err mw_ap_assoc_wait(int16_t tout_frames); - -/************************************************************************//** - * \brief Sets default AP/IP configuration. - * - * \param[in] slot Configuration slot to use. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note After a successful invocation, call mw_cfg_save() for changes to - * be persistent - ****************************************************************************/ -enum mw_err mw_def_ap_cfg(uint8_t slot); - -/************************************************************************//** - * \brief Dissasociates from a previously associated AP. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_ap_disassoc(void); - -/************************************************************************//** - * \brief Gets default AP/IP configuration slot. - * - * \return The default configuration slot, of -1 on error. - ****************************************************************************/ -int16_t mw_def_ap_cfg_get(void); - -/************************************************************************//** - * \brief Tries establishing a TCP connection with specified server. - * - * \param[in] ch Channel used for the connection. - * \param[in] dst_addr Address (IP or DNS entry) of the server. - * \param[in] dst_port Port in which server is listening. - * \param[in] src_port Port from which try establishing connection. Set to - * 0 or empty string for automatic port allocation. - * - * \return MW_ERR_NONE on success, other code if connection failed. - ****************************************************************************/ -enum mw_err mw_tcp_connect(uint8_t ch, const char *dst_addr, - const char *dst_port, const char *src_port); - -/************************************************************************//** - * \brief Closes and disconnects a socket from specified channel. - * - * This function can be used to free the channel associated to both TCP and - * UDP sockets. - * - * \param[in] ch Channel associated to the socket to disconnect. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_close(uint8_t ch); - -/// Closes a TCP socket. This is an alias of mw_close(). -#define mw_tcp_disconnect(ch) mw_close(ch) - -/************************************************************************//** - * \brief Configures a UDP socket to send/receive data. - * - * \param[in] ch Channel used for the connection. - * \param[in] dst_addr Address (IP or DNS entry) to send data to. - * \param[in] dst_port Port to send data to. - * \param[in] src_port Local port to listen message on. - * - * \return MW_ERR_NONE on success, other code if connection failed. - * - * \note Setting to NULL dst_addr and/or dst_port, enables reuse mode. - ****************************************************************************/ -enum mw_err mw_udp_set(uint8_t ch, const char *dst_addr, const char *dst_port, - const char *src_port); - -/// Frees a UDP socket. This is an alias of mw_close(). -#define mw_udp_unset(ch) mw_close(ch) - -/************************************************************************//** - * \brief Binds a socket to a port, and listens to connections on the port. - * If a connection request is received, it will be automatically - * accepted. - * - * \param[in] ch Channel associated to the socket bound t port. - * \param[in] port Port number to which the socket will be bound. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_tcp_bind(uint8_t ch, uint16_t port); - -/************************************************************************//** - * \brief Polls a socket until it is ready to transfer data. Typical use of - * this function is after a successful mw_tcp_bind(). - * - * \param[in] ch Channel associated to the socket to monitor. - * \param[in] tout_frames Maximum number of frames to wait for connection. - * Set to 0 for an infinite wait. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_sock_conn_wait(uint8_t ch, int16_t tout_frames); - -/************************************************************************//** - * \brief Receive data, asyncrhonous interface. - * - * \param[in] buf Reception buffer. - * \param[in] len Length of the receive buffer. - * \param[in] ctx Context pointer to pass to the reception callbak. - * \param[in] recv_cb Callback to run when reception is complete or errors. - * - * \return Status of the receive procedure. - ****************************************************************************/ -static inline enum lsd_status mw_recv(char *buf, int16_t len, void *ctx, - lsd_recv_cb recv_cb) -{ - return lsd_recv(buf, len, ctx, recv_cb); -} - -/************************************************************************//** - * \brief Receive data using an UDP socket in reuse mode. - * - * \param[in] data Receive buffer including the remote address and the - * data payload. - * \param[in] len Length of the receive buffer. - * \param[in] ctx Context pointer to pass to the reception callbak. - * \param[in] recv_cb Callback to run when reception is complete or errors. - * - * \return Status of the receive procedure. - ****************************************************************************/ -static inline enum lsd_status mw_udp_reuse_recv(struct mw_reuse_payload *data, - int16_t len, void *ctx, lsd_recv_cb recv_cb) -{ - return lsd_recv((char*)data, len, ctx, recv_cb); -} - -/************************************************************************//** - * \brief Send data using a UDP socket in reuse mode. - * - * \param[in] ch Channel to use for the send operation. - * \param[in] data Send buffer including the remote address and the - * data payload. - * \param[in] len Length of the receive buffer. - * \param[in] ctx Context pointer to pass to the reception callbak. - * \param[in] send_cb Callback to run when sending completes or errors. - * - * \return Status of the receive procedure. - ****************************************************************************/ -static inline enum lsd_status mw_udp_reuse_send(uint8_t ch, - const struct mw_reuse_payload *data, int16_t len, void *ctx, - lsd_send_cb send_cb) -{ - return lsd_send(ch, (const char*)data, len, ctx, send_cb); -} - -/************************************************************************//** - * \brief Sends data through a socket, using a previously allocated channel. - * Asynchronous interface. - * - * \param[in] ch Channel used to send the data. - * \param[in] data Buffer to send. - * \param[in] len Length of the data to send. - * \param[in] ctx Context for the send callback function. - * \param[in] send_cb Callback to run when send completes or errors. - * - * \return Status of the send procedure. Usually LSD_STAT_BUSY is returned, - * and the send procedure is then performed in background. - * \note Calling this function while there is a send procedure in progress, - * will cause the function call to fail with LSD_STAT_SEND_ERR_IN_PROGRESS. - * \warning For very short data frames, it is possible that the send callback - * is run before this function returns. In this case, the function returns - * LSD_STAT_COMPLETE. - ****************************************************************************/ -static inline enum lsd_status mw_send(uint8_t ch, const char *data, int16_t len, - void *ctx, lsd_send_cb send_cb) -{ - return lsd_send(ch, data, len, ctx, send_cb); -} - -/************************************************************************//** - * \brief Receive data, syncrhonous interface. - * - * \param[out] ch Channel on which data was received. - * \param[out] buf Reception buffer. - * \param[inout] buf_len On input, length of the buffer. - * On output, received data length in bytes. - * \param[in] tout_frames Reception timeout in frames. Set to TSK_PEND_FOREVER - * for infinite wait (dangerous!). - * - * \return Status of the receive procedure. - * \warning Do not use more than one syncrhonous call at once. You must wait - * until a syncrhonous call ends to issue another one. - ****************************************************************************/ -enum mw_err mw_recv_sync(uint8_t *ch, char *buf, int16_t *buf_len, - int16_t tout_frames); - -/************************************************************************//** - * \brief Sends data through a socket, using a previously allocated channel. - * Synchronous interface. - * - * \param[in] ch Channel used to send the data. - * \param[in] data Buffer to send. - * \param[in] len Length of the data to send. - * \param[in] tout_frames Timeout for send operation in frames. Set to 0 for - * infinite wait (dangerous!). - * - * \return Status of the send procedure. Usually LSD_STAT_BUSY is returned, - * and the send procedure is then performed in background. - * \warning Do not use more than one syncrhonous call at once. You must wait - * until a syncrhonous call ends to issue another one. - ****************************************************************************/ -enum mw_err mw_send_sync(uint8_t ch, const char *data, uint16_t len, - int16_t tout_frames); - -/************************************************************************//** - * \brief Get system status. - * - * \return Pointer to system status structure on success, or NULL on error. - ****************************************************************************/ -union mw_msg_sys_stat *mw_sys_stat_get(void); - -/************************************************************************//** - * \brief Get socket status. - * - * \param[in] ch Channel associated to the socket asked for status. - * - * \return Socket status data on success, or -1 on error. - ****************************************************************************/ -enum mw_sock_stat mw_sock_stat_get(uint8_t ch); - -/************************************************************************//** - * \brief Configure SNTP parameters and timezone. - * - * \param[in] tz_str Timezone string (e.g. "CET"). See tzset(3) for details. - * \param[in] server Array of up to three NTP servers. If less than three - * servers are desired, unused entries must be empty. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note After a successful invocation, call mw_cfg_save() for changes to - * be persistent - ****************************************************************************/ -enum mw_err mw_sntp_cfg_set(const char *tz_str, const char *server[3]); - -/************************************************************************//** - * \brief Get SNTP parameters and timezone configuration. - * - * \param[out] tz_str Timezone string (e.g. "CET"). See tzset(3) for details. - * \param[out] server Array of three NTP server pointers. If less than 3 - * servers are configured, unused ones will be NULL. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_sntp_cfg_get(char **tz_str, char *server[3]); - -/************************************************************************//** - * \brief Get date and time. - * - * \param[out] dt_bin Date and time in seconds since Epoch. If set to NULL, - * this info is not filled (but return value will still - * be properly set). - * - * \return A string with the date and time in textual format, e.g.: "Thu Mar - * 3 12:26:51 2016", or NULL if error. - ****************************************************************************/ -char *mw_date_time_get(uint32_t dt_bin[2]); - -/************************************************************************//** - * \brief Get the identifiers of the flash chip in the WiFi module. - * - * \param[out] man_id ID of the flash chip manufacturer. - * \param[out] dev_id Device IDs of the flash chip. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_flash_id_get(uint8_t *man_id, uint16_t *dev_id); - -/************************************************************************//** - * \brief Erase a 4 KiB Flash sector. Every byte of an erased sector will be - * read as 0xFF. - * - * \param[in] sect Sector number to erase. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_flash_sector_erase(uint16_t sect); - -/************************************************************************//** - * \brief Write data to specified flash address. - * - * \param[in] addr Address to which data will be written. - * \param[in] data Data to be written to flash chip. - * \param[in] data_len Length in bytes of data field. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_flash_write(uint32_t addr, uint8_t *data, uint16_t data_len); - -/************************************************************************//** - * \brief Read data from specified flash address. - * - * \param[in] addr Address from which data will be read. - * \param[in] data_len Number of bytes to read from addr. - * - * \return Pointer to read data on success, or NULL if command failed. - ****************************************************************************/ -uint8_t *mw_flash_read(uint32_t addr, uint16_t data_len); - -/************************************************************************//** - * \brief Puts the WiFi module in reset state. - ****************************************************************************/ -#define mw_module_reset() do{uart_set_bits(MCR, MW__RESET);}while(0) - -/************************************************************************//** - * \brief Releases the module from reset state. - ****************************************************************************/ -#define mw_module_start() do{uart_clr_bits(MCR, MW__RESET);}while(0) - -/************************************************************************//** - * \brief Set gamertag information for one slot. - * - * \param[in] slot Slot to use (from 0 to 2). - * \param[in] gamertag Gamertag information to set on specified slot. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note After a successful invocation, call mw_cfg_save() for changes to - * be persistent - ****************************************************************************/ -enum mw_err mw_gamertag_set(uint8_t slot, const struct mw_gamertag *gamertag); - -/************************************************************************//** - * \brief Get gamertag information for one slot. - * - * \param[in] slot Slot to get gamertag from. - * - * \return Gamertag information on success, NULL on error. - ****************************************************************************/ -struct mw_gamertag *mw_gamertag_get(uint8_t slot); - -/************************************************************************//** - * \brief Write a message to the WiFi module log trace. - * - * \param[in] msg Message to write to the log trace. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_log(const char *msg); - -/************************************************************************//** - * \brief Set factory default configuration. - * - * \return MW_ERR_NONE on success, other code on failure. - * \note It is recommended to reboot the module after this command. - ****************************************************************************/ -enum mw_err mw_factory_settings(void); - -/************************************************************************//** - * \brief Powers off the WiFi module. - * - * The module will be put in deep sleep mode. To wake it up, the RESET pin - * must be toggled. - ****************************************************************************/ -void mw_power_off(void); - -/************************************************************************//** - * \brief Sleep the specified amount of frames - * - * \param[in] frames Number of frames to sleep. - ****************************************************************************/ -void mw_sleep(int16_t frames); - -/************************************************************************//** - * \brief Set URL for HTTP requests. - * - * \param[in] url URL to set. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_http_url_set(const char *url); - -/************************************************************************//** - * \brief Set method for HTTP requests. - * - * \param[in] method Method to set. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_http_method_set(enum mw_http_method method); - -/************************************************************************//** - * \brief Add an HTTP header. - * - * \param[in] key Header key. - * \param[in] value Value to set for the key. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_http_header_add(const char *key, const char *value); - -/************************************************************************//** - * \brief Delete a previously added HTTP header. - * - * \param[in] key Key of the header to delete. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_http_header_del(const char *key); - -/************************************************************************//** - * \brief Open HTTP connection. - * - * This functions opens the HTTP connection, sends the HTTP headers, and - * prepares the module to send the specified content_len if (if any) with - * a successive mw_send() or mw_send_sync(), using MW_HTTP_CH channel. - * - * \param[in] content_len Length of the content to write in HTTP request, - * after a successfull call to this function. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_http_open(uint32_t content_len); - -/************************************************************************//** - * \brief Finish an opened HTTP request. - * - * After a successful call to mw_http_open(), and sending the content (if - * any), call this function to receive the HTTP response headers, and obtain - * the length of the body to receive with a further call to mw_recv() or - * mw_recv_sync(), using MW_HTTP_CH. - * - * \param[out] content_len Length of the response content to receive after a - * successfull call to this function. - * \param[in] tout_frames Maximun number of frames to wait for reply. - * - * \return The HTTP status code if the request was completed, or an error - * code (lower than 100) if the HTTP request did not complete. - * \note Even if the HTTP request is completed, that does not mean there are - * no errors, if the returned status code is 4xx or 5xx, there is a client - * side or server side error. - ****************************************************************************/ -int16_t mw_http_finish(uint32_t *content_len, int16_t tout_frames); - -/************************************************************************//** - * \brief Query the X.509 hash of the installed PEM certificate. - * - * \return 0xFFFFFFFF if certificate is not installed or error occurs, or - * the installed X.509 certificate hash on success. - ****************************************************************************/ -uint32_t mw_http_cert_query(void); - -/************************************************************************//** - * \brief Set the PEM certificate to use on HTTPS requests. - * - * The certificate is stored on the non volatile memory of the module, and - * when present will be used in HTTPS requestes. This function can also be - * used to delete a previously saved certificate using a NULL input value. - * - * \param[in] cert_hash X.509 hash of the certificate to set, ignored if - * cert_len set to 0. - * \param[in] cert PEM certificate in plain text. Ignored if cert_len - * set to 0. - * previously stored certificate. - * \param[in] cert_len Certificate length in bytes. Set to 0 to delete a - * previously stored certificate. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -enum mw_err mw_http_cert_set(uint32_t cert_hash, const char *cert, - uint16_t cert_len); - -/************************************************************************//** - * \brief Clean-up an HTTP request, freeing associated resources. - * - * \return MW_ERR_NONE on success, other code on failure. - ****************************************************************************/ -int16_t mw_http_cleanup(void); - -/************************************************************************//** - * \brief Get the default server used for MegaWiFi connections. - * - * \return The server URL string, or NULL on error. - ****************************************************************************/ -char *mw_def_server_get(void); - -/************************************************************************//** - * \brief Set the default server used for MegaWiFi connections. - * - * \param[in] server_url The server URL to set. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note After a successful invocation, call mw_cfg_save() for changes to - * be persistent - ****************************************************************************/ -enum mw_err mw_def_server_set(const char *server_url); - -/************************************************************************//** - * \brief Get random numbers. - * - * \param[in] rnd_len Number of bytes of resulting random array. - * - * \return The buffer with the requested random numbers on success, or NULL - * when error. - ****************************************************************************/ -uint8_t *mw_hrng_get(uint16_t rnd_len); - -/************************************************************************//** - * \brief Set endpoint for Game API. - * - * Example endpoint for GameJolt: "https://api.gamejolt.com/api/game/v1_2/". - * - * \param[in] endpoint Endpoint for the Game API to set. - * \param[in] priv_key Private key used for request signatures. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note The endpoint set persists between successive mw_ga_request() calls. - ****************************************************************************/ -enum mw_err mw_ga_endpoint_set(const char *endpoint, const char *priv_key); - -/************************************************************************//** - * \brief Add parameters to the Game API request in key/value format. - * - * Example key:value pair for GameJolt: "game_id":"123456". - * - * \param[in] key Array with the keys to add. - * \param[in] value Array with the values to add - * \param[in] num_pairs Number of key/value pairs to add. - * - * \return MW_ERR_NONE on success, other code on failure. - * - * \note The key/value pairs set persist between successive mw_ga_request() - * calls. - * \note Call mw_ga_key_value_add(NULL, NULL, 0) to clear previously set - * key/value pairs. - * \note Key/value pairs must NOT be URL encoded. Encoding is handled - * internally. - ****************************************************************************/ -enum mw_err mw_ga_key_value_add(const char **key, const char **value, - uint16_t num_pairs); - -/************************************************************************//** - * \brief Perform a GameAPI request, with the previously set endpoint and - * key/value pairs. - * - * The request can also have URL encoded parameters. that are added to the - * previously set key/value pairs. - * - * Example request for GameJolt: - * - method: MW_HTTP_METHOD_GET - * - path: "trophies" - * - key:value: "achieved":"true" - * - * \param[in] method HTTP method to use. Most likely MW_HTTP_METHOD_GET. - * \param[in] path Additional paths to add to the request. - * \param[in] num_paths Number of additional paths to add. - * \param[in] key Additional paths to add to the request. - * \param[in] value Additional paths to add to the request. - * \param[in] num_kv_pairs Number of key/value pairs. - * \param[out] content_len Content length of the API response. - * \param[in] tout_frames Number of frames to wait before canceling the - * request due to a timeout error. - * - * \return HTTP status code on success (e.g. 200), or an error (lower than - * 100) if the HTTP request could not be completed. - * \note Even if the HTTP request is completed, that does not mean there are - * no errors, if the returned status code is 4xx or 5xx, there is a client - * side or server side error. - * \note path, key and value parameters must not be URL encoded. Encoding is - * handled internally. - ****************************************************************************/ -int16_t mw_ga_request(enum mw_http_method method, const char **path, - uint8_t num_paths, const char **key, const char **value, - uint8_t num_kv_pairs, uint32_t *content_len, - int16_t tout_frames); - -/************************************************************************//** - * \brief Over-The-Air upgrade WiFi module firmware. - * - * \param[in] name Name of the firmware blob to upgrade. - * E.g. "mw_rtos_std_v1.4.1" - * - * \return Status of the send procedure. - ****************************************************************************/ -enum mw_err mw_fw_upgrade(const char *name); - -/****** THE FOLLOWING COMMANDS ARE LOWER LEVEL AND USUALLY NOT NEEDED ******/ - -/************************************************************************//** - * \brief Send a command to the WiFi module. - * - * \param[in] cmd Pointer to the filled mw_cmd command structure. - * \param[in] ctx Context for callback function. - * \param[in] send_cb Callback for the send operation completion. - * - * \return Status of the send procedure. - ****************************************************************************/ -static inline enum lsd_status mw_cmd_send(mw_cmd *cmd, void *ctx, - lsd_send_cb send_cb) -{ - // Send data on control channel (0). - return lsd_send(MW_CTRL_CH, cmd->packet, cmd->data_len + 4, - ctx, send_cb); -} - -/************************************************************************//** - * \brief Try obtaining a reply to a command. - * - * \param[in] rep Buffer to hold the command reply. - * \param[in] ctx Context for the reception callback. - * \param[in] recv_cb Callback for data reception completion. - * - * \return Status of the reception procedure. - ****************************************************************************/ -static inline enum lsd_status mw_cmd_recv(mw_cmd *rep, void *ctx, - lsd_recv_cb recv_cb) { - return lsd_recv(rep->packet, sizeof(mw_cmd), ctx, recv_cb); -} - -#endif // MODULE_MEGAWIFI - -#endif /*_MEGAWIFI_H_*/ - -/** \} */ - +/************************************************************************//** + * \file + * + * \brief MegaWiFi API implementation. + * + * \defgroup megawifi megawifi + * \{ + * + * \brief MegaWiFi API implementation. + * + * API to communicate with the wifi module and the Internet. API calls are + * documented and most of them are self explanatory. Mostly the only weird + * thing about the API is UDP reuse mode. If you enable reuse mode (setting + * the dst_addr and/or dst_port to NULL in the mw_udp_set() call), received + * data will prepend the IP and port of the peer (using mw_reuse_payload data + * structure), and data to be sent also requires the IP and port to be + * prepended to the payload. + * + * \author Jesus Alonso (doragasu) + * \author Juan Antonio (PaCHoN) + * \date 2025 + * + * \note This module requires setting MODULE_MEGAWIFI to 1 in config.h and + * rebuilding the library (if you had to change them). + ****************************************************************************/ + +#ifndef _MEGAWIFI_H_ +#define _MEGAWIFI_H_ + +#if (MODULE_EVERDRIVE == 1) + #include "ssf.h" +#else + #include "16c550.h" +#endif +#include "mw-msg.h" +#include "lsd.h" + +/// API version implemented, major number +#define MW_API_VERSION_MAJOR 1 + +/// API version implemented, minor number +#define MW_API_VERSION_MINOR 5 + +/// Timeout for standard commands in milliseconds +#define MW_COMMAND_TOUT_MS 1000 +/// Timeout for TCP connections +#define MW_CONNECT_TOUT_MS 10000 +/// Timeout for HTTP open command in milliseconds +#define MW_HTTP_OPEN_TOUT_MS 10000 +/// Timeout for the AP scan command in milliseconds +#define MW_SCAN_TOUT_MS 10000 +/// Timeout for the AP associate command in milliseconds +#define MW_ASSOC_TOUT_MS 20000 +/// Time to sleep before waiting for assoc in milliseconds +#define MW_ASSOC_WAIT_SLEEP_MS 5000 +/// Timeout for upgrade command in milliseconds +#define MW_UPGRADE_TOUT_MS 180000 +/// Timeout for ping command in milliseconds +#define MW_PING_TOUT_MS 30000 +/// Milliseconds between status polls while in wm_ap_assoc_wait() +#define MW_STAT_POLL_MS 250 + +#if (MODULE_EVERDRIVE == 0) + /// Length of the wflash buffer + #define MW_BUFLEN 1460 +#elif (MODULE_EVERDRIVE == 1) + /// Length of the wflash buffer + #define MW_BUFLEN 1436 +#endif + +/// Error codes for MegaWiFi API functions +enum mw_err { + MW_ERR_NONE = 0, ///< No error (success) + MW_ERR, ///< General error + MW_ERR_NOT_READY, ///< Not ready to run command + MW_ERR_BUFFER_TOO_SHORT, ///< Command buffer is too small + MW_ERR_PARAM, ///< Input parameter out of range + MW_ERR_SEND, ///< Error sending data + MW_ERR_RECV ///< Error receiving data +}; + +/// Supported HTTP methods +enum mw_http_method { + MW_HTTP_METHOD_GET = 0, ///< HTTP GET Method + MW_HTTP_METHOD_POST, ///< HTTP POST Method + MW_HTTP_METHOD_PUT, ///< HTTP PUT Method + MW_HTTP_METHOD_PATCH, ///< HTTP PATCH Method + MW_HTTP_METHOD_DELETE, ///< HTTP DELETE Method + MW_HTTP_METHOD_HEAD, ///< HTTP HEAD Method + MW_HTTP_METHOD_NOTIFY, ///< HTTP NOTIFY Method + MW_HTTP_METHOD_SUBSCRIBE, ///< HTTP SUBSCRIBE Method + MW_HTTP_METHOD_UNSUBSCRIBE,///< HTTP UNSUBSCRIBE + MW_HTTP_METHOD_OPTIONS, ///< HTTP OPTIONS + MW_HTTP_METHOD_MAX, +}; + +/** \addtogroup mw_ctrl_pins mw_ctrl_pins + * \brief Pins used to control WiFi module. + * \{ */ +#define MW__RESET UART_MCR__OUT1 ///< Reset out. +#define MW__PRG UART_MCR__OUT2 ///< Program out. +#define MW__PD UART_MCR__DTR ///< Power Down out. +#define MW__DAT UART_MSR__DSR ///< Data request in. +/** \} */ + +/// Maximum SSID length (including '\0'). +#define MW_SSID_MAXLEN 32 +/// Maximum password length (including '\0'). +#define MW_PASS_MAXLEN 64 +/// Maximum length of an NTP pool URI (including '\0'). +#define MW_NTP_POOL_MAXLEN 80 +/// Number of AP configurations stored to nvflash. +#define MW_NUM_CFG_SLOTS 3 +/// Number of DSN servers supported per AP configuration. +#define MW_NUM_DNS_SERVERS 2 +/// Length of the FSM queue +#define MW_FSM_QUEUE_LEN 8 +/// Maximum number of simultaneous TCP connections +#define MW_MAX_SOCK 3 +/// Control channel used for LSD protocol +#define MW_CTRL_CH 0 +/// Channel used for HTTP requests and cert sets +#define MW_HTTP_CH LSD_MAX_CH - 1 + +/// Minimum command buffer length to be able to send all available commands +/// with minimum data payload. This length might not guarantee that commands +/// like mw_sntp_cfg_set() can be sent if payload length is big enough). +#define MW_CMD_MIN_BUFLEN 168 + +/// Access Point data. +struct mw_ap_data { + enum mw_security auth; ///< Security type + uint8_t channel; ///< WiFi channel. + int8_t rssi; ///< Signal strength. + uint8_t ssid_len; ///< Length of ssid field. + char *ssid; ///< SSID string (not NULL terminated). +}; + +/// Interface type for the mw_bssid_get() function. +enum mw_if_type { + MW_IF_STATION = 0, ///< Station interface + MW_IF_SOFTAP, ///< Access Point interface + MW_IF_MAX ///< Number of supported interface types +}; + +/************************************************************************//** + * \brief Module initialization. Must be called once before using any + * other function. It also initializes de UART. + * + * \param[in] cmd_buf Pointer to the buffer used to send and receive commands. + * \param[in] buf_len Length of cmdBuf in bytes. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +int16_t mw_init(uint16_t *cmd_buf, uint16_t buf_len); + +/************************************************************************//** + * \brief Processes sends/receives pending data. + * + * Call this function as much as possible to process incoming/outgoing data. + * + * \warning No data will be sent/received if this function is not frequently + * invoked. + ****************************************************************************/ +static inline void mw_process(void) {lsd_process();} + +/************************************************************************//** + * \brief Sets the callback function to be run when network data is received + * while waiting for a command reply. + * + * \param[in] cmd_recv_cb Callback to be run when data is received while + * waiting for a command reply. + * + * \warning If this callback is not set, data received while waiting for a + * command reply will be silently discarded. + ****************************************************************************/ +void mw_cmd_data_cb_set(lsd_recv_cb cmd_recv_cb); + +/************************************************************************//** + * \brief Performs the startup sequence for the WiFi module, and tries + * detecting it by requesting the version data. + * + * \param[out] major Major version number. + * \param[out] minor Minor version number. + * \param[out] variant String with firmware variant ("std" for standard). + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_detect(uint8_t *major, uint8_t *minor, char **variant); + +/************************************************************************//** + * \brief Obtain module version numbers and string + * + * \param[out] version Version numbers (major, minor, micro) in order. + * \param[out] variant String with firmware variant ("std" for standard). + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_version_get(uint8_t version[3], char **variant); + +/************************************************************************//** + * \brief Gets the module BSSID (the MAC address) for the specified interface. + * + * \param[in] interface_type Type of the interface to obtain BSSID from. + * + * \return The requested BSSID (6 byte binary data), or NULL on error. + ****************************************************************************/ +uint8_t *mw_bssid_get(enum mw_if_type interface_type); + +/************************************************************************//** + * \brief Set default module configuration (AKA factory settings). + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note For this command to take effect, it must be followed by a module + * reset. + ****************************************************************************/ +enum mw_err mw_default_cfg_set(void); + +/************************************************************************//** + * \brief Set access point configuration (SSID and password). + * + * \param[in] slot Configuration slot to use. + * \param[in] ssid String with the AP SSID to set. + * \param[in] pass String with the AP SSID to set. + * \param[in] phy_type Bitmask with the PHY type configuration. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note Strings must be NULL terminated. Maximum SSID length is 32 bytes, + * maximum pass length is 64 bytes. + * \note After a successful invocation, call mw_cfg_save() for changes to + * be persistent + ****************************************************************************/ +enum mw_err mw_ap_cfg_set(uint8_t slot, const char *ssid, const char *pass, + enum mw_phy_type phy_type); + +/************************************************************************//** + * \brief Gets access point configuration (SSID and password). + * + * \param[in] slot Configuration slot to use. + * \param[out] ssid String with the AP SSID got. + * \param[out] pass String with the AP SSID got. + * \param[out] phy_type Bitmask with the PHY type configuration. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \warning ssid is zero padded up to 32 bytes, and pass is zero padded up + * to 64 bytes. If ssid is 32 bytes, it will NOT be NULL terminated. + * Also if pass is 64 bytes, it will NOT be NULL terminated. + ****************************************************************************/ +enum mw_err mw_ap_cfg_get(uint8_t slot, char **ssid, char **pass, + enum mw_phy_type *phy_type); + +/************************************************************************//** + * \brief Set IPv4 configuration. + * + * \param[in] slot Configuration slot to use. + * \param[in] ip Pointer to the mw_ip_cfg structure, with IP configuration. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note After a successful invocation, call mw_cfg_save() for changes to + * be persistent + ****************************************************************************/ +enum mw_err mw_ip_cfg_set(uint8_t slot, const struct mw_ip_cfg *ip); + +/************************************************************************//** + * \brief Get IPv4 configuration. + * + * \param[in] slot Configuration slot to use. + * \param[out] ip Double pointer to mw_ip_cfg structure, with IP conf. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_ip_cfg_get(uint8_t slot, struct mw_ip_cfg **ip); + +/************************************************************************//** + * \brief Set advanced WiFi configuration. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \warning This function is dangerous. Changing these parameters is rarely + * needed, and setting incorrect values, may render the connection unstable + * and/or crash the WiFi module. Invalid configurations can even cause the + * module to crash in a bootloop, requiring a programmer to unbrick it. + * Make sure you thoroughly test the values you allow users to set here. + * \note If you want to change WiFi parameters, the recommendation is to get + * the current configuration via mw_wifi_adv_cfg_get(), and from it change + * only the required parameters. + * \note These parameters will not take effect until saved to non-volatile + * storage (with mw_cfg_save()) and issuing a module reboot. + ****************************************************************************/ +enum mw_err mw_wifi_adv_cfg_set(const struct mw_wifi_adv_cfg *wifi); + +/************************************************************************//** + * \brief Get advanced WiFi configuration. + * + * \return Pointer to the advanced WiFi configuration, or NULL on error. + ****************************************************************************/ +struct mw_wifi_adv_cfg *mw_wifi_adv_cfg_get(void); + +/************************************************************************//** + * \brief Saves changed configuration parameters to non-volatile memory. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_cfg_save(void); + +/************************************************************************//** + * \brief Get current IP configuration, of the joined AP. + * + * \param[out] ip Double pointer to mw_ip_cfg structure, with IP conf. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_ip_current(struct mw_ip_cfg **ip); + +/************************************************************************//** + * \brief Scan for access points. + * + * \param[in] phy_type Bitmask with the PHY type configuration. + * \param[out] ap_data Data of the found access points. Each entry has the + * format specified on the mw_ap_data structure. + * \param[out] aps Number of found access points. + * + * \return Length in bytes of the output data if operation completes + * successfully, or -1 if scan fails. + ****************************************************************************/ +int16_t mw_ap_scan(enum mw_phy_type phy_type, char **ap_data, uint8_t *aps); + +/************************************************************************//** + * \brief Parses received AP data and fills information of the AP at "pos". + * Useful to extract AP information from the data obtained by + * calling mw_ap_scan() function. + * + * \param[in] ap_data Access point data obtained from mw_ap_scan(). + * \param[in] pos Position at which to extract data. + * \param[out] apd Pointer to the extracted data from an AP. + * \param[in] data_len Lenght of apData. + * + * \return Position of the next AP entry in apData, 0 if no more APs + * available or MW_ERROR if ap data/pos combination is not valid. + * + * \note This functions executes locally, does not communicate with the + * WiFi module. + ****************************************************************************/ +int16_t mw_ap_fill_next(const char *ap_data, uint16_t pos, + struct mw_ap_data *apd, uint16_t data_len); + +/************************************************************************//** + * \brief Tries associating to an AP. If successful, also configures IPv4. + * + * \param[in] slot Configuration slot to use. + * + * \return MW_ERR_NONE if AP join operation has been successfully started, + ****************************************************************************/ +enum mw_err mw_ap_assoc(uint8_t slot); + +/************************************************************************//** + * \brief Polls the module status until it reports device is associated to + * AP or timeout occurs. + * + * \param[in] tout_frames Maximun number of frames to wait for association. + * Set to TSK_PEND_FOREVER for an infinite wait. + * + * \return MW_ERR_NONE if device is associated to AP. MW_ERR_NOT_READY if + * the timeout has expired. + ****************************************************************************/ +enum mw_err mw_ap_assoc_wait(int16_t tout_frames); + +/************************************************************************//** + * \brief Sets default AP/IP configuration. + * + * \param[in] slot Configuration slot to use. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note After a successful invocation, call mw_cfg_save() for changes to + * be persistent + ****************************************************************************/ +enum mw_err mw_def_ap_cfg(uint8_t slot); + +/************************************************************************//** + * \brief Dissasociates from a previously associated AP. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_ap_disassoc(void); + +/************************************************************************//** + * \brief Gets default AP/IP configuration slot. + * + * \return The default configuration slot, of -1 on error. + ****************************************************************************/ +int16_t mw_def_ap_cfg_get(void); + +/************************************************************************//** + * \brief Tries establishing a TCP connection with specified server. + * + * \param[in] ch Channel used for the connection. + * \param[in] dst_addr Address (IP or DNS entry) of the server. + * \param[in] dst_port Port in which server is listening. + * \param[in] src_port Port from which try establishing connection. Set to + * 0 or empty string for automatic port allocation. + * + * \return MW_ERR_NONE on success, other code if connection failed. + ****************************************************************************/ +enum mw_err mw_tcp_connect(uint8_t ch, const char *dst_addr, + const char *dst_port, const char *src_port); + +/************************************************************************//** + * \brief Closes and disconnects a socket from specified channel. + * + * This function can be used to free the channel associated to both TCP and + * UDP sockets. + * + * \param[in] ch Channel associated to the socket to disconnect. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_close(uint8_t ch); + +/// Closes a TCP socket. This is an alias of mw_close(). +#define mw_tcp_disconnect(ch) mw_close(ch) + +/************************************************************************//** + * \brief Configures a UDP socket to send/receive data. + * + * \param[in] ch Channel used for the connection. + * \param[in] dst_addr Address (IP or DNS entry) to send data to. + * \param[in] dst_port Port to send data to. + * \param[in] src_port Local port to listen message on. + * + * \return MW_ERR_NONE on success, other code if connection failed. + * + * \note Setting to NULL dst_addr and/or dst_port, enables reuse mode. + ****************************************************************************/ +enum mw_err mw_udp_set(uint8_t ch, const char *dst_addr, const char *dst_port, + const char *src_port); + +/// Frees a UDP socket. This is an alias of mw_close(). +#define mw_udp_unset(ch) mw_close(ch) + +/************************************************************************//** + * \brief Binds a socket to a port, and listens to connections on the port. + * If a connection request is received, it will be automatically + * accepted. + * + * \param[in] ch Channel associated to the socket bound t port. + * \param[in] port Port number to which the socket will be bound. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_tcp_bind(uint8_t ch, uint16_t port); + +/************************************************************************//** + * \brief Polls a socket until it is ready to transfer data. Typical use of + * this function is after a successful mw_tcp_bind(). + * + * \param[in] ch Channel associated to the socket to monitor. + * \param[in] tout_frames Maximum number of frames to wait for connection. + * Set to 0 for an infinite wait. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_sock_conn_wait(uint8_t ch, int16_t tout_frames); + +/************************************************************************//** + * \brief Receive data, asyncrhonous interface. + * + * \param[in] buf Reception buffer. + * \param[in] len Length of the receive buffer. + * \param[in] ctx Context pointer to pass to the reception callbak. + * \param[in] recv_cb Callback to run when reception is complete or errors. + * + * \return Status of the receive procedure. + ****************************************************************************/ +static inline enum lsd_status mw_recv(char *buf, int16_t len, void *ctx, + lsd_recv_cb recv_cb) +{ + return lsd_recv(buf, len, ctx, recv_cb); +} + +/************************************************************************//** + * \brief Receive data using an UDP socket in reuse mode. + * + * \param[in] data Receive buffer including the remote address and the + * data payload. + * \param[in] len Length of the receive buffer. + * \param[in] ctx Context pointer to pass to the reception callbak. + * \param[in] recv_cb Callback to run when reception is complete or errors. + * + * \return Status of the receive procedure. + ****************************************************************************/ +static inline enum lsd_status mw_udp_reuse_recv(struct mw_reuse_payload *data, + int16_t len, void *ctx, lsd_recv_cb recv_cb) +{ + return lsd_recv((char*)data, len, ctx, recv_cb); +} + +/************************************************************************//** + * \brief Send data using a UDP socket in reuse mode. + * + * \param[in] ch Channel to use for the send operation. + * \param[in] data Send buffer including the remote address and the + * data payload. + * \param[in] len Length of the receive buffer. + * \param[in] ctx Context pointer to pass to the reception callbak. + * \param[in] send_cb Callback to run when sending completes or errors. + * + * \return Status of the receive procedure. + ****************************************************************************/ +static inline enum lsd_status mw_udp_reuse_send(uint8_t ch, + const struct mw_reuse_payload *data, int16_t len, void *ctx, + lsd_send_cb send_cb) +{ + return lsd_send(ch, (const char*)data, len, ctx, send_cb); +} + +/************************************************************************//** + * \brief Sends data through a socket, using a previously allocated channel. + * Asynchronous interface. + * + * \param[in] ch Channel used to send the data. + * \param[in] data Buffer to send. + * \param[in] len Length of the data to send. + * \param[in] ctx Context for the send callback function. + * \param[in] send_cb Callback to run when send completes or errors. + * + * \return Status of the send procedure. Usually LSD_STAT_BUSY is returned, + * and the send procedure is then performed in background. + * \note Calling this function while there is a send procedure in progress, + * will cause the function call to fail with LSD_STAT_SEND_ERR_IN_PROGRESS. + * \warning For very short data frames, it is possible that the send callback + * is run before this function returns. In this case, the function returns + * LSD_STAT_COMPLETE. + ****************************************************************************/ +static inline enum lsd_status mw_send(uint8_t ch, const char *data, int16_t len, + void *ctx, lsd_send_cb send_cb) +{ + return lsd_send(ch, data, len, ctx, send_cb); +} + +/************************************************************************//** + * \brief Receive data, syncrhonous interface. + * + * \param[out] ch Channel on which data was received. + * \param[out] buf Reception buffer. + * \param[inout] buf_len On input, length of the buffer. + * On output, received data length in bytes. + * \param[in] tout_frames Reception timeout in frames. Set to TSK_PEND_FOREVER + * for infinite wait (dangerous!). + * + * \return Status of the receive procedure. + * \warning Do not use more than one syncrhonous call at once. You must wait + * until a syncrhonous call ends to issue another one. + ****************************************************************************/ +enum mw_err mw_recv_sync(uint8_t *ch, char *buf, int16_t *buf_len, + int16_t tout_frames); + +/************************************************************************//** + * \brief Sends data through a socket, using a previously allocated channel. + * Synchronous interface. + * + * \param[in] ch Channel used to send the data. + * \param[in] data Buffer to send. + * \param[in] len Length of the data to send. + * \param[in] tout_frames Timeout for send operation in frames. Set to 0 for + * infinite wait (dangerous!). + * + * \return Status of the send procedure. Usually LSD_STAT_BUSY is returned, + * and the send procedure is then performed in background. + * \warning Do not use more than one syncrhonous call at once. You must wait + * until a syncrhonous call ends to issue another one. + ****************************************************************************/ +enum mw_err mw_send_sync(uint8_t ch, const char *data, uint16_t len, + int16_t tout_frames); + +/************************************************************************//** + * \brief Get system status. + * + * \return Pointer to system status structure on success, or NULL on error. + ****************************************************************************/ +union mw_msg_sys_stat *mw_sys_stat_get(void); + +/************************************************************************//** + * \brief Get socket status. + * + * \param[in] ch Channel associated to the socket asked for status. + * + * \return Socket status data on success, or -1 on error. + ****************************************************************************/ +enum mw_sock_stat mw_sock_stat_get(uint8_t ch); + +/************************************************************************//** + * \brief Configure SNTP parameters and timezone. + * + * \param[in] tz_str Timezone string (e.g. "CET"). See tzset(3) for details. + * \param[in] server Array of up to three NTP servers. If less than three + * servers are desired, unused entries must be empty. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note After a successful invocation, call mw_cfg_save() for changes to + * be persistent + ****************************************************************************/ +enum mw_err mw_sntp_cfg_set(const char *tz_str, const char *server[3]); + +/************************************************************************//** + * \brief Get SNTP parameters and timezone configuration. + * + * \param[out] tz_str Timezone string (e.g. "CET"). See tzset(3) for details. + * \param[out] server Array of three NTP server pointers. If less than 3 + * servers are configured, unused ones will be NULL. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_sntp_cfg_get(char **tz_str, char *server[3]); + +/************************************************************************//** + * \brief Get date and time. + * + * \param[out] dt_bin Date and time in seconds since Epoch. If set to NULL, + * this info is not filled (but return value will still + * be properly set). + * + * \return A string with the date and time in textual format, e.g.: "Thu Mar + * 3 12:26:51 2016", or NULL if error. + ****************************************************************************/ +char *mw_date_time_get(uint32_t dt_bin[2]); + +/************************************************************************//** + * \brief Get the identifiers of the flash chip in the WiFi module. + * + * \param[out] man_id ID of the flash chip manufacturer. + * \param[out] dev_id Device IDs of the flash chip. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_flash_id_get(uint8_t *man_id, uint16_t *dev_id); + +/************************************************************************//** + * \brief Erase a 4 KiB Flash sector. Every byte of an erased sector will be + * read as 0xFF. + * + * \param[in] sect Sector number to erase. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_flash_sector_erase(uint16_t sect); + +/************************************************************************//** + * \brief Write data to specified flash address. + * + * \param[in] addr Address to which data will be written. + * \param[in] data Data to be written to flash chip. + * \param[in] data_len Length in bytes of data field. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_flash_write(uint32_t addr, uint8_t *data, uint16_t data_len); + +/************************************************************************//** + * \brief Read data from specified flash address. + * + * \param[in] addr Address from which data will be read. + * \param[in] data_len Number of bytes to read from addr. + * + * \return Pointer to read data on success, or NULL if command failed. + ****************************************************************************/ +uint8_t *mw_flash_read(uint32_t addr, uint16_t data_len); + +/************************************************************************//** + * \brief Puts the WiFi module in reset state. + ****************************************************************************/ +#define mw_module_reset() do{uart_set_bits(MCR, MW__RESET);}while(0) + +/************************************************************************//** + * \brief Releases the module from reset state. + ****************************************************************************/ +#define mw_module_start() do{uart_clr_bits(MCR, MW__RESET);}while(0) + +/************************************************************************//** + * \brief Set gamertag information for one slot. + * + * \param[in] slot Slot to use (from 0 to 2). + * \param[in] gamertag Gamertag information to set on specified slot. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note After a successful invocation, call mw_cfg_save() for changes to + * be persistent + ****************************************************************************/ +enum mw_err mw_gamertag_set(uint8_t slot, const struct mw_gamertag *gamertag); + +/************************************************************************//** + * \brief Get gamertag information for one slot. + * + * \param[in] slot Slot to get gamertag from. + * + * \return Gamertag information on success, NULL on error. + ****************************************************************************/ +struct mw_gamertag *mw_gamertag_get(uint8_t slot); + +/************************************************************************//** + * \brief Write a message to the WiFi module log trace. + * + * \param[in] msg Message to write to the log trace. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_log(const char *msg); + +/************************************************************************//** + * \brief Set factory default configuration. + * + * \return MW_ERR_NONE on success, other code on failure. + * \note It is recommended to reboot the module after this command. + ****************************************************************************/ +enum mw_err mw_factory_settings(void); + +/************************************************************************//** + * \brief Powers off the WiFi module. + * + * The module will be put in deep sleep mode. To wake it up, the RESET pin + * must be toggled. + ****************************************************************************/ +void mw_power_off(void); + +/************************************************************************//** + * \brief Sleep the specified amount of frames + * + * \param[in] frames Number of frames to sleep. + ****************************************************************************/ +void mw_sleep(int16_t frames); + +/************************************************************************//** + * \brief Set URL for HTTP requests. + * + * \param[in] url URL to set. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_http_url_set(const char *url); + +/************************************************************************//** + * \brief Set method for HTTP requests. + * + * \param[in] method Method to set. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_http_method_set(enum mw_http_method method); + +/************************************************************************//** + * \brief Add an HTTP header. + * + * \param[in] key Header key. + * \param[in] value Value to set for the key. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_http_header_add(const char *key, const char *value); + +/************************************************************************//** + * \brief Delete a previously added HTTP header. + * + * \param[in] key Key of the header to delete. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_http_header_del(const char *key); + +/************************************************************************//** + * \brief Open HTTP connection. + * + * This functions opens the HTTP connection, sends the HTTP headers, and + * prepares the module to send the specified content_len if (if any) with + * a successive mw_send() or mw_send_sync(), using MW_HTTP_CH channel. + * + * \param[in] content_len Length of the content to write in HTTP request, + * after a successfull call to this function. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_http_open(uint32_t content_len); + +/************************************************************************//** + * \brief Finish an opened HTTP request. + * + * After a successful call to mw_http_open(), and sending the content (if + * any), call this function to receive the HTTP response headers, and obtain + * the length of the body to receive with a further call to mw_recv() or + * mw_recv_sync(), using MW_HTTP_CH. + * + * \param[out] content_len Length of the response content to receive after a + * successfull call to this function. + * \param[in] tout_frames Maximun number of frames to wait for reply. + * + * \return The HTTP status code if the request was completed, or an error + * code (lower than 100) if the HTTP request did not complete. + * \note Even if the HTTP request is completed, that does not mean there are + * no errors, if the returned status code is 4xx or 5xx, there is a client + * side or server side error. + ****************************************************************************/ +int16_t mw_http_finish(uint32_t *content_len, int16_t tout_frames); + +/************************************************************************//** + * \brief Query the X.509 hash of the installed PEM certificate. + * + * \return 0xFFFFFFFF if certificate is not installed or error occurs, or + * the installed X.509 certificate hash on success. + ****************************************************************************/ +uint32_t mw_http_cert_query(void); + +/************************************************************************//** + * \brief Set the PEM certificate to use on HTTPS requests. + * + * The certificate is stored on the non volatile memory of the module, and + * when present will be used in HTTPS requestes. This function can also be + * used to delete a previously saved certificate using a NULL input value. + * + * \param[in] cert_hash X.509 hash of the certificate to set, ignored if + * cert_len set to 0. + * \param[in] cert PEM certificate in plain text. Ignored if cert_len + * set to 0. + * previously stored certificate. + * \param[in] cert_len Certificate length in bytes. Set to 0 to delete a + * previously stored certificate. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +enum mw_err mw_http_cert_set(uint32_t cert_hash, const char *cert, + uint16_t cert_len); + +/************************************************************************//** + * \brief Clean-up an HTTP request, freeing associated resources. + * + * \return MW_ERR_NONE on success, other code on failure. + ****************************************************************************/ +int16_t mw_http_cleanup(void); + +/************************************************************************//** + * \brief Get the default server used for MegaWiFi connections. + * + * \return The server URL string, or NULL on error. + ****************************************************************************/ +char *mw_def_server_get(void); + +/************************************************************************//** + * \brief Set the default server used for MegaWiFi connections. + * + * \param[in] server_url The server URL to set. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note After a successful invocation, call mw_cfg_save() for changes to + * be persistent + ****************************************************************************/ +enum mw_err mw_def_server_set(const char *server_url); + +/************************************************************************//** + * \brief Get random numbers. + * + * \param[in] rnd_len Number of bytes of resulting random array. + * + * \return The buffer with the requested random numbers on success, or NULL + * when error. + ****************************************************************************/ +uint8_t *mw_hrng_get(uint16_t rnd_len); + +/************************************************************************//** + * \brief Set endpoint for Game API. + * + * Example endpoint for GameJolt: "https://api.gamejolt.com/api/game/v1_2/". + * + * \param[in] endpoint Endpoint for the Game API to set. + * \param[in] priv_key Private key used for request signatures. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note The endpoint set persists between successive mw_ga_request() calls. + ****************************************************************************/ +enum mw_err mw_ga_endpoint_set(const char *endpoint, const char *priv_key); + +/************************************************************************//** + * \brief Add parameters to the Game API request in key/value format. + * + * Example key:value pair for GameJolt: "game_id":"123456". + * + * \param[in] key Array with the keys to add. + * \param[in] value Array with the values to add + * \param[in] num_pairs Number of key/value pairs to add. + * + * \return MW_ERR_NONE on success, other code on failure. + * + * \note The key/value pairs set persist between successive mw_ga_request() + * calls. + * \note Call mw_ga_key_value_add(NULL, NULL, 0) to clear previously set + * key/value pairs. + * \note Key/value pairs must NOT be URL encoded. Encoding is handled + * internally. + ****************************************************************************/ +enum mw_err mw_ga_key_value_add(const char **key, const char **value, + uint16_t num_pairs); + +/************************************************************************//** + * \brief Perform a GameAPI request, with the previously set endpoint and + * key/value pairs. + * + * The request can also have URL encoded parameters. that are added to the + * previously set key/value pairs. + * + * Example request for GameJolt: + * - method: MW_HTTP_METHOD_GET + * - path: "trophies" + * - key:value: "achieved":"true" + * + * \param[in] method HTTP method to use. Most likely MW_HTTP_METHOD_GET. + * \param[in] path Additional paths to add to the request. + * \param[in] num_paths Number of additional paths to add. + * \param[in] key Additional paths to add to the request. + * \param[in] value Additional paths to add to the request. + * \param[in] num_kv_pairs Number of key/value pairs. + * \param[out] content_len Content length of the API response. + * \param[in] tout_frames Number of frames to wait before canceling the + * request due to a timeout error. + * + * \return HTTP status code on success (e.g. 200), or an error (lower than + * 100) if the HTTP request could not be completed. + * \note Even if the HTTP request is completed, that does not mean there are + * no errors, if the returned status code is 4xx or 5xx, there is a client + * side or server side error. + * \note path, key and value parameters must not be URL encoded. Encoding is + * handled internally. + ****************************************************************************/ +int16_t mw_ga_request(enum mw_http_method method, const char **path, + uint8_t num_paths, const char **key, const char **value, + uint8_t num_kv_pairs, uint32_t *content_len, + int16_t tout_frames); + +/************************************************************************//** + * \brief List available upgrades WiFi module firmware. + * + * \param[in] page Page of list + * \param[in] page_size Page size of list + * \param[in] offset Offset number + * \param[out] listUpgrades Pointer to list of char* + * \param[out] len Result length + * \param[out] total Total elements + * + * \return Status of the send procedure. + ****************************************************************************/ +enum mw_err mw_fw_list_upgrades(uint8_t page, uint8_t size, uint8_t offset, char **listUpgrades, uint8_t *len, uint8_t *total); + +/************************************************************************//** + * \brief Over-The-Air upgrade WiFi module firmware. + * + * \param[in] name Name of the firmware blob to upgrade. + * E.g. "mw_rtos_std_v1.4.1" + * + * \return Status of the send procedure. + ****************************************************************************/ +enum mw_err mw_fw_upgrade(const char *name); + +/************************************************************************//** + * \brief Run ICMP requesto to domain. + * + * \param[in] domain Domain to ICMP request. + * E.g. "www.example.com" + * \param[in] retries retries to ICMP request + * E.g. 5 + * + * \return Status of the send procedure. + ****************************************************************************/ +struct mw_ping_response *mw_ping(const char* domain, u8 retries); + +/****** THE FOLLOWING COMMANDS ARE LOWER LEVEL AND USUALLY NOT NEEDED ******/ + +/************************************************************************//** + * \brief Send a command to the WiFi module. + * + * \param[in] cmd Pointer to the filled mw_cmd command structure. + * \param[in] ctx Context for callback function. + * \param[in] send_cb Callback for the send operation completion. + * + * \return Status of the send procedure. + ****************************************************************************/ +static inline enum lsd_status mw_cmd_send(mw_cmd *cmd, void *ctx, + lsd_send_cb send_cb) +{ + // Send data on control channel (0). + return lsd_send(MW_CTRL_CH, cmd->packet, cmd->data_len + 4, + ctx, send_cb); +} + +/************************************************************************//** + * \brief Try obtaining a reply to a command. + * + * \param[in] rep Buffer to hold the command reply. + * \param[in] ctx Context for the reception callback. + * \param[in] recv_cb Callback for data reception completion. + * + * \return Status of the reception procedure. + ****************************************************************************/ +static inline enum lsd_status mw_cmd_recv(mw_cmd *rep, void *ctx, + lsd_recv_cb recv_cb) { + return lsd_recv(rep->packet, sizeof(mw_cmd), ctx, recv_cb); +} + +#endif /*_MEGAWIFI_H_*/ + +/** \} */ + diff --git a/inc/ext/mw/mw-msg.h b/inc/ext/mw/mw-msg.h index 668fed54..5707ff9b 100644 --- a/inc/ext/mw/mw-msg.h +++ b/inc/ext/mw/mw-msg.h @@ -12,7 +12,8 @@ * is not directly used by the application, but by megawifi internally. * * \author Jesus Alonso (doragasu) - * \date 2015~2019 + * \author Juan Antonio (PaCHoN) + * \date 2015~2025 ****************************************************************************/ #ifndef _MW_MSG_H_ #define _MW_MSG_H_ @@ -310,6 +311,23 @@ struct mw_ga_request { char req[]; ///< Request data }; +struct mw_ping_request{ + uint8_t retries; + char domain[64]; +}; + +struct mw_ping_response{ + uint32_t transmitted; + uint32_t received; + uint32_t total_time_ms; +}; + +struct mw_upgrade_list_response{ + uint16_t total; + uint16_t len; + char *payload; +}; + /// Command sent to system FSM typedef union mw_cmd { char packet[MW_CMD_MAX_BUFLEN + 2 * sizeof(uint16_t)]; ///< Packet raw data @@ -334,6 +352,9 @@ typedef union mw_cmd { struct mw_msg_flash_data fl_data; ///< Flash memory data struct mw_msg_flash_range fl_range; ///< Flash memory range struct mw_msg_bind bind; ///< Bind message + struct mw_ping_request ping; ///< Ping message + struct mw_ping_response ping_response; ///< Ping message + struct mw_upgrade_list_response ug_list_response; ///< Ping message union mw_msg_sys_stat sys_stat; ///< System status struct mw_gamertag_set_msg gamertag_set;///< Gamertag set struct mw_gamertag gamertag_get; ///< Gamertag get diff --git a/inc/ext/mw/ssf.h b/inc/ext/mw/ssf.h new file mode 100644 index 00000000..d5337ae2 --- /dev/null +++ b/inc/ext/mw/ssf.h @@ -0,0 +1,148 @@ +/************************************************************************ + * \brief Simple SSF driver. + * + * \author Juan Antonio Ruiz (PaCHoN) + * \date 2024 + * \defgroup SSF SSF + * \brief + * USB IO + * 0xA130E2 [........ DDDDDDDD] read/write + * D data bits + * + * IO status + * 0xA130E4 [.C...... .....RWS] read only + * S SPI controller ready. Not used on WIFI. + * W USB fifo ready to write + * R USB fifo ready to read + * C SD card type. 0=SD, 1=SDHC. Not used on WIFI. +*************************************** */ +#ifndef _SSF_H_ +#define _SSF_H + +#include "config.h" +#include "types.h" + +#if (MODULE_MEGAWIFI == 1 && MODULE_EVERDRIVE == 1) +//#define SSF_CTRL_P 0x8000 //register accesss protection bit. should be set, otherwise register will ignore any attempts to write +//#define SSF_CTRL_X 0x4000 //32x mode +//#define SSF_CTRL_W 0x2000 //ROM memory write protection +//#define SSF_CTRL_L 0x1000 //led + +//#define USB_RD_BUSY while ((REG_STE & STE_USB_RD_RDY) == 0) +//#define USB_WR_BUSY while ((REG_STE & STE_USB_WR_RDY) == 0) +/**************************************** */ + + +/// SSF UART base address +#define UART_BASE 0xA13000 + +/// Length of the TX FIFO in bytes +#define UART_TX_FIFO_LEN 1 + +/// Receiver holding register. Read only. +#define UART_DATA (*((volatile u16*)(UART_BASE + 226))) +#define UART_STE (*((volatile u16*)(UART_BASE + 228))) +#define UART_REG_CFG (*((volatile u16*)(UART_BASE + 230))) +#define UART_REG_SSF_CTRL (*((volatile u16*)(UART_BASE + 240))) + +#define SSF_CTRL_P 0x8000 //register accesss protection bit. should be set, otherwise register will ignore any attempts to write +#define SSF_CTRL_X 0x4000 //32x mode +#define SSF_CTRL_W 0x2000 //ROM memory write protection +#define SSF_CTRL_L 0x1000 //led + +#define UART_SPR UART_DATA + +#define UART_STE_WR_RDY 2//usb write ready bit +#define UART_STE_RD_RDY 4//usb read ready bit + +//spi chip select signal +#define CFG_SPI_SS 1 +#define CFG_SPI_QRD 6 +#define CFG_SPI_QWR 2 + +/************************************************************************//** + * \brief Initializes the driver. The baud rate is set to UART_BR, and the + * UART FIFOs are enabled. This function must be called before using + * any other API call. + ****************************************************************************/ +void uart_init(void); + +/************************************************************************//** + * \brief Checks if UART transmit register/FIFO is ready. In FIFO mode, up to + * 16 characters can be loaded each time transmitter is ready. + * + * \return TRUE if transmitter is ready, FALSE otherwise. + ****************************************************************************/ +#define uart_tx_ready() (UART_STE & UART_STE_WR_RDY) + +/************************************************************************//** + * \brief Checks if UART receive register/FIFO has data available. + * + * \return TRUE if at least 1 byte is available, FALSE otherwise. + ****************************************************************************/ +#define uart_rx_ready() (UART_STE & UART_STE_RD_RDY) + +/************************************************************************//** + * \brief Sends a character. Please make sure there is room in the transmit + * register/FIFO by calling uart_rx_ready() before using this function. + * + * \return Received character. + ****************************************************************************/ +#define uart_putc(c) do{UART_DATA = (c);}while(0); + +/************************************************************************//** + * \brief Returns a received character. Please make sure data is available by + * calling uart_rx_ready() before using this function. + * + * \return Received character. + ****************************************************************************/ +#define uart_getc() (UART_DATA) + +/************************************************************************//** + * \brief Sets a value in IER, FCR, LCR or MCR register. + * + * \param[in] reg Register to modify (IER, FCR, LCR or MCR). + * \param[in] val Value to set in IER, FCR, LCR or MCR register. + ****************************************************************************/ +#define uart_set(reg, val) while(0); + +/************************************************************************//** + * \brief Gets value of IER, FCR, LCR or MCR register. + * + * \param[in] reg Register to read (IER, FCR, LCR or MCR). + * \return The value of the requested register. + ****************************************************************************/ +#define uart_get(reg) while(0); + +/************************************************************************//** + * \brief Sets bits in IER, FCR, LCR or MCR register. + * + * \param[in] reg Register to modify (IER, FCR, LCR or MCR). + * \param[in] val Bits set in val, will be set in reg register. + ****************************************************************************/ +#define uart_set_bits(reg, val) while(0) + +/************************************************************************//** + * \brief Clears bits in IER, FCR, LCR or MCR register. + * + * \param[in] reg Register to modify (IER, FCR, LCR or MCR). + * \param[in] val Bits set in val, will be cleared in reg register. + ****************************************************************************/ +#define uart_clr_bits(reg, val) while(0) + +/************************************************************************//** + * \brief Test Connection with registers + * + * \param[in] reg Register to modify + * \param[in] val Bits set in val, will be readed from reg register. + ****************************************************************************/ +#define uart_test(reg, val) while(0) + +/************************************************************************//** + * \brief Reset TX and RX FIFOs. + ****************************************************************************/ +#define uart_reset_fifos() while(0) +void ssf_set_rom_bank(u8 bank, u8 val); +#endif +#endif /*_SSF_H_*/ + diff --git a/sample/megawifi/megawifi.cbp b/sample/megawifi/basic/megawifi.cbp similarity index 100% rename from sample/megawifi/megawifi.cbp rename to sample/megawifi/basic/megawifi.cbp diff --git a/sample/megawifi/out/rom.bin b/sample/megawifi/basic/out/rom.bin similarity index 100% rename from sample/megawifi/out/rom.bin rename to sample/megawifi/basic/out/rom.bin diff --git a/sample/megawifi/src/boot/rom_head.c b/sample/megawifi/basic/src/boot/rom_head.c similarity index 100% rename from sample/megawifi/src/boot/rom_head.c rename to sample/megawifi/basic/src/boot/rom_head.c diff --git a/sample/megawifi/src/boot/sega.s b/sample/megawifi/basic/src/boot/sega.s similarity index 100% rename from sample/megawifi/src/boot/sega.s rename to sample/megawifi/basic/src/boot/sega.s diff --git a/sample/megawifi/src/main.c b/sample/megawifi/basic/src/main.c similarity index 100% rename from sample/megawifi/src/main.c rename to sample/megawifi/basic/src/main.c diff --git a/sample/megawifi/menu/.gitignore b/sample/megawifi/menu/.gitignore new file mode 100644 index 00000000..cfd08c85 --- /dev/null +++ b/sample/megawifi/menu/.gitignore @@ -0,0 +1,3 @@ +out/ +src/boot/ +.vscode \ No newline at end of file diff --git a/sample/megawifi/menu/README.MD b/sample/megawifi/menu/README.MD new file mode 100644 index 00000000..2911297a --- /dev/null +++ b/sample/megawifi/menu/README.MD @@ -0,0 +1,3 @@ +# My Awesome Game + +This is My Awesome 16 Bit Cartridge game for mega Drive \ No newline at end of file diff --git a/sample/megawifi/menu/inc/.gitkeep b/sample/megawifi/menu/inc/.gitkeep new file mode 100644 index 00000000..43bae573 --- /dev/null +++ b/sample/megawifi/menu/inc/.gitkeep @@ -0,0 +1 @@ +# delete this file after add content to the directory \ No newline at end of file diff --git a/sample/megawifi/menu/inc/mw-api/configuration.h b/sample/megawifi/menu/inc/mw-api/configuration.h new file mode 100644 index 00000000..fac99865 --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/configuration.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _CONFIGURATION_H_ +#define _CONFIGURATION_H_ + +#include "genesis.h" +#include "utils.h" +#include "mw-api/configuration_slot.h" +#include "mw-api/configuration_cert.h" + +void CONFIG_start(); + +void CONFIG_paint(bool repaint); + +bool CONFIG_doAction(u16 button, u8 max_option); + +#endif // _CONFIGURATION_H_ diff --git a/sample/megawifi/menu/inc/mw-api/configuration_ap.h b/sample/megawifi/menu/inc/mw-api/configuration_ap.h new file mode 100644 index 00000000..a3941f28 --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/configuration_ap.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _CONFIGURATION_AP_H_ +#define _CONFIGURATION_AP_H_ + +#include "genesis.h" +#include "utils.h" + +#define AP_MAX_ITEMS_PER_PAGE 20 + +static struct mw_ap_data apData __attribute__((unused)); + +void CONFIG_AP_start(u8 slot); + +u8 CONFIG_AP_paint(bool repaint, char* ap_data, u16 apLength, u8 page, u8 total_pages, s16 *selected_ap); + +bool CONFIG_AP_doAction(u16 button, u8 max_option, u8 *page, u8 total_pages, s16 selected_ap, char* ap_data, u16 apLength, u8 slot); + +void CONFIG_AP_paintApData(struct mw_ap_data apData, u8 line); +void CONFIG_AP_save(const char *ap_data, u16 data_len, u16 pos, char *pass, u8 slot, enum mw_phy_type phy_type); + +#endif // _CONFIGURATION_AP_H_ diff --git a/sample/megawifi/menu/inc/mw-api/configuration_cert.h b/sample/megawifi/menu/inc/mw-api/configuration_cert.h new file mode 100644 index 00000000..3ac89827 --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/configuration_cert.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _CONFIGURATION_CERT_H_ +#define _CONFIGURATION_CERT_H_ + +#include "genesis.h" +#include "utils.h" +#include "random.h" + +void CONFIG_CERT_start(); + +void CONFIG_CERT_paint(bool repaint); + +bool CONFIG_CERT_doAction(u16 button, u8 max_option); + +void CONFIG_CERT_clear(); + +#endif // _CONFIGURATION_CERT_H_ diff --git a/sample/megawifi/menu/inc/mw-api/configuration_slot.h b/sample/megawifi/menu/inc/mw-api/configuration_slot.h new file mode 100644 index 00000000..141d7255 --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/configuration_slot.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _CONFIGURATION_SLOT_H_ +#define _CONFIGURATION_SLOT_H_ + +#include "genesis.h" +#include "utils.h" +#include "mw-api/configuration_ap.h" + +void CONFIG_SLOT_start(); + +void CONFIG_SLOT_paint(bool repaint); + +bool CONFIG_SLOT_doAction(u16 button, u8 max_option); + +void CONFIG_SLOT_toogleConnection(); + +#endif // _CONFIGURATION_SLOT_H_ diff --git a/sample/megawifi/menu/inc/mw-api/datetime.h b/sample/megawifi/menu/inc/mw-api/datetime.h new file mode 100644 index 00000000..37f65ae9 --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/datetime.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _DT_H_ +#define _DT_H_ + +#include "genesis.h" +#include "utils.h" + +void DT_start(); + +void DT_paint(bool repaint); + +bool DT_doAction(u16 button, u8 max_option); + +void DT_test(); + +#endif // _DT_H_ diff --git a/sample/megawifi/menu/inc/mw-api/flash.h b/sample/megawifi/menu/inc/mw-api/flash.h new file mode 100644 index 00000000..faeecaba --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/flash.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _FLASH_H_ +#define _FLASH_H_ + +#include "genesis.h" +#include "utils.h" + +static u8 manufacturer __attribute__((unused)); +static u16 device __attribute__((unused)); + +void FLASH_start(); + +void FLASH_paint(bool repaint); + +bool FLASH_doAction(u16 button, u8 max_option); + +#endif // _FLASH_H_ diff --git a/sample/megawifi/menu/inc/mw-api/http.h b/sample/megawifi/menu/inc/mw-api/http.h new file mode 100644 index 00000000..9550b8dd --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/http.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _HTTP_H_ +#define _HTTP_H_ + +#include "genesis.h" +#include "utils.h" +#include "mw-api/configuration_cert.h" + +void HTTP_start(); + +void HTTP_paint(bool repaint); + +bool HTTP_doAction(u16 button, u8 max_option); + +void HTTP_test(const char* url, bool ssl); + +#endif // _HTTP_H_ diff --git a/sample/megawifi/menu/inc/mw-api/ping.h b/sample/megawifi/menu/inc/mw-api/ping.h new file mode 100644 index 00000000..260fe0aa --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/ping.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _PING_H_ +#define _PING_H_ + +#include "genesis.h" +#include "utils.h" + +void PING_start(); + +void PING_paint(bool repaint); + +bool PING_doAction(u16 button, u8 max_option); + +void PING_test(); + +#endif // _PING_H_ diff --git a/sample/megawifi/menu/inc/mw-api/random.h b/sample/megawifi/menu/inc/mw-api/random.h new file mode 100644 index 00000000..2a65a36b --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/random.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _RANDOM_H_ +#define _RANDOM_H_ + +#include "genesis.h" +#include "utils.h" + +void RND_start(); + +void RND_paint(bool repaint); + +bool RND_doAction(u16 button, u8 max_option); + +void RND_test(); + +#endif // _RANDOM_H_ diff --git a/sample/megawifi/menu/inc/mw-api/tcp.h b/sample/megawifi/menu/inc/mw-api/tcp.h new file mode 100644 index 00000000..0129dcfb --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/tcp.h @@ -0,0 +1,26 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _TCP_H_ +#define _TCP_H_ + +#include "genesis.h" +#include "utils.h" + +void TCP_start(); + +void TCP_paint(bool repaint); + +bool TCP_doAction(u16 button, u8 max_option); + +void TCP_test(); + +void TCP_test_server(); + +#endif // _TCP_H_ diff --git a/sample/megawifi/menu/inc/mw-api/udp.h b/sample/megawifi/menu/inc/mw-api/udp.h new file mode 100644 index 00000000..9c12a036 --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/udp.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _UDP_H_ +#define _UDP_H_ + +#include "genesis.h" +#include "utils.h" + +void UDP_start(); + +void UDP_paint(bool repaint); + +bool UDP_doAction(u16 button, u8 max_option); + +void UDP_normal_test(); + +void UDP_reuse_test(); + +/// UDP receive functions callback +void udp_recv_cb(enum lsd_status stat, uint8_t ch, char *data, uint16_t len, void *ctx); +void udp_send_complete_cb(enum lsd_status stat, void *ctx); + +#endif // _UDP_H_ diff --git a/sample/megawifi/menu/inc/mw-api/upgrade.h b/sample/megawifi/menu/inc/mw-api/upgrade.h new file mode 100644 index 00000000..d9351796 --- /dev/null +++ b/sample/megawifi/menu/inc/mw-api/upgrade.h @@ -0,0 +1,26 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _UPGRADE_H_ +#define _UPGRADE_H_ + +#include "genesis.h" +#include "utils.h" + +static char *listUpgrades __attribute__((unused)) = NULL; +static uint8_t len __attribute__((unused)) = 0; +static uint8_t total __attribute__((unused)) = 0; + +void UPGRADE_start(); + +void UPGRADE_paint(bool repaint); + +bool UPGRADE_doAction(u16 button, u8 max_option); + +#endif // _UPGRADE_H_ diff --git a/sample/megawifi/menu/inc/utils.h b/sample/megawifi/menu/inc/utils.h new file mode 100644 index 00000000..d5ca4670 --- /dev/null +++ b/sample/megawifi/menu/inc/utils.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include "genesis.h" + +#define DEFAULT_DELAY 500 +#define DEFAULT_MW_DELAY 1000 + +/// Shut compiler warnings for unused parameters +#define UNUSED_PARAM(par) (void)(par) + +/// Tuned for 60 Hz, change it for PAL consoles +#define MS_TO_FRAMES(ms) ((((ms) * 60 / 500) + 1)/2) + +/// Certificate for www.example.org +static const char cert[] = "-----BEGIN CERTIFICATE-----\n" +"MIIDeTCCAv+gAwIBAgIQCwDpLU1tcx/KMFnHyx4YhjAKBggqhkjOPQQDAzBhMQsw" +"CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu" +"ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe" +"Fw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaMFkxCzAJBgNVBAYTAlVTMRUw" +"EwYDVQQKEwxEaWdpQ2VydCBJbmMxMzAxBgNVBAMTKkRpZ2lDZXJ0IEdsb2JhbCBH" +"MyBUTFMgRUNDIFNIQTM4NCAyMDIwIENBMTB2MBAGByqGSM49AgEGBSuBBAAiA2IA" +"BHipnHWuiF1jpK1dhtgQSdavklljQyOF9EhlMM1KNJWmDj7ZfAjXVwUoSJ4Lq+vC" +"05ae7UXSi4rOAUsXQ+Fzz21zSDTcAEYJtVZUyV96xxMH0GwYF2zK28cLJlYujQf1" +"Z6OCAYIwggF+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFIoj655r1/k3" +"XfltITl2mqFn3hCoMB8GA1UdIwQYMBaAFLPbSKT5ocXYrjZBzBFjaWIpvEvGMA4G" +"A1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYI" +"KwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j" +"b20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp" +"Q2VydEdsb2JhbFJvb3RHMy5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny" +"bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdEczLmNybDA9BgNVHSAE" +"NjA0MAsGCWCGSAGG/WwCATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgG" +"BmeBDAECAzAKBggqhkjOPQQDAwNoADBlAjB+Jlhu7ojsDN0VQe56uJmZcNFiZU+g" +"IJ5HsVvBsmcxHcxyeq8ickBCbmWE/odLDxkCMQDmv9auNIdbP2fHHahv1RJ4teaH" +"MUSpXca4eMzP79QyWBH/OoUGPB2Eb9P1+dozHKQ=" +"-----END CERTIFICATE-----"; + +static const uint16_t cert_len = sizeof(cert); +/// Certificate hash, obtained with command: +/// openssl x509 -hash in -noout +static const uint32_t cert_hash = 0xa6570d26; + +static u16 option __attribute__((unused)) = 0; +static char buffer[128] __attribute__((unused)); +static long ciclo __attribute__((unused)) = 0; + +/// Command buffer. Must be word aligned +static char cmd_buf[MW_BUFLEN] __attribute__((unused)); + +#define SCREEN_COLUMNS 20U // Número de columnas en pantalla +#define SCREEN_ROWS 28U // Número de filas en pantalla + +void clearScreen(); +void println(const char *str); +void print(); +void paint_long_char(const char *cert, u16 len, u8 line); +u16 readButton(u16 joy); +int readText(char* buffer, size_t lengthMax); +void delay_ms(u16 milliseconds); +void printStatus(union mw_msg_sys_stat * status); + + +#endif // _UTILS_H_ diff --git a/sample/megawifi/menu/res/.gitkeep b/sample/megawifi/menu/res/.gitkeep new file mode 100644 index 00000000..43bae573 --- /dev/null +++ b/sample/megawifi/menu/res/.gitkeep @@ -0,0 +1 @@ +# delete this file after add content to the directory \ No newline at end of file diff --git a/sample/megawifi/menu/src/main.c b/sample/megawifi/menu/src/main.c new file mode 100644 index 00000000..e4d841eb --- /dev/null +++ b/sample/megawifi/menu/src/main.c @@ -0,0 +1,205 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * This example demonstrates the following: + * - How to initialize MegaWiFi + * - How to configurate MegaWiFi and manage Certs + * - How to do ICMP tests + * - How to do TCP connections and TCP server + * - How to do HTTP queries + * - How to send and receive data using UDP protocol + * - How to get the date/time (synchronized to NTP servers) + * - How to get random numbers + * - How to work with flash store + * - How to get MegaWiFi Firmware updates + * + * To build this example set MODULE_MEGAWIFI to 1 in config.h and + * rebuild the library. + * You can use: + * - MODULE_EVERDRIVE to 1 + * - ENABLE_BANK_SWITCH to 1 + * in config.h to use everdive and MegaWifi Addon + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "genesis.h" +#include "utils.h" +#include "mw-api/configuration.h" +#include "mw-api/http.h" +#include "mw-api/tcp.h" +#include "mw-api/udp.h" +#include "mw-api/datetime.h" +#include "mw-api/random.h" +#include "mw-api/flash.h" +#include "mw-api/upgrade.h" +#include "mw-api/ping.h" + +#if (MODULE_MEGAWIFI == 0) +#error "Set MODULE_MEGAWIFI to 1 in config.h and rebuild the library" +#endif + +/// TCP port to use (set to Megadrive release year ;-) +#define MW_CH_PORT 1985 + +/// Idle task, run using the spare CPU time available when the main task +/// pends or yields. See mw/tsk.h for details. Basically this task polls +/// the WiFi module to send and receive data. +static void idle_tsk(void) +{ + while (1) { + mw_process(); + } +} + +static void printMainMenu(bool repaint){ + if (repaint){ + clearScreen(); + VDP_drawText("Show Configurations", 1u, 1u); + VDP_drawText("DateTime Test", 1u, 2u); + VDP_drawText("Random Test", 1u, 3u); + VDP_drawText("Flash Test", 1u, 4u); + VDP_drawText("Upgrade Test", 1u, 5u); + VDP_drawText("Ping Test", 1u, 6u); + VDP_drawText("TCP Test", 1u, 7u); + VDP_drawText("HTTP Test", 1u, 8u); + VDP_drawText("UDP Test", 1u, 9u); + VDP_drawText("Option: Ciclo: ", 15u, 28u); + } + + VDP_drawText("*", 0, option + 1U); + print(); +} + +static bool doActionMainMenu(u16 button){ + switch (button){ + case BUTTON_UP: + if(option > 0) { + option--; + return TRUE; + } + break; + case BUTTON_DOWN: + if(option < 9u){ + option = option+1u % 9u; + return TRUE; + } + break; + case BUTTON_START: + switch (option){ + case 0U: + CONFIG_start(); + break; + case 1U: + DT_start(); + break; + case 2U: + RND_start(); + break; + case 3U: + FLASH_start(); + break; + case 4U: + UPGRADE_start(); + break; + case 5U: + PING_start(); + break; + case 6U: + TCP_start(); + break; + case 7U: + HTTP_start(); + break; + case 8U: + UDP_start(); + break; + default: + break; + } + return TRUE; + default: + return FALSE; + } + return FALSE; +} + +/// Associates to the AP and runs the tests +static void run_app(void) +{ + //enum mw_err err; + bool repaint = TRUE; + while(1) { + printMainMenu(repaint); + repaint = doActionMainMenu(readButton(JOY_1)); + SYS_doVBlankProcess(); + ciclo++; + } +} + +/// MegaWiFi initialization +/// Returns true on error +static bool megawifi_init(void) +{ + uint8_t ver_major = 0, ver_minor = 0; + char *variant = NULL; + enum mw_err err; + char line[] = "MegaWiFi version X.Y - zzz"; + bool ret; + + // Try detecting the module + err = mw_detect(&ver_major, &ver_minor, &variant); + + if (MW_ERR_NONE != err) { + // Megawifi not found + println("MegaWiFi not found!"); + ret = TRUE; + } else { + // Megawifi found + line[17] = ver_major + '0'; + line[19] = ver_minor + '0'; + memcpy(&(line[23]), variant, 3); + println(line); + ret = FALSE; + } + + return ret; +} + +/// Set idle task that polls WiFi module +static void tasking_init(void) +{ + TSK_userSet(idle_tsk); +} + +/// Global initialization +static void init(void) +{ + // initialization + VDP_setScreenWidth320(); + // Initialize MegaWiFi + mw_init((u16*) cmd_buf, MW_BUFLEN); + // Initialize multitasking (for WiFi background checks) + tasking_init(); +} + +/// Entry point +int main(bool hard) +{ + UNUSED_PARAM(hard); + bool err; + init(); + err = megawifi_init(); + delay_ms(DEFAULT_DELAY); + if (!err) { + union mw_msg_sys_stat *status; + while(!(status = mw_sys_stat_get())); + printStatus(status); + run_app(); + } + + return 0; +} diff --git a/sample/megawifi/menu/src/mw-api/configuration.c b/sample/megawifi/menu/src/mw-api/configuration.c new file mode 100644 index 00000000..5194758b --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/configuration.c @@ -0,0 +1,65 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/configuration.h" + +void CONFIG_start(){ + u16 button; + bool repaint = TRUE; + option = 0; + do{ + CONFIG_paint(repaint); + button = readButton(JOY_1); + repaint = CONFIG_doAction(button, 2u); + VDP_drawText("*", 0u, option + 2); + print(); + }while(button != BUTTON_A); + +} + +void CONFIG_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("Slots Configuration", 1u, 2u); + VDP_drawText("Cert Configuration", 1u, 3u); + VDP_drawText("Press START to selec", 0u, 4u); + VDP_drawText("Press A to return", 0u, 5u); + } +} + +bool CONFIG_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_UP: + VDP_drawText(" ", 0u, option + 2); + if(option > 0) { + option--; + } + break; + case BUTTON_DOWN: + VDP_drawText(" ", 0u, option + 2); + if(option < max_option - 1){ + option = option+1u % max_option; + } + break; + case BUTTON_START:{ + switch(option){ + case 0: + CONFIG_SLOT_start((u8)option); + break; + case 1: + CONFIG_CERT_start((u8)option); + break; + default: + } + return TRUE; + } + default: + } + return FALSE; +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/configuration_ap.c b/sample/megawifi/menu/src/mw-api/configuration_ap.c new file mode 100644 index 00000000..1aa70826 --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/configuration_ap.c @@ -0,0 +1,144 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/configuration_ap.h" + +void CONFIG_AP_paintApData(struct mw_ap_data apData, u8 line){ + + sprintf(buffer, "%d", apData.rssi); + VDP_drawText(buffer , 1, line); + switch(apData.auth){ + case MW_SEC_OPEN: + VDP_drawText("OPEN", 5, line); + break; + case MW_SEC_WEP: + VDP_drawText("WEP", 5, line); + break; + case MW_SEC_WPA_PSK: + VDP_drawText("WPA", 5, line); + break; + case MW_SEC_WPA2_PSK: + VDP_drawText("WPA2", 5, line); + break; + case MW_SEC_WPA_WPA2_PSK: + VDP_drawText("MIX", 5, line); + break; + default: + VDP_drawText("UNK" , 5, line); + } + char apBuff[apData.ssid_len]; + memcpy(apBuff,apData.ssid, apData.ssid_len); + VDP_drawText(apBuff, 10, line); +} + +u8 CONFIG_AP_paint(bool repaint, char* ap_data, u16 apLength, u8 page, u8 total_pages, s16 *selected_ap){ + u8 max_option = AP_MAX_ITEMS_PER_PAGE; + if(repaint){ + clearScreen(); + u8 count = 0, aux_count = 0; + u8 pageAux = 0; + s16 pos = 0; + do{ + if(option == count && pageAux == page) *selected_ap = pos; + pos = mw_ap_fill_next(ap_data, pos ,&apData, apLength); + if(pos){ + if(pageAux == page){ + CONFIG_AP_paintApData(apData, count + 1U); + count++; + }else{ + aux_count++; + if(aux_count == AP_MAX_ITEMS_PER_PAGE){ + aux_count = 0; + pageAux++; + } + } + } + }while(pos && pos < apLength && count < AP_MAX_ITEMS_PER_PAGE); + max_option = count - 1; + VDP_drawText("*", 0, option + 1U); + VDP_drawText("Press A to return", 0u, AP_MAX_ITEMS_PER_PAGE + 2u); + repaint = FALSE; + } + + sprintf(buffer, "%u/%u", page + 1,total_pages); + VDP_drawText(buffer, 0u, 27u); + print(); + return max_option; +} + +void CONFIG_AP_save(const char *ap_data, u16 data_len, u16 pos, char *pass, u8 slot, enum mw_phy_type phy_type){ + struct mw_ap_data apd; + mw_ap_fill_next(ap_data, pos ,&apd, data_len); + char apBuff[apd.ssid_len + 1u]; + memcpy(apBuff,apd.ssid, apd.ssid_len); + apBuff[apd.ssid_len]= '\0'; + while(mw_ap_cfg_set(slot,apBuff, pass, phy_type) + mw_cfg_save()); +} + +bool CONFIG_AP_doAction(u16 button, u8 max_option, u8 *page, u8 total_pages, s16 selected_ap, char* ap_data, u16 apLength, u8 slot){ + switch (button){ + case BUTTON_UP: + if(option > 0) { + option--; + } + return TRUE; + case BUTTON_DOWN: + if(option < max_option ){ + option = option+1u % max_option; + } + return TRUE; + case BUTTON_LEFT: + option = 0; + if(*page > 0) { + (*page)--; + } + return TRUE; + case BUTTON_RIGHT: + option = 0; + if((*page) < total_pages -1){ + (*page) = (*page)+1u % total_pages; + } + return TRUE; + case BUTTON_START:{ + if(selected_ap >= 0){ + char bufferPass[MW_PASS_MAXLEN]; + int length = readText(bufferPass, MW_PASS_MAXLEN); + char pass[length]; + memcpy(pass,bufferPass, length); + CONFIG_AP_save(ap_data, apLength, selected_ap, pass, slot, MW_PHY_11BGN); + button = BUTTON_A; + } + return TRUE; + } + default: + } + return FALSE; +} + +void CONFIG_AP_start(u8 slot){ + char* ap_data; + s16 selected_ap = -1; + u8 aps = 0, page = 0; + bool repaint = TRUE; + u16 button; + option = 0; + u8 max_option = AP_MAX_ITEMS_PER_PAGE; + clearScreen(); + println("Searching Aps..."); + s16 apLength = mw_ap_scan(MW_PHY_11BGN, &ap_data, &aps); + if (!apLength) println("No Ap"); + u8 total_pages = aps / AP_MAX_ITEMS_PER_PAGE; + if (aps % AP_MAX_ITEMS_PER_PAGE > 0) total_pages++; + do{ + if (apLength) max_option = CONFIG_AP_paint(repaint, ap_data, apLength,page,total_pages, &selected_ap); + button = readButton(JOY_1); + if (apLength) repaint = CONFIG_AP_doAction(button, max_option, &page, total_pages, selected_ap, ap_data, apLength, slot); + ciclo++; + }while(button != BUTTON_A); +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/configuration_cert.c b/sample/megawifi/menu/src/mw-api/configuration_cert.c new file mode 100644 index 00000000..a3770ea1 --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/configuration_cert.c @@ -0,0 +1,65 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/configuration_cert.h" + +void CONFIG_CERT_start(){ + + u16 button; + bool repaint = TRUE; + option = 0; + + do{ + CONFIG_CERT_paint(repaint); + button = readButton(JOY_1); + repaint = CONFIG_CERT_doAction(button, 2); + print(); + }while(button != BUTTON_A); + +} + +void CONFIG_CERT_paint(bool repaint){ + if(repaint){ + clearScreen(); + println("Checking Cert..."); + delay_ms(DEFAULT_DELAY); + u32 hash = mw_http_cert_query(); + sprintf(buffer, "Store Cert Hash: %08lx", hash); + VDP_drawText(buffer, 0u, 2u); + VDP_drawText("Press START to Set Cert", 0u, 4u); + VDP_drawText("Press A to return", 0u, 6u); + VDP_drawText("Press B to clear Cert", 0u, 7u); + sprintf(buffer, "Cert Hash: %08lx len: %u", cert_hash, cert_len); + VDP_drawText(buffer, 0u, 9u); + paint_long_char(cert, cert_len, 11u); + repaint = false; + } +} + +bool CONFIG_CERT_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_B: + CONFIG_CERT_clear(); + break; + case BUTTON_START:{ + int rety = 3; + while(mw_http_cert_set(cert_hash, cert, cert_len) && rety-->0); + return TRUE; + } + default: + } + return FALSE; +} + +void CONFIG_CERT_clear(){ + char mycert[20]; + memset(&mycert, 0,20); + int rety = 3; + while(mw_http_cert_set(23, mycert, 20) && rety--); +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/configuration_slot.c b/sample/megawifi/menu/src/mw-api/configuration_slot.c new file mode 100644 index 00000000..5920c6ab --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/configuration_slot.c @@ -0,0 +1,132 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/configuration_slot.h" + +void CONFIG_SLOT_start(){ + u16 button; + bool repaint = TRUE; + option = 0; + do{ + CONFIG_SLOT_paint(repaint); + button = readButton(JOY_1); + repaint = CONFIG_SLOT_doAction(button, MW_NUM_CFG_SLOTS); + VDP_drawText("*", 0u, option + 5); + print(); + }while(button != BUTTON_A); + +} + +void CONFIG_SLOT_paint(bool repaint){ + char *ssid, *pass; + enum mw_phy_type phyType; + if(repaint){ + clearScreen(); + println("Searching Configs..."); + int i = 0; + for(i = 0; i < MW_NUM_CFG_SLOTS; i++){ + while(mw_ap_cfg_get((u8)i, &ssid, &pass, &phyType)); + sprintf(buffer, "%u: SSID:%s", (u8)i, ssid); + VDP_drawText(buffer, 1u, (u16)i + 5); + sprintf(buffer, "PASS: %.1s**", pass); + VDP_drawText(buffer, 30u, (u16)i + 5); + strclr(buffer); + } + s16 slotDefault = mw_def_ap_cfg_get(); + sprintf(buffer, "Default Slot: %d", slotDefault); + VDP_drawText(buffer, 1u, (u16)10); + VDP_drawText("Press Start to Configure Slot", 0u, (u16)16); + VDP_drawText("Press A to Return", 0u, (u16)17); + VDP_drawText("Press B associate/des to AP from Slot", 0u, (u16)18); + VDP_drawText("Press C to Set Default AP Config Slot", 0u, (u16)19); + repaint = false; + } +} + +bool CONFIG_SLOT_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_UP: + VDP_drawText(" ", 0u, option + 5); + if(option > 0) { + option--; + } + break; + case BUTTON_DOWN: + VDP_drawText(" ", 0u, option + 5); + if(option < max_option - 1){ + option = option+1u % max_option; + } + break; + case BUTTON_B:{ + CONFIG_SLOT_toogleConnection(); + union mw_msg_sys_stat *status; + while (!(status = mw_sys_stat_get())); + printStatus(status); + break; + } + case BUTTON_C: + mw_def_ap_cfg((u8)option); + mw_cfg_save(); + return TRUE; + case BUTTON_START: + CONFIG_AP_start((u8)option); + return TRUE; + default: + } + return FALSE; +} + +void CONFIG_SLOT_toogleConnection(){ + + union mw_msg_sys_stat *status; + while (!(status = mw_sys_stat_get())); + printStatus(status); + if(status->online){ + println("Disconecting... "); + SYS_doVBlankProcess(); + mw_sleep(MS_TO_FRAMES(DEFAULT_MW_DELAY)); + while (!mw_ap_disassoc()); + println("Disconected "); + SYS_doVBlankProcess(); + }else{ + struct mw_ip_cfg *ipConf; + println("AP Connecting..."); + SYS_doVBlankProcess(); + mw_sleep(MS_TO_FRAMES(DEFAULT_MW_DELAY)); + int i =3; + println("AP Waiting... "); + SYS_doVBlankProcess(); + while(mw_ap_assoc((u8)option) && i--){ + sprintf(buffer, "%d", i); + VDP_drawText(buffer, 22u, 0); + SYS_doVBlankProcess(); + } + i =3; + if(mw_ap_assoc_wait(MS_TO_FRAMES(60000))){ + println("ERROR "); + SYS_doVBlankProcess(); + }else{ + println("Checking IP..."); + SYS_doVBlankProcess(); + while(mw_ip_current(&ipConf)); + sprintf(buffer, "IP: %u.%u.%u.%u", ipConf->addr.byte[0], ipConf->addr.byte[1], ipConf->addr.byte[2], ipConf->addr.byte[3]); + VDP_drawText(buffer, 0u, 21u); + sprintf(buffer, "D1: %u.%u.%u.%u", ipConf->dns1.byte[0], ipConf->dns1.byte[1], ipConf->dns1.byte[2], ipConf->dns1.byte[3]); + VDP_drawText(buffer, 0u, 22u); + sprintf(buffer, "D2: %u.%u.%u.%u", ipConf->dns2.byte[0], ipConf->dns2.byte[1], ipConf->dns2.byte[2], ipConf->dns2.byte[3]); + VDP_drawText(buffer, 0u, 23u); + sprintf(buffer, "GW: %u.%u.%u.%u", ipConf->gateway.byte[0], ipConf->gateway.byte[1], ipConf->gateway.byte[2], ipConf->gateway.byte[3]); + VDP_drawText(buffer, 0u, 24u); + sprintf(buffer, "MK: %u.%u.%u.%u", ipConf->mask.byte[0], ipConf->mask.byte[1], ipConf->mask.byte[2], ipConf->mask.byte[3]); + VDP_drawText(buffer, 0u, 25u); + println("IP Checked "); + SYS_doVBlankProcess(); + } + } +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/datetime.c b/sample/megawifi/menu/src/mw-api/datetime.c new file mode 100644 index 00000000..987a545c --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/datetime.c @@ -0,0 +1,64 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/datetime.h" + +void DT_start(){ + + u16 button; + bool repaint = TRUE; + + do{ + DT_paint(repaint); + button = readButton(JOY_1); + repaint = DT_doAction(button, 0); + print(); + }while(button != BUTTON_A); + +} + +void DT_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("DT Test", 1u, 2u); + VDP_drawText("Press START to launch", 0u, 3u); + VDP_drawText("Press A to return", 0u, 4u); + } +} + +bool DT_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_START:{ + DT_test(); + } + default: + } + return FALSE; +} + + +/// Waits until date/time is synchronized and gets the date/time +void DT_test() +{ + const char *datetime; + uint32_t dt_bin[2] = {}; + union mw_msg_sys_stat *stat; + + // Wait until date/time is set + do { + mw_sleep(60); + stat = mw_sys_stat_get(); + if (!stat) { + println("Failed to get date/time"); + return; + } + } while (!stat->dt_ok); + datetime = mw_date_time_get(dt_bin); + println(datetime); +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/flash.c b/sample/megawifi/menu/src/mw-api/flash.c new file mode 100644 index 00000000..4541dc3a --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/flash.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/flash.h" + +void FLASH_start(){ + u16 button; + bool repaint = TRUE; + option = 0; + + //mw_flash_info_get(); //TODO: NOT implemented ON SGDK ni RTOS + do{ + FLASH_paint(repaint); + button = readButton(JOY_1); + repaint = FLASH_doAction(button, 4u); + VDP_drawText("*", 0u, option + 2); + print(); + }while(button != BUTTON_A); + +} + +void FLASH_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("Get Flash IDS", 1u, 2u); + VDP_drawText("Read Flash", 1u, 3u); + VDP_drawText("Write Flash", 1u, 4u); + VDP_drawText("Erase Flash", 1u, 5u); + VDP_drawText("Press Start to exec", 0u, 6u); + VDP_drawText("Press A to return", 0u, 7u); + + if(manufacturer && device){ + sprintf(buffer, "Manufacturer: %2X Device: %4X", manufacturer, device); + VDP_drawText(buffer, 0u, 9u); + } + } +} + +bool FLASH_doAction(u16 button, u8 max_option){ + + enum mw_err err; + switch (button){ + case BUTTON_UP: + VDP_drawText(" ", 0u, option + 2); + if(option > 0) { + option--; + } + break; + case BUTTON_DOWN: + VDP_drawText(" ", 0u, option + 2); + if(option < max_option - 1){ + option = option+1u % max_option; + } + break; + case BUTTON_START:{ + switch(option){ + case 0: + err = mw_flash_id_get(&manufacturer, &device); + if(err){ + sprintf(buffer, "MW-ERROR: %u ", err); + println(buffer); + }else{ + return TRUE; + } + break; + case 1:{ + u8* response = mw_flash_read(0x0000, 512U); + if(!response){ + println("FLASH READ 0x0000 512B FAIL"); + }else{ + paint_long_char((const char *)response, 512U, 11u); + } + break; + } + case 2: + err = mw_flash_write(0x0000, (u8 *)"DATA STORE ON FLASS", 20); + if(err){ + sprintf(buffer, "MW-ERROR: %u ", err); + println(buffer); + } + break; + case 3: + err = mw_flash_sector_erase(0); + if(err){ + sprintf(buffer, "MW-ERROR: %u ", err); + println(buffer); + } + break; + default: + } + } + default: + } + return FALSE; +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/http.c b/sample/megawifi/menu/src/mw-api/http.c new file mode 100644 index 00000000..a9d74341 --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/http.c @@ -0,0 +1,120 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/http.h" + + +/// Receives data from the HTTP test +static int http_recv(uint32_t len) +{ + int16_t recv_last; + int err = FALSE; + uint32_t recvd = 0; + uint8_t ch = MW_HTTP_CH; + + u8 line = 11u; + + while (recvd < len && !err) { + recv_last = MW_BUFLEN; + err = mw_recv_sync(&ch, cmd_buf, &recv_last, TSK_PEND_FOREVER) != MW_ERR_NONE; + paint_long_char(cmd_buf, recv_last - recvd, line); + line++; + recvd += recv_last; + } + + return err; +} + +void HTTP_start(){ + + u16 button; + bool repaint = TRUE; + + do{ + HTTP_paint(repaint); + button = readButton(JOY_1); + repaint = HTTP_doAction(button, 0); + print(); + }while(button != BUTTON_A); + +} + +void HTTP_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("HTTPs Test", 1u, 2u); + VDP_drawText("Press START to launch", 0u, 3u); + VDP_drawText("Press A to return", 0u, 4u); + VDP_drawText("Press B by Http", 0u, 5u); + } +} + +bool HTTP_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_START: + HTTP_test("https://www.example.com", TRUE); + break; + case BUTTON_B: + HTTP_test("http://www.example.com", FALSE); + break; + default: + } + return FALSE; +} + +/// Sets the certificate for the HTTPS TLS connection +static void http_cert_set(void) +{ + uint32_t hash = mw_http_cert_query(); + if (hash != cert_hash) { + mw_http_cert_set(cert_hash, cert, cert_len); + } +} + + +/// Test an HTTP GET request to https://www.example.com +void HTTP_test(const char* url, bool ssl) { + uint32_t len = 0; + enum mw_err err = MW_ERR_NONE; + s16 errHttp = -1; + if(ssl){ + http_cert_set(); + }else{ + CONFIG_CERT_clear(); + } + + println("Setting URL "); + print(); + delay_ms(DEFAULT_DELAY); + err = mw_http_url_set(url); + if (err) goto err_out; + println("Setting Method "); + print(); + delay_ms(DEFAULT_DELAY); + err = mw_http_method_set(MW_HTTP_METHOD_GET); + if (err) goto err_out; + println("Open URL "); + print(); + delay_ms(DEFAULT_DELAY); + err = mw_http_open(0); + if (err) goto err_out; + println("Finish URL "); + print(); + delay_ms(DEFAULT_DELAY); + errHttp = mw_http_finish(&len, MS_TO_FRAMES(20000)); + if (errHttp < 100) goto err_out; + if (len) { + if (http_recv(len)) goto err_out; + } + println("HTTP test SUCCESS "); + return; +err_out: + sprintf(buffer, "MW-ERROR: %u HTTP-ERROR: %d", err, errHttp); + println(buffer); +} diff --git a/sample/megawifi/menu/src/mw-api/ping.c b/sample/megawifi/menu/src/mw-api/ping.c new file mode 100644 index 00000000..662b5d92 --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/ping.c @@ -0,0 +1,62 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/ping.h" + +void PING_start(){ + + u16 button; + bool repaint = TRUE; + + do{ + PING_paint(repaint); + button = readButton(JOY_1); + repaint = PING_doAction(button, 0); + print(); + }while(button != BUTTON_A); + +} + +void PING_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("PING Test", 1u, 2u); + VDP_drawText("Press START to launch", 0u, 3u); + VDP_drawText("Press A to return", 0u, 4u); + } +} + +bool PING_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_START:{ + PING_test(); + break; + } + default: + } + return FALSE; +} + + +/// Waits until date/time is synchronized and gets the date/time +void PING_test() +{ + struct mw_ping_response *ping_response; + + println("PING to www.example.com ..."); + print(); + delay_ms(DEFAULT_DELAY); + ping_response = mw_ping("www.example.com", 5u); + if(ping_response){ + sprintf(buffer, "t: %2lu, r: %2lu ms: %5lu", ping_response->transmitted, ping_response->received, ping_response->total_time_ms); + println(buffer); + }else{ + println("FAIL PING to www.example.com"); + } +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/random.c b/sample/megawifi/menu/src/mw-api/random.c new file mode 100644 index 00000000..e9f41d42 --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/random.c @@ -0,0 +1,58 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/random.h" + +void RND_start(){ + + u16 button; + bool repaint = TRUE; + + do{ + RND_paint(repaint); + button = readButton(JOY_1); + repaint = RND_doAction(button, 0); + print(); + }while(button != BUTTON_A); + +} + +void RND_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("RANDOM Test", 1u, 2u); + VDP_drawText("Press START to launch", 0u, 3u); + VDP_drawText("Press A to return", 0u, 4u); + } +} + +bool RND_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_START:{ + RND_test(); + } + default: + } + return FALSE; +} + + +/// Waits until date/time is synchronized and gets the date/time +void RND_test() +{ + u8 *rnd; + + rnd = mw_hrng_get(1u); + if (!rnd) { + println("Failed to get Random"); + return; + } + sprintf(buffer, "Random Result: %u", *rnd); + println(buffer); +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/tcp.c b/sample/megawifi/menu/src/mw-api/tcp.c new file mode 100644 index 00000000..466a59d8 --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/tcp.c @@ -0,0 +1,95 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/tcp.h" + +void TCP_start(){ + + u16 button; + bool repaint = TRUE; + option = 0; + + do{ + TCP_paint(repaint); + button = readButton(JOY_1); + repaint = TCP_doAction(button, 0); + print(); + }while(button != BUTTON_A); + +} + +void TCP_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("TCP Test", 1u, 2u); + VDP_drawText("Press START to launch", 0u, 3u); + VDP_drawText("Press A to return", 0u, 4u); + VDP_drawText("Press B to TCP SERVER", 0u, 5u); + } +} + +bool TCP_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_START:{ + TCP_test(); + break; + } + case BUTTON_B:{ + TCP_test_server(); + } + default: + } + return FALSE; +} + + +/// Test an TCP GET request to TCPs://www.example.com +void TCP_test() { + enum mw_err err; + + // Connect to www.example.com on port 80 + println("Connecting to www.example.com"); + print(); + delay_ms(DEFAULT_DELAY); + err = mw_tcp_connect(1, "www.example.com", "80", NULL); + if (err) { + println("TCP test FAILED "); + }else{ + println("TCP test SUCCESS "); + } + //mw_send_sync(1,"a", 1, MS_TO_FRAMES(DEFAULT_MW_DELAY)); + //mw_recv_sync(1); + mw_tcp_disconnect(1); +} + +void TCP_test_server(){ + println("Creating Server on port 80"); + print(); + delay_ms(DEFAULT_DELAY); + enum mw_err err = mw_tcp_bind(1u, 80u); + if(err)goto err; + println("Creating Server OK "); + print(); + delay_ms(DEFAULT_DELAY); + println("Waiting connections... "); + print(); + s16 buf_len; + u8 ch; + err = mw_recv_sync(&ch, cmd_buf, &buf_len, DEFAULT_MW_DELAY); + if(err)goto err; + println("Receving OK "); + paint_long_char(cmd_buf, buf_len, 11u); + mw_tcp_disconnect(1u); + return; +err: + sprintf(buffer, "MW-ERROR: %u ", err); + println(buffer); + mw_tcp_disconnect(1u); + +} diff --git a/sample/megawifi/menu/src/mw-api/udp.c b/sample/megawifi/menu/src/mw-api/udp.c new file mode 100644 index 00000000..aeba4542 --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/udp.c @@ -0,0 +1,140 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/udp.h" + +void UDP_start(){ + + u16 button; + bool repaint = TRUE; + option = 0; + + do{ + UDP_paint(repaint); + button = readButton(JOY_1); + repaint = UDP_doAction(button, 0); + print(); + }while(button != BUTTON_A); + +} + +void UDP_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("UDP Test", 1u, 2u); + VDP_drawText("Press START to launch", 0u, 3u); + VDP_drawText("Press A to return", 0u, 4u); + VDP_drawText("Press B to reuse", 0u, 5u); + } +} + +bool UDP_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_START: + UDP_normal_test(); + break; + case BUTTON_B: + UDP_reuse_test(); + break; + default: + } + return FALSE; +} + + + +/// Callback set by mw_udp_reuse_recv(), run when data is received +void udp_recv_cb(enum lsd_status stat, uint8_t ch, char *data, uint16_t len, void *ctx) { + const struct mw_reuse_payload *udp = + (const struct mw_reuse_payload*)data; + UNUSED_PARAM(ctx); + + // Ignore frame if not from channel 2 + if (LSD_STAT_COMPLETE == stat && 2 == ch) { + mw_udp_reuse_send(2, udp, len, NULL, udp_send_complete_cb); + } else { + mw_udp_reuse_recv((struct mw_reuse_payload*)cmd_buf, + MW_BUFLEN, NULL, udp_recv_cb); + } +} + +/// Callback set by mw_upd_reuse_send(), run when data is sent +void udp_send_complete_cb(enum lsd_status stat, void *ctx) +{ + struct mw_reuse_payload *pkt = (struct mw_reuse_payload * const)cmd_buf; + UNUSED_PARAM(ctx); + UNUSED_PARAM(stat); + + // Trigger reception of another UDP packet + mw_udp_reuse_recv(pkt, MW_BUFLEN, NULL, udp_recv_cb); +} + +/// Sends "MegaWiFi UDP test!" string to 127.0.0.1:12345. If you are not using +/// an emulator, change the 127.0.0.1 address with the one for your PC. On your +/// PC you can receive UDP data e.g. by running command: $ nc -lu 12345 +void UDP_normal_test(void) { + char line[40]; + int16_t len = sizeof(line); + uint8_t ch = 1; + enum mw_err err = MW_ERR_NONE, errS = MW_ERR_NONE, errR = MW_ERR_NONE; + + // Make sure you are listening on the target address, e.g. with command: + // $ nc -lu 12345 + println("Send to UDP 12345, waiting for reply"); + // Send UDP data to peer and wait for reply. Localhost works only when + // using emulators, so change IP as needed when using the real thing. + err = mw_udp_set(ch, "127.0.0.1", "12345", NULL); + if (err) goto err; + errS = mw_send_sync(ch, "MegaWiFi UDP test!\n", 20, DEFAULT_MW_DELAY); + errR = mw_recv_sync(&ch, line, &len, DEFAULT_MW_DELAY); + line[min(39, len)] = '\0'; + if (1 == ch) { + VDP_drawText("Got UDP reply:", 0u, 7u); + paint_long_char(line, len, 8u); + } + mw_close(ch); + + return; + +err: + sprintf(buffer, "MW-ERROR: %u MW-ES: %u MW-ER: %u", err, errS, errR); + println(buffer); + mw_close(ch); +} + +/// Implements an UDP echo server at port 8007. You can send data to this port +/// and receive the echo e.g. by running command: $ nc -u 8007 +void UDP_reuse_test(void) { + struct mw_reuse_payload *pkt = (struct mw_reuse_payload * const)cmd_buf; + + enum mw_err err = MW_ERR_NONE; + enum lsd_status errR = LSD_STAT_COMPLETE; + + // You can send text and get the echo e.g. by: + // nc -u 8007 + println("Doing echo on UDP port 8007"); + print(); + delay_ms(DEFAULT_DELAY); + // Start UDP echo task + err = mw_udp_set(2, NULL, NULL, "8007"); + if (err) goto err; + println("Receiving from UDP 8007, waiting for reply"); + print(); + delay_ms(DEFAULT_DELAY); + errR = mw_udp_reuse_recv(pkt, MW_BUFLEN, NULL, udp_recv_cb); + if (errR) goto err; + paint_long_char(pkt->payload, MW_CMD_MAX_BUFLEN - 4 - 2, 11u); + mw_close(2); + return; + +err: + sprintf(buffer, "MW-ERROR: %u LSD-ER: %d", err, errR); + println(buffer); + mw_close(2); +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/mw-api/upgrade.c b/sample/megawifi/menu/src/mw-api/upgrade.c new file mode 100644 index 00000000..15e5fd6c --- /dev/null +++ b/sample/megawifi/menu/src/mw-api/upgrade.c @@ -0,0 +1,82 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "mw-api/upgrade.h" + +void UPGRADE_start(){ + u16 button; + bool repaint = TRUE; + option = 0; + do{ + UPGRADE_paint(repaint); + button = readButton(JOY_1); + repaint = UPGRADE_doAction(button, 2u); + VDP_drawText("*", 0u, option + 2); + print(); + }while(button != BUTTON_A); + +} + +void UPGRADE_paint(bool repaint){ + if(repaint){ + clearScreen(); + VDP_drawText("List Firmwares", 1u, 2u); + VDP_drawText("Upgrade", 1u, 3u); + VDP_drawText("Press START to select", 0u, 4u); + VDP_drawText("Press A to return", 0u, 5u); + } +} + +bool UPGRADE_doAction(u16 button, u8 max_option){ + switch (button){ + case BUTTON_UP: + VDP_drawText(" ", 0u, option + 2); + if(option > 0) { + option--; + } + break; + case BUTTON_DOWN: + VDP_drawText(" ", 0u, option + 2); + if(option < max_option - 1){ + option = option+1u % max_option; + } + break; + case BUTTON_START:{ + switch(option){ + case 0: + if(mw_fw_list_upgrades(0u, 10u, 0u, &listUpgrades, &len, &total)){ + println("Upgrade Error"); + }else{ + len = 0; + println("Upgrade OK"); + } + break; + case 1: + if(len){ + u16 i = 0; + while(listUpgrades[i++]); + char rtosName[64]; + memcpy(rtosName, listUpgrades, i - 1); + if(mw_fw_upgrade(rtosName)){ + println("Upgrade Error"); + }else{ + println("Upgrade OK"); + } + }else{ + println("No Upgrades available"); + } + break; + default: + } + return TRUE; + } + default: + } + return FALSE; +} \ No newline at end of file diff --git a/sample/megawifi/menu/src/utils.c b/sample/megawifi/menu/src/utils.c new file mode 100644 index 00000000..4bf36acb --- /dev/null +++ b/sample/megawifi/menu/src/utils.c @@ -0,0 +1,166 @@ +/**************************************************************************** + * \brief MegaWiFi2 example. + * + * \author Juan Antonio (PaCHoN) + * \author Jesus Alonso (doragasu) + * + * \date 01/2025 + ****************************************************************************/ + +#include "genesis.h" + +#include "utils.h" + +u16 readButton(u16 joy){ + static u16 lastValue = 0; + u16 val = JOY_readJoypad(JOY_1); + if(val != lastValue){ + lastValue = val; + }else{ + return 0; + } + return lastValue; +} +void clearScreen(){ + strclr(buffer); + //VDP_clearTextArea(0u,3u, 1u, 3u); + for(u16 i = 0; i< SCREEN_ROWS - 1; i++){ + VDP_clearTextLine(i); + } +} + +// Obtener el carácter en la posición (x, y) +static char getCharFromPosition(u16 x, u16 y) { + // Calcula el carácter basado en su posición en la cuadrícula + // Offset por empezar en el carácter 32 (espacio) + u16 index = y * SCREEN_COLUMNS + x; // Filas comienzan en 1 + char c = 32 + index; + + if (c >= 32 && c <= 126) { + return c; + } + + return '\0'; // Fuera del rango imprimible +} + +// Actualizar la posición del cursor según la entrada del pad +static void updateCursor(u16 *cursorX, u16 *cursorY, u16 button) { + if (button == BUTTON_RIGHT && *cursorX < SCREEN_COLUMNS - 1) (*cursorX)++; + if (button == BUTTON_LEFT && *cursorX > 0) (*cursorX)--; + if (button == BUTTON_DOWN && *cursorY < 5) (*cursorY)++; + if (button == BUTTON_UP && *cursorY > 0) (*cursorY)--; // Comienza en fila 1 +} + +// Dibujar el alfabeto en la pantalla +static void drawAlphabet() { + char c[2] = " "; // Primer carácter imprimible (espacio) + + for (u16 y = 1; y < SCREEN_ROWS; y++) { // Comienza en la fila 1 (para no tapar el buffer de texto) + for (u16 x = 0; x < SCREEN_COLUMNS; x++) { + if (c[0] > 126) return; // Detener cuando se pinten todos los caracteres + VDP_drawText(c, (x) * 2u + 1u, y); + c[0]++; // Siguiente carácter + } + } +} + +void delay_ms(u16 milliseconds) { + u16 frames = MS_TO_FRAMES(milliseconds); + for (u16 i = 0; i < frames; i++) { + SYS_doVBlankProcess(); // Espera al siguiente VBlank + } +} + +void println(const char *str) +{ + if (str) { + VDP_drawText(str, 1, 0); + } +} + +int readText(char* buffer, size_t lengthMax){ + u16 button; + bool next = TRUE; + strclr(buffer); + u16 textIndex = 0; + u16 cursorX = 0, cursorY = 0; // Posición inicial del cursor + u16 prevCursorX = cursorX, prevCursorY = cursorY; // Para evitar redibujos innecesarios + clearScreen(); + drawAlphabet(); + VDP_drawText(">", cursorX * 2, cursorY + 1 ); // Dibuja el nuevo cursor + do { + button = readButton(JOY_1); + // Actualizar la posición del cursor + updateCursor(&cursorX, &cursorY, button); + + // Dibujar el cursor en la nueva posición + if (cursorX != prevCursorX || cursorY != prevCursorY) { + VDP_drawText(" ", prevCursorX * 2, prevCursorY + 1); // Borra el cursor anterior + VDP_drawText(">", cursorX * 2, cursorY + 1 ); // Dibuja el nuevo cursor + prevCursorX = cursorX; + prevCursorY = cursorY; + } + // Leer la entrada del botón A para seleccionar el carácter + if (button == BUTTON_A) { + char selectedChar = getCharFromPosition(cursorX, cursorY); + if (selectedChar && textIndex < lengthMax - 1) { + buffer[textIndex++] = selectedChar; // Agregar al buffer + buffer[textIndex] = '\0'; // Finalizar la cadena + + // Mostrar el texto construido hasta ahora en la esquina superior izquierda + VDP_clearTextLine(0); // Borra la línea superior + VDP_drawText(buffer, 0, 0); + } + } + if (button == BUTTON_B) { + char selectedChar = getCharFromPosition(cursorX, cursorY); + if (selectedChar && textIndex < lengthMax - 1) { + textIndex--; + buffer[textIndex] = '\0'; // Finalizar la cadena + // Mostrar el texto construido hasta ahora en la esquina superior izquierda + VDP_clearTextLine(0); // Borra la línea superior + VDP_drawText(buffer, 0, 0); + } + } + if (button == BUTTON_START) { + next = FALSE; + } + SYS_doVBlankProcess(); + }while(next); + return textIndex; +} + +void print(){ + ciclo++; + sprintf(buffer, "%2u", option); + VDP_drawText(buffer, 1u, 27u); + sprintf(buffer, "%6lu", ciclo); + VDP_drawText(buffer, 25u, 27u); + SYS_doVBlankProcess(); +} + +void printStatus(union mw_msg_sys_stat * status){ + if(status!=NULL){ + if(status->sys_stat == MW_ST_READY){ + VDP_drawText("READY", 5u, 27u); + }else{ + VDP_drawText("NO CON", 5u, 27u); + } + } + SYS_doVBlankProcess(); +} + + + +void paint_long_char(const char *cert, u16 len, u8 line){ + u16 from = 0; + u8 cicles = 0; + for(u16 i = 0; i < len && cicles < 5; i++){ + if(cert[i] == '\n' || (i + 1) % 40u == 0 || i == len - 1u){ + VDP_drawText(cert + from, 0u, line++); + from = i + 1; + cicles++; + } + } + VDP_drawText("...", 0u, line); +} \ No newline at end of file diff --git a/src/ext/mw/16c550.c b/src/ext/mw/16c550.c index 29b92310..f6dae1fc 100644 --- a/src/ext/mw/16c550.c +++ b/src/ext/mw/16c550.c @@ -1,36 +1,36 @@ -#include "config.h" - -#if (MODULE_MEGAWIFI != 0) - -#include "ext/mw/16c550.h" - -/// Shadow copy of the UART registers -UartShadow sh; - -void uart_init(void) { - // Set line to BR,8N1. LCR[7] must be set to access DLX registers - UART_LCR = 0x83; - UART_DLM = UART_DLM_VAL; - UART_DLL = UART_DLL_VAL; - uart_set(LCR, 0x03); - - // Enable auto RTS/CTS. - uart_set(MCR, 0x22); - - // Enable FIFOs, set trigger level to 14 bytes. - // NOTE: Even though trigger level is 14 bytes, RTS is de-asserted when - // receiving the first bit of the 16th byte entering the FIFO. See Fig. 9 - // of the SC16C550B datasheet. - UART_FCR = 0xC1; - // Reset FIFOs - uart_set(FCR, 0xC7); - - // Set IER default value (for the shadow register to load). - uart_set(IER, 0x00); - - // Ready to go! Interrupt and DMA modes were not configured since the - // Megadrive console lacks interrupt/DMA control pins on cart connector - // (shame on Masami Ishikawa for not including a single interrupt line!). -} - +#include "config.h" + +#if (MODULE_MEGAWIFI == 1 && MODULE_EVERDRIVE == 0) + +#include "ext/mw/16c550.h" + +/// Shadow copy of the UART registers +UartShadow sh; + +void uart_init(void) { + // Set line to BR,8N1. LCR[7] must be set to access DLX registers + UART_LCR = 0x83; + UART_DLM = UART_DLM_VAL; + UART_DLL = UART_DLL_VAL; + uart_set(LCR, 0x03); + + // Enable auto RTS/CTS. + uart_set(MCR, 0x22); + + // Enable FIFOs, set trigger level to 14 bytes. + // NOTE: Even though trigger level is 14 bytes, RTS is de-asserted when + // receiving the first bit of the 16th byte entering the FIFO. See Fig. 9 + // of the SC16C550B datasheet. + UART_FCR = 0xC1; + // Reset FIFOs + uart_set(FCR, 0xC7); + + // Set IER default value (for the shadow register to load). + uart_set(IER, 0x00); + + // Ready to go! Interrupt and DMA modes were not configured since the + // Megadrive console lacks interrupt/DMA control pins on cart connector + // (shame on Masami Ishikawa for not including a single interrupt line!). +} + #endif // MODULE_MEGAWIFI \ No newline at end of file diff --git a/src/ext/mw/lsd.c b/src/ext/mw/lsd.c index 356e965c..ea94a0c1 100644 --- a/src/ext/mw/lsd.c +++ b/src/ext/mw/lsd.c @@ -4,7 +4,8 @@ * data link. * * \author Jesus Alonso (doragasu) - * \date 2016 + * \author Juan Antonio (PaCHoN) + * \date 2016~2025 * \todo Implement UART RTS/CTS handshaking. * \todo Current implementation uses polling. Unfortunately as the Genesis/ * Megadrive does not have an interrupt pin on the cart, implementing @@ -17,7 +18,7 @@ #include "memory.h" -#if (MODULE_MEGAWIFI != 0) +#if (MODULE_MEGAWIFI == 1) #include "ext/mw/lsd.h" /// Uart used for LSD @@ -363,11 +364,13 @@ enum lsd_status lsd_recv_sync(char *buf, uint16_t *len, uint8_t *ch) void lsd_line_sync(void) { +#if (MODULE_EVERDRIVE == 0) for (int i = 0; i < 256; i++) { if (uart_tx_ready()) { uart_putc(0x55); } } +#endif } #endif // MODULE_MEGAWIFI \ No newline at end of file diff --git a/src/ext/mw/megawifi.c b/src/ext/mw/megawifi.c index 61d76f4b..a71281c2 100644 --- a/src/ext/mw/megawifi.c +++ b/src/ext/mw/megawifi.c @@ -2,7 +2,8 @@ * \brief MeGaWiFi API implementation. * * \author Jesus Alonso (doragasu) - * \date 2015 + * \author Juan Antonio (PaCHoN) + * \date 2025 * * \note Module is not reentrant. * @@ -17,7 +18,7 @@ #include "memory.h" #include "task.h" -#if (MODULE_MEGAWIFI != 0) +#if (MODULE_MEGAWIFI == 1) #include "ext/mw/megawifi.h" @@ -138,10 +139,8 @@ int16_t mw_init(uint16_t *cmd_buf, uint16_t buf_len) uart_clr_bits(MCR, MW__PRG | MW__PD); // Try accessing UART scratch pad register to see if it is installed - UART_SPR = 0x55; - if (UART_SPR != 0x55) return MW_ERR; - UART_SPR = 0xAA; - if (UART_SPR != 0xAA) return MW_ERR; + uart_test(UART_SPR, 0x55); + uart_test(UART_SPR, 0xAA); // Enable control channel lsd_ch_enable(MW_CTRL_CH); @@ -1476,4 +1475,52 @@ enum mw_err mw_fw_upgrade(const char *name) return MW_ERR_NONE; } +enum mw_err mw_fw_list_upgrades(uint8_t page, uint8_t size, uint8_t offset, char **listUpgrades, uint8_t *len, uint8_t *total) +{ + enum mw_err err; + + if (!d.mw_ready) { + return MW_ERR_NOT_READY; + } + + d.cmd->cmd = MW_CMD_UPGRADE_LIST; + d.cmd->data[0] = page; + d.cmd->data[1] = size; + d.cmd->data[2] = offset; + d.cmd->data_len = 3; + err = mw_command(MW_UPGRADE_TOUT); + if (err) { + return MW_ERR; + } + + *listUpgrades = d.cmd->ug_list_response.payload; + *len = d.cmd->ug_list_response.len; + *total = d.cmd->ug_list_response.total; + return MW_ERR_NONE; +} + +struct mw_ping_response *mw_ping(const char* domain, u8 retries) +{ + enum mw_err err; + + if (!d.mw_ready) { + return NULL; + } + + if (!domain) { + return NULL; + } + + d.cmd->cmd = MW_CMD_PING; + d.cmd->data_len = sizeof(struct mw_ping_request); + strcpy(d.cmd->ping.domain, domain); + d.cmd->ping.retries = retries; + err = mw_command(MW_CONNECT_TOUT * retries); + if (err) { + return NULL; + } + + return &d.cmd->ping_response; +} + #endif // MODULE_MEGAWIFI \ No newline at end of file diff --git a/src/ext/mw/ssf.c b/src/ext/mw/ssf.c new file mode 100644 index 00000000..0494e7ef --- /dev/null +++ b/src/ext/mw/ssf.c @@ -0,0 +1,33 @@ +/************************************************************************ + * \brief Simple SSF driver. + * + * \author Juan Antonio Ruiz (PaCHoN) + * \date 2024 + * \defgroup SSF SSF + * \brief + * SSF Implementation. +*************************************** */ +#include "config.h" + +#if (MODULE_MEGAWIFI == 1 && MODULE_EVERDRIVE == 1) + +#include "ext/mw/ssf.h" + +volatile u16 ctrl; +volatile u16 cfg_io; + +void uart_init(void) { + ctrl = SSF_CTRL_P; + cfg_io = CFG_SPI_SS; + + UART_REG_SSF_CTRL = ctrl; + UART_REG_CFG = cfg_io; + + for (u8 i = 1; i < 8; i++)ssf_set_rom_bank(i, i); +} + +void ssf_set_rom_bank(u8 bank, u8 val) { + *((volatile u16 *)(UART_BASE + UART_REG_SSF_CTRL + bank * 2u)) = val; +} + +#endif // MODULE_MEGAWIFI SSF IMPL \ No newline at end of file