Linux驅動:class_create、device_create

udev是什么

  • 動態管理設備文件
    傳統的 Linux 系統通過靜態創建 /dev 目錄下的設備文件(如早期的 mknod 命令),但現代系統中硬件設備(如 USB 設備、存儲設備、串口等)熱插拔頻繁,udev 可實時響應設備事件,自動生成或刪除對應的設備文件。
    例如:插入 U 盤時,udev 會自動在 /dev 下創建如 /dev/sdb 或 /dev/sdb1 等-設備節點。

  • 設備屬性與權限配置
    udev 可根據設備的硬件信息(如 Vendor ID、Product ID、總線類型等)為設備文件設置 自定義權限、所有者、組屬 或 符號鏈接(如 /dev/usb/dev_acm0 指向實際設備節點),方便用戶或程序訪問。
    例如:為 USB 串口設備設置權限為 666,或為攝像頭設備創建易讀的符號鏈接 /dev/my_camera。

  • 響應內核事件
    udev 通過監聽內核的 uevent 機制(內核向用戶空間發送的設備事件通知),獲取設備的插拔、狀態變化等信息,并觸發預設的規則進行處理。

  • udev 規則文件
    規則文件定義了設備匹配條件和對應的操作,存儲在 /etc/udev/rules.d/ 和 /lib/udev/rules.d/ 目錄下,文件名通常以數字開頭(如 50-udev-default.rules),數字越小優先級越高。

# 為 Vendor ID=046d、Product ID=c52f 的 USB 鼠標設置權限為 666,并創建符號鏈接
SUBSYSTEM=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52f", MODE="0666", SYMLINK+="usb_mouse"

SUBSYSTEM:設備所屬子系統(如 usb、block、tty 等)。
ATTRS{…}:設備屬性(如 Vendor ID、Product ID)。
MODE:設備文件權限。
SYMLINK:創建符號鏈接。

  • udevadm 工具
    用于調試和管理 udev,常見命令:
    udevadm monitor:監聽 udev 事件。
    udevadm info:查看設備的 udev 規則和屬性。
    udevadm trigger:手動觸發 udev 規則(如插入新設備后刷新設備節點)。

  • udev 與設備驅動的配合
    在 Linux 設備驅動開發中,若需自動創建設備文件,通常配合以下步驟:
    使用 class_create(內核函數)創建一個設備類(用于 /sys/class/ 下的分類)。
    使用 device_create(內核函數)在該類下創建具體設備,此時內核會通過 uevent 通知 udev。
    udev 根據預設規則或動態生成規則,在 /dev 下創建對應的設備文件。

驅動中自動創建設備文件示例

#include <linux/device.h>static struct class *my_class;
static struct device *my_device;// 驅動初始化函數中
my_class = class_create(THIS_MODULE, "my_class");
if (IS_ERR(my_class)) {return PTR_ERR(my_class);
}my_device = device_create(my_class, NULL, dev_num, NULL, "my_device");
if (IS_ERR(my_device)) {class_destroy(my_class);return PTR_ERR(my_device);
}// 驅動退出函數中
device_destroy(my_class, dev_num);
class_destroy(my_class);

執行后,udev 會根據 /sys/class/my_class/my_device 中的信息生成 /dev/my_device 設備文件。

class_create

struct class *class_create(struct module *owner, const char *name);

參數:
owner:一般為 THIS_MODULE(表示模塊所有者)。
name:設備類名稱(如 CLASS_NAME)。
返回值:
成功:指向 struct class 的指針。
失敗:ERR_PTR(需通過 IS_ERR() 檢查)。

device_create

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);

參數:
class:指向 class_create 返回的 struct class 指針。
parent:父設備(通常為 NULL)。
devt:設備號(需與注冊字符設備時的 dev_num 一致)。
drvdata:設備特定數據(通常為 NULL)。
fmt:設備名稱(如 DEVICE_NAME)。
返回值:
成功:指向 struct device 的指針。
失敗:ERR_PTR(需通過 IS_ERR() 檢查)。

