panda.jpg 


안녕하세요 판다 이우영입니다.


저번 시간에 이어서 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_ONLED_OFF 는 이름에 맞는 동작을 정의했습니다.


LED_TIMER는 넘겨주는 인자값의 시간마다 LED를 ON, OFF하는 명령 입니다.


GET_LED_TIME은 LED_TIMER에서 설정한 시간을 읽어 옵니다.



그럼 오늘도 여기까지!!


다음시간에 이어서 하도록 하겠습니다.


그럼 다음시간에 만나요~