C H A P T E R  2

User Flash

This chapter describes the user flash driver for the onboard flash PROMs and how to use it. The Netra CP2000/CP2100 series boards are equipped with user flash memory. This chapter includes the following sections:


User Flash Usage and Implementation

The customer can use the flash memory for various purposes such as storage for RTOS, user data storage, OpenBoot PROM information or to store dropins. Dropins simplify customizing a system for the user.

When OpenBoot PROM in system flash is corrupted, and if a backup copy of OpenBoot PROM is stored in user flash, you can switch the SMC switch to boot the OpenBoot PROM from the user flash and then use flash update to get a good OpenBoot PROM image back into the system flash.

A user flash switch SW2501 determines whether the user flash is detected during OpenBoot PROM boot and whether or not it is write-enabled. See Switch Settings for more information.

The user flash includes flash PROM chips that can be programmed by users (see TABLE 2-1).

TABLE 2-1 User Flash Implementation

CompactPCI Board

Implementation

Total Memory Size

Netra CP2040

Two user flash modules

2 X 4MB

Netra CP2060

One user flash module

1 x 4 MB

Netra CP2080

One user flash module

1 x 4 MB

Netra CP2140

Two user flash modules

2 x 4MB

Netra CP2160

One user flash module

1 x 8MB



User Flash Address Range

The address range for 1 x 4MB user flash : 0x1ff.f040.0000 to 0x1ff.f07f.ffff.

The address range for 1 X 8MB flash: 0x1ff.f040.0000 to 0x1ff.f0bf.ffff


System Compatibility

TABLE 2-2 lists the compatible releases that support the user flash driver.

TABLE 2-2 Compatible Releases That Support the User Flash Driver

CompactPCI Board

Component

Compatible Release

Netra CP2060

Hardware

 

OpenBoot PROM

 

 

 

 

Operating environment

 

All board versions

 

OpenBoot PROM Release 4.0.45

SMC Firmware Release 3.10.5

FPGA Version 1.2

PLD Version 4.2

All the above versions or other versions that support this feature

Solaris 8 1/01 operating environment or other versions that support this feature

 

Netra CP2080

Hardware

 

OpenBoot PROM

 

 

 

 

Operating environment

 

All board versions

 

OpenBoot PROM Release 4.0.45

SMC Firmware Release 3.10.5

FPGA Version 1.2

PLD Version 4.2

All the above versions or other versions that support this feature

Solaris 8 1/01 operating environment or other versions that support this feature

 

Netra CP2040

Hardware

 

OBP

 

 

 

 

Operating environment

All board versions

 

OpenBoot PROM Release 4.0.27

SMC Firmware Release 3.4.4

FPGA Version 1.0

PLD Version 1.2

All the above versions or other versions that support this feature

Solaris 8 1/01 operating environment or other versions that support this feature

 

Netra CP2140

Hardware

 

OBP

 

 

 

 

Operating environment

 

All board versions

 

OpenBoot PROM Release 4.0.3

SMC Firmware Release 3.4.10

FPGA Version 1.0

PLD Version 1.3

All the above versions or other versions that support this feature

Solaris 8 2/02 operating environment or other versions that support this feature

 

Netra CP2160

Hardware

 

OBP

All board versions

 

OpenBoot PROM Release 4.0.11

SMC Firmware Release 4.0.6

FPGA Version 1.2

PLD Version 4.2

All the above versions or other versions that support this feature

Solaris 8 2/02 operating environment or other versions that support this feature


 


User Flash Driver

The uflash is the device driver for flash PROM devices on the Netra CP2000/CP2100 series boards. Access to the driver is carried out through open, read, write, pread, pwrite and ioctl system interfaces.

Depending on the platform, one or more of these devices are supported. There is one logical device file for each physical device that can be accessed from applications. Users can use these devices for storing applications and data.

When multiple user flash devices are supported by the system, an instance of the driver is loaded per device. The driver blocks any reads to the device, while a write is in progress. Multiple, concurrent reads can go through to the same device at the same time. Writes to a device occur independently of the others. All read and write operations are supported at this time.

Access to the device normally happens a byte at a time. Devices support buffers to speed up writes. The driver automatically switches to the buffer mode, when the feature is available and the request is of sufficient size.

