Presentation is loading. Please wait.

Presentation is loading. Please wait.

GPIO Port 활용 디바이스 드라이버 Lecture #15.

Similar presentations


Presentation on theme: "GPIO Port 활용 디바이스 드라이버 Lecture #15."— Presentation transcript:

1 GPIO Port 활용 디바이스 드라이버 Lecture #15

2 목 차 GPIO Led/Push-switch Device Driver Keypad Device Driver

3 GPIO LED/Push-switch Device Driver - 개 요
PXA255-PRO3보드에 실장되어 있는 LED, Push Switch의 Device Driver를 작성한다. Device Driver를 작성하기 위해 회로도를 분석하고 LED, Push Switch의 동작방법을 이해한다.

4 GPIO LED/Push-switch Device Driver - 목 차
관련 Hardware의 이해 GPIO 회로도 분석 LED, Push Switch LED, Push Switch 제어용 Device Driver LED, Push Switch Device Driver 테스트

5 GPIO(General Purpose I/O) (1)
PXA255 processor 81개의 GPIO pins을 제공하며 27개의 register 로 제어한다. PXA255의 GPIO Block Diagram

6 GPIO(General Purpose I/O) (2)
GAFR(GPIO Alternate Function Register) PXA255의 GPIO는 1개 이상의 기능을 제공하며 GAFR을 설정하여 어느 기능으 로 사용할지를 정한다. 여기서는 GPIO용도로 사용하도록 설정. e.g) GPIO32는 ‘AC97 Sdata_in1’, ‘I2S System Clock’, ‘GPIO’ 중에 기능을 선택하 여 사용할 수 있다. GPDR(GPIO Pin Direction Register) GPIO의 방향(입력, 출력)을 설정한다. e.g) GPIO82, 83은 LED가 연결되어 있으므로 출력으로 설정하면 되고, GPIO32 는 Switch가 연결되어 있으므로 입력으로 설정하면 된다. GPLR(GPIO Pin Level Register) GPIO Pin의 입력레벨을 나타낸다. e.g) Push Switch가 눌려지면 해당 GPLR의 비트는 ‘1’이 되고 안눌려지면 ‘0’이 될것이다.

7 GPIO(General Purpose I/O) (3)
GPSR(GPIO Pin Output Set Register) GPIO Pin을 ‘Set’(High)으로 만들 때 사용한다. 이 레지스터는 write-only register이다 ‘0’을 쓰게 되면 Pin level에 영향을 주지 않는다. e.g) LED를 소등하려면 GPIO 82, 83에 해당하는 GPSR의 비트에 ‘1’을 write하면 된다. GPCR(GPIO Pin Output Clear Register) GPIO Pin을 ‘Clear’(Low)로 만들 때 사용한다. e.g) LED를 점등하려면 GPIO 82, 83에 해당하는 GPSR의 비트에 ‘1’을 write하면 된다.

8 GPIO(General Purpose I/O) (4)
GRER(GPIO Rising Edge Detect Enable Register) GPIO pin을 입력으로 사용할 때 Rising Edge Detect를 하려면 해당 비트를 ‘1’ 로 한다. GFER(GPIO Falling Edge Detect Enable Register) GPIO pin을 입력으로 사용할 때 Falling Edge Detect를 하려면 해당 비트를 ‘1’ 로 한다. Both Edge로 하려면 GRER과 GFER을 둘 다 설정하면 된다. GEDR(GPIO Edge Detect Status Register) GPIO pin의 Edge Detect Status를 나타낸다. 좀더 자세한 부분은 PXA255 datasheet를 참고한다.

9 회로도 분석 (1) LED0, LED1, SWITCH0 연결 확인 LED0와 LED1이 GPIO 82, 83에 연결되어 있다.
SWITCH0은 GPIO 32에 연결되어 있다.

10 회로도 분석 (2) LED0, LED1, SWITCH0 연결 확인
아래의 그림에서 좌측에 pull-up이 연결되어 있고 전류 방향은 그 림과 같이 된다. LED를 점등하려면 그림과 같이 전류가 흘러야 하므로 LED0을 ‘Low’로 하면 되고 LED를 소등하려면 LED0을 ‘High’로 하면 된다. 또한 LED를 ON/OFF하기 위해서는 CPU 82, 83 port는 출력으로 설정하여야 한다. 전류 방향 LED0이 CPU와 연결되어 있다.

