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