Linux驅動入門——編寫第一個驅動

目錄

前言

驅動入門知識

1.APP 打開的文件在內核中如何表示

2.打開字符設備節點時,內核中也有對應的 struct file

編寫 Hello 驅動程序步驟

1.流程介紹

2.驅動代碼:

3.應用層代碼:

4.本驅動程序的 Makefile 內容:

5.上機實驗:


前言

在編譯驅動程序之前要先編譯內核,原因有三點:

  • 驅動程序要用到內核文件
  • 編譯驅動時用的內核、開發板上運行到內核,要一致
  • 更換板子上的內核后,板子上的其他驅動也要更換

編譯內核步驟看我之前寫過的文章,編譯替換內核_設備樹_驅動_IMX6ULL-CSDN博客

驅動入門知識

1.首先我們通常都是在Linux的終端上打開一個可執行文件,然后可執行文件就會執行程序。那么這個可執行文件做了什么呢?

2.可執行文件先是在應用層讀取程序,其中會有很多庫函數,庫函數是屬于內核之中。而內核又會往下調用驅動層程序。最終驅動層控制具體硬件。

  • 其實應用程序到庫是比較容易理解的,比如我們剛學習C語言的時候,使用了printf,scanf等等這些函數。而這些函數就在庫中。
  • 庫可以和系統內核相連接,具體怎么實現的我也不太清楚。
  • 我們寫了一個驅動程序,就需要告訴內核,這個過程叫做注冊。我們注冊了驅動之后,內核里面就會有這個驅動程序的信息,然后上層應用就可以調用。

3.所以我們只需要知道,咱們需要編寫兩個程序,一個是驅動層的,一個是應用層的,最后驅動層需要注冊進入內核,應用層才能夠使用。其他的先不要管。

4.我們在應用層調用read函數,對應驅動層的read函數。write函數和write函數對應。open函數和open函數對應。close函數和release函數對應(這個為什么不一樣我也不清楚)。

5.我們對Linux 應用程序對驅動程序的調用流程有一個簡單了解之后,我得知道整個程序編寫流程應該怎么做。至于流程為什么是這樣的,我們記住即可。因為這些都是人規定的,如果之后學的深了再進行深究也不遲,現在我們主要是入門? ? ?

1.APP 打開的文件在內核中如何表示

APP 打開文件時,可以得到一個整數,這個整數被稱為文件句柄。對于 APP 的每一個文件句柄,在內核里面都有一個“struct file”與之對應。

我們使用 open 打開文件時,傳入的 flags、mode 等參數會被記錄在內核中對應的 struct file 結構體里(f_flags、f_mode):

int open(const char *pathname, int flags, mode_t mode);

去讀寫文件時,文件的當前偏移地址也會保存在 struct file 結構體的 f_pos 成員里。

2.打開字符設備節點時,內核中也有對應的 struct file

注意這個結構體中的結構體:struct file_operations *f_op,這是由驅動程序提供的。

結構體 struct file_operations 的定義如下:

編寫 Hello 驅動程序步驟

主要為一下七個步驟:

  1. 確定主設備號,也可以讓內核分配
  2. 定義自己的 file_operations 結構體
  3. 實現對應的 drv_open/drv read/drv write 等函數,填入 file operations 結構體
  4. 把 file_operations 結構體告訴內核: register_chrdev
  5. 誰來注冊驅動程序啊? 得有一個入口函數:安裝驅動程序時,就會去調用這個入口函數
  6. 有入口函數就應該有出口函數: 卸載驅動程序時,出口函數調用unregister_chrdev
  7. 其他完善:提供設備信息,自動創建設備節點: class_create,device_create

1.流程介紹

<1>我們首先需要編寫一個file_operations類型的結構體,這個結構體用于管理驅動程序。之后我們將驅動程序注冊進入內核之后,我們在應用層調用這個驅動,那么就可以直接通過這個結構體來操作驅動中的open,write,read等函數。

<2>實現對應的 drv_open/drv_read/drv_write 等函數,填入 file_operations 結構體。這樣我們在應用層調用open,write,read等函數,就是調用這個驅動了。

這個時候有人可能會問了,有這么多個驅動,我怎么知道open對應的是哪一個驅動?很簡單,咱們在寫應用層程序的時候,是不是第一個參數是需要傳入一個設備號。系統根據這個設備號來判斷是調用的哪一個驅動。

