这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » 配置tffs文件系统时,执行tffsShow时报错,amdMTDIdentify

共13条 1/2 1 2 跳转至

配置tffs文件系统时,执行tffsShow时报错,amdMTDIdentify函数读出ID是0,请大侠指教!!!

菜鸟
2007-12-04 17:07:51     打赏
我用的flash是AM29lv160D,
请教大家,,我用的amdmtd.c文件是Tornado里面自带的,在里面加了一些打印语句,在执行tffsShow时,调用到了里面的amdMTDIdentify函数,该函数读出的ID号是0,请问是不是要自己写一个amdmtd.c函数吗??怎么办呢??



关键词: 配置     文件     系统     执行     tffsShow     报错     a    

菜鸟
2007-12-05 21:10:08     打赏
2楼


sst39vf160.c
#include "tffs/flflash.h"
#include "tffs/backgrnd.h"
#include "stdio.h"
#include "config.h"

/*需与sysTffs.c中相同定义一致*/
#define FLASH_BASE_ADRS   (0x02000000)
#define FLASH_SIZE       (0x00200000)
#define FLASH_BOOT_ADRS   (0x02000000)
#define FLASH_BOOT_SIZE      (0x00000000)

typedef struct
{
 FlashWPTR unlockAddr1;
 FlashWPTR   unlockAddr2;
} Vars;

static Vars mtdVars[DRIVES];

#define thisVars   ((Vars *) vol.mtdVars)

#define SETUP_ERASE  0x80
#define SETUP_WRITE  0xa0
#define READ_ID   0x90
#define SECTOR_ERASE 0x30
#define BLOCK_ERASE  0x50  /*块擦除*/
#define READ_ARRAY  0xf0        /*软件ID读模式退出*/

#define UNLOCK_1  0xaa        /*解锁写入值*/
#define UNLOCK_2  0x55

#define UNLOCK_ADDR1 0x5555  /*解锁偏移地址[字]*/
#define UNLOCK_ADDR2 0x2aaa

/* JEDEC ids for this MTD */
#define AM29LV160D_DEID 0x2782      /*设备ID*/

#define DEBUG_PRINT  printErr

static STATUS  lv160OpOverDetect(void * ptr,  int timeCounter);

 


