TIPs

임베디드_드라이버 포팅

겨울거북이 2008. 10. 30. 09:36

맨날 퍼온다..

술만 퍼먹다가

이젠 글을 퍼먹는건가 -_-;;

 

------------------------------------

이게 원본일 꺼야 아마도..
http://kin.naver.com/detail/detail.php?d1id=1&dir_id=10104&eid=JZD3rAflsVgzNeXL0V3GTCrhvC3aXGHU&qb=bWtub2QgbWFqb3IgZGV2aWNl&pid=fbt/BdoQsDhssaRYVe8sss--055529&sid=SGB4t7JkYEgAAHDdcKM

 

------------------------------------


질문의 전체적인 내용을 모두 설명하기엔 굉장히 많은 분량이구요. 제대로 된 교재를 통해

공부하셔야 편하실꺼에요.

일단 nfs서버데몬을 설치하고 셋팅하는 것등과 일반적인 프로그램 상식은 알고 계신 다는

가정으로 설명 드리겠습니다.

우선 gpio(16) 버튼에 대한 device드라이버와, 버튼이 눌렸음을 메세지로 출력해 주는 간단한 테스트 프로그램 소스를 보여드리고 설명드리겠습니다.

 

//gpio.c


#include < linux/kernel.h>
#include < linux/module.h>
#include < linux/init.h>
#include < linux/config.h>
#include < linux/sched.h>
#include < linux/string.h>
#include < linux/delay.h>
#include < linux/errno.h>
#include < linux/types.h>

#include < asm/hardware.h>
#include < asm/uaccess.h>
#include < asm/irq.h>
#include < asm/param.h>

 


#define IRQ_BUTTON  IRQ_GPIO(16)


static void button_interrupt(int irq, void *dev_id, struct pt_regs *regs);

static int GPIO_MAJOR = 0;

static DECLARE_WAIT_QUEUE_HEAD(wait_queue);

static void button_interrupt(int irq, void *dev_id, struct pt_regs *regs){
 wake_up_interruptible(&wait_queue);
}

static ssize_t gpio_read(struct file *filp, char *buf, size_t count, loff_t *l){
 char hello[] = "GPIO_16 port was pushed!";

 interruptible_sleep_on(&wait_queue);
   
 copy_to_user(buf,&hello,sizeof(hello));
 return 0;
}

static int gpio_open(struct inode *inode, struct file *filp){
 int res;
 unsigned int gafr;

 gafr = 0x3;
 GAFR0_U &= ~gafr;


        printk("IRQ_BUTTON = %d\n",IRQ_BUTTON);
        printk("IRQ_TO_GPIO = %d\n",IRQ_TO_GPIO_2_80(IRQ_BUTTON));


 GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) &= ~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON));
 set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON),GPIO_FALLING_EDGE);


 res = request_irq(IRQ_BUTTON,&button_interrupt,SA_INTERRUPT,"Button",NULL);
 if(res < 0)
  printk(KERN_ERR "%s: Request for IRQ %d failed\n",__FUNCTION__,IRQ_BUTTON);
 else {
  enable_irq(IRQ_BUTTON);
 }

 MOD_INC_USE_COUNT;
 return 0;
}


static int gpio_release(struct inode *inode, struct file *filp)
{
 free_irq(IRQ_BUTTON,NULL);
 disable_irq(IRQ_BUTTON);

 MOD_DEC_USE_COUNT;
 return 0;
}


static struct file_operations gpio_fops = {
 read: gpio_read,
 open: gpio_open,
 release: gpio_release,
};

 

int init_module(void){
 int result;
 result = register_chrdev(GPIO_MAJOR,"GPIO INTERRUPT",&gpio_fops);

 if(result < 0) {
  printk(KERN_WARNING"Can't get major %d\n",GPIO_MAJOR);
  return result;
 }

 if(GPIO_MAJOR == 0) GPIO_MAJOR = result;
 printk("init module, GPIO major number : %d\n",result);

 return 0;
}

void cleanup_module(void){
 unregister_chrdev(GPIO_MAJOR,"GPIO INTERRUPT");
 return;
}
 
---------------------------------------------------------------------------------------------------------------

// test.c

