當前位置:首頁 » 操作系統 » linux設備驅動模型

linux設備驅動模型

發布時間: 2023-08-24 17:28:27

linux驅動程序開發實例的目錄

前言
第1章 Linux設備驅動程序模型 1
1.1 設備驅動程序基礎 1
1.1.1 驅動程序的概念 1
1.1.2 驅動程序的載入方式 2
1.1.3 編寫可載入模塊 3
1.1.4 帶參數的可載入模塊 5
1.1.5 設備驅動程序的分類 6
1.2 字元設備驅動程序原理 7
1.2.1 file_operations結構 7
1.2.2 使用register_chrdev注冊字元
設備 9
1.2.3 使用cdev_add注冊字元設備 11
1.2.4 字元設備的讀寫 13
1.2.5 ioctl介面 14
1.2.6 seek介面 16
1.2.7 poll介面 18
1.2.8 非同步通知 22
1.3 proc文件系統 24
1.3.1 proc文件系統概述 24
1.3.2 seq_file機制 25
1.3.3 使用proc文件系統 27
1.4 塊設備驅動程序 32
1.4.1 Linux塊設備驅動程序原理 32
1.4.2 簡單的塊設備驅動程序實例 35
1.5 網路設備驅動程序 39
1.5.1 網路設備的特殊性 39
1.5.2 sk_buff結構 40
1.5.3 Linux網路設備驅動程序架構 42
1.5.4 虛擬網路設備驅動程序實例 46
1.6 Linux 2.6設備管理機制 50
1.6.1 kobject和kset 50
1.6.2 sysfs文件系統 51
1.6.3 設備模型層次 52
1.6.4 platform的概念 54
第2章 Linux內核同步機制 58
2.1 鎖機制 58
2.1.1 自旋鎖 58
2.1.2 讀寫鎖 60
2.1.3 RCU 61
2.2 互斥 64
2.2.1 原子操作 64
2.2.2 信號量 65
2.2.3 讀寫信號量 67
2.3 等待隊列 68
2.3.1 等待隊列原理 68
2.3.2 阻塞式I/O實例 68
2.3.3 完成事件 70
2.4 關閉中斷 71
第3章 內存管理與鏈表 72
3.1 物理地址和虛擬地址 72
3.2 內存分配與釋放 72
3.3 IO埠到虛擬地址的映射 73
3.3.1 靜態映射 73
3.3.2 動態映射 75
3.4 內核空間到用戶空間的映射 76
3.4.1 內核空間到用戶空間的地址
映射原理 76
3.4.2 mmap地址映射實例 78
3.5 內核鏈表 80
3.5.1 Linux內核中的鏈表 80
3.5.2 內核鏈表實例 81
第4章 延遲處理 83
4.1 內核線程 83
4.2 軟中斷機制 85
4.2.1 軟中斷原理 85
4.2.2 tasklet 87
4.3 工作隊列 89
4.3.1 工作隊列原理 89
4.3.2 工作隊列實例 91
4.4 內核時間 92
4.4.1 Linux中的時間概念 92
4.4.2 Linux中的延遲 93
4.4.3 內核定時器 93
第5章 簡單設備驅動程序 96
5.1 寄存器訪問 96
5.1.1 S3C6410地址映射 96
5.1.2 S3C6410看門狗驅動程序實例 98
5.1.3 S3C6410蜂鳴器驅動程序實例 102
5.2 電平控制 107
5.2.1 S3C6410 LED驅動程序實例 107
5.2.2 掃描型S3C6410按鍵驅動
程序實例 109
5.3 時序產生 112
5.3.1 時序圖原理 112
5.3.2 AT24C02晶元原理 112
5.3.3 AT24C02驅動程序開發實例 115
5.4 硬中斷處理 123
5.4.1 硬中斷處理原理 123
5.4.2 中斷型S3C6410按鍵驅動
程序實例 127
5.5 Linux I/O埠控制 132
5.5.1 Linux I/O埠讀寫 132
5.5.2 在應用層訪問Linux I/O
埠 133
5.5.3 /dev/port設備 134
第6章 深入Linux內核 135
6.1 嵌入式Linux系統構成 135
6.2 Linux內核導讀 136
6.2.1 Linux內核組成 136
6.2.2 Linux的代碼結構 137
6.2.3 內核Makefile 138
6.2.4 S3C6410硬體初始化 139
6.3 Linux文件系統 141
6.3.1 虛擬文件系統 141
6.3.2 根文件系統 143
6.3.3 文件系統載入 143
6.3.4 ext3文件系統 145
6.4 Flash文件系統 145
6.4.1 MTD設備 145
6.4.2 MTD字元設備 148
6.4.3 MTD塊設備 150
6.4.4 cramfs文件系統 153
6.4.5 JFFS2文件系統 153
6.4.6 YAFFS文件系統 155
6.4.7 文件系統總結 156
6.5 Linux內核移植 156
6.5.1 體系配置 156
6.5.2 添加yaffs2 157
6.5.3 Nand flash驅動程序移植 157
6.5.4 配置啟動參數 159
6.5.5 移植RTC驅動程序 160
6.6 根文件系統製作 162
6.6.1 Busybox 162
6.6.2 shell基礎 165
6.6.3 根文件系統構建實例 166
6.7 udev模型 167
6.7.1 udev模型原理 167
6.7.2 mdev的使用 167
第7章 I2C匯流排驅動程序 169
7.1 Linux的I2C驅動程序架構 169
7.1.1 I2C適配器 169
7.1.2 I2C演算法 170
7.1.3 I2C驅動程序結構 170
7.1.4 I2C從設備 171
7.1.5 i2c-dev設備層 171
7.2 Linux I2C驅動程序開發 174
7.2.1 S3C2410X的I2C控制器 174
7.2.2 S3C2410X的I2C驅動程序
分析 175
7.3 S3C2410的I2C訪問實例 182
7.4 I2C客戶端驅動程序 185
第8章 TTY與串口驅動程序 190
8.1 TTY概念 190
8.2 Linux TTY驅動程序體系 190
8.2.1 TTY驅動程序調用關系 190
8.2.2 TTY驅動程序原理 191
8.3 線路規程 194
8.4 串口驅動程序與TTY 196
8.4.1 串口設備驅動程序原理 196
8.4.2 S3C6410的串口驅動程序
實例 199
8.5 TTY應用層 202
第9章 網路設備驅動程序 205
9.1 DM9000網卡驅動程序
開發 205
9.1.1 DM9000原理 205
9.1.2 DM9000X驅動程序分析 207
9.1.3 DM9000網口驅動程序移植 215
9.2 NFS根文件系統搭建 219
9.2.1 主機配置 219
9.2.2 NFS根文件系統搭建實例 220
9.3 netlink Socket 224
9.3.1 netlink機制 224
9.3.2 netlink應用層編程 228
9.3.3 netlink驅動程序實例 229
第10章 framebuffer驅動程序 232
10.1 Linux framebuffer驅動
程序原理 232
10.1.1 framebuffer核心數據結構 232
10.1.2 framebuffer操作介面 234
10.1.3 framebuffer驅動程序的文件
介面 236
10.1.4 framebuffer驅動程序框架 236
10.2 S3C6410 顯示控制器 238
10.3 S3C6410 LCD驅動程序實例 243
10.4 framebuffer應用層 250
10.5 Qt4界面系統移植 251
第11章 輸入子系統驅動程序 253
11.1 Linux輸入子系統概述 253
11.1.1 input_dev結構 253
11.1.2 輸入事件 255
11.2 input_handler 256
11.2.1 Input Handler層 256
11.2.2 常用的Input Handler 259
11.3 輸入設備應用層 261
11.4 鍵盤輸入設備驅動程序
實例 262
11.5 event介面 267
11.6 觸摸屏驅動程序實例 270
11.6.1 S3C6410觸摸屏控制器 270
11.6.2 S3C6410觸摸屏驅動程序
設計 273
11.7 觸摸屏校準 282
11.7.1 觸摸屏校準原理 282
11.7.2 利用TSLIB庫校準觸摸屏 282
第12章 USB驅動程序 284
12.1 USB體系概述 284
12.1.1 USB系統組成 284
12.1.2 USB主機 284
12.1.3 USB設備邏輯層次 285
12.2 Linux USB驅動程序體系 287
12.2.1 USB總體結構 287
12.2.2 USB設備驅動程序 287
12.2.3 主機控制器驅動程序 288
12.2.4 USB請求塊urb 289
12.2.5 USB請求塊的填充 291
12.3 S3C6410 USB主機控制器
驅動程序 292
12.3.1 USB主機控制器驅動程序
分析 292
12.3.2 S3C6410 USB驅動程序
載入 294
12.4 USB鍵盤設備驅動程序
分析 296
12.5 USB Gadget驅動程序 301
12.5.1 Linux USB Gadget驅動程序 301
12.5.2 Linux USB Gadget驅動程序
實例 302
第13章 音頻設備驅動程序 303
13.1 ALSA音頻體系 303
13.2 ALSA驅動層API 304
13.2.1 音效卡和設備管理 304
13.2.2 PCM API 304
13.2.3 控制與混音API 305
13.2.4 AC97 API 306
13.2.5 SOC層驅動 307
13.3 ALSA驅動程序實例 308
13.3.1 S3C6410的AC97控制
單元 308
13.3.2 S3C6410音效卡電路原理 309
13.3.3 S3C6410的數字音頻介面 310
13.3.4 wm9713的數字音頻介面 313
13.4 ALSA音頻編程介面 316
13.4.1 ALSA PCM介面實例 316
13.4.2 ALSA MIDI介面實例 320
13.4.3 ALSA mixer介面實例 321
13.4.4 ALSA timer介面實例 322
第14章 video4linux2視頻
驅動程序 327
14.1 video4linux2驅動程序
架構 327
14.1.1 video4linux2驅動程序的
注冊 327
14.1.2 v4l2_fops介面 331
14.1.3 常用的結構 332
14.1.4 video4linux2的ioctl函數 333
14.2 S3C6410攝像頭驅動程序
分析 333
14.2.1 電路原理 333
14.2.2 驅動程序分析 334
14.3 video4linux2應用層實例 339
第15章 SD卡驅動程序 346
15.1 Linux SD卡驅動程序體系 346
15.1.1 SD卡電路原理 346
15.1.2 MMC卡驅動程序架構 347
15.1.3 MMC卡驅動程序相關
結構 347
15.1.4 MMC卡塊設備驅動程序 350
15.1.5 SD卡主機控制器介面驅動
程序 356
15.2 S3C6410 SD卡控制器驅動
程序分析 360
15.2.1 電路原理 360
15.2.2 S3C6410 SDHCI驅動
程序原理 360
15.2.3 SD卡的載入實例 364
參考文獻 366

