ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 디바이스 드라이버 / 디바이스의 제어
    Linux/Linux Device Driver 2020. 9. 1. 22:55

    하드웨어에 데이터를 써넣을 때는 write() 함수를, 반대로 하드웨어에서 발생한 데이터를 읽을 때는 read() 함수를 이용한다. 하지만 read와 write만으로 하드웨어를 제어할 수 없는 경우도 종종 발생한다.

     

    그래서 리눅스 커널은 ioctl이라는 방식을 제공한다. ioctl() 함수는 디바이스 파일 이외에는 사용할 수 없는 디바이스 파일 전용 함수이므로 각 디바이스다마다 고유하게 선언하여 사용한다. 그래서 read()함수와 write() 함수 같이 정형화된 형태를 기본적으로 유지하지만 사용 방법은 다비아스마다 모두 다르다.

     

     

    개념적인 xxx_ioctl 구성

     

    static long xxx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {

       // cmd 유효성 검사

       // arg에 전달된 사용자 메모리 검사

     

       switch(cmd)

       {

          case 구별상수 1 :

             처리루틴 1

             break;

          case 구별상수 1 :

             처리루틴 1

             break;

       }

     

       return 0;

    }

     

     

    ※ 2.6 버전에 ioctl() 함수가 사라지고 unlocked_ioctl() 함수와  compat_ioctl() 함수가 생겼다.

     

    왜 ioctl() 함수가 사라지고 unlocked_ioctl() 함수와  compat_ioctl() 함수가 생겼을까?

    더보기

    추가 중..

     

     

     

     

     

    ioctl에 전달되는 cmd와 관련된 매크로 함수

    cmd 구성

    읽기쓰기구분(DIR): 읽기를 위한 요구 명령인지 쓰기를 위한 요구 명령인지 구분하는 속성

    데이터 크기(size): 매개변수 arg를 통해 전달되는 메모리의 크기

    매직번호(type): 다른 디바이스 드라이버의 ioctl 명령과 구분하기 위한 값 

    구분번호(nr): 명령을 구분하는 명령어의 순차 번호

     

     

    cmd 명령을 만드는 매크로 함수

    _IO(type, nr): 부가적인 데이터가 없는 명령을 만드는 매크로

    _IOR(type, nr, size): 디바이스 드라이버에서 데이터를 읽어오는 명령을 만드는 매크로

    _IOW(type, nr, size): 디바이스 드라이버에 데이터를 쓸 명령을 만드는 매크로

    _IOWR(type, nr, size): 디바이스 드라이버에 데이터를 쓰고 읽기 위한 명령을 만드는 매크로

     

    cmd 명령을 해석하는 매크로 함수

    _IOC_NONE 속성이 없다.

    _IOC_READ: 읽기 속성이다.

    _IOC_WRITE: 쓰기 속성이다.

    _IOC_READ|_IOC_WRITE: 읽기, 쓰기 속성이다.

     

    _IOC_DIR(cmd): 디바이스 드라이버에서

    _IOC_TYPE(cmd): 디바이스 드라이버에서 매직번호를 추출하는 매크로

    _IOC_NR(cmd): 디바이스 드라이버가 명령에서 이 구분 번호를 추출하는 매크로

    _IOC_SIZE(cmd): 디바이스 드라이버에서 전달된 명령에서 크기값을 추출하는 매크로

     

     

     

     

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ nano ioctl_test_gpio.h

    #define GPIO_BASE	0x3F200000
    #define GPIO_SIZE	180
    
    #define GPIO_IN(g)	(*(gpio + (g / 10)) &= ~(7 << ((g % 10) * 3)))
    #define GPIO_OUT(g)	(*(gpio + (g / 10)) |= (1 << ((g % 10) * 3)))
    #define GPIO_SET(g)	(*(gpio + 7) = 1 << g)
    #define GPIO_CLR(g)	(*(gpio + 10) = 1 << g)
    #define GPIO_GET(g)	(*(gpio + 13) & (1 << g))
    
    volatile unsigned *gpio;

     

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ nano ioctl_test.h

    #define IOCTL_TEST_MAGIC	't'
    
    typedef struct
    {
    	int pin;
    	int state;
    } __attribute__((packed)) ioctl_test_info;
    
    #define IOCTL_TEST_GPIO_INPUT	_IO(IOCTL_TEST_MAGIC, 0)
    #define IOCTL_TEST_GPIO_OUTPUT	_IO(IOCTL_TEST_MAGIC, 1)
    #define IOCTL_TEST_LED_ON	_IO(IOCTL_TEST_MAGIC, 2)
    #define IOCTL_TEST_LED_OFF	_IO(IOCTL_TEST_MAGIC, 3)
    #define IOCTL_TEST_LED_STATE	_IO(IOCTL_TEST_MAGIC, 4)
    
    #define IOCTL_TEST_MAX_NR	5

     

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ nano ioctl_test_driver.c

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/uaccess.h>
    #include <linux/slab.h>
    #include <linux/io.h>
    
    #include "ioctl_test.h"
    #include "ioctl_test_gpio.h"
    
    #define MAJOR_DEV_NAME	"ioctl_test_dev"
    #define MAJOR_DEV_NUM	200
    
    static int ioctl_test_open(struct inode *inode, struct file *file)
    {
    	void *map;
    
    	printk("ioctl_test_open\n");
    
    	map = ioremap(GPIO_BASE, GPIO_SIZE);
    
    	if(!map)
    	{
    		printk("ioremapp error\n");
    
    		return -EBUSY;
    	}
    
    	gpio =  map;
    
    	return 0;
    }
    
    static ssize_t ioctl_test_close(struct inode *inode, struct file *file)
    {
    	printk("ioctl_test_close\n");
    
    	if(gpio)
    	{
    		iounmap(gpio);
    	}
    
    	return 0;
    }
    
    static long ioctl_test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
    	int err, size;
    
    	ioctl_test_info led_info;
    
    	printk("ioctl_test_ioctl\n");
    
    
    
    	if(_IOC_TYPE(cmd) != IOCTL_TEST_MAGIC)
    	{
    		return -EINVAL;
    	}
    
    	if(_IOC_NR(cmd) >= IOCTL_TEST_MAX_NR)
    	{
    		return -EINVAL;
    	}
    
    	size = _IOC_SIZE(cmd);
    
    	if(size)
    	{
    		err = 0;
    
    		if(_IOC_DIR(cmd) & _IOC_READ)
    		{
    			err = access_ok(VERIFY_WRITE, (void *) arg, size);
    		}
    		else if(_IOC_DIR(cmd) & _IOC_WRITE)
    		{
    			err = access_ok(VERIFY_READ, (void *) arg, size);
    		}
    
    		if(!err)
    		{
    			return err;
    		}
    	}
    
    
    	switch(cmd)
    	{
    		case IOCTL_TEST_GPIO_INPUT:
    			err = copy_from_user(&led_info, (const void *)arg, sizeof(led_info));
    
    			if(err)
    			{
    				printk("copy_from_user error\n");
    
    				return err;
    			}
    
    			GPIO_IN(led_info.pin);
    
    			break;
    
    		case IOCTL_TEST_GPIO_OUTPUT:
    			err = copy_from_user(&led_info, (const void *)arg, sizeof(led_info));
    
                            if(err)
    			{
    				printk("copy_from_user error\n");
    
    				return err;
    			}
    
    			GPIO_OUT(led_info.pin);
    
    			break;
    
    		case IOCTL_TEST_LED_ON:
    			err = copy_from_user(&led_info, (const void *)arg, sizeof(led_info));
    
                            if(err)
    			{
    				printk("copy_from_user error\n");
    
    				return err;
    			}
    
    			GPIO_SET(led_info.pin);
    
    			break;
    
    		case IOCTL_TEST_LED_OFF:
    			err = copy_from_user(&led_info, (const void *)arg, sizeof(led_info));
    
                            if(err)
    			{
    				printk("copy_from_user error\n");
    
    				return err;
    			}
    
    			GPIO_CLR(led_info.pin);
    
    			break;
    
    		case IOCTL_TEST_LED_STATE:
    			err = copy_from_user(&led_info, (const void *)arg, sizeof(led_info));
    
                            if(err)
    			{
    				printk("copy_from_user error\n");
    
    				return err;
    			}
    
    			led_info.state = GPIO_GET(led_info.pin);
    
    			err = copy_to_user((void *)arg, &led_info, sizeof(led_info));
    
                            if(err)
    			{
    				printk("copy_to_user error\n");
    
    				return err;
    			}
    
    			break;
    	}
    
    	return 0;
    }
    
    struct file_operations ioctl_test_fops = {
    .owner = THIS_MODULE,
    .open = ioctl_test_open,
    .release = ioctl_test_close,
    .unlocked_ioctl = ioctl_test_ioctl,
    };
    
    int ioctl_test_init(void)
    {
    	int err;
    
    	printk("ioctl_test_init\n");
    
    	err = register_chrdev(MAJOR_DEV_NUM, MAJOR_DEV_NAME, &ioctl_test_fops);
    
    	if(err < 0)
    	{
    		printk("register_chrdev error\n");
    
    		return err;
    	}
    
    	return 0;
    }
    
    void ioctl_test_exit(void)
    {
    	printk("ioctl_test_exit\n");
    
    	unregister_chrdev(MAJOR_DEV_NUM, MAJOR_DEV_NAME);
    }
    
    module_init(ioctl_test_init);
    module_exit(ioctl_test_exit);
    
    MODULE_AUTHOR("icjk1003@gmail.com");
    MODULE_LICENSE("GPL");

     

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ nano ioctl_test_app.c

    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ioctl.h>
    
    #include "ioctl_test.h"
    
    #define MAJOR_DEV_NAME "/dev/ioctl_test_dev"
    
    int main(int argc, char** argv)
    {
    	int fd;
    	ioctl_test_info led_info;
    
    	fd = open(MAJOR_DEV_NAME, O_RDWR);
    
    	if(fd < 0)
    	{
    		printf(MAJOR_DEV_NAME " open error\n");
    
    		exit(1);
    	}
    
    	led_info.pin = 18;
    
    	ioctl(fd, IOCTL_TEST_GPIO_OUTPUT, &led_info);
    
    	ioctl(fd, IOCTL_TEST_LED_ON, &led_info);
    	ioctl(fd, IOCTL_TEST_LED_STATE, &led_info);
    	printf("%d\n", led_info.state);
    	sleep(5);
    
    	ioctl(fd, IOCTL_TEST_LED_OFF, &led_info);
    	ioctl(fd, IOCTL_TEST_LED_STATE, &led_info);
    	printf("%d\n", led_info.state);
    	sleep(5);
    
    	close(fd);
    
    	return 0;
    }

     

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ nano Makefile

    obj-m := ioctl_test_driver.o
    KDIR := /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)
    
    default:  ioctl_test_app ioctl_test_driver
    
    ioctl_test_app:
    	gcc -o $@ ioctl_test_app.c
    ioctl_test_driver:
    	make -C $(KDIR) M=$(PWD) modules
    clean:
    	make -C $(KDIR) M=$(PWD) clean
    	rm ioctl_test_app

     

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ make

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ sudo mknod /dev/ioctl_test_dev c 200 1

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ sudo insmod ioctl_test_driver.ko

     

    pi@raspberrypi:~/Downloads/devicedriver/ioctl $ sudo ./ioctl_test_app

     

     

    LED를 킨다.

    LED의 상태를 출력한다.

    (LED가 켜져있으면 32비트중 비트 18가 1이므로 1 << 18 == 262,144이다. 다른 핀도 사용 중이라면 다른 비트 값이 더해져 262144보다 큰 값이 나온다.)

    5초간 대기.

    LED를 끈다.

    LED의 상태를 출력한다.

    (LED가 꺼져있으면 32비트중 비트 18가 0이므로 0이다. 다른 핀도 사용 중이라면 다른 비트의 값이 남이있어서 0보다 큰 값이 나온다.)

    5초간 대기.

    종료

     

     

     

    성공..

    댓글

Designed by Tistory.