본문 바로가기
Linux BSP

[Linux Device Driver] Blocking I/O

by TYB 2024. 2. 28.
반응형

 

 

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

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

program-developers-story.tistory.com

이전 글에서의 문제점은 잠들지 않고 지속적으로 폴링을 하고 있는 프로세스 때문에 top을 쳤을 때 cpu 점유율이 100%라는 점인데, 프로세스를 sleep 상태로 만들어서 cpu 사용량을 0%로 만드는 것이 목표임.

 


이전글 코드 복사해서 그대로 사용하겠음.

 

 

 

키를 읽었는데, 키값이 0이다? => process를 sleep(block) 상태로 보냄.

키가 눌렸다면 ISR 호출하고 ISR이 리턴되기 전에 Read함수에서 재웠던 process를 깨워서 다시 키 값을 읽어서 return하면됨.

 

Nonblock이니까 open할 때 O_NDELAY 옵션을 주면 안됨.

 

자기가 할당한 시간은 다써야 그 다음 프로세스가 실행되는 RR방식임. 하지만 자신의 프로세스를 자신이 sleep 시키면 context switching이 일어남. 그래서 2번째 프로세스가 실행되는 거임. 근데 2번 프로세스가 실행 도중 인터럽트가 발생됨. 2번 프로세스가 종료되면 스케줄러가 ISR을 할당해줌.(우선순위가 높기 때문) 그렇게 ISR 처리하고 return된 후 그 다음에 예정되어 있던 프로세스 실행하는거

 

대기 큐 변수를 사용해야 하는데, 이를 위해 사용하는 함수와 변수들이 있음.

DECLARE_WAIT_QUEUE_HEAD에서 초기화도 같이 해줌.

프로세스를 재울 때 사용하는 함수

옛날에 쓰던게 이거고 요즘엔 바뀜.

프로세스를 깨울 때 사용하는 함수

 

 

Read에 대한 이벤트는 interrupt로 처리 write는 그닥..

 

 

dev.c 수정

 

yyp 한줄 복사 밑 아랫줄에 붙여넣기

cw 한단어 지우면서 입력모드

 

커서가 있는 위치에 블록킹 I/O 로직을 넣어야 겠죠? 읽었는데 key값이 0이니까?

키가 눌릴 때 까지 잠들어 있는거까지 구현했음. 이제 깨워줄 친구를 찾아줘야겠죠?

 

여기서 return하기 전에 wake up 해주면 되겠쥬?
뚝딱!


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)

 

 

app.c

ubuntu@ubuntu8:~/pi_bsp/drivers/p399_ledkey_blockio$ 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_NONBLOCK );
    dev = open( DEVICE_FILENAME, O_RDWR );
        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;
}

 

 

dev.c

ubuntu@ubuntu8:~/pi_bsp/drivers/p399_ledkey_blockio$ 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>
#include <linux/wait.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);

DECLARE_WAIT_QUEUE_HEAD(WaitQueue_Read);

static 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
        wake_up_interruptible(&WaitQueue_Read);
        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)
{

        int i;
        int result;
        char * irqName[8] = {"IrqKey0","IrqKey1","IrqKey2","IrqKey3","IrqKey4","IrqKey5","IrqKey6","IrqKey7"};
        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);
        }
}

static 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);
        if(!pKeyData)
                return -ENOMEM;
        pKeyData->keyNumber = 0;

#if DEBUG
    printk( "ledkey open -> major : %d\n", num0 );
    printk( "ledkey open -> minor : %d\n", num1 );
#endif
        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;
}

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

        if(pKeyData->keyNumber == 0)
        {
                if(!(filp->f_flags & O_NONBLOCK))
                {
                        wait_event_interruptible(WaitQueue_Read,pKeyData->keyNumber);
                }
        }

    put_user(pKeyData->keyNumber,buf);
        if(pKeyData->keyNumber)
                pKeyData->keyNumber = 0;
//  result = copy_to_user(buf, &kbuf, count);
    return count;
}

static 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)
{

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

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

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

static 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;
}

static 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");
MODULE_AUTHOR("KCCI-AIOT KSH");
MODULE_DESCRIPTION("led key test module");

ledkey_app 프로세스 상태보는거

pi@pi08:~ $ ps -auwx | grep ledkey_app

 

S+ 상태는 Sleep

 

 


Time-Out Interrupt

 

228             wait_event_interruptible(WaitQueue_Read,pKeyData->keyNumber); 라인의 내용을 아래의 내용으로 바꾸면 

wait_event_interruptible_timeout(WaitQueue_Read,pKeyData->keyNumber,100);     //100 * 1/HZ = 100 * 1/100 = 100 * 0.01 = 1Sec

 

sudo cat /proc/kmsg

1초마다 인터럽트가 발생해서 kernel message 출력되는걸 확인 가능

 

 


더 발전시켜봅싀당.

 

 

[Linux Device Driver] 입출력 다중화

[Linux Device Driver] 블록 IO ㅇ program-developers-story.tistory.com 이전 코드의 문제점은 led를 켜고 싶으나, 잠들어 있어서 값을 읽어오지 못하는 상황이 발생하는거임. 즉, led를 켜고 싶어도 키가 안눌리면

program-developers-story.tistory.com

 

반응형