7  serial_port_list.c 소스

 

7.1 동작 원리

 

    1) 시스템이 제공하는 사용 포트 목록 추출
   
        /proc/tty/drivers 을 열은 후
        5 개의 문자열로 분리 한 후 
        마지막 인자가 "serial" 문자열이면 시리얼 디바이스라고 판단하고
        3번 인자를 주번호로
        4번 인자를 부번호의 시작 번호와 종료 번호로 분리 한후( '-' 가 있으면 )
        port_info 구조체에 등록한다.
        이때 내부적으로 다루는 이름을 "/dev/ttyS" 로 시작하고 번호는 '1' 부터 시작하도록 만든다.
       
    2) '.' 형으로 함수 호출 및 객체 멤버 변수 처리
   
        값을 대입하는 경우에는 메타 테이블 serial_port_list_meta 에 의해 spl_newindex() 함수가 호출되고
        값을 읽는 경우에는 spl_index()가 호출된다.
       
        이 두 경우 모두 다 문자열 타입이면 내부적으로 처리되는 멤버 변수명이거나 함수명인가를 확인하고
        해당 하는 것에 대한 처리를 즉시 처리 한다.
       
        '.' 를 통한 함수를 호출할 경우에 함수 호출시 스택에는 사용자 데이터가 전달되지 않기 때문에
        spl_index() 함수가 호출될때 전역 변수로 사용자 정보 구조체를 등록하여 처리 한다.
        이런 이유로 이 사용자 데이터는 쓰레드에 취약 한 구조를 갖는다.
       
        **) 나중에 이 부분은 보완해야 한다.
       
7.2 구조체 설명

 

    SERIAL_PORT_LIST    : 시리얼 객체를 구별하거나 메타 테이블을 위한 문자열
    MAX_SERIAL_PORT     : 객체가 다룰수 있는 최대 시리얼 포트 정보
   
    serial_port_info_t  : 하나의 시리얼 포트 정보 구조체

                            typedef struct {
                                char device_name[128];  // 장치 파일 이름 예) "/dev/ttyS0"
                                int  major;;            // 장치 주 번호
                                int  minor;             // 장치 부 번호
                            } serial_port_info_t;
                           
    serial_port_list_t  : 시리얼 포트를 관리 하기 위한 구조체
   
                            typedef struct {
                                int                 count;                  // 총 갯수
                                int                 select;                 // 선택된 목록
                                serial_port_info_t  ports[MAX_SERIAL_PORT]; // 포트 정보
                            } serial_port_list_t;


    luaL_reg serial_port_list_meta[] : 객체를 위한 메타 테이블
   

7.3 함수 요약

 

    serial_port_list_t *spl_new_userdata(lua_State *L);             // 새로운 사용자 데이터를 만들어 스택에 넣는다.
    serial_port_list_t *spl_get_data(lua_State *L, int index);      // 스택에서 사용자 구조체 주소를 얻는다.
    int                 spl_newindex( lua_State *L );               // 객체에 spl.select = 3 과 같은 객체 멤버에 접근 했을때의 처리를 한다.
    int                 spl_index( lua_State *L );                  // 객체에 ret = sp.select 와 같은 객체 멤버의 값을 얻거나

                                                                                    // 멤버 함수를 호출했을 때 처리를 한다.
    int                 spl_tostring (lua_State *L)                 // 객체에 대한 값을 얻고자 할때 문자열로 돌려준다.
    int                 spl_gc (lua_State *L)                       // 가베지 처리를 루아가 요청할때 호출되어 처리된다.
    int                 serial_port_list_new_metatable(lua_State *L );  // 메타 테이블을 글로벌 변수로 등록한다.
    int                 serial_port_list( lua_State *L );           // 포트 리스트를 최초에 구하는 실제 함수
  

7.4 소스

 

1) serial_port_list.h


 