/*------------------------------------------------------------------------
 Procedure:     lv160MTDMap ID:1
 Purpose:       映射Flash片内地址为CPU全局地址
 Input:         addr-相对地址
 Output:  
 Errors:
------------------------------------------------------------------------*/
static void FAR0 * lv160MTDMap0(FLFlash *vol, CardAddress addr, int length)
{
 UINT32 ret;
 /*???baseAddress可能系统启动读时未初始化*/
 ret = FLASH_BASE_ADRS + addr;
 
 return (void FAR0 *)ret;
}
static void FAR0 * lv160MTDMap1(FLFlash *vol, CardAddress addr, int length)
{
 UINT32 ret;
 /*???不能根据vol->socket->serialNo来分支判断*/
 ret = FLASH_BOOT_ADRS + addr;

 return (void FAR0 *)ret;
}
/*------------------------------------------------------------------------
 Procedure:     lv160OpOverDetect ID:1
 Purpose:       探测write,erase操作是否结束,超时错误
 Input:        
 Output:  
 Errors:
------------------------------------------------------------------------*/
static STATUS  lv160OpOverDetect(void * ptr,  int timeCounter)
{
 FlashWPTR pFlash = ptr;
 INT16 buf1,buf2;
 
 buf1 = *pFlash & 0x40;
 while(1)
 {
      buf2  = *pFlash & 0x40;
      if(buf1 == buf2)
    break;
      else
       buf1 = buf2;
      if(timeCounter-- <= 0)
      {
    return ERROR;
      }
 }

 return OK;
}
/*------------------------------------------------------------------------
 Procedure:     lv160MTDWrite ID:1
 Purpose:       MTD写Flash函数
 Input:        
 Output:  
 Errors:
------------------------------------------------------------------------*/
static FLStatus lv160MTDWrite(FLFlash vol, CardAddress address, const void FAR1 *buffer, int length, FLBoolean overwrite)
{
 int cLength;
 FlashWPTR flashPtr, flashTmp;
 volatile UINT16 *gBuffer;

 flashTmp = flashPtr = (FlashWPTR) vol.map(&vol, address, length);

 if(length&1)
 {
  printf("warning! the data length can not divided by 2."); 
 }
 cLength = length/2;

 gBuffer = (UINT16 *)buffer;

 while (cLength >= 1)
 {
  *thisVars->unlockAddr1 = UNLOCK_1;
     *thisVars->unlockAddr2 = UNLOCK_2;
     *thisVars->unlockAddr1 = SETUP_WRITE;

  *flashPtr = *gBuffer;
  
  if(lv160OpOverDetect((void *)flashPtr, 0x1000000));
  if(*flashPtr != *gBuffer)
  {
   *flashPtr = READ_ARRAY;
   #ifdef DEBUG_PRINT
   DEBUG_PRINT("Debug: lv160MTDWrite timeout.\n");
   #endif
   return flWriteFault;
  }
  cLength--;
  flashPtr++;
  gBuffer++;
 }
 if (tffscmp((void FAR0 *) flashTmp, buffer,length))
 {
  /* verify the data */
  #ifdef DEBUG_PRINT
    DEBUG_PRINT("Debug: lv160MTDWrite fail.\n");
  #endif
  return flWriteFault;
 }

 return flOK;
}
/*------------------------------------------------------------------------
 Procedure:     lv160MTDWrite ID:1
 Purpose:       MTD擦除Flash函数
 Input:        
 Output:  
 Errors:
------------------------------------------------------------------------*/
static FLStatus lv160MTDErase(FLFlash vol, int firstErasableBlock, int numOfErasableBlocks)
{
 int iBlock;    FlashWPTR flashPtr;
 unsigned int offset; 

 if(numOfErasableBlocks <= 0) return ERROR;

 for (iBlock = 0; iBlock < numOfErasableBlocks; iBlock++)
 {
  int i;
  offset = (firstErasableBlock + iBlock) * vol.erasableBlockSize;
  flashPtr = (FlashWPTR) vol.map(&vol, offset, vol.interleaving);

  *thisVars->unlockAddr1 = UNLOCK_1;
     *thisVars->unlockAddr2 = UNLOCK_2;
     *thisVars->unlockAddr1 = SETUP_ERASE;
  *thisVars->unlockAddr1 = UNLOCK_1;
     *thisVars->unlockAddr2 = UNLOCK_2;
  *flashPtr = SECTOR_ERASE;

  lv160OpOverDetect((void *)flashPtr, 0x2000000);     
  for(i=0; i<vol.erasableBlockSize/2; i++,flashPtr++) 
  {
     if(*flashPtr != 0xffff)  break;
  }

  *flashPtr = READ_ARRAY;
   
  if(i < vol.erasableBlockSize/2)
  {
   #ifdef DEBUG_PRINT
     DEBUG_PRINT("Debug: lv160MTDErase fail.\n");
   #endif
   return flWriteFault;
  }
 }
 
 printf("\Erase ok\n");
 return flOK;
}
/*------------------------------------------------------------------------
 Procedure:     fllv160Identify ID:1
 Purpose:       MTD读Flash标识
 Input:        
 Output:  
 Errors:
------------------------------------------------------------------------*/
static FLStatus fllv160Identify(FLFlash vol, UINT32 offset)
{

 FlashWPTR flashPtr;

 
 flashPtr = (FlashWPTR) vol.map(&vol, 0, vol.interleaving);
 printf("\nenter the fllv160Identify\n");

 *thisVars->unlockAddr1 = UNLOCK_1;
    *thisVars->unlockAddr2 = UNLOCK_2;
    *thisVars->unlockAddr1 = READ_ID;

    printf("\nthisVars->unlockAddr1 %x,*thisVars->unlockAddr2 %x\n", thisVars->unlockAddr1, thisVars->unlockAddr2);

    /* 0X01 read device Id, 0X00 manufacture Id.*/
   flashPtr = (FlashWPTR) vol.map(&vol, offset, vol.interleaving); 
 
 printf("\nthe flashPtr is %x\n",flashPtr);
 
 vol.type = *flashPtr;
 flashPtr = (FlashWPTR)vol.map(&vol, 0, vol.interleaving) ;
 *flashPtr = READ_ARRAY;    /*回到读状态*/

   return flOK;
}
/*------------------------------------------------------------------------
 Procedure:     lv160MTDIdentify ID:1
 Purpose:       MTD读Flash标识[extern]
 Input:        
 Output:  
 Errors:
------------------------------------------------------------------------*/
FLStatus lv160MTDIdentify(FLFlash vol)
{
 FlashWPTR  baseFlashPtr;

 vol.interleaving = 1;

 flSetWindowBusWidth(vol.socket,16);/* use 16-bits */
 flSetWindowSpeed(vol.socket,120);  /* 120 nsec. */
 if (vol.socket->serialNo == 0){ 
  flSetWindowSize(vol.socket, FLASH_SIZE>>12);
  vol.chipSize = FLASH_SIZE;
  vol.map   = lv160MTDMap0;
 }
 else if(vol.socket->serialNo == 1){ 
  flSetWindowSize(vol.socket, FLASH_BOOT_SIZE>>12);
  vol.chipSize = FLASH_BOOT_SIZE;
  vol.map   = lv160MTDMap1;
 }
 vol.mtdVars = &mtdVars[flSocketNoOf(vol.socket)];
 baseFlashPtr = (FlashWPTR)vol.map (&vol, (CardAddress)0, vol.interleaving);

   printf("\nthe baseFlashPtr is %x\n",baseFlashPtr);

 /*UNLOCK_ADDR为字地址, 赋值转换x2*/
    thisVars->unlockAddr1 = (FlashWPTR)((long)baseFlashPtr) + UNLOCK_ADDR1;
 thisVars->unlockAddr2 = (FlashWPTR)((long)baseFlashPtr) + UNLOCK_ADDR2;
  
 fllv160Identify(&vol, 2);        /*get flash device ID.*/

 printf("\nthe thisVars->unlockAddr1 is %x, thisVars->unlockAddr2 is %x\n",thisVars->unlockAddr1,thisVars->unlockAddr2);

       printf("\nthe type is %x\n",vol.type);
 if (vol.type != AM29LV160D_DEID)
 {
   #ifdef DEBUG_PRINT
     DEBUG_PRINT("Debug: can not identify SST39VF160 media.\n");
   #endif
     return flUnknownMedia;
 }

 vol.noOfChips =0x1;                 /*one chip.*/
 vol.erasableBlockSize = 0x10000;   /* 64k bytes.*/
 vol.flags |= SUSPEND_FOR_WRITE;

 /* Register our flash handlers */
 vol.write = lv160MTDWrite;
 vol.erase = lv160MTDErase;
 
 printf("\nidentify ok\n");
 return flOK;
}


菜鸟
2007-12-05 21:10:44     打赏
3楼

/* sysTffs.c - Motorola MVME177 system-dependent TrueFFS library */

/* Copyright 1984-1997 Wind River Systems, Inc. */
#include "copyright_wrs.h"

/* FAT-FTL Lite Software Development Kit
 * Copyright (C) M-Systems Ltd. 1995-1996 */

/*
modification history
--------------------
01j,31may99,yp  Added comments suggested in SPR #25319
01i,21apr98,yp   added tffs to files included from there
01h,11mar98,yp   made including tffsConfig.c conditional so man page
                 generation does not include it.
01g,09mar99,kbw  made man page edits to fix problems found by QE
01f,02jan98,yp   doc cleanup
01e,18dec97,hdn  added comment.  cleaned up.
01d,05dec97,hdn  added tffsSocket[].  cleaned up.
01c,11nov97,hdn  fixed typo.
01b,05nov97,hdn  cleaned up.
01a,09oct97,and  written by Andray in M-Systems
*/

