嵌入式技术分享

Embedded Technology Sharing

嵌入式Linux字符设备驱动——4注册字符类设备

《嵌入式Linux字符设备驱动——4注册字符类设备》

嵌入式Linux字符设备驱动开发流程——以LED为例

1.设备模块加载及卸载
2.静态申请设备号
3.动态申请设备号
4.注册字符类设备
5.生成字符设备节点
6.完善字符类设备驱动
7.根据完善后的模板编写相关设备驱动

前言

留空

头文件

#include <linux/cdev.h>    //字符设备描述
#include <linux/slab.h>    //内存分配头文件        

内存申请kmalloc

文件(路径):未知

static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
    if (__builtin_constant_p(size)) {
        if (size > KMALLOC_MAX_CACHE_SIZE)
            return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
        if (!(flags & GFP_DMA)) {
            int index = kmalloc_index(size);

            if (!index)
                return ZERO_SIZE_PTR;

            return kmem_cache_alloc_trace(kmalloc_caches[index],
                    flags, size);
        }
#endif
    }
}       

字符设备结构体

所属文件名:cdev.h

struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

字符设备初始化

所属文件名:未知

/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
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;
}

字符类设备注册

所属文件名:未知

/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
*         device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately.  A negative error code is returned on failure.
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    p->dev = dev;
    p->count = count;
    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

字符设备卸载

所属文件名:未知

/**
* cdev_del() - remove a cdev from the system
* @p: the cdev structure to be removed
*
* cdev_del() removes @p from the system, possibly freeing the structure
* itself.
*/
void cdev_del(struct cdev *p)
{
    cdev_unmap(p->dev, p->count);
    kobject_put(&p->kobj);
}

内存释放

所属文件名:未知

void kfree(const void *x)
{
    struct page *page;
    void *object = (void *)x;

    trace_kfree(_RET_IP_, x);

    if (unlikely(ZERO_OR_NULL_PTR(x)))
        return;

    page = virt_to_head_page(x);
    if (unlikely(!PageSlab(page))) {
        BUG_ON(!PageCompound(page));
        kmemleak_free(x);
        put_page(page);
        return;
    }
    slab_free(page->slab, page, object, _RET_IP_);
}

程序

#include <linux/module.h>           //模块头文件
#include <linux/kernel.h>           //内核头文件
#include <linux/init.h>             //内核初始化
#include <linux/fs.h>               //字符设备函数
#include <linux/cdev.h>             //字符设备描述
#include <linux/kdev_t.h>           //系列设备号处理宏
#include <linux/slab.h>             //内存分配头文件

#define DEVICE_NAME     "leds"      //字符设备名称
#define DEVICE_MINOR_NUM    2       //字符设备数量

#define DEV_MAJOR   0               //主设备号
#define DEV_MINOR   0               //次设备号,0为自动分配

#define REGDEV_SIZE 3000

static int leds_major = DEV_MAJOR;  //主设备号变量
static int leds_minor = DEV_MINOR;  //次设备号变量

static dev_t leds_dev;              //设备号
struct cdev *leds_cdev;             //字符设备结构体变量

struct file_operations leds_fops = {
    .owner = THIS_MODULE,
};

static __init int leds_init(void)
{
    int ret = 0;
    int i;
    
    ret = alloc_chrdev_region(&leds_dev, leds_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
    if(ret < 0){
        printk(KERN_EMERG "register_chrdev_region req %d is failed!\n", DEV_MAJOR);
        return ret;
    }
    
    leds_major = MAJOR(leds_dev);   //主设备号
    leds_minor = MINOR(leds_dev);   //次设备号
    
    printk(KERN_EMERG "leds chrdev major=%d, minor=%d\n", leds_major, leds_minor);
    
    leds_cdev = kmalloc(DEVICE_MINOR_NUM * sizeof(struct cdev), GFP_KERNEL);
    if(leds_cdev == NULL)
    {
        printk(KERN_EMERG "kmalloc failed");
        unregister_chrdev_region(leds_dev, DEVICE_MINOR_NUM);
        return -ENOMEM;
    }
    
    //memset(leds_cdev, 0, DEVICE_MINOR_NUM * sizeof(struct dev_cdev));
    
    for(i=0; i<DEVICE_MINOR_NUM; i++){
        
        cdev_init(&leds_cdev[i], &leds_fops);
        
        leds_cdev[i].owner = THIS_MODULE;
        leds_cdev[i].ops = &leds_fops;
        
        ret = cdev_add(&leds_cdev[i], MKDEV(leds_major, leds_minor+1), 1);
        if(ret < 0){
            printk(KERN_EMERG "cdev_add %d failed!\n", i);
        }
        else{
            printk(KERN_EMERG "cdev_add %d success!\n", i);
        }
    }
    
    return ret;
}

static __exit void leds_exit(void)
{
    int i;
    for(i=0; i<DEVICE_MINOR_NUM; i++){
        cdev_del(&leds_cdev[i]);                            //注销设备
    }
    kfree(leds_cdev);                                       //释放内存
    unregister_chrdev_region(leds_dev, DEVICE_MINOR_NUM);   //注销设备号
    printk(KERN_EMERG "leds chrdev exit \n");
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE("GPL");

结束语

以上则为嵌入式Linux注册字符类设备的相关内容。

如果文章对您有帮助,欢迎移至上方按钮打赏博主;

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注