드라이버 동작 형태

1. IOCTL 형태로 동작되며 사용자가 시간을 입력한다. (application 실행)

2. 드라이버는 해당 명령어를 받아서 유저 영역의 시간 데이터를 커널 영역으로 복사

3. 해당 GPIO 를 SET(high level) 시키고

4. 타이머를 기동 시킨다.

5. 지정된 시간 이후에 타이머 등록시 지정한 콜백함수를 호출하여 해당 GPIO를 Reset (LOW)을 시킨다.


ODROID XU4/XU3 의 핀 중에서 15번핀을 출력으로 사용한다.



하기 경로의 레지스터 맵을 참조 하여 해당 GPIO의 Base Address를 알아냄

http://odroid.com/dokuwiki/doku.php?id=en:xu3_gpio_register



1.  커널 타이머를 이용한 GPIO 타이밍 제어


정밀제어가 불가함

아무래도 스케쥴링 에 따른 시간차로 인해 발생되는 문제로 보이며 하기 사항으로 인하여 발생되는 현상 같다.

“ 커널타이머는 프로세스 스케줄러를 동작시키기 위해 발생되는 타이머 인터럽트를 통해 구현되는데 커널 타이머 인터럽트가 발생되면 스케쥴링에 대한 처리후 커널타이머 목록의 데이터를 검사하게 된다 “[출처 : 리눅스 디바이스 드라이버 – 유영창]


2. hrtimer 이용한 GPIO 타이밍 제어

hrtimer는 고분해능 타이머 기능으로 나노초 단위로 동작된다.

hrtimer 구조체 선언후  초기화 시킨뒤에 (구조체 등록,클록정의,타이머모드설정) 콜백함수를 등록한다.

이후에 타이머를 스타트 시키면 hrtimrt가 기동 된다.

hrtimer 는 hardkernel의 ioboard-keyled.c 파일에도 작성 되어있었으며 예제 구현은 하기의 경로를 참조 하였다.

http://xenostudy.tistory.com/306

http://lxr.free-electrons.com/source/include/linux/hrtimer.h#L33

결과적으로 msec로 제어 결과 hrtimer는 정확하게 제어 되었다.




드라이버 코드와 어플리케이션 공통 헤더파일은 하기와 같다.

코드상에는 커널 타이머 관련된 내용이 혼재 되어 있으나 실질적 IOCTL 함수 내부에서 사용하지 않고 있다(주석처리).

#mknod /dev/gpio_ioctl c 240 0

#insmod gpio_ioctl.ko

#lsmod (드라이버 적재 상태 확인)


하기 부분은 해당 파일을 XU4로 옮겨서 XU4에서 직접 컴파일

#gcc –o app_gpio_ioctl app_gpio_ioctl.c

#./app_gpio_ioctl

실행하여 동작 확인


4번눌러서 원하는 타이밍 입력후(msec 단위) 엔터 치면 해당 시간만큼 포트 출력됨.




#ifndef _GPIO_IOCTL_H_ 
#define _GPIO_IOCTL_H_

#define GPIO_IOCTL_MAGIC        't'

typedef struct 
{ 
        unsigned int msec; 
}__attribute__((packed)) gpio_parameter;

#define GPIO_LED_OFF        _IO(GPIO_IOCTL_MAGIC,0) 
#define GPIO_LED_ON        _IO(GPIO_IOCTL_MAGIC,1) 
#define GPIO_ON_OFF        _IOW(GPIO_IOCTL_MAGIC,2,gpio_parameter) 
#define GPIO_TIME_CTRL    _IOW(GPIO_IOCTL_MAGIC,3,gpio_parameter)

#define GPIO_IOCTL_MAXNR        4



#endif



#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include 

#include  
#include 

#include  
#include 

#include "gpio_ioctl.h"

#define GPIO_MMAP_DEV_NAME        "gpio_ioctl"

#define GPIO_DEV_MAJOR        240


//hardware gpio memory map define 
#define GPX1_BASE            0x13400000 
#define GPX1_CONFIG        (*(volatile unsigned int *)(0x13400000+(0x0c20 >> 2))) 
#define GPX1_2_SETTING    (*(volatile unsigned int *)(0x13400000+(0x0c24 >> 2)))

// 
typedef struct 
{ 
    struct timer_list timer; 
    unsigned long data ; 
}__attribute__((packed)) KERNEL_TIMER_MANAGER;

static KERNEL_TIMER_MANAGER *ptrmng = NULL;

static struct hrtimer         test_hrtimer;

volatile unsigned int *virt_gpio_addr;

// function

void kerneltimer_expire(unsigned long arg); 
void kerneltimer_timeover(unsigned long arg); 
static enum hrtimer_restart gpio_hrtimer_callback(struct hrtimer *timer);

//////////////////////////////////////////////////////// 
//      Device Driver Open 
//////////////////////////////////////////////////////// 
static int gpio_open(struct inode *inode, struct file *filp) 
{ 
        //kerner timer initialize 
        ptrmng = kmalloc(sizeof(KERNEL_TIMER_MANAGER),GFP_KERNEL); 
        if(ptrmng == NULL) return -ENOMEM;

        memset(ptrmng,0, sizeof(KERNEL_TIMER_MANAGER)); 
        ptrmng->data = 0;

        //hrtimer initialize 
        hrtimer_init(&test_hrtimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL); 
        test_hrtimer.function = gpio_hrtimer_callback; 
    
        //gpio initialize 
        //가상 주소를 획득한다. 물리적 주소 접근시 segmentaion fault 발생 
        //레지스터 address 참조 http://odroid.com/dokuwiki/doku.php?id=en:xu3_gpio_register 
        virt_gpio_addr = ioremap(GPX1_BASE,0x10000); 
        if (virt_gpio_addr != NULL) 
        { 
            printk("virture adress mapping success@\r\n"); 
        } 
        *(virt_gpio_addr + (0x0c20 >> 2)) |= (0x1 << 8);

        printk("gpio open & GPX1 Configration Complete!\n"); 
        return 0; 
}

//////////////////////////////////////////////////////// 
//      Device Driver Release 
////////////////////////////////////////////////////////

static int gpio_released(struct inode *inode, struct file *filp) 
{ 
        //gpio shutdown 
        return 0; 
}

//////////////////////////////////////////////////////// 
//      Device Driver kernel timer register 
//////////////////////////////////////////////////////// 
void kerneltimer_registertimer(KERNEL_TIMER_MANAGER *pdata, unsigned long timeover) 
{ 
    init_timer(&(pdata->timer)); 
    pdata->timer.expires  = get_jiffies_64() + timeover; 
    pdata->timer.data     = (unsigned long) pdata; 
    pdata->timer.function = kerneltimer_timeover; 
    add_timer(&(pdata->timer)); 
}

