linux2.6標準字元裝置驅動模型(手動註冊)
linux2.6標準字元裝置核心結構
//雖然linux26有核心結構體,但在程式碼中沒有具體操作,他們的呼叫在相關注冊函式中對其有操作
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
關鍵成員介紹
- ops
檔案操作集合 - dev
32位裝置號,包含主裝置號和次裝置號 - count
佔用連續的次裝置號數量,從dev中的次裝置號開始 - 其餘成員與系統呼叫相關,使用者不用配置
裝置號
linux2.6以後用dev_t 32位整數表示裝置號,實質是u32型別,其中高12位是主裝置號,低20位是次裝置號。
主裝置號:dev_t高12位,2^12=4K,(10是給雜項裝置使用的)
早期字元裝置範圍:0~255 Linux2.6是0~4095
次裝置號:dev_t低20位,2^20=1M
早期字元裝置範圍:0~255(主裝置號一旦註冊,下屬次裝置號全部被註冊), Linux2.6是0~1M-1
合成裝置號:
MKDEV(ma,mi)
;ma:主裝置號;mi:次裝置號
分解裝置號:
MAJOR(dev)
MINOR(dev)
核心中定義
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
特徵
- 安裝後不會在
/dev
目錄下建立裝置檔案節點,需要手動mknod
- 呼叫一個
cdev_add
註冊後,指定數量的次裝置號將被佔用,數量自己制定,一個主裝置號可以被cdev_add
註冊多次。 - 裝置號使用前需要提前申請,
register_chrdev_region
靜態分配,或alloc_chrdev_region
動態分配裝置號函式申請。
裝置號申請/登出函式
靜態裝置號申請函式
//靜態申請裝置號
int register_chrdev_region(dev_t from, unsigned count, const char *name)
from :指定的裝置號 250 1
count :裝置個數 5 250 1 250 2 250 3 250 4 250 5
name: 裝置名
返回值:成功返回0,失敗返回負數錯誤碼
- 標頭檔案
#include<linux/fs.h>
- 功能
註冊一個裝置號範圍 - 引數
- [ ] from起始裝置號(主、次)
- [ ] count連續的次裝置號數量
- [ ] name裝置名,不需要和
/dev
的裝置名相同 - [ ] 其餘引數由核心使用,使用者不直接操作
- 返回值
成功:返回0, 失敗:返回負數
動態裝置號註冊函式
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
dev: 接收動態分配的一系列裝置號的第一個裝置號
baseminor:指定一系列裝置號的第一個裝置號的次裝置號
count :裝置個數
name :裝置名
返回值:成功0,失敗為負數
- 標頭檔案
#include<linux/fs.h>
- 功能
註冊一個裝置號的範圍 - 引數
- [ ] *dev:存放分配到的第一個裝置(包含主次裝置號)
- [ ] baseminor:要分配的起始次裝置號
- [ ] count連續次裝置號的數量
- [ ] name裝置名,不需要和
/dev
的裝置名相同 - 返回值
成功:返回0 失敗:返回負數
裝置號釋放函式
//釋放裝置號
void unregister_chrdev_region(dev_t from, unsigned count)
from: 第一個裝置號
count:裝置個數
- 標頭檔案
#include<linux/fs.h>
- 功能
登出一個裝置號範圍 - 引數
- [ ] from:起始裝置號(主、次)(包含主次裝置號)
- [ ] count:連續的次裝置號數量
- 返回值
無
核心結構分配函式
struct cdev *cdev_alloc(void); //分配儲存區
功能:在堆空間中分配一個核心結構體,注意不使用時用kfree
函式釋放,
引數:無
返回值:返回分配到struct cdev
的結構首地址
說明:用完記得釋放,否則會造成記憶體洩漏
第一種方式:
struct cdev k
;//定義變數,定以後已經有了struct cdev
解構記憶體空間
第二種方式:
struct cdev *p
://這種寫法只是定義了指標,但是沒有分配struct cdev
記憶體空間,p=struct_cdev();
//寫在模組載入函式中
核心結構struct cdev初始化函式
初始化函式
void cdev_init(struct cdev *xxx, const struct file_operations *fops); //初始化
- 標頭檔案
#include<linux/cdev.h>
- 功能
初始化核心結構,具體的做法是清零核心結構,初始化核心結構體的list,kobj,ops成員
- 引數
- [ ] cdev:需要初始化的核心結構體
- [ ] fops:檔案操作集合函式結構體
- 返回值
無
說明:寫這種驅動模型的時候,不需要定義struct cdev結構體初始化,因為在呼叫
cdev_init
結構體會初始化把它請0,定義時候的初始無效
真正的設備註冊/登出函式
//註冊驅動
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
p: 核心結構體
dev:裝置號
count:裝置個數
返回值:成功返回0,失敗返回負數錯誤碼
- 標頭檔案
#include<linux/cdev.h>
- 功能
註冊一個cdev
結構 - 引數
- [ ] p:已經初始化的核心結構體指標
- [ ] dev:起始裝置號(包含主次裝置號在內)
- [ ] count:連續的次裝置號數量
- 返回值
成功:返回0 失敗:返回負數
//登出函式
void cdev_del(struct cdev *p);
p:核心結構體
- 標頭檔案
#include<linux/cdev.h>
- 功能
登出一個cdev
結構 - 引數
p 前面註冊的struct cdev
結構指標 - 返回
無
linux2.6標準字元裝置驅動程式設計步驟
回顧雜項裝置
定義雜項裝置核心結構體,填充核心結構體,註冊核心結構。這個過程中,使用倒推法則,實現要什麼給什麼。
linux2.6模型方法相同。
模組的初始化函式
- 使用
cdev_alloc(void)
分配cdev空間 - 申請裝置號:靜態或者動態申請
//靜態申請
int register_chrdev_region(dev_t from, unsigned count, const char *name);
//動態申請
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
- 初始化cdev結構
void cdev_init(struct cdev *xxx, const struct file_operations *fops); //初始化
- 註冊已初始化好的cdev結構
//註冊驅動
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
模組解除安裝函式做的事情和載入的函式相反,順序也相反。
- 登出cdev結構
void cdev_del(struct cdev *p);
- 釋放裝置號
void unregister_chrdev_region(dev_t from, unsigned count);
- 釋放cdev解構空間
kfree(void *p);
驅動核心
- 實現
struct file_operations
結構
示例
程式碼
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/kdev_t.h>
#include<linux/slab.h>
#define MY_DEVICE_NAME "linux26"
static struct cdev *pcdev;
static dev_t dev_no; //第一個裝置號(包含主次)
static int major=0;
ssize_t my_device_open(struct inode *node, struct file *file)
{
printk("my device is open\n");
return 0;
}
ssize_t my_device_close(struct inode *node, struct file *file)
{
printk("my device is close\n");
return 0;
}
ssize_t my_device_read(struct file *fp, char __user *buf, size_t size, loff_t *loff)
{
printk("my device is read\n");
return 0;
}
ssize_t my_device_write(struct file *file, const char __user *buf, size_t size, loff_t *loff)
{
printk("my device is write\n");
return 0;
}
//檔案結構體
struct file_operations my_fops={
.owner=THIS_MODULE,
.open=my_device_open,
.read=my_device_read,
.write=my_device_write,
.release=my_device_close,
};
static int __init linux26_cdev_init(void)
{
//分配cdev空間
pcdev=cdev_alloc();
//動態分配裝置號 次裝置號0開始 數量為2個 名稱為巨集
alloc_chrdev_region(&dev_no,0,2,MY_DEVICE_NAME);
//初始化結構體
cdev_init(pcdev,&my_fops);
//註冊驅動
cdev_add(pcdev,dev_no,2);
//再去查註冊檔案很麻煩,這裡列印主裝置號方便建立節點
major=MAJOR(dev_no);
printk("linux26 major is %d\n",major);
return 0;
}
static void __exit linux26_cdev_exit(void)
{
cdev_del(pcdev);
unregister_chrdev_region(dev_no,2);
kfree(pcdev);
printk("cdev module is exit\n");
}
module_init(linux26_cdev_init);
module_exit(linux26_cdev_exit);
MODULE_LICENSE("GPL");
app程式碼
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc,char *argv[])
{
char buf[20];
int fd = open(argv[1],O_RDWR);
if(fd == -1)
{
printf("開啟失敗\n");
return 0;
}
read(fd,buf,0);
write(fd,NULL,0);
close(fd);
return 0;
}
Makefile
KERN_DIR = /zhangchao/linux3.5/linux-3.5
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
rm -f *.mod.o *.order *.symvers *.o *.mod.c
obj-m += cdev.o
模組編譯
[[email protected] zhangchao]# make
make -C /zhangchao/linux3.5/linux-3.5 M=`pwd` modules
make[1]: 進入目錄“/zhangchao/linux3.5/linux-3.5”
CC [M] /zhangchao/rootfs/zhangchao/cdev.o
Building modules, stage 2.
MODPOST 1 modules
LD [M] /zhangchao/rootfs/zhangchao/cdev.ko
make[1]: 離開目錄“/zhangchao/linux3.5/linux-3.5”
[[email protected] zhangchao]# ls
cdev_app.c cdev.ko cdev.mod.o Makefile Module.symvers
cdev.c cdev.mod.c cdev.o modules.order
[[email protected] zhangchao]#
app 編譯
[[email protected] zhangchao]# arm-linux-gcc cdev_app.c -o cdevapp
[[email protected] zhangchao]# ls
cdevapp cdev.c cdev.mod.c cdev.o modules.order
cdev_app.c cdev.ko cdev.mod.o Makefile Module.symvers
開發板裝載模組
沒有采取自動建立裝置節點,所以/dev
沒有該模組的裝置檔案節點
[root@ZC/zhangchao]#insmod cdev.ko
[ 742.520000] linux26 major is 250
[root@ZC/zhangchao]#lsmod
Module Size Used by Tainted: G
cdev 1518 0
[root@ZC/zhangchao]#
通過/proc/devices
設備註冊檔案表檢視是否安裝成功
可以看到主裝置號為250的linux26裝置識別名出現在列表中,證明註冊沒有問題。
[root@ZC/zhangchao]#cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
···
189 usb_device
204 ttySAC
216 rfcomm
250 linux26
251 ttyGS
252 watchdog
253 media
254 rtc
Block devices:
1 ramdisk
···
179 mmc
254 device-mapper
建立檔案節點
[root@ZC/zhangchao]#mknod /dev/linux26 c 250 1
[root@ZC/zhangchao]#ls /dev/linux26 -l
crw-r--r-- 1 root root 250, 1 Mar 29 16:37 /dev/linux26
[root@ZC/zhangchao]#
執行app
app呼叫驅動模組成功
[root@ZC/zhangchao]#./cdevapp /dev/linux26
[ 1345.240000] my device is open
[ 1345.240000] my device is read
[ 1345.240000] my device is write
[ 1345.240000] my device is close
[root@ZC/zhangchao]#
思考下面的問題
[root@ZC/zhangchao]#rm /dev/linux26
[root@ZC/zhangchao]#mknod /dev/linux26 c 250 2
[root@ZC/zhangchao]#./cdevapp /dev/linux26
開啟失敗
[root@ZC/zhangchao]#rm /dev/linux26
[root@ZC/zhangchao]#mknod /dev/linux26 c 250 1
[root@ZC/zhangchao]#./cdevapp /dev/linux26
[ 1564.045000] my device is open
[ 1564.045000] my device is read
[ 1564.045000] my device is write
[ 1564.045000] my device is close
[root@ZC/zhangchao]#
如果將次裝置號定義為2,執行app將不能執行驅動模組,這是為什麼呢?因為我們註冊時,申請的次裝置號數量為兩個,在主裝置號下其他次裝置號沒有被佔用的情況下,所以只有0和1可以被註冊,裝置號為2時,他將是次裝置號的第三個
解除安裝驅動模組
[root@ZC/zhangchao]#rmmod cdev.ko
[ 1977.955000] cdev module is exit
[root@ZC/zhangchao]#
相關推薦
linux2.6標準字元裝置驅動模型(手動註冊)
linux2.6標準字元裝置核心結構 //雖然linux26有核心結構體,但在程式碼中沒有具體操作,他們的呼叫在相關注冊函式中對其有操作 struct cdev { struct kobject kobj; struct module *o
(三)從解析DTS到建立device_從device_node到併入裝置驅動模型(結合原始碼)
從device_node到併入裝置驅動模型 此篇部落格有很多參考其他文章的內容,由於參考內容繁雜,不一一標註角標了,在末尾會貼上所有參考部落格的link,如有侵權,請聯絡本人處理,謝謝。 深入,並且廣泛 -沉默犀牛 上一篇文章已經詳細的分析了兩個問題: 1.如何根據
Linux字元裝置驅動模型--字元裝置的註冊
當我們編寫字元裝置驅動程式的時候,在進行字元裝置物件cdev的分配、初始化,裝置號的註冊這些初始化階段之後,就可以將它加入到系統中,這樣才能使用這個字元裝置。將一個字元裝置加入到系統中呼叫的函式時cdev_add,核心原始碼如下: int cdev_add(struct cdev *
Linux 字元裝置驅動結構(二)—— 自動建立裝置節點
上一篇我們介紹到建立裝置檔案的方法,利用cat /proc/devices檢視申請到的裝置名,裝置號。 第一種是使用mknod手工建立:mknod filename type major minor 第二種是自動建立裝置節點:利用u
Linux 字元裝置驅動結構(一)—— cdev 結構體、裝置號相關知識解析
一、字元裝置基礎知識 1、裝置驅動分類 linux系統將裝置分為3類:字元裝置、塊裝置、網路裝置。使用驅動程式: 字元裝置:是指只能一個位元組一個位元組讀寫的裝置,不能隨機讀取裝置記憶體中的某一資料,讀取資料需要按照先後資料。
Linux:驅動之字元裝置工作原理(未完)
字元裝置驅動工作原理 系統整體工作原理 應用層->API->裝置驅動->硬體? API:open、read、write、close等? 驅動原始碼中提供真正的open、read、write、close等函式實體? file_
6.1Linux字元裝置驅動結構
cdev結構體 用於描述字元裝置 1 struct cdev 2 { 3 struct kobject kobj; /* 內嵌的kobject物件*/ 4 struct module *owner; /*所屬模組*/ 5 struct file_operations *o
linux字元裝置驅動模型
一.雜項裝置驅動模型 雜項的主裝置號固定為10,只有255個次裝置號。可以直接生成驅動核心。 1.需要確定每個模型都會用到的檔案操作方法集合指標 2.確定核心的結構體 static struct miscdevice abc 確定三個引數,第一個為次裝置號,第二個是次裝
Linux 字元裝置驅動結構(三)—— file、inode結構體及chardevs陣列等相關知識解析
先看下面這張圖,這是Linux 中虛擬檔案系統、一般的裝置檔案與裝置驅動程式值間的函式呼叫關係; 上面這張圖展現了一個應用程式呼叫字元裝置驅動的過程, 在裝置驅動程式的設計中,一般而言,會關心 file 和 inode 這兩個結構體
Linux 字元裝置驅動結構(三)—— file、inode結構體及chardevs陣列等相關知識解析[轉載]
先看下面這張圖,這是Linux 中虛擬檔案系統、一般的裝置檔案與裝置驅動程式值間的函式呼叫關係; 上面這張圖展現了一個應用程式呼叫字元裝置驅動的過程, 在裝置驅動程式的設計中,一般而言,會關心 file 和 inode 這兩個結構體
普通字元裝置驅動的兩種註冊方式(新&舊)
普通字元裝置驅動的兩種註冊方式(新&舊) 在核心中,對於一個普通的字元裝置驅動,不難發現有兩種註冊方式: register_chrdev族函式+建立裝置類、檔案的函式:這種方法是2.4版本
Linux 字元裝置驅動結構(一)—— cdev 結構體、裝置號相關知識解析[轉載]
一、字元裝置基礎知識1、裝置驅動分類 linux系統將裝置分為3類:字元裝置、塊裝置、網路裝置。使用驅動程式:字元裝置:是指只能一個位元組一個位元組讀寫的裝置,不能隨機讀取裝置記憶體中的某一資料,讀取資料需要按照先後資料。字元裝置是面向流的裝置,常見的字元裝置有滑鼠
Linux 字元裝置驅動結構(四)—— file_operations 結構體知識解析
前面在 Linux 字元裝置驅動開發基礎 (三)—— 字元裝置驅動結構(中) ,我們已經介紹了兩種重要的資料結構 struct inode{...}與 struct file{...} ,下面來介紹另一個比較重要資料結構 struct _file_oper
Linux(2.6.35.7)字元裝置驅動註冊介面
1. 老介面 (1)註冊函式 static inline int register_chrdev(unsigned int major, const char *name, const struct f
20 字元裝置驅動相關的函式和引數及實現(虛擬檔案)
字元裝置驅動相關的函式和引數及實現(虛擬檔案) 使用者程序呼叫函式順序: open ---> kernel ---> cdev.ops->open(..) read ---> kernel ---> cdev.ops->read(
裝置驅動模型之:kobject,kset,ktype(六)
本篇部落格介紹kset與kobject/kset之間的關係,好了,廢話不多說,直接上ktype的結構體: struct kobj_type { void (*release)(struct kobject *kobj); const struct sysf
裝置驅動模型之:kobject,kset,ktype(五)
在《裝置驅動模型之:kobject,kset,ktype(四)》這篇博文裡面已經詳細介紹了kset的操作以及kset與kobject的關係,下面則是對於這篇博文的一些實際操作: #include <linux/module.h> #include <linux/init
裝置驅動模型之:kobject,kset,ktype(一)
概述 kobject結構是linux驅動程式的基礎,也是裝置模型中抽象的一部分。如果想要了解驅動程式必須瞭解kobject結構的具體資料組成以及kobject結構的作用。核心為了相容各種形形色色的裝置,必須對各種裝置的共同特性進行抽象。這種抽象在C++中稱之為基礎類,但是C語言沒有繼承特性,
linux裝置驅動(3)字元驅動 -按鍵(查詢法)
本文描述查詢法。 所謂查詢法,就是在應用程式裡面執行 while (1) { read(fd, key_vals, sizeof(key_vals)); ... } 載入驅動並在後臺執行應用程式時, 通過top可以看到CPU利用率,該應用程序佔用9
linux裝置驅動模型一字元裝置open系統呼叫流程
從前面 的例子可以看到,我們在操作一個調和時都 是通過open系統呼叫先去開啟這個裝置,不管是裝置還是檔案,我們要訪問它都要稱通過open函式來先開啟, 這樣才能呼叫其它的函式如read、write來操作它,即通知核心新建一個代表該檔案的結構,並且返回該檔案的描述符(一個整