I am trying to send raw mmc commands to SD card using Linux MMC subsystem mmc-utils
and ioctl
and running into Connection timed out (errno: 110)
. The SD card is micro-sd card that is used in raspberry pi 4B. The binary will be executed in initramfs
stage on the raspberry pi.
Below is the source files
test_mmc.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#include <linux/mmc/ioctl.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "test_debug.h"
#include "test_protocol.h"
static int mmc_fd = -1;
static int retry_count = 0;
int debug_level = DEBUG_ERROR;
// Function prototypes
static int init_mmc(const char *device);
static int transmit_apdu(uint8_t *block);
static ssize_t test_cmd(const uint8_t *request, size_t request_len,
uint8_t *response, size_t response_len);
static int get_status(STATUS_RESPONSE *pres);
// MMC device initialization
static int init_mmc(const char *device)
{
mmc_fd = open(device, O_RDWR);
if (mmc_fd < 0) {
LOGD(DEBUG_INFO, "%s%s\n", "Failed to open MMC device: ", device);
return -1;
}
return 0;
}
static void close_mmc(void)
{
if (mmc_fd >= 0) {
close(mmc_fd);
mmc_fd = -1;
}
}
// MMC command helper
static int send_cmd13(void) {
struct mmc_ioc_cmd ioc = {0};
unsigned int response = 0;
int loop_counter = 10;
// Setup CMD13
memset(&ioc, 0, sizeof(ioc));
ioc.write_flag = 0;
ioc.opcode = MMC_CMD_SEND_STATUS; // CMD13
ioc.arg = 1 << 16; // RCA in upper 16 bits
ioc.flags = MMC_RSP_R1 | MMC_RSP_SPI_S1;
ioc.blksz = 0;
ioc.blocks = 0;
do {
int ret = ioctl(mmc_fd, MMC_IOC_CMD, &ioc);
if (ret < 0) {
LOGD(DEBUG_ERROR, "CMD13 ioctl failed: %s (errno: %d)\n",
strerror(errno), errno);
return -1;
}
response = ioc.response[0];
LOGD(DEBUG_VERBOSE, "CMD13 response: 0x%08x\n", response);
// Check card state (bits 9:7 in R1 response)
int card_state = (response >> 9) & 0xF;
LOGD(DEBUG_VERBOSE, "Card state: %d\n", card_state);
if (response == 0x900) {
LOGD(DEBUG_VERBOSE, "Card ready\n");
return 0;
}
loop_counter--;
usleep(200000); // 200ms delay
LOGD(DEBUG_VERBOSE, "Retrying CMD13, attempts left: %d\n", loop_counter);
} while (loop_counter > 0);
LOGD(DEBUG_ERROR, "CMD13 timeout after %d attempts\n", 10 - loop_counter);
return -1;
}
static int transmit_apdu(uint8_t *block) {
struct mmc_ioc_cmd ioc = {0};
int ret;
CACHE_ALIGN uint8_t res[512];
// Initial status check
ret = send_cmd13();
if (ret < 0) {
LOGD(DEBUG_INFO, "%s", "Initial status check failed");
return ret;
}
.
.
.
return 0;
}
// High level APDU command handler
static ssize_t test_cmd(const uint8_t *request, size_t request_len,
uint8_t *response, size_t response_len)
{
CACHE_ALIGN uint8_t block[512];
uint16_t length;
int retries = 3;
int err;
// Build request block
size_t request_lenplus = request_len + 2;
length = htons((uint16_t)request_lenplus);
memset(block, 0x00, sizeof(block));
memcpy(block, &length, sizeof(length)); // 2 byte length of command
memcpy(block+sizeof(length), request, request_len); // APDU
do {
err = transmit_apdu(block);
} while (err && retries--);
if (err != 0)
return err;
.
.
.
return length;
}
static int get_status(STATUS_RESPONSE *pres)
{
static const uint8_t request[] = { 0xff, 0x70, 0x00, 0x00, 0x00 };
LOGD(DEBUG_INFO, "%s", "Getting card status");
ssize_t length = test_cmd(request, sizeof(request), (void *)pres,
sizeof(STATUS_RESPONSE));
if (length != sizeof(STATUS_RESPONSE))
return -1;
if (ntohs(pres->status) != 0x9000)
return -1;
return 0;
}
int main(int argc, char *argv[])
{
STATUS_RESPONSE status;
const char *device = "/dev/mmcblk0";
int ret, opt;
printf("\nTest SD card communication\n\n");
// Initialize MMC device
if (init_mmc(device) < 0) {
printf("No card found!\n");
return 1;
}
// Get card status
if (get_status(&status) < 0) {
printf("Failed to get card status\n");
close_mmc();
return 1;
}
close_mmc();
return 1;
}
test_protocol.h
#ifndef TEST_PROTOCOL_H
#define TEST_PROTOCOL_H
#include <stdint.h>
#define MMC_CMD_SEND_STATUS 13
#define MMC_CMD_SWITCH 6
// Response types
#define MMC_RSP_SPI_S1 (1 << 7)
#define MMC_RSP_PRESENT (1 << 0)
#define MMC_RSP_CRC (1 << 2)
#define MMC_RSP_BUSY (1 << 3)
#define MMC_RSP_OPCODE (1 << 4)
#define MMC_RSP_NONE (0)
#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
typedef struct _STATUS_RESPONSE {
uint8_t mode;
uint8_t state;
int8_t counter;
int8_t so_counter;
uint32_t reset_counter;
uint8_t rfu[2];
uint32_t cdrom;
uint8_t flags;
uint16_t status;
} __attribute__((packed)) STATUS_RESPONSE;
#endif /* TEST_PROTOCOL_H */
cross-compiled in Ubuntu VM and added via init script to run in initramfs.
But I get error
[ 24.888477] sdhci-iproc fe340000.mmc: mmc_blk_ioctl_cmd: cmd error -110
[ 24.896115] sdhci-iproc fe340000.mmc: mmc_blk_ioctl_cmd: cmd error -110
[ 24.903652] sdhci-iproc fe340000.mmc: mmc_blk_ioctl_cmd: cmd error -110
[ 24.911243] sdhci-iproc fe340000.mmc: mmc_blk_ioctl_cmd: cmd error -110
/home/test/.../test_mmc/src/test_mmc.c:525:dp_status(): Getting card status/home/test/.../test_mmc/src/test_mmc.c:238:send_cmd13(): CMD13 ioctl failed: Connection timed out (errno: 110)
/home/test/.../test_mmc/src/test_mmc.c:274:transmit_apdu(): Initial status check failed/home/test/.../test_mmc/src/test_mmc.c:238:send_cmd13(): CMD13 ioctl failed: Connection timed out (errno: 110)
/home/test/.../test_mmc/src/test_mmc.c:274:transmit_a[ 24.975333] mmc0: card 0001 removed
pdu(): I[ 24.975404] mmcblk0: unable to read partition table
/home/test/.../test_mmc/src/test_mmc.c:238:send_cmd13(): CMD13 ioctl failed: Connection timed out (errno: 110)
/home/test/.../test_mmc/src/test_mmc.c:274:transmit_apdu(): Initial status check failed/home/test/.../test_mmc/src/test_mmc.c:238:send_cmd13(): CMD13 ioctl failed: Connection timed out (errno: 110)
/home/test/.../test_mmc/src/test_mmc.c:274:transmit_apdu(): Initial status check failedFailed to get card status
(initramfs) [ 25.212940] mmc0: new ultra high speed DDR50 SDHC card at address 0001
[ 25.220392] mmcblk0: mmc0:0001 N1DP5 15.2 GiB
[ 25.230203] mmcblk0: p1 p2
[ 25.233535] mmcblk0: mmc0:0001 N1DP5 15.2 GiB
[ 25.240661] vc4-drm gpu: bound fe400000.hvs (ops vc4_hvs_ops [vc4])
what is the issue here? How can I achieve low level access to SD card?
In the initramfs shell, I tried mmc extcsd read /dev/mmcblk0
it returned
[ 294.419709] sdhci-iproc fe340000.mmc: __mmc_blk_ioctl_cmd: cmd error -110
ioctl: Connection timed out
Cou[ 294.429294] mmcblk0: p1 p2
ld not read EXT_CSD from /dev/mmcblk0
(This mybe due to SD-cards not having the EXT_CSD registers,... I am not sure)
Thanks in advance.
P.S: please let me know if any info is missing