//////////////////////////////////////////////////////// 
//      Device Driver IOCTL Functioin 
//////////////////////////////////////////////////////// 
static long gpio_ioctl(struct file *filp,unsigned int cmd, unsigned long arg) 
{ 
    gpio_parameter gpio_info; 
    int err, size; 
    long ret;


    if(_IOC_TYPE(cmd) != GPIO_IOCTL_MAGIC ) return -EINVAL; 
    if(_IOC_NR(cmd)    >= GPIO_IOCTL_MAXNR ) return -EINVAL;

    size = _IOC_SIZE(cmd); 
    printk("%d_IOC_SIZE\r\n",size); 
    
    if(size) 
    { 
        err = 0; 
        if (_IOC_DIR(cmd) & _IOC_READ) 
        { 
            err = !access_ok(VERIFY_WRITE, (void __user *)arg,size); 
            printk("IOC_READ\r\n"); 
        } 
        else if (_IOC_DIR(cmd) & _IOC_WRITE) 
        { 
            err = !access_ok(VERIFY_READ,(void __user *)arg,size); 
            printk("IOC_WRITE\r\n %d :err_number\r\n",err); 
        }

        if (err) return err; 
    } 
    
    switch (cmd) 
    { 
        case GPIO_LED_ON: 
        *(virt_gpio_addr + (0x0c24 >> 2)) |= (1 << 2); 
        printk("GPIO LED ON\n"); 
        break;

        case GPIO_LED_OFF: 
        *(virt_gpio_addr + (0x0c24 >> 2)) &= ~(1 << 2); 
        printk("GPIO LED OFF\n"); 
        break;

        case GPIO_ON_OFF: 
        *(virt_gpio_addr + (0x0c24 >> 2)) |= (1 << 2); 
        printk("GPIO LED ON OFF\n"); 
        kerneltimer_registertimer(ptrmng,gpio_info.msec); 
        break;

        case GPIO_TIME_CTRL: 
        printk("GPIO TIME_CTRL\n"); 
        ret = copy_from_user((void *)&gpio_info,(const void *)arg,size); //user appllication 에서 data 가져옴 
        //kerneltimer_registertimer(ptrmng,gpio_info.msec); 
        hrtimer_start(&test_hrtimer,ktime_set(0,gpio_info.msec*1000000),HRTIMER_MODE_REL); 
        gpio_info.msec = 0; 
        *(virt_gpio_addr + (0x0c24 >> 2)) |= (1 << 2); 
        printk("ioctl timer %d\r\n",(unsigned int )gpio_info.msec); 
        break;

        default : 
        break; 
        
    } 
    return 0; 
}

//////////////////////////////////////////////////////// 
//      Device Driver filr operation fild 
//////////////////////////////////////////////////////// 
static struct file_operations gpioioctl_fops = 
{ 
        .owner = THIS_MODULE, 
        .unlocked_ioctl = gpio_ioctl, 
        .open = gpio_open, 
        .release = gpio_released,

};

//////////////////////////////////////////////////////// 
//        Device Driver hrtimer expire excute 
/////////////////////////////////////////////////////// 
static enum hrtimer_restart gpio_hrtimer_callback(struct hrtimer *timer) 
{   
    //0으로 포트 lowlevel 변경 
    *(virt_gpio_addr + (0x0c24 >> 2)) &= ~(1 << 2);

    return HRTIMER_NORESTART; 
}

//////////////////////////////////////////////////////// 
//      Device Driver kernel timer expire excute 
//////////////////////////////////////////////////////// 
void kerneltimer_timeover(unsigned long arg) 
{ 
    KERNEL_TIMER_MANAGER *pdata = NULL;

    pdata = (KERNEL_TIMER_MANAGER *) arg;

    //0으로 포트 초기화 
    *(virt_gpio_addr + (0x0c24 >> 2)) &= ~(1 << 2);

}



//////////////////////////////////////////////////////// 
//      Device Driver init & exit 
//////////////////////////////////////////////////////// 
static void gpio_ioctl_exit(void) 
{ 
    int ret; 
    if (ptrmng != NULL) 
    { 
        del_timer(&(ptrmng->timer)); 
        kfree(ptrmng); 
    }

    //hrtimer 해제 
    ret = hrtimer_cancel(&test_hrtimer);

    unregister_chrdev(GPIO_DEV_MAJOR,GPIO_MMAP_DEV_NAME);

}

static int gpio_ioctl_init(void) 
{

    int result;

    result = register_chrdev(GPIO_DEV_MAJOR,GPIO_MMAP_DEV_NAME,&gpioioctl_fops);

    if (result < 0 ) 
    { 
        printk(" drv register fail\r\n"); 
        return result; 
    } 
    else 
    { 
        printk(" drv register success\r\n"); 
    } 
    
    return 0; 
    }

module_init(gpio_ioctl_init); 
module_exit(gpio_ioctl_exit);

MODULE_DESCRIPTION("GPIO Control Driver for ODROID_BOARDXU3/4"); 
MODULE_AUTHOR("S.Jung/sjung8432@pnstech.ner/www.pnstech.co.kr"); 
MODULE_LICENSE("Dual BSD/GPL");

application Code 는 하기와 같다.

#include 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include 

#include  
#include 

#include  
#include 

#include "gpio_ioctl.h"

#define GPIO_MMAP_DEV_NAME        "gpio_ioctl"

#define GPIO_DEV_MAJOR        240


//hardware gpio memory map define 
#define GPX1_BASE            0x13400000 
#define GPX1_CONFIG        (*(volatile unsigned int *)(0x13400000+(0x0c20 >> 2))) 
#define GPX1_2_SETTING    (*(volatile unsigned int *)(0x13400000+(0x0c24 >> 2)))

// 
typedef struct 
{ 
    struct timer_list timer; 
    unsigned long data ; 
}__attribute__((packed)) KERNEL_TIMER_MANAGER;

static KERNEL_TIMER_MANAGER *ptrmng = NULL;

static struct hrtimer         test_hrtimer;

volatile unsigned int *virt_gpio_addr;

// function

void kerneltimer_expire(unsigned long arg); 
void kerneltimer_timeover(unsigned long arg); 
static enum hrtimer_restart gpio_hrtimer_callback(struct hrtimer *timer);

//////////////////////////////////////////////////////// 
//      Device Driver Open 
//////////////////////////////////////////////////////// 
static int gpio_open(struct inode *inode, struct file *filp) 
{ 
        //kerner timer initialize 
        ptrmng = kmalloc(sizeof(KERNEL_TIMER_MANAGER),GFP_KERNEL); 
        if(ptrmng == NULL) return -ENOMEM;

        memset(ptrmng,0, sizeof(KERNEL_TIMER_MANAGER)); 
        ptrmng->data = 0;

        //hrtimer initialize 
        hrtimer_init(&test_hrtimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL); 
        test_hrtimer.function = gpio_hrtimer_callback; 
    
        //gpio initialize 
        //가상 주소를 획득한다. 물리적 주소 접근시 segmentaion fault 발생 
        //레지스터 address 참조 http://odroid.com/dokuwiki/doku.php?id=en:xu3_gpio_register 
        virt_gpio_addr = ioremap(GPX1_BASE,0x10000); 
        if (virt_gpio_addr != NULL) 
        { 
            printk("virture adress mapping success@\r\n"); 
        } 
        *(virt_gpio_addr + (0x0c20 >> 2)) |= (0x1 << 8);

        printk("gpio open & GPX1 Configration Complete!\n"); 
        return 0; 
}

