• 검색 결과가 없습니다.

LED I/O 출력

문서에서 GettingStarted-with-HBE-EMPOSII (페이지 90-96)

8. 디바이스 드라이버

8.2. EMPOSⅡ에서 디바이스 드라이버 작성

8.2.1 LED I/O 출력

z get_user(void *x. const void *addr)

*addr의 값은 커널 영역인 x로 sizeof(addr) 만큼 복사한다

z put_user(void *x, const void *addr)

*x의 값을 user영역인 addr로 sizeof(addr)만큼 복사한다.

z copy_to_user(void *to, void *from, unsigned long size)

커널영역의 어드레스 from부터 size만큼 유저 영역의 어드레스 to로 데이터를 복사한 다.

z copy_from_user(void *to, void *from, unsigned long size)

user영역의 어드레스 from으로부터 size만큼 커널영역의 어드레스 to로 데이터를 복 put_user( *add, (unsigned short *)(gdata) );

위에서 사용자 영역에서 커널영역으로 데이터를 전송하는 방법을 보았고 다음은 프로그램 에서 I/O를 제어하는 방법에 관하여 살펴보자.

앞선 챕터의 응용 프로그램에서 보았듯이 I/O제어의 가장 기본적인 방법은 포인터를 직접 이용하는 방법이다 “포인터는 곧 번지이다” 라는 말과 같이 변수의 내용이 보관되어 있는 곳의 어드레스를 말한다.

unsigned char *addr;

addr = (unsigned char *)(0xf1600000);

*addr = 0xa5;

위와 같이 프로그램을 작성하였을 경우 포인터 변수로 addr을 선언했고 이 변수를 0xf1600000으로 초기화 하여 위의 주소에 0xa5값을 쓰라는 의미를 가진다.

또한 c 라이브러리에서 I/O 포트 제어를 위한 여러 함수들을 만들어 놓고 일반 프로그래 머들이 사용할 수 있게 했다. 다음은 이러한 함수들의 예이다.

- I/O로 부터 값을 읽은 함수 __u8 inb(unsigned int port) __u16 inw(unsigned int port) __u32 inl(unsigned int port) - I/O로 부터 값을 쓰는 함수

void outb(__u8 data, unsigned int port);

void outw(__u16 data, unsigned int port);

void outl(__u32 data, unsigned int port);

EMPOSⅡ에 있는 led를 제어하는 프로그램을 작성하기 위해선 먼저 ‘__initdata’의 구조체 에서 정의된 led의 해당 가상 어드레스 주소인 0xf1600000의 값을 변화 시켜 led를 제어 할 수 있다. 관련 소스는 다음과 같다.

- ledioport.c

#include <linux/ioport.h>

#include <asm/uaccess.h>

#include <linux/module.h>

#include <linux/fs.h>

#include <asm/io.h>

#define LEDIOPORT_MAJOR 0

#define LEDIOPORT_NAME "LED IO PORT"

#define LEDIOPORT_MODULE_VERSION "LED IO PORT V0.1"

#define LEDIOPORT_ADDRESS 0xf1600000

#define LEDIOPORT_ADDRESS_RANGE 1

//Global variable

static int ledioport_usage = 0;

if(ledioport_usage != 0) return -EBUSY;

MOD_INC_USE_COUNT;

ledioport_usage = 1;

return 0;

