모듈 헤더. 커널 모듈형식으로 제작한 디바이스 드라이버 코드로 framework와 같다.
커널 로그에 메시지를 찍을 때 사용하는 헤더. prink()는 이전 방식이며, 요즘은 pr_로그레벨
로 사용한다.
log lev | method | mean |
---|---|---|
0 | pr_emerg() | 시스템이 동작하지 않음 |
1 | pr_alert() | 즉시 출력 메시지 |
2 | pr_crit() | 치명적 에러 메시지 |
3 | pr_err() | 에러 메시지 |
4 | pr_warn() | 경고 메시지 |
5 | pr_notice() | 정상 메시지 |
6 | pr_info() | 시스템 정보 메시지 |
7 | pf_debug() | 디버깅 정보 |
__init
, __exit
매크로를 갖는다.
__init
: 초기화 함수 앞에 붙이며, 호출된 뒤 메모리를 해제한다. __exit
: 모듈 언로드시 메모리 해제한다.file system 접근을 위한 헤더이다.
device file 관리용 헤더이다. device_create(), class_create() 등 device file을 생성 및 관리하는 API를 모아놨다.
//devicefile open 시 호출
static int deviceFile_open(struct inode *inode, struct file *filp){
pr_info("Open Device\n");
return 0;
}
//devicefile close 시 호출
static int deviceFile_release(struct inode *inode, struct file *filp){
pr_info("Close Device\n");
return 0;
}
//구조체 지정초기화를 이용한 fops 구조체 초기화
static struct file_operations fops = {
.owner = THIS_MODULE, // device driver 모듈의 host, THIS_MODULE 매크로로 지정(linux/module.h)
.open = deviceFile_open, // deviceFile_open() 으로 등록
.release = deviceFile_release, // deviceFile_release() 로 등록
};
fops 동작 방법
deviceFile에 특정 API로 접근하면, 등록된 함수가 호출된다. 이때 특정 API란 System Call이다.
int register_chrdev(unsigned int major, const char name, const struct file_operations fops)
커널이 관리하는 chrdev의 배열에 캐릭터 디바이스 드라이버를 등록하는 API.
static int __init deviceFile_init(void)
{
int ret = register_chrdev(NOD_MAJOR, NOD_NAME, &fops); //chrdev를 모듈에 등록
if( ret < 0 ){
pr_alert("Register File\n");
return ret;
}
pr_info("Insmod Module\n");
return 0;
}
void unregister_chrdev(unsigned int major, const char* name)
캐릭터 디바이스 드라이버를 해제하는 API.
static void __exit deviceFile_exit(void)
{
unregister_chrdev(NOD_MAJOR, NOD_NAME);
pr_info("Unload Module\n");
}
struct class class_create(owner, char name)
device file들을 그룹화하고 관리하는 class를 생성하는 API.
struct class *cls = class_create(THIS_MODULE, NOD_NAME);
device_create(struct class class, struct device parent, dev_t devt, fmt)
device file 생성
device_create(cls, NULL, MKDEV(NOD_MAJOR, 0), NULL, NOD_NAME);
device file 해제
device_destroy(cls, MKDEV(NOD_MAJOR, 0));
class_destroy(struct class* class)
device file의 class 삭제
class_destroy(cls);
int ioctl(int fd, unsigned long request, unsigned long arg)
//ioctl로 /dev/deviceFile 에 _IO() 매크로로 arg 값 전달
ioctl(fd, _IO(0,3), 16); //16
ioctl(fd, _IO(0,4), 0xf); //15
ioctl(fd, _IO(0,5), 0b1111); //15
int ret = ioctl(fd, _IO(0,6), 0);
cmd parameter
kernel에서의 안전한 명령 코드 작성을 위해 만들어진 "명령 코드 작성 규격"이다. 약속된 cmd 변수의 비트 단위 Format은 32bit이다.
cmd parameter를 다 지키며 개발하는 것은 매우 많은 시간이 걸리기 때문에 매크로가 제공된다.
_IO(type, number)
: 단순한 타입_IOR(type, number, 전송 받을 데이터 타입)
: read 용_IOW(type, number, 전송 보낼 데이터 타입)
: write 용_IOWR(type, number, 전송 주고 받을 데이터 타입)
: read + write 용_IO(0, 1), (0, 2)
은 시스템에서 사용하고 있기 때문에 _IO(0, 3)
부터 사용한다. app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>
#define NOD_NAME "/dev/deviceFile"
void btn_interrupt(int signum){
for(int i=0; i<3; i++){
printf("PRESSED!\n");
}
}
int main(){
signal(SIGIO, btn_interrupt);
int fd = open(NOD_NAME, O_RDWR);
if( fd<0 ){
printf("ERROR\n");
exit(1);
}
int pid = getpid();
ioctl(fd, _IO(0,3), pid);
printf("Start! pid : %d\n", pid);
while(1){
printf("====HI====\n");
usleep(300*1000);
}
printf("End!\n");
close(fd);
return 0;
}
devicedriver.c
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#define NOD_NAME "deviceFile"
MODULE_LICENSE("GPL");
static int NOD_MAJOR;
static struct class *cls;
#define BTN 2
#define DEV_NAME "BTN"
static int irq_num;
static int app_pid;
static struct kernel_siginfo sig_info;
static struct task_struct *task;
static irqreturn_t btn_handler(int irq, void* data){
pr_info("%s pressed!\n", DEV_NAME);
if( app_pid>0 ){
task = pid_task(find_vpid(app_pid), PIDTYPE_PID);
if( task ){
pr_info("Find app!\n");
send_sig_info(SIGIO, &sig_info, task);
pr_info("Send Signal\n");
}
}
return IRQ_HANDLED;
}
static int deviceFile_open(struct inode *inode, struct file *filp){
pr_info("Open Device\n");
return 0;
}
static int deviceFile_release(struct inode *inode, struct file *filp){
pr_info("Close Device\n");
return 0;
}
static ssize_t deviceFile_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
switch(cmd){
case _IO(0,3):
app_pid = (int)arg;
pr_info("PID : %d\n", app_pid);
break;
default :
return -EINVAL;
}
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = deviceFile_open,
.release = deviceFile_release,
.unlocked_ioctl = deviceFile_ioctl,
};
static int __init deviceFile_init(void)
{
NOD_MAJOR = register_chrdev(0, NOD_NAME, &fops);
if( NOD_MAJOR < 0 ){
pr_alert("Register File\n");
return NOD_MAJOR;
}
pr_info("Insmod Module\n");
cls = class_create(THIS_MODULE, NOD_NAME);
device_create(cls, NULL, MKDEV(NOD_MAJOR, 0), NULL, NOD_NAME);
pr_info("Major number %d\n", NOD_MAJOR);
pr_info("Device file : /dev/%s\n", NOD_NAME);
gpio_request(BTN, "BTN");
irq_num = gpio_to_irq(BTN);
gpio_direction_input(BTN);
int ret = request_irq(irq_num, btn_handler, IRQF_TRIGGER_FALLING, DEV_NAME, NULL);
return 0;
}
static void __exit deviceFile_exit(void)
{
free_irq(irq_num, DEV_NAME);
gpio_free(BTN);
device_destroy(cls, MKDEV(NOD_MAJOR, 0));
class_destroy(cls);
unregister_chrdev(NOD_MAJOR, NOD_NAME);
pr_info("Unload Module\n");
}
module_init(deviceFile_init);
module_exit(deviceFile_exit);
Makefile
KERNEL_HEADERS=/lib/modules/$(shell uname -r)/build
CC = gcc
TARGET := app
obj-m += devicedriver.o
PWD := $(CURDIR)
#make 시 make all 실행
all: driver app
#driver build
driver:
make -C $(KERNEL_HEADERS) M=$(PWD) modules
#app build
app:
$(CC) -o $@ $@.c
#driver, app 모두 제거
clean:
make -C $(KERNEL_HEADERS) M=$(PWD) clean
rm -f *.o $(TARGET)
좋은 정보 감사합니다