/*
DESCRIPTION
This library provides board-specific hardware access routines for TrueFFS. 
In effect, these routines comprise the socket component driver (or drivers)
for your flash device hardware.  At socket registration time, TrueFFS stores
pointers to the functions of this socket component driver in an 'FLSocket'
structure.  When TrueFFS needs to access the flash device, it uses these
functions. 

Because this file is, for the most part, a device driver that exports its
functionality by registering function pointers with TrueFFS, very few of the
functions defined here are externally callable.  For the record, these
external functions are flFitInSocketWindow() and flDelayLoop().  You should
never have any need to call these functions. 

However, one of the most import functions defined in this file is neither
referenced in an 'FLSocket' structure, nor is it externally callable.  This
function is sysTffsInit().  TrueFFS calls this function at initialization
time to register socket component drivers for all the flash devices attached
to your target.  It is this call to sysTffs() that results in assigning
drive numbers to the flash devices on your target hardware.  Drive numbers
are assigned by the order in which the socket component drivers are registered.
The first to be registered is drive 0, the second is drive 1, and so on up to
4.  As shipped, TrueFFS supports up to five flash drives. 

After registering socket component drivers for a flash device, you may
format the flash medium even though there is not yet a block device driver
associated with the flash (see the reference entry for the tffsDevCreate()
routine).  To format the flash medium for use with TrueFFS,
call tffsDevFormat() or, for some BSPs, sysTffsFormat(). 

The sysTffsFormat() routine is an optional but BSP-specific externally
callable helper function.  Internally, it calls tffsDevFormat() with a
pointer to a 'FormatParams' structure initialized to values that leave a
space on the flash device for a boot image. This space is outside the
region managed by TrueFFS.  This special region is necessary for boot
images because the normal translation and wear-leveling services of TrueFFS
are incompatible with the needs of the boot program and the boot image it
relies upon.  To write a boot image (or any other data) into this area,
use tffsBootImagePut(). 

Finally, this file also contains define statements for symbolic constants
that determine which MTDs, translation layer modules, and other utilities
are ultimately included in TrueFFS.  These defines are as follows:

.IP "INCLUDE_TL_NFTL"
To include the NAND-based translation layer module.
.IP "INCLUDE_TL_FTL"
To include the NOR-based translation layer module.
.IP "INCLUDE_TL_SSFDC"
To include the SSFDC-appropriate translation layer module.
.IP "INCLUDE_MTD_I28F016"
For Intel 28f016 flash devices.
.IP "INCLUDE_MTD_I28F008"
For Intel 28f008 flash devices.
.IP "INCLUDE_MTD_I28F008_BAJA"
For Intel 28f008 flash devices on the Heurikon Baja 4700.
.IP "INCLUDE_MTD_AMD"
For AMD, Fujitsu: 29F0{40,80,16} 8-bit flash devices.
.IP "INCLUDE_MTD_CDSN"
For Toshiba, Samsung: NAND CDSN flash devices.
.IP "INCLUDE_MTD_DOC2"
For Toshiba, Samsung: NAND DOC flash devices.
.IP "INCLUDE_MTD_CFISCS"
For CFI/SCS flash devices.
.IP "INCLUDE_MTD_WAMD"
For AMD, Fujitsu 29F0{40,80,16} 16-bit flash devices.
.IP "INCLUDE_TFFS_BOOT_IMAGE"
To include tffsBootImagePut() in TrueFFS for Tornado.
.LP
To exclude any of the modules mentioned above, edit sysTffs.c and undefine
its associated symbolic constant.

INCLUDE FILES: flsocket.h, tffsDrv.h

SEE ALSO : tffsDrv tffsConfig
*/

#include "vxWorks.h"
#include "config.h"
#include "tffs/flsocket.h"
#include "tffs/tffsDrv.h"


/* defines */
#define INCLUDE_MTD_LV160
#undef INCLUDE_MTD_I28F016  /* Intel: 28f016 */
#undef INCLUDE_MTD_I28F008  /* Intel: 28f008 */
#undef INCLUDE_MTD_AMD   /* AMD, Fujitsu: 29f0{40,80,16} 8bit */
#undef INCLUDE_MTD_CDSN  /* Toshiba, Samsung: NAND, CDSN */
#undef INCLUDE_MTD_DOC2  /* Toshiba, Samsung: NAND, DOC */
#undef INCLUDE_MTD_CFISCS  /* CFI/SCS */
#undef INCLUDE_MTD_WAMD  /* AMD, Fujitsu: 29f0{40,80,16} 16bit */
#undef INCLUDE_TL_NFTL   /* NFTL translation layer */
#define INCLUDE_TL_FTL   /* FTL translation layer */
#undef INCLUDE_TL_SSFDC  /* SSFDC translation layer */
#undef  INCLUDE_TFFS_BOOT_IMAGE /* include tffsBootImagePut() */
#define FLASH_BASE_ADRS   (0x02000000)
#define FLASH_SIZE       (0x00200000)
#define FLASH_BOOT_ADRS   (0x02000000)
#define FLASH_BOOT_SIZE      (0x00000000)

/* locals */


/* forward declarations */

LOCAL void  rfaWriteProtect (void);
LOCAL void  rfaWriteEnable (void);
LOCAL FLBoolean  rfaCardDetected (FLSocket vol);
LOCAL void  rfaVccOn (FLSocket vol);
LOCAL void  rfaVccOff (FLSocket vol);
#ifdef SOCKET_12_VOLTS
LOCAL FLStatus  rfaVppOn (FLSocket vol);
LOCAL void  rfaVppOff (FLSocket vol);
#endif /* SOCKET_12_VOLTS */
LOCAL FLBoolean  rfaGetAndClearCardChangeIndicator (FLSocket vol);
LOCAL FLBoolean  rfaWriteProtected (FLSocket vol);
LOCAL void  rfaSetWindow (FLSocket vol);
LOCAL void  rfaSetMappingContext (FLSocket vol, unsigned page);
LOCAL FLStatus  rfaSocketInit (FLSocket vol);
LOCAL FLStatus  rfaRegister (void);

#ifndef DOC
/*#include "tffs/tffsConfig.c"*/
#include "tffsConfig.c"
#endif /* DOC */

/*******************************************************************************
*
* sysTffsInit - board-level initialization for TrueFFS
*
* This routine calls the socket registration routines for the socket component
* drivers that will be used with this BSP. The order of registration determines
* the logical drive number given to the drive associated with the socket.
*
* RETURNS: N/A
*/

LOCAL void sysTffsInit (void)
    {
   
    rfaRegister ();
    rfaRegister ();
    }

/*******************************************************************************
*
* rfaRegister - registration routine for the RFA on MVME177
*
* This routine populates the 'vol' structure for a logical drive with the
* socket component driver routines for the RFA on the MVME177 board. All
* socket routines are referanced through the 'vol' structure and never
* from here directly
*
* RETURNS: flOK, or flTooManyComponents if there're too many drives
*/

