|  | /* | 
|  | * Freescale i.MX28 image generator | 
|  | * | 
|  | * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> | 
|  | * on behalf of DENX Software Engineering GmbH | 
|  | * | 
|  | * SPDX-License-Identifier:	GPL-2.0+ | 
|  | */ | 
|  |  | 
|  | #include <fcntl.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "compiler.h" | 
|  |  | 
|  | /* | 
|  | * Default BCB layout. | 
|  | * | 
|  | * TWEAK this if you have blown any OCOTP fuses. | 
|  | */ | 
|  | #define	STRIDE_PAGES		64 | 
|  | #define	STRIDE_COUNT		4 | 
|  |  | 
|  | /* | 
|  | * Layout for 256Mb big NAND with 2048b page size, 64b OOB size and | 
|  | * 128kb erase size. | 
|  | * | 
|  | * TWEAK this if you have different kind of NAND chip. | 
|  | */ | 
|  | uint32_t nand_writesize = 2048; | 
|  | uint32_t nand_oobsize = 64; | 
|  | uint32_t nand_erasesize = 128 * 1024; | 
|  |  | 
|  | /* | 
|  | * Sector on which the SigmaTel boot partition (0x53) starts. | 
|  | */ | 
|  | uint32_t sd_sector = 2048; | 
|  |  | 
|  | /* | 
|  | * Each of the U-Boot bootstreams is at maximum 1MB big. | 
|  | * | 
|  | * TWEAK this if, for some wild reason, you need to boot bigger image. | 
|  | */ | 
|  | #define	MAX_BOOTSTREAM_SIZE	(1 * 1024 * 1024) | 
|  |  | 
|  | /* i.MX28 NAND controller-specific constants. DO NOT TWEAK! */ | 
|  | #define	MXS_NAND_DMA_DESCRIPTOR_COUNT		4 | 
|  | #define	MXS_NAND_CHUNK_DATA_CHUNK_SIZE		512 | 
|  | #define	MXS_NAND_METADATA_SIZE			10 | 
|  | #define	MXS_NAND_COMMAND_BUFFER_SIZE		32 | 
|  |  | 
|  | struct mx28_nand_fcb { | 
|  | uint32_t		checksum; | 
|  | uint32_t		fingerprint; | 
|  | uint32_t		version; | 
|  | struct { | 
|  | uint8_t			data_setup; | 
|  | uint8_t			data_hold; | 
|  | uint8_t			address_setup; | 
|  | uint8_t			dsample_time; | 
|  | uint8_t			nand_timing_state; | 
|  | uint8_t			rea; | 
|  | uint8_t			rloh; | 
|  | uint8_t			rhoh; | 
|  | }			timing; | 
|  | uint32_t		page_data_size; | 
|  | uint32_t		total_page_size; | 
|  | uint32_t		sectors_per_block; | 
|  | uint32_t		number_of_nands;		/* Ignored */ | 
|  | uint32_t		total_internal_die;		/* Ignored */ | 
|  | uint32_t		cell_type;			/* Ignored */ | 
|  | uint32_t		ecc_block_n_ecc_type; | 
|  | uint32_t		ecc_block_0_size; | 
|  | uint32_t		ecc_block_n_size; | 
|  | uint32_t		ecc_block_0_ecc_type; | 
|  | uint32_t		metadata_bytes; | 
|  | uint32_t		num_ecc_blocks_per_page; | 
|  | uint32_t		ecc_block_n_ecc_level_sdk;	/* Ignored */ | 
|  | uint32_t		ecc_block_0_size_sdk;		/* Ignored */ | 
|  | uint32_t		ecc_block_n_size_sdk;		/* Ignored */ | 
|  | uint32_t		ecc_block_0_ecc_level_sdk;	/* Ignored */ | 
|  | uint32_t		num_ecc_blocks_per_page_sdk;	/* Ignored */ | 
|  | uint32_t		metadata_bytes_sdk;		/* Ignored */ | 
|  | uint32_t		erase_threshold; | 
|  | uint32_t		boot_patch; | 
|  | uint32_t		patch_sectors; | 
|  | uint32_t		firmware1_starting_sector; | 
|  | uint32_t		firmware2_starting_sector; | 
|  | uint32_t		sectors_in_firmware1; | 
|  | uint32_t		sectors_in_firmware2; | 
|  | uint32_t		dbbt_search_area_start_address; | 
|  | uint32_t		badblock_marker_byte; | 
|  | uint32_t		badblock_marker_start_bit; | 
|  | uint32_t		bb_marker_physical_offset; | 
|  | }; | 
|  |  | 
|  | struct mx28_nand_dbbt { | 
|  | uint32_t		checksum; | 
|  | uint32_t		fingerprint; | 
|  | uint32_t		version; | 
|  | uint32_t		number_bb; | 
|  | uint32_t		number_2k_pages_bb; | 
|  | }; | 
|  |  | 
|  | struct mx28_nand_bbt { | 
|  | uint32_t		nand; | 
|  | uint32_t		number_bb; | 
|  | uint32_t		badblock[510]; | 
|  | }; | 
|  |  | 
|  | struct mx28_sd_drive_info { | 
|  | uint32_t		chip_num; | 
|  | uint32_t		drive_type; | 
|  | uint32_t		tag; | 
|  | uint32_t		first_sector_number; | 
|  | uint32_t		sector_count; | 
|  | }; | 
|  |  | 
|  | struct mx28_sd_config_block { | 
|  | uint32_t			signature; | 
|  | uint32_t			primary_boot_tag; | 
|  | uint32_t			secondary_boot_tag; | 
|  | uint32_t			num_copies; | 
|  | struct mx28_sd_drive_info	drv_info[1]; | 
|  | }; | 
|  |  | 
|  | static inline uint32_t mx28_nand_ecc_size_in_bits(uint32_t ecc_strength) | 
|  | { | 
|  | return ecc_strength * 13; | 
|  | } | 
|  |  | 
|  | static inline uint32_t mx28_nand_get_ecc_strength(uint32_t page_data_size, | 
|  | uint32_t page_oob_size) | 
|  | { | 
|  | if (page_data_size == 2048) | 
|  | return 8; | 
|  |  | 
|  | if (page_data_size == 4096) { | 
|  | if (page_oob_size == 128) | 
|  | return 8; | 
|  |  | 
|  | if (page_oob_size == 218) | 
|  | return 16; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static inline uint32_t mx28_nand_get_mark_offset(uint32_t page_data_size, | 
|  | uint32_t ecc_strength) | 
|  | { | 
|  | uint32_t chunk_data_size_in_bits; | 
|  | uint32_t chunk_ecc_size_in_bits; | 
|  | uint32_t chunk_total_size_in_bits; | 
|  | uint32_t block_mark_chunk_number; | 
|  | uint32_t block_mark_chunk_bit_offset; | 
|  | uint32_t block_mark_bit_offset; | 
|  |  | 
|  | chunk_data_size_in_bits = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 8; | 
|  | chunk_ecc_size_in_bits  = mx28_nand_ecc_size_in_bits(ecc_strength); | 
|  |  | 
|  | chunk_total_size_in_bits = | 
|  | chunk_data_size_in_bits + chunk_ecc_size_in_bits; | 
|  |  | 
|  | /* Compute the bit offset of the block mark within the physical page. */ | 
|  | block_mark_bit_offset = page_data_size * 8; | 
|  |  | 
|  | /* Subtract the metadata bits. */ | 
|  | block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; | 
|  |  | 
|  | /* | 
|  | * Compute the chunk number (starting at zero) in which the block mark | 
|  | * appears. | 
|  | */ | 
|  | block_mark_chunk_number = | 
|  | block_mark_bit_offset / chunk_total_size_in_bits; | 
|  |  | 
|  | /* | 
|  | * Compute the bit offset of the block mark within its chunk, and | 
|  | * validate it. | 
|  | */ | 
|  | block_mark_chunk_bit_offset = block_mark_bit_offset - | 
|  | (block_mark_chunk_number * chunk_total_size_in_bits); | 
|  |  | 
|  | if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) | 
|  | return 1; | 
|  |  | 
|  | /* | 
|  | * Now that we know the chunk number in which the block mark appears, | 
|  | * we can subtract all the ECC bits that appear before it. | 
|  | */ | 
|  | block_mark_bit_offset -= | 
|  | block_mark_chunk_number * chunk_ecc_size_in_bits; | 
|  |  | 
|  | return block_mark_bit_offset; | 
|  | } | 
|  |  | 
|  | static inline uint32_t mx28_nand_mark_byte_offset(void) | 
|  | { | 
|  | uint32_t ecc_strength; | 
|  | ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize); | 
|  | return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) >> 3; | 
|  | } | 
|  |  | 
|  | static inline uint32_t mx28_nand_mark_bit_offset(void) | 
|  | { | 
|  | uint32_t ecc_strength; | 
|  | ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize); | 
|  | return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) & 0x7; | 
|  | } | 
|  |  | 
|  | static uint32_t mx28_nand_block_csum(uint8_t *block, uint32_t size) | 
|  | { | 
|  | uint32_t csum = 0; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < size; i++) | 
|  | csum += block[i]; | 
|  |  | 
|  | return csum ^ 0xffffffff; | 
|  | } | 
|  |  | 
|  | static struct mx28_nand_fcb *mx28_nand_get_fcb(uint32_t size) | 
|  | { | 
|  | struct mx28_nand_fcb *fcb; | 
|  | uint32_t bcb_size_bytes; | 
|  | uint32_t stride_size_bytes; | 
|  | uint32_t bootstream_size_pages; | 
|  | uint32_t fw1_start_page; | 
|  | uint32_t fw2_start_page; | 
|  |  | 
|  | fcb = malloc(nand_writesize); | 
|  | if (!fcb) { | 
|  | printf("MX28 NAND: Unable to allocate FCB\n"); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | memset(fcb, 0, nand_writesize); | 
|  |  | 
|  | fcb->fingerprint =			0x20424346; | 
|  | fcb->version =				0x01000000; | 
|  |  | 
|  | /* | 
|  | * FIXME: These here are default values as found in kobs-ng. We should | 
|  | * probably retrieve the data from NAND or something. | 
|  | */ | 
|  | fcb->timing.data_setup =		80; | 
|  | fcb->timing.data_hold =			60; | 
|  | fcb->timing.address_setup =		25; | 
|  | fcb->timing.dsample_time =		6; | 
|  |  | 
|  | fcb->page_data_size =		nand_writesize; | 
|  | fcb->total_page_size =		nand_writesize + nand_oobsize; | 
|  | fcb->sectors_per_block =	nand_erasesize / nand_writesize; | 
|  |  | 
|  | fcb->num_ecc_blocks_per_page =	(nand_writesize / 512) - 1; | 
|  | fcb->ecc_block_0_size =		512; | 
|  | fcb->ecc_block_n_size =		512; | 
|  | fcb->metadata_bytes =		10; | 
|  |  | 
|  | if (nand_writesize == 2048) { | 
|  | fcb->ecc_block_n_ecc_type =		4; | 
|  | fcb->ecc_block_0_ecc_type =		4; | 
|  | } else if (nand_writesize == 4096) { | 
|  | if (nand_oobsize == 128) { | 
|  | fcb->ecc_block_n_ecc_type =	4; | 
|  | fcb->ecc_block_0_ecc_type =	4; | 
|  | } else if (nand_oobsize == 218) { | 
|  | fcb->ecc_block_n_ecc_type =	8; | 
|  | fcb->ecc_block_0_ecc_type =	8; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (fcb->ecc_block_n_ecc_type == 0) { | 
|  | printf("MX28 NAND: Unsupported NAND geometry\n"); | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | fcb->boot_patch =			0; | 
|  | fcb->patch_sectors =			0; | 
|  |  | 
|  | fcb->badblock_marker_byte =	mx28_nand_mark_byte_offset(); | 
|  | fcb->badblock_marker_start_bit = mx28_nand_mark_bit_offset(); | 
|  | fcb->bb_marker_physical_offset = nand_writesize; | 
|  |  | 
|  | stride_size_bytes = STRIDE_PAGES * nand_writesize; | 
|  | bcb_size_bytes = stride_size_bytes * STRIDE_COUNT; | 
|  |  | 
|  | bootstream_size_pages = (size + (nand_writesize - 1)) / | 
|  | nand_writesize; | 
|  |  | 
|  | fw1_start_page = 2 * bcb_size_bytes / nand_writesize; | 
|  | fw2_start_page = (2 * bcb_size_bytes + MAX_BOOTSTREAM_SIZE) / | 
|  | nand_writesize; | 
|  |  | 
|  | fcb->firmware1_starting_sector =	fw1_start_page; | 
|  | fcb->firmware2_starting_sector =	fw2_start_page; | 
|  | fcb->sectors_in_firmware1 =		bootstream_size_pages; | 
|  | fcb->sectors_in_firmware2 =		bootstream_size_pages; | 
|  |  | 
|  | fcb->dbbt_search_area_start_address =	STRIDE_PAGES * STRIDE_COUNT; | 
|  |  | 
|  | return fcb; | 
|  |  | 
|  | err: | 
|  | free(fcb); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static struct mx28_nand_dbbt *mx28_nand_get_dbbt(void) | 
|  | { | 
|  | struct mx28_nand_dbbt *dbbt; | 
|  |  | 
|  | dbbt = malloc(nand_writesize); | 
|  | if (!dbbt) { | 
|  | printf("MX28 NAND: Unable to allocate DBBT\n"); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | memset(dbbt, 0, nand_writesize); | 
|  |  | 
|  | dbbt->fingerprint	= 0x54424244; | 
|  | dbbt->version		= 0x1; | 
|  |  | 
|  | return dbbt; | 
|  | } | 
|  |  | 
|  | static inline uint8_t mx28_nand_parity_13_8(const uint8_t b) | 
|  | { | 
|  | uint32_t parity = 0, tmp; | 
|  |  | 
|  | tmp = ((b >> 6) ^ (b >> 5) ^ (b >> 3) ^ (b >> 2)) & 1; | 
|  | parity |= tmp << 0; | 
|  |  | 
|  | tmp = ((b >> 7) ^ (b >> 5) ^ (b >> 4) ^ (b >> 2) ^ (b >> 1)) & 1; | 
|  | parity |= tmp << 1; | 
|  |  | 
|  | tmp = ((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 1) ^ (b >> 0)) & 1; | 
|  | parity |= tmp << 2; | 
|  |  | 
|  | tmp = ((b >> 7) ^ (b >> 4) ^ (b >> 3) ^ (b >> 0)) & 1; | 
|  | parity |= tmp << 3; | 
|  |  | 
|  | tmp = ((b >> 6) ^ (b >> 4) ^ (b >> 3) ^ | 
|  | (b >> 2) ^ (b >> 1) ^ (b >> 0)) & 1; | 
|  | parity |= tmp << 4; | 
|  |  | 
|  | return parity; | 
|  | } | 
|  |  | 
|  | static uint8_t *mx28_nand_fcb_block(struct mx28_nand_fcb *fcb) | 
|  | { | 
|  | uint8_t *block; | 
|  | uint8_t *ecc; | 
|  | int i; | 
|  |  | 
|  | block = malloc(nand_writesize + nand_oobsize); | 
|  | if (!block) { | 
|  | printf("MX28 NAND: Unable to allocate FCB block\n"); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | memset(block, 0, nand_writesize + nand_oobsize); | 
|  |  | 
|  | /* Update the FCB checksum */ | 
|  | fcb->checksum = mx28_nand_block_csum(((uint8_t *)fcb) + 4, 508); | 
|  |  | 
|  | /* Figure 12-11. in iMX28RM, rev. 1, says FCB is at offset 12 */ | 
|  | memcpy(block + 12, fcb, sizeof(struct mx28_nand_fcb)); | 
|  |  | 
|  | /* ECC is at offset 12 + 512 */ | 
|  | ecc = block + 12 + 512; | 
|  |  | 
|  | /* Compute the ECC parity */ | 
|  | for (i = 0; i < sizeof(struct mx28_nand_fcb); i++) | 
|  | ecc[i] = mx28_nand_parity_13_8(block[i + 12]); | 
|  |  | 
|  | return block; | 
|  | } | 
|  |  | 
|  | static int mx28_nand_write_fcb(struct mx28_nand_fcb *fcb, char *buf) | 
|  | { | 
|  | uint32_t offset; | 
|  | uint8_t *fcbblock; | 
|  | int ret = 0; | 
|  | int i; | 
|  |  | 
|  | fcbblock = mx28_nand_fcb_block(fcb); | 
|  | if (!fcbblock) | 
|  | return -1; | 
|  |  | 
|  | for (i = 0; i < STRIDE_PAGES * STRIDE_COUNT; i += STRIDE_PAGES) { | 
|  | offset = i * nand_writesize; | 
|  | memcpy(buf + offset, fcbblock, nand_writesize + nand_oobsize); | 
|  | } | 
|  |  | 
|  | free(fcbblock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int mx28_nand_write_dbbt(struct mx28_nand_dbbt *dbbt, char *buf) | 
|  | { | 
|  | uint32_t offset; | 
|  | int i = STRIDE_PAGES * STRIDE_COUNT; | 
|  |  | 
|  | for (; i < 2 * STRIDE_PAGES * STRIDE_COUNT; i += STRIDE_PAGES) { | 
|  | offset = i * nand_writesize; | 
|  | memcpy(buf + offset, dbbt, sizeof(struct mx28_nand_dbbt)); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int mx28_nand_write_firmware(struct mx28_nand_fcb *fcb, int infd, | 
|  | char *buf) | 
|  | { | 
|  | int ret; | 
|  | off_t size; | 
|  | uint32_t offset1, offset2; | 
|  |  | 
|  | size = lseek(infd, 0, SEEK_END); | 
|  | lseek(infd, 0, SEEK_SET); | 
|  |  | 
|  | offset1 = fcb->firmware1_starting_sector * nand_writesize; | 
|  | offset2 = fcb->firmware2_starting_sector * nand_writesize; | 
|  |  | 
|  | ret = read(infd, buf + offset1, size); | 
|  | if (ret != size) | 
|  | return -1; | 
|  |  | 
|  | memcpy(buf + offset2, buf + offset1, size); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void usage(void) | 
|  | { | 
|  | printf( | 
|  | "Usage: mxsboot [ops] <type> <infile> <outfile>\n" | 
|  | "Augment BootStream file with a proper header for i.MX28 boot\n" | 
|  | "\n" | 
|  | "  <type>	type of image:\n" | 
|  | "                 \"nand\" for NAND image\n" | 
|  | "                 \"sd\" for SD image\n" | 
|  | "  <infile>     input file, the u-boot.sb bootstream\n" | 
|  | "  <outfile>    output file, the bootable image\n" | 
|  | "\n"); | 
|  | printf( | 
|  | "For NAND boot, these options are accepted:\n" | 
|  | "  -w <size>    NAND page size\n" | 
|  | "  -o <size>    NAND OOB size\n" | 
|  | "  -e <size>    NAND erase size\n" | 
|  | "\n" | 
|  | "For SD boot, these options are accepted:\n" | 
|  | "  -p <sector>  Sector where the SGTL partition starts\n" | 
|  | ); | 
|  | } | 
|  |  | 
|  | static int mx28_create_nand_image(int infd, int outfd) | 
|  | { | 
|  | struct mx28_nand_fcb *fcb; | 
|  | struct mx28_nand_dbbt *dbbt; | 
|  | int ret = -1; | 
|  | char *buf; | 
|  | int size; | 
|  | ssize_t wr_size; | 
|  |  | 
|  | size = nand_writesize * 512 + 2 * MAX_BOOTSTREAM_SIZE; | 
|  |  | 
|  | buf = malloc(size); | 
|  | if (!buf) { | 
|  | printf("Can not allocate output buffer of %d bytes\n", size); | 
|  | goto err0; | 
|  | } | 
|  |  | 
|  | memset(buf, 0, size); | 
|  |  | 
|  | fcb = mx28_nand_get_fcb(MAX_BOOTSTREAM_SIZE); | 
|  | if (!fcb) { | 
|  | printf("Unable to compile FCB\n"); | 
|  | goto err1; | 
|  | } | 
|  |  | 
|  | dbbt = mx28_nand_get_dbbt(); | 
|  | if (!dbbt) { | 
|  | printf("Unable to compile DBBT\n"); | 
|  | goto err2; | 
|  | } | 
|  |  | 
|  | ret = mx28_nand_write_fcb(fcb, buf); | 
|  | if (ret) { | 
|  | printf("Unable to write FCB to buffer\n"); | 
|  | goto err3; | 
|  | } | 
|  |  | 
|  | ret = mx28_nand_write_dbbt(dbbt, buf); | 
|  | if (ret) { | 
|  | printf("Unable to write DBBT to buffer\n"); | 
|  | goto err3; | 
|  | } | 
|  |  | 
|  | ret = mx28_nand_write_firmware(fcb, infd, buf); | 
|  | if (ret) { | 
|  | printf("Unable to write firmware to buffer\n"); | 
|  | goto err3; | 
|  | } | 
|  |  | 
|  | wr_size = write(outfd, buf, size); | 
|  | if (wr_size != size) { | 
|  | ret = -1; | 
|  | goto err3; | 
|  | } | 
|  |  | 
|  | ret = 0; | 
|  |  | 
|  | err3: | 
|  | free(dbbt); | 
|  | err2: | 
|  | free(fcb); | 
|  | err1: | 
|  | free(buf); | 
|  | err0: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int mx28_create_sd_image(int infd, int outfd) | 
|  | { | 
|  | int ret = -1; | 
|  | uint32_t *buf; | 
|  | int size; | 
|  | off_t fsize; | 
|  | ssize_t wr_size; | 
|  | struct mx28_sd_config_block *cb; | 
|  |  | 
|  | fsize = lseek(infd, 0, SEEK_END); | 
|  | lseek(infd, 0, SEEK_SET); | 
|  | size = fsize + 4 * 512; | 
|  |  | 
|  | buf = malloc(size); | 
|  | if (!buf) { | 
|  | printf("Can not allocate output buffer of %d bytes\n", size); | 
|  | goto err0; | 
|  | } | 
|  |  | 
|  | ret = read(infd, (uint8_t *)buf + 4 * 512, fsize); | 
|  | if (ret != fsize) { | 
|  | ret = -1; | 
|  | goto err1; | 
|  | } | 
|  |  | 
|  | cb = (struct mx28_sd_config_block *)buf; | 
|  |  | 
|  | cb->signature = 0x00112233; | 
|  | cb->primary_boot_tag = 0x1; | 
|  | cb->secondary_boot_tag = 0x1; | 
|  | cb->num_copies = 1; | 
|  | cb->drv_info[0].chip_num = 0x0; | 
|  | cb->drv_info[0].drive_type = 0x0; | 
|  | cb->drv_info[0].tag = 0x1; | 
|  | cb->drv_info[0].first_sector_number = sd_sector + 4; | 
|  | cb->drv_info[0].sector_count = (size - 4) / 512; | 
|  |  | 
|  | wr_size = write(outfd, buf, size); | 
|  | if (wr_size != size) { | 
|  | ret = -1; | 
|  | goto err1; | 
|  | } | 
|  |  | 
|  | ret = 0; | 
|  |  | 
|  | err1: | 
|  | free(buf); | 
|  | err0: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int parse_ops(int argc, char **argv) | 
|  | { | 
|  | int i; | 
|  | int tmp; | 
|  | char *end; | 
|  | enum param { | 
|  | PARAM_WRITE, | 
|  | PARAM_OOB, | 
|  | PARAM_ERASE, | 
|  | PARAM_PART, | 
|  | PARAM_SD, | 
|  | PARAM_NAND | 
|  | }; | 
|  | int type; | 
|  |  | 
|  | if (argc < 4) | 
|  | return -1; | 
|  |  | 
|  | for (i = 1; i < argc; i++) { | 
|  | if (!strncmp(argv[i], "-w", 2)) | 
|  | type = PARAM_WRITE; | 
|  | else if (!strncmp(argv[i], "-o", 2)) | 
|  | type = PARAM_OOB; | 
|  | else if (!strncmp(argv[i], "-e", 2)) | 
|  | type = PARAM_ERASE; | 
|  | else if (!strncmp(argv[i], "-p", 2)) | 
|  | type = PARAM_PART; | 
|  | else	/* SD/MMC */ | 
|  | break; | 
|  |  | 
|  | tmp = strtol(argv[++i], &end, 10); | 
|  | if (tmp % 2) | 
|  | return -1; | 
|  | if (tmp <= 0) | 
|  | return -1; | 
|  |  | 
|  | if (type == PARAM_WRITE) | 
|  | nand_writesize = tmp; | 
|  | if (type == PARAM_OOB) | 
|  | nand_oobsize = tmp; | 
|  | if (type == PARAM_ERASE) | 
|  | nand_erasesize = tmp; | 
|  | if (type == PARAM_PART) | 
|  | sd_sector = tmp; | 
|  | } | 
|  |  | 
|  | if (strcmp(argv[i], "sd") && strcmp(argv[i], "nand")) | 
|  | return -1; | 
|  |  | 
|  | if (i + 3 != argc) | 
|  | return -1; | 
|  |  | 
|  | return i; | 
|  | } | 
|  |  | 
|  | int main(int argc, char **argv) | 
|  | { | 
|  | int infd, outfd; | 
|  | int ret = 0; | 
|  | int offset; | 
|  |  | 
|  | offset = parse_ops(argc, argv); | 
|  | if (offset < 0) { | 
|  | usage(); | 
|  | ret = 1; | 
|  | goto err1; | 
|  | } | 
|  |  | 
|  | infd = open(argv[offset + 1], O_RDONLY); | 
|  | if (infd < 0) { | 
|  | printf("Input BootStream file can not be opened\n"); | 
|  | ret = 2; | 
|  | goto err1; | 
|  | } | 
|  |  | 
|  | outfd = open(argv[offset + 2], O_CREAT | O_TRUNC | O_WRONLY, | 
|  | S_IRUSR | S_IWUSR); | 
|  | if (outfd < 0) { | 
|  | printf("Output file can not be created\n"); | 
|  | ret = 3; | 
|  | goto err2; | 
|  | } | 
|  |  | 
|  | if (!strcmp(argv[offset], "sd")) | 
|  | ret = mx28_create_sd_image(infd, outfd); | 
|  | else if (!strcmp(argv[offset], "nand")) | 
|  | ret = mx28_create_nand_image(infd, outfd); | 
|  |  | 
|  | close(outfd); | 
|  | err2: | 
|  | close(infd); | 
|  | err1: | 
|  | return ret; | 
|  | } |