② 如何編譯一個linux下的驅動模塊

linux下編譯運行驅動
嵌入式linux下設備驅動的運行和linux x86 pc下運行設備驅動是類似的,由於手頭沒有嵌入式linux設備,先在vmware上的linux上學習驅動開發。
按照如下方法就可以成功編譯出hello world模塊驅動。
1、首先確定本機linux版本
怎麼查看Linux的內核kernel版本?
'uname'是Linux/unix系統中用來查看系統信息的命令,適用於所有Linux發行版。配合使用'uname'參數可以查看當前伺服器內核運行的各個狀態。
#uname -a
Linux whh 3.5.0-19-generic #30-Ubuntu SMPTue Nov 13 17:49:53 UTC 2012 i686 i686 i686 GNU/Linux

只列印內核版本,以及主要和次要版本:
#uname -r
3.5.0-19-generic

要列印系統的體系架構類型,即的機器是32位還是64位,使用:
#uname -p
i686

/proc/version 文件也包含系統內核信息:
# cat /proc/version
Linux version 3.5.0-19-generic(buildd@aatxe) (gcc version 4.7.2 (Ubuntu/Linaro 4.7.2-2ubuntu1) ) #30-UbuntuSMP Tue Nov 13 17:49:53 UTC 2012

發現自己的機器linux版本是:3.5.0-19-generic
2、下載機器內核對應linux源碼
到下面網站可以下載各個版本linux源碼https://www.kernel.org/
如我的機器3.5.0版本源碼下載地址為:https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.5.tar.bz2
下載完後,找一個路徑解壓,如我解壓到/linux-3.5/
然後很重要的一步是:執行命令uname -r,可以看到Ubuntu的版本信息是3.5.0-19-generic
。進入linux源碼目錄,編輯Makefile,將EXTRAVERSION = 修改為EXTRAVERSION= -19-generic。
這些都是要配置源碼的版本號與系統版本號,如果源碼版本號和系統版本號不一致,在載入模塊的時候會出現如下錯誤:insmod: error inserting 'hello.ko': -1 Invalid mole format。
原因很明確:編譯時用的hello.ko的kenerl 不是我的pc的kenerl版本。