Devices also support erase and lock features. Applications can use them through the IOCTL interface. Devices are divided into logical blocks. Applications that issue these operations also supply a block number or a range of blocks that are a target of these operations. Locks are preserved across reboots. Locking a block prevents an erase or write operation on that block.

Switch Settings

The user flash modules on the Netra boards are write enabled by default. The user flash is detected during OpenBoot PROM boot by default.

See the following documents for more details on switch settings:

OpenBoot PROM Device Tree and Properties

This section provides information on the user flash OpenBoot PROM device node and its properties.

User flash OpenBoot PROM device node:

/pci@1f,0/pci@1,1/ebus@1/flashprom@10,800000/pci@1f,0/pci@1,1/ebus@1/flashprom@10,400000

See TABLE 2-3 for the user flash node properties.

TABLE 2-3 User Flash Node Properties

Property

Description/Value

compatible

user flash

user

 

reg

00000010 00400000 00400000

block-size

00010000

dcode-offset

00000002

blocks-per-bank

00000020

model

SUNW,yyy-yyyy



User Flash Packages

The user flash packages are as follows:

These packages are available with the rest of the software on the CP2000 Supplemental CD 4.0 for Solaris 8.

User Flash Device Files

The user flash device files are as follows:

Interface (Header) File

The user flash header file is located in the following path:

/usr/include/sys/uflash_if.h

Application Programming Interface

Access to the user flash device from the Solaris operating environment is through a C program. No command-line tool is available. User programs open these device files and then issue read, write, or ioctl commands to use the user flash device.

The systems calls are listed below in TABLE 2-4.

TABLE 2-4 System Calls

Call

Description

read(), pread()

reads devices

pwrite()

writes devices

ioctl()

erases device, queries device parameters


The ioctl commands are listed below.

#define UIOCIBLK 	(uflashIOC|0) 	/* identify */
#define UIOCQBLK 	(uflashIOC|1) 	/* query a block */
#define UIOCLBLK 	(uflashIOC|2) 	/* lock a block */
#define UIOCMLCK 	(uflashIOC|3) 	/* master lock */
#define UIOCCLCK 	(uflashIOC|4) 	/* clear all locks */
#define UIOCEBLK 	(uflashIOC|5) 	/* erase a block */
#define UIOCEALL 	(uflashIOC|6) 	/* erase all unlocked blocks */
#define UIOCEFUL 	(uflashIOC|7) 	/* erase full chip */

Structures to Use in IOCTL Arguments

PROM Information Structure

The PROM information structure holds device information returned by the driver in response to an identify command.

CODE EXAMPLE 2-1 PROM Information Structure
/*
 * PROM info structure.
 */
typedef struct {
        uint16_t        mfr_id;            /* manufacturer id */
        uint16_t        dev_id;            /* device id */
        /* allow future expansion */
        int8_t          blk_status[256];   /* blks status filled by driver */
        int32_t         blk_num;           /* total # of blocks */
        int32_t         blk_size;          /* # of bytes per block */
} uflash_info_t;

User Flash User Interface Structure

The user flash user interface structure holds user parameters to commands such as erase.

CODE EXAMPLE 2-2 User Flash Interface Structure
/*
 * uflash user interface structure.
 */
typedef struct {
        int             blk_num;
        int             num_of_blks;
        uflash_info_t   info;              /* to be filled by the driver */
} uflash_if_t;

Errors

EINVAL

Application passed one or more incorrect arguments to the system call.

EACCESS

Write or Erase operation was attempted on a locked block.

ECANCELLED

A hardware malfunction has been detected. Normally, retrying the command should fix this problem. If the problem persists, power cycling the system may be necessary.

ENXIO

This error indicates problems with the driver state. Power cycle of the system or reinstallation of driver may be necessary.

EFAULT

An error was encountered when copying arguments between the application and driver (kernel) space.

ENOMEM

System was low on memory when the driver attempted to acquire it.



Example Programs

Example programs are provided in this section for the following actions on user flash device:

  • Read
  • Write
  • Erase
  • Block Erase

Read Example Program

CODE EXAMPLE 2-3 contains the Read Action on the user flash device.

