/*
* 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.
*/
/* Handle read/write/erase of various devices used by the firmware */
#define LOG_TAG "fwtool"
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "flash_device.h"
#include "fmap.h"
#include "update_log.h"
#include "vboot_interface.h"
static const struct flash_device_ops *devices[] = {
&flash_mtd_ops,
&flash_ec_ops,
&flash_file_ops,
};
struct flash_device {
const struct flash_device_ops *ops;
void *priv_data;
struct fmap *fmap;
uint8_t *gbb;
size_t gbb_size;
size_t total_size;
size_t write_size;
size_t erase_size;
};
struct flash_device *flash_open(const char *name, const void *params)
{
const struct flash_device_ops *ops = devices[0];
struct flash_device *dev;
if (name) {
unsigned i;
for (i = 0; i < sizeof(devices)/sizeof(devices[0]); i++)
if (!strcmp(devices[i]->name, name)) {
ops = devices[i];
break;
}
}
ALOGD("Using flash device '%s'\n", ops->name);
dev = calloc(1, sizeof(struct flash_device));
if (!dev)
return NULL;
dev->ops = ops;
dev->priv_data = dev->ops->open(params);
if (!dev->priv_data)
goto out_free;
dev->fmap = NULL;
dev->gbb = NULL;
dev->gbb_size = 0;
dev->total_size = dev->ops->get_size(dev->priv_data);
dev->write_size = dev->ops->get_write_size(dev->priv_data);
dev->erase_size = dev->ops->get_erase_size(dev->priv_data);
return dev;
out_free:
free(dev);
return NULL;
}
void flash_close(struct flash_device *dev)
{
dev->ops->close(dev->priv_data);
if (dev->gbb)
free(dev->gbb);
if (dev->fmap)
free(dev->fmap);
free(dev);
}
int flash_read(struct flash_device *dev, off_t off, void *buff, size_t len)
{
return dev->ops->read(dev->priv_data, off, buff, len);
}
int flash_write(struct flash_device *dev, off_t off, void *buff, size_t len)
{
if ((off % dev->write_size) || (len % dev->write_size)) {
ALOGW("Bad write alignment offset %ld size %zd\n",
off, len);
return -EINVAL;
}
return dev->ops->write(dev->priv_data, off, buff, len);
}
int flash_erase(struct flash_device *dev, off_t off, size_t len)
{
if ((off % dev->erase_size) || (len % dev->erase_size)) {
ALOGW("Bad erase alignment offset %ld size %zd\n",
off, len);
return -EINVAL;
}
return dev->ops->erase(dev->priv_data, off, len);
}
size_t flash_get_size(struct flash_device *dev)
{
return dev->ops->get_size(dev->priv_data);
}
struct fmap *flash_get_fmap(struct flash_device *dev)
{
if (!dev->fmap) {
off_t end = dev->ops->get_fmap_offset(dev->priv_data);
off_t off = fmap_scan_offset(dev, end);
dev->fmap = fmap_load(dev, off);
}
if (!dev->fmap)
ALOGW("No FMAP found\n");
return dev->fmap;
}
uint8_t *flash_get_gbb(struct flash_device *dev, size_t *size)
{
if (!dev->gbb)
dev->gbb = fmap_read_section(dev, "GBB", &dev->gbb_size, NULL);
if (!dev->gbb)
ALOGW("No GBB found\n");
else if (size)
*size = dev->gbb_size;
return dev->gbb;
}
int flash_cmd(struct flash_device *dev, int cmd, int ver,
const void *odata, int osize, void *idata, int isize)
{
if (!dev->ops->cmd)
return -ENOENT;
return dev->ops->cmd(dev->priv_data, cmd, ver,
odata, osize, idata, isize);
}