執行命令cp /boot/config-3.5.0-19-generic ./config,覆蓋原有配置文件。
進入linux源碼目錄,執行make menuconfig配置內核,執行make編譯內核。

3、寫一個最簡單的linux驅動代碼hello.c

/*======================================================================
Asimple kernel mole: "hello world"
======================================================================*/
#include <linux/init.h>
#include <linux/mole.h>
MODULE_LICENSE("zeroboundaryBSD/GPL");
static int hello_init(void)
{
printk(KERN_INFO"Hello World enter\n");
return0;
}

static void hello_exit(void)
{
printk(KERN_INFO"Hello World exit\n ");
}

mole_init(hello_init);
mole_exit(hello_exit);

MODULE_AUTHOR("zeroboundary");
MODULE_DESCRIPTION("A simple HelloWorld Mole");
MODULE_ALIAS("a simplestmole");

4、寫一個Makefile對源碼進行編譯
KERN_DIR = /linux-3.5
all:
make-C $(KERN_DIR) M=`pwd` moles
clean:
make-C $(KERN_DIR) M=`pwd` clean

obj-m += hello.o

5、模塊載入卸載測試
insmod hello.ko
rmmod hello.ko

然後dmesg|tail就可以看見結果了

最後,再次編譯驅動程序hello.c得到hello.ko。執行insmod ./hello.ko,即可正確insert模塊。

使用insmod hello.ko 將該Mole加入內核中。在這里需要注意的是要用 su 命令切換到root用戶,否則會顯示如下的錯誤:insmod: error inserting 'hello.ko': -1 Operation not permitted

內核模塊版本信息的命令為modinfo hello.ko
通過lsmod命令可以查看驅動是否成功載入到內核中
通過insmod命令載入剛編譯成功的time.ko模塊後,似乎系統沒有反應,也沒看到列印信息。而事實上,內核模塊的列印信息一般不會列印在終端上。驅動的列印都在內核日誌中,我們可以使用dmesg命令查看內核日誌信息。dmesg|tail