// serial_port_list.h
#define SERIAL_PORT_LIST    "SERIAL_PORT_LIST"
#define MAX_SERIAL_PORT     256
typedef struct {
    char device_name[128];
    int  major;
    int  minor;
} serial_port_info_t;
typedef struct {
    int                 count;
    int                 select;
    serial_port_info_t  ports[MAX_SERIAL_PORT];
} serial_port_list_t;
extern int serial_port_list( lua_State *L );
extern int serial_port_list_new_metatable(lua_State *L );

 

2) serial_port_list.c

 

 #include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/limits.h>
#define LUA_LIB
#include <lua.h>
#include <lauxlib.h>
#include <serial_port_list.h>
static serial_port_list_t *call_func_spl = NULL;
static serial_port_list_t *spl_new_userdata(lua_State *L)
{
  serial_port_list_t *spl = (serial_port_list_t *) 
                            lua_newuserdata(L, sizeof(serial_port_list_t));
  luaL_getmetatable(L, SERIAL_PORT_LIST);
  lua_setmetatable(L, -2);
  return spl;
}
static serial_port_list_t * spl_get_data(lua_State *L, int index)
{
    serial_port_list_t *spl = (serial_port_list_t *)
                              lua_touserdata(L, index);
    if (spl == NULL) luaL_typerror(L, index, SERIAL_PORT_LIST);
    return spl;
}
int serial_port_list( lua_State *L )
{
    FILE                *fp;
    int                 ret;
    char                linebuff        [128];
    char                device_name     [128];
    char                device_file_name[128];
    char                minor_name      [128];
    char                type_name       [128];
    int                 major;
    int                 minor_start;
    int                 minor_end;
    serial_port_list_t *spl;
    serial_port_info_t *port_info;
    int                 lp;
    spl = spl_new_userdata(L);
    spl->count  = 0;
    spl->select = 0;
    fp = fopen("/proc/tty/drivers", "r");
    if(fp == NULL)  return 1;
    while(1) 
    {
        if( fgets (linebuff, sizeof(linebuff) , fp ) == NULL ) break;
        ret = sscanf(linebuff, "%s %s %d %s %s", 
                                device_name, 
                                device_file_name,
                                &major,
                                minor_name,
                                type_name
                    );
        if( ret != 5 ) continue;
        if( strcmp( type_name, "serial" ) ) continue;
        if( strchr( minor_name, '-' ) == NULL )
        {
            ret = sscanf( minor_name, "%d", &minor_start );
            minor_end = minor_start;
        }
        else
        {
            ret = sscanf( minor_name, "%d-%d", &minor_start, &minor_end );
        }   
        spl->count = 0;
        for( lp = minor_start; lp <= minor_end; lp++ )
        {
            port_info = (serial_port_info_t *) &(spl->ports[lp-minor_start]);
            sprintf( port_info->device_name, "/dev/ttyS%d", spl->count );
            port_info->major = major;
            port_info->minor = lp;
            spl->count++;
        }
    }
    fclose( fp );
    return 1;
}
static int spl_newindex( lua_State *L )
{
    serial_port_list_t  *spl;
    char                *var_str; 
    int                 select;
    if( lua_type(L, 2) == LUA_TSTRING ) 
    {
        spl     = spl_get_data ( L, 1 );
        var_str = (char * ) lua_tostring ( L, 2 );  
        select  = lua_tointeger( L, 3 );    
        if( !strcmp( var_str, "select"  ) ) 
        { 
            if( ( select > 0 ) && ( select <= spl->count ) )
            {
                spl->select = select;
            }   
        }   
    }   
    return 0;
}
static int spl_index( lua_State *L )
{
    serial_port_list_t  *spl;
    serial_port_info_t *port_info;
    char                *var_str; 
    if( lua_type(L, 2) == LUA_TSTRING ) 
    {
        spl         = spl_get_data( L, 1 );
        port_info   = (serial_port_info_t *) &(spl->ports[spl->select - 1]);
        var_str = (char *) lua_tostring( L, 2 );          
        if( !strcmp( var_str, "count"   ) ) { lua_pushinteger( L, spl->count            ); return 1; }
        if( !strcmp( var_str, "select"  ) ) { lua_pushinteger( L, spl->select           ); return 1; }
        if( !strcmp( var_str, "name"    ) ) { lua_pushstring ( L, port_info->device_name); return 1; }
        if( !strcmp( var_str, "major"   ) ) { lua_pushinteger( L, port_info->major      ); return 1; }
        if( !strcmp( var_str, "minor"   ) ) { lua_pushinteger( L, port_info->minor      ); return 1; }
        if( !strcmp( var_str, "make_device_file" ) ) { call_func_spl = spl; }
        lua_getmetatable(L, 1);
        lua_replace(L, 1);
        lua_rawget(L, 1);
        return 1;
    }
    return 0;
}
static int spl_tostring (lua_State *L)
{
    serial_port_list_t *spl;
    serial_port_info_t *port_info;
    luaL_Buffer         b;
    int                 lp;
    spl = spl_get_data( L, 1 );
    luaL_buffinit(L, &b);
    for( lp = 0; lp < spl->count; lp++ )
    {
        port_info = (serial_port_info_t *) &(spl->ports[lp]);
        luaL_addstring(&b,port_info->device_name );
        if( lp != (spl->count-1) ) luaL_addchar (&b, ' ');
    }
    luaL_pushresult(&b);
    return 1;
}
static int spl_gc (lua_State *L)
{
    return 0;
}
static int spl_make_device_file(lua_State *L)
{
    serial_port_list_t *spl;
    serial_port_info_t *port_info;
    int                 lp;
    spl = call_func_spl;
    if( spl != NULL )
    {   
        for( lp = 0; lp < spl->count; lp++ )
        {
            port_info = (serial_port_info_t *) &(spl->ports[lp]);
            remove(port_info->device_name);
            mknod( port_info->device_name, 
                   (S_IRWXU|S_IRWXG|S_IFCHR), 
                   (port_info->major<<8) + port_info->minor 
                 );
        }
    }
    call_func_spl = NULL;
    return 0;   
}
static const luaL_reg serial_port_list_meta[] = {
  {"__newindex"         , spl_newindex          },
  {"__index"            , spl_index             },
  {"__tostring"         , spl_tostring          },
  {"__gc"               , spl_gc                },
  {"make_device_file"   , spl_make_device_file  },
  {0, 0}
};
int serial_port_list_new_metatable(lua_State *L )
{
    int top;
    top = lua_gettop(L);   
    luaL_newmetatable(L, SERIAL_PORT_LIST );          
    lua_pushvalue(L, -1);              
    lua_setfield(L, -2, "__index");
    luaL_register(L, NULL, serial_port_list_meta );
    lua_settop(L, top);
    return 1;
}

8 실행예

 

    다음은 EZ-S3C2440 에서 실행한 예 이다.

 

    [root@falinux ~]$ ./nfsmnt                                         
                            >> 192.168.10.29                        
    [root@falinux ~]$ cd /mnt/nfs/lua                               
    [root@falinux lua]$ ls -al                                      
    drwxrwxrwx    2 root     root         4096 Mar 19  2011 .       
    drwxrwxrwx   83 root     root         4096 Mar 22  2011 ..      
    -rwxr-xr-x    1 root     root       394464 Mar 19  2011 lua     
    -rwxr--r--    1 root     root          531 Mar 24  2011 run.lua 
    -rwxr-xr-x    1 root     root       298960 Mar 24  2011 serial.so
    [root@falinux lua]$ ./run.lua                                   
    >> serial port list                                             
       total = 3                                                    
       list  = /dev/ttyS0 /dev/ttyS1 /dev/ttyS2                     
       select = 1 device name = /dev/ttyS0 major = 204 minor = 64   
       select = 2 device name = /dev/ttyS1 major = 204 minor = 65   
       select = 3 device name = /dev/ttyS2 major = 204 minor = 66   
    [root@falinux lua]$                                             

C000_lua.gif