嵌入式Linux驅動開發基礎-1 hello驅動

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

1.1

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

? ? ? ? 當我們使用 open 打開文件時,傳入的 flags mode 等參數會被記 錄在內核中對應的 struct file 結構體里 (f_flags f_mode)
int open(const char *pathname, int flags, mode_t mode);
例如: fd = open(argv[1], O_RDWR);
???????讀寫文件時,文件的當前偏移地址也會保存在 struct file 結構體的 f_pos 成員里。

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

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

????????結構體 struct file_operations 的定義如下:包含了open,write,read等文件函數。

2:如何編寫驅動程序

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

3:代碼編寫

參考 driver/char 中的程序,包含頭文件,寫框架,傳輸數據:
? 驅動中實現 open, read, write, release APP 調用這些函數時,都打印內核信息
? APP 調用 write 函數時,傳入的數據保存在驅動中
? APP 調用 read 函數時,把驅動中保存的數據返回給 APP

3.1:主要代碼分析

3.1.1:確定主設備號。

????????/* 1. 確定主設備號*/

static int major = 0;? ? ? ?

????????//后續在入口函數中,使用下列代碼:注冊函數進行分配

major = register_chrdev(0, "hello", &hello_drv);

3.1.2:定義自己的 file_operations 結構體

/* 2. 定義自己的file_operations結構體 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?*/

static struct file_operations hello_drv = {

? ? .owner = THIS_MODULE,? ? //必須存在

? ? .open = hello_drv_open,? ? ?//hello驅動程序的open函數,后續我們需要自己實現這個函數

? ? .read = hello_drv_read,? ??//hello驅動程序的read函數,后續我們需要自己實現這個函數

? ? .write = hello_drv_write,????//hello驅動程序的write函數,后續我們需要自己實現這個函數?

? ? .release = hello_drv_close,?//hello驅動程序的clos函數,后續我們需要自己實現這個函數

};

3.1.3:實現對應的 drv_open/drv_read/drv_write 等函數,??

注:我們現在編寫的函數屬于驅動函數,那么我們在串口掉用這些函數時,

????????串口那邊,我們屬于APP

? ? ? ? 而這些驅動屬于 “內核”

????????我們串口調用read函數? len = read(fd, buf, 1024);時,我們是需要把kernel_buf的數據讀到buf中。所以在這read代碼中,我們使用? err = copy_to_user(buf, kernel_buf, MIN(1024, size));這行代碼,將kernel_buf寫入到buf中。

????????我們串口調用write函數?write(fd, argv[2], len);時,是把argv[2]的數據寫入到kernel_buf中。所以在這write代碼中,我們使用? err = copy_from_user(kernel_buf, buf, MIN(1024, size));這行代碼,將buf也就是argv[2]寫入到kernel_buf中。

? ? ? ? open函數和close函數只是打印一下內核信息,不做數據處理。

/* 3. 實現對應的open/read/write等函數,填入file_operations結構體 ? ? ? ? ? ? ? ? ? */

static char kernel_buf[1024];

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;

}

3.1.4:把 file_operations 結構體告訴內核:register_chrdev

3.1.5. 誰來注冊驅動程序啊?得有一個入口函數:?

? ? ? ? 3.4和3.5可同時在入口函數中實現,安裝驅動程序時,系統去調用這個入口函數,這是直接在入口函數中直接將結構體告訴內核

static int __init hello_init(void)

{

? ? int err;

? ? printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

//3.4也就是這行代碼。在入口函數中進行 注冊驅動程序

? ? 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;

}

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

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");

}

3.1.7:其他完善:提供設備信息,自動創建設備節點:class_create, device_create

/* 7. 其他完善:提供設備信息,自動創建設備節點 */

module_init(hello_init);

module_exit(hello_exit);

MODULE_LICENSE("GPL");

3.2:全部代碼

3.2.1: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.2.2 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;
}

3.3:測試

./hello_drv_test -w www.100ask.net // 把字符串“www.100ask.net”發給驅動程序
./hello_drv_test -r // 把驅動中保存的字符串讀回

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

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

相關文章

目標跟蹤存在問題以及解決方案

