Home | History | Annotate | Download | only in updater
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 /* Read/write/erase SPI flash through Linux kernel MTD interface */
     17 
     18 #define LOG_TAG "fwtool"
     19 
     20 #include <stdint.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <unistd.h>
     24 #include <errno.h>
     25 #include <fcntl.h>
     26 #include <sys/ioctl.h>
     27 #include <sys/types.h>
     28 
     29 #include <mtd/mtd-user.h>
     30 
     31 #include "flash_device.h"
     32 #include "update_log.h"
     33 #include "vboot_interface.h"
     34 
     35 static const char * const DEFAULT_MTD_FILE = "/dev/mtd/mtd0";
     36 
     37 struct mtd_data {
     38 	int fd;
     39 	struct mtd_info_user info;
     40 };
     41 
     42 static void *mtd_open(const void *params)
     43 {
     44 	const char *path = params ? params : DEFAULT_MTD_FILE;
     45 	struct mtd_data *dev = calloc(1, sizeof(struct mtd_data));
     46 	if (!dev)
     47 		return NULL;
     48 
     49 	dev->fd = open(path, O_RDWR);
     50 	if (dev->fd == -1) {
     51 		ALOGE("No MTD device %s : %d\n", path, errno);
     52 		goto out_free;
     53 	}
     54 
     55 	if (ioctl(dev->fd, MEMGETINFO, &dev->info)) {
     56 		ALOGE("Cannot get MTD info for %s : %d\n", path, errno);
     57 		goto out_close;
     58 	}
     59 
     60 	if (dev->info.type != MTD_NORFLASH) {
     61 		ALOGE("Unsupported MTD device type: %d\n", dev->info.type);
     62 		goto out_close;
     63 	}
     64 
     65 	ALOGD("MTD %s: size %d erasesize %d min_io_size %d\n",
     66 		path, dev->info.size, dev->info.erasesize, dev->info.writesize);
     67 
     68 	return dev;
     69 
     70 out_close:
     71 	close(dev->fd);
     72 	dev->fd = -1;
     73 out_free:
     74 	free(dev);
     75 
     76 	return NULL;
     77 }
     78 
     79 static void mtd_close(void *hnd)
     80 {
     81 	struct mtd_data *dev = hnd;
     82 
     83 	close(dev->fd);
     84 	free(dev);
     85 }
     86 
     87 static int mtd_read(void *hnd, off_t offset, void *buffer, size_t count)
     88 {
     89 	struct mtd_data *dev = hnd;
     90 	ssize_t res;
     91 	uint8_t *ptr = buffer;
     92 
     93 	if (lseek(dev->fd, offset, SEEK_SET) != offset) {
     94 		ALOGW("Cannot seek to %ld\n", offset);
     95 		return errno;
     96 
     97 	}
     98 
     99 	while (count) {
    100 		res = read(dev->fd, ptr, count);
    101 		if (res < 0) {
    102 			ALOGW("Cannot read at %ld : %zd\n", offset, res);
    103 			return errno;
    104 		}
    105 		count -= res;
    106 		ptr += res;
    107 	}
    108 	return 0;
    109 }
    110 
    111 static int mtd_write(void *hnd, off_t offset, void *buffer, size_t count)
    112 {
    113 	struct mtd_data *dev = hnd;
    114 	ssize_t res;
    115 	uint8_t *ptr = buffer;
    116 
    117 	if (lseek(dev->fd, offset, SEEK_SET) != offset) {
    118 		ALOGW("Cannot seek to %ld\n", offset);
    119 		return errno;
    120 	}
    121 
    122 	while (count) {
    123 		res = write(dev->fd, ptr, count);
    124 		if (res < 0) {
    125 			ALOGW("Cannot write at %ld : %zd\n", offset, res);
    126 			return errno;
    127 		}
    128 		count -= res;
    129 		ptr += res;
    130 	}
    131 	return 0;
    132 }
    133 
    134 static int mtd_erase(void *hnd, off_t offset, size_t count)
    135 {
    136 	struct mtd_data *dev = hnd;
    137 	int res;
    138 	struct erase_info_user ei;
    139 
    140 	ei.start = offset;
    141 	ei.length = count;
    142 	res = ioctl(dev->fd, MEMERASE, &ei);
    143 	if (res < 0) {
    144 		ALOGW("Cannot erase at %ld : %d\n", offset, res);
    145 		return errno;
    146 	}
    147 
    148 	return 0;
    149 }
    150 
    151 /*
    152  * Write Protect handling :
    153  * struct erase_info_user ei;
    154  *
    155  * ei.start = eb * info.erasesize;
    156  * ei.length = info.erasesize;
    157  * ret = ioctl(fd, MEMISLOCKED, &ei);
    158  * ret = ioctl(fd, MEMLOCK, &ei);
    159  * ret = ioctl(fd, MEMUNLOCK, &ei);
    160  */
    161 
    162 static size_t mtd_get_size(void *hnd)
    163 {
    164 	struct mtd_data *dev = hnd;
    165 
    166 	return dev && dev->fd > 0 ? dev->info.size : 0;
    167 }
    168 
    169 static size_t mtd_get_write_size(void *hnd)
    170 {
    171 	struct mtd_data *dev = hnd;
    172 
    173 	return dev && dev->fd > 0 ? dev->info.writesize : 0;
    174 }
    175 
    176 static size_t mtd_get_erase_size(void *hnd)
    177 {
    178 	struct mtd_data *dev = hnd;
    179 
    180 	return dev && dev->fd > 0 ? dev->info.erasesize : 0;
    181 }
    182 
    183 static off_t mtd_get_fmap_offset(void *hnd __attribute__((unused)))
    184 {
    185 	/* Get the SPI FMAP offset passed by the firmware in the device-tree */
    186 	return fdt_read_u32("fmap-offset") + 64;
    187 }
    188 
    189 const struct flash_device_ops flash_mtd_ops = {
    190 	.name = "spi",
    191 	.open = mtd_open,
    192 	.close = mtd_close,
    193 	.read = mtd_read,
    194 	.write = mtd_write,
    195 	.erase = mtd_erase,
    196 	.get_size = mtd_get_size,
    197 	.get_write_size = mtd_get_write_size,
    198 	.get_erase_size = mtd_get_erase_size,
    199 	.get_fmap_offset = mtd_get_fmap_offset,
    200 };
    201