blob: 96ded31f8e37102d61e0cf7e0b12f46c2a82e1f3 [file] [log] [blame]
/*
* 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,
&sector_start, &sector_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,
&sector_start, &sector_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),
&sector_start, &sector_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, &sector_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, &sector_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;
}