3D 跟蹤 一、數據特性引發的跟蹤挑戰 1. 點云稀疏性與遠距離特征缺失 問題表現&#xff1a; 激光雷達點云密度隨距離平方衰減&#xff08;如 100 米外車輛點云數不足近距離的 1/10&#xff09;&#xff0c;導致遠距離目標幾何特征&#xff08;如車輪、車頂輪廓&#xff09;不…

JavaSE-JDK安裝

目錄 一.在官網下載安裝包 二.安裝JDK 三.檢測JDK是否安裝成功 四.配置系統環境變量 一.在官網下載安裝包 Oracle官網https://www.oracle.com/cn/java/technologies/downloads/ 二.安裝JDK 1.首先在C盤以為的其他盤中創建一個自己可以找到的存放JDK路徑&#xff1a; 2.雙擊下…

使用docker搭建redis主從架構,一主2從

使用Docker搭建Redis主從架構&#xff08;一主兩從&#xff09; Redis主從架構是提高系統可用性和讀取性能的重要方案&#xff0c;通過Docker可以快速搭建該架構。下面將詳細介紹搭建步驟。 架構設計 我們將搭建包含以下組件的架構&#xff1a; 1個主節點&#xff08;Maste…

機器學習3——參數估計之極大似然估計

參數估計 問題背景&#xff1a; P ( ω i ∣ x ) p ( x ∣ ω i ) P ( ω i ) p ( x ) p ( x ) ∑ j 1 c p ( x ∣ ω j ) P ( ω j ) \begin{aligned} & P\left(\omega_i \mid \mathbf{x}\right)\frac{p\left(\mathbf{x} \mid \omega_i\right) P\left(\omega_i\right)…