//////////////////////////////////////////////////////// 
//      Device Driver Release 
////////////////////////////////////////////////////////

static int gpio_released(struct inode *inode, struct file *filp) 
{ 
        //gpio shutdown 
        return 0; 
}

//////////////////////////////////////////////////////// 
//      Device Driver kernel timer register 
//////////////////////////////////////////////////////// 
void kerneltimer_registertimer(KERNEL_TIMER_MANAGER *pdata, unsigned long timeover) 
{ 
    init_timer(&(pdata->timer)); 
    pdata->timer.expires  = get_jiffies_64() + timeover; 
    pdata->timer.data     = (unsigned long) pdata; 
    pdata->timer.function = kerneltimer_timeover; 
    add_timer(&(pdata->timer)); 
}

//////////////////////////////////////////////////////// 
//      Device Driver IOCTL Functioin 
//////////////////////////////////////////////////////// 
static long gpio_ioctl(struct file *filp,unsigned int cmd, unsigned long arg) 
{ 
    gpio_parameter gpio_info; 
    int err, size; 
    long ret;


    if(_IOC_TYPE(cmd) != GPIO_IOCTL_MAGIC ) return -EINVAL; 
    if(_IOC_NR(cmd)    >= GPIO_IOCTL_MAXNR ) return -EINVAL;

    size = _IOC_SIZE(cmd); 
    printk("%d_IOC_SIZE\r\n",size); 
    
    if(size) 
    { 
        err = 0; 
        if (_IOC_DIR(cmd) & _IOC_READ) 
        { 
            err = !access_ok(VERIFY_WRITE, (void __user *)arg,size); 
            printk("IOC_READ\r\n"); 
        } 
        else if (_IOC_DIR(cmd) & _IOC_WRITE) 
        { 
            err = !access_ok(VERIFY_READ,(void __user *)arg,size); 
            printk("IOC_WRITE\r\n %d :err_number\r\n",err); 
        }

        if (err) return err; 
    } 
    
    switch (cmd) 
    { 
        case GPIO_LED_ON: 
        *(virt_gpio_addr + (0x0c24 >> 2)) |= (1 << 2); 
        printk("GPIO LED ON\n"); 
        break;

        case GPIO_LED_OFF: 
        *(virt_gpio_addr + (0x0c24 >> 2)) &= ~(1 << 2); 
        printk("GPIO LED OFF\n"); 
        break;

        case GPIO_ON_OFF: 
        *(virt_gpio_addr + (0x0c24 >> 2)) |= (1 << 2); 
        printk("GPIO LED ON OFF\n"); 
        kerneltimer_registertimer(ptrmng,gpio_info.msec); 
        break;

        case GPIO_TIME_CTRL: 
        printk("GPIO TIME_CTRL\n"); 
        ret = copy_from_user((void *)&gpio_info,(const void *)arg,size); //user appllication 에서 data 가져옴 
        //kerneltimer_registertimer(ptrmng,gpio_info.msec); 
        hrtimer_start(&test_hrtimer,ktime_set(0,gpio_info.msec*1000000),HRTIMER_MODE_REL); 
        gpio_info.msec = 0; 
        *(virt_gpio_addr + (0x0c24 >> 2)) |= (1 << 2); 
        printk("ioctl timer %d\r\n",(unsigned int )gpio_info.msec); 
        break;

        default : 
        break; 
        
    } 
    return 0; 
}

//////////////////////////////////////////////////////// 
//      Device Driver filr operation fild 
//////////////////////////////////////////////////////// 
static struct file_operations gpioioctl_fops = 
{ 
        .owner = THIS_MODULE, 
        .unlocked_ioctl = gpio_ioctl, 
        .open = gpio_open, 
        .release = gpio_released,

};

//////////////////////////////////////////////////////// 
//        Device Driver hrtimer expire excute 
/////////////////////////////////////////////////////// 
static enum hrtimer_restart gpio_hrtimer_callback(struct hrtimer *timer) 
{   
    //0으로 포트 lowlevel 변경 
    *(virt_gpio_addr + (0x0c24 >> 2)) &= ~(1 << 2);

    return HRTIMER_NORESTART; 
}

//////////////////////////////////////////////////////// 
//      Device Driver kernel timer expire excute 
//////////////////////////////////////////////////////// 
void kerneltimer_timeover(unsigned long arg) 
{ 
    KERNEL_TIMER_MANAGER *pdata = NULL;

    pdata = (KERNEL_TIMER_MANAGER *) arg;

    //0으로 포트 초기화 
    *(virt_gpio_addr + (0x0c24 >> 2)) &= ~(1 << 2);

}



//////////////////////////////////////////////////////// 
//      Device Driver init & exit 
//////////////////////////////////////////////////////// 
static void gpio_ioctl_exit(void) 
{ 
    int ret; 
    if (ptrmng != NULL) 
    { 
        del_timer(&(ptrmng->timer)); 
        kfree(ptrmng); 
    }

    //hrtimer 해제 
    ret = hrtimer_cancel(&test_hrtimer);

    unregister_chrdev(GPIO_DEV_MAJOR,GPIO_MMAP_DEV_NAME);

}

static int gpio_ioctl_init(void) 
{

    int result;

    result = register_chrdev(GPIO_DEV_MAJOR,GPIO_MMAP_DEV_NAME,&gpioioctl_fops);

    if (result < 0 ) 
    { 
        printk(" drv register fail\r\n"); 
        return result; 
    } 
    else 
    { 
        printk(" drv register success\r\n"); 
    } 
    
    return 0; 
    }

module_init(gpio_ioctl_init); 
module_exit(gpio_ioctl_exit);

MODULE_DESCRIPTION("GPIO Control Driver for ODROID_BOARDXU3/4"); 
MODULE_AUTHOR("S.Jung/sjung8432@pnstech.ner/www.pnstech.co.kr"); 
MODULE_LICENSE("Dual BSD/GPL");

크리에이티브 커먼즈 라이센스
Creative Commons License
2015/12/11 13:50 2015/12/11 13:50

댓글을 달아 주세요

  1. 이은영 2018/08/13 02:30  address  modify / delete  reply

    얼마나 걸릴까요?

  2. 솔루션 2018/08/13 18:51  address  modify / delete  reply

    스카이프 pvaltrade 텔레그램@pvalrade