LOCAL FLStatus rfaRegister (void)
    {
    FLSocket vol = flSocketOf (noOfDrives);

    if (noOfDrives >= DRIVES)
        return (flTooManyComponents);

    tffsSocket[noOfDrives] = "RFA";
   
    vol.serialNo = noOfDrives;
    if  (noOfDrives == 0)
     vol.window.baseAddress = FLASH_BASE_ADRS >> 12;
    else if(noOfDrives == 1)
  vol.window.baseAddress = FLASH_BOOT_ADRS >> 12;
 noOfDrives++;

    /* fill in function pointers */

    vol.cardDetected      = rfaCardDetected;
    vol.VccOn             = rfaVccOn;
    vol.VccOff            = rfaVccOff;
#ifdef SOCKET_12_VOLTS
    vol.VppOn             = rfaVppOn;
    vol.VppOff            = rfaVppOff;
#endif
    vol.initSocket        = rfaSocketInit;
    vol.setWindow         = rfaSetWindow;
    vol.setMappingContext = rfaSetMappingContext;
    vol.getAndClearCardChangeIndicator =
                          rfaGetAndClearCardChangeIndicator;
    vol.writeProtected    = rfaWriteProtected;

    return (flOK);
    }

/*******************************************************************************
*
* rfaCardDetected - detect if a card is present (inserted)
*
* This routine detects if a card is present (inserted).
*
* RETURNS: TRUE, or FALSE if the card is not present.
*/

LOCAL FLBoolean rfaCardDetected
    (
    FLSocket vol
    )
    {
    return (TRUE);
    }

/*******************************************************************************
*
* rfaVccOn - turn on Vcc (3.3/5 Volts)
*
* This routine turns on Vcc (3.3/5 Volts).  Vcc must be known to be good
* on exit.
*
* RETURNS: N/A
*/

LOCAL void rfaVccOn
    (
    FLSocket vol
    )
    {
    rfaWriteEnable ();
    }

/*******************************************************************************
*
* rfaVccOff - turn off Vcc (3.3/5 Volts)
*
* This routine turns off Vcc (3.3/5 Volts).
*
* RETURNS: N/A
*/

LOCAL void rfaVccOff
    (
    FLSocket vol
    )
    {
    rfaWriteProtect ();
    }

#ifdef SOCKET_12_VOLTS

/*******************************************************************************
*
* rfaVppOn - turns on Vpp (12 Volts)
*
* This routine turns on Vpp (12 Volts). Vpp must be known to be good on exit.
*
* RETURNS: flOK always
*/

LOCAL FLStatus rfaVppOn
    (
    FLSocket vol  /* pointer identifying drive */
    )
    {
    return (flOK);
    }

/*******************************************************************************
*
* rfaVppOff - turns off Vpp (12 Volts)
*
* This routine turns off Vpp (12 Volts).
*
* RETURNS: N/A
*/

LOCAL void rfaVppOff
    (
    FLSocket vol  /* pointer identifying drive */
    )
    {
    }

#endif /* SOCKET_12_VOLTS */


/*******************************************************************************
*
* rfaSocketInit - perform all necessary initializations of the socket
*
* This routine performs all necessary initializations of the socket.
*
* RETURNS: flOK always
*/

LOCAL FLStatus rfaSocketInit
    (
    FLSocket vol  /* pointer identifying drive */
    )
    {
    rfaWriteEnable ();

    vol.cardChanged = FALSE;

    /* enable memory window and map it at address 0 */
    rfaSetWindow (&vol);

    return (flOK);
    }

/*******************************************************************************
*
* rfaSetWindow - set current window attributes, Base address, size, etc
*
* This routine sets current window hardware attributes: Base address, size,
* speed and bus width.  The requested settings are given in the 'vol.window'
* structure.  If it is not possible to set the window size requested in
* 'vol.window.size', the window size should be set to a larger value,
* if possible. In any case, 'vol.window.size' should contain the
* actual window size (in 4 KB units) on exit.
*
* RETURNS: N/A
*/

LOCAL void rfaSetWindow
    (
    FLSocket vol  /* pointer identifying drive */
    )
    {
    /* Physical base as a 4K page */
    if     (vol.serialNo == 0){
     vol.window.baseAddress = FLASH_BASE_ADRS >> 12;
     flSetWindowSize (&vol, FLASH_SIZE >> 12);
    }
    else if(vol.serialNo == 1){
     vol.window.baseAddress = FLASH_BOOT_ADRS >> 12;
     flSetWindowSize (&vol, FLASH_BOOT_SIZE >> 12);
 }
    }

/*******************************************************************************
*
* rfaSetMappingContext - sets the window mapping register to a card address
*
* This routine sets the window mapping register to a card address.
* The window should be set to the value of 'vol.window.currentPage',
* which is the card address divided by 4 KB. An address over 128MB,
* (page over 32K) specifies an attribute-space address. On entry to this
* routine vol.window.currentPage is the page already mapped into the window.
* (In otherwords the page that was mapped by the last call to this routine.)
*
* The page to map is guaranteed to be on a full window-size boundary.
*
* RETURNS: N/A
*/

LOCAL void rfaSetMappingContext
    (
    FLSocket vol,  /* pointer identifying drive */
    unsigned page  /* page to be mapped */
    )
    {
     }

/*******************************************************************************
*
* rfaGetAndClearCardChangeIndicator - return the hardware card-change indicator
*
* This routine returns the hardware card-change indicator and clears it if set.
*
* RETURNS: FALSE, or TRUE if the card has been changed
*/

LOCAL FLBoolean rfaGetAndClearCardChangeIndicator
    (
    FLSocket vol  /* pointer identifying drive */
    )
    {
    return (FALSE);
    }

/*******************************************************************************
*
* rfaWriteProtected - return the write-protect state of the media
*
* This routine returns the write-protect state of the media
*
* RETURNS: FALSE, or TRUE if the card is write-protected
*/

LOCAL FLBoolean rfaWriteProtected
    (
    FLSocket vol  /* pointer identifying drive */
    )
    {
    return (FALSE);
    }

/*******************************************************************************
*
* rfaWriteProtect - disable write access to the RFA
*
* This routine disables write access to the RFA.
*
* RETURNS: N/A
*/

