본문 바로가기
Linux BSP

[Linux Device Driver] 커널 메모리 동적 할당 및 irq매개변수를 통한 인터럽트 데이터 전달

by TYB 2024. 2. 27.
반응형
 

[Linux Device Driver] Interrupt 처리

실습 [Linux Device Driver] Device Driver와 application으로 led, 버튼 제어 폴더 새로 만들고 그 안에 c코드 추가 ubuntu@ubuntu8:~/pi_bsp/drivers/p238_ledkey$ vi ledkey_app.c 일단 application부터 만들어봅시다. system call 함

program-developers-story.tistory.com

이전 글 코드를 그대로 복사하겠슴다


두개 하겠소

1. kmalloc(kernel memory allocate)

2. irq 전역변수->매개변수를 넣어주기 위한 지역변수로 변경

 

 

ubuntu@ubuntu8:~/pi_bsp/drivers$ cp -r p369_ledkey_int/ p369_ledkey_int_kmalloc/

ubuntu@ubuntu8:~/pi_bsp/drivers/p369_ledkey_int_kmalloc$ vi ledkey_dev.c

 

irq에서 쓰였던 전역변수들을 지역변수로 변경 -> interrupt 함수의 매개변수를 사용

 

새로운 구조체를 만들어서 그 구조체를 매개변수로 넘겨주려고 한다.

 

같은 파일의 파일포인터를 매개변수로 받기 때문에 system call 함수간 공유해야할 데이터가 있다면, 파일 포인터의 어딘가에 데이터를 저장해 둔다면? 다 접근이 됨.

근데? insmod 할때 실행되는 init 과 rmmod 할때 실행되는 exit는 매개변수를 받지 않는다.

 

하지만 application이 호출하는 open과 close 함수에서 insmod와 rmmod에서 실행하는 내용을 수행한다면?

 

딱 되겠죠? b

 

ubuntu@ubuntu8:~/pi_bsp/kernel/linux/include/linux$ vi fs.h

private_data를 지원함.

 

그니까 이런 느낌으로 open할 때 key_data를 kernel memory에 동적할당 해주고, 동적할당 된 그 메모리의 주소를 filp->private_data에 넣어준다면 filp만 받으면 모두가 공유할 수 있는 데이터가 생긴거임.

 

일단  insmod rmmod할 때 호출되는 init과 exit모듈 내용을 open과 close에 넣어주자.

 

 

일단 복붙만 함. 매개변수 적용 시켜 보겠음,.

 

여기서 쓰이는 값을 쓰고 있는 곳은? 어딜까요?

 

호출할 때 주소 넘겨주는 매개변수 추가해야겠죠?

open함수의 irqinit 함수 매개변수 넣어주고

 

 

key_isr함수 수정해주시고, 원형은 냅둬야됨 건들면 안됨 이런건.

void pointer data에 값을 넣어줘야겠죠?

result = request_irq(pkeyData->key_irq[i],key_isr,IRQF_TRIGGER_RISING,irqName[i],NULL);

마지막 매개변수인 NULL에 값을 넣어줘야함.

key_isr과 공유하고 싶은 메모리 공간을 넘겨주면 되는거임.

 

 

다 이런식으로 해주고 make 해보면서 에러발생하면 디버깅해나가면 됨.

 

동작은 이전 글과 똑같아야함.

 

ubuntu@ubuntu8:~/pi_bsp/drivers/p369_ledkey_int_kmalloc$ cat Makefile
APP := ledkey_app
MOD := ledkey_dev
OBJ := $(APP).o
obj-m := $(MOD).o

CROSS = ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
CC := arm-linux-gnueabihf-gcc
KDIR := /home/ubuntu/pi_bsp/kernel/linux
PWD := $(shell pwd)

default:$(OBJ)
        $(MAKE) -C $(KDIR) M=$(PWD) modules $(CROSS)
        cp $(MOD).ko /srv/nfs
%.o:%.c
        $(CC) -o $(APP) $<
        cp $(APP) /srv/nfs
clean:
        rm -rf *.ko
        rm -rf *.mod.*
        rm -rf .*.cmd
        rm -rf *.o
        rm -rf modules.order
        rm -rf Module.symvers
        rm -rf $(MOD).mod
        rm -rf .tmp_versions
        rm -rf $(APP)

 

ubuntu@ubuntu8:~/pi_bsp/drivers/p369_ledkey_int_kmalloc$ cat ledkey_app.c

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

#define DEVICE_FILENAME  "/dev/ledkey_dev"

void print_OX(unsigned char);
int main(int argc,char * argv[])
{
    char buff = 0;
        char oldBuff = 0;
    int dev;
    int ret;
        unsigned long val;
        if(argc < 2)
        {
        printf("Usage : %s ledValue[0x00~0xff]\n",argv[0]);
                return 1;
        }
        val = strtoul(argv[1],0,16);
        if(val<0 || 0xff<val)
    {
        printf("Usage : %s ledValue[0x00~0xff]\n",argv[0]);
        return 2;
    }
        buff = val;
    dev = open( DEVICE_FILENAME, O_RDWR|O_NDELAY );
        if(dev<0)
        {
                perror("open()");
                return 2;
        }
    ret = write(dev,&buff,sizeof(buff));
        if(ret < 0)
        {
                perror("write()");
                return 3;
        }

        buff = 0;
        do {
                read(dev,&buff,sizeof(buff));
                if(oldBuff != buff)
                {
                        if(buff != 0)
                        {
                                printf("key : %d\n",buff);
                                print_OX(buff);
                        write(dev,&buff,sizeof(buff));
                                if(buff == 8) //key:8
                                        break;
                        }
                        oldBuff = buff;
                }
        } while(1);


    close(dev);
    return 0;
}
void print_OX(unsigned char led)
{
        int i;
        led = 1 << led-1;
        puts("1:2:3:4:5:6:7:8");
        for(i=0;i<=7;i++)
        {
                if(led & (0x01 << i))
                        putchar('O');
                else
                        putchar('X');
                if(i < 7 )
                        putchar(':');
                else
                        putchar('\n');
        }
        return;
}

 

 

 

ubuntu@ubuntu8:~/pi_bsp/drivers/p369_ledkey_int_kmalloc$ cat ledkey_dev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>

#define DEBUG 1
#define LEDKEY_DEV_NAME            "ledkey_dev"
#define LEDKEY_DEV_MAJOR            230

#define OFF 0
#define ON 1
#define GPIOLEDCNT 8
#define GPIOKEYCNT 8
static int gpioLed[GPIOLEDCNT] = {6,7,8,9,10,11,12,13};
static int gpioKey[GPIOKEYCNT] = {16,17,18,19,20,21,22,23};
typedef struct {
        int key_irq[8];
        int keyNumber;
}keyData;

static int gpioLedInit(void);
static void gpioLedSet(long);
static void gpioLedFree(void);
static int gpioKeyInit(void);
//static int gpioKeyGet(void);
static void gpioKeyFree(void);
static int gpioKeyIrqInit(keyData * pkeyData);
static void gpioKeyIrqFree(keyData *pkeyData);
irqreturn_t key_isr(int irq, void *data)
{
        int i;
        keyData * pkeyData = (keyData*)data;
        for(i=0;i<GPIOKEYCNT;i++)
        {
                if(irq == pkeyData->key_irq[i])
                {
                        pkeyData->keyNumber = i+1;
                        break;
                }
        }
#if DEBUG
        printk("key_isr() irq : %d, KeyNumber : %d\n",irq, pkeyData->keyNumber);
#endif
        return IRQ_HANDLED;
}
static int gpioLedInit(void)
{
        int i;
        int ret=0;
        char gpioName[10];
        for(i=0;i<GPIOLEDCNT;i++)
        {
                sprintf(gpioName,"led%d",i);
                ret = gpio_request(gpioLed[i],gpioName);
                if(ret < 0) {
                        printk("Failed gpio_request() gpio%d error \n",i);
                        return ret;
                }

                ret = gpio_direction_output(gpioLed[i],OFF);
                if(ret < 0) {
                        printk("Failed gpio_direction_output() gpio%d error \n",i);
                        return ret;
                }
        }
        return ret;
}

