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