11 회로도 분석 (3) LED0, LED1, SWITCH0 연결 확인
그림에서 74LVT14는 ‘Inverting Schmitt-trigger’소자이다. Switch의 입력에서 발생되는 노이즈를 제거하기 위해 사용하였으며 값은 반 전되어서 출력된다. Push Switch를 누르지 않은 상태에서 Switch0의 상태는 중간에 pull-up이 있으므로 ‘Low’이고, 누르게 되면 ‘High’가 된다. Switch입력을 상태를 확인하기 위해서는 GPIO32 port를 입력으로 설정하여야 한다. 반전 소자이다.

12 GPIO 디바이스 드라이브 – 기본개념 (1) 기본 개념
응용프로그램은 시스템 호출을 이용하여 해당 주변 장치를 제어하 는 커널코드인 디바이스 드라이버에 접근한다. 기본적으로 open, close, read, write, ioctl 다섯 가지 시스템호출 함수를 제공하며, 이을 이용해 각종 디바이스 드라이버에 접근할 수 있는 공통된 방법을 제공한다. 모든 디바이스들은 /dev에 특수 파일의 형태로 나타나고, 디바이 스를 제어하기 위해서는 /dev밑의 해당 디바이스에 적합한 파일에 대해 시스템 호출을 이용하여 접근을 하면 된다.

13 GPIO 디바이스 드라이브 – 기본개념 (2) open() close()
open() 시스템 호출은 파일이나 주변장치를 파일 디스크립터(fd)에 연결 시킨다. 응용프로그램에서 파일 혹은 디바이스를 특정 숫자로 매핑시키는 것이다. open()함수에서 매핑된 fd를 이용하여 read(), write()등등의 작업을 하게된다. oflag에 대한 값들은 O_RDONLY, O_WRONLY, ORDWR, O_APPEND 등이 있는데 단 어의 뜻이 나타내는 것처럼 읽기전용, 쓰기전용, 읽기쓰기 등을 나타낸다. close() open()으로 열린 파일이나 디바이스를 닫을 때 사용한다. 인자는 open()에서 반환된 fd이다. #include <sys/types.h> #include <sys/stat.h> #include <fclntl.h> int open(const char *path, int oflag, …); #include <unistd.h> int close(int fd);

14 GPIO 디바이스 드라이브 – 기본개념 (3) read(), write()
디바이스에 접근하여 디바이스 고유의 데이터를 읽거나 쓸 수 있도록 해 준다. read()는 n-byte의 크기만큼 buf에 읽어 오도록 요청한다. 반환 값은 실제 읽은 데이터의 바이트 수를 의미한다. write()는 buf에 있는 n-byte의 크기만큼 쓰기를 요청한다. 호출이 성공하 면, 실제로 쓴 데이터의 바이트 수를 반환한다. #include <unistd.h> int read(int fd, void *buf, size_t nbytes); Int write(int fd, const void *buf, size_t n);

15 GPIO 디바이스 드라이브 – 기본개념 (4) ioctl()
디바이스에 어떠한 정보를 넘겨서 디바이스의 동작을 제어하거나, 디바이스로부터 특정 정보를 얻을 때 사용한다. 간단한 데어터의 읽기,쓰기의 경우도 ioctl()을 이용하여 구현할 수도 있다. 디바이스 드라이버 작성자의 의도에 따라 상당히 유연하게 코딩이 가능하다. 두 번 째 인자는ioctl-number가 들어간다. 세 번 째 인자는 두 번 째 인자인 ioctl-number에 따라 바뀌게 된다. 경우에 따라 없을 수도 있 다. 디바이스에 따라 틀리기 때문에 호출하는 규칙만 존재하고, 실제 내 용은 제어하고자 하는 디바이스 드라이버의 사용법을 보아야한다. #include <sys/ioctl.h> int ioctl(int fd, int request, …);

16 GPIO 디바이스 드라이브 – 관련커널함수 (1)
pxa_gpio_mode() XScale architecture에서 지원하는 함수이다. gpio의 alternate function을 설정하는 함수이다. 앞에서 본 GAFR, GPDR 등을 설정한다. #include <asm/arch/hardware.h> int pxa_gpio_mode(int gpio_mode);