CODE EXAMPLE 2-3 Read Action on User Flash Device
/*
* uflash_read.c
 * An example that shows how to read user flash
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <uflash_if.h>
char *uflash0 = "/dev/uflash0";
char *uflash1 = "/dev/uflash1";
int ufd0, ufd1;
uflash_if_t ufif0, ufif1;
char *buf0;
char *buf1;
char *module;
static int
uflash_init() {
  char *buf0 = malloc(ufd0.info.blk_size);
  char *buf1 = malloc(ufd1.info.blk_size);
if (!buf0 || !buf1) {
    printf("%s: cannot allocate memory\n", module);
    return(-1);
  }
/* open device(s) */
  if ((ufd0 = open(uflash0, O_RDWR)) == -1 ) {
    perror("uflash0: ");
  }
if ((ufd1 = open(uflash1, O_RDWR)) == -1 ) {
    perror("uflash1: ");
  }
if ((ufd0 == -1) && (ufd1 == -1)) {
    printf("\n%s: cannot open uflash devices\n");
    exit(1);
  }
if (ufd0 == -1)
    ufd0 = 0;
  if (ufd1 == -1)
    ufd1 = 0;
/* get uflash sizes */
  if (ufd0 && ioctl(ufd0, UIOCIBLK, &ufif0) == -1 ) {
    perror("ioctl(ufd0, UIOCIBLK): ");
    exit(1);
  }
if (ufd1 && ioctl(ufd1, UIOCIBLK, &ufif1) == -1 ) {
    perror("ioctl(ufd1, UIOCIBLK): ");
    exit(1);
  }
if (ufd0) {
    printf("%s: \n", uflash0);
    printf("manfacturer id = 0x%p\n", ufd0.info.mfr_id);
    printf("device id = 0x%p\n", ufd0.info.dev_id);
    printf("number of blocks = 0x%p", ufd0.info.blk_num); 
    printf("block size = 0x%p"  ufd0.info.blk_size);
  }
if (ufd1) {
    printf("%s: \n", uflash1);
    printf("manfacturer id = 0x%p\n", ufd1.info.mfr_id);
    printf("device id = 0x%p\n", ufd1.info.dev_id);
    printf("number of blocks = 0x%p", ufd1.info.blk_num); 
    printf("block size = 0x%p"  ufd1.info.blk_size);
  }
}
static int
uflash_uninit() {
  if (ufd0)
    close(ufd0);
  if (ufd1)
    close(ufd1);
cleanup:
  if (buf0)
    free(buf0);
  if (buf1)
    free(buf1);
}
static int
uflash_read() {
  /* read block 0 of user flash 0 */
  if (pread(ufd0, buf0, ufd0.info.blk_size, 0) != ufd0.info.blk_size)
        perror("uflash0:read");
/* read block 1 of user flash 1 */
  if (pread(ufd1, buf1, ufd1.info.blk_size, ufd0.info.blk_size)
  	!= ufd1.info.blk_size)
        perror("uflash1:read");
return(0);
}
main() {
  int ret;
module = argv[0];
ret = uflash_init();
if (!ret)
    uflash_read();
uflash_uninit();
} 

Write Example Program

CODE EXAMPLE 2-4 contains the Write Action on the user flash device.

CODE EXAMPLE 2-4 Write Action on User Flash Device
/*
 * uflash_write.c
 * An example that shows how to write user flash
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <uflash_if.h>
char *uflash0 = "/dev/uflash0";
char *uflash1 = "/dev/uflash1";
int ufd0, ufd1;
uflash_if_t ufif0, ufif1;
char *buf0;
char *buf1;
char *module;
static int
uflash_init() {
  char *buf0 = malloc(ufd0.info.blk_size);
  char *buf1 = malloc(ufd1.info.blk_size);
if (!buf0 || !buf1) {
    printf("%s: cannot allocate memory\n", module);
    return(-1);
  }
/* open device(s) */
  if ((ufd0 = open(uflash0, O_RDWR)) == -1 ) {
    perror("uflash0: ");
  }
if ((ufd1 = open(uflash1, O_RDWR)) == -1 ) {
    perror("uflash1: ");
  }
if ((ufd0 == -1) && (ufd1 == -1)) {
    printf("\n%s: cannot open uflash devices\n");
    exit(1);
  }
if (ufd0 == -1)
    ufd0 = 0;
  if (ufd1 == -1)
    ufd1 = 0;
/* get uflash sizes */
  if (ufd0 && ioctl(ufd0, UIOCIBLK, &ufif0) == -1 ) {
    perror("ioctl(ufd0, UIOCIBLK): ");
    exit(1);
  }
