1/*
2 * MTD utility functions
3 *
4 * Copyright (C) 2014, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: mtd.c 289794 2011-10-14 08:24:08Z $
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25#include <limits.h>
26#include <sys/sysmacros.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <errno.h>
31#include <error.h>
32#include <sys/ioctl.h>
33#include <sys/sysinfo.h>
34
35#ifdef LINUX26
36#include <mtd/mtd-user.h>
37#else /* LINUX26 */
38#include <linux/mtd/mtd.h>
39#endif /* LINUX26 */
40
41#include <trxhdr.h>
42#include <bcmutils.h>
43#include <bcmendian.h>
44#include <bcmnvram.h>
45#include <shutils.h>
46
47/*
48 * Open an MTD device
49 * @param	mtd	path to or partition name of MTD device
50 * @param	flags	open() flags
51 * @return	return value of open()
52 */
53int
54mtd_open(const char *mtd, int flags)
55{
56	FILE *fp;
57	char dev[PATH_MAX];
58	int i;
59
60	if ((fp = fopen("/proc/mtd", "r"))) {
61		while (fgets(dev, sizeof(dev), fp)) {
62			if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
63#ifdef LINUX26
64				snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
65#else
66				snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
67#endif
68				fclose(fp);
69				return open(dev, flags);
70			}
71		}
72		fclose(fp);
73	}
74
75	return open(mtd, flags);
76}
77
78/*
79 * Erase an MTD device
80 * @param	mtd	path to or partition name of MTD device
81 * @return	0 on success and errno on failure
82 */
83int
84mtd_erase(const char *mtd)
85{
86	int mtd_fd;
87	mtd_info_t mtd_info;
88	erase_info_t erase_info;
89	int cnt;
90	int isNvram = 0;
91
92	/* Open MTD device */
93	if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0) {
94		perror(mtd);
95		return errno;
96	}
97
98	/* Get sector size */
99	if (ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
100		perror(mtd);
101		close(mtd_fd);
102		return errno;
103	}
104
105    /* Foxconn Bob modified start, 09/06/2013, a workaround to avoid erase to next partition,
106       unknown reason, erase bad block won't return error in the case of 15th block(last block of nvram partition) is bad block */
107	if(!strcmp(mtd, "/dev/mtd1"))
108	    isNvram = 1;
109	erase_info.length = mtd_info.erasesize;
110	cnt = 0;
111
112	for (erase_info.start = 0;
113	     erase_info.start < mtd_info.size;
114	     erase_info.start += mtd_info.erasesize) {
115		(void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
116		if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0) {
117		    cprintf("%s: erase failed, could be bad block, continue next block!\n", mtd);
118		        mtd_info.size -= mtd_info.erasesize;
119		    continue;
120			//perror(mtd);
121			//close(mtd_fd);
122			//return errno;
123		}
124		    cnt++;
125		    if(isNvram==1 && cnt>=4)
126		        break;
127	}
128	/* Foxconn Bob modified end on 09/06/2013 */
129
130	close(mtd_fd);
131	return 0;
132}
133
134extern int http_get(const char *server, char *buf, size_t count, off_t offset);
135
136/*
137 * Write a file to an MTD device
138 * @param	path	file to write or a URL
139 * @param	mtd	path to or partition name of MTD device
140 * @return	0 on success and errno on failure
141 */
142int
143mtd_write(const char *path, const char *mtd)
144{
145	int mtd_fd = -1;
146	mtd_info_t mtd_info;
147	erase_info_t erase_info;
148
149	struct sysinfo info;
150	struct trx_header trx;
151	unsigned long crc;
152
153	FILE *fp;
154	char *buf = NULL;
155	long count, len, off;
156	int ret = -1;
157
158	/* Examine TRX header */
159	if ((fp = fopen(path, "r")))
160		count = safe_fread(&trx, 1, sizeof(struct trx_header), fp);
161	else
162		count = http_get(path, (char *) &trx, sizeof(struct trx_header), 0);
163	if (count < sizeof(struct trx_header)) {
164		fprintf(stderr, "%s: File is too small (%ld bytes)\n", path, count);
165		goto fail;
166	}
167
168	/* Open MTD device and get sector size */
169	if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0 ||
170	    ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0 ||
171	    mtd_info.erasesize < sizeof(struct trx_header)) {
172		perror(mtd);
173		goto fail;
174	}
175
176	if (trx.magic != TRX_MAGIC ||
177	    trx.len > mtd_info.size ||
178	    trx.len < sizeof(struct trx_header)) {
179		fprintf(stderr, "%s: Bad trx header\n", path);
180		goto fail;
181	}
182
183
184	/* Allocate temporary buffer */
185	/* See if we have enough memory to store the whole file */
186	sysinfo(&info);
187	if (info.freeram >= trx.len) {
188		erase_info.length = ROUNDUP(trx.len, mtd_info.erasesize);
189		if (!(buf = malloc(erase_info.length)))
190			erase_info.length = mtd_info.erasesize;
191	}
192	/* fallback to smaller buffer */
193	else {
194		erase_info.length = mtd_info.erasesize;
195		buf = NULL;
196	}
197	if (!buf && (!(buf = malloc(erase_info.length)))) {
198		perror("malloc");
199		goto fail;
200	}
201
202	/* Calculate CRC over header */
203	crc = hndcrc32((uint8 *) &trx.flag_version,
204	               sizeof(struct trx_header) - OFFSETOF(struct trx_header, flag_version),
205	               CRC32_INIT_VALUE);
206
207	if (trx.flag_version & TRX_NO_HEADER)
208		trx.len -= sizeof(struct trx_header);
209
210	/* Write file or URL to MTD device */
211	for (erase_info.start = 0; erase_info.start < trx.len; erase_info.start += count) {
212		len = MIN(erase_info.length, trx.len - erase_info.start);
213		if ((trx.flag_version & TRX_NO_HEADER) || erase_info.start)
214			count = off = 0;
215		else {
216			count = off = sizeof(struct trx_header);
217			memcpy(buf, &trx, sizeof(struct trx_header));
218		}
219		if (fp)
220			count += safe_fread(&buf[off], 1, len - off, fp);
221		else
222			count += http_get(path, &buf[off], len - off, erase_info.start + off);
223		if (count < len) {
224			fprintf(stderr, "%s: Truncated file (actual %ld expect %ld)\n", path,
225				count - off, len - off);
226			goto fail;
227		}
228		/* Update CRC */
229		crc = hndcrc32((uint8 *)&buf[off], count - off, crc);
230		/* Check CRC before writing if possible */
231		if (count == trx.len) {
232			if (crc != trx.crc32) {
233				fprintf(stderr, "%s: Bad CRC\n", path);
234				goto fail;
235			}
236		}
237		/* Do it */
238		(void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
239		if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 ||
240		    write(mtd_fd, buf, count) != count) {
241			perror(mtd);
242			goto fail;
243		}
244	}
245
246#ifdef PLC
247  eval("gigle_util restart");
248  nvram_set("plc_pconfig_state", "2");
249  nvram_commit();
250#endif
251
252	printf("%s: CRC OK\n", mtd);
253	ret = 0;
254
255fail:
256	if (buf) {
257		/* Dummy read to ensure chip(s) are out of lock/suspend state */
258		(void) read(mtd_fd, buf, 2);
259		free(buf);
260	}
261
262	if (mtd_fd >= 0)
263		close(mtd_fd);
264	if (fp)
265		fclose(fp);
266	return ret;
267}
268