【嵌入式Linux驅動開發】三、字符設備驅動(一)

1. 基本步驟

(1)確定主設備號和次設備號

(2)實現字符驅動程序

  • 實現file_operations結構體;
  • 實現初始化函數,註冊字符設備;
  • 實現銷燬函數,釋放字符設備;
  • 實現字符設備其他基本成員函數。

(3)創建設備文件節點

2. 什麼是主設備號/次設備號

主設備號是內核識別一個設備的標識。它是一個整數(佔12位),通常使用1~255。

次設備號由內核使用,用於正確確定設備文件所指的設備。它也是一個整數(佔20位),通常使用0~255。

注:同一類設備的主設備號相同,不同的是次設備號。如多個串口的主設備號是相同的,次設備號不同。

3. 設備編號的內部表達

(1)dev_t類型(32位):用來保存設備編號

(2)從dev_t獲得主、次設備號:

  • MAJOR(dev_t):    //主
  • MINOR(dev_t);    //次

(3)將主、次設備號轉換成dev_t類型:

  • MKDEV(int major, int minor);

4. 分配設備號

通常在模塊加載函數中調用。

(1)手工分配:找一個內核沒有使用的主設備號來使用

#include<linux/fs.h>
int register_chrdev_region(dev_t first, unsigned int count, char *name);
//first是設備號,次設備號通常爲0;count是要分配設備號的個數,即次設備號個數;name是此類設備的名字。

(2)動態分配:

#include<linux/fs.h>
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
//dev是輸出的設備號;firstminor是要申請第一個次設備號;count是要申請的設備號個數;name是此類設備的名字。

5. 釋放設備號

通常在模塊清理函數中調用。

#include<linux/fs.h>
void unregister_chrdev_region(dev_t dev, unsigned int count);

6.  重要結構體

(1)cdev結構體

struct cdev
{
    struct kobject kobj;    //內嵌的kobject對象
    struct module *owner;    //所屬模塊
    struct file_operations *ops;    //文件操作結構體
    struct list_head list;
    dev_t dev;    //設備號
    unsigned int count;
};
操作cdev結構體的函數:

void cdev_init(struct cdev *cdev, struct file_operations *ops);
//用於初始化已分配的cdev結構,並建立cdev和file_operations之間的連接

struct cdev *cdev_alloc(void);
//用於動態申請一個cdev內存

int cdev_add(struct cdev *cdev, dev_t num, unsigned int count);
//向內核添加一個cdev,完成字符設備的註冊,通常在模塊加載函數中調用

void cdev_del(struct cdev *cdev);
//刪除一個cdev,完成字符設備的註銷,通常在模塊卸載函數中調用

(2)file_operations結構體

  • 是字符驅動和內核的接口,在include/linux/fs.h中定義;
  • 字符驅動都要實現一個file_operations結構體;

file_operations的主要成員:

struct module *owner:指向模塊自身(THIS_MODULE)

open:打開設備

release:關閉設備

read:從設備上讀數據

write:向設備上寫數據

ioctl:I/O控制函數

llseek:定位當前讀寫位置指針

mmap:映射設備空間到進程的地址空間

(3)file結構體

  • file_operations結構相關的一個結構體,描述一個正在打開的設備文件。

file的成員:

loff_t  f_pos:當前讀寫位置

unsigned  int  f_flags:標識文件打開時是否可讀或可寫,O_RDONLY、O_NONBLOCK、O_SYNC

struct  file_operations *f_op:文件相關的操作,指向所實現的struct  file_operations

void  *private_data:私有數據指針,驅動程序可以將這個字段用於任何目的或者忽略(設爲NULL)這個字段

(4)inode結構體

  • 內核用inode結構在內部表示文件;
  • inode與file的區別:file表示打開的文件描述符,多個表示打開的文件描述符的file結構可以指向一個inode結構。

inode的重要成員:

dev_t  i_rdev:對錶示設備文件的inode結構,該字段包含了真正的設備號

struct  cdev  *i_cdev:表示字符設備在內核中的內部結構

7. 字符設備驅動程序模版

//字符設備驅動模塊加載函數
static int __init xxx_init(void)
{
    ...
    cdev_init(&xxx_dev.cdev, &xxx_fops);    //初始化cdev
    xxx_dev.cdev.owner =  THIS_MODULE;
    //獲取字符設備編號
    if(xxx_major)
    {
        register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
    }
    else
    {
        alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
    }
    ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1);    //註冊設備
    ...
}

//字符設備驅動模塊卸載函數
static void __exit xxx_exit(void)
{
    unregister_chrdev_region(xxx_dev_no, 1);    //釋放佔用的設備號
    cdev_del(&xxx_dev.cdev);    //註銷設備
    ...
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章