| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> | 
					
						
							|  |  |  |  * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission to use, copy, modify, and distribute this software for any | 
					
						
							|  |  |  |  * purpose with or without fee is hereby granted, provided that the above | 
					
						
							|  |  |  |  * copyright notice and this permission notice appear in all copies. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
					
						
							|  |  |  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
					
						
							|  |  |  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
					
						
							|  |  |  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
					
						
							|  |  |  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
					
						
							|  |  |  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 
					
						
							|  |  |  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "sdmmc_common.h"
 | 
					
						
							| 
									
										
										
										
											2019-07-12 13:20:04 +08:00
										 |  |  | #include "esp_attr.h"
 | 
					
						
							| 
									
										
										
										
											2019-10-15 18:01:05 -03:00
										 |  |  | #include "esp_compiler.h"
 | 
					
						
							| 
									
										
										
										
											2019-07-12 13:20:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CIS_TUPLE(NAME)  (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&cis_tuple_func_default, }
 | 
					
						
							|  |  |  | #define CIS_TUPLE_WITH_FUNC(NAME, FUNC)  (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&(FUNC), }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CIS_CHECK_SIZE(SIZE, MINIMAL) do {int store_size = (SIZE); if((store_size) < (MINIMAL)) return ESP_ERR_INVALID_SIZE;} while(0)
 | 
					
						
							|  |  |  | #define CIS_CHECK_UNSUPPORTED(COND) do {if(!(COND)) return ESP_ERR_NOT_SUPPORTED;} while(0)
 | 
					
						
							|  |  |  | #define CIS_GET_MINIMAL_SIZE    32
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef esp_err_t (*cis_tuple_info_func_t)(const void* tuple_info, uint8_t* data, FILE* fp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |     int code; | 
					
						
							|  |  |  |     const char *name; | 
					
						
							|  |  |  |     cis_tuple_info_func_t   func; | 
					
						
							|  |  |  | } cis_tuple_t; | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | static const char* TAG = "sdmmc_io"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-12 13:20:04 +08:00
										 |  |  | static esp_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp); | 
					
						
							|  |  |  | static esp_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp); | 
					
						
							|  |  |  | static esp_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp); | 
					
						
							|  |  |  | static esp_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const cis_tuple_t cis_table[] = { | 
					
						
							|  |  |  |     CIS_TUPLE(NULL), | 
					
						
							|  |  |  |     CIS_TUPLE(DEVICE), | 
					
						
							|  |  |  |     CIS_TUPLE(CHKSUM), | 
					
						
							|  |  |  |     CIS_TUPLE(VERS1), | 
					
						
							|  |  |  |     CIS_TUPLE(ALTSTR), | 
					
						
							|  |  |  |     CIS_TUPLE(CONFIG), | 
					
						
							|  |  |  |     CIS_TUPLE_WITH_FUNC(CFTABLE_ENTRY, cis_tuple_func_cftable_entry), | 
					
						
							|  |  |  |     CIS_TUPLE_WITH_FUNC(MANFID, cis_tuple_func_manfid), | 
					
						
							|  |  |  |     CIS_TUPLE(FUNCID), | 
					
						
							|  |  |  |     CIS_TUPLE(FUNCE), | 
					
						
							|  |  |  |     CIS_TUPLE(VENDER_BEGIN), | 
					
						
							|  |  |  |     CIS_TUPLE(VENDER_END), | 
					
						
							|  |  |  |     CIS_TUPLE(SDIO_STD), | 
					
						
							|  |  |  |     CIS_TUPLE(SDIO_EXT), | 
					
						
							|  |  |  |     CIS_TUPLE_WITH_FUNC(END, cis_tuple_func_end), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  | esp_err_t sdmmc_io_reset(sdmmc_card_t* card) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint8_t sdio_reset = CCCR_CTL_RES; | 
					
						
							|  |  |  |     esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CTL, SD_ARG_CMD52_WRITE, &sdio_reset); | 
					
						
							|  |  |  |     if (err == ESP_ERR_TIMEOUT || (host_is_spi(card) && err == ESP_ERR_NOT_SUPPORTED)) { | 
					
						
							|  |  |  |         /* Non-IO cards are allowed to time out (in SD mode) or
 | 
					
						
							|  |  |  |          * return "invalid command" error (in SPI mode). | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |     } else if (err == ESP_ERR_NOT_FOUND) { | 
					
						
							|  |  |  |         ESP_LOGD(TAG, "%s: card not present", __func__); | 
					
						
							|  |  |  |         return err; | 
					
						
							|  |  |  |     } else if (err != ESP_OK) { | 
					
						
							|  |  |  |         ESP_LOGE(TAG, "%s: unexpected return: 0x%x", __func__, err ); | 
					
						
							|  |  |  |         return err; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_init_io(sdmmc_card_t* card) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
 | 
					
						
							|  |  |  |      * Non-IO cards will not respond to this command. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     esp_err_t err = sdmmc_io_send_op_cond(card, 0, &card->ocr); | 
					
						
							|  |  |  |     if (err != ESP_OK) { | 
					
						
							|  |  |  |         ESP_LOGD(TAG, "%s: io_send_op_cond (1) returned 0x%x; not IO card", __func__, err); | 
					
						
							|  |  |  |         card->is_sdio = 0; | 
					
						
							|  |  |  |         card->is_mem = 1; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         card->is_sdio = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (card->ocr & SD_IO_OCR_MEM_PRESENT) { | 
					
						
							|  |  |  |             ESP_LOGD(TAG, "%s: IO-only card", __func__); | 
					
						
							|  |  |  |             card->is_mem = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         card->num_io_functions = SD_IO_OCR_NUM_FUNCTIONS(card->ocr); | 
					
						
							|  |  |  |         ESP_LOGD(TAG, "%s: number of IO functions: %d", __func__, card->num_io_functions); | 
					
						
							|  |  |  |         if (card->num_io_functions == 0) { | 
					
						
							|  |  |  |             card->is_sdio = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         uint32_t host_ocr = get_host_ocr(card->host.io_voltage); | 
					
						
							|  |  |  |         host_ocr &= card->ocr; | 
					
						
							|  |  |  |         err = sdmmc_io_send_op_cond(card, host_ocr, &card->ocr); | 
					
						
							|  |  |  |         if (err != ESP_OK) { | 
					
						
							|  |  |  |             ESP_LOGE(TAG, "%s: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err); | 
					
						
							|  |  |  |             return err; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         err = sdmmc_io_enable_int(card); | 
					
						
							|  |  |  |         if (err != ESP_OK) { | 
					
						
							|  |  |  |             ESP_LOGD(TAG, "%s: sdmmc_enable_int failed (0x%x)", __func__, err); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     esp_err_t err; | 
					
						
							|  |  |  |     card->log_bus_width = 0; | 
					
						
							|  |  |  |     if (card->host.flags & SDMMC_HOST_FLAG_4BIT) { | 
					
						
							|  |  |  |         uint8_t card_cap = 0; | 
					
						
							|  |  |  |         err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CARD_CAP, | 
					
						
							|  |  |  |                 SD_ARG_CMD52_READ, &card_cap); | 
					
						
							|  |  |  |         if (err != ESP_OK) { | 
					
						
							|  |  |  |             ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read SD_IO_CCCR_CARD_CAP) returned 0x%0x", __func__, err); | 
					
						
							|  |  |  |             return err; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         ESP_LOGD(TAG, "IO card capabilities byte: %02x", card_cap); | 
					
						
							|  |  |  |         if (!(card_cap & CCCR_CARD_CAP_LSC) || | 
					
						
							|  |  |  |                 (card_cap & CCCR_CARD_CAP_4BLS)) { | 
					
						
							|  |  |  |             // This card supports 4-bit bus mode
 | 
					
						
							|  |  |  |             uint8_t bus_width = CCCR_BUS_WIDTH_4; | 
					
						
							|  |  |  |             err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_BUS_WIDTH, | 
					
						
							|  |  |  |                                 SD_ARG_CMD52_WRITE, &bus_width); | 
					
						
							|  |  |  |             if (err != ESP_OK) { | 
					
						
							|  |  |  |                 ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write SD_IO_CCCR_BUS_WIDTH) returned 0x%0x", __func__, err); | 
					
						
							|  |  |  |                 return err; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             card->log_bus_width = 2; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     /* If the host is configured to use low frequency, don't attempt to switch */ | 
					
						
							|  |  |  |     if (card->host.max_freq_khz < SDMMC_FREQ_DEFAULT) { | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  |         card->max_freq_khz = card->host.max_freq_khz; | 
					
						
							|  |  |  |         return ESP_OK; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     } else if (card->host.max_freq_khz < SDMMC_FREQ_HIGHSPEED) { | 
					
						
							|  |  |  |         card->max_freq_khz = SDMMC_FREQ_DEFAULT; | 
					
						
							|  |  |  |         return ESP_OK; | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* For IO cards, do write + read operation on "High Speed" register,
 | 
					
						
							|  |  |  |      * setting EHS bit. If both EHS and SHS read back as set, then HS mode | 
					
						
							|  |  |  |      * has been enabled. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     uint8_t val = CCCR_HIGHSPEED_ENABLE; | 
					
						
							|  |  |  |     esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_HIGHSPEED, | 
					
						
							|  |  |  |             SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &val); | 
					
						
							|  |  |  |     if (err != ESP_OK) { | 
					
						
							|  |  |  |         ESP_LOGD(TAG, "%s: sdmmc_io_rw_direct returned 0x%x", __func__, err); | 
					
						
							|  |  |  |         return err; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ESP_LOGD(TAG, "%s: CCCR_HIGHSPEED=0x%02x", __func__, val); | 
					
						
							|  |  |  |     const uint8_t hs_mask = CCCR_HIGHSPEED_ENABLE | CCCR_HIGHSPEED_SUPPORT; | 
					
						
							|  |  |  |     if ((val & hs_mask) != hs_mask) { | 
					
						
							|  |  |  |         return ESP_ERR_NOT_SUPPORTED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     card->max_freq_khz = SDMMC_FREQ_HIGHSPEED; | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     esp_err_t err = ESP_OK; | 
					
						
							|  |  |  |     sdmmc_command_t cmd = { | 
					
						
							|  |  |  |         .flags = SCF_CMD_BCR | SCF_RSP_R4, | 
					
						
							|  |  |  |         .arg = ocr, | 
					
						
							|  |  |  |         .opcode = SD_IO_SEND_OP_COND | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     for (size_t i = 0; i < 100; i++) { | 
					
						
							|  |  |  |         err = sdmmc_send_cmd(card, &cmd); | 
					
						
							|  |  |  |         if (err != ESP_OK) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ((MMC_R4(cmd.response) & SD_IO_OCR_MEM_READY) || | 
					
						
							|  |  |  |             ocr == 0) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         err = ESP_ERR_TIMEOUT; | 
					
						
							|  |  |  |         vTaskDelay(SDMMC_IO_SEND_OP_COND_DELAY_MS / portTICK_PERIOD_MS); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (err == ESP_OK && ocrp != NULL) | 
					
						
							|  |  |  |         *ocrp = MMC_R4(cmd.response); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int func, | 
					
						
							|  |  |  |         uint32_t reg, uint32_t arg, uint8_t *byte) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     esp_err_t err; | 
					
						
							|  |  |  |     sdmmc_command_t cmd = { | 
					
						
							|  |  |  |         .flags = SCF_CMD_AC | SCF_RSP_R5, | 
					
						
							|  |  |  |         .arg = 0, | 
					
						
							|  |  |  |         .opcode = SD_IO_RW_DIRECT | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     arg |= (func & SD_ARG_CMD52_FUNC_MASK) << SD_ARG_CMD52_FUNC_SHIFT; | 
					
						
							|  |  |  |     arg |= (reg & SD_ARG_CMD52_REG_MASK) << SD_ARG_CMD52_REG_SHIFT; | 
					
						
							|  |  |  |     arg |= (*byte & SD_ARG_CMD52_DATA_MASK) << SD_ARG_CMD52_DATA_SHIFT; | 
					
						
							|  |  |  |     cmd.arg = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     err = sdmmc_send_cmd(card, &cmd); | 
					
						
							|  |  |  |     if (err != ESP_OK) { | 
					
						
							|  |  |  |         ESP_LOGV(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err); | 
					
						
							|  |  |  |         return err; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *byte = SD_R5_DATA(cmd.response); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function, | 
					
						
							|  |  |  |         uint32_t addr, uint8_t *out_byte) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     esp_err_t ret = sdmmc_io_rw_direct(card, function, addr, SD_ARG_CMD52_READ, out_byte); | 
					
						
							| 
									
										
										
										
											2019-10-15 18:01:05 -03:00
										 |  |  |     if (unlikely(ret != ESP_OK)) { | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  |         ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read 0x%x) returned 0x%x", __func__, addr, ret); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function, | 
					
						
							|  |  |  |         uint32_t addr, uint8_t in_byte, uint8_t* out_byte) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint8_t tmp_byte = in_byte; | 
					
						
							|  |  |  |     esp_err_t ret = sdmmc_io_rw_direct(card, function, addr, | 
					
						
							|  |  |  |             SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &tmp_byte); | 
					
						
							| 
									
										
										
										
											2019-10-15 18:01:05 -03:00
										 |  |  |     if (unlikely(ret != ESP_OK)) { | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  |         ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write 0x%x) returned 0x%x", __func__, addr, ret); | 
					
						
							|  |  |  |         return ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (out_byte != NULL) { | 
					
						
							|  |  |  |         *out_byte = tmp_byte; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func, | 
					
						
							|  |  |  |     uint32_t reg, int arg, void *datap, size_t datalen) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     esp_err_t err; | 
					
						
							|  |  |  |     const size_t max_byte_transfer_size = 512; | 
					
						
							|  |  |  |     sdmmc_command_t cmd = { | 
					
						
							|  |  |  |         .flags = SCF_CMD_AC | SCF_RSP_R5, | 
					
						
							|  |  |  |         .arg = 0, | 
					
						
							|  |  |  |         .opcode = SD_IO_RW_EXTENDED, | 
					
						
							|  |  |  |         .data = datap, | 
					
						
							|  |  |  |         .datalen = datalen, | 
					
						
							|  |  |  |         .blklen = max_byte_transfer_size /* TODO: read max block size from CIS */ | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uint32_t count; /* number of bytes or blocks, depending on transfer mode */ | 
					
						
							|  |  |  |     if (arg & SD_ARG_CMD53_BLOCK_MODE) { | 
					
						
							|  |  |  |         if (cmd.datalen % cmd.blklen != 0) { | 
					
						
							|  |  |  |             return ESP_ERR_INVALID_SIZE; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         count = cmd.datalen / cmd.blklen; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         if (datalen > max_byte_transfer_size) { | 
					
						
							|  |  |  |             /* TODO: split into multiple operations? */ | 
					
						
							|  |  |  |             return ESP_ERR_INVALID_SIZE; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (datalen == max_byte_transfer_size) { | 
					
						
							|  |  |  |             count = 0;  // See 5.3.1 SDIO simplifed spec
 | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             count = datalen; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         cmd.blklen = datalen; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     arg |= (func & SD_ARG_CMD53_FUNC_MASK) << SD_ARG_CMD53_FUNC_SHIFT; | 
					
						
							|  |  |  |     arg |= (reg & SD_ARG_CMD53_REG_MASK) << SD_ARG_CMD53_REG_SHIFT; | 
					
						
							|  |  |  |     arg |= (count & SD_ARG_CMD53_LENGTH_MASK) << SD_ARG_CMD53_LENGTH_SHIFT; | 
					
						
							|  |  |  |     cmd.arg = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((arg & SD_ARG_CMD53_WRITE) == 0) { | 
					
						
							|  |  |  |         cmd.flags |= SCF_CMD_READ; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     err = sdmmc_send_cmd(card, &cmd); | 
					
						
							|  |  |  |     if (err != ESP_OK) { | 
					
						
							|  |  |  |         ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err); | 
					
						
							|  |  |  |         return err; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function, | 
					
						
							|  |  |  |         uint32_t addr, void* dst, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* host quirk: SDIO transfer with length not divisible by 4 bytes
 | 
					
						
							|  |  |  |      * has to be split into two transfers: one with aligned length, | 
					
						
							|  |  |  |      * the other one for the remaining 1-3 bytes. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     uint8_t *pc_dst = dst; | 
					
						
							|  |  |  |     while (size > 0) { | 
					
						
							|  |  |  |         size_t size_aligned = size & (~3); | 
					
						
							|  |  |  |         size_t will_transfer = size_aligned > 0 ? size_aligned : size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         esp_err_t err = sdmmc_io_rw_extended(card, function, addr, | 
					
						
							|  |  |  |                 SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT, | 
					
						
							|  |  |  |                 pc_dst, will_transfer); | 
					
						
							| 
									
										
										
										
											2019-10-15 18:01:05 -03:00
										 |  |  |         if (unlikely(err != ESP_OK)) { | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  |             return err; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         pc_dst += will_transfer; | 
					
						
							|  |  |  |         size -= will_transfer; | 
					
						
							|  |  |  |         addr += will_transfer; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function, | 
					
						
							|  |  |  |         uint32_t addr, const void* src, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* same host quirk as in sdmmc_io_read_bytes */ | 
					
						
							|  |  |  |     const uint8_t *pc_src = (const uint8_t*) src; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (size > 0) { | 
					
						
							|  |  |  |         size_t size_aligned = size & (~3); | 
					
						
							|  |  |  |         size_t will_transfer = size_aligned > 0 ? size_aligned : size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         esp_err_t err = sdmmc_io_rw_extended(card, function, addr, | 
					
						
							|  |  |  |                 SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT, | 
					
						
							|  |  |  |                 (void*) pc_src, will_transfer); | 
					
						
							| 
									
										
										
										
											2019-10-15 18:01:05 -03:00
										 |  |  |         if (unlikely(err != ESP_OK)) { | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  |             return err; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         pc_src += will_transfer; | 
					
						
							|  |  |  |         size -= will_transfer; | 
					
						
							|  |  |  |         addr += will_transfer; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function, | 
					
						
							|  |  |  |         uint32_t addr, void* dst, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-15 18:01:05 -03:00
										 |  |  |     if (unlikely(size % 4 != 0)) { | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  |         return ESP_ERR_INVALID_SIZE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return sdmmc_io_rw_extended(card, function, addr, | 
					
						
							|  |  |  |             SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE, | 
					
						
							|  |  |  |             dst, size); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function, | 
					
						
							|  |  |  |         uint32_t addr, const void* src, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-15 18:01:05 -03:00
										 |  |  |     if (unlikely(size % 4 != 0)) { | 
					
						
							| 
									
										
										
										
											2018-06-20 19:59:11 +08:00
										 |  |  |         return ESP_ERR_INVALID_SIZE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return sdmmc_io_rw_extended(card, function, addr, | 
					
						
							|  |  |  |             SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE, | 
					
						
							|  |  |  |             (void*) src, size); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_enable_int(sdmmc_card_t* card) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (card->host.io_int_enable == NULL) { | 
					
						
							|  |  |  |         return ESP_ERR_NOT_SUPPORTED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return (*card->host.io_int_enable)(card->host.slot); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (card->host.io_int_wait == NULL) { | 
					
						
							|  |  |  |         return ESP_ERR_NOT_SUPPORTED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return (*card->host.io_int_wait)(card->host.slot, timeout_ticks); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-12 13:20:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Print the CIS information of a CIS card, currently only ESP slave supported. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static esp_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const cis_tuple_t* tuple = (const cis_tuple_t*)p; | 
					
						
							|  |  |  |     uint8_t code = *(data++); | 
					
						
							|  |  |  |     int size = *(data++); | 
					
						
							|  |  |  |     if (tuple) { | 
					
						
							|  |  |  |         fprintf(fp, "TUPLE: %s, size: %d: ", tuple->name, size); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         fprintf(fp, "TUPLE: unknown(%02X), size: %d: ", code, size); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     for (int i = 0; i < size; i++) fprintf(fp, "%02X ", *(data++)); | 
					
						
							|  |  |  |     fprintf(fp, "\n"); | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static esp_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const cis_tuple_t* tuple = (const cis_tuple_t*)p; | 
					
						
							|  |  |  |     data++; | 
					
						
							|  |  |  |     int size = *(data++); | 
					
						
							|  |  |  |     fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size); | 
					
						
							|  |  |  |     CIS_CHECK_SIZE(size, 4); | 
					
						
							|  |  |  |     fprintf(fp, "  MANF: %04X, CARD: %04X\n", *(uint16_t*)(data), *(uint16_t*)(data+2)); | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static esp_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const cis_tuple_t* tuple = (const cis_tuple_t*)p; | 
					
						
							|  |  |  |     data++; | 
					
						
							|  |  |  |     fprintf(fp, "TUPLE: %s\n", tuple->name); | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static esp_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const cis_tuple_t* tuple = (const cis_tuple_t*)p; | 
					
						
							|  |  |  |     data++; | 
					
						
							|  |  |  |     int size = *(data++); | 
					
						
							|  |  |  |     fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size); | 
					
						
							|  |  |  |     CIS_CHECK_SIZE(size, 2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     CIS_CHECK_SIZE(size--, 1); | 
					
						
							|  |  |  |     bool interface = data[0] & BIT(7); | 
					
						
							|  |  |  |     bool def = data[0] & BIT(6); | 
					
						
							|  |  |  |     int conf_ent_num = data[0] & 0x3F; | 
					
						
							|  |  |  |     fprintf(fp, "  INDX: %02X, Intface: %d, Default: %d, Conf-Entry-Num: %d\n", *(data++), interface, def, conf_ent_num); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (interface) { | 
					
						
							|  |  |  |         CIS_CHECK_SIZE(size--, 1); | 
					
						
							|  |  |  |         fprintf(fp, "  IF: %02X\n", *(data++)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     CIS_CHECK_SIZE(size--, 1); | 
					
						
							|  |  |  |     bool misc = data[0] & BIT(7); | 
					
						
							|  |  |  |     int mem_space = (data[0] >> 5 )&(0x3); | 
					
						
							|  |  |  |     bool irq = data[0] & BIT(4); | 
					
						
							|  |  |  |     bool io_sp = data[0] & BIT(3); | 
					
						
							|  |  |  |     bool timing = data[0] & BIT(2); | 
					
						
							|  |  |  |     int power = data[0] & 3; | 
					
						
							|  |  |  |     fprintf(fp, "  FS: %02X, misc: %d, mem_space: %d, irq: %d, io_space: %d, timing: %d, power: %d\n", *(data++), misc, mem_space, irq, io_sp, timing, power); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     CIS_CHECK_UNSUPPORTED(power == 0);  //power descriptor is not handled yet
 | 
					
						
							|  |  |  |     CIS_CHECK_UNSUPPORTED(!timing);     //timing descriptor is not handled yet
 | 
					
						
							|  |  |  |     CIS_CHECK_UNSUPPORTED(!io_sp);      //io space descriptor is not handled yet
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (irq) { | 
					
						
							|  |  |  |         CIS_CHECK_SIZE(size--, 1); | 
					
						
							|  |  |  |         bool mask = data[0] & BIT(4); | 
					
						
							|  |  |  |         fprintf(fp, "  IR: %02X, mask: %d, ",*(data++), mask); | 
					
						
							|  |  |  |         if (mask) { | 
					
						
							|  |  |  |             CIS_CHECK_SIZE(size, 2); | 
					
						
							|  |  |  |             size-=2; | 
					
						
							|  |  |  |             fprintf(fp, "  IRQ: %02X %02X\n", data[0], data[1]); | 
					
						
							|  |  |  |             data+=2; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mem_space) { | 
					
						
							|  |  |  |         CIS_CHECK_SIZE(size, 2); | 
					
						
							|  |  |  |         size-=2; | 
					
						
							|  |  |  |         CIS_CHECK_UNSUPPORTED(mem_space==1); //other cases not handled yet
 | 
					
						
							|  |  |  |         int len = *(uint16_t*)data; | 
					
						
							|  |  |  |         fprintf(fp, "  LEN: %04X\n", len); | 
					
						
							|  |  |  |         data+=2; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     CIS_CHECK_UNSUPPORTED(misc==0);    //misc descriptor is not handled yet
 | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const cis_tuple_t* get_tuple(uint8_t code) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     for (int i = 0; i < sizeof(cis_table)/sizeof(cis_tuple_t); i++) { | 
					
						
							|  |  |  |         if (code == cis_table[i].code) return &cis_table[i]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ESP_LOG_BUFFER_HEXDUMP("CIS", buffer, buffer_size, ESP_LOG_DEBUG); | 
					
						
							|  |  |  |     if (!fp) fp = stdout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uint8_t* cis = buffer; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |         const cis_tuple_t* tuple = get_tuple(cis[0]); | 
					
						
							|  |  |  |         int size = cis[1]; | 
					
						
							|  |  |  |         esp_err_t ret = ESP_OK; | 
					
						
							|  |  |  |         if (tuple) { | 
					
						
							|  |  |  |             ret = tuple->func(tuple, cis, fp); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             ret = cis_tuple_func_default(NULL, cis, fp); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (ret != ESP_OK) return ret; | 
					
						
							|  |  |  |         cis += 2 + size; | 
					
						
							|  |  |  |         if (tuple && tuple->code == CISTPL_CODE_END) break; | 
					
						
							|  |  |  |     } while (cis < buffer + buffer_size) ; | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Check tuples in the buffer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param buf Buffer to check | 
					
						
							|  |  |  |  * @param buffer_size Size of the buffer | 
					
						
							|  |  |  |  * @param inout_cis_offset | 
					
						
							|  |  |  |  *          - input: the last cis_offset, relative to the beginning of the buf. -1 if | 
					
						
							|  |  |  |  *                      this buffer begin with the tuple length, otherwise should be no smaller than | 
					
						
							|  |  |  |  *                      zero. | 
					
						
							|  |  |  |  *          - output: when the end tuple found, output offset of the CISTPL_CODE_END | 
					
						
							|  |  |  |  *                      byte + 1 (relative to the beginning of the buffer; when not found, output | 
					
						
							|  |  |  |  *                      the address of next tuple code. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @return true if found, false if haven't. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static bool check_tuples_in_buffer(uint8_t* buf, int buffer_size, int* inout_cis_offset) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int cis_offset = *inout_cis_offset; | 
					
						
							|  |  |  |     if (cis_offset == -1) { | 
					
						
							|  |  |  |         //the CIS code is checked in the last buffer, skip to next tuple
 | 
					
						
							|  |  |  |         cis_offset += buf[0] + 2; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(cis_offset >= 0); | 
					
						
							|  |  |  |     while (1) { | 
					
						
							|  |  |  |         if (cis_offset < buffer_size) { | 
					
						
							|  |  |  |             //A CIS code in the buffer, check it
 | 
					
						
							|  |  |  |             if (buf[cis_offset] == CISTPL_CODE_END) { | 
					
						
							|  |  |  |                 *inout_cis_offset = cis_offset + 1; | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (cis_offset + 1 < buffer_size) { | 
					
						
							|  |  |  |             cis_offset += buf[cis_offset+1] + 2; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *inout_cis_offset = cis_offset; | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     esp_err_t ret = ESP_OK; | 
					
						
							|  |  |  |     WORD_ALIGNED_ATTR uint8_t buf[CIS_GET_MINIMAL_SIZE]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 15:34:34 +02:00
										 |  |  |     /* Pointer to size is a mandatory parameter */ | 
					
						
							|  |  |  |     assert(inout_cis_size); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-12 13:20:04 +08:00
										 |  |  |     /*
 | 
					
						
							|  |  |  |      * CIS region exist in 0x1000~0x17FFF of FUNC 0, get the start address of it | 
					
						
							|  |  |  |      * from CCCR register. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     uint32_t addr; | 
					
						
							|  |  |  |     ret = sdmmc_io_read_bytes(card, 0, 9, &addr, 3); | 
					
						
							|  |  |  |     if (ret != ESP_OK) return ret; | 
					
						
							|  |  |  |     //the sdmmc_io driver reads 4 bytes, the most significant byte is not the address.
 | 
					
						
							|  |  |  |     addr &= 0xffffff; | 
					
						
							|  |  |  |     if (addr < 0x1000 || addr > 0x17FFF) { | 
					
						
							|  |  |  |         return ESP_ERR_INVALID_RESPONSE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |      * To avoid reading too long, take the input value as limitation if | 
					
						
							|  |  |  |      * existing. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     size_t max_reading = UINT32_MAX; | 
					
						
							| 
									
										
										
										
											2019-10-21 15:34:34 +02:00
										 |  |  |     if (*inout_cis_size != 0) { | 
					
						
							| 
									
										
										
										
											2019-07-12 13:20:04 +08:00
										 |  |  |         max_reading = *inout_cis_size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |      * Parse the length while reading. If find the end tuple, or reaches the | 
					
						
							|  |  |  |      * limitation, read no more and return both the data and the size already | 
					
						
							|  |  |  |      * read. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     int buffer_offset = 0; | 
					
						
							|  |  |  |     int cur_cis_offset = 0; | 
					
						
							|  |  |  |     bool end_tuple_found = false; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |         ret = sdmmc_io_read_bytes(card, 0, addr + buffer_offset, &buf, CIS_GET_MINIMAL_SIZE); | 
					
						
							|  |  |  |         if (ret != ESP_OK) return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //calculate relative to the beginning of the buffer
 | 
					
						
							|  |  |  |         int offset = cur_cis_offset - buffer_offset; | 
					
						
							|  |  |  |         bool finish = check_tuples_in_buffer(buf, CIS_GET_MINIMAL_SIZE, &offset); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int remain_size = buffer_size - buffer_offset; | 
					
						
							|  |  |  |         int copy_len; | 
					
						
							|  |  |  |         if (finish) { | 
					
						
							|  |  |  |             copy_len = MIN(offset, remain_size); | 
					
						
							|  |  |  |             end_tuple_found = true; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             copy_len = MIN(CIS_GET_MINIMAL_SIZE, remain_size); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (copy_len > 0) { | 
					
						
							|  |  |  |             memcpy(out_buffer + buffer_offset, buf, copy_len); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         cur_cis_offset = buffer_offset + offset; | 
					
						
							|  |  |  |         buffer_offset += CIS_GET_MINIMAL_SIZE; | 
					
						
							|  |  |  |     } while (!end_tuple_found && buffer_offset < max_reading); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (end_tuple_found) { | 
					
						
							|  |  |  |         *inout_cis_size = cur_cis_offset; | 
					
						
							|  |  |  |         if (cur_cis_offset > buffer_size) { | 
					
						
							|  |  |  |             return ESP_ERR_INVALID_SIZE; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return ESP_OK; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return ESP_ERR_NOT_FOUND; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |