驅動程式 - Linux Device Driver (LDD) - 使用範例 - Assembly (ARM) - Char Device - Device Node



參考資訊:
https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html

User Application可以透過檔案操作方式跟驅動程式溝通,在Linux Kernel中,將檔案分成字元驅動程式(Char)和區塊驅動程式(Block)兩大類,主要區別在於傳輸方式,可以把字元驅動程式想像成每次傳輸都是以1個Byte為單位,而區塊驅動程式則是多個Bytes為單位,如:512 Bytes,User Application可以透過open()、read()、write()、ioctl()、close()跟字元驅動程式溝通,那User Application怎麼知道路徑在哪呢?答案是Symbolic Link(/dev/xxx),那Symbolic Link又如何連結到字元驅動程式呢?答案是Major、Minor號碼,Major、Minor號碼是驅動程式的裝置索引,字元驅動程式在被系統載入時,會註冊Major和Minor號碼,使用者可以透過mknod工具,去建立一個Symbolic Link(名稱可以由使用者隨意取)並且綁定到字元驅動程式的這個Major、Minor號碼,完成連接的管道

字元驅動程式建立步驟:

1. alloc_chrdev_region()
2. cdev_init()
3. cdev_add()
4. cdev_del()
5. unregister_chrdev_region()

alloc_chrdev_region()可以取得空閒的裝置號碼,如果想要自訂Major號碼,可以使用register_chrdev_region()

ldd.S

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
    .global init_module
    .global cleanup_module
 
    .section .modinfo, "ae"
__UNIQUE_ID_0: .asciz "license=GPL"
__UNIQUE_ID_1: .asciz "author=Steward Fu"
__UNIQUE_ID_2: .asciz "description=Linux Driver"
 
    .equ cdev.kobj,  0
    .equ cdev.owner, 36
    .equ cdev.ops,   40
    .equ cdev.list,  44
    .equ cdev.dev,   52
    .equ cdev.count, 56
 
    .struct 0
cdev_s:
    c0: .struct . + 36
    c1: .struct . + 4
    c2: .struct . + 4
    c3: .struct . + 8
    c4: .struct . + 4
    c5: .struct . + 4
cdev_e:
cdev_l = cdev_e - cdev_s
 
    .equ file_operations.owner,             0
    .equ file_operations.llseek,            4
    .equ file_operations.read,              8
    .equ file_operations.write,             12
    .equ file_operations.read_iter,         16
    .equ file_operations.write_iter,        20
    .equ file_operations.iterate,           24
    .equ file_operations.iterate_shared,    28
    .equ file_operations.poll,              32
    .equ file_operations.unlocked_ioctl,    36
    .equ file_operations.compat_ioctl,      40
    .equ file_operations.mmap,              44
    .equ file_operations.open,              48
    .equ file_operations.flush,             52
    .equ file_operations.release,           56
    .equ file_operations.fsync,             60
    .equ file_operations.fasync,            64
    .equ file_operations.lock,              68
    .equ file_operations.sendpage,          72
    .equ file_operations.get_unmapped_area, 76
    .equ file_operations.check_flags,       80
    .equ file_operations.setfl,             84
    .equ file_operations.flock,             88
    .equ file_operations.splice_write,      92
    .equ file_operations.splice_read,       96
    .equ file_operations.setlease,          100
    .equ file_operations.fallocate,         104
    .equ file_operations.show_fdinfo,       108
    .equ file_operations.mmap_capabilities, 112
    .equ file_operations.copy_file_range,   116
    .equ file_operations.clone_file_range,  120
    .equ file_operations.dedupe_file_range, 124
 
    .struct 0
file_operations_s:
    i0: .struct . + 4
    i1: .struct . + 4
    i2: .struct . + 4
    i3: .struct . + 4
    i4: .struct . + 4
    i5: .struct . + 4
    i6: .struct . + 4
    i7: .struct . + 4
    i8: .struct . + 4
    i9: .struct . + 4
    i10: .struct . + 4
    i11: .struct . + 4
    i12: .struct . + 4
    i13: .struct . + 4
    i14: .struct . + 4
    i15: .struct . + 4
    i16: .struct . + 4
    i17: .struct . + 4
    i18: .struct . + 4
    i19: .struct . + 4
    i20: .struct . + 4
    i21: .struct . + 4
    i22: .struct . + 4
    i23: .struct . + 4
    i24: .struct . + 4
    i25: .struct . + 4
    i26: .struct . + 4
    i27: .struct . + 4
    i28: .struct . + 4
    i29: .struct . + 4
    i30: .struct . + 4
    i31: .struct . + 4
file_operations_e:
file_operations_l = file_operations_e - file_operations_s
 
    .section .data
base:      .dcb 4
mycdev:    .space cdev_l
myfops:    .space file_operations_l
chr_name:  .asciz "myfile"
fmt:       .asciz "major:%d, minor:%d\n"
open_msg:  .asciz "myopen\n"
close_msg: .asciz "myclose\n"
 
    .align 2
    .section .text
myopen:
    push {lr}
    ldr r0, =open_msg
    bl printk
    mov r0, #0
    pop {pc}
 
myclose:
    push {lr}
    ldr r0, =close_msg
    bl printk
    mov r0, #0
    pop {pc}
 
init_module:
    push {r4, lr}
 
    ldr r0, =myfops
    add r1, r0, #file_operations.open
    ldr r2, =myopen
    str r2, [r1]
    add r1, r0, #file_operations.release
    ldr r2, =myclose
    str r2, [r1]
 
    ldr r0, =base
    mov r1, #0
    mov r2, #1
    ldr r3, =chr_name
    bl alloc_chrdev_region
    ldr r4, =base
 
    ldr r0, =mycdev
    ldr r1, =myfops
    bl cdev_init
 
    ldr r0, =mycdev
    ldr r1, [r4]
    mov r2, #1
    bl cdev_add
 
    ldr r0, =fmt
    ldr r1, [r4]
    ubfx r2, r1, #0, #0x14
    mov r1, r1, asr#20
    bl printk
 
    mov r0, #0
    pop {r4, pc}
 
cleanup_module:
    push {lr}
    ldr r0, =mycdev
    bl cdev_del
    ldr r0, =base
    ldr r0, [r0]
    mov r1, #1
    bl unregister_chrdev_region
    pop {pc}
    .end

init_module: 建立字元驅動程式
myopen: 對應User Application的open()
myclose: 對應User Application的close()
cleanup_module: 刪除字元驅動程式

安裝驅動

# insmod /boot/main.ko 
    major:243, minor:0

P.S. Linux驅動程式使用Major、Minor號碼當作裝置索引

make node

# mknod /dev/myfile c 243 0

# ls -al /dev/myfile
    crw-r--r--    1 root     root      243,   0 Jan  1 00:00 /dev/myfile

P.S. 建立Symbolic符號,供User Application開啟使用

開啟裝置

# echo "" > /dev/myfile
    myopen
    sh: write error: Invalid argument myclose