LOCAL void rfaWriteProtect (void)
    {
    /* clear GPOEN1 bit (#17), make sure GPIO1 bit (#13) is clear  */
    /**VMECHIP2_IOCR = (*VMECHIP2_IOCR) & ((~IOCR_GPOEN1) & (~IOCR_GPIOO1_HIGH));*/
    }

/*******************************************************************************
*
* rfaWriteEnable - enable write access to the RFA
*
* This routine enables write access to the RFA.
*
* RETURNS: N/A
*/

LOCAL void  rfaWriteEnable (void)
    {
    /* set GPOEN1 bit (#17), make sure GPIO1 bit (#13) is clear */
    /**VMECHIP2_IOCR = ((*VMECHIP2_IOCR) | IOCR_GPOEN1) & (~IOCR_GPIOO1_HIGH);*/
    }

/*******************************************************************************
*
* flFitInSocketWindow - check whether the flash array fits in the socket window
*
* This routine checks whether the flash array fits in the socket window.
*
* RETURNS: A chip size guaranteed to fit in the socket window.
*/

long int flFitInSocketWindow
    (
    long int chipSize,  /* size of single physical chip in bytes */
    int      interleaving, /* flash chip interleaving (1,2,4 etc) */
    long int windowSize  /* socket window size in bytes */
    )
    {
    if (chipSize*interleaving > windowSize) /* doesn't fit in socket window */
        {
        int  roundedSizeBits;

        /* fit chip in the socket window */
        chipSize = windowSize / interleaving;

        /* round chip size at powers of 2 */
        for (roundedSizeBits = 0; (0x1L << roundedSizeBits) <= chipSize;
             roundedSizeBits++)
     ;

        chipSize = (0x1L << (roundedSizeBits - 1));
        }

    return (chipSize);
    }

#if FALSE
/*******************************************************************************
*
* sysTffsCpy - copy memory from one location to another
*
* This routine copies <size> characters from the object pointed
* to by <source> into the object pointed to by <destination>. If copying
* takes place between objects that overlap, the behavior is undefined.
*
* INCLUDE FILES: string.h
*
* RETURNS: A pointer to <destination>.
*
* NOMANUAL
*/

void * sysTffsCpy
    (
    void *       destination,   /* destination of copy */
    const void * source,        /* source of copy */
    size_t       size           /* size of memory to copy */
    )
    {
    bcopy ((char *) source, (char *) destination, (size_t) size);
    return (destination);
    }

/*******************************************************************************
*
* sysTffsSet - set a block of memory
*
* This routine stores <c> converted to an `unsigned char' in each of the
* elements of the array of `unsigned char' beginning at <m>, with size <size>.
*
* INCLUDE FILES: string.h
*
* RETURNS: A pointer to <m>.
*
* NOMANUAL
*/

void * sysTffsSet
    (
    void * m,                   /* block of memory */
    int    c,                   /* character to store */
    size_t size                 /* size of memory */
    )
    {
    bfill ((char *) m, (int) size, c);
    return (m);
    }
#endif /* FALSE */

/*******************************************************************************
*
* flDelayLoop - consume the specified time
*
* This routine consumes the specified time.
*
* RETURNS: N/A
*/

void flDelayLoop
    (
    int cycles   /* loop count to be consumed */
    )
    {
    while (--cycles)
 ;
    }


菜鸟
2007-12-05 21:12:05     打赏
4楼

/* tffsConfig.c - TrueFFS configuration file for VxWorks */

/* Copyright 1984-1997 Wind River Systems, Inc. */
#include "copyright_wrs.h"

/* FAT-FTL Lite Software Development Kit
 * Copyright (C) M-Systems Ltd. 1995-1997 */

/*
modification history
--------------------
01p,30dec99,jmw  Add cfiscsVr4121Identify for NEC vr4121
01o,04feb99,yp   added MACRO INCLUDE_TFFS_SHOW for better scalability.
01m,21apr98,yp   added tffs subdir to include path
01n,28feb98,kbw  made man page edits to fix problems found by QE
01m,28feb98,kbw  made man page edits
01l,19jan98,hdn  added new function tffsShowAll().
01k,08jan98,hdn  added new MTD for Heurikon Baja4700.
01j,06jan98,hdn  cleaned up tffsShow() and tffsBootImagePut().
01i,18dec97,yp   added documentation on sharing bootrom with TFFS
01h,15dec97,yp   doc cleanup
01g,11dec97,hdn  added tffsRawio()'s return value check in tffsShow().
01f,08dec97,hdn  renamed mkbootTffs() to tffsCopy().
01e,05dec97,hdn  changed tffsSocket[] to LOCAL variable.
01d,05dec97,hdn  added tffsShow() and mkbootTffs().
01c,11nov97,hdn  fixed documentation of flRegisterComponents().
01b,07nov97,hdn  cleaned up.
01a,07jul97,ami  written by Amirban at M-Systems
*/

/*
DESCRIPTION
This source file, with the help of sysTffs.c, configures TrueFFS for VxWorks. 
The functions defined here are generic to all BSPs.  To include these functions
in the BSP-specific module, the BSP's sysTffs.c file includes this file. 
Within the sysTffs.c file, define statements determine which functions from
the tffsConfig.c file are ultimately included in TrueFFS.

The only externally callable routines defined in this file
are tffsShow(), tffsShowAll(), and tffsBootImagePut().  You can
exclude the show utilities if you edit config.h and
undefine INCLUDE_SHOW_ROUTINES.  You can exclude tffsBootImagePut() if you
edit sysTffs.c and undefine INCLUDE_TFFS_BOOT_IMAGE.  (If you find these
utilities are missing and you want them included, edit config.h and
define INCLUDE_SHOW_ROUTINES and INCLUDE_TFFS_BOOT_IMAGE.)

If you wish to include only the TrueFFS specific show routines you could
define INCLUDE_TFFS_SHOW instead of INCLUDE_SHOW_ROUTINES in config.h.

However, for the most part, these externally callable routines are only
a small part of the TrueFFS configuration needs handled by this file. 
The routines internal to this file make calls into the MTDs and translation
layer modules of TrueFFS.  At link time, resolving the symbols associated
with these calls pulls MTD and translation layer modules into VxWorks.

However, each of these calls to the MTDs and the translation layer modules
is only conditionally included.  The constants that control the includes
are defined in sysTffs.c.  To exclude an MTD or translation layer module,
you edit sysTffs.c, undefine the appropriate constant, and rebuild sysTffs.o.
These constants are described in the reference entry for 'sysTffs'.

INCLUDE FILES: stdcomp.h
*/


