Kernel設備驅動程式是將底層硬體公開給系統其餘部分的機制。
作為嵌入式系統的開發者,您需要了解這些設備驅動程式如何適合整體架構,以及如何從用戶空間程式中訪問它們。
您的系統可能會有一些新奇的硬體組件,您需要找到一種方法來訪問它們。
在許多情況下,您會發現已經為您提供了一些設備驅動程式,您可以在不編寫任何Kernel代碼的情況下實現所需的一切。
例如,您可以使用 sysfs 中的文件來操作 GPIO 引腳和 LED,並且有庫可以訪問串行匯流排,包括 SPI(串行週邊介面)和 I2C(雙線串行匯流排)。
字符設備(Character Device)適用於 非緩衝的輸入/輸出(unbuffered I/O)
操作,提供了多種豐富的功能,並在應用程式代碼和驅動程式之間保持了簡潔的界面。當您需要實現自訂的設備驅動程式時,這通常是首選方案。換句話說,它能夠讓您的應用程式直接和硬體互動,而不需要太多繁雜的中間步驟。(這裡的"中間步驟"是指介於應用程式和驅動程式之間的額外操作、轉換或處理,這可能會增加代碼的複雜性和執行時間。)
例如,您可能會通過 /sys/class/gpio 目錄下的一組文件來訪問 GPIO 驅動程式。
字符設備在用戶空間中通過稱為設備節點的特殊文件來識別。這個文件名通過主要和次要編號來映射到設備驅動程式。主要編號將設備節點映射到特定的驅動程式,次要編號則告訴驅動程式訪問的介面。
例如,在 ARM Versatile PB 上,
第一個串口的設備節點是 /dev/ttyAMA0
,
它的主要編號是 204,次要編號是 64
。
第二個串口的設備節點有相同的主要編號
,但次要編號是 65
。這些編號可以從目錄清單中看到。
# ls -l /dev/ttyAMA*
crw-rw---- 1 root root 204, 64 Jan 1 1970 /dev/ttyAMA0
crw-rw---- 1 root root 204, 65 Jan 1 1970 /dev/ttyAMA1
crw-rw---- 1 root root 204, 66 Jan 1 1970 /dev/ttyAMA2
crw-rw---- 1 root root 204, 67 Jan 1 1970 /dev/ttyAMA3
標準的主要(major number) 和次要(minor number)編號清單可以在Linux Kernel文檔中的 Documentation/devices.txt 找到。該清單不會經常更新,且不包含前面段落中描述的 ttyAMA 設備。然而,如果您查看位於 drivers/tty/serial/amba-pl011.c 的Kernel源代碼,您將看到major number和minor number的define:
#define SERIAL_AMBA_MAJOR 204
#define SERIAL_AMBA_MINOR 64
這些聲明指定了特定設備(例如 ttyAMA)的major number和minor number,以便Kernel和應用程式可以識別和訪問這些設備。雖然該標準清單可能不包含所有設備,但它提供了一個參考,供開發者了解如何設定major number和minor number以進行設備識別。
當存在多個同類型的設備實例時,例如 ttyAMA 驅動程式,形成設備節點名稱的慣例是採用基本名稱 ttyAMA,然後在這個例子中從 0 到 3 添加實例編號。
我的raspberry pi 如下
root@raspberrypi:~# ls -l /dev/ttyAMA*
crw-rw---- 1 root dialout 204, 64 Aug 5 10:38 /dev/ttyAMA0
P.S. 在一些系統中,特別是像 Linux 等操作系統中,串口(UART)設備的命名可能會類似於 ttyS0、ttyS1 等。不同的系統和設備可能會有不同的命名慣例。 在 Raspberry Pi 上,UART 串口的命名通常是 ttyAMA0。但是,如果您使用的是較新的 Raspberry Pi 版本,例如 Raspberry Pi 3 或更高版本,可能會使用 ttyS0 來表示第一個 UART 串口。
linux 裝置管理員 有以下這麼多種:
devtmpfs:當設備驅動程式使用由驅動程式提供的基本名稱(例如 ttyAMA)和實例編號註冊新的設備介面時,設備節點將被創建。
udev
或 mdev(不使用 devtmpfs):與使用 devtmpfs 基本相同,不同之處在於需要使用用戶空間的守護程序從 sysfs 中提取設備名稱並創建節點。稍後我會談到 sysfs。
mknod:如果您正在使用靜態設備節點,則可以使用 mknod 手動創建它們。
major number已經擴展為12位元,有效的編號範圍從1到4,095,而minor number則擴展為20位元,範圍從0到1,048,575。
當你打開一個字符設備節點時,內核會檢查主要和次要編號是否落在由字符設備驅動程式註冊的範圍內。如果是,它會將呼叫傳遞給驅動程式,否則打開呼叫將失敗。設備驅動程式可以提取次要編號,以了解要使用哪個硬體介面。
所以 major number + minor number = 32個位元
假設你有個應用程式(Application)如下,一個簡單的例子是虛擬隨機數發生器 urandom,每次讀取它時會返回隨機數據的位元組:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int f;
unsigned int rnd;
int n;
f = open("/dev/urandom", O_RDONLY);
if (f < 0) {
perror("Failed to open urandom");
return 1;
}
n = read(f, &rnd, sizeof(rnd));
if (n != sizeof(rnd)) {
perror("Problem reading urandom");
return 1;
}
printf("Random number = 0x%x\n", rnd);
close(f);
return 0;
}
我的RPI如下:
/dev/urandom 不是一個真的硬體,但軟體RD 喜歡這樣,把東西都給虛擬化、抽象化。
一旦您運行了 Linux 系統,了解已加載的設備驅動程式以及它們的狀態將非常有用。您可以通過讀取 /proc 和 /sys 目錄中的文件來獲得許多信息。
# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyprintk
7 vcs
10 misc
13 input
14 sound
29 fb
81 video4linux
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 ttyAMA
226 drm
240 cec
241 media
242 uio
243 hidraw
244 rpmb
245 bcm2835-gpiomem
246 vc-mem
247 bsg
248 watchdog
249 ptp
250 pps
251 lirc
252 rtc
253 dma_heap
254 gpiochip
Block devices:
1 ramdisk
7 loop
8 sd
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
259 blkext
/proc 是告訴我,目前那些東西已經啟動
root@raspberrypi:~# cd /proc/
root@raspberrypi:/proc# ls
1 208 281 405 522 684 cgroups key-users swaps
10 209 285 41 53 688 cmdline kmsg sys
100 210 289 42 553 690 consoles kpagecgroup sysrq-trigger
11 211 29 420 554 741 cpu kpagecount sysvipc
112 212 3 421 567 743 cpuinfo kpageflags thread-self
114 213 32 43 58 744 crypto latency_stats timer_list
12 214 33 44 59 746 devices loadavg tty
13 22 35 451 591 749 device-tree locks uptime
138 23 36 452 6 88 diskstats meminfo version
14 24 360 456 60 89 driver misc vmallocinfo
146 253 362 464 61 90 execdomains modules vmstat
15 256 363 465 613 91 fb mounts zoneinfo
16 257 37 482 62 92 filesystems net
165 27 372 485 63 93 fs pagetypeinfo
17 272 378 5 633 95 interrupts partitions
178 273 38 50 64 96 iomem schedstat
179 277 385 502 65 97 ioports self
18 279 39 51 656 asound irq slabinfo
19 28 4 516 66 buddyinfo kallsyms softirqs
2 280 40 52 683 bus keys stat
您可以將 sysfs 在嚴格的定義中看作是內核對象、屬性和關係的表示。內核對象是一個目錄,屬性是一個文件,而關係則是從一個對象到另一個對象的符號連結。從一個更實際的角度來看,由於 Linux 設備驅動程式模型將所有設備和驅動程式都表示為內核對象,您可以透過查看 /sys 來看到內核對系統的視圖,如下所示:
root@raspberrypi:/proc# ls /sys/class
backlight extcon iscsi_iface pps spi_master
bcm2835-gpiomem gpio iscsi_session ptp spi_slave
bdi graphics iscsi_transport pwm thermal
block hidraw leds rc tty
bluetooth hwmon lirc regulator udc
bsg i2c-adapter mdio_bus rfkill uio
devcoredump ieee80211 mem rtc vc
devlink input misc scsi_device vc-mem
dma iscsi_connection mmc_host scsi_disk video4linux
dma_heap iscsi_endpoint net scsi_host vtconsole
drm iscsi_host power_supply sound watchdog
root@raspberrypi:/proc# ls /dev
autofs loop4 ram2 tty17 tty41 tty9 vcsu1
block loop5 ram3 tty18 tty42 ttyAMA0 vcsu2
btrfs-control loop6 ram4 tty19 tty43 ttyprintk vcsu3
bus loop7 ram5 tty2 tty44 ttyS0 vcsu4
cachefiles loop-control ram6 tty20 tty45 uhid vcsu5
cec0 mapper ram7 tty21 tty46 uinput vcsu6
char media0 ram8 tty22 tty47 urandom vhci
console media1 ram9 tty23 tty48 v4l video10
cuse media2 random tty24 tty49 vchiq video11
disk mem rfkill tty25 tty5 vcio video12
dma_heap mmcblk0 serial0 tty26 tty50 vc-mem video13
dri mmcblk0p1 serial1 tty27 tty51 vcs video14
fd mmcblk0p2 shm tty28 tty52 vcs1 video15
full mqueue snd tty29 tty53 vcs2 video16
fuse net stderr tty3 tty54 vcs3 video18
gpiochip0 null stdin tty30 tty55 vcs4 video20
gpiochip1 ppp stdout tty31 tty56 vcs5 video21
gpiomem ptmx tty tty32 tty57 vcs6 video22
hwrng pts tty0 tty33 tty58 vcsa video23
initctl ram0 tty1 tty34 tty59 vcsa1 video31
input ram1 tty10 tty35 tty6 vcsa2 watchdog
kmsg ram10 tty11 tty36 tty60 vcsa3 watchdog0
log ram11 tty12 tty37 tty61 vcsa4 zero
loop0 ram12 tty13 tty38 tty62 vcsa5
loop1 ram13 tty14 tty39 tty63 vcsa6
loop2 ram14 tty15 tty4 tty7 vcsm-cma
loop3 ram15 tty16 tty40 tty8 vcsu
root@raspberrypi:/proc# cd ~
root@raspberrypi:~# ls /sys/class/tty/ttyAMA0
close_delay dev iomem_base line type
closing_wait device iomem_reg_shift port uartclk
console flags io_type power uevent
custom_divisor hci0 irq subsystem xmit_fifo_size
root@raspberrypi:~# cat /sys/class/tty/ttyAMA0/dev
204:64
都希望在class 下產生,SW RD 想要抽象化,希望使用者,可以直接透過class 存取到device就好,不用直接去改到driver code。
這邊以raspberry pi3 為例
$ sudo apt install bc bison crossbuild-essential-armhf flex git libc6-dev libncurses5-dev libssl-dev
$ export ARCH=arm
$ export KERNEL=kernel7
$ export CROSS_COMPILE=arm-linux-gnueabihf-
$ git clone --depth=1 https://github.com/raspberrypi/linux
$ cd linux
$ make bcm2709_defconfig
//main.c
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("Dual BSD/GPL");
extern void sub(void);
static int hello_init(void)
{
printk(KERN_ALERT "driver loaded\n");
sub();
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "driver unloaded\n");
}
module_init(hello_init);
module_exit(hello_exit);
//sub.c
#include <linux/module.h>
#include <linux/init.h>
void sub(void)
{
printk("%s: sub() called\n", __func__);
}
Makefile :
obj-$(CONFIG_HELLO) += hello.o
hello-objs := main.o sub.o
# export PATH=$PATH:/home/user/buildroot/MA35D1_Buildroot/output/host/bin
PWD := $(shell pwd)
KDIR = /home/user/buildroot/MA35D1_Buildroot/output/build/linux-custom
obj-m += mymodule.o
mymodule-objs := main.o sub.o
ARCH ?= arm64
CROSS_COMPILE ?= aarch64-linux-
all drivers:
make -C $(KDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) modules
clean:
make -C $(KDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) clean
下載看…..