17 GPIO 디바이스 드라이브 – 관련커널함수 (2)
set_irq_type() interrupt의 발생 조건을 설정한다. irq는 Interrupt의 번호를 나타낸다. type은 Interrupt를 발생시키는 조건을 나타낸다. IRQ_TYPE_EDGE_RISING IRQ_TYPE_EDGE_FALLING IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) #include <linux/irq.h> int set_irq_type(unsigned int irq, unsigned int type);

18 GPIO 디바이스 드라이브 – 관련커널함수 (3)
request_irq() irq 등록함수이다. 첫 번째 인자는 irq 번호를 나타낸다. 두 번째 인자는 interrupt service routine이 된다. 세 번째 인자는 interrupt type flag이다. SA_INTERRUPT: IRQF_DISABLED로 define되어 있다. 신속한 인터럽트 서비스를 위해서 다른 인터럽트를 금지하고 가장 우선으로 인터럽트 처리 루틴을 수행한다. SA_SHIRQ: PCI 디바이스가 인터럽트를 공유할 때 사용한다. SA_SAMPLE_RANDOM 네 번째 인자는 device name이다. 다섯 번째 인자는 device id이다. Share IRQ가 아닐 때는 ‘NULL’로 한다. #include <linux/interrupt.h> int request_irq(unsigned int, irq_handler_t, unsigned long, const char *, void *);

19 GPIO 디바이스 드라이브 – 관련커널함수 (4)
free_irq() 등록된 interrupt handler를 제거하는 함수이다. 첫 번째 인자는 irq 번호를 나타낸다. 두 번째 인자는 device id이다. Share IRQ가 아닐 때는 ‘NULL’로 한 다. #include <linux/interrupt.h> int free_irq(unsigned int, void *);

20 GPIO 디바이스 드라이브 – 관련커널함수 (5)
misc_register() miscellaneous device 등록함수이다. misc_deregister() miscellaneous device 등록해제함수이다. #include <linux/miscdevice.h> int misc_register (struct miscdevice *misc); int misc_deregister (struct miscdevice *misc);

21 GPIO 디바이스 드라이브 – 관련커널함수 (6)
init_timer() timer 초기화 함수이다. mod_timer() timer의 timeout 값을 수정하는 함수이다. 두 번째 인자는 new timeout 값이다. del_timer() timer를 더 이상 사용하지 않을 때 제거하는 함수이다. timer_pending() timer가 pending되었는지를 나타낸다. 리턴 값이 ‘1’이면 timer pending이고, ‘0’이면 아니다. #include <linux/timer.h> void fastcall init_timer (struct timer_list *timer); int mod_timer (struct timer_list *timer, unsigned long expires); Int del_timer (struct timer_list *timer); Static inline int timer_pending (const struct timer_list * timer)

22 GPIO 디바이스 드라이브 – 기본 동작방식 1. call 2. return 3. call 4. return 5. call
커널 내 모듈적재 gpio_init() gpio_open() 디바이스 드라이버 프로그램 드라이버 동작(write) gpio_write() gpio_relese() 커널 내 모듈제거 gpio_exit() rmmod insmod 디바이스 열기 open() 사용자 프로그램 write() 디바이스 닫기 close() 2. return 1. call 성공 4. return 3. call 6. return 5. call 종료 Main() 실패

23 GPIO 디바이스 드라이브 – 매크로/전역변수 (gpio_driver.c)
#include <asm-arm/io.h> #include <asm-arm/uaccess.h> #include <asm-arm/irq.h> #include <asm/arch/pxa-regs.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/version.h> #include <linux/miscdevice.h> #include <linux/timer.h> #define GPIO_MINOR 221 #define GPIO_NAME "gpio" #define GPIO83_ON 0x01 #define GPIO82_ON 0x02 #define GPIO83_OFF 0x10 #define GPIO82_OFF 0x20 #define ALL_ON 0x03 #define ALL_OFF 0x30 //Global variable static int gpio_usage = 0; static struct timer_list ledoff_timer;