/* includes */

#include "tffs/stdcomp.h"
#include "tffs/tffsDrv.h"
#include "tffs/fatlite.h"
#include "stdio.h"


/* defines */

#ifdef INCLUDE_SHOW_ROUTINES
#define INCLUDE_TFFS_SHOW
#endif /* INCLUDE_SHOW_ROUTINES */

 

/* externs */
FLStatus lv160MTDIdentify(FLFlash vol);


/* globals */

MTDidentifyRoutine mtdTable[] =  /* MTD tables */
    {
#ifdef INCLUDE_MTD_LV160
 lv160MTDIdentify,
#endif

#ifdef INCLUDE_MTD_VR4121_CFISCS
    cfiscsVr4121Identify,
#endif /* INCLUDE_MTD_VR4121_CFISCS */

#ifdef INCLUDE_MTD_I28F016
    i28f016Identify,
#endif /* INCLUDE_MTD_I28F016 */

#ifdef INCLUDE_MTD_I28F008
    i28f008Identify,
#endif /* INCLUDE_MTD_I28F008 */

#ifdef INCLUDE_MTD_I28F008_BAJA
    i28f008BajaIdentify,
#endif /* INCLUDE_MTD_I28F008_BAJA */

#ifdef INCLUDE_MTD_AMD
    amdMTDIdentify,
#endif /* INCLUDE_MTD_AMD */

#ifdef INCLUDE_MTD_CDSN
    cdsnIdentify,
#endif /* INCLUDE_MTD_CDSN */

#ifdef INCLUDE_MTD_DOC2
    doc2Identify,
#endif /* INCLUDE_MTD_DOC2 */

#ifdef INCLUDE_MTD_CFISCS
    cfiscsIdentify,
#endif /* INCLUDE_MTD_CFISCS */

#ifdef INCLUDE_MTD_WAMD
    flwAmdMTDIdentify,
#endif /* INCLUDE_MTD_WAMD */
    };
int noOfMTDs = NELEMENTS (mtdTable); /* number of MTDs */

TLentry tlTable[] =    /* TL tables */
    {
#ifdef INCLUDE_TL_NFTL
#ifdef FORMAT_VOLUME
    {mountNFTL, formatNFTL},
#else
    mountNFTL,
#endif /* FORMAT_VOLUME */
#endif /* INCLUDE_TL_NFTL */

#ifdef INCLUDE_TL_FTL
#ifdef FORMAT_VOLUME
    {mountFTL, formatFTL},
#else
    mountFTL,
#endif /* FORMAT_VOLUME */
#endif /* INCLUDE_TL_FTL */

#ifdef INCLUDE_TL_SSFDC
#ifdef FORMAT_VOLUME
    {mountSSFDC, formatSSFDC},
#else
    mountSSFDC,
#endif /* FORMAT_VOLUME */
#endif /* INCLUDE_TL_SSFDC */
    };
int noOfTLs = NELEMENTS (tlTable); /* number of TLs */


/* locals */

LOCAL char * tffsSocket[DRIVES] = {NULL}; /* name of the socket interface */
LOCAL char * tffsVersion = "2.0"; /* Msystem's version number of TFFS */


/* forward declarations */

LOCAL VOID sysTffsInit (void);  /* BSP dependent init routine */


/*******************************************************************************
*
* flRegisterComponents - register MTD and translation layer components for use
*
* This routine registers MTD and translation layer components for use.
* This function is called by FLite once only, at initialization of the
* FLite system.
*
* NOMANUAL
*
* RETURNS: N/A
*/

void flRegisterComponents (void)
    {
    sysTffsInit ();
    }

#ifdef INCLUDE_TFFS_SHOW
/*******************************************************************************
*
* tffsShowAll - show device information on all socket interfaces
*
* This routine prints device information on all socket interfaces.
*
* RETURNS: N/A
*/

void tffsShowAll (void)
    {
    int ix;

    printf ("TFFS Version %s\n", tffsVersion);
    for (ix = 0; ix < noOfDrives; ix++)
 tffsShow (ix);
    }

/*******************************************************************************
*
* tffsShow - show device information on a specific socket interface
*
* This routine prints device information on the specified socket interface.
* This information is particularly useful when trying to determine the
* number of Erase Units required to contain a boot image.  The field called
* unitSize reports the size of an Erase Unit.
*
* If the process of getting physical information fails, an error code is
* printed. The error codes can be found in flbase.h.
*
* RETURNS: N/A
*/

void tffsShow
    (
    int driveNo   /* TFFS drive number */
    )
    {
    PhysicalInfo info;
    FLStatus status;

    if (tffsSocket[driveNo] == NULL)
 {
        printf ("%d: ---- no socket interface installed ----\n", driveNo);
 return;
 }
    status = tffsRawio (driveNo, TFFS_GET_PHYSICAL_INFO, (int)&info, 0, 0);
    if (status != OK)
 {
        printf ("%d: **** communication failed with error %d ****\n",
                driveNo, status);
 return;
 }
    printf ("%d: socket=%s: ", driveNo, tffsSocket[driveNo]);
    printf ("type=0x%x, unitSize=0x%x, mediaSize=0x%x\n", info.type,
     (UINT)info.unitSize, (UINT)info.mediaSize);
    }

#endif /* INCLUDE_TFFS_SHOW */

