393 lines
12 KiB
C
Executable File
393 lines
12 KiB
C
Executable File
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <dirent.h>
|
||
#include <unistd.h>
|
||
#include <sys/mount.h>
|
||
#include <sys/stat.h>
|
||
#include <fcntl.h>
|
||
#include <errno.h>
|
||
#include <sys/types.h>
|
||
#include <unistd.h>
|
||
#include <sys/mman.h>
|
||
#include <linux/netlink.h>
|
||
#include <linux/rtnetlink.h>
|
||
#include <sys/select.h>
|
||
#include <sys/socket.h>
|
||
|
||
#include "sample_comm.h"
|
||
#include "sample_comm_periph.h"
|
||
#include "xmedia_sys.h"
|
||
|
||
|
||
// 最大设备名长度
|
||
#define MAX_DEVICE_NAME_LENGTH 256
|
||
|
||
#define MAX_NL_BUF_SIZE (1024 * 16)
|
||
#define MAX_SELECT_TIMEOUT (2 * 1000 * 1000)
|
||
|
||
// 查找设备的分区(返回分区号,如 p1)
|
||
static xmedia_s32 sdcard_find_partition(const xmedia_char *device_name, xmedia_char *partition)
|
||
{
|
||
DIR *dir;
|
||
struct dirent *entry;
|
||
xmedia_char path[MAX_DEVICE_NAME_LENGTH + 32] = {0};
|
||
xmedia_s32 found = 0;
|
||
|
||
snprintf(path, sizeof(path), "/sys/block/%s", device_name);
|
||
|
||
dir = opendir(path);
|
||
if (!dir) {
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
|
||
while ((entry = readdir(dir)) != NULL) {
|
||
if (strncmp(entry->d_name, device_name, strlen(device_name)) == 0 &&
|
||
strstr(entry->d_name, "p") != NULL) {
|
||
strncpy(partition, entry->d_name, MAX_DEVICE_NAME_LENGTH);
|
||
found = 1;
|
||
break;
|
||
}
|
||
}
|
||
closedir(dir);
|
||
return found ? XMEDIA_SUCCESS : XMEDIA_FAILURE;
|
||
}
|
||
|
||
// 挂载设备
|
||
static xmedia_s32 mount_device(const xmedia_char *mount_point, const xmedia_char *device_path)
|
||
{
|
||
const xmedia_char *fstypes[] = {"vfat", "ext4", "ntfs", "auto", NULL};
|
||
|
||
// 创建挂载点目录(若不存在)
|
||
if (mkdir(mount_point, 0755) && errno != EEXIST) {
|
||
SAMPLE_ERR("mkdir failed: %s\n", strerror(errno));
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
|
||
// 尝试挂载常见文件系统类型(按优先级)
|
||
for (xmedia_s32 i = 0; fstypes[i]; i++) {
|
||
if (mount(device_path, mount_point, fstypes[i], MS_NOATIME, NULL) == 0) {
|
||
SAMPLE_PRT("Mounted %s (%s) to %s\n", device_path, fstypes[i], mount_point);
|
||
return XMEDIA_SUCCESS;
|
||
}
|
||
}
|
||
|
||
// 所有类型尝试失败
|
||
SAMPLE_ERR("Failed to mount %s: %s\n", device_path, strerror(errno));
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
|
||
static xmedia_bool device_driver_is_bind(const xmedia_char *device, const xmedia_char *driver)
|
||
{
|
||
xmedia_char path[256] = {'\0'};
|
||
snprintf(path, 256, "%s/%s", driver, device);
|
||
return (access(path, F_OK) == 0);
|
||
}
|
||
|
||
static xmedia_s32 device_attach_driver(const xmedia_char *device, const xmedia_char *driver)
|
||
{
|
||
xmedia_s32 s32Ret = XMEDIA_FAILURE;
|
||
xmedia_char path[256] = {'\0'};
|
||
xmedia_s32 fd = -1;
|
||
|
||
snprintf(path, sizeof(path), "%s/bind", driver);
|
||
fd = open(path, O_WRONLY | O_NONBLOCK | O_CLOEXEC);
|
||
if (fd < 0) {
|
||
SAMPLE_ERR("[%s()] can't open %s because %s\n", __func__, driver, strerror(errno));
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
//MEDIA_PRT("[%s()] start bind %s to %s\n", __func__, device, driver);
|
||
s32Ret = write(fd, device, strlen(device));
|
||
if (s32Ret < 0) {
|
||
SAMPLE_ERR("[%s()] bind %s to %s failed because %s\n", __func__, device, driver,strerror(errno));
|
||
close(fd);
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
close(fd);
|
||
return XMEDIA_SUCCESS;
|
||
}
|
||
|
||
static xmedia_s32 device_detach_driver(const xmedia_char *device, const xmedia_char *driver)
|
||
{
|
||
xmedia_s32 s32Ret = XMEDIA_FAILURE;
|
||
xmedia_char path[256] = {'\0'};
|
||
xmedia_s32 fd = -1;
|
||
snprintf(path, sizeof(path), "%s/unbind", driver);
|
||
fd = open(path, O_WRONLY | O_NONBLOCK | O_CLOEXEC);
|
||
if (fd < 0) {
|
||
SAMPLE_ERR("[%s()] can't open %s because %s\n", __func__, path, strerror(errno));
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
//MEDIA_PRT("[%s()] start unbind %s from %s\n", __func__, device, driver);
|
||
s32Ret = write(fd, device, strlen(device));
|
||
if (s32Ret < 0) {
|
||
SAMPLE_ERR("[%s()] unbind %s from %s failed because %s\n", __func__, device, driver, strerror(errno));
|
||
close(fd);
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
close(fd);
|
||
return XMEDIA_SUCCESS;
|
||
}
|
||
|
||
static xmedia_bool device_action_check(const xmedia_char *device, const xmedia_char *notice_str, xmedia_char *buf)
|
||
{
|
||
xmedia_char *action = XMEDIA_NULL;
|
||
xmedia_char *subsystem = XMEDIA_NULL;
|
||
xmedia_char *devtype = XMEDIA_NULL;
|
||
|
||
xmedia_char *field = buf;
|
||
if (strstr(device, "sdhci") != XMEDIA_NULL) {
|
||
|
||
if (strcmp(notice_str, "add") == 0) {
|
||
while (*field != '\0' && field < buf + MAX_NL_BUF_SIZE) {
|
||
if (strstr(field, "ACTION=")) {
|
||
action = field + strlen("ACTION=");
|
||
} else if (strstr(field, "SUBSYSTEM=")) {
|
||
subsystem = field + strlen("SUBSYSTEM=");
|
||
} else if (strstr(field, "DEVTYPE=")) {
|
||
devtype = field + strlen("DEVTYPE=");
|
||
}
|
||
while (*field != '\0') field++;
|
||
field++;
|
||
}
|
||
|
||
if (subsystem && strcmp(subsystem, "block") == 0 && devtype && strcmp(devtype, "disk") == 0) {
|
||
if (strcmp(action, notice_str) == 0) {
|
||
//SAMPLE_ERR("notice_str :%s success\n", notice_str);
|
||
return XMEDIA_TRUE;
|
||
}
|
||
}
|
||
} else if (strcmp(notice_str, "remove") == 0) {
|
||
while (*field != '\0' && field < buf + MAX_NL_BUF_SIZE) {
|
||
if (strstr(field, "ACTION=")) {
|
||
action = field + strlen("ACTION=");
|
||
} else if (strstr(field, "SUBSYSTEM=")) {
|
||
subsystem = field + strlen("SUBSYSTEM=");
|
||
}
|
||
while (*field != '\0') field++;
|
||
field++;
|
||
}
|
||
|
||
if (subsystem && strcmp(subsystem, "mmc_host") == 0) {
|
||
if (strcmp(action, notice_str) == 0) {
|
||
//SAMPLE_ERR("notice_str :%s success\n", notice_str);
|
||
return XMEDIA_TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return XMEDIA_FALSE;
|
||
}
|
||
|
||
xmedia_s32 sample_comm_periph_mount_sdcard(const xmedia_char* mount_path, xmedia_s32 timeout_ms)
|
||
{
|
||
DIR *block_dir;
|
||
struct dirent *entry;
|
||
xmedia_u64 start_time = 0;
|
||
xmedia_u64 end_time = 0;
|
||
xmedia_char partition[MAX_DEVICE_NAME_LENGTH] = {0};
|
||
xmedia_char device_path[MAX_DEVICE_NAME_LENGTH + 32] = {0};
|
||
|
||
xmedia_sys_get_cur_pts(&start_time);
|
||
|
||
LOOP_AGAIN:
|
||
block_dir = opendir("/sys/block");
|
||
if (!block_dir) {
|
||
SAMPLE_ERR("Failed to open /sys/block: %s\n", strerror(errno));
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
|
||
while ((entry = readdir(block_dir)) != NULL) {
|
||
// 只处理 mmcblk 设备
|
||
if (strncmp(entry->d_name, "mmcblk", 6) != 0) {
|
||
continue;
|
||
}
|
||
|
||
// 查找分区
|
||
if (sdcard_find_partition(entry->d_name, partition) == 0) {
|
||
// 挂载分区(如 /dev/mmcblk0p1)
|
||
snprintf(device_path, sizeof(device_path), "/dev/%s", partition);
|
||
if (mount_device(mount_path, device_path) == XMEDIA_SUCCESS) {
|
||
closedir(block_dir);
|
||
return XMEDIA_SUCCESS;
|
||
}
|
||
} else {
|
||
// 无分区,挂载整个设备(如 /dev/mmcblk1)
|
||
snprintf(device_path, sizeof(device_path), "/dev/%s", entry->d_name);
|
||
if (mount_device(mount_path, device_path) == XMEDIA_SUCCESS) {
|
||
closedir(block_dir);
|
||
return XMEDIA_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
|
||
closedir(block_dir);
|
||
|
||
xmedia_sys_get_cur_pts(&end_time);
|
||
if(end_time - start_time < timeout_ms * 1000) {
|
||
usleep(50 * 1000);
|
||
SAMPLE_PRT("No SD card found, find again\n");
|
||
goto LOOP_AGAIN;
|
||
}
|
||
|
||
SAMPLE_ERR("No SD card found.\n");
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
|
||
xmedia_s32 sample_comm_periph_umount_sdcard(const xmedia_char *mount_path)
|
||
{
|
||
const xmedia_s32 max_retries = 30;
|
||
xmedia_s32 retry_count = 0;
|
||
|
||
while (retry_count < max_retries) {
|
||
if (umount(mount_path) == 0) {
|
||
return XMEDIA_SUCCESS;
|
||
}
|
||
|
||
// 错误处理
|
||
switch (errno) {
|
||
case EBUSY:
|
||
SAMPLE_ERR("ERROR: %s is busy (retry %d/%d)\n",
|
||
mount_path, retry_count + 1, max_retries);
|
||
break;
|
||
case EINVAL:
|
||
SAMPLE_ERR("Error: %s not mounted or invalid path\n", mount_path);
|
||
return XMEDIA_FAILURE;
|
||
case EPERM:
|
||
SAMPLE_ERR("Error: Requires root privileges\n");
|
||
return XMEDIA_FAILURE;
|
||
default:
|
||
SAMPLE_ERR("Unmount failed: %s\n", strerror(errno));
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
|
||
retry_count++;
|
||
usleep(10 * 1000);
|
||
}
|
||
|
||
SAMPLE_ERR("Error: Failed to unmount %s after %d attempts\n", mount_path, max_retries);
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
|
||
xmedia_s32 sample_comm_periph_drv_bind(const xmedia_char *device, const xmedia_char *driver, const xmedia_char *notice_str)
|
||
{
|
||
xmedia_s32 s32Ret = 0;
|
||
xmedia_s32 fd = -1;
|
||
xmedia_char buf[MAX_NL_BUF_SIZE] = {'\0'};
|
||
fd_set read_set;
|
||
struct timeval timeout;
|
||
struct sockaddr_nl addr;
|
||
|
||
if (device_driver_is_bind(device, driver)) {
|
||
SAMPLE_ERR("[%s()] sdcard device already bind!\n", __func__);
|
||
return XMEDIA_SUCCESS;
|
||
}
|
||
|
||
memset(&addr, 0, sizeof(addr));
|
||
addr.nl_family = AF_NETLINK;
|
||
addr.nl_groups = 1;
|
||
addr.nl_pid = 0;
|
||
|
||
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
|
||
if (fd < 0) {
|
||
SAMPLE_ERR("[%s()] Failed to open sdcard netlink because %s\n", __func__, strerror(errno));
|
||
return XMEDIA_FAILURE;
|
||
} else if (bind(fd, (struct sockaddr *)(&addr), sizeof(addr)) != 0) {
|
||
SAMPLE_ERR("[%s()] bind sdcard netlink addr failed because %s\n", __func__, strerror(errno));
|
||
goto FAILED;
|
||
}
|
||
|
||
FD_ZERO(&read_set);
|
||
FD_SET(fd, &read_set);
|
||
timeout.tv_sec = 0;
|
||
timeout.tv_usec = MAX_SELECT_TIMEOUT;
|
||
|
||
// bind sdcard
|
||
if (device_attach_driver(device, driver) != XMEDIA_SUCCESS) {
|
||
goto FAILED;
|
||
}
|
||
|
||
// wait for bind success
|
||
RETRY:
|
||
s32Ret = select(fd + 1, &read_set, NULL, NULL, &timeout);
|
||
if (s32Ret > 0) {
|
||
memset(&buf, 0, sizeof(buf));
|
||
read(fd, buf, sizeof(buf));
|
||
buf[MAX_NL_BUF_SIZE - 1] = '\0';
|
||
if (device_action_check(device, notice_str, buf) == XMEDIA_TRUE) {
|
||
goto SUCCESS;
|
||
}
|
||
goto RETRY; // drop all message
|
||
} else {
|
||
SAMPLE_ERR("[%s()] select error %s\n", __func__, strerror(errno));
|
||
goto FAILED;
|
||
}
|
||
SUCCESS:
|
||
close(fd);
|
||
return XMEDIA_SUCCESS;
|
||
FAILED:
|
||
close(fd);
|
||
return XMEDIA_FAILURE;
|
||
}
|
||
|
||
xmedia_s32 sample_comm_periph_drv_unbind(const xmedia_char *device, const xmedia_char *driver, const xmedia_char *notice_str)
|
||
{
|
||
xmedia_s32 s32Ret = 0;
|
||
xmedia_s32 fd = -1;
|
||
xmedia_char buf[MAX_NL_BUF_SIZE] = {'\0'};
|
||
fd_set read_set;
|
||
struct timeval timeout;
|
||
struct sockaddr_nl addr;
|
||
|
||
if (!device_driver_is_bind(device, driver)) {
|
||
SAMPLE_ERR("[%s()] sdcard device already unbind!\n", __func__);
|
||
return XMEDIA_SUCCESS;
|
||
}
|
||
|
||
memset(&addr, 0, sizeof(addr));
|
||
addr.nl_family = AF_NETLINK;
|
||
addr.nl_groups = 1;
|
||
addr.nl_pid = 0;
|
||
|
||
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
|
||
if (fd < 0) {
|
||
SAMPLE_ERR("[%s()] Failed to open sdcard netlink because %s\n", __func__, strerror(errno));
|
||
return XMEDIA_FAILURE;
|
||
} else if (bind(fd, (struct sockaddr *)(&addr), sizeof(addr)) != 0) {
|
||
SAMPLE_ERR("[%s()] bind sdcard netlink addr failed because %s\n", __func__, strerror(errno));
|
||
goto FAILED;
|
||
}
|
||
|
||
memset(&buf, 0, sizeof(buf));
|
||
FD_ZERO(&read_set);
|
||
FD_SET(fd, &read_set);
|
||
timeout.tv_sec = 0;
|
||
timeout.tv_usec = MAX_SELECT_TIMEOUT;
|
||
|
||
// unbind sdcard
|
||
if (device_detach_driver(device, driver) != XMEDIA_SUCCESS)
|
||
goto FAILED;
|
||
// wait for unbind success
|
||
RETRY:
|
||
s32Ret = select(fd + 1, &read_set, NULL, NULL, &timeout);
|
||
if (s32Ret > 0) {
|
||
memset(&buf, 0, sizeof(buf));
|
||
read(fd, buf, sizeof(buf));
|
||
buf[MAX_NL_BUF_SIZE - 1] = '\0';
|
||
if (device_action_check(device, notice_str, buf) == XMEDIA_TRUE) {
|
||
goto SUCCESS;
|
||
}
|
||
goto RETRY; // drop all message
|
||
} else {
|
||
SAMPLE_ERR("[%s()] select error %s\n", __func__, strerror(errno));
|
||
goto FAILED;
|
||
}
|
||
SUCCESS:
|
||
close(fd);
|
||
return XMEDIA_SUCCESS;
|
||
FAILED:
|
||
close(fd);
|
||
return XMEDIA_FAILURE;
|
||
}
|