-
디바이스 드라이버 / 디바이스의 제어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초간 대기.
종료
성공..
'Linux > Linux Device Driver' 카테고리의 다른 글
디바이스 드라이버 / 블록킹 I/O (0) 2020.09.01 디바이스 드라이버 / 인터럽트 처리 (0) 2020.09.01 디바이스 드라이버 / 시간 처리와 커널 타이머 (0) 2020.09.01 디바이스 드라이버 / LED 제어 (0) 2020.09.01 디바이스 드라이버 / 부 번호에 의한 파일 처리 (0) 2020.09.01