| /* |
| * 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, |
| }; |