最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

linux字符設(shè)備驅(qū)動基礎(chǔ)

 更新時間:2026年02月15日 14:57:26   作者:cear  
這篇文章主要介紹了linux字符設(shè)備驅(qū)動基礎(chǔ)的相關(guān)資料,需要的朋友可以參考下

1.字符設(shè)備驅(qū)動相關(guān)系統(tǒng)函數(shù)簡介

1.1 container_of

/**
 * container_of 通過一個結(jié)構(gòu)體成員的指針,獲取包含該成員的結(jié)構(gòu)體的起始地址
 * @ptr:        變量的指針
 * @type:       指針指向的結(jié)構(gòu)體類型
 * @member:     結(jié)構(gòu)體中的變量類型
 */
#define container_of(ptr, type, member) ({              \
    void *__mptr = (void *)(ptr);                   \
    BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&   \
                     !__same_type(*(ptr), void),            \
                     "pointer type mismatch in container_of"); \
    ((type *)(__mptr - offsetof(type, member))); })

為了簡化理解,可以將 container_of 函數(shù)理解為如下表示

#define container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - offsetof(type, member)))

該函數(shù)根據(jù) 結(jié)構(gòu)體成員變量的指針,通過該變量相對于結(jié)構(gòu)體的偏移,得到了該變量對應(yīng)的結(jié)構(gòu)體的指針。

這是一個非常靈活的用法,通過保存某個結(jié)構(gòu)體變量的指針,可以通過該指針反向推出該結(jié)構(gòu)體的指針。在后續(xù)案例中有詳細(xì)解釋。

1.2 register_chrdev_region

 register_chrdev_region 是 Linux 內(nèi)核中用于注冊字符設(shè)備編號范圍的函數(shù)。該函數(shù)為驅(qū)動程序預(yù)留一段連續(xù)的設(shè)備號(主設(shè)備號 + 起始次設(shè)備號),后續(xù)將字符設(shè)備(通過  cdev_add )綁定到這些設(shè)備號上,函數(shù)原型如下

/**
 * first:dev_t 類型,指定要注冊的起始設(shè)備號。使用 MKDEV(major, minor) 宏生成
 * count:需要注冊的連續(xù)設(shè)備號數(shù)量(次設(shè)備號的范圍)
 * name:設(shè)備的名稱,該名稱會出現(xiàn)在 /proc/devices 文件中,用于標(biāo)識該組設(shè)備號屬于哪個驅(qū)動。
 * 返回值: 成功返回0
 *        參數(shù)無效返回     -EINVAL
 *        設(shè)備號被占用返回 -EBUSY
*/
int register_chrdev_region(dev_t first, unsigned int count, const char *name);

1.3 cdev_init

 struct cdev 是內(nèi)核表示字符設(shè)備的對象,每個字符設(shè)備驅(qū)動都需要創(chuàng)建 cdev 實例,并將其注冊到內(nèi)核

 cdev_init  負(fù)責(zé)設(shè)置 cdev 的基本字段,為后續(xù)的 cdev_add做準(zhǔn)備