if (ufd1 && ioctl(ufd1, UIOCIBLK, &ufif1) == -1 ) {
    perror("ioctl(ufd1, UIOCIBLK): ");
    exit(1);
  }
if (ufd0) {
    printf("%s: \n", uflash0);
    printf("manfacturer id = 0x%p\n", ufd0.info.mfr_id);
    printf("device id = 0x%p\n", ufd0.info.dev_id);
    printf("number of blocks = 0x%p", ufd0.info.blk_num); 
    printf("block size = 0x%p"  ufd0.info.blk_size);
  }
if (ufd1) {
    printf("%s: \n", uflash1);
    printf("manfacturer id = 0x%p\n", ufd1.info.mfr_id);
    printf("device id = 0x%p\n", ufd1.info.dev_id);
    printf("number of blocks = 0x%p", ufd1.info.blk_num); 
    printf("block size = 0x%p"  ufd1.info.blk_size);
  }
}
static int
uflash_uninit() {
  if (ufd0)
    close(ufd0);
  if (ufd1)
    close(ufd1);
cleanup:
  if (buf0)
    free(buf0);
  if (buf1)
    free(buf1);
}
static int
uflash_write() {
  int i;
/* write some pattern to the buffers */
  for (i = 0; i < ufd0.info.blk_size; i += sizeof(int))
        *((int *) (buf0 + i)) = 0xDEADBEEF;
for (i = 0; i < ufd1.info.blk_size; i += sizeof(int))
        *((int *) (buf1 + i)) = 0xDEADBEEF;
/* write block 0 of user flash 0 */
  if (pwrite(ufd0, buf0, ufd0.info.blk_size, 0) != ufd0.info.blk_size)
        perror("uflash0:write");
/* write block 1 of user flash 1 */
  if (pwrite(ufd1, buf1, ufd1.info.blk_size, ufd0.info.blk_size)
  	!= ufd1.info.blk_size)
        perror("uflash1:write");
return(0);
}
main() {
  int ret;
module = argv[0];
ret = uflash_init();
if (!ret)
    uflash_write();
uflash_uninit();
} 

Erase Example Program

CODE EXAMPLE 2-5 contains the Erase Action on the User Flash Device.

CODE EXAMPLE 2-5 Erase Action on User Flash Device
/*
 * uflash_erase.c
 * An example that shows how to erase user flash
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <uflash_if.h>
char *uflash0 = "/dev/uflash0";
char *uflash1 = "/dev/uflash1";
int ufd0, ufd1;
uflash_if_t ufif0, ufif1;
char *module;
static int
uflash_init() {
 /* open device(s) */
  if ((ufd0 = open(uflash0, O_RDWR)) == -1 ) {
    perror("uflash0: ");
  }
if ((ufd1 = open(uflash1, O_RDWR)) == -1 ) {
    perror("uflash1: ");
  }
if ((ufd0 == -1) && (ufd1 == -1)) {
    printf("\n%s: cannot open uflash devices\n");
    exit(1);
  }
if (ufd0 == -1)
    ufd0 = 0;
  if (ufd1 == -1)
    ufd1 = 0;
/* get uflash sizes */
  if (ufd0 && ioctl(ufd0, UIOCIBLK, &ufif0) == -1 ) {
    perror("ioctl(ufd0, UIOCIBLK): ");
    exit(1);
  }
if (ufd1 && ioctl(ufd1, UIOCIBLK, &ufif1) == -1 ) {
    perror("ioctl(ufd1, UIOCIBLK): ");
    exit(1);
  }
if (ufd0) {
    printf("%s: \n", uflash0);
    printf("manfacturer id = 0x%p\n", ufd0.info.mfr_id);
    printf("device id = 0x%p\n", ufd0.info.dev_id);
    printf("number of blocks = 0x%p", ufd0.info.blk_num); 
    printf("block size = 0x%p"  ufd0.info.blk_size);
  }
