1. 程式人生 > >字符設備驅動

字符設備驅動

%s 比較 stat 夢想 字符 truct name 出口 hrd

1.驅動中如何描述一個對象

  由結構體描述

struct cdev {
            struct kobject kobj;  //kobject  kset 文件系統 ---sysfs文件系統
            struct module *owner; //THIS_MODULE
            const struct file_operations *ops; //操作方法集
            struct list_head list; //鏈表,用來管理字符設備
            dev_t dev; //設備號  typedef unsigned long  u_long;  32位   ------主設備號 + 次設備號
unsigned int count; // //隸屬於同一主設備號的次設備號的個數 12 20 };

應用層和驅動層的邏輯關系如圖技術分享圖片

在驅動中通過函數指針實現對硬件的操作

    struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int); //定位
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); //
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); // long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //ioctl long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //向下兼容linux內核 int (*mmap) (struct file *, struct
vm_area_struct *); //顯示屏 - 音視頻驅動的比較多 int (*open) (struct inode *, struct file *); //打開 int (*release) (struct inode *, struct file *); //關閉 }; const struct file_operations *ops; //操作方法集

字符設備的定義和初始化:

1.定義一個字符設備 struct cdev* cdev;

2.結構體的指針並沒有實際分配內存,所以需要對cdev分配內存,在驅動中使用kzalloc或者直接調用cdev_alloc分配內存空間

3.初始化字符設備void cdev_init(struct cdev *cdev, const struct file_operations *fops),第二個參數可自己定義,結構體中的方法可不全部使用,使用.xxx的方式來使用

設備號的獲取:

  在linux中設備號是唯一用來區分設備是幹什麽的,相當於設備的身份證,設備好號分為主設備號和次設備號,主設備號表示一類設備,次設備號表示某個設備

  在驅動中使用dev_t dev 獲取主設備號使用 MAJOR(dev) 獲取次設備號MINOR(dev) 制作設備號使用MKDEV(ma,mi)

設備號的動態註冊(系統分配) 

  ---------寫代碼的時候,以框架為主題,然後框架當中你的接口需要什麽類型,你就給他什麽類型。

  有註冊一定要有註銷

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
        函數的功能;動態獲取設備號
        參數:
            @dev : 設備號:函數返回之後會將設備號保存到dev中
            @baseminor :最小的次設備號的基地址 0
            @count  :設備的個數 3  
            @name    :自己填充,最好見名知意
        返回值:成功返回0
            失敗返回錯誤碼 

設備號的靜態註冊(自己指定)

例子

靜態註冊設備號: 
        int major = 249;
        MKDEV(major,0);
        int register_chrdev_region(dev_t from, unsigned count, const char *name)
        函數的功能;靜態獲取設備號
        參數:
            @from : 指定的主設備號:
            @count  : 設備的個數 3  
            @name    :自己填充,最好見名知意
        返回值:成功返回0
            失敗返回錯誤碼 

設備號的註銷:

void unregister_chrdev_region(dev_t from, unsigned count)
        功能:註銷設備號
        參數:
        * @from:  被註銷的第一個設備值,就是你指定的主設備號的值
        * @count: 設備數量

添加一個字符設備到系統當中去:

添加進系統是我們最初的夢想,現在想來cdev_add需要 cdev我們定義了cdev--》分配內存--》cdev初始化 ,第二個參數需要dev_t 我們動態/靜態註冊設備號

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    函數的功能;添加一個字符設備到系統當中去
    參數:
        @p         :字符設備 
        @dev     :設備號
        @count  :個數
    返回值:失敗返回負值 
            成功返回0

貼上全部的代碼

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/cdev.h>

//定義一個字符設備對象
struct cdev *cdev; 
dev_t dev;
unsigned int baseminor = 0;
unsigned int count = 1;
const char * name = "demochar";



//向上層提供的接口
//向下層操作硬件----在接口裏去操作硬件

const struct file_operations fops = {

};

static int __init demo_init(void)
{
    int retval;
    printk("----%s---%d.\n",__func__,__LINE__);

    //1、為字符設備分配空間
    cdev = cdev_alloc();
    if(cdev == NULL){
        printk("cdev_alloc faild.\n");
        return -ENOMEM;
    }

    //2、初始化字符設備
    cdev_init(cdev,&fops);

    //3、動態申請設備號
    retval = alloc_chrdev_region(&dev, baseminor, count, name);
    if(retval){
        printk("alloc_chrdev_region faild.\n");
        goto err0;
    }
    printk("major :%d.\n",MAJOR(dev));

    //4、添加到內核
    retval = cdev_add(cdev, dev, count);
    if(retval){
        printk("cdev_add failed.\n");
        goto err1;
    }
    
    //5、硬件初始化

    
    return 0;

err1:
    unregister_chrdev_region(dev, count);
err0:
    return retval;
    
}

static void __exit demo_exit(void)
{
    printk("----%s---%d.\n",__func__,__LINE__);

    cdev_del(cdev);
    unregister_chrdev_region(dev, count);

}

//入口函數
module_init(demo_init);
//出口函數
module_exit(demo_exit);
//GPL許可協議
MODULE_LICENSE("GPL");

重點來了 register_chrdev

我們上面說的註冊流程有點啰嗦,聰明的人,將其封裝成一個接口供我們使用,但是我們知其然還要知道其所以然否則以後出錯都不知道怎麽debug

register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)	
		功能: 創建並註冊一個字符設備
		參數: 
			major :主設備號
				major == 0;      動態分配 
				0 < major < 254 ;靜態分配
			name  :設備的名字
			fops  :操作方法集
		返回值:
				 * If @major == 0 返回主設備號major 
				 * If @major > 0 成功返回0
				失敗返回錯誤碼

  

13、釋放設備號:
static inline void unregister_chrdev(unsigned int major, const char *name)
@major :主設備號
@name :名字
```

太簡短了。。。

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/cdev.h>

//定義一個字符設備對象
struct cdev *cdev; 

unsigned int major = 0;

const char * name = "demochar";

//向上層提供的接口
//向下層操作硬件----在接口裏去操作硬件

const struct file_operations fops = {
    
};

static int __init demo_init(void)
{
    printk("----%s---%d.\n",__func__,__LINE__);


    major = register_chrdev(0, name, &fops);
    if(major <= 0){
        printk("register_chrdev failed.\n");
        return major;
    }
    printk("major :%d.\n",major);
    
    //5、硬件初始化
    
    return 0;

    
}

static void __exit demo_exit(void)
{
    printk("----%s---%d.\n",__func__,__LINE__);

    unregister_chrdev(major, name);
}

//入口函數
module_init(demo_init);
//出口函數
module_exit(demo_exit);
//GPL許可協議
MODULE_LICENSE("GPL");

字符設備驅動