#include < stdio.h>
#include < stdlib.h>
#include < unistd.h>
#include < sys/types.h>
#include < sys/stat.h>
#include < fcntl.h>
#include < errno.h>

static int dev;

int main(void)
{
 char buff[40];

 dev = open("/dev/gpio",O_RDWR);

 if(dev < 0) {
  printf( "Device Open ERROR!\n");
  exit(1);
 }

 printf("Please push the GPIO_16 port!\n");
 read(dev,buff,40);
 printf("%s\n",buff);

 close(dev);
 return 0;
}
---------------------------------------------------------------------------------------------------------------

// Makefile

#CC     = armv5l-unknown-linux-gcc
CC = arm-linux-gcc

KERNELDIR = /working/empos2/kernel/linux-2.4.19-rmk4-pxa2-empx1
INCLUDEDIR = -I$(KERNELDIR)/include -I./

CFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 -I$(INCLUDEDIR)

MODULE_OBJS = gpio.o
MODULE_SRCS = gpio.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) $(TEST_OBJS) -o $@

clean:
 rm -f *.o
 rm -f $(TEST_TARGET)
--------------------------------------------------------------------------------------------------------------

디바이스 드라이버의 일반적인 구조는 위에 처음 소스에서 빨간색으로 표시한 부분이

주요 부분들입니다.

init_module과 cleanup_module은 장치 드라이버를 등록/해제 하는데 관련한 함수이구요

장치드라이버의 object파일을 타겟보드에 등록할때

커널을 부팅후 insmod 라는 명령어를 통해 등록합니다. 이때 드아리버의 init_module

함수가 호출되어 해당 장치드라이버를 등록하게 됩니다. 삭제는 rmmod 명령어를 통하며

cleanup_module 함수가 호출됩니다.

실질적으로 커널에 장치등록을 요구하고  major number(드라이버를 구별하기 위한 숫자)를

획득할땐 init_module()함수 안에서 register_chardev() 함수를 통하게 됩니다.

반대로 삭제시 kernel로 부터 장치 제거 요구는 unregister_chardev()를 통해 합니다.

static struct file_operations gpio_fops = {
 read: gpio_read,
 open: gpio_open,
 release: gpio_release,
};

에서 gpio_read, gpio_open, gpio_release는 각각 c프로그램에서 파일에 대한

read/open/close에 대응하는 함수들입니다. 드라이버 작성시 만든 함수를 위의 구조체에서

각각을 맵핑 시켜주셔야 합니다. 이 예에서는 write는 없는데요 버튼이기 때문에 버튼이

눌렸는지 확인하는 read기능만 있기 때문입니다.

이것 외에 ioctl 을 정의해 주실수 있는데요 이것은 해당 디바이스 의존적인 특화된 기능을

사용할 때 쓰는 함수 입니다. 예를들면 플로피 디스크 드라이버에 read/write는 일반적으로

디스크에 읽기/쓰기와 기능적으로 동일 합니다. 이때 ioctl함수는 디스크 포맷과 같은 기능

을 수행하게 하는 장치마다 특별하게 조작이 필요한 기능을 위해 필요에 따라 정의합니다.

 

이렇게 장치 드라이버를 작성후엔, kernel에 등록해야 합니다.

타겟보드로 장치 드라이버를 전송하기 위해,

장치 드라이버를 ramdisk이미지에 추가하신 후 tftp로 전송하여 퓨징하셔도 되구요

NFS를 설정하신후 타겟보드에서 mount를 하여 타겟보드와 호스트가 공유하는 디렉토리에 복사하여 드라이버를 등록 하실 수 있습니다.

/]$ mount -t nfs 165.246.43.171: /nfs_resource /mnt/nfs

/]$ cd /mnt/nfs

nfs]$ insmod goio.o

nfs]$ mknod /dev/gpio c 253 0

호스트 pc에서 작성한 디바이스 드라이버 를 호스트의 /nfs_resource에 복사해 둔 후

위처럼 타겟보드에서 호스트의 nfs_resource를 자신의 파일 디렉토리 /mnt/nfs에

마운트하여 드라이버를 등록하시고 테스트프로그램을 실행해 보시면 됩니다.

 

더 궁금하신 사항이 있으시면

관련 강의록을 보내드릴수 있습니다.

메일 주세요.^^