1/* 2 * MTD utility functions 3 * 4 * Copyright 2006, Broadcom Corporation 5 * All Rights Reserved. 6 * 7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY 8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM 9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS 10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. 11 * 12 * $Id: mtd.c,v 1.1.1.1 2008/10/15 03:28:48 james26_jang Exp $ 13 */ 14 15#include <stdio.h> 16#include <stdlib.h> 17#include <string.h> 18#include <unistd.h> 19#include <limits.h> 20#include <sys/sysmacros.h> 21#include <sys/types.h> 22#include <sys/stat.h> 23#include <fcntl.h> 24#include <errno.h> 25#include <error.h> 26#include <sys/ioctl.h> 27#include <sys/sysinfo.h> 28 29#include <linux/mtd/mtd.h> 30 31#include <trxhdr.h> 32#include <rts/crc.h> 33#include <bcmutils.h> 34#include <shutils.h> 35 36/* 37 * Open an MTD device 38 * @param mtd path to or partition name of MTD device 39 * @param flags open() flags 40 * @return return value of open() 41 */ 42int 43mtd_open(const char *mtd, int flags) 44{ 45 FILE *fp; 46 char dev[PATH_MAX]; 47 int i; 48 49 if ((fp = fopen("/proc/mtd", "r"))) { 50 while (fgets(dev, sizeof(dev), fp)) { 51 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) { 52 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i); 53 fclose(fp); 54 return open(dev, flags); 55 } 56 } 57 fclose(fp); 58 } 59 60 return open(mtd, flags); 61} 62 63/* 64 * Erase an MTD device 65 * @param mtd path to or partition name of MTD device 66 * @return 0 on success and errno on failure 67 */ 68int 69mtd_erase(const char *mtd) 70{ 71 int mtd_fd; 72 mtd_info_t mtd_info; 73 erase_info_t erase_info; 74 75 /* Open MTD device */ 76 if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0) { 77 perror(mtd); 78 return errno; 79 } 80 81 /* Get sector size */ 82 if (ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) { 83 perror(mtd); 84 close(mtd_fd); 85 return errno; 86 } 87 88 erase_info.length = mtd_info.erasesize; 89 90 for (erase_info.start = 0; 91 erase_info.start < mtd_info.size; 92 erase_info.start += mtd_info.erasesize) { 93 (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info); 94 if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0) { 95 perror(mtd); 96 close(mtd_fd); 97 return errno; 98 } 99 } 100 101 close(mtd_fd); 102 return 0; 103} 104 105extern int http_get(const char *server, char *buf, size_t count, off_t offset); 106 107/* 108 * Write a file to an MTD device 109 * @param path file to write or a URL 110 * @param mtd path to or partition name of MTD device 111 * @return 0 on success and errno on failure 112 */ 113int 114mtd_write(const char *path, const char *mtd) 115{ 116 int mtd_fd = -1; 117 mtd_info_t mtd_info; 118 erase_info_t erase_info; 119 120 struct sysinfo info; 121 struct trx_header trx; 122 unsigned long crc; 123 124 FILE *fp; 125 char *buf = NULL; 126 long count, len, off; 127 int ret = -1; 128 129 /* Examine TRX header */ 130 if ((fp = fopen(path, "r"))) 131 count = safe_fread(&trx, 1, sizeof(struct trx_header), fp); 132 else 133 count = http_get(path, (char *) &trx, sizeof(struct trx_header), 0); 134 if (count < sizeof(struct trx_header)) { 135 fprintf(stderr, "%s: File is too small (%ld bytes)\n", path, count); 136 goto fail; 137 } 138 if (trx.magic != TRX_MAGIC || 139 trx.len > TRX_MAX_LEN || 140 trx.len < sizeof(struct trx_header)) { 141 fprintf(stderr, "%s: Bad trx header\n", path); 142 goto fail; 143 } 144 145 /* Open MTD device and get sector size */ 146 if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0 || 147 ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0 || 148 mtd_info.erasesize < sizeof(struct trx_header)) { 149 perror(mtd); 150 goto fail; 151 } 152 153 /* Allocate temporary buffer */ 154 /* See if we have enough memory to store the whole file */ 155 sysinfo(&info); 156 if (info.freeram >= trx.len) { 157 erase_info.length = ROUNDUP(trx.len, mtd_info.erasesize); 158 if (!(buf = malloc(erase_info.length))) 159 erase_info.length = mtd_info.erasesize; 160 } 161 /* fallback to smaller buffer */ 162 else { 163 erase_info.length = mtd_info.erasesize; 164 buf = NULL; 165 } 166 if (!buf && (!(buf = malloc(erase_info.length)))) { 167 perror("malloc"); 168 goto fail; 169 } 170 171 /* Calculate CRC over header */ 172 crc = crc32((uint8 *) &trx.flag_version, 173 sizeof(struct trx_header) - OFFSETOF(struct trx_header, 174 flag_version), CRC32_INIT_VALUE); 175 176 if (trx.flag_version & TRX_NO_HEADER) 177 trx.len -= sizeof(struct trx_header); 178 179 /* Write file or URL to MTD device */ 180 for (erase_info.start = 0; erase_info.start < trx.len; erase_info.start += count) { 181 len = MIN(erase_info.length, trx.len - erase_info.start); 182 if ((trx.flag_version & TRX_NO_HEADER) || erase_info.start) 183 count = off = 0; 184 else { 185 count = off = sizeof(struct trx_header); 186 memcpy(buf, &trx, sizeof(struct trx_header)); 187 } 188 if (fp) 189 count += safe_fread(&buf[off], 1, len - off, fp); 190 else 191 count += http_get(path, &buf[off], len - off, erase_info.start + off); 192 if (count < len) { 193 fprintf(stderr, "%s: Truncated file (actual %ld expect %ld)\n", path, 194 count - off, len - off); 195 goto fail; 196 } 197 /* Update CRC */ 198 crc = crc32(&buf[off], count - off, crc); 199 /* Check CRC before writing if possible */ 200 if (count == trx.len) { 201 if (crc != trx.crc32) { 202 fprintf(stderr, "%s: Bad CRC\n", path); 203 goto fail; 204 } 205 } 206 /* Do it */ 207 (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info); 208 if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 || 209 write(mtd_fd, buf, count) != count) { 210 perror(mtd); 211 goto fail; 212 } 213 } 214 215 printf("%s: CRC OK\n", mtd); 216 ret = 0; 217 218fail: 219 if (buf) { 220 /* Dummy read to ensure chip(s) are out of lock/suspend state */ 221 (void) read(mtd_fd, buf, 2); 222 free(buf); 223 } 224 225 if (mtd_fd >= 0) 226 close(mtd_fd); 227 if (fp) 228 fclose(fp); 229 return ret; 230} 231