1.5 驱动设备类文件创建

1、class结构体

(1)内核通过class_create(…)函数,创建一个类,这个类存放于/sys中

(2)device_create(…)函数来在/dev目录下创建相应的设备节点。

(3)在加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sys下寻找对应的类从而创建设备节点。

struct class {
        const char              *name;                                         // 类名称
        struct module           *owner;                                        // 类所属的模块,比如 usb模块、led模块等
 
        struct class_attribute          *class_attrs;                          // 类所添加的属性
        const struct attribute_group    **dev_groups;                          // 类所包含的设备所添加的属性
        struct kobject                  *dev_kobj;                             // 用于标识 类所包含的设备属于块设备还是字符设备
 
        int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);    // 用于在设备发出 uevent 消息时添加环境变量
        char *(*devnode)(struct device *dev, umode_t *mode);                   // 设备节点的相对路径名
 
        void (*class_release)(struct class *class);                            // 类被释放时调用的函数
        void (*dev_release)(struct device *dev);                               // 设备被释放时调用的函数
 
        int (*suspend)(struct device *dev, pm_message_t state);                // 设备休眠时调用的函数
        int (*resume)(struct device *dev);                                     // 设备被唤醒时调用的函数
 
        const struct kobj_ns_type_operations *ns_type;
        const void *(*namespace)(struct device *dev);
 
        const struct dev_pm_ops *pm;                                            // 用于电源管理的函数
 
        struct subsys_private *p;                                               // 指向 class_private 结构的指针

};

2、设备文件创建的函数

/***********************************************************************
 * @description        : 创建一个class
 * @param - owner    : THIS_MODULE
 * @param - name    : 类名字
 * @return         : 成功,返回struct class的指针;其他 失败
 ***********************************************************************/
struct class *class_create(struct module *owner, const char *name);

/*******************************************************************************
 * @description        : 生成设备文件
 * @param - class    : 指向该设备应注册到的struct class的指针(上一个函数)
 * @param - parent    : 指向此新设备的父struct设备的指针(如果有),没有填NULL
 * @param - devt    : 要添加的char设备的dev_t
 * @param - drvdata    : 要添加到设备中以进行回调的数据(如果有),没有填NULL
 * @param - fmt        : 设备名称的字符串
 * @return         : 成功,返回struct device指针;其他 失败
 *********************************************************************************/
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
;

3、设备文件销毁的函数

/* 删除设备类 */
void class_destroy(struct class *cls);
 
/* 创建设备文件 */
void device_destroy(struct class *class, dev_t devt);

4、示例

dev_t s_devNo;        /* 设备号 */

static struct cdev *pcdev;
static struct class *module_class;

#define GPJ0CON    0xE0200240    //GPJ0CON物理地址
#define GPJ0DAT    0xE0200244    //GPJ0DAT物理地址

unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;

static int LED_test_open(struct inode *inode, struct file *file)
{
    //***************** 动态映射操作寄存器 *************/
    //动态映射GPJ0CON
    if (!request_mem_region(GPJ0CON, 4, "GPJ0CON"))
        return -EINVAL;
    pGPJ0CON = ioremap(GPJ0CON, 4);
    
    //动态映射GPJ0CON                                                   
    if (!request_mem_region(GPJ0DAT, 4, "GPJ0DAT"))
        return -EINVAL;


     pGPJ0DAT = ioremap(GPJ0DAT, 4);
 
    return 0;
}

static int LED_test_release(struct inode *inode, struct file *file)
{
    // 解除映射
    iounmap(pGPJ0CON);
    release_mem_region(GPJ0CON, 4);
    
    iounmap(pGPJ0DAT);

    release_mem_region(GPJ0DAT, 4);
    
    return 0;
}

//应用层和驱动之间的数据交换,copy_from_user:从用户空间复制到内核空间,copy_to_user:从内核空间复制到用户空间
static ssize_t LED_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    char kBuf[1024] = "module_test_read";
    int ret = copy_to_user(ubuf, kBuf, count);
    if (ret == 0)
        printk(KERN_DEBUG, "copy_to_user success");
    return count;
}
static ssize_t LED_test_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
    char kBuf[1024];
    int ret = copy_from_user(kBuf, ubuf, count);
    if (ret == 0)
        printk(KERN_DEBUG, "copy_from_user success");
        
    *pGPJ0CON = 0x11111111; 
    if (kBuf[0] = '1')
        *pGPJ0DAT = (0 << 3);
    else if (kBuf[0] = '0')
        *pGPJ0DAT = (1 << 3);
        
    return count;
}

struct file_operations LED_test_fops =
{
    .owner = THIS_MODULE,
    .read = LED_test_read,
    .write = LED_test_write,
    .open = LED_test_open,
    .release = LED_test_realse,
};

static int __init chrdev_LED_init(void)
{
    //printk是内核源码中用来打印信息的函数,KERN_DEBUG是打印级别
    printk(KERN_DEBUG "chrdev_LED_init");
    
    /*************** 动态申请设备号 *****************/
    int ret = alloc_chrdev_region(&s_devNo, 0, 1, "LED_MODULE_TEST");
    if (ret < 0)
    {
        printk(KERN_ERR "alloc_chrdev_region failed");
        goto chrdev_error;
    }
    
    /*************** 注册字符设备驱动 **************/
    //建立cdev和fops连接
    pcdev = cdev_alloc();
    pcdev->owner = THIS_MODULE;
    cdev_init(pcdev, &LED_test_fops);
    
    //向内核注册字符设备驱动
    ret = cdev_add(pcdev, s_devNo, 1);
    if (ret < 0)
    {
        printk(KERN_ERR "cdev_add failed");
        unregister_chrdev_region(s_devNo, 1);
        goto cdev_error;
    }
    
    /************* 创建设备类和设备文件 *************/
    module_class = class_create(THIS_MODULE, "module_class");
    if (IS_ERR(module_class))
    return -EINVAL;
  

    // 设备文件名是/dev/module_test
    device_create(module_class, NULL, s_devNo, NULL, "module_test");
    
chrdev_error:
    return -EINVAL;

cdev_error:
    cdev_del(pcdev);

    unregister_chrdev_region(s_devNo, 1);
    
class_error:   
    cdev_del(pcdev);

    unregister_chrdev_region(s_devNo, 1);           
 
    return 0;
}

static void __exit chrdev_LED_exit(void)
{
    printk(KERN_DEBUG "chrdev_LED_exit");
    
    device_destroy(module_class, s_devNo);
    class_destroy(module_class);

    //注销字符设备驱动
    cdev_del(pcdev);
    
    //释放设备号
    unregister_chrdev_region(s_devNo, 1);
}

module_init(chrdev_LED_init);
module_exit(chrdev_LED_exit);

//宏定义
MODULE_LICENSE("GPL")                     //模块的许可证
MODULE_AUTHOR("xy_L")                     //模块的作者
MODULE_DESCIPTION("chrdev LED module")    //模块的描述
MODULE_ALIAS("LED_test")                  //模块的别名

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值