240226

Yonggeun Park·2024년 2월 26일
0

지난 시간에는 1바이트만 보냈는데 이제 더 많은 데이터를 보내려 한다
8바이트를 보내보자


파일 이름만 수정한다

Makefile 수정

Pi에서 잘 되는 것 확인했다

이제 배열로 바꾼다
app.c 파일 수정한다

 11     char buff[8];

 34     for(i=0;i<8;i++)
 35     {
 36         buff[i] = (val >> i) & 0x01;
 37     }

dev.c 파일에서 1바이트때는 getuser 해도 됐지만, 여러개면 배열을 사용한다

(copy_from_user)
수정하면 아래와 같다

app.c이고

dev.c이다

근데 app.c에서 usleep(100000)을 빼면

cpu 점유율이 100이된다

주 번호와 부 번호 처리

작성된 예제를 분석하고 넘어가기로 함
make를 하고 다음과 같이 만든다

주번호를 조사하고 부번호를 조사해서 등록한다

같은 주번호의 open에서 부번호를 operation을 정의하고 open에서 부번호를 재정의

주번호가 256개밖에 안되기 때문에 부번호로 분류함

부번호를 정하는 용도

  • 디바이스 드라이버가 다루는 실제 디바이스의 구분
  • 용도에 따른 디바이스의 구분(misc 계열 디바이스)
  • 블록 디바이스의 파티션 구분

잡다한 주변장치가 주번호 10번에 있다
이 것을 misc라고 한다

디바이스 제어

지금부터 5챕터의 내용들을 응용해서 최종 과제를 할 것이다
첫 문제는 디바이스 드라이버의 타이머를 제어해야한다

vforl : video for linux
ioctl : io control
지금까지 open close read write를 했다

다양한 부가 기능을 read write로만으로는 구현할 수 없다
그래서 표준 시스템 콜 함수인 ioctl을 사용한다

fd :
request : command
arqp : 자료형이 필요할 때 사용하는 매개변수(가변인자)
inode값을 주지 않는다


호출은 위와 같이 한다

ioctl 함수의 특징

  • read(), write() 함수와 같이 쓰기와 읽기 처리가 기능하다
  • 하드웨어의 제어나 상태를 얻기 위해 사용된다
  • 응용 프로그램의 명령에 따라 디바이스 드라이버의 매개변수 해석이 달라진다


동작하는 개념

맨 위에 4가지 함수의 매크로가 있다

최상위 2비트에 쓰여져서 구분된다

  • _IO : 부가적인 데이터가 없는 명령을 만드는 매크로
  • _IOR : 디바이스 드라이버에서 데이터를 읽어오기(R) 위한 명령을 만드는 매크로
  • _IOW : 디바이스 드라이버에 데이터를 써넣기(W) 위한 명령을 만드는 매크로
  • _IOWR : 디바이스 드라이버에 읽고(R) 쓰기(W)를 수행하기 위한 명령을 만드는 매크로

자세한 내용은 p296~301을 참고할 것

p306을 mnt에서 복사해와서 실행해본다


잘 된다

사용 권한이 없을 때
1. sudo를 붙여서 사용
2. sudo chmod 를 이용해서 권한 부여하기


여기서 00003600이 출력 되었는데
.h 파일을 보면

6을 아스키코드로 하면 36이라 그렇다


만약 이거로 하면 00003604가 출력된다


이것은 맨 처음에 01
두번째에 84(8진수 132를 16진수로 바꾸면 1000 0100이므로)
세번째 36
네번째 05
그래서 80843605가 출력된다

매크로 함수는 전처리기된걸 컴파일하기 때문에 디버깅이 어렵다
매크로 상수값이 8 이상이면 MAXNR로 정의한다

  • _IOC_NR: 구분 번호 필드값을 읽는 매크로
  • _IOC_TYPE: 매직 번호 필드값을 읽는 매크로
  • _IOC_SIZE: 데이터의 크기 필드값을 읽는 매크로
  • _IOC_DIR: 읽기와 쓰기 속성 필드값을 읽는 매크로


내가 처리하려는 명령어가 맞는지 확인하는 부분
(아스키코드가 의도한대로 된건지 확인
8이 넘는지 확인)

커널 타이머 실습

기본 코드가 출력만 0x00 0xff가 반복적으로 토글되는 것이었다
이제는 ff에는 모든 불빛이 나오게 수정한다(완료)

과제
매개변수로 받아서 보수값으로 교차 토글하기
스위치를 누르면 해당 led 토글 및 보수값 교차 토글

app.c 파일

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

#define DEVICE_FILENAME  "/dev/kerneltimer"

void print_led(unsigned char);
void print_key(unsigned char);
int main(int argc,char * argv[])
{
    int dev;
    char buff = 0;
    char oldBuff = 0;
    int ret;
    if(argc < 2)
    {
        printf("USAGE : %s [ledval] \n",argv[0]);
        return 1;
    }
//  buff = atoi(argv[1]);
    buff = (char)strtoul(argv[1],0,16);

    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;
    }
    print_led(buff);

    buff = 0;
    do {
        read(dev,&buff,sizeof(buff));
//      buff = 1 << buff-1;
        if((buff != 0) && (oldBuff != buff))
        {
            printf("key : %#04x\n",buff);
//          print_key(buff);
            write(dev,&buff,sizeof(buff));
            print_led(buff);
            oldBuff = buff;
            if(buff == 0x80) //key:8
                break;
        }
    } while(1);


    close(dev);
    return 0;
}
void print_led(unsigned char led)
{
    int i;
    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;
}