static void gpioLedSet(long val)
{
        int i;
        for(i=0;i<GPIOLEDCNT;i++)
        {
                gpio_set_value(gpioLed[i],(val>>i) & 0x1);
        }
}
static void gpioLedFree(void)
{
        int i;
        for(i=0;i<GPIOLEDCNT;i++)
        {
                gpio_free(gpioLed[i]);
        }
}
static int gpioKeyInit(void)
{
        int i;
        int ret=0;
        char gpioName[10];
        for(i=0;i<GPIOKEYCNT;i++)
        {
                sprintf(gpioName,"key%d",gpioKey[i]);
                ret = gpio_request(gpioKey[i], gpioName);
                if(ret < 0) {
                        printk("Failed Request gpio%d error\n", 6);
                        return ret;
                }
        }
        for(i=0;i<GPIOKEYCNT;i++)
        {
                ret = gpio_direction_input(gpioKey[i]);
                if(ret < 0) {
                        printk("Failed direction_output gpio%d error\n", 6);
                return ret;
                }
        }
        return ret;
}
/*
static int      gpioKeyGet(void)
{
        int i;
        int ret;
        int keyData=0;
        for(i=0;i<GPIOKEYCNT;i++)
        {
//              ret=gpio_get_value(gpioKey[i]) << i;
//              keyData |= ret;
                ret=gpio_get_value(gpioKey[i]);
                keyData = keyData | ( ret << i );
        }
        return keyData;
}
*/
static void gpioKeyFree(void)
{
        int i;
        for(i=0;i<GPIOKEYCNT;i++)
        {
                gpio_free(gpioKey[i]);
        }
}

static int gpioKeyIrqInit(keyData * pkeyData)
{
        char * irqName[8] = {"IrqKey0","IrqKey1","IrqKey2","IrqKey3","IrqKey4","IrqKey5","IrqKey6","IrqKey7"};
        int result;
        int i;
        for(i=0;i<GPIOKEYCNT;i++)
        {
                pkeyData->key_irq[i] = gpio_to_irq(gpioKey[i]);
                if(pkeyData->key_irq[i] < 0)
                {
                        printk("gpioKeyIrq() Failed gpio %d\n",gpioKey[i]);
                        return pkeyData->key_irq[i];
                }
        }
        for(i=0;i<GPIOKEYCNT;i++)
        {
                result = request_irq(pkeyData->key_irq[i],key_isr,IRQF_TRIGGER_RISING,irqName[i],pkeyData);
                if(result < 0)
                {
                        printk("request_irq() failed irq %d\n",pkeyData->key_irq[i]);
                        return result;
                }
        }
        return 0;
}

static void gpioKeyIrqFree(keyData *pkeyData)
{
        int i;
        for(i=0;i<GPIOKEYCNT;i++)
        {
                free_irq(pkeyData->key_irq[i],pkeyData);
        }
}

