Module 16: ioctl 을활용한 LED 제어디바이스드라이버 ESP30076 임베디드시스템프로그래밍 (Embedded System Programming) 조윤석 전산전자공학부
주차별목표 ioctl() 을활용법배우기 커널타이머와 ioctl 을활용하여 LED 제어용디바이스드라이브작성하기 2
IOCTL 을이용한드라이버제어 ioctl() 함수활용 어떤경우에는읽는용도로만쓰고, 어떨때는쓰는용도로만쓰다가하나의명령으로읽고쓰기를하거나혹은어떠한값을어떤방식으로처리하고싶은경우 각디바이스마다고유하게선언하여사용하는전용함수 ioctl() 함수의특징 read(), write() 함수와같이읽기와쓰기처리가가능 하드웨어를제어하거나상태값을읽어올수도있음 응용프로그램의요구에따라디바이스드라이버의매개변수해석이달라짐 3
ioctl() 함수의매개변수전달과정 ret = ioctl ( int fd, int request, char *argp); // #include <sys/ioctl.h> int dev_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) return ret; 4
디바이스드라이버내의 ioctl() 구조 int dev_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) switch( cmd ) case 구별상수 1: 처리루틴 1; break; case 구별상수 2: 처리루틴 2; break; return 0; 5
ioctl() ioctl() cmd(32-bit), arg 32-bit cmd 구성 cmd 명령을만드는매크로 매크로이름 _IO(type, nr) _IOR(type, nr, datatype) _IOW(type, nr, datatype) _IOWR(type, nr, datatype) 기능부가적인데이터가없는, 즉매개변수가없는명령생성디바이스드라이버에서데이터를읽어오기 (R) 위한명령생성디바이스드라이버에서데이터를쓰기 (R) 위한명령생성디바이스드라이버에서데이터를읽고쓰기위한명령생성 6
cmd 선언 Macro 인자의미 : _IOR(type, nr, datatype) type: 매직번호 (8-bit) nr: 구분번호 / 명령어번호 (8-bit) datatype: 전송할데이터타입 실제 cmd 선언예 #define MY_IOCTL_READ _IOR(MY_IOCTL_MAGIC, 0, int) #define MY_IOCTL_WRITE _IOW(MY_IOCTL_MAGIC, 1, int) #define MY_IOCTL_STATUS _IO(MY_IOCTL_MAGIC, 2) #define MY_IOCTL_READ_WRITE _IOWR(MY_IOCTL_MAGIC, 3, int) #define MY_IOCTL_NR 4 // 네가지명령지정 7
ioctl(): cmd 명령을해석하는매크로 cmd 명령을해석하는매크로 include/ioctl.h or include/asm-i386/ioctl.h 매크로이름 _IOC_NR(cmd) _IOC_TYPE(cmd) _IOC_SIZE(cmd) _IOC_DIR(cmd) 기능구분번호필드값을읽는매크로매직번호필드값을읽는매크로데이터크기필드값을읽는매크로읽기와쓰기속성필드값을읽는매크로 8
ioctl() 을사용하여 LED 제어하기 LED 제어기능 기능내용인자 0 0 이입력되면, LED 를모두 OFF arg 사용안함 1 1 이입력되면, LED 를모두 ON arg 사용안함 2 2 가입력되고 arg 값이들어오면, arg 값에따라 LED 를토글 arg 사용 기능이 2 이고 arg 값이 15 인경우, 하위 LED 4 개가모두꺼지고상위 LED 4 개가켜지며이후토글동작으로 LED 점멸 9
LED 제어디바이스드라이버 root@esp:~# mkdir /root/work/dd/ioctl_ioremap root@esp:~# cd /root/work/dd/ioctl_ioremap root@esp:~/work/dd/ioctl_ioremap# vi ioctl_led_driver.h #ifndef LED_KERNEL_TIMER_DRIVER #define LED_KERNEL_TIMER_DRIVER #include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> #include <asm/io.h> #include <asm/uaccess.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/slab.h> // kmalloc () #include <linux/timer.h> typedef struct struct timer_list timer; unsigned long led_state; attribute ((packed)) KERNEL_TIMER_STRUCT; #endif 10
LED 제어디바이스드라이버 root@esp:~/work/dd/ioctl_ioremap# vi myled_ioctl.h 11 #ifndef MYLED_IOCTL #define MYLED_IOCTL #define IOCTL_LED_MAGIC 'h' typedef struct unsigned long set_data; // unsigned long get_data; attribute ((packed)) ioctl_led_data_t; #define LED_OFF 0xFF #define LED_ON 0x00 #define LED_MODE_OFF 0 #define LED_MODE_ON 1 #define LED_MODE_TW 2 #define LED_IOCTL_CMD_OFF _IO(IOCTL_LED_MAGIC, LED_MODE_OFF) #define LED_IOCTL_CMD_ON _IO(IOCTL_LED_MAGIC, LED_MODE_ON) #define LED_IOCTL_CMD_TW _IOW(IOCTL_LED_MAGIC, LED_MODE_TW, ioctl_led_data_t) #define IOCTL_LED_MAXNR 3 // three commands; OFF, ON< TW #define DEV_NAME "/dev/iom_ioctl_led" #endif
LED 제어디바이스드라이버 root@esp:~/work/dd/fnd_ioremap# vi ioctl_led_driver.c 12 /* ioctl LED driver Example FILE: ioctl_led_driver.c */ #include "./myled_ioctl.h" #include "./ioctl_led_driver.h" #define IOM_LED_IOCTL_MAJOR_NUM 243 #define IOM_LED_ADDRESS 0xA8000000 #define TIME_STEP (5*HZ/10) /* 0.5 sec */ #define BLINK_MODE 1 static int ledport_usage = 0; static unsigned char *iom_led_addr; static KERNEL_TIMER_STRUCT *ptimermgr = NULL; static unsigned char led_blink = 0; static unsigned char led_tw_value = 0; MODULE_LICENSE("GPL"); MODULE_AUTHOR("HGU"); int iom_ioctl_led_init(void); void iom_ioctl_led_exit(void); module_init(iom_ioctl_led_init); module_exit(iom_ioctl_led_exit); void led_timer_timeover(unsigned long arg); /* timer function */ void led_timer_register(kernel_timer_struct *pdata, unsigned long timerover);
ioctl_led_driver.c (2) int iom_ioctl_led_open (struct inode *, struct file *); int iom_ioctl_led_release (struct inode *, struct file *); int iom_ioctl_led_ioctl (struct inode *, struct file *, unsigned int, unsigned long); struct file_operations iom_ioctl_led_fops =.owner = THIS_MODULE,.open = iom_ioctl_led_open,.ioctl = iom_ioctl_led_ioctl,.release = iom_ioctl_led_release, ; int init iom_ioctl_led_init(void) int major_num; major_num = register_chrdev(iom_led_ioctl_major_num, DEV_NAME, &iom_ioctl_led_fops); if ( major_num < 0 ) printk(kern_warning"%s: can't get or assign major number %d\n", DEV_NAME, IOM_LED_IOCTL_MAJOR_NUM); return major_num; printk("success to load the device %s. Major number is %d\n", DEV_NAME, IOM_LED_IOCTL_MAJOR_NUM); iom_led_addr = ioremap(iom_led_address, 0x1); return 0; 13
ioctl_led_driver.c (3) void led_timer_register(kernel_timer_struct *pdata, unsigned long timeover) init_timer(&(pdata->timer)); pdata->timer.expires = get_jiffies_64() + timeover; pdata->timer.function = led_timer_timeover; /* handler of the timeout */ pdata->timer.data = (unsigned long)pdata; /* argument of the handler */ add_timer(&(pdata->timer)); void led_timer_timeover(unsigned long arg) KERNEL_TIMER_STRUCT *pdata=null; pdata = (KERNEL_TIMER_STRUCT *) arg; if ( led_blink == BLINK_MODE ) pdata->led_state = ~(pdata->led_state); if(pdata->led_state ) outb(led_tw_value, (unsigned int)iom_led_addr); else outb(~led_tw_value, (unsigned int)iom_led_addr); else outb(led_off, (unsigned int)iom_led_addr); led_timer_register(pdata, TIME_STEP); /* end of led_timer_timeover() */ 14
ioctl_led_driver.c (4) int iom_ioctl_led_open (struct inode *inode, struct file *filp) if ( ledport_usage ) return -EBUSY; ledport_usage = 1; return 0; int iom_ioctl_led_release (struct inode *inode, struct file *filp) ledport_usage = 0; return 0; int iom_ioctl_led_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ioctl_led_data_t led_data; int size, ret; if ( _IOC_TYPE(cmd)!= IOCTL_LED_MAGIC ) return -EINVAL; if ( _IOC_NR(cmd) >= IOCTL_LED_MAXNR ) return -EINVAL; size = _IOC_SIZE(cmd); printk(kern_alert"[_ioc_type(cmd)]=%c, [_IOC_TYPE_NR(cmd)]=%d, [_IOC_SIZE(cmd)] = %d\n", _IOC_TYPE(cmd), _IOC_NR(cmd), size); 15
ioctl_led_driver.c (5) 16 switch (cmd) case LED_IOCTL_CMD_OFF: led_blink = 0; if(ptimermgr!= NULL ) del_timer(&(ptimermgr->timer)); kfree(ptimermgr); ptimermgr = NULL; outb(led_off, (unsigned int) iom_led_addr); break; case LED_IOCTL_CMD_ON: led_blink = 0; if(ptimermgr!= NULL ) del_timer(&(ptimermgr->timer)); kfree(ptimermgr); ptimermgr = NULL; outb(led_on, (unsigned int) iom_led_addr); break; case LED_IOCTL_CMD_TW: ret = copy_from_user( (void *)&led_data, (const void *)arg, sizeof(led_data)); led_blink = 1; led_tw_value = led_data.set_data; ptimermgr = kmalloc ( sizeof(kernel_timer_struct), GFP_KERNEL GFP_ZERO); if ( ptimermgr == NULL ) return -ENOMEM; led_timer_register(ptimermgr, TIME_STEP); break; default: return 0; break;
ioctl_led_driver.c (6) void exit iom_ioctl_led_exit(void) if ( ptimermgr!= NULL ) printk(kern_alert"[driver][exit]... ptimermgr is not NULL...\n"); del_timer(&(ptimermgr->timer)); kfree(ptimermgr); ptimermgr = NULL; outb(led_off, (unsigned int)iom_led_addr); iounmap(iom_led_addr); unregister_chrdev(iom_led_ioctl_major_num, DEV_NAME); printk("success to unload the device %s...\n", DEV_NAME); 17
LED 구동응용프로그램 (ioctl_led_test.c) root@esp:~/work/dd/fnd_ioremap# vi ioctl_led_test.c /* IOCTL Test program */ #include "myled_ioctl.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> int main(int argc, char **argv) int led_fd; ioctl_led_data_t led_data; unsigned char get_cmd, get_arg; led_fd = open(dev_name, O_RDWR); if ( led_fd < 0 ) printf("led ioctl driver open failure..\n"); return -1; 18 if (!(argc == 2 argc == 3) ) printf("%s [cmd(0 ~ 2)] or [arg(0~255)]\n", argv[0]); printf(" cmd: 0 - OFF\n"); printf(" cmd: 1 - ON\n"); printf(" cmd: 2 - Twinkle with the value of arg(0~255)\n"); exit(1);
ioctl_led_test.c (2) get_cmd = (unsigned char) atoi(argv[1]); if ( argc == 3 ) get_arg = (unsigned char) atoi(argv[2]); if ( get_arg < 0 get_arg > 255 ) get_arg = 255; switch (get_cmd) case LED_MODE_OFF: printf("[test]: OFF mode\n"); ioctl(led_fd, LED_IOCTL_CMD_OFF); break; case LED_MODE_ON: printf("[test]: ON mode\n"); ioctl(led_fd, LED_IOCTL_CMD_ON); break; case LED_MODE_TW: printf("[test]: TW mode\n"); led_data.set_data = get_arg; ioctl(led_fd, LED_IOCTL_CMD_TW, &led_data); break; default: break; close(led_fd); return 0; 19
컴파일 % vi Makefile CC = arm-linux-gcc obj-m = ioctl_led_driver.o KDIR = /root/download/kernel-2.6.35 PWD = $(shell pwd) TEST_TARGET = ioctl_led_test TEST_SRCS = $(TEST_TARGET).c all: module test_pgm module: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules test_pgm: $(CC) $(TEST_SRCS) -o $(TEST_TARGET) clean: rm -rf *.ko rm -rf *.o *.mod.* rm -rf *.symvers *.order.*.cmd rm -rf $(TEST_TARGET) % make 20
실행 타겟보드에서실행 Host 컴퓨터의 /root 디렉토리를 NFS 로연결 # cd /root/nfs/work/dd/ioctl_ioremap # insmod ioctl_led_driver.ko # mknod /dev/ioctl_iom_led c 243 0 #./ioctl_led_test 1 #./ ioctl_led_test 0 #./ ioctl_led_test 2 15 # rmmod ioctl_led_driver 21