5、注册字符类设备

字符设备

cdev结构体

Linux中使用cdev结构体描述一个字符设备。结构体定义在include/linux/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 函数初始化 cdev结构体,成员变量,建立cdev 和file_operations 之间的联系

使用cdev_add 函数向系统添加一个cdv结构体,也就是添加一个字符设备。

使用cdev_delete函数删除一个字符设备;

file_operations结构体:

对设备节点进行文件操作时,最终会调用 设备驱动里的 file_operations 结构体里的文件操作函数:

例如:open、 release、read、write、ioctl、prode;

file_operations 结构体定义在include/linux/fs.h文件中

一个简化的 file_operations 结构体的定义示例:

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 *);  
    ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);  
    ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);  
    int (*iterate) (struct file *, struct dir_context *);  
    unsigned int (*poll) (struct file *, struct poll_table_struct *);  
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);  
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);  
    int (*mmap) (struct file *, struct vm_area_struct *);  
    int (*open) (struct inode *, struct file *);  
    int (*flush) (struct file *, fl_owner_t id);  
    int (*release) (struct inode *, struct file *);  
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);  
    int (*fasync) (int, struct file *, int);  
    int (*lock) (struct file *, int, struct file_lock *);  
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);  
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);  
    int (*check_flags)(int);  
    int (*flock) (struct file *, int, struct file_lock *);  
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);  
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);  
    int (*setlease)(struct file *, long, struct file_lock **);  
    long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);  
    void (*show_fdinfo)(struct seq_file *m, struct file *f);  
    #ifdef CONFIG_AIO  
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);  
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);  
    int (*aio_fsync) (struct kiocb *, int datasync);  
    #endif  
    /* ... 其他可能的成员函数 ... */  
};

设备节点

每个设备在Linux系统中都有一个设备文件,这个设备文件就是通常所说的设备节点,应用程序通过操作这个设备文件,便可以操作相对应的硬件;

设备节点创建在、dev目录下;

创建设备节点的方式

      1、手动创建

     

  mknod  /dev/test c 236 0

2、函数中自动创建

        可以通过mdev机制自动创建

udev机制:

        Linux中通过udev来实现设备节点的创建和删除,udev是一个用户程序,可以根据系统中设备的状态来创建或者删除设备节点,比如说,如果驱动程序成功加载到Linux时,会自动在/dev目录下创建相对应的设备节点,当驱动程序卸载时,会自动删除/dev目录下的设备节点,

在嵌入式linux系统中使用mdev,mdev时udev的简化板本,在使用busybox构建根文件系统时,busybox会自动创建mdev

相关函数

 class_create函数

        函数定义在 include/linux/device.h文件当中,使用这个函数,将会在系统的/sys/class 下创建一个文件;

#include <linux/device.h>  
  
struct class *class_create(struct module *owner, const char *name);

device_create 函数

        使用class_create 创建好类之后,还需要 device_create 函数在类下面创建一个设备。定义在include/linux/device.h文件中
 

#include <linux/device.h>  
  
struct device *device_create(struct class *cls, struct device *parent,  
                             dev_t devt, void *drvdata,  
                             const char *fmt, ...);

device_destroy 函数

        删掉创建的设备

 class_destroy 函数

        删掉创建的类

内核空间和用户空间的数据交换

应用层和内核不能直接进行数据传输,可以使用 copy_from_user,copy_to_user,需要包含头文件 Linux/uaccess.h,这些函数提供了对访问违规(如地址无效或越界)的检查,从而增加了内核的稳定性。

copy_to_user

        内核空间数据copy到用户空间

#define copy_to_user(to, from, n) _copy_to_user((to), (from), (n))  

copy_from_user

        用户空间copy到内核空间

例程

#include <linux/module.h>
#include <linux/init.h>
 
#include <linux/fs.h>
#include <linux/modulepram.h>
#include <linux/kdev_t.h>

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/asm-generic/uaccess.h>  

 
static int major;
static int minor; 
 
 
module_param(major,int,S_IRUGO);
MODULE_PARAM_DESC(major,"e.g:major=1");
 
module_param(minor,int,S_IRUGO);
MODULE_PARAM_DESC(minor,"e.g:minor=1");
 
 
static struct cdev cdev_test;
 
struct class *class;
struct device *device;



static  int cdev_test_open(struct inode *node, struct file *file)
{
     printk("cdev_test_open \n");
     return 0;
}

static int cdev_test_release(struct inode *node, struct file *file)
{

     printk("cdev_test_release \n");
     return 0;

}

static ssize_t cdev_test_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{
    char kbuf[64]="123456hello";
    
    copy_to_user(buf,kbuf,strlen(kbuf));

     printk("cdev_test_read \n");


}  

static ssize_t cdev_test_write (struct file *file, const char __user *buf, size_t size, loff_t *off)
{

     char kbuf[64];
     copy_from_user(kbuf,buf,size);
     printk("write buf[%s]\n",kbuf);
     printk("cdev_test_write\n");




}


struct file_operations cdev_test_ops={

    .owner=THIS_MODULE,
    .open=cdev_test_open,
    .release=cdev_test_release,
    .read=cdev_test_read,
    .write=cdev_test_write,
    .ioctl=cdev_test_ioctl
};

 
static int cdv_init(void)
{
    dev_t dev_num;
    int ret;
    printk("input major=%d minor=%d \n",major,minor);
 
 
    if(major == 0)
    {
        //静态申请
        dev_num=MKDEV(major,minor);
        ret=register_chrdev_region(dev_num,1,"chrdev_num");
        if(0 != ret)
        {
            printk("register_chrdev_region err %d\n",ret);
            return ret;
        }
    }
    else
    {
       //动态申请
        ret=alloc_chrdev_region(&dev_num,0,1,"chrdev_num");
        if(0 != ret)
        {
            printk("alloc_chrdev_regionerr %d\n",ret);
            return ret;
        }
       printk("dev_num=%d \n",dev_num);
       major = MAJOR(dev_num);
       minor = MINOR(dev_num);
       printk("input major=%d minor=%d \n",major,minor);
 
    }

    cdev_test.owner= THIS_MODULE;
    cdev_init(&cdev_test,&&cdev_test_ops);  
    cdev_add(&cdev_test,dev_num,1);

    class = class_create(THIS_MODULE,"test");
    device = device_create(class,NULL,dev_num,NULL,"/dev/test");
    
    
    printk("cdv init\n");
    return 0;
}
 
static void cdv_exit(void)
{

    //释放设备号
    unregister_chrdev_region(dev_num,1);
    cdev_del(&cdev_test);

    device_destroy(class,dev_num);
    classs_destroy(class);

 
    printk("cdv exit\n");
    return 0;
}
 
 
module_init(cdv_init);
module_exit(cdv_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SONG");
MODULE_VERSION("v1.0");

APP应用程序

app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int arc, char *argv[])
{
    int fd;
    char buf[64]={0};
    fd = open("/dev/test",O_RDWR);
    if(fd < 0)
    {
        printf("open err\n");
    }

    read(fd,buf,sizeof(buf));
    printf("read [%s]\n",buf);
    
    char buf1[64]="4578kkk";
    write(fd,buf1,strlen(buf1));
   
    close(fd);
    return 0;


}

  • 18
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值