Spring AOP Pointcut 表達式的語法是怎樣的?(execution(...) 是最常用的,還有哪些

Pointcut 表達式是 AOP 的核心&#xff0c;我將詳細解析最常用的 execution 表達式&#xff0c;并介紹其他幾種同樣非常有用的表達式。 1. execution 指示符 (最常用&#xff0c;最強大) execution 用于匹配方法的執行&#xff08;Join Point&#xff09;。它的語法結構最為完…

基于 SpringBoot+Vue 的臺球廳管理系統的設計與實現(畢業論文)

基于 SpringBootVue 的臺球廳管理系統的設計與實現&#xff08;模板&#xff09;[三號宋體加粗&#xff0c;居中] 摘 要[首行縮進2字符&#xff0c;五號黑體加粗]&#xff1a;摘要內容[五號楷體]本文所提出的基于J2EE/EJB標準的電子化采購平臺及其CRM組件綜合解決方案&#xf…

運營醫療信息化建設的思路

醫療機構加強運營管理&#xff0c;必須依賴強有力的醫院信息系統。信息化很重要&#xff0c;但不能為了信息化而信息化。運營信息化必須有明確的建設目標。 運營信息化建設的目標&#xff0c;包括幾個方面&#xff1a; 1.實時反映業務&#xff1b; 2.體現內控思維&#xff1b…

6.24_JAVA_微服務day07_RabbitMQ高級

1、 RabbitListener(queuesToDeclare/*此處是固定寫法&#xff0c;只能寫這個玩意兒&#xff0c;因為這里是庫里的方法*/ Queue(name "lazy.queue",//如果不存在就創建lazy.queue隊列durable "true",//把耐用打開arguments Argument(name "x-que…

Python打卡:Day38

知識點回顧&#xff1a; Dataset類的__getitem__和__len__方法&#xff08;本質是python的特殊方法&#xff09;Dataloader類minist手寫數據集的了解 浙大疏錦行

質量管理五大核心工具之SPC

SPC&#xff08;Statistical Process Control&#xff0c;統計過程控制&#xff09;是一種基于統計學的質量控制方法&#xff0c;旨在通過監控和分析生產過程數據&#xff0c;識別異常波動并消除異常因素&#xff0c;從而確保過程穩定受控&#xff0c;提升產品質量一致性145。以…

【世紀龍科技】新能源汽車VR虛擬體驗展示館-解鎖認知新維度

解鎖新能源汽車深度認知新維度&#xff1a;沉浸式 VR 虛擬體驗展示館 在科技不斷突破邊界的當下&#xff0c;人們對新能源汽車的探索渴望愈發強烈。無論是希望深入了解行業發展脈絡的求知者&#xff0c;還是想要直觀掌握汽車技術原理的學習者&#xff0c;傳統的展示方式似乎總…

oracle基礎審計管理

Oracle數據庫審計功能詳解(簡單易懂!) 更新時間&#xff1a;2024年01月30日 16:21:27 作者&#xff1a;前程的前程也迷茫 Oracle審計查詢是一項重要的任務,可以幫助DBA更好的管理Oracle數據庫,下面這篇文章主要給大家介紹了關于Oracle數據庫審計功能的相關資料,文中通過代碼介紹…

Day44 預訓練模型

目錄 一、預訓練的概念 二、常見的分類預訓練模型 2.1 CNN架構預訓練模型 2.2 Transformer類預訓練模型 2.3 自監督預訓練模型 三、圖像預訓練模型的發展史 四、預訓練的策略 五、預訓練代碼實戰&#xff1a;resnet18 六、嘗試在cifar10對比alexnet 七、嘗試通過ctrl進…

尋找兩個正序數組的中位數:二分查找的終極算法

引言&#xff1a;中位數的「C位之爭」 如果把數組比作排隊買奶茶的隊伍&#xff0c;中位數就是那個站在正中間的幸運兒——不需要知道所有人的位置&#xff0c;只需要找到那個「剛剛好」的中間位置。這個問題看似簡單&#xff0c;卻藏著算法世界的「效率密碼」&#xff0c;尤其…

使用tensorflow的線性回歸的例子(一)

擬合y2x1 import matplotlib.pyplot as plt import numpy as np import tensorflow as tf print(tf.__version__) %matplotlib inline #載入隨機種子 np.random.seed(5) #生成100個等差序列&#xff0c;每個值在-1 - 1 之間 x_data np.linspace(-1,1,100) #y 2x …

OpenLayers 渲染之矢量影像圖層

前言 :::block-1 對于像GeoJSON、KML等地理數據格式的文件&#xff0c;最常用的方法都是通過VectorLayer進行渲染。除此之外&#xff0c;還可以使用VectorImage&#xff08;矢量影像圖層&#xff09;進行渲染。本文主要介紹在客戶端拖動上傳GeoJSON、KML等文件&#xff0c;并采…

Feign 實戰指南:從 REST 替代到性能優化與最佳實踐

Feign 實戰指南&#xff1a;從 REST 替代到性能優化與最佳實踐 一 . Feign 替代 RestTemplate1.1 RestTemplate 方式調用存在的問題1.2 Feign 的介紹1.3 定義和使用 Feign 客戶端1.3.1 引入依賴1.3.2 添加注解1.3.3 編寫 Feign 的客戶端進行接口聲明1.3.4 測試小結 1.4 通過 PO…

什么是國際期貨?期貨交易平臺搭建

國際期貨&#xff08;International Futures&#xff09;&#xff0c;又稱外盤期貨或全球期貨&#xff0c;是指在中國大陸以外的交易所進行標準化合約交易的金融衍生品市場。其核心特征、功能及與國內期貨的區別如下&#xff1a; &#x1f4cd; 一、定義與核心特征 全球化交易…

考取華為HCIE-AI有什么用?

在人工智能技術重塑各行各業的浪潮中&#xff0c;掌握核心AI能力成為專業人士的制勝關鍵。華為推出的HCIE-AI Solution Architect&#xff08;華為認證ICT專家-AI解決方案架構師&#xff09;&#xff0c;正是面向這一領域頂尖人才設立的最高級別認證。主要是為了培養和認證掌握…

Maven 使用說明和配置

作者&#xff1a;小凱 沉淀、分享、成長&#xff0c;讓自己和他人都能有所收獲&#xff01;&#x1f604; 一、前言 Apache Maven (opens new window)是一個軟件項目管理、構建和依賴工具。基于項目對象模型 (POM) 的概念&#xff0c;Maven 可以通過中央信息來管理項目的構建、…