可能還會遇到這種問題insmod: error inserting 'hello.ko': -1 Invalid mole format
用dmesg|tail查看內核日誌詳細錯誤
disagrees about version of symbolmole_layout,詳細看這里。
http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmoles/index.html
在X86上我的辦法是:
make -C/usr/src/linux-headers-3.5.0-19-generic SUBDIRS=$PWD moles

③ Linux網路設備驅動的結構

Linux網路設備驅動程序的體系結構從上到下可以劃分為4層,依次為網路協議介面層、網路設備介面層、提供實際功能的設備驅動功能層以及網路設備與媒介層,這4層的作用如下所示。
1)網路協議介面層向網路層協議提供統一的數據包收發介面,不論上層協議是ARP,還是IP,都通過dev_queue_xmit() 函數發送數據,並通過netif rx ()函數接收數據。這一層的存在使得上層協議獨立於具體的設備。
2)網路設備介面層向協議介面層提供統一的用於描述具體網路設備屬性和操作的結構體net device,該結構體是設備驅動功能層中各函數的容器。實際上,網路設備介面層從宏觀上規劃了具體操作硬體的設備驅動功能層的結構。
3)設備驅動功能層的各函數是網路設備介面層net_device數據結構的具體成員,是驅使網路設備硬體完成相應動作的程序,它通過hard_start_ xmit ()函數啟動發送操作,並通過網路設備上的中斷觸發接收操作。
4)網路設備與媒介層是完成數據包發送和接收的物理實體,包括網路適配器和具體的傳輸媒介,網路適配器被設備驅動功能層中的函數在物理上驅動。對於Linux系統而言,網路設備和媒介都可以是虛擬的。

④ linux驅動程序結構框架及工作原理分別是什麼

