| /* |
| * Copyright (C) 2013 Mike Dunn <mikedunn@newsguy.com> |
| * |
| * This file is released under the terms of GPL v2 and any later version. |
| * See the file COPYING in the root directory of the source tree for details. |
| * |
| * |
| * This is a userspace Linux utility that, when run on the Treo 680, will |
| * program u-boot to flash. The docg4 driver *must* be loaded with the |
| * reliable_mode and ignore_badblocks parameters enabled: |
| * |
| * modprobe docg4 ignore_badblocks=1 reliable_mode=1 |
| * |
| * This utility writes the concatenated spl + u-boot image to the start of the |
| * mtd device in the format expected by the IPL/SPL. The image file and mtd |
| * device node are passed to the utility as arguments. The blocks must have |
| * been erased beforehand. |
| * |
| * When you compile this, note that it links to libmtd from mtd-utils, so ensure |
| * that your include and lib paths include this. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <mtd/mtd-user.h> |
| #include "libmtd.h" |
| |
| #define RELIABLE_BLOCKSIZE 0x10000 /* block capacity in reliable mode */ |
| #define STANDARD_BLOCKSIZE 0x40000 /* block capacity in normal mode */ |
| #define PAGESIZE 512 |
| #define PAGES_PER_BLOCK 512 |
| #define OOBSIZE 7 /* available to user (16 total) */ |
| |
| uint8_t ff_oob[OOBSIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| |
| /* this is the magic number the IPL looks for (ASCII "BIPO") */ |
| uint8_t page0_oob[OOBSIZE] = {'B', 'I', 'P', 'O', 0xff, 0xff, 0xff}; |
| |
| int main(int argc, char * const argv[]) |
| { |
| int devfd, datafd, num_blocks, block; |
| off_t file_size; |
| libmtd_t mtd_desc; |
| struct mtd_dev_info devinfo; |
| uint8_t *blockbuf; |
| char response[8]; |
| |
| if (argc != 3) { |
| printf("usage: %s <image file> <mtd dev node>\n", argv[0]); |
| return -EINVAL; |
| } |
| |
| mtd_desc = libmtd_open(); |
| if (mtd_desc == NULL) { |
| int errsv = errno; |
| fprintf(stderr, "can't initialize libmtd\n"); |
| return -errsv; |
| } |
| |
| /* open the spl image file and mtd device */ |
| datafd = open(argv[1], O_RDONLY); |
| if (datafd == -1) { |
| int errsv = errno; |
| perror(argv[1]); |
| return -errsv; |
| } |
| devfd = open(argv[2], O_WRONLY); |
| if (devfd == -1) { |
| int errsv = errno; |
| perror(argv[2]); |
| return -errsv; |
| } |
| if (mtd_get_dev_info(mtd_desc, argv[2], &devinfo) < 0) { |
| int errsv = errno; |
| perror(argv[2]); |
| return -errsv; |
| } |
| |
| /* determine the number of blocks needed by the image */ |
| file_size = lseek(datafd, 0, SEEK_END); |
| if (file_size == (off_t)-1) { |
| int errsv = errno; |
| perror("lseek"); |
| return -errsv; |
| } |
| num_blocks = (file_size + RELIABLE_BLOCKSIZE - 1) / RELIABLE_BLOCKSIZE; |
| file_size = lseek(datafd, 0, SEEK_SET); |
| if (file_size == (off_t)-1) { |
| int errsv = errno; |
| perror("lseek"); |
| return -errsv; |
| } |
| printf("The mtd partition contains %d blocks\n", devinfo.eb_cnt); |
| printf("U-boot will occupy %d blocks\n", num_blocks); |
| if (num_blocks > devinfo.eb_cnt) { |
| fprintf(stderr, "Insufficient blocks on partition\n"); |
| return -EINVAL; |
| } |
| |
| printf("IMPORTANT: These blocks must be in an erased state!\n"); |
| printf("Do you want to proceed?\n"); |
| scanf("%s", response); |
| if ((response[0] != 'y') && (response[0] != 'Y')) { |
| printf("Exiting\n"); |
| close(devfd); |
| close(datafd); |
| return 0; |
| } |
| |
| blockbuf = calloc(RELIABLE_BLOCKSIZE, 1); |
| if (blockbuf == NULL) { |
| int errsv = errno; |
| perror("calloc"); |
| return -errsv; |
| } |
| |
| for (block = 0; block < num_blocks; block++) { |
| int ofs, page; |
| uint8_t *pagebuf = blockbuf, *buf = blockbuf; |
| uint8_t *oobbuf = page0_oob; /* magic num in oob of 1st page */ |
| size_t len = RELIABLE_BLOCKSIZE; |
| int ret; |
| |
| /* read data for one block from file */ |
| while (len) { |
| ssize_t read_ret = read(datafd, buf, len); |
| if (read_ret == -1) { |
| int errsv = errno; |
| if (errno == EINTR) |
| continue; |
| perror("read"); |
| return -errsv; |
| } else if (read_ret == 0) { |
| break; /* EOF */ |
| } |
| len -= read_ret; |
| buf += read_ret; |
| } |
| |
| printf("Block %d: writing\r", block + 1); |
| fflush(stdout); |
| |
| for (page = 0, ofs = 0; |
| page < PAGES_PER_BLOCK; |
| page++, ofs += PAGESIZE) { |
| if (page & 0x04) /* Odd-numbered 2k page */ |
| continue; /* skipped in reliable mode */ |
| |
| ret = mtd_write(mtd_desc, &devinfo, devfd, block, ofs, |
| pagebuf, PAGESIZE, oobbuf, OOBSIZE, |
| MTD_OPS_PLACE_OOB); |
| if (ret) { |
| fprintf(stderr, |
| "\nmtd_write returned %d on block %d, ofs %x\n", |
| ret, block + 1, ofs); |
| return -EIO; |
| } |
| oobbuf = ff_oob; /* oob for subsequent pages */ |
| |
| if (page & 0x01) /* odd-numbered subpage */ |
| pagebuf += PAGESIZE; |
| } |
| } |
| |
| printf("\nDone\n"); |
| |
| close(devfd); |
| close(datafd); |
| free(blockbuf); |
| return 0; |
| } |