Device Driver를 작성하고 나서 커널에 함께 넣어서 컴파일하면 시간도 오래걸리고 하니까.

작성한 파일만 컴파일 하여 모듈타입으로 적재 시키는 방법이다.

Device Driver를 컴파일 하기 위해서 커널의 경로를 지정해준다 (KDIR부분)

크로스 컴파일 이기 때문에 CC에 크로스 컴파일러를 지정 해줘야 하며

Target 커널 컴파일 시에 적용했던 Cross compiler 와 동일한 버전을 사용하는게 안정적이다.

obj-m 은 C파일의 이름과 동일하게 지정 하여 주며 확장자는 .o 로 설정 한다.

 

컴파일 한뒤에

# file 드라이버명.ko

하면 파일 속성에서 ARM 용으로 컴파일 된걸 볼수 있다.

 

Make 파일 내역은 하기와 같다.

 

 

CC := arm-eabi-gcc

 

obj-m    := ioboard-keyled.o

 

 

KDIR    := /home/ippuu/develop/odroidxu3-3.10.y

 

PWD    := $(shell pwd)

 

default:

#    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

    make -C $(KDIR) SUBDIRS=$(PWD) modules

 

clean:

    rm -rf *.ko

    rm -rf *.mod.*

    rm -rf .*.cmd

    rm -rf *.o

 

크리에이티브 커먼즈 라이센스
Creative Commons License
2015/12/08 18:12 2015/12/08 18:12

댓글을 달아 주세요


ODROID XU3에는 30PIN 외부 IO 단자 커넥터가 달려 있다.

















ODROID XU4에는 추가적으로 12핀 커넥터가 달려있다.


사용자 삽입 이미지



























30핀과 12핀의 Pin description이다.


CON10 - 2×15 pins

