Steward
分享是一種喜悅、更是一種幸福
驅動程式 - 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