<3>把 file_operations 結構體告訴內核: register_chrdev。我們寫了一個驅動,但是內核是不知道的。那么怎么辦呢?我們就去注冊他,內核就明白,有了這個驅動,然后給他分配一個設備號。之后應用層就可以根據這個設備號來調用驅動層了。

<4> 這個時候,有人就有疑問了,誰來注冊這個結構體?于是我們需要一個入口函數來進行注冊,安裝驅動程序時,就會去調用這個入口函數。

<5>有入口函數就應該有出口函數:卸載驅動程序時,出口函數調用unregister_chrdev。

<6>最后需要加入GPL協議。因為Linux是遵頊GPL協議的,所以你如果需要使用Linux其他的驅動層函數,就必須遵頊GPL協議,強制要求開源代碼。根據這個協議,你可以要求所有使用Linux的廠商提供驅動層源代碼,同時別人也可以要求你公開你的驅動層代碼,這個是相互的。不過很多廠商為了規避這個協議,驅動源代碼很簡單,復雜的東西放在應用層。至于還有一個作者名字的添加,隨便寫不寫。? ? ? ? ? ? ? ? ? ? ? ? ? ?

2.驅動代碼:

hello_drv.c

#include <linux/module.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>/* 1. 確定主設備號                                                                 */
static int major = 0;
static char kernel_buf[1024];
static struct class *hello_class;#define MIN(a, b) (a < b ? a : b)/* 3. 實現對應的open/read/write等函數,填入file_operations結構體                   */
static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_to_user(buf, kernel_buf, MIN(1024, size));return MIN(1024, size);
}static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_from_user(kernel_buf, buf, MIN(1024, size));return MIN(1024, size);
}static int hello_drv_open (struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static int hello_drv_close (struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 2. 定義自己的file_operations結構體                                              */
static struct file_operations hello_drv = {.owner	 = THIS_MODULE,.open    = hello_drv_open,.read    = hello_drv_read,.write   = hello_drv_write,.release = hello_drv_close,
};/* 4. 把file_operations結構體告訴內核:注冊驅動程序                                */
/* 5. 誰來注冊驅動程序啊?得有一個入口函數:安裝驅動程序時,就會去調用這個入口函數 */
static int __init hello_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "hello", &hello_drv);  /* /dev/hello */hello_class = class_create(THIS_MODULE, "hello_class");err = PTR_ERR(hello_class);if (IS_ERR(hello_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "hello");return -1;}device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */return 0;
}/* 6. 有入口函數就應該有出口函數:卸載驅動程序時,就會去調用這個出口函數           */
static void __exit hello_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(hello_class, MKDEV(major, 0));class_destroy(hello_class);unregister_chrdev(major, "hello");
}/* 7. 其他完善:提供設備信息,自動創建設備節點                                     */module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE("GPL");

3.應用層代碼:

hello_drv_test.c


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>/** ./hello_drv_test -w abc* ./hello_drv_test -r*/
int main(int argc, char **argv)
{int fd;char buf[1024];int len;/* 1. 判斷參數 */if (argc < 2) {printf("Usage: %s -w <string>\n", argv[0]);printf("       %s -r\n", argv[0]);return -1;}/* 2. 打開文件 */fd = open("/dev/hello", O_RDWR);if (fd == -1){printf("can not open file /dev/hello\n");return -1;}/* 3. 寫文件或讀文件 */if ((0 == strcmp(argv[1], "-w")) && (argc == 3)){len = strlen(argv[2]) + 1;len = len < 1024 ? len : 1024;write(fd, argv[2], len);}else{len = read(fd, buf, 1024);		buf[1023] = '\0';printf("APP read : %s\n", buf);}close(fd);return 0;
}

怎么把.c 文件編譯為驅動程序.ko?

這要借助內核的頂層 Makefile,先設置好交叉編譯工具鏈,編譯好你的板子所用的內核,然后修改 Makefile 指定內核源碼路徑,最后即可執行 make 命令編譯驅動程序和測試程序。

4.本驅動程序的 Makefile 內容:

KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88all:make -C $(KERN_DIR) M=`pwd` modules$(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.cclean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f hello_drv_testobj-m   += hello_drv.o

5.上機實驗:

執行 make 命令編譯驅動程序和測試程序

啟動單板后,可以通過 NFS 掛載 Ubuntu 的某個目錄,訪問該目錄中的程序。

打開內核打印:echo "7 4 1 7" > /proc/sys/kernel/printk

關閉內核打印:echo 0 ? ? ? 4 ? ? ? 0 ? ? ?7 ?> /proc/sys/kernel/printk

insmod 就是install module的縮寫(載入模塊)

insmod hello_drv.ko裝載驅動

ls /dev/hello -l // 驅動程序會生成設備節點?驅動程序會生成設備節點

lsmod 確認驅動已經安裝

我們知道驅動已經安裝好了,那么我們需要知道這個驅動的設備號

cat /proc/devices,查看當前已經被使用掉的設備號

驅動名字與我們在驅動層使用register_chrdev()函數的第二個參數有關

./hello_drv_test // 查看測試程序的用法

./hello_drv_test -w zglnb?// 往驅動程序中寫入字符串

./hello_drv_test -r?// 從驅動程序中讀出字符串

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/214624.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214624.shtml
英文地址,請注明出處:http://en.pswp.cn/news/214624.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

4fiddler抓包工具的使用

一、定義 1.1 抓包的定義 說明&#xff1a;客戶端向服務器發送請求以及服務器響應客戶端的請求&#xff0c;都是以數據包來傳遞的。 抓包(packet capture)&#xff1a;通過工具攔截客戶端與服務器交互的數據包 1.2 fiddler的介紹 Fiddler是一個http協議調試代理工具&#…

Java程序設計基礎 - 課程概述

文章目錄 一、程序員最具共性的心理特征二、Java開發工程師的崗位要求(一)素質和職業道德需求(二)崗位能力需求統計三、針對Java工程師崗位需求的課程目標(一)熟練掌握Java編程語言,掌握編程技能(二)精通使用集成開發工具Eclipse或IntelliJ IDEA(三)需要將“用戶體驗…

市場全局復盤 20231208

一、板塊成交額排名&#xff1a; 資金流入前三個板塊K 線&#xff1a; 行業成交額排名&#xff1a; 個股資金流入排名&#xff1a; select 成交額排名 ,近日指標提示 ,短線主題 ,漲停分析,CODE,名稱,DDE大單凈量,現價,量比,連板天,周漲停,月漲停,年漲停天,連漲天,…

【每日一題】—— B. StORage room(Codeforces Round 912 (Div. 2))(位操作符)

&#x1f30f;博客主頁&#xff1a;PH_modest的博客主頁 &#x1f6a9;當前專欄&#xff1a;每日一題 &#x1f48c;其他專欄&#xff1a; &#x1f534; 每日反芻 &#x1f7e1; C跬步積累 &#x1f7e2; C語言跬步積累 &#x1f308;座右銘&#xff1a;廣積糧&#xff0c;緩稱…

工業傳感器

工業傳感器 電子元器件百科 文章目錄 工業傳感器前言一、工業傳感器是什么二、工業傳感器的類別三、工業傳感器的應用實例四、工業傳感器的作用原理總結前言 工業傳感器的應用可以幫助提高工業過程的效率、安全性和可靠性,實現工業自動化和智能化。 一、工業傳感器是什么 工…

使用阿里云國際CDN加速后網站無法訪問的排查步驟

使用阿里云國際CDN加速后網站無法訪問的排查步驟&#xff0c;下面是一些常見的問題&#xff0c;以&#xff1a;www.c.9he.com為例&#xff0c;如果解決不了來信服務器廠商解決。 檢查CDN訪問異常是CDN節點的問題還是源站問題 如果是源站訪問異常&#xff0c;請直接排查源站服務…

Faster R-CNN

Faster R-CNN是作者Ross Girshick繼Fast R-CNN后的又一力作。同樣使用VGG16作推理速度在GPU上達到5fps(包括候選區域的生成)&#xff0c;準確率為網絡的backbone&#xff0c;也有進一步的提升。在2015年的ILSVRC以及COCO競賽中獲得多個項目的第一名。 算法流程 右邊這部分和Fa…

算法Day27 身材管理(三維背包)

身材管理&#xff08;三維背包&#xff09; Description Input Output Sample 代碼 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt(); // 輸入n的值int money sca…

KaiOS 運營商相關文件operator_variant_manager.js代碼功能和調試

gaia/apps/system/js/operator_variant_manager.js at master mozilla-b2g/gaia GitHub js文件接口功能 No 接口/常量 功能 1 OperatorVariantManager var OperatorVariantManager function(core) 2 OperatorVariantManager.IMPORTS OperatorVariantManager.I…

搜集怎么繪制三維曲線和曲面?

1、針對函數對象是單一變量、兩個函數的情況。用plot3函數&#xff1b;&#xff08;三維曲線&#xff09; 看一下matlab官方的例子&#xff1a; t 0:pi/50:10*pi; st sin(t); ct cos(t); plot3(st,ct,t) 繪制出來的曲線&#xff1a; 幾個比較關鍵的點&#xff1a; &…

【Marp】基于Markdown-Marp快速制作PPT

【Marp】基于Markdown-Marp快速制作PPT 文章目錄 【Marp】基于Markdown-Marp快速制作PPT零、參考資料一、Marp基本語法&#xff08;創建分頁&#xff0c;排版圖片&#xff0c;更換主題&#xff0c;Marp擴展指令修改樣式&#xff09;1、創建新的PPT頁面2、插入圖片 & 排版圖…

小功能實現(二十)分類統計,Map取值自增

前言 從Map取值&#xff0c;根據獲取的類型進行統計&#xff0c;若有則1&#xff0c;若無則新增新的分類純屬靈機一動 步驟 for (String str : stringList) {int counttypeMap.getOrDefault(str,0);typeMap.put(resultList[2],count); }

解決刪除文件后 WSL2 磁盤空間不釋放的問題

查看 Linux distributions 打開 PowerShell 并執行如下命令&#xff1a; wsl -l -v 搜索并找到 ext4.vhdx 文件 我的 ext4.vhdx 文件如下&#xff1a; C:\Users\xxx\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc\LocalState\ext4.vhdx 由于…

vue中yarn install超時問題

囚籠中的網絡固然可以穩定局勢&#xff0c;不讓猴子們得以隨時醒悟&#xff01;給你吃的你就好好吃&#xff0c;不要有其他的翻然醒悟的時刻。無論如何&#xff0c;愚蠢的活著也是一種幸福&#xff0c;聽著那些耐心尋味的統計幸福指數&#xff0c;我們不由的幸福的一批。。 最…

軟件開發流程分析

軟件開發流程分析 相關概念1 原型設計2 產品設計3 交互設計4 代碼實現詳細步驟 相關概念 前端&#xff1a;自研API&#xff0c;調用第三放API 后端&#xff1a;自研API&#xff0c;第三方API 數據庫&#xff1a;Mysql&#xff0c;數據采集&#xff0c;數據遷移 服務器&#xf…

算法與數據結構--最短路徑Dijkstra算法

題目&#xff1a; 算法與數據結構實驗題 10.20 迷路 ★實驗任務 學長經常迷路&#xff0c;現在他又遇到問題了&#xff0c;需要求救。 假設他有一張地圖&#xff0c;上面有N個點&#xff0c;M條路&#xff0c;他現在在編號為S的地方&#xff0c;想要去編號為E的地方&#x…

Linux中的幾個重要指令

關於 Process 處理的指令 1. ps ps 是用來顯示目前你的 process 或系統 processes 的狀況。 以下列出比較常用的參數: 其選項說明如下: -a 列出包括其他 users 的 process 狀況。 -u 顯示 user - oriented 的 process 狀況 。 -x 顯示包括沒有 terminal 控制的 process 狀…

程序員養生指南。。。

【關注微信公眾號&#xff1a;跟強哥學SQL&#xff0c;回復“筆試”免費領取大廠SQL筆試題。】 作為一個程序員&#xff0c;確實需要特別關注健康問題。長時間的熬夜加班、久坐不動等工作習慣可能會導致身體亞健康狀態。以下是一些養生延壽的建議&#xff1a; 1. 定期運動&…

數據結構:第13關:查找兩個單詞鏈表共同后綴的起始結點

任務描述編程要求 輸入輸出測試說明來源 任務描述 本關任務&#xff1a;假定采用帶頭結點的單鏈表保存單詞&#xff0c;當兩個單詞有相同的后綴時&#xff0c;則可共享相同的后綴空間。 例如&#xff0c;“loading”和“being”的存儲映像如下圖所示&#xff1a; 設str1和str2…

離線環境下安裝python庫(推薦pip download)

離線環境下安裝python庫&#xff08;推薦pip download&#xff09; 目錄 1.需求 2.失敗操作&#xff08;注意&#xff09; 3.成功操作 4.其它參考 1.需求 機器部署web系統環境后&#xff0c;就不可再次聯網&#xff0c;所以升級python web后端&#xff0c;需要離線安裝pyt…