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]$


