kuainiao

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
问题一:
API函数cyg_io_lookup的形式如下:
// Lookup a device and return its handle
Cyg_ErrNo cyg_io_lookup(
const char *name,
cyg_io_handle_t *handle );
struct cyg_devtab_entry中lookup的形式有如下:
Cyg_ErrNo  (*lookup)(struct cyg_devtab_entry **tab,
                     struct cyg_devtab_entry *sub_tab,
                     const char *name);
那么 cyg_io_lookup 的 handle指针 是怎么得到的呢?
 
---------------------------------------------------------------------
很明显,cyg_io_lookup是更上层的函数,上层函数当然会去调度下层的函数,
那我们就先从cyg_io_lookup函数的源代码看起,它定义在
....\packages\io\common\current\src\iosys.c文件中,
//
// Look up the devtab entry for a named device and return its handle.
// If the device is found and it has a "lookup" function, call that
// function to allow the device/driver to perform any necessary
// initializations.
//
 
Cyg_ErrNo
cyg_io_lookup(const char *name, cyg_io_handle_t *handle)
{
    union devtab_entry_handle_union {
        cyg_devtab_entry_t *st;
        cyg_io_handle_t h;
    } stunion;
    cyg_devtab_entry_t *t;
    Cyg_ErrNo res;
    const char *name_ptr;
    for (t = &__DEVTAB__[0]; t != &__DEVTAB_END__; t++) {
        if (cyg_io_compare(name, t->name, &name_ptr)) {
            // FUTURE: Check 'avail'/'online' here
            if (t->dep_name) {
                res = cyg_io_lookup(t->dep_name, &stunion.h);
                if (res != ENOERR) {
                    return res;
                }
            } else {
                stunion.st = NULL;
            }
            if (t->lookup) {
                // This indirection + the name pointer allows the lookup routine
                // to return a different 'devtab' handle.  This will provide for
                // 'pluggable' devices, file names, etc.
                res = (t->lookup)(&t, stunion.st, name_ptr);
                if (res != ENOERR) {
                    return res;
                }
            }
            *handle = (cyg_io_handle_t)t;
            return ENOERR;
        }
    }
    return -ENOENT;  // Not found
}
cyg_io_lookup函数将参数输入的 设备名字符串 与 所有的已安装的device的name做对比,见下面两行代码:
for (t = &__DEVTAB__[0]; t != &__DEVTAB_END__; t++) {
    if (cyg_io_compare(name, t->name, &name_ptr)) {
 
针对"问题一"而言,追究到此就可以得到答案了.   
如果对比成功找到的话,就调用找到的这个device的struct cyg_devtab_entry_t的lookup函数,
进行一番操作(至于操作什么那牵扯到另外的问题,针对"问题一"可以不管),操作成功的话,
就把对比成功的device的 cyg_devtab_entry_t指针 传给handle,这样就OK了!
这也是一个阶段性的成功。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
 
 
 
 
 
 
 
 
 
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
问题二:
沿着刚才的问题一思路,继续深入探究,
我的UART明明printf和scanf都可以正常使用,而且保证device name正确性,
那为什么cyg_io_lookup返回错误"-ENOENT"?
我怎么能看到已安装的所有设备?
已打开的设备能否重复打开?
如果cyg_io_lookup找不到device,那printf和scanf又是如何操作的?
----------------------------------------------------------------------
 
2.1 参看所有设备 和 解释为何cyg_io_lookup返回"-ENOENT"
我学着cyg_io_lookup函数中的方法,利用如下代码:
for (t = &__DEVTAB__[0]; t != &__DEVTAB_END__; t++)
      printf("%s\n", t->name);
看到了所有已安装的设备,目前我的系统中已安装devices只有如下:
/dev/ttydiag
 
/dev/haldiag
 
根本没有我想要的/dev/uart_usb,怪不得cyg_io_lookup函数执行时会找不到,
返回"-ENOENT".
 
 
2.2 何为真正的 "安装设备"?
2.2.1 怎样将设备加入设备表中?
进一步思考,根据cyg_io_lookup函数的source code看,所有安装的设备都必须在一个
以__DEVTAB__[0]开头,以__DEVTAB_END__结尾的设备表里。
找了一下__DEVTAB__和__DEVTAB_END__的声明和定义,
声明在...\packages\io\common\current\include\devtab.h中
extern cyg_devtab_entry_t __DEVTAB__[], __DEVTAB_END__;
 
定义在....\packages\io\common\current\src\ioinit.cxx中
// Define table boundaries
CYG_HAL_TABLE_BEGIN( __DEVTAB__, devtab );
CYG_HAL_TABLE_END( __DEVTAB_END__, devtab );
 
上面的两个宏定义在....\packages\hal\common\current\include\hal_tables.h中
#define __string(_x) #_x
#define __xstring(_x) __string(_x)
 
#ifndef CYG_HAL_TABLE_BEGIN
#define CYG_HAL_TABLE_BEGIN( _label, _name )                                 \
__asm__(".section \".ecos.table." __xstring(_name) ".begin\",\"aw\"\n"       \
    ".globl " __xstring(CYG_LABEL_DEFN(_label)) "\n"                         \
    ".type    " __xstring(CYG_LABEL_DEFN(_label)) ",object\n"                \
    ".p2align " __xstring(CYGARC_P2ALIGNMENT) "\n"                           \
__xstring(CYG_LABEL_DEFN(_label)) ":\n"                                      \
    ".previous\n"                                                            \
       )
#endif
 
#ifndef CYG_HAL_TABLE_END
#define CYG_HAL_TABLE_END( _label, _name )                                   \
__asm__(".section \".ecos.table." __xstring(_name) ".finish\",\"aw\"\n"      \
    ".globl " __xstring(CYG_LABEL_DEFN(_label)) "\n"                         \
    ".type    " __xstring(CYG_LABEL_DEFN(_label)) ",object\n"                \
    ".p2align " __xstring(CYGARC_P2ALIGNMENT) "\n"                           \
__xstring(CYG_LABEL_DEFN(_label)) ":\n"                                      \
    ".previous\n"                                                            \
       )
#endif
 
这部分代码有些复杂,要看明白需要许多GNU内嵌asm的许多知识,暂时先不表吧。
 
其中多次用到的宏CYG_LABEL_DEFN定义在...\packages\infra\current\include\cyg_type.h中
#ifndef CYG_LABEL_DEFN
 
#define CYG_LABEL_DEFN(_label) _label
 
#endif
 
看了这么多source code,安装一个device
需要把它弄到__DEVTAB__[0]和__DEVTAB_END__之间的一个table里,
这一点是确定无疑的。
 
但之前的经验看到代码中,安装一个device也只是用宏DEVTAB_ENTRY实例化一个struct cyg_devtab_entry的变量,
那是怎么把实例化的结构变量弄进了__DEVTAB__[0]和__DEVTAB_END__之间的table里的?
 
让我们再来回顾一下 DEVTAB_ENTRY实例化 操作,
还是以nios的UART device为例,来看其实例化的代码,
在...\packages\devs\serial\sopc\altera_avalon_uart\current\src\altera_avalon_uart_devices.c中,
/*
* Create all necessary device instances using the auto-generated devices.h.
*/
 
#define ALTERA_AVALON_UART_INSTANCE(name, device)                                  \
  static altera_avalon_uart_dev device##_info = {                                  \
    name##_BASE,                                                                   \
    name##_IRQ,                                                                    \
    name##_FREQ,                                                                   \
    (name##_FIXED_BAUD ? ALT_AVALON_UART_FB : 0) |                                 \
       (name##_USE_CTS_RTS ? ALT_AVALON_UART_FC : 0)                               \
    };                                                                             \
                                                                                   \
  static SERIAL_CHANNEL_USING_INTERRUPTS(device##_channel,                         \
                        altera_avalon_uart_funs,                                   \
                        device##_info,                                             \
                        ALTERA_AVALON_UART_BAUD(name##_BAUD),                      \
                        (name##_STOP_BITS == 1) ? CYGNUM_SERIAL_STOP_1: CYGNUM_SERIAL_STOP_2, \
                        (name##_PARITY == 'N') ? CYGNUM_SERIAL_PARITY_NONE :       \
                          (name##_PARITY == 'E') ? CYGNUM_SERIAL_PARITY_EVEN :     \
                          CYGNUM_SERIAL_PARITY_ODD,                                \
                        ALTERA_AVALON_UART_WORD_LENGTH(name##_DATA_BITS),           \
                        ((CYG_SERIAL_FLAGS_DEFAULT & ~ALTERA_AVALON_UART_RTSCTS) | \
                          name##_USE_CTS_RTS ? 0: ALTERA_AVALON_UART_RTSCTS),      \
                        &device##_info.tx_buf[0],                                  \
                        sizeof(device##_info.tx_buf),                              \
                        &device##_info.rx_buf[0],                                  \
                        sizeof(device##_info.rx_buf));                             \
                                                                                   \
  DEVTAB_ENTRY(device##_io,                                                        \
               name##_NAME,                                                        \
               0,                                                                  \
               &cyg_io_serial_devio,                                               \
               altera_avalon_uart_init,                                            \
               altera_avalon_uart_lookup,                                          \
               &device##_channel);
 
 
左看看又看看,也就只有这一个DEVTAB_ENTRY实例化操作和这个有关。
那还是好好的研究一个DEVTAB_ENTRY这个宏吧,它的定义和struct cyg_devtab_entry的定义
在...\packages\io\common\current\include\devtab.h中:
 
typedef struct cyg_devtab_entry {
    const char        *name;
    const char        *dep_name;
    cyg_devio_table_t *handlers;
    bool             (*init)(struct cyg_devtab_entry *tab);
    Cyg_ErrNo        (*lookup)(struct cyg_devtab_entry **tab,
                               struct cyg_devtab_entry *sub_tab,
                               const char *name);
    void              *priv;
    unsigned long     status;
} CYG_HAL_TABLE_TYPE cyg_devtab_entry_t;
 
#define CYG_DEVTAB_STATUS_AVAIL   0x0001
#define CYG_DEVTAB_STATUS_CHAR    0x1000
#define CYG_DEVTAB_STATUS_BLOCK   0x2000
 
extern cyg_devtab_entry_t __DEVTAB__[], __DEVTAB_END__;
 
#define CHAR_DEVTAB_ENTRY(_l,_name,_dep_name,_handlers,_init,_lookup,_priv)  \
cyg_devtab_entry_t _l CYG_HAL_TABLE_ENTRY(devtab) = {                   \
   _name,                                                               \
   _dep_name,                                                           \
   _handlers,                                                           \
   _init,                                                               \
   _lookup,                                                             \
   _priv,                                                               \
   CYG_DEVTAB_STATUS_CHAR                                               \
};
 
#define BLOCK_DEVTAB_ENTRY(_l,_name,_dep_name,_handlers,_init,_lookup,_priv)  \
cyg_devtab_entry_t _l CYG_HAL_TABLE_ENTRY(devtab) = {                   \
   _name,                                                               \
   _dep_name,                                                           \
   _handlers,                                                           \
   _init,                                                               \
   _lookup,                                                             \
   _priv,                                                               \
   CYG_DEVTAB_STATUS_BLOCK                                              \
};
 
#define DEVTAB_ENTRY(_l,_name,_dep_name,_handlers,_init,_lookup,_priv) \
        CHAR_DEVTAB_ENTRY(_l,_name,_dep_name,_handlers,_init,_lookup,_priv)
 
仔仔细细的观察,真正定义struct cyg_devtab_entry变量的代码是
cyg_devtab_entry_t _l CYG_HAL_TABLE_ENTRY(devtab) = {                   \
    .
    .
    .
};
注意一般定义一个名为"_l"的struct cyg_devtab_entry变量
只需要cyg_devtab_entry_t _l = {....} 就够了,
那多出来的CYG_HAL_TABLE_ENTRY(devtab)是什么东西呢?这可能就是关键所在!
trace宏CYG_HAL_TABLE_ENTRY,发现其与CYG_HAL_TABLE_BEGIN和CYG_HAL_TABLE_END一样
定义在....\packages\hal\common\current\include\hal_tables.h中
#ifndef CYG_HAL_TABLE_ENTRY
#define CYG_HAL_TABLE_ENTRY( _name ) \
        CYGBLD_ATTRIB_SECTION(".ecos.table." __xstring(_name) ".data")
#endif
其中又用了一个宏CYGBLD_ATTRIB_SECTION,定义在...\packages\infra\current\include\cyg_type.h中,
// Assign a defined variable to a specific section
# if !defined(CYGBLD_ATTRIB_SECTION)
#  define CYGBLD_ATTRIB_SECTION(__sect__) __attribute__((section (__sect__)))
# endif
 
(注:
  其中的__xstring也定义在hal_tables.h中,就在CYG_HAL_TABLE_BEGIN的定义上面(细心的话会已经发现),
  #define __string(_x) #_x
  #define __xstring(_x) __string(_x)
  "#_x"中的 这个"单井号" 意思是将其后面的宏参数进行字符串化操作,
  简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。
)
 
这样贯串起来,让我们试着来展开CYG_HAL_TABLE_ENTRY(devtab),其等于:
__attribute__((section (".ecos.table." "devtab" ".data")))
 
详细的懒得去查了,但其大致的意义应该是说cyg_devtab_entry_t _l这个变量
其存放的位置有限定属性(".ecos.table." "devtab" ".data").
 
这样好像有点模糊的联系起来了,再来回顾__DEVTAB__和__DEVTAB_END__的声明和定义,
 
声明在...\packages\io\common\current\include\devtab.h中
extern cyg_devtab_entry_t __DEVTAB__[], __DEVTAB_END__;
 
定义在....\packages\io\common\current\src\ioinit.cxx中
// Define table boundaries
CYG_HAL_TABLE_BEGIN( __DEVTAB__, devtab );
CYG_HAL_TABLE_END( __DEVTAB_END__, devtab );
 
上面的两个宏定义在....\packages\hal\common\current\include\hal_tables.h中
#define __string(_x) #_x
#define __xstring(_x) __string(_x)
 
#ifndef CYG_HAL_TABLE_BEGIN
#define CYG_HAL_TABLE_BEGIN( _label, _name )                                 \
__asm__(".section \".ecos.table." __xstring(_name) ".begin\",\"aw\"\n"       \
    ".globl " __xstring(CYG_LABEL_DEFN(_label)) "\n"                         \
    ".type    " __xstring(CYG_LABEL_DEFN(_label)) ",object\n"                \
    ".p2align " __xstring(CYGARC_P2ALIGNMENT) "\n"                           \
__xstring(CYG_LABEL_DEFN(_label)) ":\n"                                      \
    ".previous\n"                                                            \
       )
#endif
 
#ifndef CYG_HAL_TABLE_END
#define CYG_HAL_TABLE_END( _label, _name )                                   \
__asm__(".section \".ecos.table." __xstring(_name) ".finish\",\"aw\"\n"      \
    ".globl " __xstring(CYG_LABEL_DEFN(_label)) "\n"                         \
    ".type    " __xstring(CYG_LABEL_DEFN(_label)) ",object\n"                \
    ".p2align " __xstring(CYGARC_P2ALIGNMENT) "\n"                           \
__xstring(CYG_LABEL_DEFN(_label)) ":\n"                                      \
    ".previous\n"                                                            \
       )
#endif
 
结合起来看,其意义就是在名为"devtab"(它带入了宏参数_name)的.ecos.table.表中,
定义了一个label  __DEVTAB__ 作为表的begin,
定义了另一个label  __DEVTAB_END__作为表的finish,
因为这个label本身也就是标明存储空间中的位置,
所以在...\packages\io\common\current\include\devtab.h中
extern cyg_devtab_entry_t __DEVTAB__[], __DEVTAB_END__;
申明一下,它们也就成为了指向struct cyg_devtab_entry变量的指针了,
其中的__DEVTAB__还可以作为数组名. 高明的做法!
 
注意到了没,这里.ecos.table.表的名字为devtab,
而刚刚CYG_HAL_TABLE_ENTRY(devtab) = __attribute__((section (".ecos.table." "devtab" ".data")))
这里.ecos.table.表的名字也为devtab,
这说明了用 "cyg_devtab_entry_t _l CYG_HAL_TABLE_ENTRY(devtab)= {..."
定义的变量都在 名为devtab的.ecos.table.表中,
这也就意味着,我们每次用DEVTAB_ENTRY实例化struct cyg_devtab_entry变量的同时
就已经将之加入到了__DEVTAB__[0]和__DEVTAB_END__之间的table里。
这个问题搞定!
 
 
 
 
2.2.2 怎么确定哪个设备的DEVTAB_ENTRY实例化代码被执行了?
 
我总觉得device的安装应该和ecos编译前的configure选项有关系,这其中到底有什么联系呢?
 
实际上确实是有关系的,我们知道ecos是以灵活的模块化配置安装为特色,
ecos中package的配置和编译,不像linux中以makefile为工具完成,其有一套自己独特的cdl脚本文件,
就是package的配置文件。
 
还是以nios ecos的串口驱动为例,上面的分析中,我们已知串口的DEVTAB_ENTRY实例化代码在
在...\packages\devs\serial\sopc\altera_avalon_uart\current\src\altera_avalon_uart_devices.c中,
那看实例化有没有执行,也就是看这个文件有没有执行,这个文件执不执行应该看其所在package的CDL文件,
很容易根据找到altera_avalon_uart_devices.c所在目录找到该文件,
...\packages\devs\serial\sopc\altera_avalon_uart\current\cdl\altera_avalon_uart.cdl
内容如下:
cdl_package CYGPKG_ALTERA_AVALON_UART {
    display       "Altera Avalon UART serial device driver"
 
    parent        CYGPKG_IO_SERIAL_DEVICES
    active_if     CYGPKG_IO_SERIAL
    active_if     {!CYGHWR_DETECTED_SOPC_DEVICES || is_substr (CYGHWR_DETECTED_SOPC_DEVICE_LIST, " altera_avalon_uart ")}
 
    description   "
        This option enables the serial device drivers for the
        Altera Avalon UART."
 
    compile       altera_avalon_uart.c
    compile -library=libextras.a  altera_avalon_uart_devices.c
 
    cdl_option CYGDAT_ALT_AVALON_UART_BUF_LEN {
        display       "Receive/transmit buffer size"
        flavor        data
        default_value { "64" }
        description "
            The length of the transmit and receive buffers in bytes."
    }
 
    .
    .
    .
}
其中定义了这个package的parent,和其active的条件,
以及它会compile altera_avalon_uart.c和altera_avalon_uart_devices.c.
而我们现在的问题是根据2.2中所述,设备表中没有/dev/uart_usb设备,
那就说明altera_avalon_uart_devices.c中的UART的DEVTAB_ENTRY实例化代码没有被执行,
那只能说明这个package没有被active。
为什么呢?
那就只能看ecos的编译配置文件了,是一个.ecc文件,
打开我正在使用的这个有问题的ecos配置ecc文件,找到相对应的cdl_package CYGPKG_ALTERA_AVALON_UART项,内容如下:
# >
# Altera Avalon UART serial device driver
# This option enables the serial device drivers for the
# Altera Avalon UART.
#
cdl_package CYGPKG_ALTERA_AVALON_UART {
    # Packages cannot be added or removed, nor can their version be changed,
    # simply by editing their value. Instead the appropriate configuration
    # should be used to perform these actions.
 
    # This option is not active
    # The parent CYGPKG_IO_SERIAL_DEVICES is disabled
    # ActiveIf constraint: CYGPKG_IO_SERIAL
    #     CYGPKG_IO_SERIAL == current
    #   --> 1
    # ActiveIf constraint: !CYGHWR_DETECTED_SOPC_DEVICES || is_substr (CYGHWR_DETECTED_SOPC_DEVICE_LIST, " altera_avalon_uart ")
    #     CYGHWR_DETECTED_SOPC_DEVICES == 1
    #     CYGHWR_DETECTED_SOPC_DEVICE_LIST == " altera_avalon_tri_state_bridge altera_avalon_cfi_flash altera_avalon_new_sdram_controller altera_avalon_timer altera_avalon_uart altera_avalon_pio altera_avalon_sysid altera_avalon_dma altera_avalon_spi "
    #   --> 1
 
    # This value cannot be modified here.
    # Flavor: booldata
    # Current value: 1 current
};
 
看到没,这个package没有active,原因是The parent CYGPKG_IO_SERIAL_DEVICES is disabled.
那它的parent的情况如下:
# <
# Hardware serial device drivers
# This option enables the hardware device drivers
# for the current platform.
#
cdl_component CYGPKG_IO_SERIAL_DEVICES {
    # Flavor: bool
    user_value 0
    # value_source user
    # Default value: 0
 
    # The following properties are affected by this value
    # option CYGPKG_IO_SERIAL_TESTS
    #     Calculated:  CYGPKG_IO_SERIAL_DEVICES ? "tests/serial1 tests/serial2 tests/serial3 tests/serial4 tests/serial5 tests/tty1 tests/tty2 tests/flow1 tests/flow2" : ""
};
看到没,其user_value 0,怪不得呢,原来问题的根子在这里。
一定是我在configure时在ecos的configtool界面中,没有勾选到CYGPKG_IO_SERIAL_DEVICES所对应项。
 
 
2.2.3 一般uart设备与console device的"冲突"?
知道了问题所在,那我们就去去重新configure ecos吧,把CYGPKG_IO_SERIAL_DEVICES对应项勾选上,
重新编译,重新编如下代码测试,
for (t = &__DEVTAB__[0]; t != &__DEVTAB_END__; t++)
  printf("%s\n", t->name);
 
err = cyg_io_lookup("/dev/uart_usb",&ser);   
if (ENOERR == err)
{
    while(1)
    {
      len = 1;
      cyg_io_read(ser,&ccc,&len);
      len = 1;
      cyg_io_write(ser, &ccc, &len);
    }
}
else printf("error code %d!\n",err);
 
 
啊,还不行......
虽然代码
for (t = &__DEVTAB__[0]; t != &__DEVTAB_END__; t++)
   printf("%s\n", t->name);
的运行结果中有了/dev/uart_usb
但cyg_io_lookup函数查找/dev/uart_usb,返回错误"-EPERM".
为啥呢?点解?
让我们来重新看一下cyg_io_lookup的代码吧:
//
// Look up the devtab entry for a named device and return its handle.
// If the device is found and it has a "lookup" function, call that
// function to allow the device/driver to perform any necessary
// initializations.
//
 
Cyg_ErrNo
cyg_io_lookup(const char *name, cyg_io_handle_t *handle)
{
    union devtab_entry_handle_union {
        cyg_devtab_entry_t *st;
        cyg_io_handle_t h;
    } stunion;
    cyg_devtab_entry_t *t;
    Cyg_ErrNo res;
    const char *name_ptr;
    for (t = &__DEVTAB__[0]; t != &__DEVTAB_END__; t++) {
        if (cyg_io_compare(name, t->name, &name_ptr)) {
            .
            .
            .
            if (t->lookup) {
                // This indirection + the name pointer allows the lookup routine
                // to return a different 'devtab' handle.  This will provide for
                // 'pluggable' devices, file names, etc.
                res = (t->lookup)(&t, stunion.st, name_ptr);
                if (res != ENOERR) {
                    return res;
                }
            }
            *handle = (cyg_io_handle_t)t;
            return ENOERR;
        }
    }
    return -ENOENT;  // Not found
}
可以看到其在 查找到与输入参数name这个设备名相匹配的设备后,会调用这个设备的lookup函数,
    res = (t->lookup)(&t, stunion.st, name_ptr);
 
对于nios ecos的UART设备来说,它的struct cyg_devtab_entry_t的lookup函数在哪儿呢?
在....\packages\devs\serial\sopc\altera_avalon_uart\current\src\altera_avalon_uart_devices.c文件中,
有UART设备的struct cyg_devtab_entry_t实例化代码,如下:
L108  DEVTAB_ENTRY(device##_io,                                                        \
                   name##_NAME,                                                        \
                   0,                                                                  \
                   &cyg_io_serial_devio,                                               \
                   altera_avalon_uart_init,                                            \
                   altera_avalon_uart_lookup,                                          \
L114               &device##_channel);
从中可看出struct cyg_devtab_entry_t的lookup函数是altera_avalon_uart_lookup函数,
它定义在....\packages\devs\serial\sopc\altera_avalon_uart\current\src\altera_avalon_uart.c中,如下:
/*---------------------------------------------------------------------
* altera_avalon_uart_lookup
*
* This is called to initialise a device upon first access.
*/
 
Cyg_ErrNo altera_avalon_uart_lookup(struct cyg_devtab_entry **tab,
                                    struct cyg_devtab_entry *sub_tab,
                                    const char *name)
{
  serial_channel *chan = (serial_channel *)(*tab)->priv;
  altera_avalon_uart_dev *uart_chan = (altera_avalon_uart_dev *)chan->dev_priv;
 
  if (__builtin_strcmp (DEV_NAME(CYGHWR_HAL_NIOS2_VV_DEBUG_DEV), (*tab)->name) &&
      __builtin_strcmp (DEV_NAME(CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV), (*tab)->name))
  {
    (chan->callbacks->serial_init)(chan);
 
    /* enable interrupts at the device */
   
    if (chan->out_cbuf.len != 0)
    {
      cyg_drv_interrupt_create(uart_chan->irq,
                               99,                     /* Priority - unused */
                               (cyg_addrword_t)chan,   /* Data item passed to interrupt handler */
                               altera_avalon_uart_ISR,
                               altera_avalon_uart_DSR,
                               &uart_chan->serial_interrupt_handle,
                               &uart_chan->serial_interrupt);
 
      cyg_drv_interrupt_attach(uart_chan->serial_interrupt_handle);
 
      IOWR_ALTERA_AVALON_UART_CONTROL(uart_chan->base,
                      ALTERA_AVALON_UART_CONTROL_RTS_MSK  |
                      ALTERA_AVALON_UART_CONTROL_RRDY_MSK |
                      ALTERA_AVALON_UART_CONTROL_DCTS_MSK);
 
      cyg_drv_interrupt_unmask(uart_chan->irq);
    }
    return ENOERR;
  }
  else
  {
    return EPERM;
  }
}
(注:
  宏DEV_NAME的定义也在altera_avalon_uart.c中,如下:
    /*
     * Macro used to convert the CDL supplied device name into a string.
     */
    #define _DEV_NAME(dev) #dev
    #define DEV_NAME(dev) _DEV_NAME(dev)
 
  而__builtin_strcmp函数,可是让人大费了一番周折,下面是我好不容易找到的:
    __builtin_strcmp()函数是GNU本身内建的还是,它也不是针对某一种CPU架构的特殊builtin函数,
    而是对于所有的ISO C90 functions,GNU本身就内建了。strcmp真是ISO C90 functions之一。
    看看GNU文档怎么说吧:
    The ISO C90 functions abort, abs, acos, asin, atan2, atan, calloc, ceil, cosh, cos, exit, exp,
    fabs, floor, fmod, fprintf, fputs, frexp, fscanf, isalnum, isalpha, iscntrl, isdigit, isgraph,
    islower, isprint, ispunct, isspace, isupper, isxdigit, tolower, toupper, labs, ldexp, log10, log,
    malloc, memchr, memcmp, memcpy, memset, modf, pow, printf, putchar, puts, scanf, sinh, sin, snprintf,
    sprintf, sqrt, sscanf, strcat, strchr, strcmp, strcpy, strcspn, strlen, strncat, strncmp, strncpy,
    strpbrk, strrchr, strspn, strstr, tanh, tan, vfprintf, vprintf and vsprintf are all recognized as built-in functions
    unless -fno-builtin is specified (or -fno-builtin-function is specified for an individual function).
    All of these functions have corresponding versions prefixed with __builtin_.
 
  还有一点,原来strcmp函数,如果两个字符串相等,返回值是0,我一开始一直想当然的认为相等的话返回值是1,
    结果逻辑都搞反了。
)
 
其中create了interrrupt并对串口硬件的流控进行了一些配置,不过这些对与我们当前研究的主题不重要,
对我们重要的是这两行:   
if (__builtin_strcmp (DEV_NAME(CYGHWR_HAL_NIOS2_VV_DEBUG_DEV), (*tab)->name) &&
    __builtin_strcmp (DEV_NAME(CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV), (*tab)->name))
{
  .
  .
  .
}
else
{
  return EPERM;
}
注意到没,整个函数体就是这个if结构,如果一开始的if条件不满足,就直接返回EPERM,
这也正是我们刚才出错的返回值。
这个if条件的意思就是,如果我们的 传入的设备名(*tab)->name 如果和
DEV_NAME(CYGHWR_HAL_NIOS2_VV_DEBUG_DEV) 和
DEV_NAME(CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV)
都不相等,才执行....,并return ENOERR
如有一个相等,就return EPERM.
 
那就赶紧编段代码,看看当前DEV_NAME(CYGHWR_HAL_NIOS2_VV_DEBUG_DEV)和
DEV_NAME(CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV)的值是什么,
代码如下:
printf("DEV_NAME(CYGHWR_HAL_NIOS2_VV_DEBUG_DEV) = %s\n", DEV_NAME(CYGHWR_HAL_NIOS2_VV_DEBUG_DEV) );
printf("DEV_NAME(CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV) = %s\n", DEV_NAME(CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV) );
结果如下:
DEV_NAME(CYGHWR_HAL_NIOS2_VV_DEBUG_DEV) = none
 
DEV_NAME(CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV) = /dev/uart_usb
 
啊,原来这样,怪不得!
 
实际上CYGHWR_HAL_NIOS2_VV_DEBUG_DEV和CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV都在ecos的配置文件ecc中,
看我们当前的ecc文件中他们俩的相关内容:
# Debug device
# The name of the device connected to the debug channel.
#
cdl_option CYGHWR_HAL_NIOS2_VV_DEBUG_DEV {
    # Flavor: data
    user_value none
    # value_source user
    # Default value: /dev/uart_usb
    # Legal values:  "/dev/uart_usb" "none"
};
 
# Diagnostic console device
# The name of the device connected to the console channel.
#
cdl_option CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV {
    # Flavor: data
    # No user value, uncomment the following line to provide one.
    # user_value /dev/uart_usb
    # value_source default
    # Default value: /dev/uart_usb
    # Legal values:  "/dev/uart_usb" "none"
};
确实和我们刚刚用代码测试的结果一致。
 
一个很自然的想法,那我们在configure ecos时,
把CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV和CYGHWR_HAL_NIOS2_VV_DEBUG_DEV一样都搞成none,
不就在altera_avalon_uart_lookup函数中就可以执行过了,这样cyg_io_lookup函数不就可以返回return ENOERR了。
 
好的,说干就干,重新configure ecos,重新测试。
for (t = &__DEVTAB__[0]; t != &__DEVTAB_END__; t++)
  printf("%s\n", t->name);
 
err = cyg_io_lookup("/dev/uart_usb",&ser);   
if (ENOERR == err)
{
    while(1)
    {
      len = 1;
      cyg_io_read(ser,&ccc,&len);
      len = 1;
      cyg_io_write(ser, &ccc, &len);
    }
}
else printf("error code %d!\n",err);
啊,不好,怎么程序失灵了,怎么程序执行什么都不打印了?
我当初也吓了一跳,以为整个ecos系统出什么问题了,到处找原因,
后来仔细一想,不对,
其实
    while(1)
    {
      len = 1;
      cyg_io_read(ser,&ccc,&len);
      len = 1;
      cyg_io_write(ser, &ccc, &len);
    }
这段代码还是在执行的,可以测试硬件串口收发是完全工作正常的。
原来是printf函数失灵了,那应该是我们把CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV改成了none造成的。
看来printf函数是用的CONSOLE_DEV设备呀,而原来的CONSOLE_DEV设备使用的就是我们的/dev/uart_usb.
 
没有了printf函数还是很麻烦的,printf我们还是要用的,那要怎么办才好呢?
有两种方法,两种我都试过:
 
第一种,我们就不用/dev/uart_usb设备算了,也就不必在configure ecos时
        把"cdl_package CYGPKG_ALTERA_AVALON_UART"给active出来了。
        直接使用/dev/haldiag就好了(我们在上面打印整个设备表时,有这个device),
        测试代码如下:
        err = cyg_io_lookup("/dev/haldiag",&ser);   
        if (ENOERR == err)
        {
            while(1)
            {
              len = 1;
              cyg_io_read(ser,&ccc,&len);
              len = 1;
              cyg_io_write(ser, &ccc, &len);
            }
        }
        else printf("error code %d!\n",err);
        这样,硬件串口也是可以完全正常工作的,printf也ok,我测试过。
        这种方法简单方便,不必改configure ecos,只改我们的代码即可。
        但总觉得不过瘾,不尽兴。
  
第二种,我就偏 既想用printf又想用/dev/uart_usb,鱼与熊掌都想兼得,
        怎么办?
        那就只能修改altera_avalon_uart_lookup这个函数的源代码了,
        我找到ecos系统源代码中的altera_avalon_uart.c这个文件,先将其备份好,
        注意其原是只读属性,修改前需先改属性.
        将altera_avalon_uart_lookup修改成如下:       
        Cyg_ErrNo altera_avalon_uart_lookup(struct cyg_devtab_entry **tab,
                                    struct cyg_devtab_entry *sub_tab,
                                    const char *name)
        {
          serial_channel *chan = (serial_channel *)(*tab)->priv;
          altera_avalon_uart_dev *uart_chan = (altera_avalon_uart_dev *)chan->dev_priv;
       
        //  if (__builtin_strcmp (DEV_NAME(CYGHWR_HAL_NIOS2_VV_DEBUG_DEV), (*tab)->name) &&   //dl: These lines was commented by DingLei.
        //      __builtin_strcmp (DEV_NAME(CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV), (*tab)->name))
        //  {
            (chan->callbacks->serial_init)(chan);
       
            /* enable interrupts at the device */
           
            if (chan->out_cbuf.len != 0)
            {
              cyg_drv_interrupt_create(uart_chan->irq,
                                       99,                     /* Priority - unused */
                                       (cyg_addrword_t)chan,   /* Data item passed to interrupt handler */
                                       altera_avalon_uart_ISR,
                                       altera_avalon_uart_DSR,
                                       &uart_chan->serial_interrupt_handle,
                                       &uart_chan->serial_interrupt);
       
              cyg_drv_interrupt_attach(uart_chan->serial_interrupt_handle);
       
              IOWR_ALTERA_AVALON_UART_CONTROL(uart_chan->base,
                              ALTERA_AVALON_UART_CONTROL_RTS_MSK  |
                              ALTERA_AVALON_UART_CONTROL_RRDY_MSK |
                              ALTERA_AVALON_UART_CONTROL_DCTS_MSK);
       
              cyg_drv_interrupt_unmask(uart_chan->irq);
            }
            return ENOERR;
        //  }
        //  else                 //dl: These lines was commented by DingLei.
        //  {
        //    return EPERM;
        //  }
        }
        将整个if结构给注释掉,让进来就不必检查什么劳什子的
        CYGHWR_HAL_NIOS2_VV_DEBUG_DEV和CYGHWR_HAL_NIOS2_VV_CONSOLE_DEV了,
        直接去执行中断创建等,然后返回return ENOERR.
        然后再configure ecos时,还是勾选CYGPKG_IO_SERIAL_DEVICES对应项
        把cdl_package CYGPKG_ALTERA_AVALON_UART"给active出来,
        并把CONSOLE_DEV设为/dev/uart_usb,DEBUG_DEV设为none.
        然后重新编译ecos系统。
        测试,
        for (t = &__DEVTAB__[0]; t != &__DEVTAB_END__; t++)
          printf("%s\n", t->name);
         
        err = cyg_io_lookup("/dev/uart_usb",&ser);   
        if (ENOERR == err)
        {
            while(1)
            {
              len = 1;
              cyg_io_read(ser,&ccc,&len);
              len = 1;
              cyg_io_write(ser, &ccc, &len);
            }
        }
        else printf("error code %d!\n",err);
        测试printf正常,cyg_io_read和cyg_io_write串口收发也正常,
        和第一种方法结果一样。
        但这种方法要野蛮一些,要过瘾些。
       
        但是,我觉得这种也存在一定的隐患,这样等于CONSOLE_DEV 如printf 和
        我们自己的串口收发程序 使用的是同一个device: /dev/uart_usb,
        这样可能多线程应用中可能出现冲突的情况(单线程程序中当然没问题),比如,
        一个线程中正在printf,而另一个线程也要运行我自己编的串口收发程序,
        那可能就要悲催了....这种情况我还没测试过,等以后遇到了再说。
        不行就乖乖再用第一种方法呗。
       
      
OK,nios ecos中串口驱动的问题到此算完美解决!
posted on 2013-03-20 17:36  kuainiao  阅读(557)  评论(0编辑  收藏  举报
View Code