int ledkey_open (struct inode *inode, struct file *filp)
{
        int result;
    int num0 = MAJOR(inode->i_rdev);
    int num1 = MINOR(inode->i_rdev);
        keyData *pkeyData = (keyData *) kmalloc(sizeof(keyData),GFP_KERNEL); //int형 배열 8칸이랑 int 하나니깐 36byte
                //automatic으로 하면 메모리가 할당 안될 가능성은 없음. 하지만 36byte밖에 안되므로 GFP_KERNEL로도 가능
                //pkeyData는 동적할당 했으니까 힙영역의 주소임.
        if(!pkeyData)//pkeyData가 NULL이라면
                return -ENOMEM;//momory 부족 에러코드
        pkeyData->keyNumber = 0;//초기화 안하면 쓰레기값들어가 있어서 read함수에서 초기에 1번 오작동할 가능성이 매우 높다.
    printk( "ledkey open -> major : %d\n", num0 );
    printk( "ledkey open -> minor : %d\n", num1 );
        try_module_get(THIS_MODULE);


        result=gpioLedInit();
        if(result < 0)
                return result;
        result=gpioKeyInit();
        if(result < 0)
                return result;
        result = gpioKeyIrqInit(pkeyData);
        if(result < 0)
                return result;

        filp->private_data = pkeyData;//
    return 0;
}

ssize_t ledkey_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
        keyData *pkeyData = (keyData *)filp->private_data;
//      int result;
    char kbuf;
#if DEBUG
    printk( "ledkey read -> buf : %08X, count : %08X \n", (unsigned int)buf, count );
#endif
//    kbuf = (char)gpioKeyGet();
        kbuf = pkeyData->keyNumber;
    put_user(kbuf,buf);
        if(pkeyData->keyNumber)
                pkeyData->keyNumber = 0;
//    result = copy_to_user(buf, &kbuf, count);
    return count;
}

ssize_t ledkey_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
//      int i;
//      int result;
        char kbuff;
/*      char kbuff[10];
        for(i=0;i<count;i++)
                get_user(kbuff[i],buf++);
*/
/*      char kbuff[10];
        copy_from_user(kbuff,buf,count);
*/
#if DEBUG
    printk( "ledkey write -> buf : %08X, count : %08X \n", (unsigned int)buf, count );
#endif
        get_user(kbuff,buf);
//      result = copy_from_user(&kbuff,buf,count);
        gpioLedSet(kbuff);
    return count;
}

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

    printk( "ledkey ioctl -> cmd : %08X, arg : %08X \n", cmd, (unsigned int)arg );
    return 0x53;
}

int ledkey_release (struct inode *inode, struct file *filp)
{
        keyData *pkeyData = (keyData *)filp->private_data;
    printk( "ledkey release \n" );
        module_put(THIS_MODULE);
        gpioKeyIrqFree(pkeyData);
    gpioKeyFree();
        gpioLedFree();
        kfree(pkeyData);
    return 0;
}

struct file_operations ledkey_fops =
{
//    .owner    = THIS_MODULE,
    .open     = ledkey_open,
    .read     = ledkey_read,
    .write    = ledkey_write,
        .unlocked_ioctl = ledkey_ioctl,
    .release  = ledkey_release,
};

int ledkey_init(void)
{
    int result;

    printk( "ledkey ledkey_init \n" );

    result = register_chrdev( LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME, &ledkey_fops);
    if (result < 0) return result;

    return 0;
}

void ledkey_exit(void)
{
    printk( "ledkey ledkey_exit \n" );
    unregister_chrdev( LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME );
}

module_init(ledkey_init);
module_exit(ledkey_exit);

MODULE_LICENSE("Dual BSD/GPL");

 

ubuntu@ubuntu8:~/pi_bsp/drivers/p369_ledkey_int_kmalloc$ make

 

pi@pi08:/mnt/ubuntu_nfs $ sudo mknod /dev/ledkey_dev c 230 0

pi@pi08:/mnt/ubuntu_nfs $ sudo insmod ledkey_dev.ko

pi@pi08:/mnt/ubuntu_nfs $ sudo chmod 666 /dev/ledkey_dev

pi@pi08:/mnt/ubuntu_nfs $ ./ledkey_app 0x0f


작동은 이래 되야함.

 

 

근데 top을 살펴보니, cpu 사용량이 100%임.

 

block io를 활용해서 해결해야겠음.

 

 

[Linux Device Driver] 블록 IO

program-developers-story.tistory.com

 

반응형