182 lines
4.6 KiB
C
182 lines
4.6 KiB
C
/*
|
|
* Copyright (c) 2023 Antmicro <www.antmicro.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/storage/disk_access.h>
|
|
|
|
#include "ext2.h"
|
|
#include "ext2_struct.h"
|
|
|
|
LOG_MODULE_DECLARE(ext2);
|
|
|
|
static struct disk_data {
|
|
const char *name;
|
|
uint32_t sector_size;
|
|
uint32_t sector_count;
|
|
} disk_data;
|
|
|
|
static int64_t disk_access_device_size(struct ext2_data *fs)
|
|
{
|
|
struct disk_data *disk = fs->backend;
|
|
|
|
return disk->sector_count * disk->sector_size;
|
|
}
|
|
|
|
static int64_t disk_access_write_size(struct ext2_data *fs)
|
|
{
|
|
struct disk_data *disk = fs->backend;
|
|
|
|
return disk->sector_size;
|
|
}
|
|
|
|
static int disk_read(const char *disk, uint8_t *buf, uint32_t start, uint32_t num)
|
|
{
|
|
int rc, loop = 0;
|
|
|
|
do {
|
|
rc = disk_access_ioctl(disk, DISK_IOCTL_CTRL_SYNC, NULL);
|
|
if (rc == 0) {
|
|
rc = disk_access_read(disk, buf, start, num);
|
|
LOG_DBG("disk read: (start:%d, num:%d) (ret: %d)", start, num, rc);
|
|
}
|
|
} while ((rc == -EBUSY) && (loop++ < 16));
|
|
return rc;
|
|
}
|
|
|
|
static int disk_write(const char *disk, const uint8_t *buf, uint32_t start, uint32_t num)
|
|
{
|
|
int rc, loop = 0;
|
|
|
|
do {
|
|
rc = disk_access_ioctl(disk, DISK_IOCTL_CTRL_SYNC, NULL);
|
|
if (rc == 0) {
|
|
rc = disk_access_write(disk, buf, start, num);
|
|
LOG_DBG("disk write: (start:%d, num:%d) (ret: %d)", start, num, rc);
|
|
}
|
|
} while ((rc == -EBUSY) && (loop++ < 16));
|
|
return rc;
|
|
}
|
|
|
|
static int disk_prepare_range(struct disk_data *disk, uint32_t addr, uint32_t size,
|
|
uint32_t *s_start, uint32_t *s_count)
|
|
{
|
|
*s_start = CONFIG_EXT2_DISK_STARTING_SECTOR + addr / disk->sector_size;
|
|
*s_count = size / disk->sector_size;
|
|
|
|
LOG_DBG("addr:0x%x size:0x%x -> sector_start:%d sector_count:%d",
|
|
addr, size, *s_start, *s_count);
|
|
|
|
/* Check for overflow. */
|
|
if (*s_count > UINT32_MAX - *s_start) {
|
|
LOG_ERR("Requested range (%d:+%d) can't be accessed due to overflow.",
|
|
*s_start, *s_count);
|
|
return -ENOSPC;
|
|
}
|
|
|
|
/* Cannot read or write outside the disk. */
|
|
if (*s_start + *s_count > disk->sector_count) {
|
|
LOG_ERR("Requested sectors: %d-%d are outside of disk (num_sectors: %d)",
|
|
*s_start, *s_start + *s_count, disk->sector_count);
|
|
return -ENOSPC;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int disk_access_read_block(struct ext2_data *fs, void *buf, uint32_t block)
|
|
{
|
|
int rc;
|
|
struct disk_data *disk = fs->backend;
|
|
uint32_t sector_start, sector_count;
|
|
|
|
rc = disk_prepare_range(disk, block * fs->block_size, fs->block_size,
|
|
§or_start, §or_count);
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
return disk_read(disk->name, buf, sector_start, sector_count);
|
|
}
|
|
|
|
static int disk_access_write_block(struct ext2_data *fs, const void *buf, uint32_t block)
|
|
{
|
|
int rc;
|
|
struct disk_data *disk = fs->backend;
|
|
uint32_t sector_start, sector_count;
|
|
|
|
rc = disk_prepare_range(disk, block * fs->block_size, fs->block_size,
|
|
§or_start, §or_count);
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
return disk_write(disk->name, buf, sector_start, sector_count);
|
|
}
|
|
|
|
static int disk_access_read_superblock(struct ext2_data *fs, struct ext2_disk_superblock *sb)
|
|
{
|
|
int rc;
|
|
struct disk_data *disk = fs->backend;
|
|
uint32_t sector_start, sector_count;
|
|
|
|
rc = disk_prepare_range(disk, EXT2_SUPERBLOCK_OFFSET, sizeof(struct ext2_disk_superblock),
|
|
§or_start, §or_count);
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
return disk_read(disk->name, (uint8_t *)sb, sector_start, sector_count);
|
|
}
|
|
|
|
static int disk_access_sync(struct ext2_data *fs)
|
|
{
|
|
struct disk_data *disk = fs->backend;
|
|
|
|
LOG_DBG("Sync disk %s", disk->name);
|
|
return disk_access_ioctl(disk->name, DISK_IOCTL_CTRL_SYNC, NULL);
|
|
}
|
|
|
|
static const struct ext2_backend_ops disk_access_ops = {
|
|
.get_device_size = disk_access_device_size,
|
|
.get_write_size = disk_access_write_size,
|
|
.read_block = disk_access_read_block,
|
|
.write_block = disk_access_write_block,
|
|
.read_superblock = disk_access_read_superblock,
|
|
.sync = disk_access_sync,
|
|
};
|
|
|
|
int ext2_init_disk_access_backend(struct ext2_data *fs, const void *storage_dev, int flags)
|
|
{
|
|
int rc;
|
|
uint32_t sector_size, sector_count;
|
|
const char *name = (const char *)storage_dev;
|
|
|
|
rc = disk_access_init(name);
|
|
if (rc < 0) {
|
|
LOG_ERR("FAIL: unable to find disk %s: %d\n", name, rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = disk_access_ioctl(name, DISK_IOCTL_GET_SECTOR_COUNT, §or_count);
|
|
if (rc < 0) {
|
|
LOG_ERR("Disk access (sector count) error: %d", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = disk_access_ioctl(name, DISK_IOCTL_GET_SECTOR_SIZE, §or_size);
|
|
if (rc < 0) {
|
|
LOG_ERR("Disk access (sector size) error: %d", rc);
|
|
return rc;
|
|
}
|
|
|
|
disk_data = (struct disk_data) {
|
|
.name = storage_dev,
|
|
.sector_size = sector_size,
|
|
.sector_count = sector_count,
|
|
};
|
|
|
|
fs->backend = &disk_data;
|
|
fs->backend_ops = &disk_access_ops;
|
|
return 0;
|
|
}
|