void print_key(unsigned char key)
{
    int i;
    puts("1:2:3:4:5:6:7:8");
    for(i=0;i<=7;i++)
    {
        if(i+1 == key)
            putchar('O');
        else
            putchar('X');

        if(i < 7 )
            putchar(':');
        else
            putchar('\n');
    }
    return;
}

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/slab.h>
#include <asm/uaccess.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/gpio.h>
#include <linux/moduleparam.h>

#define DEBUG 1
#define GPIOLEDCNT 8
#define GPIOKEYCNT 8
#define OFF 0
#define ON 1

#define LEDKEY_DEV_NAME     "kerneltimer_dev"
#define LEDKEY_DEV_MAJOR        230

int gpioLed[GPIOLEDCNT] = {6,7,8,9,10,11,12,13};
int gpioKey[GPIOKEYCNT] = {16,17,18,19,20,21,22,23};

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 timerVal = 100;  // f=100Hz, T=1/100=10ms, 100*10ms=1Sec
module_param(timerVal, int, 0);
static int ledVal = 0;
module_param(ledVal, int, 0);
struct timer_list timerLed;

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 Request gpio%d error\n", 6);
            return ret;
        }
    }
    for(i=0;i<GPIOLEDCNT;i++)
    {
        ret = gpio_direction_output(gpioLed[i], OFF);
        if(ret < 0) {
            printk("Failed direction_output gpio%d error\n", 6);
         return ret;
        }
    }
    return ret;
}
void gpioLedSet(long val)
{
    int i;
    for(i=0;i<GPIOLEDCNT;i++)
    {
        gpio_set_value(gpioLed[i], (val>>i) & 0x01);
    }
}
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 ledkey_open(struct inode *inode, struct file *filp)
{
    int num0 = MAJOR(inode->i_rdev);
    int num1 = MINOR(inode->i_rdev);
    printk("ledkey open-> major : %d\n", num0);
    printk("ledkey open-> minor : %d\n", num1);
    try_module_get(THIS_MODULE);
    return 0;
}
static loff_t ledkey_llseek(struct file *filp, loff_t off, int whence)
{
    printk("ledkey llseek -> off : %08X, whence : %08X\n", (unsigned int)off, whence);
    return 0x23;
}
static ssize_t ledkey_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
    char kbuf;
//    printk("ledkey read -> buf : %08X, count : %08X \n", (unsigned int)buf, count);
    kbuf=(char)gpioKeyGet();
    put_user(kbuf, buf);
    return 0x33;
}
static ssize_t ledkey_write(struct file *filp, const char *buf, size_t count, loff_t *f_ops)
{
    char kbuff;
    get_user(kbuff, buf);
    ledVal = 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;
}
static int ledkey_release(struct inode *inode, struct file *filp)
{
    printk("ledkey release \n");
    module_put(THIS_MODULE);
    return 0;
}
struct file_operations ledkey_fops =
{
    .llseek = ledkey_llseek,
    .read = ledkey_read,
    .write = ledkey_write,
    .unlocked_ioctl = ledkey_ioctl,
    .open = ledkey_open,
    .release = ledkey_release,
};

void kerneltimer_func(struct timer_list *t);
void kerneltimer_registertimer(unsigned long timeover)
{
    timer_setup(&timerLed, kerneltimer_func, 0);
    timerLed.expires = get_jiffies_64() + timeover;  // 10ms * 100 = 1sec
    add_timer(&timerLed);
}
void kerneltimer_func(struct timer_list * t)
{
#if DEBUG
    printk("ledVal : %#04x\n", (unsigned int)(ledVal));
#endif
    ledVal = ~ledVal & 0xff;
    gpioLedSet(ledVal);
    mod_timer(t, get_jiffies_64() + timerVal);
}

int kerneltimer_init(void)
{
    int ret;
#if DEBUG
    printk("timerVal : %d, sec : %d \n", timerVal, timerVal/HZ);
#endif
    kerneltimer_registertimer(timerVal);

    ret = gpioLedInit();
    if(ret<0){ return ret; }
    ret = gpioKeyInit();
    if(ret<0){ return ret; }

    ret = register_chrdev(LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME, &ledkey_fops    );
    return ret;
}
void kerneltimer_exit(void)
{
    if(timer_pending(&timerLed))
        del_timer(&timerLed);
    unregister_chrdev(LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME);
    gpioLedFree();
    gpioKeyFree();
}
module_init(kerneltimer_init);
module_exit(kerneltimer_exit);
MODULE_AUTHOR("KCCI-AIOT PYG");
MODULE_DESCRIPTION("led key test module");
MODULE_LICENSE("Dual BSD/GPL");

수정내용
이렇게 read에 KeyGet 함수와 put_user로 키 값을 받아와서 쓰고
write 함수에 get_user로 해당 값을 가져와서 ledVal(전역변수)에 쓴다
그리고 kerneltimer_func에 해당 키값으로 Led를 키고
init에서 led와 key를 init한 뒤 register_chrdev를 이용한다
exit에서는 unregister_chrdev를 이용한 뒤 led와 key를 Free 해준다

profile
Dragon_muscle

0개의 댓글

관련 채용 정보