if (ufd1) {
    printf("%s: \n", uflash1);
    printf("manfacturer id = 0x%p\n", ufd1.info.mfr_id);
    printf("device id = 0x%p\n", ufd1.info.dev_id);
    printf("number of blocks = 0x%p", ufd1.info.blk_num); 
    printf("block size = 0x%p"  ufd1.info.blk_size);
  }
}
static int
uflash_uninit() {
  if (ufd0)
    close(ufd0);
  if (ufd1)
    close(ufd1);
}
static int
uflash_erase() {   
  if (ufd0 && ioctl(ufd0, UIOCEFUL, &ufd0) == -1 ) {
    perror("ioctl(ufd0, UIOCEFUL): ");
    return(-1);
  }
  printf("\nerase successful on %s\n", uflash0);
if (ufd1 && ioctl(ufd1, UIOCEFUL, &ufd1) == -1 ) {
    perror("ioctl(ufd1, UIOCEFUL): ");
    return(-1);
  }
  dprintf("\nerase successful on %s\n", uflash1);
return(0);
}
main() {
  int ret;
module = argv[0];
ret = uflash_init();
if (!ret)
    uflash_erase();
uflash_uninit();
} 

Block Erase Example Program

CODE EXAMPLE 2-6 contains the Block Erase Action on the user flash device.

CODE EXAMPLE 2-6 Block Erase Action on User Flash Device
/*
 * uflash_blockerase.c
 * An example that shows how to erase block(s) of user flash
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <uflash_if.h>
char *uflash0 = "/dev/uflash0";
char *uflash1 = "/dev/uflash1";
int ufd0, ufd1;
uflash_if_t ufif0, ufif1;
char *module;
static int
uflash_init() {
 /* open device(s) */
  if ((ufd0 = open(uflash0, O_RDWR)) == -1 ) {
    perror("uflash0: ");
  }
if ((ufd1 = open(uflash1, O_RDWR)) == -1 ) {
    perror("uflash1: ");
  }
if ((ufd0 == -1) && (ufd1 == -1)) {
    printf("\n%s: cannot open uflash devices\n");
    exit(1);
  }
if (ufd0 == -1)
    ufd0 = 0;
  if (ufd1 == -1)
    ufd1 = 0;
/* get uflash sizes */
  if (ufd0 && ioctl(ufd0, UIOCIBLK, &ufif0) == -1 ) {
    perror("ioctl(ufd0, UIOCIBLK): ");
    exit(1);
  }
if (ufd1 && ioctl(ufd1, UIOCIBLK, &ufif1) == -1 ) {
    perror("ioctl(ufd1, UIOCIBLK): ");
    exit(1);
  }
if (ufd0) {
    printf("%s: \n", uflash0);
    printf("manfacturer id = 0x%p\n", ufd0.info.mfr_id);
    printf("device id = 0x%p\n", ufd0.info.dev_id);
    printf("number of blocks = 0x%p", ufd0.info.blk_num); 
    printf("block size = 0x%p"  ufd0.info.blk_size);
  }
if (ufd1) {
    printf("%s: \n", uflash1);
    printf("manfacturer id = 0x%p\n", ufd1.info.mfr_id);
    printf("device id = 0x%p\n", ufd1.info.dev_id);
    printf("number of blocks = 0x%p", ufd1.info.blk_num); 
    printf("block size = 0x%p"  ufd1.info.blk_size);
  }
}
static int
uflash_uninit() {
  if (ufd0)
    close(ufd0);
  if (ufd1)
    close(ufd1);
}
static int
uflash_blockerase() {
  /* erase 2 blocks starting from block 1 of user flash 0 */
  uf0.blk_num = 1;
  uf0.num_of_blks = 2;
  if (ufd0 && ioctl(ufd0, UIOCEBLK, &ufd0) == -1 ) {
    perror("ioctl(ufd0, UIOCEBLK): ");
    return(-1);
  }
  printf("\nblockerase successful on %s\n", uflash0);
/* erase 4 blocks starting from block 5 of user flash 1 */
  uf1.blk_num = 5;
  uf1.num_of_blks = 4;
  if (ufd1 && ioctl(ufd1, UIOCEBLK, &ufd1) == -1 ) {
    perror("ioctl(ufd1, UIOCEBLK): ");
    return(-1);
  }
  printf("\nblockerase successful on %s\n", uflash1);
return(0);
}
main() {
  int ret;
module = argv[0];
ret = uflash_init();
if (!ret)
    uflash_blockerase();
uflash_uninit();
} 

Sample User Flash Application Program

You can use the following program to test the user flash device and driver. This program also demonstrates how this device can be used.

