#define BUF_SIZE 1024
static char kernel_write_buffer[BUF_SIZE];
static dev_t kdt_dev;
static struct cdev kdt_cdev;
static struct class *kdt_class;
static struct task_struct *wait_thread;
static int wait_queue_flag = 0;
DECLARE_WAIT_QUEUE_HEAD(wait_queue);
#define DRIVER_NAME "kdt_kthread_driver"
#define DRIVER_CLASS "kdt_kthread_class"
#define KDT_GPIO_OUTPUT 17 // 예시
#define KDT_GPIO_INPUT 16 // 예시// 유저 공간에서 read 시스템 콜 호출 시 실행
static ssize_t kdt_driver_read(struct file *filp, char __user *buf, size_t size, loff_t *offset) {
if (copy_to_user(buf, "t", 1)) {
pr_err("read: error\n");
return -EFAULT;
}
pr_info("Read Function\n");
wait_queue_flag = 1; // 대기 큐 플래그 설정
wake_up_interruptible(&wait_queue); // 대기 중인 kthread 깨우기
pr_info("Value of button: %d\n", gpio_get_value(KDT_GPIO_INPUT)); // GPIO 값 읽기 (예시)
return BUF_SIZE;
}
// kthread로 실행되는 Bottom Half 함수
static int kdt_wait_kthread(void *unused) {
while (1) {
pr_info("waiting for event\n");
wait_event_interruptible(wait_queue, wait_queue_flag != 0); // 이벤트 대기
if (wait_queue_flag == 2) {
return 0;
}
pr_info("wake-up!!!\n");
wait_queue_flag = 0; // 대기 큐 플래그 초기화
}
do_exit(0);
return 0;
}
// 모듈 초기화 함수
static int __init kdt_driver_init(void) {
// ... (디바이스 등록 코드 생략) ...
// kthread 생성
wait_thread = kthread_create(kdt_wait_kthread, NULL, "kdt_wait_thread");
if (wait_thread) {
pr_info("Thread created successfully\n");
wake_up_process(wait_thread); // 스레드 깨우기
} else {
pr_info("Thread creation failed\n");
return -ENOMEM;
}
// ... (GPIO 설정 코드 생략) ...
return 0;
}
// 모듈 종료 함수
static void __exit kdt_driver_exit(void) {
// ... (디바이스 해제 코드 생략) ...
// ... (GPIO 해제 코드 생략) ...
}
module_init(kdt_driver_init);
module_exit(kdt_driver_exit);
MODULE_LICENSE("GPL");
#define SIGR 45 // 유저 공간에 전달할 시그널 번호#define BUF_SIZE 1024
static char kernel_write_buffer[BUF_SIZE];
static dev_t kdt_dev;
static struct cdev kdt_cdev;
static struct class *kdt_class;
static struct task_struct *wait_thread;
int wait_queue_flag = 0;
unsigned int button_irq;
static struct task_struct *user_space_task = NULL; // 유저 공간 태스크 구조체
DECLARE_WAIT_QUEUE_HEAD(wait_queue);
#define DRIVER_NAME "kdt_interrupt_driver"#define DRIVER_CLASS "kdt_interrupt_class"#define KDT_GPIO_OUTPUT 17 // 예시#define KDT_GPIO_INPUT 16
// 유저 공간에서 read 시스템 콜 호출 시 실행
static ssize_t kdt_driver_read(struct file *filp, char __user *buf, size_t size, loff_t *offset) {
if (copy_to_user(buf, "t", 1)) {
pr_err("read: error\n");
return -EFAULT;
}
user_space_task = get_current(); // 시그널을 전달할 유저 공간 태스크 저장
pr_info("Userspace PID: %d is registered\n", user_space_task->pid);
wait_queue_flag = 3; // 대기 큐 플래그 설정
wake_up_interruptible(&wait_queue); // 대기 중인 kthread 깨우기
pr_info("Value of button: %d\n", gpio_get_value(KDT_GPIO_INPUT));
return BUF_SIZE;
}
// kthread로 실행되는 Bottom Half 함수
static int kdt_wait_kthread(void *unused) {
while (1) {
pr_info("waiting for event\n");
wait_event_interruptible(wait_queue, wait_queue_flag != 0);
if (wait_queue_flag == 2) {
return 0;
}
pr_info("wake-up!!!\n");
wait_queue_flag = 0;
}
do_exit(0);
return 0;
}
// GPIO 인터럽트 핸들러 (Bottom Half)
irqreturn_t kdt_gpio_irq_signal_handler(int irq, void *data) {
pr_info("kdt_gpio_irq_signal_handler: interrupt triggered!!!\n");
if (user_space_task) {
send_sig(SIGR, user_space_task, 0); // 유저 공간 태스크에 시그널 전송
}
return IRQ_HANDLED;
}
// 모듈 초기화 함수
static int __init kdt_driver_init(void) {
int ret;
// ... (디바이스 등록 코드 생략) ...
// kthread 생성
wait_thread = kthread_create(kdt_wait_kthread, NULL, "kdt_wait_thread");
if (wait_thread) {
pr_info("Thread created successfully\n");
wake_up_process(wait_thread); // 스레드 깨우기
} else {
pr_info("Thread creation failed\n");
return -ENOMEM;
}
// GPIO 핀을 IRQ 번호로 변환
button_irq = gpio_to_irq(KDT_GPIO_INPUT);
// 디바운스 설정 (채터링 방지)
gpio_set_debounce(KDT_GPIO_INPUT, 300);
// 인터럽트 등록
if (request_threaded_irq(button_irq, (irq_handler_t)kdt_gpio_irq_signal_handler, IRQF_TRIGGER_RISING, "kdt_gpio_irq_signal", NULL) != 0) {
pr_err("Error! Can not request interrupt nr: %d\n", button_irq);
gpio_free(KDT_GPIO_INPUT);
return -1;
}
return 0;
}
// 모듈 종료 함수
static void __exit kdt_driver_exit(void) {
// ... (디바이스 해제 코드 생략) ...
}
module_init(kdt_driver_init);
module_exit(kdt_driver_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#define MODULE_FILENAME "/dev/kdt_interrupt_driver" // 커널 모듈 디바이스 파일#define SIGTX 45 // 사용할 시그널 번호// 시그널 핸들러 함수
void signalhandler(int sig) {
printf("Button pressed!\n");
}
int main(int argc, char *argv[]) {
int dev;
char buff;
// 시그널 핸들러 등록
signal(SIGTX, signalhandler);
printf("PID: %d\n", getpid());
// 커널 모듈 디바이스 파일 열기
dev = open(MODULE_FILENAME, O_RDWR | O_NDELAY);
if (dev < 0) {
printf("module open error\n");
exit(1);
}
// 커널 모듈에 유저 공간 태스크 등록
if (read(dev, &buff, 1) < 0) {
printf("read error\n");
goto err;
}
printf("read data: %c\n", buff);
printf("Waiting for button press...\n");
while (1) {
sleep(1); // 1초 간격으로 대기
}
err:
close(dev);
return 0;
}
지금까지 Bottom Half의 개념과 Kthread + Wait Queue를 사용한 구현 방법 그리고 GPIO 인터럽트를 활용한 실전 예제를 통해 인터럽트 처리에 대해 알아보았습니다.