#ifdef INCLUDE_TFFS_BOOT_IMAGE
/*******************************************************************************
*
* tffsBootImagePut - write to the boot-image region of the flash device
*
* This routine writes an input stream to the boot-image region (if any) of
* a flash memory device.  Typically, the input stream contains a boot image,
* such as the VxWorks boot image, but you are free to use this function to
* write any data needed. The size of the boot-image region is set by
* the tffsDevFormat() call (or the sysTffsFormat() call, a BSP-specific helper
* function that calls tffsDevFormat() internally) that formats the flash
* device for use with TrueFFS. 
*
* If tffsBootImagePut() is used to put a VxWorks boot image in flash, you
* should not use the s-record version of the boot image typically produced
* by make.  Instead, you should take the pre s-record version (usually
* called 'bootrom' instead of 'bootrom.hex'), and filter out its loader
* header information using an <xxx>'ToBin' utility.  For example:
* .CS
* elfToBin < bootrom > bootrom.bin
* .CE
*
* Use the resulting 'bootrom.bin' as input to tffsBootImagePut().
*
* The discussion above assumes that you want only to use the flash device to
* store a VxWorks image that is retrieved from the flash device and then run
* out of RAM. However, because it is possible to map many flash devices
* directly into the target's memory, it is also possible run the VxWorks
* image from flash memory, although there are some restrictions:
* .IP "-"
* The flash device must be non-NAND.
* .IP "-"
* Only the text segment of the VxWorks image ('vxWorks.res_rom') may run out
* of flash memory. The data segment of the image must reside in standard RAM.
* .IP "-"
* No part of the flash device may be erased while the VxWorks image is running
* from flash memory.
* .LP
* Because TrueFFS garbage collection triggers an erase, this last restriction
* means that you cannot run a VxWorks boot image out of a flash device that
* must also support a writable file system (although a read-only file system
* is OK).
*
* This last restriction arises from the way in which flash devices are
* constructed. The current physical construction of flash memory devices does
* not allow access to the device while an erase is in
* progress anywhere on the flash device. As a result, if TrueFFS tries to
* erase a portion of the flash device, the entire device becomes inaccessible
* to all other users.  If that other user happens to be the VxWorks image
* looking for its next instruction, the VxWorks image crashes.
*
*RETURNS: OK or ERROR
*/

STATUS tffsBootImagePut
    (
    int driveNo,  /* TFFS drive number */
    int offset,   /* offset in the flash chip/card */
    char * filename  /* binary format of the bootimage */
    )
    {
    PhysicalInfo info;
    UINT unitNo;
    UINT unitSize;
    UINT addr;
    char * pBuf;
    int fd;
    int ix;

    if (tffsSocket[driveNo] == NULL)
 return (ERROR);

    if (tffsRawio (driveNo, TFFS_GET_PHYSICAL_INFO, (int)&info, 0, 0) != OK)
        {
        printErr ("Unable to get physical info from drive\n");
 return (ERROR);
 }

   pBuf = (char *)malloc (info.unitSize);

    if ((fd = open (filename, O_RDONLY, 0644)) == ERROR)
        {
        printErr ("Can't open \"%s\"\n", filename);
        return (ERROR);
 }

    addr = offset;
    unitNo = offset / info.unitSize;
    unitSize = info.unitSize - (offset % info.unitSize);

    /* If the offset is not Erase Unit aligned we need to save the the
     * contents of the region begining at the start of this erase unit
     * and ending at the specified address so we can restore it after
     * we erase the Erase Unit
     */
    if (tffsRawio (driveNo, TFFS_PHYSICAL_READ, unitNo * info.unitSize,
            offset % info.unitSize, (int)pBuf) != OK)
        {
 printErr ("Failed attempting to save Erase Unit %d\n", unitNo);
 close (fd);
 return (ERROR);
 }
       
    if (tffsRawio (driveNo, TFFS_PHYSICAL_ERASE, unitNo, 1, 0) != OK)
 {
 printErr ("Failed attempting to erase Erase Unit %d\n", unitNo);
 close (fd);
 return (ERROR);
 }

    if (tffsRawio (driveNo, TFFS_PHYSICAL_WRITE, unitNo * info.unitSize,
            offset % info.unitSize, (int)pBuf) != OK)
        {
 printErr ("Failed attempting to restore Erase Unit %d\n", unitNo);
 close (fd);
 return (ERROR);
 }

    while (unitSize)
 {
        if ((ix = read (fd, pBuf, unitSize)) == ERROR)
            {
            printErr ("Error reading inputfile: 0x%x\n", errno);
            return (ERROR);
            }

        if (ix == 0)
     break;

        if ((addr + ix) > info.mediaSize)
     {
     printErr ("Error : Attempting to write beyond Flash boundary\n");
     close (fd);
     return (ERROR);
     }

        if (tffsRawio (driveNo, TFFS_PHYSICAL_WRITE, addr, ix, (int)pBuf) != OK)
     {
     printErr ("Physical write failed at address 0x%x\n", addr);
     close (fd);
     return (ERROR);
     }

 addr += ix;
 unitSize -= ix;
 if (unitSize == 0)
     {
            unitSize = info.unitSize;
            unitNo++;
            if (tffsRawio (driveNo, TFFS_PHYSICAL_ERASE, unitNo, 1, 0) != OK)
         {
  printErr ("Failed attempting to erase Erase Unit %d\n", unitNo);
         close (fd);
         return (ERROR);
         }
     }
 }

    close (fd);
    return (OK);
    }
   
#endif /* INCLUDE_TFFS_BOOT_IMAGE */


菜鸟
2007-12-05 21:14:34     打赏
5楼
用上面的那几个文件吧!
参照着改改!

菜鸟
2007-12-06 16:44:21     打赏
6楼

谢谢您的帮助!
我昨天把自己那个amdmtd.c文件里的identify() ,write() ,erase()函数根据flash芯片好好改了一下,在运行tffsshow函数时通过了,但在tffsdevformat函数时失败了。我在擦写函数里输出打印信息,显示每个sector都擦写通过了,,可是为什么tffsdevformat最后还是返回-1,失败了呢????
能帮我分析一下吗??
我再把您的代码拷回去试试!


菜鸟
2007-12-06 21:03:34     打赏
7楼
最好用我给的sst39vf160.c
参照修改!对于你的芯片是没有问题的!
注意一下vol.noOfChips =0x1;                 /*one chip.*/
 vol.erasableBlockSize = 0x10000;   /* 64k bytes.*/
 vol.flags |= SUSPEND_FOR_WRITE;

vol.erasableBlockSize 是一个sector的大小!你的芯片肯定不一样!

菜鸟
2007-12-06 21:22:14     打赏
8楼
看看这文章会有些帮助!

