feat(sdio_slave): add new driver for sdio_slave

This commit is contained in:
michael 2017-11-29 13:33:07 +08:00 committed by Michael (XIAO Xufeng)
parent c73575de4f
commit f613859e29
2 changed files with 1565 additions and 0 deletions

View file

@ -0,0 +1,290 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _DRIVER_SDIO_SLAVE_H_
#define _DRIVER_SDIO_SLAVE_H_
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "esp_err.h"
#include "rom/queue.h"
#include "soc/host_reg.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SDIO_SLAVE_RECV_MAX_BUFFER (4096-4)
typedef void(*sdio_event_cb_t)(uint8_t event);
/// Mask of interrupts sending to the host.
typedef enum {
SDIO_SLAVE_HOSTINT_SEND_NEW_PACKET = HOST_SLC0_RX_NEW_PACKET_INT_ENA, ///< New packet available
SDIO_SLAVE_HOSTINT_RECV_OVF = HOST_SLC0_TX_OVF_INT_ENA, ///< Slave receive buffer overflow
SDIO_SLAVE_HOSTINT_SEND_UDF = HOST_SLC0_RX_UDF_INT_ENA, ///< Slave sending buffer underflow (this case only happen when the host do not request for packet according to the packet len).
SDIO_SLAVE_HOSTINT_BIT7 = HOST_SLC0_TOHOST_BIT7_INT_ENA, ///< General purpose interrupt bits that can be used by the user.
SDIO_SLAVE_HOSTINT_BIT6 = HOST_SLC0_TOHOST_BIT6_INT_ENA,
SDIO_SLAVE_HOSTINT_BIT5 = HOST_SLC0_TOHOST_BIT5_INT_ENA,
SDIO_SLAVE_HOSTINT_BIT4 = HOST_SLC0_TOHOST_BIT4_INT_ENA,
SDIO_SLAVE_HOSTINT_BIT3 = HOST_SLC0_TOHOST_BIT3_INT_ENA,
SDIO_SLAVE_HOSTINT_BIT2 = HOST_SLC0_TOHOST_BIT2_INT_ENA,
SDIO_SLAVE_HOSTINT_BIT1 = HOST_SLC0_TOHOST_BIT1_INT_ENA,
SDIO_SLAVE_HOSTINT_BIT0 = HOST_SLC0_TOHOST_BIT0_INT_ENA,
} sdio_slave_hostint_t;
/// Timing of SDIO slave
typedef enum {
SDIO_SLAVE_TIMING_NSEND_PSAMPLE = 0,///< Send at negedge, and sample at posedge. Default value for SD protocol.
SDIO_SLAVE_TIMING_NSEND_NSAMPLE, ///< Send at negedge, and sample at negedge
SDIO_SLAVE_TIMING_PSEND_PSAMPLE, ///< Send at posedge, and sample at posedge
SDIO_SLAVE_TIMING_PSEND_NSAMPLE, ///< Send at posedge, and sample at negedge
} sdio_slave_timing_t;
/// Configuration of SDIO slave mode
typedef enum {
SDIO_SLAVE_SEND_STREAM = 0, ///< Stream mode, all packets to send will be combined as one if possible
SDIO_SLAVE_SEND_PACKET = 1, ///< Packet mode, one packets will be sent one after another (only increase packet_len if last packet sent).
} sdio_slave_sending_mode_t;
/// Configuration of SDIO slave
typedef struct {
sdio_slave_timing_t timing; ///< timing of sdio_slave. see `sdio_slave_timing_t`.
sdio_slave_sending_mode_t sending_mode; ///< mode of sdio_slave. `SDIO_SLAVE_MODE_STREAM` if the data needs to be sent as much as possible; `SDIO_SLAVE_MODE_PACKET` if the data should be sent in packets.
int send_queue_size; ///< max buffers that can be queued before sending.
size_t recv_buffer_size;
///< If buffer_size is too small, it costs more CPU time to handle larger number of buffers.
///< If buffer_size is too large, the space larger than the transaction length is left blank but still counts a buffer, and the buffers are easily run out.
///< Should be set according to length of data really transferred.
///< All data that do not fully fill a buffer is still counted as one buffer. E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer.
///< Buffer size of the slave pre-defined between host and slave before communication. All receive buffer given to the driver should be larger than this.
sdio_event_cb_t event_cb; ///< when the host interrupts slave, this callback will be called with interrupt number (0-7).
} sdio_slave_config_t;
/** Handle of a receive buffer, register a handle by calling ``sdio_slave_recv_register_buf``. Use the handle to load the buffer to the
* driver, or call ``sdio_slave_recv_unregister_buf`` if it is no longer used.
*/
typedef void *sdio_slave_buf_handle_t;
/** Initialize the sdio slave driver
*
* @param config Configuration of the sdio slave driver.
*
* @return
* - ESP_ERR_NOT_FOUND if no free interrupt found.
* - ESP_ERR_INVALID_STATE if already initialized.
* - ESP_ERR_NO_MEM if fail due to memory allocation failed.
* - ESP_OK if success
*/
esp_err_t sdio_slave_initialize(sdio_slave_config_t *config);
/** De-initialize the sdio slave driver to release the resources.
*/
void sdio_slave_deinit();
/** Start hardware for sending and receiving, as well as set the IOREADY1 to 1.
*
* @note The driver will continue sending from previous data and PKT_LEN counting, keep data received as well as start receiving from current TOKEN1 counting.
* See ``sdio_slave_reset``.
*
* @return
* - ESP_ERR_INVALID_STATE if already started.
* - ESP_OK otherwise.
*/
esp_err_t sdio_slave_start();
/** Stop hardware from sending and receiving, also set IOREADY1 to 0.
*
* @note this will not clear the data already in the driver, and also not reset the PKT_LEN and TOKEN1 counting. Call ``sdio_slave_reset`` to do that.
*/
void sdio_slave_stop();
/** Clear the data still in the driver, as well as reset the PKT_LEN and TOKEN1 counting.
*
* @return always return ESP_OK.
*/
esp_err_t sdio_slave_reset();
/*---------------------------------------------------------------------------
* Receive
*--------------------------------------------------------------------------*/
/** Register buffer used for receiving. All buffers should be registered before used, and then can be used (again) in the driver by the handle returned.
*
* @param start The start address of the buffer.
*
* @note The driver will use and only use the amount of space specified in the `recv_buffer_size` member set in the `sdio_slave_config_t`.
* All buffers should be larger than that. The buffer is used by the DMA, so it should be DMA capable and 32-bit aligned.
*
* @return The buffer handle if success, otherwise NULL.
*/
sdio_slave_buf_handle_t sdio_slave_recv_register_buf(uint8_t *start);
/** Unregister buffer from driver, and free the space used by the descriptor pointing to the buffer.
*
* @param handle Handle to the buffer to release.
*
* @return ESP_OK if success, ESP_ERR_INVALID_ARG if the handle is NULL or the buffer is being used.
*/
esp_err_t sdio_slave_recv_unregister_buf(sdio_slave_buf_handle_t handle);
/** Load buffer to the queue waiting to receive data. The driver takes ownership of the buffer until the buffer is returned by
* ``sdio_slave_send_get_finished`` after the transaction is finished.
*
* @param handle Handle to the buffer ready to receive data.
*
* @return
* - ESP_ERR_INVALID_ARG if invalid handle or the buffer is already in the queue. Only after the buffer is returened by
* ``sdio_slave_recv`` can you load it again.
* - ESP_OK if success
*/
esp_err_t sdio_slave_recv_load_buf(sdio_slave_buf_handle_t handle);
/** Get received data if exist. The driver returns the ownership of the buffer to the app.
*
* @param handle_ret Handle to the buffer holding received data. Use this handle in ``sdio_slave_recv_load_buf`` to receive in the same buffer again.
* @param start_o Start address output, set to NULL if not needed.
* @param len_o Actual length of the data in the buffer, set to NULL if not needed.
* @param wait Time to wait before data received.
*
* @note Call ``sdio_slave_load_buf`` with the handle to re-load the buffer onto the link list, and receive with the same buffer again.
* The address and length of the buffer got here is the same as got from `sdio_slave_get_buffer`.
*
* @return
* - ESP_ERR_INVALID_ARG if handle_ret is NULL
* - ESP_ERR_TIMEOUT if timeout before receiving new data
* - ESP_OK if success
*/
esp_err_t sdio_slave_recv(sdio_slave_buf_handle_t* handle_ret, uint8_t **start_o, size_t *len_o, TickType_t wait);
/** Retrieve the buffer corresponding to a handle.
*
* @param handle Handle to get the buffer.
* @param len_o Output of buffer length
*
* @return buffer address if success, otherwise NULL.
*/
uint8_t* sdio_slave_recv_get_buf( sdio_slave_buf_handle_t handle, size_t *len_o);
/*---------------------------------------------------------------------------
* Send
*--------------------------------------------------------------------------*/
/** Put a new sending transfer into the send queue. The driver takes ownership of the buffer until the buffer is returned by
* ``sdio_slave_send_get_finished`` after the transaction is finished.
*
* @param addr Address for data to be sent. The buffer should be DMA capable and 32-bit aligned.
* @param len Length of the data, should not be longer than 4092 bytes (may support longer in the future).
* @param arg Argument to returned in ``sdio_slave_send_get_finished``. The argument can be used to indicate which transaction is done,
* or as a parameter for a callback. Set to NULL if not needed.
* @param wait Time to wait if the buffer is full.
*
* @return
* - ESP_ERR_INVALID_ARG if the length is not greater than 0.
* - ESP_ERR_TIMEOUT if the queue is still full until timeout.
* - ESP_OK if success.
*/
esp_err_t sdio_slave_send_queue(uint8_t* addr, size_t len, void* arg, TickType_t wait);
/** Return the ownership of a finished transaction.
* @param arg_o Argument of the finished transaction.
* @param wait Time to wait if there's no finished sending transaction.
*
* @return ESP_ERR_TIMEOUT if no transaction finished, or ESP_OK if succeed.
*/
esp_err_t sdio_slave_send_get_finished(void** arg_o, TickType_t wait);
/** Start a new sending transfer, and wait for it (blocked) to be finished.
*
* @param addr Start address of the buffer to send
* @param len Length of buffer to send.
*
* @return
* - ESP_ERR_INVALID_ARG if the length of descriptor is not greater than 0.
* - ESP_ERR_TIMEOUT if the queue is full or host do not start a transfer before timeout.
* - ESP_OK if success.
*/
esp_err_t sdio_slave_transmit(uint8_t* addr, size_t len);
/*---------------------------------------------------------------------------
* Host
*--------------------------------------------------------------------------*/
/** Read the spi slave register shared with host.
*
* @param pos register address, 0-27 or 32-63.
*
* @note register 28 to 31 are reserved for interrupt vector.
*
* @return value of the register.
*/
uint8_t sdio_slave_read_reg(int pos);
/** Write the spi slave register shared with host.
*
* @param pos register address, 0-11, 14-15, 18-19, 24-27 and 32-63, other address are reserved.
* @param reg the value to write.
*
* @note register 29 and 31 are used for interrupt vector.
*
* @return ESP_ERR_INVALID_ARG if address wrong, otherwise ESP_OK.
*/
esp_err_t sdio_slave_write_reg(int pos, uint8_t reg);
/** Get the interrupt enable for host.
*
* @return the interrupt mask.
*/
sdio_slave_hostint_t sdio_slave_get_host_intena();
/** Set the interrupt enable for host.
*
* @param ena Enable mask for host interrupt.
*/
void sdio_slave_set_host_intena(sdio_slave_hostint_t ena);
/** Interrupt the host by general purpose interrupt.
*
* @param pos Interrupt num, 0-7.
*
* @return
* - ESP_ERR_INVALID_ARG if interrupt num error
* - ESP_OK otherwise
*/
esp_err_t sdio_slave_send_host_int( uint8_t pos );
/** Clear general purpose interrupt to host.
*
* @param mask Interrupt bits to clear, by bit mask.
*/
void sdio_slave_clear_host_int(uint8_t mask);
/** Wait for general purpose interrupt from host.
*
* @param pos Interrupt source number to wait for.
* is set.
* @param wait Time to wait before interrupt triggered.
*
* @note this clears the interrupt at the same time.
*
* @return ESP_OK if success, ESP_ERR_TIMEOUT if timeout.
*/
esp_err_t sdio_slave_wait_int(int pos, TickType_t wait);
#ifdef __cplusplus
}
#endif
#endif /*_DRIVER_SDIO_SLAVE_H */

File diff suppressed because it is too large Load diff