24 GPIO 디바이스 드라이브 – 매크로/전역변수 (gpio_driver.c)
// define functions... int gpio_open (struct inode *minode, struct file *mfile); ssize_t gpio_write(struct file *, const char *, size_t, loff_t *); int gpio_release (struct inode *minode, struct file *mfile); static irqreturn_t gpio_handler (void); static void ledoff_timeout (unsigned long unused); static const struct file_operations gpio_fops = { .open = gpio_open, .write = gpio_write, .release = gpio_release, }; static struct miscdevice gpio_miscdev = { .minor = GPIO_MINOR, .name = GPIO_NAME, .fops = &gpio_fops,

25 GPIO 디바이스 드라이브 – open/release (gpio_driver.c)
int gpio_open (struct inode *minode, struct file *mfile) { if (gpio_usage != 0) return -EBUSY; gpio_usage = 1; return 0; } int gpio_release (struct inode *minode, struct file *mfile) gpio_usage = 0;

26 GPIO 디바이스 드라이브 – write (gpio_driver.c)
ssize_t gpio_write (struct file *file, const char *buffer, size_t length, loff_t * offset) { const char *tmp = buffer; unsigned char c=0; get_user( c, (unsigned char *)(tmp) ); switch(c){ case GPIO82_ON : GPCR2 = 1 << 18; break; /* GPIO82 ON */ case GPIO83_ON : GPCR2 = 1 << 19; break; /* GPIO83 ON */ case GPIO82_OFF : GPSR2 = 1 << 18; break; /* GPIO82 OFF */ case GPIO83_OFF : GPSR2 = 1 << 19; break; /* GPIO38 OFF */ case ALL_ON : GPCR2 = (1 << 18); GPCR2 = (1 << 19); break; /* ALL ON */ case ALL_OFF : GPSR2 = (1 << 18); GPSR2 = (1 << 19); break; /* ALL OFF */ default : printk("\n LED Write Error "); break; } return (length);

27 GPIO 디바이스 드라이브 – IRQ/Timer Handler (gpio_driver.c)
static irqreturn_t gpio_handler (void) { mod_timer (&ledoff_timer, jiffies + 200); GPCR2 = 1<<18; return IRQ_HANDLED; } static void ledoff_timeout( unsigned long unused ) GPSR2 = 1<<18;

28 GPIO 디바이스 드라이브 – init/cleanup (gpio_driver.c)
int __init gpio_init (void) { int rc = -EBUSY; /* * initialize led off timer */ init_timer (&ledoff_timer); ledoff_timer.function = ledoff_timeout; * set gpio mode pxa_gpio_mode (32 | GPIO_IN); pxa_gpio_mode (82 | GPIO_OUT); pxa_gpio_mode (83 | GPIO_OUT); set_irq_type (IRQ_GPIO (32), IRQ_TYPE_EDGE_RISING); if (request_irq (IRQ_GPIO (32), (void *)gpio_handler, SA_INTERRUPT, "pro3 gpio", NULL)) { printk (KERN_WARNING "pro3 gpio: IRQ %d is not free.\n“,IRQ_GPIO(32)); rc = -EIO; goto err_out_region2; }

29 GPIO 디바이스 드라이브 – init/cleanup (gpio_driver.c)
if (misc_register (&gpio_miscdev)) { printk(KERN_ERR "pro3 gpio: Couldn't register device 10, %d.\n", GPIO_MINOR); rc = -EBUSY; goto err_out_irq; } printk("init module, %s misc device 10: minor number : %d\n", GPIO_NAME, GPIO_MINOR); return 0; err_out_irq: free_irq (IRQ_GPIO (32), NULL); err_out_region2: return rc; void __exit gpio_exit (void) { del_timer (&ledoff_timer); misc_deregister (&gpio_miscdev); free_irq (IRQ_GPIO(32), NULL); printk("exit module, %s misc device 10: minor number : %d\n", module_init (gpio_init); module_exit (gpio_exit); MODULE_LICENSE ("GPL");

30 GPIO 디바이스 드라이브 – Test Program (test_gpio.c)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <signal.h> #define GPIO_BASE 0xbf #define GPIO_ID_SET _IOW(GPIO_BASE, 0, int) #define GPIO83_ON 0x01 #define GPIO82_ON 0x02 #define GPIO83_OFF 0x10 #define GPIO82_OFF 0x20 #define ALL_ON 0x03 #define ALL_OFF 0x30 unsigned char quit = 0; void signal_quit(int sig) { quit = 1; } int main(void) int fd, id; unsigned short c; fd = open("/dev/gpio", O_RDWR);

31 GPIO 디바이스 드라이브 – Test Program (test_gpio.c)
if (fd < 0){ printf("Device Open Error\n"); close(fd); exit(1); } c = ALL_OFF; write (fd, &c, 1); c = GPIO82_ON; c = GPIO83_ON; sleep(1); (void)signal(SIGINT, signal_quit); printf("Press <ctrl+c> to quit. \n\n"); while(!quit){ sleep(1000);

32 GPIO 디바이스 드라이브 – Makefile
#Makefile for a basic kernel module obj-m := gpio_driver.o KDIR :=/home/jbh/PXA255_Pro3/Kernel/linux pro3 PWD :=$(shell pwd) all: driver app #all: driver driver: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules app: arm-linux-gcc -o test_gpio test_gpio.c clean: rm -rf *.ko rm -rf *.mod.* rm -rf *.o rm -rf test_gpio rm -rf Module.symvers

33 GPIO 디바이스 드라이브 – IRQ 동작과정
Interrupt GPIO83 LED On GPIO32_irq pushsw_handler Timer 동작 2초 후 time_out GPIO83_LED Off

34 GPIO 디바이스 드라이브 – 테스트 타겟보드를 Linux로 부팅한 후 nfs 등을 이용하여 Host PC에서 gpio_driver.ko와 test_gpio를 복사한다. 위와 같이 타겟보드의 콘솔에서 실행한다. led가 점멸되는지 확인하고 push_switch를 누른 후 led가 on/off되는지 확인한다. (참고: driver 코드에서 misc device로 등록을 시켜서 device node는 자동으로 생성된다.) Push Switch(gpio32) LED(gpio82, 83)

35 Keypad 디바이스 드라이브 – 개 요 PXA255-PRO3보드에 실장되어 있는 Key Pad의 Device Driver를 작성한다. Device Driver를 작성하기 위해 회로도를 분석하고 Key Pad의 동작방법을 이해한다.

36 Keypad 디바이스 드라이브 – 목 차 Keypad 회로도 분석 기본 개념 동작 흐름 예제소스 분석 테스트 실행

37 Keypad 회로도 분석 (1) keypad 연결 확인 Push switch의 양쪽 접점이 Key Encoder와 연결된다.

38 Keypad 회로도 분석 (2) 74C922 Key Encoder
올바른 key값이 입력되면 ‘Data Avaliable’는 High Level을 출력한다. Key입력이 해제되면 Low Level을 출력한다. 내부 레지스터를 통해서 key가 release된 후 last key pressed 값을 출력한다.

39 Keypad 회로도 분석 (3) keypad 연결 확인 Key Available가 CPU의 GPIO[0]에 연결되었다.

40 Keypad 회로도 분석 (4) keypad 연결 확인 Data Bus Output Enable이
Chip Select [5]에 연결되었다. Key Encoder의 Data Output이 Data Bus의 하위 4bit에 연결되었다. Non-inverting Bidirectional Buffer이다.

41 Keypad 디바이스 드라이브 – 관련커널함수 (1)
ioremap() ARM은 메모리맵 IO방식을 지원한다. ioremap함수는 메모리맵 IO방식의 물리적 주소 공간을 커널에서 사용 가능한 가상 주소로 매핑하는 함수이다. 매개변수로 물리주소와 크기를 지정한다. 여기서 크기에는 MMU 가 페이지 사이즈로 관리하기 때문에 4096byte의 배수이어야 한 다. 성공적으로 매핑되면 가상주소의 선두 주소가 반환이 되고, 실패 하면 NULL을 반환한다. iounmap() ioremap함수를 이용해서 할당 후 리턴 된 가상메모리주소를 인자 로 받아서 할당영역을 해제한다. #include <asm/io.h> void *ioremap(unsigned long phys_addr, unsigned long size); void iounmap(void *addr);

42 Keypad 디바이스 드라이브 – 관련커널함수 (2)
Wait Queue 프로세스가 시스템 자원을 기다려야 하는 경우가 많이 있다. 예를 들어, Key Pad에서 데이터를 읽어올 때는 Key Pad가 눌려진 후에 데이터를 read해야 한다. 리눅스 커널은 “Wait Queue”라는 자료구조를 사용 프로세스의 task_struct에 대한 포인터와 대기큐에 있는 다음 원소에 대 한 포인터를 가짐 프로세스가 대기큐의 끝에 추가가 되면, 이들은 인터럽트 가능 (interruptible), 또는 인터럽트 불가능(uninterruptible) 상태가 된다. 인터럽트 가능한 프로세스는 대기큐에 있는 동안 발생하는 타이머 만 료나 시그널 같은 이벤트들에 의해서 인터럽트가 될 수 있다. 대기중인 프로세스의 상태는 이를 반영하여 TASK_INTERRUPTIBLE 또 는 TASK_UNINTERRUPTIBLE 둘 중의 하나가 될 것이다.

43 Keypad 디바이스 드라이브 – 관련커널함수 (3)
DECLARE_WAIT_QUEUE_HEAD(wait_queue_head_t) wait queue 초기화 macro이다. #include <linux/wait.h> #define DECLARE_WAIT_QUEUE_HEAD(name) \ wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

44 Keypad 디바이스 드라이브 – 관련커널함수 (4)
wake_up_interruptible(wait_queue_head_t *q) wake_up_interruptible는 __wake_up을 호출하는 macro이다. __wake_up함수는 waitqueue에 블록된 프로세스를 wake up한다. 두 번째 인자는 깨우고자 하는 프로세스의 상태이다. 세 번째 인자는 깨우고자 하는 프로세스의 수로 wake-one 또는 wake-many이다. interruptible_sleep_on (wait_queue_head_t *q) 현재 프로세스의 상태를 ‘TASK_INTERRUPTIBLE’로 만든다. 즉, 프로세스가 어떠한 조건을 만족할 때까지 중단되는 상태이다. #include <linux/wait.h> #define wake_up_interruptible (x) __wake_up (x, TASK_INTERRUPTIBLE, 1, NULL) void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key); #include <linux/wait.h> void fastcall __sched interruptible_sleep_on(wait_queue_head_t *q)

45 Keypad 디바이스 드라이브 – 동작 방식 while(1) Driver start Application start
keypad_init() keypad_open() open() key_read() 함수 call keypad_read() while(1) read() read() blocking Interruptible_sleep_on() waiting keypad interrupt Keypad push! keypad_handler() wakeup_interruptible() read() wake up

46 Keypad 디바이스 드라이브 소스 – 매크로/전역변수 (keypad_driver.c)
#include <asm-arm/io.h> #include <asm-arm/uaccess.h> #include <asm-arm/irq.h> #include <asm/arch/pxa-regs.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/version.h> #include <linux/miscdevice.h> #define KEYPAD_MINOR 222 #define KEYPAD_NAME "keypad" #define KEYPAD_ADDRESS 0x #define KEYPAD_BASE 0xbf #define KEYPAD_ID_SET _IOW(KEYPAD_BASE, 0, int) //Global variable static int keypad_usage = 0; static unsigned short *keypad_addr; static DECLARE_WAIT_QUEUE_HEAD(keypad_wait_queue); /* Used for blocking read */

47 Keypad 디바이스 드라이브 소스 – 매크로/전역변수 (keypad_driver.c)
// define functions... ssize_t keypad_read(struct file *inode, char *gdata, size_t length, loff_t *off_what); int keypad_open(struct inode *minode, struct file *mfile); int keypad_release(struct inode *minode, struct file *mfile); static irqreturn_t keypad_handler(void); static const struct file_operations keypad_fops = { .open = keypad_open, .read = keypad_read, .release = keypad_release, }; static struct miscdevice keypad_miscdev = { .minor = KEYPAD_MINOR, .name = KEYPAD_NAME, .fops = &keypad_fops,

48 Keypad 디바이스 드라이브 소스 – open/release (keypad_driver.c)
int keypad_open(struct inode *minode, struct file *mfile) { if(keypad_usage != 0) return -EBUSY; keypad_usage = 1; return 0; } int keypad_release(struct inode *minode, struct file *mfile) keypad_usage = 0;

49 Keypad 디바이스 드라이브 소스 – read / IRQ handler (keypad_driver.c)
ssize_t keypad_read(struct file *inode, char *gdata, size_t length, loff_t *off_what) { unsigned short keypad_value; interruptible_sleep_on (&keypad_wait_queue); keypad_value = inw(keypad_addr); if (copy_to_user(gdata, &keypad_value, 2)) return -EFAULT; return length; } static irqreturn_t keypad_handler(void) wake_up_interruptible (&keypad_wait_queue); return IRQ_HANDLED;

50 Keypad 디바이스 드라이브 소스 - init/cleanup (keypad_driver.c)
int __init keypad_init(void) { int rc = -EBUSY; keypad_addr = ioremap (KEYPAD_ADDRESS, 0x02); if (!keypad_addr) { printk(KERN_ERR "Unable to remap memory\n"); rc = -ENOMEM; goto err_out_region2; } pxa_gpio_mode(0 | GPIO_IN); set_irq_type(IRQ_GPIO(0), IRQ_TYPE_EDGE_RISING); if (request_irq(IRQ_GPIO(0), (void *)keypad_handler, SA_INTERRUPT, "pro3 keypad", NULL)) { printk (KERN_WARNING "pro3 keypad: IRQ %d is not free.\n", IRQ_GPIO(0)); rc = -EIO; goto err_out_ioremap; if (misc_register (&keypad_miscdev)) { printk(KERN_ERR "pro3 keypad: Couldn't register device 10, %d.\n", KEYPAD_MINOR); rc = -EBUSY; goto err_out_irq;

51 Keypad 디바이스 드라이브 소스 - init/cleanup (keypad_driver.c)
printk("init module, %s misc device 10: minor number : %d\n", KEYPAD_NAME, KEYPAD_MINOR); return 0; err_out_irq: free_irq(IRQ_GPIO(0), NULL); err_out_ioremap: iounmap(keypad_addr); err_out_region2: return rc; } void __exit keypad_exit(void) { misc_deregister(&keypad_miscdev); printk("exit module, %s misc device 10: minor number : %d\n", module_init(keypad_init); module_exit(keypad_exit); MODULE_LICENSE("GPL");

52 Keypad 디바이스 드라이브 소스 - Test Program (test_keypad.c)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #define KEYPAD_BASE 0xbf #define KEYPAD_ID_SET _IOW(KEYPAD_BASE, 0, int) unsigned char quit = 0; void signal_quit(int sig) { quit = 1; } int main(void) int fd; unsigned short keypad_buff = 0; fd = open("/dev/keypad", O_RDWR); if (fd < 0){ printf("Device Open Error\n"); close(fd); exit(1);

53 Keypad 디바이스 드라이브 소스 - Test Program (test_keypad.c)
(void)signal(SIGINT, signal_quit); printf("Press <ctrl+c> to quit. \n\n"); while(!quit){ read(fd, &keypad_buff, 2); printf("Read Keypad value: %d \n", 0x0F & keypad_buff); } close(fd);

54 Keypad 디바이스 드라이브 소스 - Makefile
#Makefile for a basic kernel module obj-m := keypad_driver.o KDIR :=/home/jbh/PXA255_Pro3/Kernel/linux pro3 PWD :=$(shell pwd) all: driver app #all: driver driver: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules app: arm-linux-gcc -o test_keypad test_keypad.c clean: rm -rf *.ko rm -rf *.mod.* rm -rf *.o rm -rf test_keypad rm -rf Module.symvers

55 Keypad 디바이스 드라이브 - 테스트 타겟보드를 Linux로 부팅한 후 nfs 등을 이용하여 Host PC에서 keypad_driver.ko와 test_keypad를 복사한다. 위와 같이 타겟보드의 콘솔에서 실행한다. keypad를 누르면 콘솔로 해당 key값이 나타나는지 확인한다. (참고: driver 코드에서 misc device로 등록을 시켜서 device node는 자동으로 생성된다.) keypad


Download ppt "GPIO Port 활용 디바이스 드라이버 Lecture #15."

Similar presentations


Ads by Google