一、Linux device driver 的概念x0dx0ax0dx0a系統調用是操作系統內核和應用程序之間的介面,設備驅動程序是操作系統內核和機器硬體之間的介面。設備驅動程序為應用程序屏蔽了硬體的細節,這樣在應用程序看來,硬體設備只是一個設備文件,應用程序可以象操作普通文件一樣對硬體設備進行操作。設備驅動程序是內核的一部分,它完成以下的功能:x0dx0ax0dx0a1、對設備初始化和釋放;x0dx0ax0dx0a2、把數據從內核傳送到硬體和從硬體讀取數據;x0dx0ax0dx0a3、讀取應用程序傳送給設備文件的數據和回送應用程序請求的數據;x0dx0ax0dx0a4、檢測和處理設備出現的錯誤。x0dx0ax0dx0a在Linux操作系統下有三類主要的設備文件類型,一是字元設備,二是塊設備,三是網路設備。字元設備和塊設備的主要區別是:在對字元設備發出讀/寫請求時,實際的硬體I/O一般就緊接著發生了,塊設備則不然,它利用一塊系統內存作緩沖區,當用戶進程對設備請求能滿足用戶的要求,就返回請求的數據,如果不能,就調用請求函數來進行實際的I/O操作。塊設備是主要針對磁碟等慢速設備設計的,以免耗費過多的CPU時間來等待。x0dx0ax0dx0a已經提到,用戶進程是通過設備文件來與實際的硬體打交道。每個設備文件都都有其文件屬性(c/b),表示是字元設備還是塊設備?另外每個文件都有兩個設備號,第一個是主設備號,標識驅動程序,第二個是從設備號,標識使用同一個設備驅動程序的不同的硬體設備,比如有兩個軟盤,就可以用從設備號來區分他們。設備文件的的主設備號必須與設備驅動程序在登記時申請的主設備號一致,否則用戶進程將無法訪問到驅動程序。x0dx0ax0dx0a最後必須提到的是,在用戶進程調用驅動程序時,系統進入核心態,這時不再是搶先式調度。也就是說,系統必須在你的驅動程序的子函數返回後才能進行其他的工作。如果你的驅動程序陷入死循環,不幸的是你只有重新啟動機器了,然後就是漫長的fsck。x0dx0ax0dx0a二、實例剖析x0dx0ax0dx0a我們來寫一個最簡單的字元設備驅動程序。雖然它什麼也不做,但是通過它可以了解Linux的設備驅動程序的工作原理。把下面的C代碼輸入機器,你就會獲得一個真正的設備驅動程序。x0dx0ax0dx0a由於用戶進程是通過設備文件同硬體打交道,對設備文件的操作方式不外乎就是一些系統調用,如 open,read,write,close?, 注意,不是fopen, fread,但是如何把系統調用和驅動程序關聯起來呢?這需要了解一個非常關鍵的數據結構:x0dx0ax0dx0aSTruct file_operatiONs {x0dx0ax0dx0aint (*seek) (struct inode * ,struct file *, off_t ,int);x0dx0ax0dx0aint (*read) (struct inode * ,struct file *, char ,int);x0dx0ax0dx0aint (*write) (struct inode * ,struct file *, off_t ,int);x0dx0ax0dx0aint (*readdir) (struct inode * ,struct file *, struct dirent * ,int);x0dx0ax0dx0aint (*select) (struct inode * ,struct file *, int ,select_table *);x0dx0ax0dx0aint (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);x0dx0ax0dx0aint (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);x0dx0ax0dx0aint (*open) (struct inode * ,struct file *);x0dx0ax0dx0aint (*release) (struct inode * ,struct file *);x0dx0ax0dx0aint (*fsync) (struct inode * ,struct file *);x0dx0ax0dx0aint (*fasync) (struct inode * ,struct file *,int);x0dx0ax0dx0aint (*check_media_change) (struct inode * ,struct file *);x0dx0ax0dx0aint (*revalidate) (dev_t dev);x0dx0ax0dx0a}x0dx0ax0dx0a這個結構的每一個成員的名字都對應著一個系統調用。用戶進程利用系統調用在對設備文件進行諸如read/write操作時,系統調用通過設備文件的主設備號找到相應的設備驅動程序,然後讀取這個數據結構相應的函數指針,接著把控制權交給該函數。這是linux的設備驅動程序工作的基本原理。既然是這樣,則編寫設備驅動程序的主要工作就是編寫子函數,並填充file_operations的各個域。x0dx0ax0dx0a下面就開始寫子程序。x0dx0ax0dx0a#include 基本的類型定義x0dx0ax0dx0a#include 文件系統使用相關的頭文件x0dx0ax0dx0a#include x0dx0ax0dx0a#include x0dx0ax0dx0a#include x0dx0ax0dx0aunsigned int test_major = 0;x0dx0ax0dx0astatic int read_test(struct inode *inode,struct file *file,char *buf,int count)x0dx0ax0dx0a{x0dx0ax0dx0aint left; 用戶空間和內核空間x0dx0ax0dx0aif (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )x0dx0ax0dx0areturn -EFAULT;x0dx0ax0dx0afor(left = count ; left > 0 ; left--)x0dx0ax0dx0a{x0dx0ax0dx0a__put_user(1,buf,1);x0dx0ax0dx0abuf++;x0dx0ax0dx0a}x0dx0ax0dx0areturn count;x0dx0ax0dx0a}x0dx0ax0dx0a這個函數是為read調用准備的。當調用read時,read_test()被調用,它把用戶的緩沖區全部寫1。buf 是read調用的一個參數。它是用戶進程空間的一個地址。但是在read_test被調用時,系統進入核心態。所以不能使用buf這個地址,必須用__put_user(),這是kernel提供的一個函數,用於向用戶傳送數據。另外還有很多類似功能的函數。請參考,在向用戶空間拷貝數據之前,必須驗證buf是否可用。這就用到函數verify_area。為了驗證BUF是否可以用。x0dx0ax0dx0astatic int write_test(struct inode *inode,struct file *file,const char *buf,int count)x0dx0ax0dx0a{x0dx0ax0dx0areturn count;x0dx0ax0dx0a}x0dx0ax0dx0astatic int open_test(struct inode *inode,struct file *file )x0dx0ax0dx0a{x0dx0ax0dx0aMOD_INC_USE_COUNT; 模塊計數加以,表示當前內核有個設備載入內核當中去x0dx0ax0dx0areturn 0;x0dx0ax0dx0a}x0dx0ax0dx0astatic void release_test(struct inode *inode,struct file *file )x0dx0ax0dx0a{x0dx0ax0dx0aMOD_DEC_USE_COUNT;x0dx0ax0dx0a}x0dx0ax0dx0a這幾個函數都是空操作。實際調用發生時什麼也不做,他們僅僅為下面的結構提供函數指針。x0dx0ax0dx0astruct file_operations test_fops = {?x0dx0ax0dx0aread_test,x0dx0ax0dx0awrite_test,x0dx0ax0dx0aopen_test,x0dx0ax0dx0arelease_test,x0dx0ax0dx0a};x0dx0ax0dx0a設備驅動程序的主體可以說是寫好了。現在要把驅動程序嵌入內核。驅動程序可以按照兩種方式編譯。一種是編譯進kernel,另一種是編譯成模塊(moles),如果編譯進內核的話,會增加內核的大小,還要改動內核的源文件,而且不能動態的卸載,不利於調試,所以推薦使用模塊方式。x0dx0ax0dx0aint init_mole(void)x0dx0ax0dx0a{x0dx0ax0dx0aint result;x0dx0ax0dx0aresult = register_chrdev(0, "test", &test_fops); 對設備操作的整個介面x0dx0ax0dx0aif (result < 0) {x0dx0ax0dx0aprintk(KERN_INFO "test: can't get major number\n");x0dx0ax0dx0areturn result;x0dx0ax0dx0a}x0dx0ax0dx0aif (test_major == 0) test_major = result; /* dynamic */x0dx0ax0dx0areturn 0;x0dx0ax0dx0a}x0dx0ax0dx0a在用insmod命令將編譯好的模塊調入內存時,init_mole 函數被調用。在這里,init_mole只做了一件事,就是向系統的字元設備表登記了一個字元設備。register_chrdev需要三個參數,參數一是希望獲得的設備號,如果是零的話,系統將選擇一個沒有被佔用的設備號返回。參數二是設備文件名,參數三用來登記驅動程序實際執行操作的函數的指針。x0dx0ax0dx0a如果登記成功,返回設備的主設備號,不成功,返回一個負值。x0dx0ax0dx0avoid cleanup_mole(void)x0dx0ax0dx0a{x0dx0ax0dx0aunregister_chrdev(test_major,"test");x0dx0ax0dx0a}x0dx0ax0dx0a在用rmmod卸載模塊時,cleanup_mole函數被調用,它釋放字元設備test在系統字元設備表中佔有的表項。x0dx0ax0dx0a一個極其簡單的字元設備可以說寫好了,文件名就叫test.c吧。x0dx0ax0dx0a下面編譯 :x0dx0ax0dx0a$ gcc -O2 -DMODULE -D__KERNEL__ -c test.c _c表示輸出制定名,自動生成.o文件x0dx0ax0dx0a得到文件test.o就是一個設備驅動程序。x0dx0ax0dx0a如果設備驅動程序有多個文件,把每個文件按上面的命令行編譯,然後x0dx0ax0dx0ald ?-r ?file1.o ?file2.o ?-o ?molename。x0dx0ax0dx0a驅動程序已經編譯好了,現在把它安裝到系統中去。x0dx0ax0dx0a$ insmod ?_f ?test.ox0dx0ax0dx0a如果安裝成功,在/proc/devices文件中就可以看到設備test,並可以看到它的主設備號。要卸載的話,運行 :x0dx0ax0dx0a$ rmmod testx0dx0ax0dx0a下一步要創建設備文件。x0dx0ax0dx0amknod /dev/test c major minorx0dx0ax0dx0ac 是指字元設備,major是主設備號,就是在/proc/devices里看到的。x0dx0ax0dx0a用shell命令x0dx0ax0dx0a$ cat /proc/devicesx0dx0ax0dx0a就可以獲得主設備號,可以把上面的命令行加入你的shell script中去。x0dx0ax0dx0aminor是從設備號,設置成0就可以了。x0dx0ax0dx0a我們現在可以通過設備文件來訪問我們的驅動程序。寫一個小小的測試程序。x0dx0ax0dx0a#include x0dx0ax0dx0a#include x0dx0ax0dx0a#include x0dx0ax0dx0a#include x0dx0ax0dx0amain()x0dx0ax0dx0a{x0dx0ax0dx0aint testdev;x0dx0ax0dx0aint i;x0dx0ax0dx0achar buf[10];x0dx0ax0dx0atestdev = open("/dev/test",O_RDWR);x0dx0ax0dx0aif ( testdev == -1 )x0dx0ax0dx0a{x0dx0ax0dx0aprintf("Cann't open file \n");x0dx0ax0dx0aexit(0);x0dx0ax0dx0a}x0dx0ax0dx0aread(testdev,buf,10);x0dx0ax0dx0afor (i = 0; i < 10;i++)x0dx0ax0dx0aprintf("%d\n",buf[i]);x0dx0ax0dx0aclose(testdev);x0dx0ax0dx0a}x0dx0ax0dx0a編譯運行,看看是不是列印出全1 x0dx0ax0dx0a以上只是一個簡單的演示。真正實用的驅動程序要復雜的多,要處理如中斷,DMA,I/O port等問題。這些才是真正的難點。上述給出了一個簡單的字元設備驅動編寫的框架和原理,更為復雜的編寫需要去認真研究LINUX內核的運行機制和具體的設備運行的機制等等。希望大家好好掌握LINUX設備驅動程序編寫的方法。

