본문 바로가기

학부_대학원/임베디드 리눅스

(임베디드)리눅스 커널 모듈 - Character Device Driver

SMALL

케릭터 디바이스 드라이버

틈틈이 드라이버를 만들었지만, 직접 디바이스를 컨트롤 해봄.

비록 간단한 led이지만, 어차피  메모리의 값을 사용하고 특정 핀?으로 데이터를 설정하는건 모두 같은 원리이니까.


linux 2.4 버전의 경우 거의 사용될 일이 없을것 같아 정리하지 않음.

linux 2.6 버전은 4.x 까지 비슷하게 사용가능


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
 
register_chrdev_region()
 
    카운트 값을 가지는 디바이스 드라이버 등록
 
    원형
 
         int register_chrdev_region(dev_t fisrt, unsigned int count, char* name)
    
    여기서 카운트 값은 부번호의 리밋    
 
alloc_chrdev_regin()
 
    디바이스 번호를 동적으로 할당 해줌
 
    원형
 
        int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char* name)
 
unregister_chrdev_region()
 
       사용중인 디바이스 번호를 해체 한다.
 
    원형
 
            void unregister_chrdev_region(dev_t first, unsigned int count);
 
 
 
 
dev_t은 Major와 Minor 번호가 조합된 형이다.
 
        macro 소개
    
        Major 12bits, Minor 20bits
        
        MAJOR(dev_t dev);        주 번호 추출
        MINOR(dev_t dev);        부 번호 추출        
        MKDEV(int ma, int mi)    번호 설정
 
 
cdev 구조체
 
        struct cdev{
            struct kobject *owner;
        
            struct module file_operations *ops
 
            struct list_head list;
 
            dev_t dev
 
            unsigned int count
        
        }
 
        2.6 커널은 내부적으로 캐릭터 디바이스를 표현하기 위해  cdev구조체를 사용 —> cdev 구조체가 생성된 이유
 
        
 
    cdev_init()
 
        cdev 구조체를 초기화
 
        원형
            void cdev_init(struct cdev *cdev, struct file_operations *fops)
 
 
    cdev_add()
 
            cdev 구조체를 커널에 등록 함
 
            원형
                vod cdev_add(struct cdev *cdev, dev_t num, unsigned int count);
 
 
cs


그리고 코드는 직관적으로 알수 있을 것이다.

저기서 led 의 주소 값이 0xE02002C0 을 볼수 있는데, 이건 해당 보드의 led 주소여서 보드마다 상이할 수있음.


여기서 또 주의깊게 볼 함수? 는 ioremap, iounmap함수인데, mmap과 같이 물리 메모리에 직접적으로 접근할 수 있도록 해준다!!


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
 
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <asm/io.h>   // ioremap(),iounmap()
#include <linux/kernel.h>   /* printk() */
#include <linux/slab.h>   /* kmalloc() */
#include <linux/fs.h>       /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define DEVICE_NAME "mydrv"

//디바이스 드라이버 주 번호
static int mydrv_major = 245;

//io addr
static unsigned long * kva = 0;
module_param(mydrv_major, int0);
 
static void led_on_(void);
static void led_off_(void);
static void init_led(void);
 
 
static int mydrv_open(struct inode *inode, struct file *file)
{
  init_led();
  printk("mydrv opened !!\n");
  return 0;
}
 
static int mydrv_release(struct inode *inode, struct file *file)
{
  printk("mydrv released !!\n");
  return 0;
}
 
static ssize_t mydrv_read(struct file *filp, char __user *buf, size_t count,
                loff_t *f_pos)
{
  printk("mydrv_read is invoked\n");
  return 0;
 
}
 
static ssize_t my_led_write(struct file *filp,const char __user *buf, size_t len,
                            loff_t *f_pos)
{
 
 
  printk("Let'g go led game\n");
 
 
    while(1){
        led_on_();
        mdelay(10000);
        led_off_();
    }
  return 0;
}
 
 
/* Set up the cdev structure for a device. */
static void mydrv_setup_cdev(struct cdev *dev, int minor,
        struct file_operations *fops)
{
    /*
     *    Major  와 Minor를 합친 dev 번호
     */
    int err, devno = MKDEV(mydrv_major, minor);
    
    /*
     *    operation 과 cdev 설정
     */
    cdev_init(dev, fops);
    dev->owner = THIS_MODULE;
    dev->ops = fops;
    err = cdev_add (dev, devno, 1);
    
    if (err)
        printk (KERN_NOTICE "Error %d adding mydrv%d", err, minor);
}
 
 
 
static struct file_operations mydrv_fops = {
    .owner   = THIS_MODULE,
    .read    = mydrv_read,
    .write   = my_led_write,
    .open    = mydrv_open,
    .release = mydrv_release,
};
 
#define MAX_MYDRV_DEV 1
 
 
/*
 * cdev 포인터 구조체 선언
 */
 
 
static struct cdev MyDrvDevs[MAX_MYDRV_DEV];
 
static int mydrv_init(void)
{
    int result;
    // 디바이스 번호를 설정
    // MAJOR 240  부 번호 0
    dev_t dev = MKDEV(mydrv_major, 0);
 
    /* Figure out our device number. */
 
 
 
    /*
     * manual device number 등록  
     */
    if (mydrv_major)
 
        result = register_chrdev_region(dev, //device number 
                        1,    //minor count
                        DEVICE_NAME //device name
        );
    /*
     *  auto device number 등록
     */
    else {
        result = alloc_chrdev_region(&dev, //dev num poninter if success, return number
                        0//allocate poll 설정
                        1//minor count
                        DEVICE_NAME); //device name
 
        mydrv_major = MAJOR(dev); //major number get
    }
    if (result < 0) {
        printk(KERN_WARNING "mydrv: unable to get major %d\n", mydrv_major);
        return result;
    }
 
    if (mydrv_major == 0)
        mydrv_major = result;
 
    mydrv_setup_cdev(MyDrvDevs,0&mydrv_fops);
    printk("mydrv_init done\n");    
    return 0;
}
 
static void mydrv_exit(void)
{
    cdev_del(MyDrvDevs);
    unregister_chrdev_region(MKDEV(mydrv_major, 0), 1);
    printk("mydrv_exit done\n");
}
 
 
static void init_led(void)
{
        kva = ioremap(0xE02002C0,0xA6C) ;
        printk("kva = 0x%x\n",(int)kva);
        *((unsigned long*)kva)=0x11111111;
        *(unsigned long*)((unsigned char*)kva+8)= 0x00000000;
 
}
 
static void led_on_(void)
{
        *(unsigned char*)((unsigned char*)kva+4=0xff;
 
}
 
static void led_off_(void)
{
        *(unsigned char*)((unsigned char*)kva+4=0x00;
}
 
 
module_init(mydrv_init);
module_exit(mydrv_exit);
 
MODULE_LICENSE("GPL");
 
cs



$mknod /dev/mydrv c 245 0


mknod : 장치 파일로만들어라

/dev/mydrv : 경로

c   : 케릭터 디바이스

245 : 주 번호

0 : 부 번호

장치 파일로 만들고 앱으로 연결하기

LIST