电子产品世界-嵌入式系统设计-[原创]在同一片nor flash上创建两个磁盘,能够实现非...
[原创]在同一片nor flash上创建两个磁盘,能够实现非2的n次幂大小的文件系统
今天为了解决系统升级问题而考虑在同一片FLASH上创建两个独立的磁盘C和D。先打电话问了下技术支
持,说理论上可以。于是就在坛子里搜相关的文章,参考了amine的[原创]将boot flash的空闲部分作为文
件系统。
先介绍下硬件情况,程序空间256K,内存1M,文件系统FLASH16M。打算采用bootrom_res加载vxworks
映像方式启动,将vxworks映像存放在文件系统中。系统升级只需升级vxworks文件即可。原只将FLASH创建
为C盘,且向用户开放格式化磁盘操作,所以vxworks存放在C盘内有可能被用户格式化掉,不安全!由此考
虑同一片FLASH上创建两个独立的C盘和D盘,C盘为系统盘,存放vxworks映像文件,不对用户开放;D盘为
用户盘。
下面介绍具体怎么做。我将C盘大小定为1M,D盘大小定为15M。
1. sysTffs.c文件的修改。
(1)
#define FLASH_IMAGE_ADRS 0x62000000 /* 关键:为此FLASH真实基地址 */
#define FLASH_IMAGE_SIZE 0x00100000 /* 关键:为C盘大小 */
#define FLASH_BASE_ADRS 0x62100000 /* 关键:为D盘偏移地址 */
#define FLASH_SIZE 0x01000000 /* 关键:为此FLASH真实大小 */
(2)
LOCAL void sysTffsInit (void)
{
**Register ();
**Register (); /* 注册第二个设备 */
}
(3) **Register (void)中
tffsSocket[noOfDrives] = "RFA";
if (noOfDrives == 0)
{
vol.serialNo = 0;
vol.window.baseAddress = FLASH_BASE_ADRS >> 12;
http://bbs.eepw.com.cn/dispbbs.asp?boardID=3&ID=67482&page=1
(第 1/4 页)2006-6-3 9:16:08
电子产品世界-嵌入式系统设计-[原创]在同一片nor flash上创建两个磁盘,能够实现非...
}
else if (noOfDrives == 1)
{
vol.serialNo = 1;
vol.window.baseAddress = FLASH_IMAGE_ADRS >> 12;
}
noOfDrives++;
(4)
static void **SetWindow
(
FLSocket vol /* pointer identifying drive */
)
{
/* Physical base as a 4K page */
if (vol.serialNo == 0)
{
vol.window.baseAddress = FLASH_BASE_ADRS >> 12;
flSetWindowSize (&vol, FLASH_SIZE >> 12);
}
else if(vol.serialNo == 1)
{
vol.window.baseAddress = FLASH_IMAGE_ADRS >> 12;
flSetWindowSize (&vol, FLASH_IMAGE_SIZE >> 12);
}
}
(5)增加一个格式化函数
STATUS sysTffsFormatD (void) /* D盘格式化函数*/
{
STATUS status;
tffsDevFormatParams params =
{
#undef HALF_FORMAT /* lower 1M for bootimage, upper for TFFS */
#ifdef HALF_FORMAT
{0x00100000l, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL}, /* 关键:空出前1M空间 */
#else
http://bbs.eepw.com.cn/dispbbs.asp?boardID=3&ID=67482&page=1
(第 2/4 页)2006-6-3 9:16:08
电子产品世界-嵌入式系统设计-[原创]在同一片nor flash上创建两个磁盘,能够实现非...
{0x00000000l, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL},
#endif /* HALF_FORMAT */
FTL_FORMAT_IF_NEEDED
};
status = tffsDevFormat (0, (int)&params); /* 设备0 */
return (status);
}
STATUS sysTffsFormatC (void) /* C盘格式化函数*/
{
STATUS status;
tffsDevFormatParams params =
{
#undef HALF_FORMAT /* lower 512KB for bootimage, upper for TFFS */
#ifdef HALF_FORMAT
{0x00080000l, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL},
#else
{0x00000000l, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL},
#endif /* HALF_FORMAT */
FTL_FORMAT_IF_NEEDED
};
status = tffsDevFormat (1, (int)&params); /* 设备1 */
return (status);
}
2. mtd文件的修改。
if (vol.socket->serialNo == 0) /* 设备0 */
vol.chipSize = 0xF00000l; /* D盘大小 */
else if (vol.socket->serialNo == 1) /* 设备1 */
vol.chipSize = 0x100000l; /* C盘大小 */
经过上述修改,启动系统,打开shell,执行
sysTffsFormatSystem ();
usrTffsConfig(1,0,"C/");
sysTffsFormat();
http://bbs.eepw.com.cn/dispbbs.asp?boardID=3&ID=67482&page=1
(第 3/4 页)2006-6-3 9:16:08
电子产品世界-嵌入式系统设计-[原创]在同一片nor flash上创建两个磁盘,能够实现非...
usrTffsConfig(0,0,"D/");
至此成功的在同一FLASH上建立C盘和D盘。
需要特别注意,经过以下试验:
1.将FLASH_SIZE的值设置成与vol.chipSize值(0xF00000)一样,但发现格式化D盘总返回-1。不成功!论
坛中有关于只能建立2的n次幂M大小的文件系统的问题,如1M,2M,4M,8M,16M。这种情况应该符合此
问题的描述。
2.将vol.chipSize值设置成与FLASH_SIZE值(0x1000000)一样,格式化D盘时会写到FLASH_BASE_ADRS +
FLASH_SIZE (即0x63000000~0x630FFFFF地址),超出了FLASH地址空间(0x62000000~
0x62FFFFFF)。在我的系统中0x63000000~0x630FFFFF地址与0x62000000~0x620FFFFF地址为同一
FLASH空间,因此格式化D盘时就会破坏C盘的空间。同样格式化C盘也会破坏D盘空间。因此也不成功。
3.将vol.chipSize设成真实值,即0xF00000, FLASH_SIZE设成芯片大小值,即0x1000000,成功且稳定!
http://bbs.eepw.com.cn/dispbbs.asp?boardID=3&ID=67482&page=1
(第 4/4 页)2006-6-3 9:16:08

菜鸟
2007-12-07 17:56:37     打赏
9楼

非常感谢!
我今天把你给的程序稍做修改,tffs终于挂上了,,]
可是如何把我的vxworks印象通过copy命令放到/tffs下呢,,
我用copy “vxworks”,“/tffs0”
结果说找不到vxworks这个文件,,是怎么让它找到呢?


菜鸟
2007-12-07 19:12:28     打赏
10楼
我一般是用FTP来传文件到tffs,你说的方法你在网上搜索一下吧!
估计是你的vxworks路径没有设置好!

共13条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]