CODE EXAMPLE 2-7 Sample User Flash Application Program
/*
 *
 *	   This application program demonstrates the user program
 *	   interface to the User Flash PROM driver.
 *
 *	   One can read or write a number of bytes up to the size of
 *	   the user PROM by means of pread() and pwrite() calls.
 *	   All other functions of the PROM can be reached by the means
 *	   of ioctl() calls such as:
 *	   	-) identify the chip,
 *	   	-) query block,
 *		-) lock block/unlock block,
 *		-) master lock,
 *		-) erase block, erase all unlocked blocks, and 
 *		   erase whole PROM
 *	   Please note that not all of the above ioctl calls are 
 *	   available for all flash PROMs. It is the user's responsibility
 *	   to find out the features of a given PROM. The type, block size,
 *	   and number of blocks of the PROM are returned by 
 *	   "identify" ioctl().
 *
 *	   The pwrite() erases the block[s] and then does the
 *	   writing.
 *	   The driver uses the buffered write. If the buffered write
 * 	   is not supported in a particular PROM, the non-buffered
 *	   writes are used instead. The buffered write is 15 folds
 *	   faster than the non-buffered write. 
 *
 *	   Use the following line to compile your custom application
 *	   programs:
 *	        make uflash_test
 */
 
#pragma ident   "@(#)uflash_test.c 1.3     99/08/03 SMI"
 
#include <stdio.h>
#include <sys/signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/stream.h>
#include "uflash_if.h"
/*
      * PROM size: 4 or 8 MBytes
      * Uncomment the right block
      */
     #if 1
     #define PROM_SIZE 0x400000 /* 4 MBytes */
     #endif
     #if 0
     #define PROM_SIZE 0x800000 /* 8 MBytes */
     #endif
static char *help[14] = {
	"0 -- read     user flash PROM",
	"1 -- write    user flash PROM",
	"2 -- identify user flash PROM",
	"3 -- query    blocks",
	"4 -- lock     blocks",
	"5 -- master   lock",
	"6 -- clear    all locks",
	"7 -- erase    blocks",
	"8 -- erase    all unlocked blocks",
	"9 -- erase    whole PROM",
	"a -- switch   PROMs",
	"q -- quit",
	"?/h -- display this menu",
	""
};
 
/*char            get_cmd(); */
 
static char
get_cmd()
{
		char    buf[10];
		gets(buf);
		return (buf[0]);
}
 
/*
 * Main 
 */
