@ -25,6 +25,7 @@
# include <sys/ioctl.h>
# include <sys/stat.h>
# include <unistd.h>
# include <dirent.h>
# ifdef MTD_OLD
# include <stdint.h>
@ -34,6 +35,8 @@
# include <mtd / mtd-user.h>
# endif
# include <mtd/ubi-user.h>
# include "fw_env_private.h"
# include "fw_env.h"
@ -58,6 +61,7 @@ struct envdev_s {
ulong erase_size ; /* device erase size */
ulong env_sectors ; /* number of environment sectors */
uint8_t mtd_type ; /* type of the MTD device */
int is_ubi ; /* set if we use UBI volume */
} ;
static struct envdev_s envdevices [ 2 ] =
@ -76,6 +80,7 @@ static int dev_current;
# define DEVESIZE(i) envdevices[(i)].erase_size
# define ENVSECTORS(i) envdevices[(i)].env_sectors
# define DEVTYPE(i) envdevices[(i)].mtd_type
# define IS_UBI(i) envdevices[(i)].is_ubi
# define CUR_ENVSIZE ENVSIZE(dev_current)
@ -120,6 +125,228 @@ static unsigned char obsolete_flag = 0;
# define DEFAULT_ENV_INSTANCE_STATIC
# include <env_default.h>
# define UBI_DEV_START " / dev / ubi"
# define UBI_SYSFS " / sys / class / ubi"
# define UBI_VOL_NAME_PATT "ubi%d_%d"
static int is_ubi_devname ( const char * devname )
{
return ! strncmp ( devname , UBI_DEV_START , sizeof ( UBI_DEV_START ) - 1 ) ;
}
static int ubi_check_volume_sysfs_name ( const char * volume_sysfs_name ,
const char * volname )
{
char path [ 256 ] ;
FILE * file ;
char * name ;
int ret ;
strcpy ( path , UBI_SYSFS " / " ) ;
strcat ( path , volume_sysfs_name ) ;
strcat ( path , " /name " ) ;
file = fopen ( path , " r " ) ;
if ( ! file )
return - 1 ;
ret = fscanf ( file , " %ms " , & name ) ;
fclose ( file ) ;
if ( ret < = 0 | | ! name ) {
fprintf ( stderr ,
" Failed to read from file %s, ret = %d, name = %s \n " ,
path , ret , name ) ;
return - 1 ;
}
if ( ! strcmp ( name , volname ) ) {
free ( name ) ;
return 0 ;
}
free ( name ) ;
return - 1 ;
}
static int ubi_get_volnum_by_name ( int devnum , const char * volname )
{
DIR * sysfs_ubi ;
struct dirent * dirent ;
int ret ;
int tmp_devnum ;
int volnum ;
sysfs_ubi = opendir ( UBI_SYSFS ) ;
if ( ! sysfs_ubi )
return - 1 ;
# ifdef DEBUG
fprintf ( stderr , " Looking for volume name \" %s \" \n " , volname ) ;
# endif
while ( 1 ) {
dirent = readdir ( sysfs_ubi ) ;
if ( ! dirent )
return - 1 ;
ret = sscanf ( dirent - > d_name , UBI_VOL_NAME_PATT ,
& tmp_devnum , & volnum ) ;
if ( ret = = 2 & & devnum = = tmp_devnum ) {
if ( ubi_check_volume_sysfs_name ( dirent - > d_name ,
volname ) = = 0 )
return volnum ;
}
}
return - 1 ;
}
static int ubi_get_devnum_by_devname ( const char * devname )
{
int devnum ;
int ret ;
ret = sscanf ( devname + sizeof ( UBI_DEV_START ) - 1 , " %d " , & devnum ) ;
if ( ret ! = 1 )
return - 1 ;
return devnum ;
}
static const char * ubi_get_volume_devname ( const char * devname ,
const char * volname )
{
char * volume_devname ;
int volnum ;
int devnum ;
int ret ;
devnum = ubi_get_devnum_by_devname ( devname ) ;
if ( devnum < 0 )
return NULL ;
volnum = ubi_get_volnum_by_name ( devnum , volname ) ;
if ( volnum < 0 )
return NULL ;
ret = asprintf ( & volume_devname , " %s_%d " , devname , volnum ) ;
if ( ret < 0 )
return NULL ;
# ifdef DEBUG
fprintf ( stderr , " Found ubi volume \" %s:%s \" -> %s \n " ,
devname , volname , volume_devname ) ;
# endif
return volume_devname ;
}
static void ubi_check_dev ( unsigned int dev_id )
{
char * devname = ( char * ) DEVNAME ( dev_id ) ;
char * pname ;
const char * volname = NULL ;
const char * volume_devname ;
if ( ! is_ubi_devname ( DEVNAME ( dev_id ) ) )
return ;
IS_UBI ( dev_id ) = 1 ;
for ( pname = devname ; * pname ! = ' \0 ' ; pname + + ) {
if ( * pname = = ' : ' ) {
* pname = ' \0 ' ;
volname = pname + 1 ;
break ;
}
}
if ( volname ) {
/* Let's find real volume device name */
volume_devname = ubi_get_volume_devname ( devname , volname ) ;
if ( ! volume_devname ) {
fprintf ( stderr , " Didn't found ubi volume \" %s \" \n " ,
volname ) ;
return ;
}
free ( devname ) ;
DEVNAME ( dev_id ) = volume_devname ;
}
}
static int ubi_update_start ( int fd , int64_t bytes )
{
if ( ioctl ( fd , UBI_IOCVOLUP , & bytes ) )
return - 1 ;
return 0 ;
}
static int ubi_read ( int fd , void * buf , size_t count )
{
ssize_t ret ;
while ( count > 0 ) {
ret = read ( fd , buf , count ) ;
if ( ret > 0 ) {
count - = ret ;
buf + = ret ;
continue ;
}
if ( ret = = 0 ) {
/*
* Happens in case of too short volume data size . If we
* return error status we will fail it will be treated
* as UBI device error .
*
* Leave catching this error to CRC check .
*/
fprintf ( stderr , " Warning: end of data on ubi volume \n " ) ;
return 0 ;
} else if ( errno = = EBADF ) {
/*
* Happens in case of corrupted volume . The same as
* above , we cannot return error now , as we will still
* be able to successfully write environment later .
*/
fprintf ( stderr , " Warning: corrupted volume? \n " ) ;
return 0 ;
} else if ( errno = = EINTR ) {
continue ;
}
fprintf ( stderr , " Cannot read %u bytes from ubi volume, %s \n " ,
( unsigned int ) count , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
static int ubi_write ( int fd , const void * buf , size_t count )
{
ssize_t ret ;
while ( count > 0 ) {
ret = write ( fd , buf , count ) ;
if ( ret < = 0 ) {
if ( ret < 0 & & errno = = EINTR )
continue ;
fprintf ( stderr , " Cannot write %u bytes to ubi volume \n " ,
( unsigned int ) count ) ;
return - 1 ;
}
count - = ret ;
buf + = ret ;
}
return 0 ;
}
static int flash_io ( int mode ) ;
static int parse_config ( struct env_opts * opts ) ;
@ -960,6 +1187,12 @@ static int flash_write (int fd_current, int fd_target, int dev_target)
DEVOFFSET ( dev_target ) , DEVNAME ( dev_target ) ) ;
# endif
if ( IS_UBI ( dev_target ) ) {
if ( ubi_update_start ( fd_target , CUR_ENVSIZE ) < 0 )
return 0 ;
return ubi_write ( fd_target , environment . image , CUR_ENVSIZE ) ;
}
rc = flash_write_buf ( dev_target , fd_target , environment . image ,
CUR_ENVSIZE ) ;
if ( rc < 0 )
@ -984,6 +1217,12 @@ static int flash_read (int fd)
{
int rc ;
if ( IS_UBI ( dev_current ) ) {
DEVTYPE ( dev_current ) = MTD_ABSENT ;
return ubi_read ( fd , environment . image , CUR_ENVSIZE ) ;
}
rc = flash_read_buf ( dev_current , fd , environment . image , CUR_ENVSIZE ,
DEVOFFSET ( dev_current ) ) ;
if ( rc ! = CUR_ENVSIZE )
@ -1165,7 +1404,8 @@ int fw_env_open(struct env_opts *opts)
DEVTYPE ( ! dev_current ) = = MTD_UBIVOLUME ) {
environment . flag_scheme = FLAG_INCREMENTAL ;
} else if ( DEVTYPE ( dev_current ) = = MTD_ABSENT & &
DEVTYPE ( ! dev_current ) = = MTD_ABSENT ) {
DEVTYPE ( ! dev_current ) = = MTD_ABSENT & &
IS_UBI ( dev_current ) = = IS_UBI ( ! dev_current ) ) {
environment . flag_scheme = FLAG_INCREMENTAL ;
} else {
fprintf ( stderr , " Incompatible flash types! \n " ) ;
@ -1271,8 +1511,12 @@ int fw_env_close(struct env_opts *opts)
static int check_device_config ( int dev )
{
struct stat st ;
int32_t lnum = 0 ;
int fd , rc = 0 ;
/* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */
ubi_check_dev ( dev ) ;
fd = open ( DEVNAME ( dev ) , O_RDONLY ) ;
if ( fd < 0 ) {
fprintf ( stderr ,
@ -1288,7 +1532,14 @@ static int check_device_config(int dev)
goto err ;
}
if ( S_ISCHR ( st . st_mode ) ) {
if ( IS_UBI ( dev ) ) {
rc = ioctl ( fd , UBI_IOCEBISMAP , & lnum ) ;
if ( rc < 0 ) {
fprintf ( stderr , " Cannot get UBI information for %s \n " ,
DEVNAME ( dev ) ) ;
goto err ;
}
} else if ( S_ISCHR ( st . st_mode ) ) {
struct mtd_info_user mtdinfo ;
rc = ioctl ( fd , MEMGETINFO , & mtdinfo ) ;
if ( rc < 0 ) {