[커널] interrupt

정재훈·2022년 5월 11일
0

Kernel

목록 보기
7/8

커널 헤더

app단에서 사용하는 헤더를 커널형으로 변환해서 사용할 수 있다.

#include <string.h> => #include <linux/string.h>
와 같이 변경해서 사용하면, app에서 사용한 라이버러리와 동일하게 사용가능하다.

모듈 파라미터

insmod를 통해 원하는 파라미터를 집어넣을 수 있다.
kernel.c

static char *whom = "world";
static int num = 1;	
module_param(num, int, S_IRUGO);	// 숫자형 변수에 파라미터를 받을 수 있다.
module_param(whom, charp, S_IRUGO);   // 문자형 변수에 파라미터를 받을 수 있다.

insmod kernel c 100 0 num=10 whom = "Jaehoon"으로 파라미터 받기!

GPIO 핀을 사용할때 메모리맵에 쉽게 접근하는 API

커널에서 사용 가능하며, GPIO에 쉽게 접근 가능한 API
헤더파일 : #include <linux/gpio.h>
사용 API :
gpio_direction_output(번호, 0)
gpio_set_value(번호, 0 또는 1)

Interrupt

app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>

void go_alert(int signum)
{
    for(int i=0; i<5; i++)
    {
        printf("WARNING!!\n");
    }

}

int main()
{
    signal(SIGIO, go_alert);

    printf("OPEN FILE!!\n");
    int fd = open("/dev/nobrand", O_RDWR);
    if (fd == -1) {
        printf("FILE OPEN ERROR!!!\n");
        close(fd);
        return 0;
    }

    int pid = getpid();
    ioctl(fd, _IO(0,4), pid);

    while(1)
    {
        ioctl(fd, _IO(0,3), 0);
        usleep(300 * 1000);
    }


    // ioctl(fd, _IO(0, 3), 0);
    // ioctl(fd, _IO(0, 4), 0);

    printf("CLOSE FILE!!\n");
    close(fd);

    return 0;
}

kernel.c

#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>

#define NOD_NAME "nobrand"
#define NOD_MAJOR 100

MODULE_LICENSE("GPL");

static uint32_t *BASE, *GPSEL2;
static uint32_t *GPIO_PUP_PDN_CNTRL_REG1;
static uint32_t *GPLEV0;

static int irq_num;
static int app_pid;

static struct kernel_siginfo sig_info;
static struct task_struct *task;

static int nobrand_open(struct inode *inode, struct file *filp) {
    printk( KERN_INFO "OPEN!!!\n");
    return 0;
}

static int nobrand_release(struct inode *inode, struct file *filp) {
    printk( KERN_INFO "CLOSE!!!\n");
    return 0;
}

static long nobrand_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int lev;
    switch (cmd)
    {
        case _IO(0, 3) :
            printk(KERN_INFO "IO 3\n");
            lev = (*GPLEV0 >> 21) & 0x1;
            printk( KERN_INFO "GPIO21 LEV = %d\n", lev);
            break;
        case _IO(0, 4) :
            printk(KERN_INFO "IO 4\n");
            app_pid = (int)arg;
            break;
    }

    printk( KERN_ALERT "%d\n", cmd);
    return 0;
}
static struct file_operations fops = {
    .open = nobrand_open,
    .release = nobrand_release,
    .unlocked_ioctl = nobrand_ioctl
};

static irqreturn_t lets_go(int irq, void *data)
{
    printk( KERN_ALERT "IT's ME\n");

    if(app_pid > 0)
    {
        sig_info.si_signo = SIGIO;
        task = pid_task(find_vpid(app_pid), PIDTYPE_PID); // task 변수에 pid 정보 집어넣기
        send_sig_info(SIGIO, &sig_info, task); // 신호를 app에 보낸다.
    }

    return IRQ_HANDLED;
}

static int nobrand_init(void) {

    int ret;

    BASE = (uint32_t *)ioremap(0xFE200000, 256);

    GPSEL2 = BASE + (0x08 / 4);
    *GPSEL2 = *GPSEL2 & ~(0x7 << 3);

    GPIO_PUP_PDN_CNTRL_REG1 = BASE + (0xe8 / 4);
    *GPIO_PUP_PDN_CNTRL_REG1 &= ~(0x3 << 10);
    *GPIO_PUP_PDN_CNTRL_REG1 |= (0x1 << 10);

    GPLEV0 = BASE + (0x34 / 4);

    irq_num = gpio_to_irq(21); // 21번의 interrupt 감시
    // 버튼을 땔 때 lets_go라는 함수 콜백한다.
    ret = request_irq(irq_num, lets_go, IRQF_TRIGGER_RISING, "interrupt", NULL);

    printk( KERN_INFO "OK HELLO NOBRAND!!\n");
    if (register_chrdev(NOD_MAJOR, NOD_NAME, &fops) < 0) {
        printk( KERN_INFO "ERROR!!! register error\n");
    }

    return 0;
}

static void nobrand_exit(void) {
    free_irq(irq_num, NULL);
    unregister_chrdev(NOD_MAJOR, NOD_NAME);
    printk( KERN_INFO "BYE BYE\n\n");
}

module_init(nobrand_init);
module_exit(nobrand_exit);
profile
여러 방향으로 접근하는 개발자

0개의 댓글