device_destroy:

void device_destroy(struct class *class, dev_t devt);

銷毀設備節點,刪除 /sys/class// 條目,并通知 udev 移除 /dev/ 下的設備文件。

class_destroy

void class_destroy(struct class *class);

銷毀設備類,刪除 /sys/class/ 目錄。

module_test.c

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <linux/uaccess.h>  // copy_to_user, copy_from_user
#include <linux/kernel.h>
#include <mach/gpio-bank.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>  
#include <linux/device.h>#define DEVICE_NAME "mychartest"  // 設備名(/dev/下的文件名)
#define CLASS_NAME "my_char_class"    // 設備類名(/sys/class/下的目錄名)#define  GPJ0CON  S5PV210_GPJ0CON
#define  GPJ0DAT  S5PV210_GPJ0DAT#define  rGPJ0CON  *((volatile unsigned int *) GPJ0CON)
#define  rGPJ0DAT  *((volatile unsigned int *) GPJ0DAT)#define BUFFER_SIZE 100
#define MYMAJOR 250
#define MYNAME "mychartest"int mymajor;
static char buffer[BUFFER_SIZE];static dev_t dev_num;             // 設備號(主設備號+次設備號)
static struct cdev my_cdev;       // 字符設備結構static struct class *my_class;  // 設備類指針
static struct device *my_device; // 設備指針int ctrled(int sta)
{if(sta){rGPJ0CON = 0x11111111;//rGPJ0DAT =((1<<3) | (1<<4) | (1<<5)) //滅1rGPJ0DAT =((1<<3) | (0<<4) | (0<<5)) ;//亮0printk(KERN_INFO "rGPJ0CON = %p\n",rGPJ0CON);printk(KERN_INFO "rGPJ0DAT = %d\n",rGPJ0DAT);printk(KERN_INFO "GPJ0CON = %p\n",GPJ0CON);printk(KERN_INFO "GPJ0DAT = %p\n",GPJ0DAT);}else{rGPJ0DAT =((0<<3) | (1<<4) | (1<<5)); //滅}}
// 打開設備函數
static int my_open(struct inode *inode, struct file *filp) {printk(KERN_INFO "Device opened\n");return 0;
}// 釋放設備函數
static int my_release(struct inode *inode, struct file *filp) {printk(KERN_INFO "Device released\n");return 0;
}// 讀取設備函數
static ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {printk(KERN_INFO "my_read\n");// 確保用戶緩沖區足夠大if (count < sizeof(unsigned int)) {return -EINVAL;}// 讀取寄存器值unsigned int reg_value = rGPJ0DAT;printk(KERN_INFO "reg_value = %d\n",reg_value);printk(KERN_INFO "sizeof(reg_value) = %d\n",sizeof(reg_value));if (copy_to_user(buf, &reg_value, sizeof(reg_value))) {return -EFAULT;}// 更新文件位置*f_pos += sizeof(reg_value);// 返回實際傳輸的字節數return sizeof(reg_value);
}// 寫入設備函數
static ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {printk(KERN_INFO "my_write\n");memset(buffer,0,sizeof(buffer));if (copy_from_user(buffer, buf, count)) {return -EFAULT;}if(!strcmp(buffer,"1")){printk(KERN_INFO "1\n");ctrled(1);}else if(!strcmp(buffer,"0")){printk(KERN_INFO "0\n");ctrled(0);}return 0;
}// 定義file_operations結構體實例
static const  struct file_operations my_fops = {.owner = THIS_MODULE,//全部一樣,都這樣寫.open = my_open,.release = my_release,.write = my_write,.read = my_read,};// 模塊安裝函數
static int __init chrdev_init(void)
{	int ret;printk(KERN_INFO "chrdev_init helloworld init\n");// 動態分配設備號(主設備號自動分配,次設備號從0開始,分配1個)ret = alloc_chrdev_region(&dev_num, 0, 1, MYNAME);if (ret < 0) {printk(KERN_ERR "Failed to allocate char device number\n");return ret;}// 打印分配的主設備號和次設備號printk(KERN_INFO "Allocated major: %d, minor: %d\n", MAJOR(dev_num), MINOR(dev_num));// 初始化 cdev 結構cdev_init(&my_cdev, &my_fops);my_cdev.owner = THIS_MODULE;// 注冊字符設備到內核ret = cdev_add(&my_cdev, dev_num, 1);if (ret < 0) {printk(KERN_ERR "Failed to add char device\n");unregister_chrdev_region(dev_num, 1); // 釋放已注冊的設備號return ret;}// 創建設備類(/sys/class/CLASS_NAME)my_class = class_create(THIS_MODULE, CLASS_NAME);if (IS_ERR(my_class)) {cdev_del(&my_cdev);unregister_chrdev_region(dev_num, 1);printk(KERN_ERR "Failed to create class\n");return PTR_ERR(my_class);}//  創建設備節點(觸發 udev 生成 /dev/DEVICE_NAME)my_device = device_create(my_class, NULL, dev_num, NULL, DEVICE_NAME);if (IS_ERR(my_device)) {class_destroy(my_class);cdev_del(&my_cdev);unregister_chrdev_region(dev_num, 1);printk(KERN_ERR "Failed to create device\n");return PTR_ERR(my_device);}printk(KERN_INFO "Successfully created device: /dev/%s\n", DEVICE_NAME);printk(KERN_INFO "chrdev_init helloworld successs... mymajor = %d\n", MYMAJOR);
}// 模塊下載函數
static void __exit chrdev_exit(void)
{// 銷毀設備節點if (my_device) {device_destroy(my_class, dev_num);}// 銷毀設備類if (my_class) {class_destroy(my_class);}// 移除字符設備cdev_del(&my_cdev);// 釋放設備號unregister_chrdev_region(dev_num, 1);printk(KERN_INFO "chrdev_exit helloworld exit\n");}module_init(chrdev_init);
module_exit(chrdev_exit);// MODULE_xxx這種宏作用是用來添加模塊描述信息
MODULE_LICENSE("GPL");				// 描述模塊的許可證
MODULE_AUTHOR("aston");				// 描述模塊的作者
MODULE_DESCRIPTION("module test");	// 描述模塊的介紹信息
MODULE_ALIAS("alias xxx");			// 描述模塊的別名信息

sys 文件系統簡介

sysfs(sys 文件系統) 是 Linux 內核提供的一種虛擬文件系統,用于在用戶空間中暴露內核數據結構和設備信息。它掛載在 /sys 目錄下,主要用于:

展示內核中設備、驅動、總線等硬件相關信息。
提供用戶空間與內核交互的接口(如讀取 / 修改內核參數)。
配合 udev 實現設備文件的自動創建和管理。

sysfs 的目錄結構與內核中的對象模型緊密關聯,主要包含以下幾類核心目錄:

/sys/bus:按總線類型(如 pci、usb)分類的設備和驅動信息。
/sys/devices:系統中所有設備的層次化列表。
/sys/class:按功能分類的設備抽象類(如 net、block、gpio),與用戶空間交互最密切。
/sys/module:內核加載的模塊信息。

/sys/class/xxx/ 目錄中文件的作用

/sys/class/ 下的每個子目錄(如 leds、tty、mydevice)代表一個 設備類(device class),用于將功能相似的設備分組。每個設備類包含多個設備實例的抽象信息,其下的文件和子目錄主要用于:

設備類屬性文件

name:設備類的名稱(字符串)。
dev:記錄該類下設備對應的主設備號和次設備號(格式為 major:minor),供 udev 自動創建設備節點時使用。
uevent:內核向用戶空間發送設備事件(如設備插入 / 移除)的接口,udev 通過監聽此文件觸發規則。

設備實例文件(在設備類子目錄中)

當設備驅動通過 class_create 和 device_create 創建設備實例后,會在設備類目錄下生成以設備名稱命名的子目錄(如 /sys/class/myclass/mydev),其下常見文件包括:

dev:設備實例對應的主設備號和次設備號(用于 udev 創建設備節點)。
power:設備電源管理相關屬性(如 status 表示電源狀態)。
subsystem:指向設備所屬的總線或類的符號鏈接(如 /sys/class/myclass)。
自定義屬性文件:驅動程序可通過 sysfs_create_file 在內核中動態創建屬性文件,用于暴露設備狀態或配置參數(如 LED 亮度、GPIO 方向等)。

與 udev 配合自動創建設備節點

/sys/class/xxx/ 目錄中的 dev 文件是 udev 自動創建設備節點的關鍵:

當內核檢測到新設備時,會在 sysfs 中生成對應的設備類和實例目錄,并寫入 dev 文件(主 / 次設備號)。
udev 監聽 sysfs 變化,根據預設規則(如 /etc/udev/rules.d/ 中的規則),結合 dev 文件中的設備號,在 /dev 目錄下創建設備節點(如 /dev/ttyUSB0)。
在這里插入圖片描述

/sys/class/my_char_class/mychartest

dev 文件

作用:
該文件存儲設備的 設備號(包括主設備號和次設備號),用于標識內核中的具體設備。
設備號是字符設備或塊設備的唯一標識,由主設備號(標識設備類型 / 驅動)和次設備號(標識具體設備實例)組成。
用戶空間工具(如 mknod、udev)可通過讀取此文件獲取設備號,從而創建設備節點(如 /dev/xxx)。
內容格式:
文件內容為兩個用逗號分隔的數字,例如:
在這里插入圖片描述

power/ 目錄

作用:
該目錄包含與設備 電源管理 相關的屬性文件,用于控制設備的電源狀態(如休眠、喚醒、功耗設置等)。
這是 Linux 內核電源管理子系統(PM subsystem)的一部分,支持 ACPI、USB 等設備的電源控制。
常見子文件:
control:
控制設備的電源狀態,可寫入 on(開啟)、auto(自動)、off(關閉)。
runtime_status:
顯示設備的運行時電源狀態(如 active、suspended)。
runtime_suspended_time:
記錄設備處于掛起狀態的總時間。
wakeup:
控制設備是否可以喚醒系統(on/off)。

echo off > /sys/class/xxx/power/control  # 關閉設備電源

subsystem 文件

作用:
該文件存儲設備所屬的 子系統(Subsystem) 路徑,用于標識設備在 Linux 設備模型中的分類。
子系統是內核中設備的邏輯分組(如 block、char、net、usb 等),同一子系統下的設備具有相似的操作接口。
內容格式:
文件內容為子系統在 /sys/subsystem/ 下的相對路徑,例如:

char  # 表示該設備屬于字符設備子系統

uevent 文件

作用:
該文件用于內核向用戶空間發送 設備熱插拔事件(uevent) 的相關信息。
當設備插入、移除或狀態改變時,內核會通過此文件傳遞事件參數(如設備類型、屬性變更等),供用戶空間工具(如 udev)處理。
內容格式:
文件內容為一系列 鍵值對,描述事件的具體信息,常見字段包括:
ACTION:事件類型(如 add、remove、change)。
DEVPATH:設備在 /sys 中的路徑。
SUBSYSTEM:設備所屬子系統。
DEVNAME:設備節點名稱(如 /dev/ttyUSB0)。
MAJOR/MINOR:設備號的主 / 次部分。
在這里插入圖片描述

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

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

相關文章

【vLLM 學習】Cpu Offload Lmcache

vLLM 是一款專為大語言模型推理加速而設計的框架&#xff0c;實現了 KV 緩存內存幾乎零浪費&#xff0c;解決了內存管理瓶頸問題。 更多 vLLM 中文文檔及教程可訪問 →https://vllm.hyper.ai/ *在線運行 vLLM 入門教程&#xff1a;零基礎分步指南 源碼 examples/offline_inf…

基于深度強化學習的Scrapy-Redis分布式爬蟲動態調度策略研究

在大數據時代&#xff0c;網絡數據的采集與分析變得至關重要&#xff0c;分布式爬蟲作為高效獲取海量數據的工具&#xff0c;被廣泛應用于各類場景。然而&#xff0c;傳統的爬蟲調度策略在面對復雜多變的網絡環境和動態的抓取需求時&#xff0c;往往存在效率低下、資源浪費等問…

openlayers實現可拖拽的節點(類似知識圖譜)

/** * 本文介紹了實現知識圖譜可視化的技術方案&#xff0c;主要分為兩個圖層實現&#xff1a; * 1、線圖層 不拖動 * 2、點圖層 需要拖動 */ 線圖層 - 負責繪制靜態連接線&#xff0c;使用LineString創建線要素并添加到矢量圖層&#xff1b; // 線圖層 export function add…

酷黑NBA足球賽事直播源碼體育直播M39模板賽事源碼

源碼名稱&#xff1a;NBA足球賽事直播源碼酷黑體育直播M39模板賽事源碼 開發環境&#xff1a;帝國cms7.5 空間支持&#xff1a;phpmysql 帶軟件采集&#xff0c;可以掛著自動采集發布&#xff0c;無需人工操作&#xff01; 演示地址&#xff1a;https://www.52muban.com/shop…

Verilog編程技巧01——如何編寫三段式狀態機

前言 Verilog編程技巧系列文章將聚焦于介紹Verilog的各種編程范式或者說技巧&#xff0c;編程技巧和編程規范有部分重合&#xff0c;但并非完全一樣。規范更注重編碼的格式&#xff0c;像變量命名、縮進、注釋風格等&#xff0c;而編程技巧則更偏重更直觀易讀、更便于維護、綜合…

豆包和deepseek 元寶 百度ai區別是什么

豆包、DeepSeek、元寶和百度 AI 有以下區別&#xff1a; 開發公司 豆包5&#xff1a;由字節跳動公司基于云雀模型開發。DeepSeek4&#xff1a;是深度求索打造的開源多模態大模型。元寶1&#xff1a;是騰訊混元模型的落地產品&#xff0c;整合了 DeepSeek - R1 與混元模型。百…

網頁端 js 讀取發票里的二維碼信息(圖片和PDF格式)

起因 為了實現在報銷流程中&#xff0c;發票不能重用的限制&#xff0c;發票上傳后&#xff0c;希望能讀出發票號&#xff0c;并記錄發票號已用&#xff0c;下次不再可用于報銷。 基于上面的需求&#xff0c;研究了OCR 的方式和讀PDF的方式&#xff0c;實際是可行的&#xff…

讀文獻先讀圖:GO弦圖怎么看?

GO弦圖&#xff08;Gene Ontology Chord Diagram&#xff09;是一種用于展示基因功能富集結果的可視化工具&#xff0c;通過弦狀連接可以更直觀的展示基因與GO term&#xff08;如生物過程、分子功能等&#xff09;之間的關聯。 GO弦圖解讀 ①內圈連線表示基因和生物過程之間的…

pandas隨筆

主要操作兩個對象&#xff1a;一維帶標簽數組 和 二維表格DataFrame 一維帶標簽數組Series pd.Series([1, 3, 5, np.nan, 6, 8]) &#xff0c;結果如下&#xff1a; 可指定索引&#xff0c;pd.Series([1, 3, 5], index[a, b, c]) 二維表格DataFrame 創建時需要指定列名&a…

java教程筆記(十一)-泛型

Java 泛型&#xff08;Generics&#xff09;是 Java 5 引入的重要特性之一&#xff0c;它允許在定義類、接口和方法時使用類型參數。泛型的核心思想是將類型由具體的數據類型推遲到使用時再確定&#xff0c;從而提升代碼的復用性和類型安全性。 1.泛型的基本概念 1. 什么是泛…

力扣刷題(第四十九天)

靈感來源 - 保持更新&#xff0c;努力學習 - python腳本學習 反轉鏈表 解題思路 迭代法&#xff1a;通過遍歷鏈表&#xff0c;逐個改變節點的指針方向。具體步驟如下&#xff1a; 使用三個指針&#xff1a;prev&#xff08;初始為None&#xff09;、curr&#xff08;初始為…

設置應用程序圖標

(1)找一張圖片 (2)然后轉ico圖片 在線生成透明ICO圖標——ICO圖標制作 驗證16x16就可以 降低exe大小 (3) 在xxx.pro修改 添加 &#xff08;4&#xff09; 刪除 build 和 xxxpro_user文件 (5)編譯project 和運行xx.exe (6)右鍵 設置快捷方式

免費wordpress模板下載

西瓜紅色的免費wordpress模板&#xff0c;簡潔實用又容易上手&#xff0c;適合新手使用。 下載 https://www.waimaoyes.com/moban/2231.html

【React】React 18 并發特性

React 18 引入了 并發特性&#xff08;Concurrent Features&#xff09;&#xff0c;這是一次對 React 渲染機制的重大升級&#xff0c;讓 React 更加智能、響應更流暢、資源更節省。 我們來詳細講解一下它的原理、特性、API 以及實際應用。 &#x1f9e0; 一、什么是并發特性…

FFMPEG 提取視頻中指定起始時間及結束時間的視頻,給出ffmpeg 命令

以下是提取視頻中指定起始時間及結束時間的 ffmpeg 命令示例: bash 復制 ffmpeg -i input.mp4 -ss 00:01:30.00 -to 00:05:00.00 -c copy output.mp4 其中,-i input.mp4 是指定要處理的輸入視頻文件為 “input.mp4”。 -ss 00:01:30.00 表示指定視頻的起始時間為 1 分 30 …

mybatis的if判斷==‘1‘不生效,改成‘1‘.toString()才生效的原因

mybatis的xml文件中的if判斷‘1’不生效&#xff0c;改成’1’.toString()才生效 Mapper接口傳入的參數 List<Table> queryList(Param("state") String state);xml內容 <where><if test"state ! null and state 1">AND EXISTS(select…

AI 模型分類全解:特性與選擇指南

人工智能&#xff08;AI&#xff09;技術正以前所未有的速度改變著我們的生活和工作方式。AI 模型作為實現人工智能的核心組件&#xff0c;種類繁多&#xff0c;功能各異。從簡單的線性回歸模型到復雜的深度學習網絡&#xff0c;從文本生成到圖像識別&#xff0c;AI 模型的應用…

01-python爬蟲-第一個爬蟲程序

開始學習 python 爬蟲 第一個獲取使用最多的網站-百度 源代碼 并將源代碼保存到文件中 from urllib.request import urlopenurl https://www.baidu.com resp urlopen(url)with open(baidu.html, w, encodingutf-8) as f:f.write(resp.read().decode(utf-8))知識點&#xf…

四六級監考《培訓學習》+《培訓考試》

1 線上注冊 &#xff08;網址&#xff1a; https://passport.neea.edu.cn 2 登錄培訓平臺參加線上必修課程學習和考核 &#xff08;平臺網址&#xff1a; https://kwstudy.neea.edu.cn 注意選擇學員入口&#xff09; 3 考試要求&#xff1a;考試成績須達應到80分以上&#xf…

回顧Java與數據庫的30年歷程

當 Java 1.0 于 1996 年推出時&#xff0c;語言和互聯網都與今天大不相同。當時&#xff0c;網絡主要是靜態的&#xff0c;而 Java 承諾通過注入交互式游戲和動畫來為網絡注入活力&#xff0c;這一承諾極具前景。根據 1995 年寫給《連線》雜志的 David Banks 的說法&#xff0c…