Linux裝置驅動之字元裝置驅動

2021-07-24 04:28:48 字數 4309 閱讀 8451

一、linux裝置的分類

linux系統將裝置分成三種基本型別,每個模組通常實現為其中某一類:字元模組、塊模組或網路模組。

這三種型別有:

字元裝置:字元裝置是個能夠像位元組流(類似檔案)一樣被訪問的裝置,由字元裝置驅動程式來實現這種特性。字元裝置可以通過檔案系統節點來訪問,比如/dev/tty1等。這些裝置檔案和普通檔案之間的唯一差別在於對普通檔案的訪問可以前後移動訪問位置,而大多數字元裝置是乙個個只能順序訪問的資料通道。

塊裝置:和字元裝置類似,塊裝置也是通過/dev目錄下的檔案系統節點來訪問。塊裝置(列如磁碟)上能夠容納檔案系統。塊裝置和字元裝置的區別僅僅在於核心內部管理資料的方式,也就是核心及驅動程式之間的軟體介面,而這些不同對使用者來講是透明的。

網路裝置:任何網路事務都經過乙個網路介面形成,即乙個能夠和其他主機交換資料的裝置。通常,介面是個硬體裝置,但也可能是個純軟體裝置,比如回環(loopback)介面。由於不是面向流的裝置,因此將網路介面對映到檔案系統中的節點比較困難。

二、字元裝置驅動的基本知識1、主裝置號和次裝置號乙個字元裝置或塊裝置都有乙個主裝置號和乙個次裝置號。通常而言,主裝置號用來標識與裝置檔案相連的驅動程式,用來反映裝置型別。次裝置號被驅動程式用來辨別操作的是哪個裝置,用來區分同型別的裝置。

在核心中,dev_t型別(在中定義)用來儲存裝置編號--------包括這裝置號和次裝置號。

用major(

dev_t dev

);獲得主裝置號。用minor(

dev_t dev

);獲得次裝置號。用mkdev(int major,int minor)將主裝置號和次裝置號轉換成裝置號。

這些巨集如下定義:

#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))

2、分配和釋放裝置號在建立乙個字元裝置驅動之前,我們的驅動程式首先要做的事情就是獲得乙個或多個裝置號。

(1)靜態分配

int register_chrdev_region(dev_t from, unsigned count, const char *name);
其中first是要分配的裝置編號範圍的起始位址。count是所請求的連續裝置號的個數。name是和該裝置號關聯的裝置名稱,它將出現在/proc/devices和sysfs中。成功返回0,在錯誤情況下返回乙個負的錯誤碼。

(2)動態分配

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
在上面的函式中,dev是僅用於輸出的引數,在成功完成呼叫後將儲存已分配範圍的第乙個裝置號。firstminor應該是要使用的被請求的第乙個次裝置號,它通常是0。count和name引數和靜態分配函式一樣。

(3)釋放裝置號

在不用時將它們釋放。

void unregister_chrdev_region(dev_t from, unsigned count);
通常,我們在模組的清除函式中呼叫它。

(4)建立裝置檔案

利用cat /proc/devices檢視申請到的裝置名,裝置號。

1.利用mknod手工建立:mknod [選項]... 名稱 型別 [主裝置號 次裝置號]

2.自動建立:利用udev(mdev)來實現裝置檔案的自動建立。

三、字元裝置驅動中一些重要的資料結構

首先,看看linux軟體系統的層次關係:

我們在應用層呼叫open,read,write這類函式時,系統會呼叫驅動程式裡的open,read,write這類函式。所以在裝置驅動中我們需要構建這些函式,事實上,在字元裝置驅動中,主要的任務也就是構造這些函式,下面來問幾個問題。

1、構建這些open,read這類函式後,核心怎麼知道它們呢?

答:

a、通過file_operations結構。在file_operations結構裡填充這些函式;

struct file_operations
b、將file_operations結構體初始化到乙個cdev結構中;

struct cdev

;

struct cdev *cdev_alloc(void);//分配乙個cdev結構

void cdev_init(struct cdev *cdev, const struct file_operations *fops);//初始化cdev,將file_operations結構加入cdev結構
c、將cde結構體加入到核心當中去,並與乙個裝置號範圍連線。

int cdev_add(struct cdev *p, dev_t dev, unsigned count)//新增cdev到核心,並與乙個裝置號範圍連線

誰來呼叫cdev_add呢?

答:驅動的入口函式。怎麼知道它是入口函式呢?用乙個巨集module_init修飾。驅動的清除函式也一樣。

接下來以乙個hello驅動程式為例,方便大家理解:

#include #include #include #include #include #include #include #include #include #include #include #include #include /* 1. 確定主裝置號 */

static int major;

static int hello_open(struct inode *inode, struct file *file)

/* 2. 構造file_operations */

static struct file_operations hello_fops = ;

#define hello_cnt 2

static struct cdev hello_cdev;

static struct class *cls;

static int hello_init(void)

else

cdev_init(&hello_cdev, &hello_fops);

cdev_add(&hello_cdev, devid, hello_cnt);

/*利用udev(mdev)自動建立節點*/

cls = class_create(this_module, "hello");

class_device_create(cls, null, mkdev(major, 0), null, "hello0"); /* /dev/hello0 */

class_device_create(cls, null, mkdev(major, 1), null, "hello1"); /* /dev/hello1 */

class_device_create(cls, null, mkdev(major, 2), null, "hello2"); /* /dev/hello2 */

return 0;

}static void hello_exit(void)

module_init(hello_init);

module_exit(hello_exit);

module_license("gpl");

Linux裝置驅動之《字元裝置驅動》

linux裝置中最大的特點就是裝置操作猶如檔案操作一般,在應用層看來,硬體裝置只是乙個裝置檔案。應用程式可以像操作檔案一樣對硬體裝置進行操作,如open close read write 等。下面是乙個字元裝置驅動程式的簡單實現test.c 模組分析 1.初始化裝置驅動的結構體 struct fil...

Linux裝置驅動之字元裝置(三)

在linux裝置驅動之字元裝置 一 中學習了裝置號的構成,裝置號的申請與釋放。在linux裝置驅動之字元裝置 二 中學習了如何建立乙個字元裝置,初始化,已經註冊到系統中和最後釋放該字元裝置。本節將結合前兩節學到的知道,編寫乙個簡單的字元裝置驅動。最後總結一下字元裝置驅動的模型。include inc...

Linux驅動基礎 platform裝置驅動

以高通平台為例,會在kernel arch arm mach msm下的相應的board c檔案裡邊用 dt machine start 這個巨集定義一系列的晶元。以高通8916為例 在kernel arch arm mach msm board 8916.c檔案裡定義了 當然下面使用哪個要看一下。...