/*
 * dev:指向要初始化的 struct cdev 結(jié)構(gòu)體的指針。
 *       該結(jié)構(gòu)體可以由驅(qū)動靜態(tài)分配 (kmalloc) ,也可以動態(tài)分配 (cdev_alloc)
 * fops:指向 struct file_operations 結(jié)構(gòu)體的指針
 *       該結(jié)構(gòu)體包含了設(shè)備支持的各種操作函數(shù)(如 open、read、write、ioctl 等)
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof *cdev);
    INIT_LIST_HEAD(&cdev->list);
    kobject_init(&cdev->kobj, &ktype_cdev_default);
    cdev->ops = fops;
}

1.4 cdev_add

該函數(shù)建立了設(shè)備號與字符設(shè)備驅(qū)動的關(guān)聯(lián),使 VFS(虛擬文件系統(tǒng))能夠通過設(shè)備號找到對應(yīng)的驅(qū)動程序,從而響應(yīng)用戶空間的打開、讀寫等操作

  • 將 struct cdev 對象與設(shè)備號綁定,在內(nèi)核的字符設(shè)備映射表中建立關(guān)聯(lián)
  • 將 cdev 對象加入內(nèi)核的全局字符設(shè)備鏈表或哈希表,使 VFS 能夠根據(jù)設(shè)備號找到對應(yīng)的cdev 
  • 激活設(shè)備:調(diào)用 cdev_add 后,該設(shè)備便可以被用戶空間訪問(需要使用mknod關(guān)聯(lián)到/dev/mydevname
/*
 * p:指向 cdev_init() 已經(jīng)初始化的 struct cdev 對象的指針。
 * dev:分配給該設(shè)備的起始設(shè)備號,應(yīng)該與 register_chrdev_region() 向內(nèi)核申請的設(shè)備號位置一致
 * count:與該設(shè)備關(guān)聯(lián)的次設(shè)備號數(shù)量
 * 返回值:成功 0
          參數(shù)無效    -EINVAL
          設(shè)備號被占用 -EBUSY (理論上在分配設(shè)備號時已經(jīng)檢查過,但這里仍可能發(fā)生,如動態(tài)添加時競爭)
          內(nèi)存不足    -ENOMEM
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

2. 使用字符設(shè)備驅(qū)動實現(xiàn)共享內(nèi)存

下面是使用字符設(shè)備驅(qū)動實現(xiàn)共享內(nèi)存的案例,用來快速熟悉驅(qū)動函數(shù)的使用方法,代碼參考書籍為Linux設(shè)備驅(qū)動詳解

#include <linux/init.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#define GLOBALMEM_SIZE    1024
#define GLOBALMEM_MAGIC    'M'
#define MEM_CLEAR        _IO(GLOBALMEM_MAGIC, 0)

struct globalmem_dev 
{
    // 字符設(shè)備
    struct cdev m_cdev;
    // 共享內(nèi)存
    unsigned char mem[GLOBALMEM_SIZE];
};


static int globalmem_major = 266;

// 存放兩個字符設(shè)備私有數(shù)據(jù)
struct globalmem_dev* globalmem_devp;


/* user open fd */
static int globalmem_open(struct inode* inode, struct file* filp) {
    
    struct globalmem_dev* dev;
    /* 下面是一種常用的區(qū)分次設(shè)備號的方法
     * 通過 globalmem_init 初始化時傳入不同的 cdev 指針實現(xiàn)區(qū)分 */
    // inode 中保存的 i_cdev 指針是 globalmem_init 函數(shù)中傳入的,globalmem_dev 結(jié)構(gòu)體變量 m_cdev 的指針
    // 所以通過 inode->i_cdev 指針,即 m_cdev 成員變量的指針,可以找到其對應(yīng)的結(jié)構(gòu)體指針
    dev = container_of(inode->i_cdev, struct globalmem_dev, m_cdev);

    // 將設(shè)備結(jié)構(gòu)體指針傳給文件私有數(shù)據(jù)指針,提供給其他函數(shù)調(diào)用時使用
    filp->private_data = dev;
    return 0;
}


/* user release fd*/
static int globalmem_release(struct inode* inode, struct file* filp) {
    return 0;
}



/* user read fd */
static ssize_t globalmem_read(struct file* filp, char __user* buf, size_t count, loff_t* ppos) {
    unsigned long p = *ppos;
    // 獲得設(shè)備結(jié)構(gòu)體的指針(這是通過open函數(shù)傳入的指針,實現(xiàn)不同次設(shè)備使用不同的共享內(nèi)存)
    struct globalmem_dev* dev = filp->private_data;    


    if(p >= GLOBALMEM_SIZE)
        return 0;
    if(count > GLOBALMEM_SIZE - p)
        count = GLOBALMEM_SIZE - p;
    
    copy_to_user(buf, (void*)(dev->mem + p), count);
    *ppos = p + count;

    return count;
}


/* user write fd */ 
static ssize_t globalmem_write(struct file* filp, const char __user* buf, size_t count, loff_t* ppos) {
    unsigned long p = *ppos;

    // 獲得設(shè)備結(jié)構(gòu)體的指針
    struct globalmem_dev* dev = filp->private_data;    

    if(p >= GLOBALMEM_SIZE)
        return 0;
    if(count > GLOBALMEM_SIZE - p)
        count = GLOBALMEM_SIZE - p;

    copy_from_user(dev->mem + p, buf, count);
    *ppos = p + count;
    return count;
}


