제10장 디바이스 드라이버
목차 10.1 GPIO PIN 설명 10.2 GPIO 제어용 Device Driver 10.3 FND 어드레스 디코딩 회로 설명 11.4 FND 제어용 Device Driver
10.1.1 GPIO GPIO와 Interrupt를 이용한 char device driver를 제작해보자. XHYPER270-TKU board에 GPIO test를 위한 2개의 key와 led가 있다. key를 눌렸을 때 interrupt발생하게 하여 interrupt service routine이 실행되면서 LED를 점등하게 한다. GPIO 35, 36, 37,41은 interrupt를 위해 입력I/O로 셋팅하고, GPIO 3, 4는 led 점등을 위한 출력 I/O로 셋팅한다.
GPIO_LED0와 GPIO_ LED1이 GPIO 3, 4에 물려 있음. GPIO – LED GPIO Pin Alignment - chip의 256개의 ball중 81개의ball이 GPIO로 사용된다. GPIO 포트는 27개의 register에 의해 configuration되고 management된다. 이 각각의 포트들은 입력이나 출력으로 설정되어 사용되며, 외부 하드웨어 모듈과 연결되어 이를 제어할 수 있다. 입력으로 설정된 경우에는 interrupt source로 사용할 수도 있다. 출력으로 설정된 경우에는 각각의 포트를 원하는 값으로 assert시킬 수 있다. GPIO_LED0와 GPIO_ LED1이 GPIO 3, 4에 물려 있음. GPIO는 General-purpost I/O ports의 의미로 일반적인 목적으로 입출력 하는 포트를 말함. PXA270는 118개의 GPIO 포트가 chip의 pin으로 나와 있다.
10.1.1 GPIO GPIO Control Register(1)
10.1.1 GPIO GPIO – GPIO Control Register(2) 27개의 32bit registers를 가지고 GPIO를 control한다. Three monitor pin state(GPLR) GPIO핀이 입력모드일 때 GPIO핀의 현재 입력 상태값(0 혹은 1)을 표시한다. 해당 필드가 1이면 입력이 high이고 0이면 low이다. Six control output pin state(GPSR, GPCR) GPSR은 GPIO핀의 출력을 high로 설정한다. 해당 필드에 1을 입력하면 출력이 high로 유지되고 0을 입력하면 아무런 영향이 없다. 즉 해당 핀의 출력을 low로 설정하고자 한다면 GPCR을 set해야 한다. GPCR은 GPIO핀의 출력을 low로 설정한다. 해당 필드에 1을 입력하면 출력이 low로 유지되고 0을 입력하면 아무런 영향이 없다. 해당 핀을 hith로 출력 하려면 GPSR을 1로 set 하여야 한다. Three control pin direction(GPDR) GPIO핀을 입력방향으로 할 것인지 출력방향으로 할 것인지를 결정한다. 해당 필드가 1이면 출력이고, 0이면 입력이다. GPLR : GPIO Level Register GPDR: GPIO Direction Register GPSR: GPIO Set Register GPCR: GPIO Clear Register
10.1.1 GPIO GPIO – GPIO Control Register(3)
10.1.1 GPIO GPIO – GPIO Control Register(4) Six control whether rising edges and/or falling edges are detected (GRER & GFER) GRER의 특정 bit를 1로 set하면 그 bit에 해당 하는 GPIO핀의 상승 에지를 감시하여 상승 에지가 발생하면 GEDR의 해당 필드의 값이 1이 된다. 즉 GPIO 입력 값의 변화(low->high)를 감시하고자 할 때 사용하며 상승 에지가 발생하면 인터럽트를 발생시킬 수 있다. GFER의 특정 bit를 1로 set하면 그 bit에 해당하는 GPIO핀의 하강 에지를 감시하여 하강 에지가 발생하면 GEDR의 해당 필드의 값이 1이 된다. 즉 GPIO입력 값의 변화(high->low)를 감시하고자 할 때 사용하며 하강 에지가 발생하면 인터럽트를 발생 시킬 수 있다. GPIO핀의 하강 에지를 감시하여 하강 에지가 발생하면 해당필드의 값이 1이된다. Three indicate when specified edge types have been detected on pins(GEDR) GPIO핀이 입력으로 설정되고 GRER 이나 GFER이 set되어 있다면 GPIO핀에 상승 에지나 하강 에지가 발생 하게 되면 GEDR의 해당 필드가 1이 된다. 0은 에지가 발생하지 않은 상태이다. GEDR이 1로 set되면 인터럽트를 발생시킬 수 있는 요건이 되기 때문에 매번 에지가 발생하였는지 이 레지스터의 값을 읽어 볼 필요가 없게 된다. GEDR을 해당 bit를 clear하려면 그 bit에 1을 써 주면 된다. 0을 쓰면 아무런 변화를 발생시키지 않는다. GRER: GPIO Rising Edge Register GFER: GPIO Falling Edge Register GEDR: GPIO Edge Detect Register
10.1.1 GPIO GPIO – GPIO Control Register(5) Six determine whether a pin is used as a normal GPIO or whether it is to be taken over by one of three possible alternate functions (GAFR_L, GAFR_U) GPIO핀을 특정 기능을 위한 핀으로 쓸 것인지 GPIO핀으로 쓸 것인지를 결정한다. 1이면 특정 기능으로 설정하고, 0이면 GPIO로 설정한다. GAFR: GPIO Alternate Function Register
10.1.1 GPIO GPIO – GPIO Control Register(6) 앞 슬라이드에서 보았던 우리가 다룰 Register Definition
10.1.1 GPIO GPIO – GPIO Control Register(7)
10.1.1 GPIO GPIO – GPIO Control Register(8)
10.1.2 Key Button Key Hardware – scheme(1)
GPIO LED Device Driver
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – 작업개요 GPIO를 이용한 led device driver를 제작해보자. XHYPER270-TKU board에 2개의 led가 있다. 2개의 LED는 GPIO3와 4에 연결되어 있다. 2개의 LED를 켜기 위한 방법으로 GPIO에 연결되어있는 key 중에서 36, 37번 GPIO에 연결된 KEY를 사용한다. 36,37번의 GPIO에 해당하는 버튼을 누르면 인터럽트 루틴이 호출되고, 인터럽트 루틴에 의해 2개의 LED를 점등하게 하게 한다.
10.2 GPIO 제어용 Device Driver LED 제어용 device driver 우리가 다룰 Register Definition(1) GPIO 3, 4번의 입/출력 방향을 결정한다.
10.2 GPIO 제어용 Device Driver LED 제어용 device driver 우리가 다룰 Register Definition(2)
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – driver coding(1) #include <linux/module.h> //모듈에 관한 정의 #include <linux/init.h> //init_module() 과 cleanup_module() #include <asm/uaccess.h> //copy_to_user() #include <asm/io.h> #include <asm/arch/pxa-regs.h> //GPIO controller에 관한 정의
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – driver coding(2) #define GPIO_LED1 GPIO_bit(3) //gpio 3번 핀 #define GPIO_LED2 GPIO_bit(4) //gpio 4번 핀 …. #define GPIO_TEST_MAJOR 241 // 메이저 번호 #define GPIO_TEST_NAME "GPIO_TEST“ // 디바이스 이름 #define GPIO_LED1_ON_OFF 0x2000 //IOCTL 매직번호 #define GPIO_LED2_ON_OFF 0x2100 //IOCTL 매직번호
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – driver coding(3) static int gpio_test_open(struct inode *inode, struct file *filp) { printk("GPIO TEST OPEN\n"); GPSR1 = (GPIO_bit(3) | GPIO_bit(4)); // LED Inital Clear if(led_usage != 0) return -EBUSY; led_usage = 1; return 0;} ….. static int gpio_test_release(struct inode *inode, struct file *filp){ led_usage = 0; return 0;}
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – driver coding(5) static struct file_operations gpio_test_fops = { .open = gpio_test_open, .read = gpio_test_read, .write = gpio_test_write, .ioctl = gpio_test_ioctl, .release = gpio_test_release,}; 커널 2.2에서는 커널에 모듈로 등록할 때 init_module()과 cleanup_module()을 사용했었는데 커널 2.4에서는 module_init()과 module_exit()로 바뀌었다. 하지만 커널 2.4에서도 init_module()과 Cleanup_module()을 사용할 수 있다.
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – driver coding(6) static int gpio_test_init(void){ …… // GPLED1, GPLED2 Setting GAFR1_L &= ~((1<<9)|(1<<8) | (1<<7)|(1<<6)); //GPIO3,4 GPDR1 |= (GPIO_LED1 | GPIO_LED2); //Output Pin GPSR1 = (GPIO_LED1 | GPIO_LED2); //켜기 GPCR1 = (GPIO_LED1 | GPIO_LED2); //끄기
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – application coding(2) static void gpio_test_exit(void){ GPSR1 = (GPIO_bit(35) | GPIO_bit(41)); // LED Clear GPIO 셋팅을 통해 LED쪽 드라이버 부분은 완성되었다. 이제 key입력을 위한 드라이버 코딩을 살펴보자.
10.2 GPIO 제어용 Device Driver GPIO와 Interrupt를 이용한 GPIO device driver를 완성해보자. - XHYPER270-TKU board에 있는 key button 중 4개가 GPIO로 연결되어 있다. - 이 key를 눌렀을 때 interrupt를 발생하고 service routine이 실행되게 한다. - 서비스 루틴에 의해 넘어온 인자값을 가지고 키입력에 따른 명령을 드라이버에 전달하여 LED가 동작 하도록 한다.
10.2 GPIO 제어용 Device Driver Driver start Application start module_init() key_init() Key_read() read() open() Interruptible_sleep_on() key_handler() wakeup_interruptible() key_read() 함수 call waiting key interrupt waiting read() blocking read() waking
10.2 GPIO 제어용 Device Driver 우리가 다룰 Register Definition(1)
10.2 GPIO 제어용 Device Driver 우리가 다룰 Register Definition(2)
10.2 GPIO 제어용 Device Driver 우리가 다룰 Register Definition(2)
10.2 GPIO 제어용 Device Driver GFER을 통하여 GPIO falling edge일때 검출 설정 GAFR을 통하여 alternate function 설정 GPDR을 통하여 GPIO 입출력 방향 설정 request_irq() 함수로 interrupt 등록 set_irq_type() 함수로 interrupt 검출 설정
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – application coding(1) ….. #define IRQ_KEY1 IRQ_GPIO(36) #define IRQ_KEY2 IRQ_GPIO(37) …. #define GPIO_KEY_READ 0x1000 //IOCTL 매직번호 char key_buf; DECLARE_WAIT_QUEUE_HEAD(key_queue);
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – application coding(2) static int gpio_test_init(void){ int result, ret; ….. if ((ret = request_irq(IRQ_KEY1, &key_interrupt SA_INTERRUPT, "GPIO36_KEY1", NULL))), { printk("failed to register IRQ KEY1\n"); return ret; } ...... set_irq_type(IRQ_KEY1, IRQT_FALLING); // 2.6 Kernel …. GFER1 |= (GPIO_bit(36) | GPIO_bit(37)); // falling edge Interrupt를 등록하는 함수. Int request_irq(unsingned int irq_number, void (*handler)(int,struct pt_regs *), unsigned long irqflags, const char *devicename, void *dev_id); Key_handler는 interrupt가 걸렸을때 처리하는 함수, 등록이 성공하면 # cat /proc/interrupts명령으로 interrupt가 등록되어 있는 것을 확인 ssize_t 는 long을 의미. falling edge일때 인터럽트 검출하도록 설정
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – application coding(3) static irqreturn_t key_interrupt(int irq, void *dev_id, struct pt_regs *regs){ DUMMY_DELAY; if((GEDR1 & GPIO_bit(36))) { key_buf = 1; } if((GEDR1 & GPIO_bit(37))) { key_buf = 2; wake_up_interruptible(&key_queue); return IRQ_HANDLED;} - /usr/local/pxa255/linux-2.4.19-cd/include/asm-arm/arch-pxa/pxa-regs.h에 가보면 GPIO_0는 0x00000001로 정의 됨 /usr/local/pxa255/linux-2.4.19-cd/include/asm-arm/arch-pxa/irqs.h에 KEY_IRQ -> IRQ_GPIO(x) -> ((x)<2)?(IRQ_GPIO0+(x)):GPIO_2_80_TO_IRQ(x)) GPIO_2_80_TO_IRQ(x))는 x값에서 10을 빼는데 10은 reserved된 interrupts이기 때문.
10.2 GPIO 제어용 Device Driver LED 제어용 device driver – application coding(4) int gpio_test_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ int data = 0; int ret = 0; /*You can add any ioctl command*/ switch (cmd) { case GPIO_KEY_READ: copy_to_user((void *)arg, &key_buf,sizeof(key_buf)); key_buf = 0; return 0; case GPIO_LED1_ON_OFF: copy_from_user(&data, (void *)arg, sizeof(data)); if( data == 1) GPSR1 = GPIO_LED1; //GPIO 35 SET LED OFF else GPCR1 = GPIO_LED1; //GPIO 35 CLEAR LED ON return 0; case GPIO_LED2_ON_OFF: GPSR1 = GPIO_LED2; //GPIO 41 SET LED OFF GPCR1 = GPIO_LED2; //GPIO 41 CLEAR LED ON return 0; default: return -EINVAL; } 사용자 프로그램으로 값을 넘긴다. 사용자 프로그램에서 값을 받아 온다. /usr/local/pxa255/linux-2.4.19-cd/drivers/message/fusion/linux_compat.h에 static DECLARE_WAIT_QUEUE_HEAD(wq)는 stuct wait_queue *wq=NULL로 지정 되어 있음.
10.2 GPIO 제어용 Device Driver application 만들기 static char gpio_testDev[] = "/dev/GPIO_TEST"; #define GPIO_KEY_READ 0x1000 #define GPIO_LED1_ON_OFF 0x2000 #define GPIO_LED2_ON_OFF 0x2100 int ON = 0; int OFF = 1; IOCTL 매직 번호
10.2 GPIO 제어용 Device Driver int main(){ int dev; char buf; if((dev = open(gpio_testDev, O_RDWR )) < 0){ perror("open faile /dev/GPIO_TEST\n"); exit(-1); } while(1){ read(dev , &buf, sizeof(buf)); // Key READ printf("KEY %d PUSH \n",buf); switch(buf){ case 1: { ioctl(dev, GPIO_LED1_ON_OFF, &ON);
Makefile 10.2 GPIO 제어용 Device Driver CC := /opt/iwmmxt-1.0.0/bin/arm-linux-gccK DIR := /PXA270/kernel/linux-2.6.11-h270-tku_v1.1 TEST_TARGET = test TEST_OBJS = $(TEST_TARGET).o TEST_SRCS = test.c obj-m := gpio_test.o build: $(TEST_TARGET) make -C $(KDIR) SUBDIRS=`pwd` modules $(TEST_TARGET): $(TEST_OBJS) $(CC) -o $@ $(TEST_OBJS) clean: rm -rf *.0 *.ko *.mod.c rm -f $(TEST_TARGET)
10.2 GPIO 제어용 Device Driver 이후 과정은 앞장에서 배운 디바이스 드라이버 과정을 참조하여 결과를 확인한다. 개략적인 과정은 host에서 위의 3파일을 make를 이용하여 컴파일한 후 드라이버와 응용프로그램을 target으로 전송한다. #insmod gpio_test.ko 를 디바이스 등록과 함께 irq등록을 한다. #mknod 로 해당 디바이스 노드를 생성한다. 응용프로그램을 실행한 후 key를 눌려보면서 결과를 확인한다.
10.3 FND 어드레스 디코딩 회로 설명 개요 7 세그먼트라고도 하고 숫자나 간단한 기호 표현에 많이 이용. 7-segment LED는 A-G 의 7개의 LED와 소수점 표시를 한 DP (Decimal Point)의 LED 1개로 구성된다. 각 LED는 표시되는 문자의 일부분을 구성하므로 각각을 세그먼트(segment : 부분, 구분)라 부른다.
10.3 FND 제어용 Device Driver FND 디바이스 물리 주소 영역 nCS4번 영역인 0x1000_0000 ~ 0x13FF_FFFF 사이에 FND 존재
10.3 FND 제어용 Device Driver IEB 보드 어드레스 디코딩 회로 CS4에 의해 어드레스 디코딩과 버퍼의 역할을 하는 FPGA 칩이 활성화
10.3 FND 제어용 Device Driver FND 어드레스 FND 물리주소(접근번지)는 0x1100_0000 ~ 0x1170_0000 영역에 8개가 할당 되어있는데 adress table을 구성해서 보면 최상위 6bit(A31~26)가 CS4 해당하고, 나머지 26bit중 최상위 6bit(A25~20)가 실질적인 어드레스 디코딩을 위해 사용되고 있다.
10.3 FND 제어용 Device Driver FND 어드레스 디코딩 회로 구성 data[31..0] D- 플리 플롭 FND의 물리주소 FND_CS0 ~ FND_CS7가 주어지면 CS4에 의해 FPGA가 선택되고 FPGA 내부에 있는 어드레드 디코딩 논리 회로에 의해 활성화 신호가 발생하고 해당 FND에 D-플리플롭에 상승엣지가 공급되어 FND에 연결된 데이타버스 하위 8비트가 FND에 전달되어 FND의 불을 켜게 된다.
10.3 FND 제어용 Device Driver FND 제어 방법(해당 비트에 데이터를 보냄) FND 1 : FND_CS = (0x01 << 0) 7 : FND_CS = (0x01 << 6) FND 6 : FND_CS = (0x01 << 5) 2 : FND_CS = (0x01 << 1) 5 : FND_CS = (0x01 << 4) 3 : FND_CS = (0x01 << 2) 8 : FND_CS = (0x01 << 7) 4 : FND_CS = (0x01 << 3)
10.3 FND 제어용 Device Driver 가상주소 맵핑 MMU를 사용하는 리눅스에서는 물리주소를 가지고 디바이스에 접근하면 페이지 폴트가 발생한다. 따라서 커널이 사용하는 가상주소를 할당받아 야 한다. ioremap 함수는 해당하는 디바이스를 위해 메모리 상에 일정영역을 커널이 관리하는 가상주소를 맵핑하여 준다. 따라서 FND에 해당하는 가상주소로 ioremap함수를 이용하여 FND를 위한 가상주소를 얻어와야 한다.
10.3 FND 제어용 Device Driver 소스 설명 Makefile fnd.c : lcd 디바이스 드라이버 소스이다. fnd.ko : lcd 디바이스 드라이버를 컴파일하면 생성되는 object 파일이다. fnd_test.c : lcd 디바이스 드라이버를 테스트하는 어플리케이션 소스이다.
10.3 FND 제어용 Device Driver FND 드라이버 소스 설명 (fnd.c) #define FPGA_FND_CS0 (0x11000000) //FND 물리주소 #define FPGA_FND_CS1 (0x11100000) #define FPGA_FND_CS2 (0x11200000) #define FPGA_FND_CS3 (0x11300000) #define FPGA_FND_CS4 (0x11400000) #define FPGA_FND_CS5 (0x11500000) #define FPGA_FND_CS6 (0x11600000) #define FPGA_FND_CS7 (0x11700000)
10.3 FND 제어용 Device Driver FND 드라이버 소스 설명 (fnd.c) mem_addr_fnd0 = FPGA_FND_CS0; …… mem_len = 0x1000; ….. static int fnd_init(void){ mem_fnd_cs0 = ioremap_nocache ( mem_addr_fnd0, mem_len); //가상주소 얻기 if( !mem_fnd_cs0) { printk("Error mapping fnd0 memery"); return -EBUSY; } ……. static void fnd_exit(void){ fnd_clear(); unregister_chrdev(FND_MAJOR, FND_NAME); iounmap(mem_fnd_cs0);
10.3 FND 제어용 Device Driver FND 응용프로그램 소스 설명 (fnd_test.c) unsigned char asc_to_fnd(int n){ unsigned char c; switch (n) { /* 여러가지 문자를 추가할수 있다 */ case 0: c = 0x3f; break; case 1: c = 0x06; break; case 2: c = 0x5b; break; case 3: c = 0x4f; break; case 4: c = 0x66; break; case 5: c = 0x6d; break; case 6: c = 0x7d; break; case 7: c = 0x07; break; ….. } return c;}
10.3 FND 제어용 Device Driver FND 응용프로그램 소스 설명 (fnd_test.c) main(int ac, char *av[]){ int n, count, dev; unsigned char buf[MAXFND+1]; dev = open( fnd_dev, O_RDWR); if (dev < 0) {fprintf(stderr, "cannot open FND (%d)", dev); exit(2); } memset(buf, 0, sizeof(buf)); for (n = 0 ; n <= 9; n++) { for( count = 0 ; count < MAXFND; count++){ buf[count]= asc_to_fnd(n);} write(dev, buf,MAXFND); usleep(500000); } }
10.3 FND 제어용 Device Driver Makefile 설명 CC := /opt/iwmmxt-1.0.0/bin/arm-linux-gcc KDIR := /PXA270/kernel/linux-2.6.11-h270-tku_v1.1 TEST_TARGET = fnd_test TEST_OBJS = fnd_test.o TEST_SRCS = fnd_test.c obj-m := fnd.o build: $(TEST_TARGET) make -C $(KDIR) SUBDIRS=`pwd` modules $(TEST_TARGET) : $(TEST_OBJS) $(CC) -o $@ $(TEST_OBJS) clean: rm -rf *.o *.ko *.mod.c rm -f $(TEST_TARGET)
10.3 FND 제어용 Device Driver # make clean # make 다운로드 : fnd.ko, fnd_test 실행방법 이미 타겟보드에 fnd의 드라이버가 올라가 있으므로 rmmod로 제거한 후 자신이 만든 fnd드라이버를 올리고 실행시켜보자. (host) # make clean # make 다운로드 : fnd.ko, fnd_test 실행 hybus~>#./fnd_test”