
강좌 & 팁
안녕하세요 판다 이우영입니다.
저번 시간에 이어서 IOCTL 디바이스 드라이버를 구현해 보겠습니다.
1. 복습!!
디바이스 드라이버에서도 전에 만들어둔 해더 파일을 사용하기 때문에 다시 한번 확인하고 넘어가겠습니다.
#ifndef _IOCTL_H_ #define _IOCTL_H_
typedef struct { unsigned long size; unsigned int buff[128]; } __attribute__((packed)) ioctl_info;
#define IOCTL_MAGIC 'g' #define LED_ON _IO( IOCTL_MAGIC, 0) #define LED_OFF _IO( IOCTL_MAGIC, 1) #define LED_TIMER _IOW( IOCTL_MAGIC, 2 , ioctl_info ) #define GET_LED_TIME _IOR( IOCTL_MAGIC, 3 , ioctl_info )
#define IOCTL_MAXNR 4 #endif // _IOCTL_H_ |
위에 보시면 사용할 명령어들이 있습니다.
매직 넘버는 “g”를 사용하며 LED를 제어하기 위하여 “LED_ON”과 “LED_OFF”명령어를 만들고,
“LED_TIMER” 명령어를 사용하며 시간 주기마다 LED를 점멸시키기 위해 만들었습니다.
“GET_LED_TIME” 명령어는 타이머 시간 주기 값을 읽어 옵니다.
2. IOCTL 디바이스 드라이버
IOCTL을 사용하는 디바이스 드라이버 부터 보겠습니다.
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h>
#include <linux/version.h> #include <linux/fs.h> #include <linux/sched.h>
#include <linux/interrupt.h> #include <linux/wait.h> #include <linux/irq.h>
#include <linux/ioport.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/proc_fs.h> #include <linux/workqueue.h>
#include <linux/time.h> #include <linux/timer.h>
#include <linux/poll.h>
#include <asm/system.h> #include <asm/uaccess.h> #include <asm/ioctl.h> #include <asm/unistd.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/gpio.h> #include <asm/mach/arch.h>
#include <plat/gpio-cfg.h> #include "ioctl-gpio-int.h" #include "ioctl.h"
#define IOCTL_DEV_NAME "dev_ioctl" #define IOCTL_DEV_MAJOR 241
#define TIME_STEP (HZ*2)
#define LED_GPIO_DATA_REGISTER 0x7F008184 #define LED0 (1<<2) #define LED1 (1<<3) #define LED2 (1<<4) #define LED ((1<<2)|(1<<3)|(1<<4)) #define PAGE_SIZES 0x1000 #define DATA_SIZE 0x1000
u32 led_time;
DECLARE_WAIT_QUEUE_HEAD(
WaitQueue_Read );
typedef struct { struct
timer_list timer; unsigned
long led; u8 flag; }
__attribute__
((packed)) KERNEL_TIMER_MANAGER;
KERNEL_TIMER_MANAGER *ptrmng =
NULL; static unsigned
int ReadQ_timet_Count = 0; volatile u32 *led_data;
void kerneltimer_led(unsigned
long arg);
void kerneltimer_registertimer(KERNEL_TIMER_MANAGER *pdata, unsigned long timeover) { init_timer(&(pdata->timer)); pdata->timer.expires =
get_jiffies_64( ) + timeover; pdata->timer.data = (unsigned long) pdata; pdata->timer.function =
kerneltimer_led; add_timer(&(pdata->timer)); }
void kerneltimer_led(unsigned
long arg) { KERNEL_TIMER_MANAGER
*pdata = NULL;
if(arg) { pdata = (KERNEL_TIMER_MANAGER
*) arg;
*led_data = (*led_data)^LED;
kerneltimer_registertimer(pdata,
led_time); } }
int ioctl_open(struct
inode *inode, struct file
*filp) { ptrmng = kmalloc(sizeof(KERNEL_TIMER_MANAGER), GFP_KERNEL); if(ptrmng
== NULL) return
-ENOMEM;
memset(ptrmng,
0, sizeof(KERNEL_TIMER_MANAGER));
return 0; }
int ioctl_ioctl(struct
inode *inode, struct file
*filp, unsigned int cmd, unsigned
long arg) { ioctl_info ctrl_info; int size;
if(_IOC_TYPE(cmd)
!= IOCTL_MAGIC) { printk("magic
err\n"); return
-EINVAL; } if(_IOC_NR(cmd)
>= IOCTL_MAXNR) { printk("NR
err\n"); return
-EINVAL; } size = _IOC_SIZE(cmd);
switch(cmd) { case LED_ON
: *led_data = 0; break; case
LED_OFF: if(ptrmng
!= NULL) { del_timer(&(ptrmng->timer)); } *led_data = LED; break; case
LED_TIMER: if(ptrmng
!= NULL) { del_timer(&(ptrmng->timer)); } copy_from_user ((void *)&ctrl_info, (const
void *)arg,
size);
led_time = ctrl_info.buff[0]; kerneltimer_registertimer(ptrmng,
led_time); break; case
GET_LED_TIME: ctrl_info.buff[0] = led_time; ctrl_info.size = 1;
copy_to_user((const void *)arg,(void *)&ctrl_info, size);
break; } }
int ioctl_release(struct
inode *inode, struct file
*filp) { if(ptrmng
!= NULL) { del_timer(&(ptrmng->timer)); kfree(ptrmng); }
return
0; }
struct file_operations ioctl_fops = { .owner = THIS_MODULE, .open = ioctl_open, .ioctl = ioctl_ioctl, .release = ioctl_release, };
int ioctl_init( void
) { int result;
led_data =
ioremap(LED_GPIO_DATA_REGISTER, PAGE_SIZES);
result =
register_chrdev(IOCTL_DEV_MAJOR,IOCTL_DEV_NAME,&ioctl_fops);
if( result
< 0) return
result;
return
0; }
void ioctl_exit( void
) { iounmap( led_data ); unregister_chrdev(IOCTL_DEV_MAJOR,
IOCTL_DEV_NAME); } module_init(
ioctl_init ); module_exit(
ioctl_exit );
MODULE_LICENSE("Dual BSD/GPL"); |
동작은 open, release 말고는 ioctl 만 있습니다.
저번 시간에 응용프로그램에서 4개의 명령이 있었죠?
(LED_ON, LED_OFF, LED_TIMER, GET_LED_TIME)
이 4개의 동작을 지원하도록 ioctl_ioctl 함수를 만들었습니다.
먼저 MAGIC 넘버가 맞는지 확인 후 IOCTL 명령을 해석합니다. 이때 정해진 명령이 아니면 에러 처리합니다.
switch 문을 통해서 4개의 동작을 정의 하고 각각의 동작을 구현했습니다.
LED_ON, LED_OFF 는 이름에 맞는 동작을 정의했습니다.
LED_TIMER는 넘겨주는 인자값의 시간마다 LED를 ON, OFF하는 명령 입니다.
GET_LED_TIME은 LED_TIMER에서 설정한 시간을 읽어 옵니다.
그럼 오늘도 여기까지!!
다음시간에 이어서 하도록 하겠습니다.
그럼 다음시간에 만나요~