驅動程式 - Linux Device Driver (LDD) - 使用範例 - Assembly (ARM) - Char Device - File Read/Write



參考資訊:
https://www.aps-web.jp/en/ca-en/21578/#anchor2

User Application可以透過read()、write()傳送資料給驅動程式,但是需要注意的地方是,在Linux Kernel中,如果要存取User Application傳送過來的資料時,必須使用copy_to_user()、copy_from_user(),而不是一般的memcpy()或者直接指標操作,否則會導致驅動程式崩潰,這是因為Linux Kernel和User Application是位於不同保護空間,因此,在存取User Application資料時,必須確保該資料已經被Map且位於記憶體

使用方式:

1. arm_copy_to_user()
2. arm_copy_from_user()

在做arm_copy_to_user()、arm_copy_from_user()之前,必須先設定Domain Access Control Register

ldd.S

    .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"
open_msg:  .asciz "myopen\n"
write_msg: .asciz "mywrite, %s\n"
read_msg:  .asciz "myread, %s\n"
close_msg: .asciz "myclose\n"
myclass:   .dcb 4
mybuf:     .dcb 255

    .align 2
    .section .text
myopen:
    push {lr}
    ldr r0, =open_msg
    bl printk
    mov r0, #0
    pop {pc}

myread:
    push {r4, lr}

    mov r4, r1
    ldr r0, =mybuf
    bl strlen
    mov r2, r0

    ldr r0, =0x55555555
    mcr p15, 0, r0, c3, c0, 0
    isb sy

    mov r0, r4
    ldr r1, =mybuf
    bl arm_copy_to_user
    mov r4, r0

    ldr r0, =read_msg
    ldr r1, =mybuf
    bl printk

    mov r0, r4
    pop {r4, pc}

mywrite:
    push {r4, lr}

    ldr r0, =0x55555555
    mcr p15, 0, r0, c3, c0, 0
    isb sy

    ldr r0, =mybuf
    bl arm_copy_from_user
    mov r4, r0

    ldr r0, =write_msg
    ldr r1, =mybuf
    bl printk

    mov r0, r4
    pop {r4, pc}

myclose:
    push {lr}
    ldr r0, =close_msg
    bl printk
    mov r0, #0
    pop {pc}

init_module:
    push {r4, r5, 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]
    add r1, r0, #file_operations.read
    ldr r2, =myread
    str r2, [r1]
    add r1, r0, #file_operations.write
    ldr r2, =mywrite
    str r2, [r1]

    ldr r0, =base
    mov r1, #0
    mov r2, #1
    ldr r3, =chr_name
    bl alloc_chrdev_region
    ldr r4, =base

    mov r0, #0
    ldr r1, =chr_name
    ldr r2, =myclass
    bl __class_create
    ldr r1, =myclass
    str r0, [r1]

    mov r1, #0
    ldr r2, [r4]
    mov r3, #0
    ldr r5, =chr_name
    push {r5}
    bl device_create
    pop {r5}

    ldr r0, =mycdev
    ldr r1, =myfops
    bl cdev_init

    ldr r0, =mycdev
    ldr r1, [r4]
    mov r2, #1
    bl cdev_add

    mov r0, #0
    pop {r4, r5, pc}

cleanup_module:
    push {lr}
    ldr r0, =myclass
    ldr r0, [r0]
    ldr r1, =base
    ldr r1, [r1]
    bl device_destroy

    ldr r0, =mycdev
    bl cdev_del

    ldr r0, =myclass
    ldr r0, [r0]
    bl class_destroy

    ldr r0, =base
    ldr r0, [r0]
    mov r1, #1
    bl unregister_chrdev_region
    pop {pc}
    .end

init_module: 建立字元驅動程式
myopen: 僅列印字串
mywrite: 儲存並且列印User Application傳送的字串
myread: 複製儲存的字串給User Application並且列印出來
myclose: 僅列印字串
cleanup_module: 刪除字元驅動程式

寫入字串

# echo "test" > /dev/myfile
    mywrite, test

P.S. 按下Ctrl+C停止

讀取字串

# cat /dev/myfile
    myopen
    myread, test
    myclose