⑤ 如何使用linux的Documentation來寫驅動

Linux I2C驅動是嵌入式Linux驅動開發人仔裂員經常需要編寫的一種驅動,因為凡是系統中使用到的I2C設備,幾乎都需要編寫相應的I2C驅動去配置和控制它,例如 RTC實時時鍾晶元、音視頻採集晶元、音視頻輸出晶元、EEROM晶元、AD/DA轉換晶元等等。
Linux I2C驅動涉及的知識點還是挺多的,主要分為Linux I2C的匯流排驅動(I2C BUS Driver)和設備驅動(I2C Clients Driver),本文主要關注如何快速地完成一個具體的I2C設備驅動(I2C Clients Driver)。關於Linux I2C驅動的整體架構、核心原理等可以在網上搜索其他相關文章學習。
本文主要參考了Linux內核源碼目錄下的 ./Documentation/i2c/writing-clients 文檔。以手頭的一款視頻採集晶元TVP5158為驅動目標,編寫Linux I2C設備驅動。
1. i2c_driver結構體對象
每一個I2C設備驅動,必須首先創造一個i2c_driver結構體對象,該結構體包含了I2C設備探測和注銷的一些基本方法和信息,示例如下:
static struct i2c_driver tvp5158_i2c_driver = { .driver = { .name = "tvp5158_i2c_driver", }, .attach_adapter = &tvp5158_attach_adapter, .detach_client = &tvp5158_detach_client, .command = NULL, };

