/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Read/write/erase SPI flash through Linux kernel MTD interface */
#define LOG_TAG "fwtool"
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "edify/expr.h"
#include "flash_device.h"
#include "update_log.h"
struct file_data {
int fd;
uint8_t *data;
struct stat info;
};
static void *file_blob_open(struct file_data *dev, const Value *param)
{
dev->fd = -1; /* No backing file */
dev->data = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(¶m->data[0]));
dev->info.st_size = param->data.size();
return dev;
}
static void *file_open(const void *params)
{
struct file_data *dev = static_cast<struct file_data*>(calloc(1, sizeof(struct file_data)));
if (!dev)
return NULL;
const Value *value = static_cast<const Value*>(params);
if (value->type == VAL_BLOB)
return file_blob_open(dev, value);
if (value->type != VAL_STRING)
goto out_free;
dev->fd = open(value->data.c_str(), O_RDWR);
if (dev->fd == -1) {
ALOGE("Cannot open file %s : %d\n", value->data.c_str(), errno);
goto out_free;
}
if (fstat(dev->fd, &dev->info)) {
ALOGE("Cannot get file info for %s : %d\n", value->data.c_str(), errno);
goto out_close;
}
dev->data = static_cast<uint8_t*>(mmap(NULL, dev->info.st_size, PROT_READ | PROT_WRITE,
MAP_SHARED, dev->fd, 0));
if (dev->data == (void *)-1) {
ALOGE("Cannot mmap %s : %d\n", value->data.c_str(), errno);
goto out_close;
}
ALOGD("File %s: size %lld blksize %ld\n", value->data.c_str(),
(long long)dev->info.st_size, (long)dev->info.st_blksize);
return dev;
out_close:
close(dev->fd);
out_free:
free(dev);
return NULL;
}
static void file_close(void *hnd)
{
struct file_data *dev = static_cast<struct file_data*>(hnd);
if (dev->fd > 0) {
munmap(dev->data, dev->info.st_size);
close(dev->fd);
}
free(dev);
}
static int file_read(void *hnd, off_t offset, void *buffer, size_t count)
{
struct file_data *dev = static_cast<struct file_data*>(hnd);
if (offset + (off_t)count > dev->info.st_size) {
ALOGW("Invalid offset/size %ld + %zd > %lld\n",
offset, count, (long long)dev->info.st_size);
return -EINVAL;
}
memcpy(buffer, dev->data + offset, count);
return 0;
}
static int file_write(void *hnd, off_t offset, void *buffer, size_t count)
{
struct file_data *dev = static_cast<struct file_data*>(hnd);
if (offset + (off_t)count > dev->info.st_size) {
ALOGW("Invalid offset/size %ld + %zd > %lld\n",
offset, count, (long long)dev->info.st_size);
return -EINVAL;
}
memcpy(dev->data + offset, buffer, count);
return 0;
}
static int file_erase(void *hnd, off_t offset, size_t count)
{
struct file_data *dev = static_cast<struct file_data*>(hnd);
if (offset + (off_t)count > dev->info.st_size) {
ALOGW("Invalid offset/size %ld + %zd > %lld\n",
offset, count, (long long)dev->info.st_size);
return -EINVAL;
}
memset(dev->data + offset, '\xff', count);
return 0;
}
static size_t file_get_size(void *hnd)
{
struct file_data *dev = static_cast<struct file_data*>(hnd);
return dev ? dev->info.st_size : 0;
}
static size_t file_get_write_size(void *hnd)
{
struct file_data *dev = static_cast<struct file_data*>(hnd);
return dev && dev->fd > 0 ? dev->info.st_blksize : 0;
}
static size_t file_get_erase_size(void *hnd)
{
struct file_data *dev = static_cast<struct file_data*>(hnd);
return dev && dev->fd > 0 ? dev->info.st_blksize : 0;
}
static off_t file_get_fmap_offset(void *hnd)
{
struct file_data *dev = static_cast<struct file_data*>(hnd);
return dev->info.st_size;
}
const struct flash_device_ops flash_file_ops = {
.name = "file",
.open = file_open,
.close = file_close,
.read = file_read,
.write = file_write,
.erase = file_erase,
.get_size = file_get_size,
.get_write_size = file_get_write_size,
.get_erase_size = file_get_erase_size,
.get_fmap_offset = file_get_fmap_offset,
};