Pin NumberNet NameGPIO & Export NoPin NumberNet NameGPIO & Export No
1 5V0   2 GND  
3 ADC_0.AIN0 XADC0AIN_0 4 UART_0.RTSN GPA0.2 (#173)
5 UART_0.CTSN GPA0.3 (#174) 6 UART_0.RXD GPA0.0 (#171)
7 SPI_1.MOISI GPA2.7 (#192) 8 UART_0.TXD GPA0.1 (#172)
9 SPI_1.MISO GPA2.6 (#191) 10 SPI_1.CLK GPA2.4 (#189)
11 SPI_1.CSN GPA2.5 (#190) 12 PWRON  
13 XE.INT13 GPX1.5 (#21) 14 I2C_1.SCL GPB3.3 (#210)
15 XE.INT10 GPX1.2 (#18) 16 I2_C1.SDA GPB3.2 (#209)
17 XE.INT14 GPX1.6 (#22) 18 XE.INT11 GPX1.3 (#19)
19 XE.INT22 GPX2.6 (#30) 20 XE.INT20 GPX2.4 (#28)
21 XE.INT21 GPX2.5 (#29) 22 XE.INT23 GPX2.7 (#31)
23 ADC_0.AIN1 XADC0AIN_1 24 XE.INT17 GPX2.1 (#25)
25 XE.INT15 GPX1.7 (Soft I2C) 26 XE.INT16 GPX2.0 (#24)
27 XE.INT25 GPX3.1 (Soft I2C) 28 GND  
29 VDD_IO(1.8V)   30 GND  

12 Pin Expansion Connectors

The Odroid-xu4 povides one 12-pin dual row expansion header “CON11”. The location and pinout of these connectors is illustrated blew. All signals on expansion headers are 1.8V.


CON11 - 2×6 pins

Pin NumberNet NameGPIO & Export NoPin NumberNet NameGPIO & Export No
1 P5V0   2 GND  
3 VDD_IO(1.8V)   4 I2C_5.SDA GPA2.2 (#187)
5 XE.INT26 GPX3.2 (#34) 6 I2C_5.SCL GPA2.3 (#188)
7 I2S_0.SCLK GPZ.0 (#225) 8 GND  
9 I2S_0.CDCLK GPZ.1 (#226) 10 I2S_0.SDO GPZ.4 (#229)
11 I2S_0.LRCK GPZ.2 (#227) 12 I2S_0.SDI GPZ.3 (#228)

 

일단 드라이버를 커널에 포함시키지 않고 모듈 타입으로 컴파일 한다.

커널에 드라이버를 포함시키면 (커널에서 해당 디바이스 드라이버 커널적제시 *로 표기 / 모듈타입은 M으로 표기)
lsmod를 하면 나타나지 않는다.


make odroidxu3_defconfig 한뒤에

make menuconfig 를 실행한다.

Device driver -> ODROID Specific Hardware -> 아래 그림과 같이 설정


사용자 삽입 이미지

























make -j8

여기서 8은 host PC의 코어(하이퍼 스레딩 포함) 개수 *1.2 정도 해준다.

컴파일한 뒤에 하기와 같이 생성된 디바이스 트리 파일 dts 랑 zImage 를 넣어준다.

주의사항은 경로의 dev/sdb1는 사용자 PC에서 XU 보드용으로 장착한 SD카드 또는 emmc의 경로이다.

파티션을 명확히 볼려면 GParted 라는 프로그램을 설치 확인 하던지  

"df -h -T" 명령어를 이용하여 확인한다.(쿤따옴표 빼고 -_-;)

//install zImage DTB File

mkdir -p mount
sudo mount /dev/sdb1 /mount

sudo cp arch/arm/boot/zImage arch/arm/boot/dts/exynos5422-odroidxu3.dtb /media/ippuu/BOOT && sync && sudo umount /mount


sudo mount /dev/sdb2 /mount

sudo make modules_install ARCH=arm INSTALL_MOD_PATH=/mount && sync && sudo umount /mount


SD카드를 빼고 XU 보드에 장착하여 부팅한다.

부팅이 되면 lsmod 를 이용하여 "ioboard-keyled" 드라이버가 리스트에 있는지 확인한다.

당연히 드라이버가 없으면

modprobe ioboard-keyled 를 세게 친다.

다시 lsmod 를 이용하여 확인 하면 하기 그림과 같다



사용자 삽입 이미지







디바이스가 적재 되었다.

dmesg를 이용하여 확인 할수도 있다. (해당부분은  ioboard가 들어간 부분만 grep 하여 보았음)


사용자 삽입 이미지






암튼 확인 하고 나면 실제 드라이버 파일의 경로 하고
디바이스가 적재 되었을때 파일이 어디에 생성되는지 는 하기 그림의 경로와 같다.

사용자 삽입 이미지



사용자 삽입 이미지






드라이버 ko파일은 /lib/modules/사용자의 커널버전/drivers/hardkernel/경로에 있고


디바이스 파일은 /sys/device/platform/ioboard-keyled/ 경로에 있다.

드라이버 적재한뒤에야 디바이스를 제어하는 파일이 생성된다. (sysfs)

당연히 insmod 로 ioboard-keyled가 적재되지 않았으면 해당 경로 조차 없다.



led 제어는 sudo 로 진행 한다.


사용자 삽입 이미지







사용자 삽입 이미지







사용자 삽입 이미지









사용자 삽입 이미지










추가적으로 Hardkernel 에서 제공하는 코드는 hrtimer가 있어서 포트가 변화 되었다가 초기값으로 다시 돌아오는데(High->low->high)
dmm으로 측정시에는 시그널의 변화가 없는 것처럼 보이므로 꼭 스코프로 찍어 봐야함...12시간 삽질 ...ㄷㄷㄷ

hrtimer기능을 주석 처리 하였으며 XU3의 30핀만 구현되어있던 것에서 XU4의 12핀 중에서 5번핀을 추가적으로 사용하도록 코드에 추가 하였다 ("led6")부분










//[*]--------------------------------------------------------------------------------------------------[*]
//
//
// 
//  ODROID IOBOARD Board : IOBOARD KEY and LED driver (charles.park)
//  2013.08.28
// 
//
//[*]--------------------------------------------------------------------------------------------------[*]
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/hrtimer.h>
#include <linux/slab.h>

#include <mach/gpio.h> #include <mach/regs-gpio.h> #include <mach/regs-pmu.h> #include <plat/gpio-cfg.h>

//[*]--------------------------------------------------------------------------------------------------[*] static  unsigned char           BoardTestFlag = 1; static  struct hrtimer          BoardTestTimer;  // Board Test Timer static  struct platform_device  *pd;

//[*]--------------------------------------------------------------------------------------------------[*] // IOBOARD KEY & LED GPIO Define //[*]--------------------------------------------------------------------------------------------------[*] enum    {     IOBOARD_SW1,     IOBOARD_SW2,     IOBOARD_SW3,     IOBOARD_SW4,     IOBOARD_LED1,     IOBOARD_LED2,     IOBOARD_LED3,     IOBOARD_LED4,     IOBOARD_LED5,  IOBOARD_LED6,     IOBOARD_MAX };

//[*]--------------------------------------------------------------------------------------------------[*] // GPIO Export Number define #define GPX2_5_EXPORT_NUM   29 #define GPX2_6_EXPORT_NUM   30 #define GPX1_6_EXPORT_NUM   22 #define GPX1_2_EXPORT_NUM   18 #define GPX2_7_EXPORT_NUM   31 #define GPX2_4_EXPORT_NUM   28 #define GPX1_3_EXPORT_NUM   19 #define GPB3_2_EXPORT_NUM   209 #define GPX2_0_EXPORT_NUM   24 #define GPX3_2_EXPORT_NUM   34

static struct {  int  gpio_index;  // Control Index  int  gpio;   // GPIO Number  char *name;   // GPIO Name == sysfs attr name (must)  bool  output;   // 1 = Output, 0 = Input  int  value;   // Default Value(only for output)  int  pud;   // Pull up/down register setting : S3C_GPIO_PULL_DOWN, UP, NONE } sControlGpios[] = {  { IOBOARD_SW1,   GPX2_5_EXPORT_NUM,  "sw1", 0, 0, S3C_GPIO_PULL_NONE },  { IOBOARD_SW2,   GPX2_6_EXPORT_NUM,  "sw2", 0, 0, S3C_GPIO_PULL_NONE },  { IOBOARD_SW3,   GPX1_6_EXPORT_NUM,  "sw3", 0, 0, S3C_GPIO_PULL_NONE },  { IOBOARD_SW4,   GPX1_2_EXPORT_NUM,  "sw4", 0, 0, S3C_GPIO_PULL_NONE },  { IOBOARD_LED1,   GPX2_7_EXPORT_NUM,  "led1", 1, 0, S3C_GPIO_PULL_NONE },  { IOBOARD_LED2,   GPX2_4_EXPORT_NUM,  "led2", 1, 0, S3C_GPIO_PULL_NONE },  { IOBOARD_LED3,   GPX1_3_EXPORT_NUM,  "led3", 1, 0, S3C_GPIO_PULL_NONE },  { IOBOARD_LED4,   GPB3_2_EXPORT_NUM,  "led4", 1, 0, S3C_GPIO_PULL_NONE },  { IOBOARD_LED5,   GPX2_0_EXPORT_NUM,  "led5", 1, 1, S3C_GPIO_PULL_NONE },  { IOBOARD_LED6, GPX3_2_EXPORT_NUM, "led6", 1,  0, S3C_GPIO_PULL_NONE }, };

//[*]--------------------------------------------------------------------------------------------------[*] //[*]--------------------------------------------------------------------------------------------------[*] static  ssize_t show_gpio  (struct device *dev, struct device_attribute *attr, char *buf) {  int i;

 for (i = 0; i < ARRAY_SIZE(sControlGpios); i++) {   if(sControlGpios[i].gpio) {    if(!strcmp(sControlGpios[i].name, attr->attr.name))     return sprintf(buf, "%d\n", (gpio_get_value(sControlGpios[i].gpio) ? 1 : 0));   }  }    return sprintf(buf, "ERROR! : Not found gpio!\n"); }

//[*]--------------------------------------------------------------------------------------------------[*] static  ssize_t set_gpio  (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {     unsigned int val, i;

    if(!(sscanf(buf, "%d\n", &val)))  return -EINVAL;

 for (i = 0; i < ARRAY_SIZE(sControlGpios); i++) {   if(sControlGpios[i].gpio) {    if(!strcmp(sControlGpios[i].name, attr->attr.name)) {     if(sControlGpios[i].output)         {gpio_set_value(sControlGpios[i].gpio, ((val != 0) ? 1 : 0));      printk("%s\n",__func__);}     else      {printk("This GPIO Configuration is INPUT!!\n");        return count;}    }   }  }

 printk("%s[%d] : undefined gpio!\n", __func__,__LINE__);     return  count; }

//[*]--------------------------------------------------------------------------------------------------[*] static  ssize_t show_test  (struct device *dev, struct device_attribute *attr, char *buf) {  return sprintf(buf, "%d\n", BoardTestFlag); }

//[*]--------------------------------------------------------------------------------------------------[*] static  ssize_t set_test  (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {     unsigned int val;

    if(!(sscanf(buf, "%d\n", &val)))  return -EINVAL;

    val = (val > 0) ? 1 : 0;

    if(BoardTestFlag != val)    {         BoardTestFlag = val;

       // if(BoardTestFlag)          //   hrtimer_start(&BoardTestTimer, ktime_set(0, 500000000), HRTIMER_MODE_REL);     }

    return  count; }

//[*]--------------------------------------------------------------------------------------------------[*] //[*]--------------------------------------------------------------------------------------------------[*] static DEVICE_ATTR(sw1,    S_IRWXUGO, show_gpio, NULL); static DEVICE_ATTR(sw2,    S_IRWXUGO, show_gpio, NULL); static DEVICE_ATTR(sw3,    S_IRWXUGO, show_gpio, NULL); static DEVICE_ATTR(sw4,    S_IRWXUGO, show_gpio, NULL);

static DEVICE_ATTR(led1,   S_IRWXUGO, NULL, set_gpio); static DEVICE_ATTR(led2,   S_IRWXUGO, NULL, set_gpio); static DEVICE_ATTR(led3,   S_IRWXUGO, NULL, set_gpio); static DEVICE_ATTR(led4,   S_IRWXUGO, NULL, set_gpio); static DEVICE_ATTR(led5,   S_IRWXUGO, NULL, set_gpio); static  DEVICE_ATTR(led6, S_IRWXUGO, NULL, set_gpio);

static DEVICE_ATTR(board_test,   S_IRWXUGO, show_test, set_test); //[*]--------------------------------------------------------------------------------------------------[*] static struct attribute *ioboard_sysfs_entries[] = {  &dev_attr_sw1.attr,  &dev_attr_sw2.attr,  &dev_attr_sw3.attr,  &dev_attr_sw4.attr,  &dev_attr_led1.attr,  &dev_attr_led2.attr,  &dev_attr_led3.attr,  &dev_attr_led4.attr,  &dev_attr_led5.attr,  &dev_attr_led6.attr,  &dev_attr_board_test.attr,  NULL };

static struct attribute_group ioboard_sysfs_attr_group = {  .name   = NULL,  .attrs  = ioboard_sysfs_entries, };

//[*]--------------------------------------------------------------------------------------------------[*] //[*]--------------------------------------------------------------------------------------------------[*] /* static enum hrtimer_restart ioboard_test_timer(struct hrtimer *timer) {     if(BoardTestFlag)   {         gpio_set_value( sControlGpios[IOBOARD_LED1].gpio,                         gpio_get_value(sControlGpios[IOBOARD_SW1].gpio) ? 0 : 1);         gpio_set_value( sControlGpios[IOBOARD_LED2].gpio,                         gpio_get_value(sControlGpios[IOBOARD_SW2].gpio) ? 0 : 1);         gpio_set_value( sControlGpios[IOBOARD_LED3].gpio,                         gpio_get_value(sControlGpios[IOBOARD_SW3].gpio) ? 0 : 1);         gpio_set_value( sControlGpios[IOBOARD_LED4].gpio,                         gpio_get_value(sControlGpios[IOBOARD_SW4].gpio) ? 0 : 1);

        gpio_set_value( sControlGpios[IOBOARD_LED5].gpio,                         gpio_get_value(sControlGpios[IOBOARD_LED5].gpio) ? 0 : 1);

  gpio_set_value( sControlGpios[IOBOARD_LED6].gpio,       gpio_get_value(sControlGpios[IOBOARD_LED6].gpio) ? 0 : 1);

        hrtimer_start(&BoardTestTimer, ktime_set(0, 500000000), HRTIMER_MODE_REL);     }     else    {         gpio_set_value(sControlGpios[IOBOARD_LED1].gpio, 0);         gpio_set_value(sControlGpios[IOBOARD_LED2].gpio, 0);         gpio_set_value(sControlGpios[IOBOARD_LED3].gpio, 0);         gpio_set_value(sControlGpios[IOBOARD_LED4].gpio, 0);         gpio_set_value(sControlGpios[IOBOARD_LED5].gpio, 1);   gpio_set_value(sControlGpios[IOBOARD_LED6].gpio, 0);     }

 return HRTIMER_NORESTART; } */

//[*]--------------------------------------------------------------------------------------------------[*] static int ioboard_keyled_resume(struct platform_device *dev) {  #if defined(DEBUG_PM_MSG)   printk("%s\n", __FUNCTION__);  #endif

    return  0; }

//[*]--------------------------------------------------------------------------------------------------[*] static int ioboard_keyled_suspend(struct platform_device *dev, pm_message_t state) {  #if defined(DEBUG_PM_MSG)   printk("%s\n", __FUNCTION__);  #endif       return  0; }

//[*]--------------------------------------------------------------------------------------------------[*] static int  ioboard_keyled_probe  (struct platform_device *pdev) {     int i;

 // Control GPIO Init  for (i = 0; i < ARRAY_SIZE(sControlGpios); i++) {

  if(sControlGpios[i].gpio) {    if(gpio_request(sControlGpios[i].gpio, sControlGpios[i].name)) {     printk("%s : %s gpio reqest err!\n", __FUNCTION__, sControlGpios[i].name);    }    else {     if(sControlGpios[i].output)  gpio_direction_output (sControlGpios[i].gpio, sControlGpios[i].value);     else       gpio_direction_input (sControlGpios[i].gpio);       s3c_gpio_setpull  (sControlGpios[i].gpio, sControlGpios[i].pud);    }   }  }

/*  hrtimer_init(&BoardTestTimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);  BoardTestTimer.function = ioboard_test_timer;    if(BoardTestFlag)         hrtimer_start(&BoardTestTimer, ktime_set(0, 500000000), HRTIMER_MODE_REL); */     printk("\n=================== %s ===================\n\n", __func__);  printk("\n=====ioboard-keyled Driver Probe S.jung\n\n");

 return sysfs_create_group(&pdev->dev.kobj, &ioboard_sysfs_attr_group); }

//[*]--------------------------------------------------------------------------------------------------[*] static int  ioboard_keyled_remove  (struct platform_device *pdev) {  int i;

 for (i = 0; i < ARRAY_SIZE(sControlGpios); i++)  {   if(sControlGpios[i].gpio) gpio_free(sControlGpios[i].gpio);  }

    sysfs_remove_group(&pdev->dev.kobj, &ioboard_sysfs_attr_group);

    //hrtimer_cancel(&BoardTestTimer);

    return 0; }

//[*]--------------------------------------------------------------------------------------------------[*] static struct platform_driver ioboard_keyled_driver = {  .driver = {   .name = "ioboard-keyled",   .owner = THIS_MODULE,  },  .probe   = ioboard_keyled_probe,  .remove  = ioboard_keyled_remove,  .suspend = ioboard_keyled_suspend,  .resume  = ioboard_keyled_resume, };

//[*]--------------------------------------------------------------------------------------------------[*] static int __init ioboard_keyled_init(void) {     int err;

    if((err = platform_driver_register(&ioboard_keyled_driver)))         return  err;

    if((pd = platform_device_alloc("ioboard-keyled", -1)) == NULL)  {         err = -ENOMEM;         goto exit_unregister;     }

    if((err = platform_device_add(pd)))         goto exit_unregister;

    return  0;

exit_unregister:     platform_driver_unregister(&ioboard_keyled_driver);     return  err; }

//[*]--------------------------------------------------------------------------------------------------[*] static void __exit ioboard_keyled_exit(void) {     platform_device_unregister(pd);     platform_driver_unregister(&ioboard_keyled_driver); }

//[*]--------------------------------------------------------------------------------------------------[*] module_init(ioboard_keyled_init); module_exit(ioboard_keyled_exit);

//[*]--------------------------------------------------------------------------------------------------[*] MODULE_DESCRIPTION("IOBOARD driver for ODROIDXU-Dev board"); MODULE_AUTHOR("Hard-Kernel"); MODULE_LICENSE("GPL");

//[*]--------------------------------------------------------------------------------------------------[*] //[*]--------------------------------------------------------------------------------------------------[*]

크리에이티브 커먼즈 라이센스
Creative Commons License
2015/12/07 19:32 2015/12/07 19:32

댓글을 달아 주세요

리눅스 커널 2.6 이상부터 제공되는 추가적 메모리 할당과 해제 방식

시스템 메모리 부족시에 가상 파일 시스템이 동작하지만

인터럽트 처리등에 따른 메모리 할당 시에는 가상 메모리 사용하기에는 무리가 있다

따라서 사전 예측 메모리를 미리 할당하고 관리하는 방식의 메모리풀 이라느 개념을 도입 실제 블록 디바이스

드라이버 부분에 주로 사용된다 

- 리눅스 디바이스 드라이버, 한빛미디어 , 저자 유영창 일부 발췌 -  



#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/slab.h>

#include <linux/mempool.h>

 

 

#define MIN_ELEMENT    4

#define TEST_ELEMENT   4

 

 

typedef struct

{

    int number;

    char string[128];

}TMemElement;

 

 

int elementcount = 0;

 

void *mempool_alloc_test(int gfp_mask, void *pool_data)

{

    TMemElement  *data;

    printk("---->mempool_alloc_test\n");

 

   data = kmalloc(sizeof(TMemElement),gfp_mask);

   if(data != NULL)  data->number = elementcount++;

   return data;

 

}

 

void mempool_free_test(void *element, void *pool_data)

{

 

    printk("---->call mempool_free_test\n");

    if(element != NULL) kfree(element);

 

}

 

int mempool_init(void)

{

    mempool_t *mp;

    TMemElement *element[TEST_ELEMENT];

    int lp;

 

    printk("Module MEMPOOL Test\n");

 

    memset(element,0,sizeof(element));

 

    printk("call mempool_create\n");

 

// mp에 선언시 mempool_alloc_test를 호출하여 MIN_ELEMENT만큼 할당한다

    mp = mempool_create(MIN_ELEMENT,mempool_alloc_test,mempool_free_test,NULL);

 

    printk("mempool allocate\n");

    for(lp=0;lp<TEST_ELEMENT;lp++)

    {

        element[lp] = mempool_alloc(mp,GFP_KERNEL);

        if(element[lp] == NULL) printk("allocate fail\n");

        else

        {

            sprintf(element[lp] -> string, "alloc data %d\n",element[lp]->number);

            printk(element[lp]-> string);

        }

    }

    printk("emepool free\n");

    for(lp=0;lp<TEST_ELEMENT;lp++)

    {

        if(element[lp] != NULL) mempool_free(element[lp],mp);

 

    }

 

   printk("call mempool_destroy\n");

   mempool_destroy(mp);

 

   return 0;

    

}

 

void mempool_exit(void)

{

     printk("Module MEMPOOL Test END\n");

 

}

 

module_init(mempool_init);

module_exit(mempool_exit);

MODULE_LICENSE("Dual BSD/GPL");

 




사용자 삽입 이미지









































크리에이티브 커먼즈 라이센스
Creative Commons License
2015/08/18 00:14 2015/08/18 00:14

댓글을 달아 주세요



 
#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/kernel.h> 
  
#include <linux/slab.h> 
#include <linux/vmalloc.h> 

//kmalloc 최대 할당 크기 32*PAGE_SIZE 페이지 사이즈는 4096바이트이다.
//전달인자
//GFP_KERNEL : 동정메모리 할당요구하며 메모리 부족시 프로세스 멈추었다가
//                 할당가능한 시점에 깨어남 (인러럽트 사용시 불가)
//GFP_ATOMIC : 무조건 할당 실행 하며 실패 할수 있음 따라서 싶패시의 조건을 염두하여 프로그래밍
//GFP_DMA : Device Driver는 가상공간에 사용해도 할당받는 물리메모리의 단편화 되어 할당 
//             받을수도 있는데 물리적으로 연속된 메모리 공간을 할당받기 위해 사용한다 
void kmalloc_test(void) 
{ 
char * buff; 
  
printk("kamalloc\n"); 
  
buff = kmalloc(1024, GFP_KERNEL); 
if (buff != NULL) 
{ 
sprintf(buff,"test memory\n"); 
printk(buff); 
kfree(buff); 
} 
  
buff = kmalloc(32 * PAGE_SIZE, GFP_KERNEL); 
  
if(buff != NULL) 
{ 
printk("big MEmoey OK\n"); 
kfree(buff); 
} 
  
} 

//vmalloc 은 kmalloc과 차이점은 크기 제한 없다.단지 주소 획득시 할당 공간이 하드디스크 영역에 있어 물리 주소 얻고자 할경우 FAIL!!
// 인터럽트 서비스 루틴에서 사용 불가 (잠드는 수가 잇음 ㄷㄷㄷ)
//느림

 
void vmalloc_test(void) 
{ 
char * buff; 
  
printk("vmalloc test\n"); 
  
buff = vmalloc(33 * PAGE_SIZE); 
  
if(buff != NULL) 
{ 
sprintf(buff,"vmalloc test ok\n"); 
printk(buff); 
vfree(buff); 
} 
  
} 
  
//잘안씀 할당 최대 크기는 kmalloc과 동일하다 
void get_free_pages_test(void) 
{ 
char * buff; 
int order; 
printk("get_free_pages test\n"); 
  
order = get_order(8192*10); 
buff = __get_free_pages(GFP_KERNEL,order); 
if(buff != NULL) 
{ 
sprintf(buff,"__get_free_page test ok[%d]\n",order); 
printk(buff); 
  
free_pages(buff,order); 
  
} 
} 
  
  
int memtest_init(void) 
{ 
char *buff; 
  
printk("module Memory\n"); 
  
kmalloc_test(); 
vmalloc_test(); 
get_free_pages_test(); 
  
return 0; 
} 
  
void memtest_exit(void) 
{ 
printk("Module Memory Test End\n"); 
} 
  
 module_init(memtest_init); 
 module_exit(memtest_exit); 
  
MODULE_LICENSE("Dual BSD/GPL"); 


#sudo make


#dmesg

실행하여 printk문을 확인한다.


사용자 삽입 이미지
크리에이티브 커먼즈 라이센스
Creative Commons License
2015/07/14 23:51 2015/07/14 23:51

댓글을 달아 주세요

Device Driver Module Parameter 전달 인자 관련 선언 방법

http://www.makelinux.net/books/lkd2/ch16lev1sec6

Device Driver Code
 
//Device Dirver Parameter code 
#include <linux/init.h> 
#include<linux/module.h> 
#include<linux/kernel.h> 
#include<linux/moduleparam.h> 
  
static int onevalue = 1; 
static char * twostring = NULL; 
  
//디바이스 드라이버에 사용되는 파라미터 값을 입력 받는 부분 
module_param(onevalue, int, 0); 
module_param(twostring, charp,0); 
  
static int hello_init(void) 
{ 
        printk("Hello, World[Onevalue=%d:twostring=%s]\n",onevalue,twostring); 
        return 0; 
} 
  
static void hello_exit(void) 
{ 
        printk("goodbye\n"); 
} 
  
module_init(hello_init); 
module_exit(hello_exit); 
  
MODULE_AUTHOR("S.Jung sjung8432@pnstech.net"); 
MODULE_DESCRIPTION("Device Driver Parameter Test Module"); 
MODULE_LICENSE("Dual BSD/GPL"); 



MakeFile

 
obj-m   := test02.o 
  
KDIR    := /lib/modules/$(shell uname -r)/build 
PWD     := $(shell pwd) 
  
default: 
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 
  
clean: 
        rm -rf *.ko 
        rm -rf *.mod.* 
        rm -rf .*.cmd 
        rm -rf *.o




컴파일 후

#sudo insmod test02.ko onevalue=0x01 twostring="Linux_device_Driver_Test"

#rmmod test02.ko

#dmesg

사용자 삽입 이미지



dmesg를 이용하여 커널에 모듈(디바이스드라이버)를 등록 해제시 발생된 메세지를 볼수 있다

test02.c에서 insmod와 rmmode시 (init/exit) 함수에 구현된 printk 구문이 출력 되는것을 볼수 있다.

전달 인자도 입력 받아 printk로 출려된것을 확인할수 있다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2015/06/28 00:41 2015/06/28 00:41

댓글을 달아 주세요

  1. android tv box bình dương 2017/02/27 17:49  address  modify / delete  reply

    Highly descriptive article, I liked that a lot. Will there be a part 2?

  2. 홈키파 2017/03/02 17:35  address  modify / delete  reply

    Sorry
    I don't have Part2.


/*커널 2.6전용 디바이스 파일임*/  
#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/kernel.h>  

//ubuntu linux kernel 3.x추가 
#include<asm-generic/bitsperlong.h>

//device init
static int hello_init(void)  
{  
    printk("Hellow World\n");  
return 0;  
}  

//device unload
static void hello_exit(void)  
{  
 printk("Goodbye World\n");   
}  
  
module_init(hello_init); //디바이스모듈 진입  
module_exit(hello_exit); //디바이스모듈 해제  
  
MODULE_LICENSE("Dual BSD/GPL"); 



Makefile


obj-m   := test.o

KDIR    := /lib/modules/$(shell uname -r)/build
PWD     := $(shell pwd)

default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
        rm -rf *.ko
        rm -rf *.mod.*
        rm -rf .*.cmd
        rm -rf *.o
크리에이티브 커먼즈 라이센스
Creative Commons License
2015/06/27 21:28 2015/06/27 21:28

댓글을 달아 주세요

  1. android tv box daklak 2017/03/02 14:07  address  modify / delete  reply

    I am regular visitor, how are you everybody?
    This piece of writing posted at this website is really good.

리눅스 디바이스 드라이벌ㄹ 공부하기 위해 2.6버전대의 책을 보다

요즘 커널 버전대가 3.X로 올라갔고 (뭐 기본적인 유형은 비슷하다고함)

어쩃든 책에 작성된 코드로는 컴파일이 안되어 하기의 방법으로 해결 하였다.

현재 사용하는 OS는 Ubuntu 64bit
커널 버전 3.16.0-30-generic

# uname -r
커널 정보 확인이 가능하다.



먼저 ubuntu의 경우

http://com.odroid.com/sigong/nf_board/nboard_view.php?brd_id=odroidx&bid=1752(새 창으로 열기) 

리플에 우분투는 소스코드를 설치 하여야 한다고 되어있음

하기의 사이트 참조

http://www.cyberciti.biz/faq/installing-full-kernel-source-ubuntu-linux/(새 창으로 열기) 




include/uapi/asm-generic/int-ll64.h:11:29: fatal error: asm/bitsperlong.h: No such file or directory

에러 발생시

http://www.cyberciti.biz/faq/installing-full-kernel-source-ubuntu-linux/(새 창으로 열기) 사이트를 참조하여

Diver 코드에 하기의 코드를 추가후 컴파일


#include <asm-generic/bitsperlong.h>



코드 컴파일 시에는 sudo 로 진행한며 컴파일시 KO 파일이 생성 됨


사용자 삽입 이미지
크리에이티브 커먼즈 라이센스
Creative Commons License
2015/06/27 17:35 2015/06/27 17:35

댓글을 달아 주세요

stdarg.h 파일 선언후 사용해야함.!!

가변인자 관련 설명 페이지

http://www.ysoh.pe.kr/entry/C%EC%96%B8%EC%96%B4%EC%97%90%EC%84%9C-%EA%B0%80%EB%B3%80-%ED%8C%8C%EB%9D%BC%EB%A9%94%ED%84%B0%EB%A5%BC-%EA%B0%80%EC%A7%80%EB%8A%94-%ED%95%A8%EC%88%98




크리에이티브 커먼즈 라이센스
Creative Commons License
2012/05/22 10:52 2012/05/22 10:52

댓글을 달아 주세요


STM32공부하다가 알게된것 API설명 문서

내가 쓰고 자픈 API 또는 다른 코드에서 사용된 API가 어떻게 사용된지 모를 경우 혹은 개발중에라도 참고하기 쉽다

해당 파일은 ST에서 제공된다.

라이브러리 릴리즈 버전에 맞는것을 봐야 한다.

본 파일의 경우 릴리즈3.5.0버전에 들어있던 것이다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/08 00:16 2012/04/08 00:16
Tag // , , ,

댓글을 달아 주세요

[WIN API] Message Box

from 공부 2012/01/05 19:40

메시지 박스 세번째 인자의 설정에 따른 출력창 형태 변경



크리에이티브 커먼즈 라이센스
Creative Commons License
2012/01/05 19:40 2012/01/05 19:40

댓글을 달아 주세요

사용자 삽입 이미지


크리에이티브 커먼즈 라이센스
Creative Commons License
2012/01/05 19:27 2012/01/05 19:27

댓글을 달아 주세요