其中,name欄位標識本驅動的名稱(不要超過31個字元),attach_adapter和detach_client欄位為函數指針,這兩個函數在I2C設備注冊的時候會自動調用,需要自己實現這兩個函數,後面將詳細講述。
2. i2c_client 結構體對象
上面定義的i2c_driver對象,抽象為一個i2c的驅動模型,提供對i2C設備的探測和注銷方法,而i2c_client結構體則是代表著一個具體的i2c設備,該結構體有一個data指針,可以指向任何私有的設備數據,在復雜點的驅動中可能會用到。示例如下:
struct tvp5158_obj{ struct i2c_client client; int users; // how many users using the driver }; struct tvp5158_obj* g_tvp5158_obj;

其中,users為示例,用戶可以自鄭戚褲己在tvp5158_obj這個結構體裡面添加感興趣的欄位,但是i2c_client欄位不可少。具體用法後面再詳細講。
3. 設備注冊及探測功能
這一步很關鍵,按照標準的要求來寫,則Linux系統會自動調用相關的代碼去探測你的I2C設備,並且添加到系統的I2C設備列表中以供後面訪問。
我們知道,每一個I2C設備晶元,都通過硬體連接設定好了該設備的I2C設備地址。因此,I2C設備的探測一般是靠設備地址來完成的。那麼,首先要在驅動代碼中聲明你要探測的I2C設備地址列表,以及一個宏。示例如下:
static unsigned short normal_i2c[] = { 0xbc >> 1, 0xbe >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD;

normal_i2c 數組包含了你需要探測的I2C設備地址列表,並且必須以I2C_CLIENT_END作為結尾,注意,上述代碼中的0xbc和0xbe是我在硬體上為我的tvp5158分配的地址,硬體上我支持通過跳線將喊簡該地址設置為 0xbc 或者 0xbe,所以把這兩個地址均寫入到探測列表中,讓系統進行探測。如果你的I2C設備的地址是固定的,那麼,這里可以只寫你自己的I2C設備地址,注意必須向右移位1。
宏 I2C_CLIENT_INSMOD 的作用網上有許多文章進行了詳細的講解,這里我就不詳細描述了,記得加上就行,我們重點關注實現。
下一步就應該編寫第1步中的兩個回調函數,一個用於注冊設備,一個用於注銷設備。探測函數示例如下:
static int tvp5158_attach_adapter(struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, &tvp5158_detect_client); }

這個回調函數系統會自動調用,我們只需要按照上述代碼形式寫好就行,這里調用了系統的I2C設備探測函數,i2c_probe(),第三個參數為具體的設備探測回調函數,系統會在探測設備的時候調用這個函數,需要自己實現。示例如下:
static int tvp5158_detect_client(struct i2c_adapter *adapter,int address,int kind) { struct tvp5158_obj *pObj; int err = 0; printk(KERN_INFO "I2C: tvp5158_detect_client at address %x ...\n", address); if( g_tvp5158_obj != NULL ) { //already allocated,inc user count, and return the allocated handle g_tvp5158_obj->users++; return 0; } /* alloc obj */ pObj = kmalloc(sizeof(struct tvp5158_obj), GFP_KERNEL); if (pObj==0){ return -ENOMEM; } memset(pObj, 0, sizeof(struct tvp5158_obj)); pObj->client.addr = address; pObj->client.adapter = adapter; pObj->client.driver = &tvp5158_i2c_driver; pObj->client.flags = I2C_CLIENT_ALLOW_USE; pObj->users++; /* attach i2c client to sys i2c clients list */ if((err = i2c_attach_client(&pObj->client))){ printk( KERN_ERR "I2C: ERROR: i2c_attach_client fail! address=%x\n",address); return err; } // store the pObj g_tvp5158_obj = pObj; printk( KERN_ERR "I2C: i2c_attach_client ok! address=%x\n",address); return 0; }

到此為止,探測並且注冊設備的代碼已經完成,以後對該 I2C 設備的訪問均可以通過 g_tvp5158_obj 這個全局的指針進行了。

⑥ 6. Linux-LCD 驅動程序概述

入局:應用程序是如何操控LCD顯示器的?
      我們知道應用程序的調用介面,無非 open/read/write ...然後通過驅動程序最終作用到硬體設備上。以字元設備為例,對於驅動的開發者,實現了應用程序調用的驅動層中與之相匹配的 drv_open/drv_read/drv_write 函數,為應用層序提供了操作實際硬體設備的通道。那麼,對於LCD驅動程序又是如何?先來了解下兩個非常重要的概念。

      LCD控制器的功能是控制驅動信號,進而驅動LCD。用戶只需要通過讀寫一系列的寄存器,完成配置和顯示驅動。在驅動LCD設計的過程中首要的是配置LCD控制器,而在配置LCD控制器中最重要的一步則是幀緩沖區(Frame Buffer)的指定。用戶所要顯示的內容皆是從緩沖區中讀出,從而顯示到屏幕上的。幀緩沖區的大小由屏幕的解析度和顯示色彩數決定。驅動幀緩沖的實現是整個驅動開發過程的重點。
      幀緩沖區是出現在Linux 2.2.xx及以後版本內核當中的一種驅動程序介面,這種介面將顯示設備抽象為幀緩沖區設備區。幀緩沖區為圖像硬體設備提供了一種抽象化處理,它代表了一些視頻硬體設備,允許應用軟體通過定義明確的界面來訪問圖像硬體設備。這樣軟體無須了解任何涉及硬體底層驅動的東西(如硬體寄存器)。它允許上層應用程序在圖形模式下直接對顯示緩沖區進行讀寫和I/O控制等操作。通過專門的設備節點可對該設備進行訪問,如/dev/fb*。用戶可以將它看成是顯示內存的一個映像,將其映射到進程地址空間之後,就可以進行讀寫操作,而讀寫操作可以反映到LCD。

      幀緩沖(Frame Buffer)是Linux為顯示設備提供的一個介面,把顯存抽象後的一種設備,允許上層應用程序在圖形模式下直接對顯示緩沖區進行讀寫操作。用戶不必關心物理顯存的位置、換頁機制等等具體細節,這些都是由Frame Buffer設備驅動來完成的。幀緩沖設備屬於字元設備。
      Linux系統Frame Buffer本質上只是提供了對圖形設備的硬體抽象,在開發者看來,Frame Buffer是一塊顯示緩存,向顯示緩存中寫入特定格式的數據就意味著向屏幕輸出內容。

由於有了frambuffer的抽象,使得應用程序通過定義好的介面就可以訪問硬體。所以應用程序不需要考慮底層的(寄存器級)的操作。應用程序對設備文件的訪問一般在/dev目錄,如 /dev/fb*。

內核中的frambuffer在: drivers/video/fbmem.c (fb: frame buffer)

(1) 創建字元設備"fb", FB_MAJOR=29,主設備號為29。
(2)創建類,但並沒有創建設備節點,因為需要注冊了LCD驅動後,才會有設備節點;

2.1 fb_open函數如下:

(1) registered_fb[fbidx] 這個數組也是fb_info結構體,其中fbidx等於次設備號id,顯然這個數組就是保存我們各個lcd驅動的信息;

2.2 fb_read函數如下:

從.open和.read函數中可以發現,都依賴於fb_info幀緩沖信息結構體,它從registered_fb[fbidx]數組中得到,這個數組保存我們各個lcd驅動的信息。由此可見,fbmem.c提供的都是些抽象出來的東西,最終都得依賴registered_fb這個數組。

這個register_framebuffer()除了注冊fb_info,還創建了設備節點。

以s3c2410fb.c為例,分析驅動的實現。

既然是匯流排設備驅動模型,那我們關心的是它的probe函數。

看到這里驅動的寫法也大致清晰:



附:
LCD的顯示過程與時序:
      1.顯示從屏幕左上角第一行的第一個點開始,一個點一個點地在LCD上顯示,點與點之間的時間間隔為VCLK(像素時鍾信號);當顯示到屏幕的最右邊就結束這一行(Line),這一行的顯示對應時序圖上的HSYNC(水平同步信號)
      2. 接下來顯示指針又回到屏幕的左邊從第二行開始顯示,顯示指針針在從第一行的右邊回到第二行的左邊是需要一定的時間的,我們稱之為行切換。
      3. 以此類推,顯示指針就這樣一行一行的顯示至矩形的右下角才把一幅圖像(幀:frame)顯示完成,這一幀的顯示時間在時序圖上表示為VSYNC(垂直同步信號)。

參考:
https://sites.google.com/a/hongdy.org/www/linux/kernel/lcddriver

⑦ 求教怎麼學習linux內核驅動

1.首先要了解為什麼要學習內核?下圖已表明,如果要從事驅動開發或系統研究,就要學習內核。

2.內核的知識就像下面的繩結一樣,一環扣一環,我們要解開它們,就必須要先找到線頭也就是內核中的函數介面。初學階段,我們一般不深入的研究內核代碼,會使用內核的介面函數就不錯了。

3.下面提供了如何學習這些內核函數的方法,就像解繩子一樣

4.學習內核的四步法則,思維導圖的設計尤為重要,這也是能否學習好內核的關鍵

5.語言基礎也需要扎實,所以需要把C語言鞏固鞏固

熱點內容
52好壓壓縮 發布:2025-03-07 06:04:47 瀏覽:68
相鄰演算法 發布:2025-03-07 06:01:51 瀏覽:581
編譯器中 發布:2025-03-07 06:01:44 瀏覽:481
電視現在什麼配置好 發布:2025-03-07 06:01:06 瀏覽:625
安卓內存很大為什麼還是卡 發布:2025-03-07 05:43:53 瀏覽:534
什麼配置的車厲害 發布:2025-03-07 05:43:06 瀏覽:456
魅族應用加密 發布:2025-03-07 05:41:51 瀏覽:654
c盤windows文件夾多大 發布:2025-03-07 05:35:58 瀏覽:616
長江存儲凈資產 發布:2025-03-07 05:35:50 瀏覽:219
gridview載入資料庫 發布:2025-03-07 05:20:31 瀏覽:372