본문 바로가기
Linux BSP

[Linux Device Driver] 입출력 다중화

by TYB 2024. 2. 28.
반응형
 

[Linux Device Driver] 블록 IO

program-developers-story.tistory.com

이전 코드의 문제점은 led를 켜고 싶으나, 잠들어 있어서 값을 읽어오지 못하는 상황이 발생하는거임.

 

즉, led를 켜고 싶어도 키가 안눌리면 led를 못키는 상황임.

 

리눅스에서 여러 장치를 open해서 어떤 data가 먼저 들어올 지 아무도 모름. 그래서 입출력 다중화를 해주는 거임.

 

 

어떨때는 키보드 값, 어떨 때는 키 값이 들어올 수 있기 때문에, 첫번째 프로세스에서 영원히 잠들지 않도록 다중 입출력을 구현해주는거임.

 


 

 

app.c부터 살펴보자.


polling에 사용되는 구조체의 생김새는 저렇고

 

 

fflush는 키보드로부터 새 값을 받으려고 하는데, 실행 이전에 키보드 입력 버퍼에 들어있을지 모르는 쓰레기값을 청소해주는거임.

 

44번 라인 - 폴링 구조체 Events의 사이즈만큼 메모리를 할당하면서 0으로 초기화

45~48은 short로 되어 있는 것들에 값을 넣어주는 거고

폴링 구조체에 2칸을 준 이유는 0번이 표준 입력장치(키보드)고 1번이 키 입력을 받는 거(gpio)

입출력 장치의 갯수만큼 배열 사이즈를 잡아주면 되는거임.

.events = POLLIN이 들어간 이유는 입력에 대한 event를 detecting 하겠다는거.

POLLOUT이면 출력에 대한 event인거

 

51번 라인의 2000값은 timeout 시간이고 2초에 한번씩 깨어나는거임.

52번은 에러처리

57번은 타임아웃인거 아래 조건 체크 

 

62번에 들어가기 위해서는 키보드에 읽기 event가 발생해야함.

fgets로 keyStr의 크기인 80Byte에 데이터가 들어간거고

67번 - fgets는 \n이 붙어서 들어옴 그걸 \0로 바꿔주고

 

72번은 gpio key에 event가 발생해야되는거

 

 


device driver도 수정해줘야겠죠?

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

함수의 원형은 이렇게 찾는거

__poll_t 함수 원형 복사해서 붙여넣기

 


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)

 

 

ledkey_app.c

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

#define DEVICE_FILENAME "/dev/ledkey_dev"

int main(int argc, char *argv[])
{
        int dev;
        char buff;
        int ret;
        int num = 1;
        struct pollfd Events[2];
        char keyStr[80];

    if(argc != 2)
    {
        printf("Usage : %s [led_data(0x00~0xff)]\n",argv[0]);
        return 1;
    }
    buff = (char)strtoul(argv[1],NULL,16);
    if((buff < 0x00) || (0xff < buff))
    {
        printf("Usage : %s [led_data(0x00~0xff)]\n",argv[0]);
        return 2;
    }

//  dev = open(DEVICE_FILENAME, O_RDWR | O_NONBLOCK);
        dev = open(DEVICE_FILENAME, O_RDWR );
        if(dev < 0)
        {
                perror("open");
                return 2;
        }
        write(dev,&buff,sizeof(buff));

        fflush(stdin);
        memset( Events, 0, sizeof(Events));
        Events[0].fd = fileno(stdin);
        Events[0].events = POLLIN;
        Events[1].fd = dev;
        Events[1].events = POLLIN;
        while(1)
        {
                ret = poll(Events, 2, 2000);
                if(ret<0)
                {
                        perror("poll");
                        exit(1);
                }
                else if(ret==0)
                {
                        printf("poll time out : %d Sec\n",2*num++);
                        continue;
                }
                if(Events[0].revents & POLLIN)  //stdin
                {
                        fgets(keyStr,sizeof(keyStr),stdin);
                        if(keyStr[0] == 'q')
                                break;
                        keyStr[strlen(keyStr)-1] = '\0';  //'\n' clear
                        printf("STDIN : %s\n",keyStr);
                        buff = (char)atoi(keyStr);
                        write(dev,&buff,sizeof(buff));
                }
                else if(Events[1].revents & POLLIN) //ledkey
                {
                        ret = read(dev,&buff,sizeof(buff));
                        printf("key_no : %d\n",buff);
                        if(buff == 8)
                                break;
                        buff = 1 << buff-1;
                        write(dev,&buff,sizeof(buff));
                }
        }
        close(dev);
        return 0;
}

 

 

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>
#include <linux/poll.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);
                        wait_event_interruptible_timeout(WaitQueue_Read,pKeyData->keyNumber,100);     //100 * 1/HZ = 100 * 1/100 = 100 * 0.01 = 1Sec

                }
        }

    put_user(pKeyData->keyNumber,buf);
//  result = copy_to_user(buf, &(pKeyData->keyNumber), count);
        if(pKeyData->keyNumber)
                pKeyData->keyNumber = 0;
    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 __poll_t ledkey_poll (struct file *filp, struct poll_table_struct *wait)
{
        unsigned int mask =0;
        keyData *pkeyData = (keyData*)filp->private_data;
#ifdef DEBUG
        printk("_key : %u\n",(wait->_key & POLLIN));
#endif
        if(wait->_key & POLLIN)
                poll_wait(filp, &WaitQueue_Read, wait);
        if(pkeyData->keyNumber >0)//2초가 안되더라도 눌렸다면 poll in을 넣어줘서 깨어나게 만듬.
                mask= POLLIN;
        return mask;
}

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,
        .poll     = ledkey_poll,
        .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");

 

 

 


 

반응형