main(int argc, char *argv[]) 
{
	int 			n_byte;			/* returned from pread/pwrite */
	int 			size, offset, pat;
	int			fd0, fd1, h, i;
	int			fd, prom_id; 	
	uflash_if_t			uflash_if;
	caddr_t 	r_buf, 	w_buf;
	char 		*devname0 = "/dev/uflash0";
	char 		*devname1 = "/dev/uflash1";
	char		c;
 
	/*
	 * Assume that the PROM size is 4 MB.
	 */
	r_buf = (caddr_t)malloc(PROM_SIZE);
	w_buf = (caddr_t)malloc(PROM_SIZE);
 
	/*
	 * Open the user flash PROM #0.
	 */
	if ((fd0 = open(devname0, O_RDWR)) < 0) {
		fprintf(stderr, "couldn't open device: %s\n", devname0);
		exit(1);
	}
	/*
	 * Open the user flash PROM #1.
	 */
	if ((fd1 = open(devname1, O_RDWR)) < 0) {
		fprintf(stderr, "couldn't open device: %s\n", devname1);
		exit(1);
	}
 
	/* set the default PROM */
	prom_id = 0;
	fd = fd0;
 
	/* let them know about the help menu */
        fprintf(stderr, "Enter <h> or <?> for help on commands\n");
 
	while (1) {
		fprintf(stderr, "[%d]command> ", prom_id);
 
		switch(get_cmd()) {
		case 'q':
			goto getout;
		
		case 'h':
		case '?':
			h = 0;
			while (*help[h]){
				fprintf(stderr, "	%s\n", help[h]);
				h++;
			}
			break;
		
		case 'a':		/* switch PROM */
			fd = (fd == fd0)? fd1: fd0;
			prom_id = (prom_id == 1)? 0: 1;
			break;
 
		case '9':       /* erase the whole flash PROM */
			fprintf(stderr,
                                "Are you sure?[y/n]");
                        scanf ("%c", &c);
 
			if (c != 'y')
				continue;
			if (ioctl(fd, UIOCEFUL, &uflash_if) == -1)
                                goto getout;
			break;
 
		case '8':       /* erase all unlocked flash PROM blocks */
			/*
			 * This ioctl is valid only for those
			 * chips that have query command.
			 */
			if (ioctl(fd, UIOCEALL, &uflash_if) == -1)
                                goto getout;
			break;
 
		case '7':       /* erase flash PROM block */
			fprintf(stderr,
                                "Enter PROM block number[0, 31]> ");
                        scanf ("%d", &uflash_if.blk_num);
 
			fprintf(stderr, 
				"Enter number of block> ");
			scanf ("%d", &uflash_if.num_of_blks);
 
			if (ioctl(fd, UIOCEBLK, &uflash_if) == -1)
                                goto getout;
			break;
 
		case '6':       /* clear all locks */
			/* on certain PROMs */
			if (ioctl(fd, UIOCCLCK, &uflash_if) == -1)
                                goto getout;
			break;
 
		case '5':       /* master lock */
			/* on certain PROMs */
			if (ioctl(fd, UIOCMLCK, &uflash_if) == -1)
                                goto getout;
			break;
 
		case '4':       /* lock flash PROM block */
			/* on certain PROMs */
			fprintf(stderr,
                                "Enter PROM block number[0, 31]> ");
                        scanf ("%d", &uflash_if.blk_num);
 
			fprintf(stderr, 
				"Enter number of block> ");
			scanf ("%d", &uflash_if.num_of_blks);
 
			if (ioctl(fd, UIOCLBLK, &uflash_if) == -1)
                                goto getout;
			break;
 
		case '3':       /* query flash PROM */
			/* on certain PROMs */
			fprintf(stderr,
                                "Enter PROM block number[0, 31]> ");
                        scanf ("%d", &uflash_if.blk_num);
 
			fprintf(stderr, 
				"Enter number of block> ");
			scanf ("%d", &uflash_if.num_of_blks);
 
			if (ioctl(fd, UIOCQBLK, &uflash_if) == -1)
                                goto getout;
			for (i = uflash_if.blk_num; 
				i < (uflash_if.blk_num+uflash_if.num_of_blks); 
				i++) 
			{
				fprintf(stderr, "block[%d] status = %x\n", 
					i, uflash_if.info.blk_status[i] & 0xF);
			}
			break;
 
		case '2':       /* identify flash PROM */
			if (ioctl(fd, UIOCIBLK, &uflash_if) == -1)
                                goto getout;
			fprintf(stderr, "manufacturer id = 0x%x, device id =\
						0x%x\n# of blks = %d, blk size = 0x%x\n",
					uflash_if.info.mfr_id & 0xFF,
					uflash_if.info.dev_id & 0xFF,
					uflash_if.info.blk_num,
					uflash_if.info.blk_size);
			break;
 
		case '1':       /* write to user flash PROM */
			fprintf(stderr,
                                "Enter PROM offset[0, 0xXX,XXXX]> ");
                        scanf ("%x", &offset);
 
			fprintf(stderr, 
				"Enter number of bytes[hex]> ");
			scanf ("%x", &size);
 
			fprintf(stderr,
                                "Enter data pattern[0, 0xFF]> ");
 
                        scanf ("%x", &pat);
 
			/*
	 		 * init write buffer.
	 		 */
			for (i = 0; i < size; i++) {
				w_buf[i] = pat;
			}
 
			n_byte = pwrite (fd, w_buf, size, offset);
			if (n_byte != size) {
				/* the write failed */
				printf ("Write process was failed at byte 0x%x \n",
					n_byte);
			}
			break;
 
		case '0':	/* read from user flash PROM */
			fprintf(stderr,
                                "Enter PROM offset[0, 0xXX,XXXX]> ");
                        scanf ("%x", &offset);
 
			fprintf(stderr, 
				"Enter number of bytes[hex]> ");
			scanf ("%x", &size);
 
			getchar();	/* clean up the char buf */
 
			n_byte = pread (fd, r_buf, size, offset);
			if (n_byte != size) {
                                /* the read failed */
                                printf ("Read process was failed at \
							byte 0x%x \n",
                                        n_byte);
				continue;
                        }
 
			printf ("\nuser data buffer:\n");
			for (i = 0; i < size; i++) {
				printf("%2x ", r_buf[i] & 0xff);
			}
		printf("\n");
 
		default:
			continue;
		}
	}
 
	/* exit */
getout:
	close(fd0);
	close(fd1);
	return;
 
} /* end of main() */