int ledioport_release(struct inode *minode, struct file *mfile) {

MOD_DEC_USE_COUNT;

ledioport_usage = 0;

return 0;

ssize_t ledioport_write_byte(struct file *inode, const char *gdata, size_t length, loff_t *off_what) {

unsigned char *addr;

unsigned char c;

get_user(c,gdata);

addr = (unsigned char *)(LEDIOPORT_ADDRESS);

*addr = c;

return length;

static struct file_operations led_fops = { write : ledioport_write_byte, open : ledioport_open,

release : ledioport_release,

;

int init_module(void) {

&led_fops);

if(result < 0) {

printk(KERN_WARNING"Can't get any major n");

return result;

ledioport_major = result;

if(!check_region(LEDIOPORT_ADDRESS,LEDIOPORT_ADDRESS_

RANGE))

request_region(LEDIOPORT_ADDRESS,LEDIOPORT_ADDRESS_

RANGE,LEDIOPORT_NAME);

else printk(KERN_WARNING"Can't get IO Region 0x%x n", EDIOPORT_ADDRESS);

printk("init module, ledioport major number : %d n",result);

return 0;

void cleanup_module(void) {

release_region(LEDIOPORT_ADDRESS,LEDIOPORT_ADDRESS_RANGE);

if(unregister_chrdev(ledioport_major,LEDIOPORT_NAME)) printk(KERN_WARNING"%s DRIVER CLEANUP FALLED n", LEDIOPORT_NAME);

- test.c

include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main(int argc, char **argv) {

int dev;

char buff;

if(argc <= 1) {

printf("please input the parameter! ex)./test 0xa1 n");

return -1;

dev = open("/dev/ledioport", O_WRONLY);

if (dev != -1){

if(argv[1][0] == '0' && (argv[1][1] =='x'||argv[1][1] == 'X')) buff = (unsigned char)strtol(&argv[1][2], NULL,16);

else

buff = atoi(argv[1]);

write(dev,&buff,1);

close(dev);

else{

printf( "Device Open ERROR! n");

exit(-1);

return(0);

- Makefile

# led Device Driver Makefile

CC = arm-linux-gcc

KERNELDIR = /usr/local/linux-2.4.19-rmk4-pxa2-empx1 INCLUDEDIR = -I$(KERNELDIR)/include -I./

MODULE_OBJS = ledioport.o MODULE_SRCS = ledioport.c

TEST_TARGET = test TEST_OBJS = test.o TEST_SRCS = test.c

all: $(MODULE_OBJS) $(TEST_TARGET)

$(MODULE_OBJS) :

$(CC) $(CFLAGS) -c $(MODULE_SRCS)

$(TEST_TARGET) : $(TEST_OBJS)

$(CC) $(CFLAGS) $(TEST_OBJS) -o $@

clean:

rm -f *.o

rm -f $(TEST_TARGET)

소스 파일을 이상 없이 생성하였다면 모든 파일을 하나의 디렉토리에 옮겨 놓고 다음과 같이 컴파일을 실행한다.

$ make

아무런 문제가 발생하지 않으면 디렉토리 안에 ledioport.o, test.o 와 test라는 파일들이 생길 것이다. 여기서 ledioport.o는 led를 제어하는 디바이스 드라이버이고 test는 ledioport.o라는 디바이스 드라이버를 열어 사용할 테스트용 어플리케이션이다.

다음으로 위의 ledioport.o와 test파일을 타겟 보드로 전송한 후 insmod를 사용하여 디바 이스로 컴파일 된 ledioport.o를 커널에 삽입한다.

[root@EMPOS/root]$ insmod ledioport.o

디바이스의 커널 삽입과정이 이상 없이 진행되었다면 다음과 같은 메시지가 minicom을 통해 출력이 된다.

[root@EMPOS/root]$ insmod ledioport.o

[root@EMPOS/root]$ mknod /dev/ledioport c 253 0 [root@EMPOS/root]$

위의 내용을 보면 insmod를 통해 ledioport.c의 init_module()이 실행되고 ‘0’으로 설정한 메이저 번호를 커널에서 동적으로 설정하여 돌려준다. 그러므로 반환된 메이저 번호를 가 지고 mknod를 사용하여 다음과 같이 특수 장치 파일을 만들 수가 있다.

[root@EMPOS/root]$ mknod /dev/ledioport c 253 0

커널에 디바이스 드라이버가 정상적으로 삽입되고 mknod를 통해 특수 장치 파일을 만들 었으면 test 프로그램을 실행 시켜 제작된 드라이버를 테스트 할 수 있다.

[root@EMPOS/root]$ ./test 0xaf

삽입된 디바이스와 test 프로그램에 이상이 없을 경우 EMPOSⅡ 의 LED에 “10101111”의 형태로 불이 들어 오는 것을 확인 할 수 있다.

※ 동작원리

사용자 응용 프로그램에서는 제일 먼저 특수 장치 파일을 다음과 같이 오픈 하여 사용하 게 된다.

dev = open("/dev/ledioport", O_WRONLY);

위의 명령을 통해 디바이스 드라이버의 ledioport_open()이 성공적으로 실행되면 다른 시 스템 호출에서 사용 할 수 있는 파일 기술자를 반환한다. 이 파일 기술자와 사용자 프로 그램의 인수 값을 사용하여 시스템 콜인 write()를 호출한다. 이 함수는 file_operations의 구조체 안에 write로 정의 되어 있는 디바이스 드라이버의 ledioport_write_byte()를 호출 한다. ledioport_write_byte()안에서는 get_user()로 사용자 영역에 있는 값을 커널 영역으 로 복사하고 led의 가상 주소에 사용자 영역으로부터 받을 값을 쓰고 리턴 하면 디바이스 드라이버의 작업은 끝나고 사용자 응용프로그램으로 돌아오게 된다. 그 다음에 close()로 특수 장치 파일을 닫으면 디바이스 드라이버의 ledioport_release()가 실행 된 후 프로그 램을 종료하게 된다.

문서에서 GettingStarted-with-HBE-EMPOSII (페이지 90-96)