/* user lseek fd */
static loff_t globalmem_llseek(struct file* filp, loff_t offset, int orig) {
    loff_t ret;
    switch(orig) {
    // 從起始位置開始移動指針
    case 0:
        if(offset < 0) {
            ret = -EINVAL;
            break;
        }
        if((unsigned int)offset > GLOBALMEM_SIZE) {
            ret = -EINVAL;
            break;
        }
        filp->f_pos = (unsigned int)offset;
        ret = filp->f_pos;
        break;
    // 從當(dāng)前位置開始移動指針
    case 1:
        if((filp->f_pos + offset) > GLOBALMEM_SIZE) {
            ret = -EINVAL;
            break;
        }
        if((filp->f_pos + offset) < 0) {
            ret = -EINVAL;
            break;
        }
        filp->f_pos += offset;
        ret = filp->f_pos;
        break;
    default:
        ret = -EINVAL;
    }
    return ret;
}


/* user ioctl fd */
static int globalmem_ioctl(struct inode* inodep, struct file* filp, unsigned int cmd, unsigned long arg) {
    // 獲取設(shè)備結(jié)構(gòu)體指針    
    struct globalmem_dev* dev = filp->private_data;    
    
    switch(cmd) {
    case MEM_CLEAR:
        memset(dev->mem, 0, GLOBALMEM_SIZE);
        break;
    default:
        return -EINVAL;
    }
    return 0;
}



static const struct file_operations globalmem_fops = {
    .owner = THIS_MODULE,
    .open = globalmem_open,
    .release = globalmem_release,
    .llseek = globalmem_llseek,
    .read = globalmem_read,
    .write = globalmem_write,
    .unlocked_ioctl = globalmem_ioctl
};


/* 設(shè)備驅(qū)動模塊insmod加載函數(shù) */
static int globalmem_init(void) {
    // 向 Linux 內(nèi)核中注冊字符設(shè)備編號范圍
    register_chrdev_region(MKDEV(globalmem_major, 0), 2, "globalmem");

    // 為2個次設(shè)備以及共享內(nèi)存分配內(nèi)存
    globalmem_devp = kmalloc(2 * sizeof(struct globalmem_dev), GFP_KERNEL);
    memset(globalmem_devp, 0, 2 * sizeof(struct globalmem_dev));
    
    // 初始化字符設(shè)備0的基本字段
    cdev_init(&(globalmem_devp[0].m_cdev), &globalmem_fops);
    globalmem_devp[0].m_cdev.owner = THIS_MODULE;
    // 將主設(shè)備號globalmem_major次設(shè)備號0,與字符設(shè)備驅(qū)動的關(guān)聯(lián)
    cdev_add(&(globalmem_devp[0].m_cdev), MKDEV(globalmem_major, 0), 1);

    // 初始化字符設(shè)備1的基本字段
    cdev_init(&(globalmem_devp[1].m_cdev), &globalmem_fops);
    globalmem_devp[1].m_cdev.owner = THIS_MODULE;
    // 將主設(shè)備號globalmem_major次設(shè)備號1,與字符設(shè)備驅(qū)動的關(guān)聯(lián)
    cdev_add(&(globalmem_devp[1].m_cdev), MKDEV(globalmem_major, 1), 1);
    return 0;
}


static int globalmem_exit(void) {
    // 注銷cdev
    cdev_del(&(globalmem_devp[0].m_cdev));
    cdev_del(&(globalmem_devp[1].m_cdev));
    // 釋放設(shè)備結(jié)構(gòu)體內(nèi)存
    kfree(globalmem_devp);    
    // 釋放設(shè)備號
    dev_t devno = MKDEV(globalmem_major, 0);
    unregister_chrdev_region(devno, 2);
}


MODULE_AUTHOR("cear");
MODULE_LICENSE("GPL");

module_param(globalmem_major, int, S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);

到此這篇關(guān)于linux字符設(shè)備驅(qū)動基礎(chǔ)的文章就介紹到這了,更多相關(guān)linux字符設(shè)備驅(qū)動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

永胜县| 哈尔滨市| 米林县| 全椒县| 高邮市| 新余市| 临夏市| 昌乐县| 雷州市| 全椒县| 奈曼旗| 汝南县| 台南县| 上犹县| 宜君县| 上杭县| 双鸭山市| 苍南县| 普安县| 彭泽县| 尚志市| 林口县| 名山县| 常德市| 福海县| 凤台县| 青铜峡市| 张家界市| 锦州市| 云和县| 天水市| 额尔古纳市| 广昌县| 商都县| 鸡西市| 江北区| 勐海县| 云龙县| 祥云县| 灵川县| 沭阳县|