Linux驱动开发之字符设备

来源:互联网 发布:js获取所有span 编辑:程序博客网 时间:2024/05/16 03:04

字符设备是驱动开发中最简单的一种设备(通过字符设备来写寄存器,从而起到一个驱动的作用),struct cdev结构体是内核用来描述一个字符设备的,是内核已经封装好的,程序员只需要直接定义一个这样的结构体变量即可。

struct cdev {
struct kobject kobj;          // 每个cdev 都有一个 kobject
struct module *owner;       // 指向实现驱动的模块
const struct file_operations *ops;   // 操纵这个字符设备文件的方法
struct list_head list;       // 与cdev 对应的字符设备文件的 inode->i_devices 的链表头
dev_t dev;                  // 起始设备编号
unsigned int count;       // 设备范围号大小
};

struct 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 (*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 (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);

};

接下来在虚拟机上写一个字符设备,代码如下:

chardevice.cpp

#include<linux/module.h>#include<linux/fs.h>#include<linux/init.h>#include<linux/cdev.h>#include<linux/slab.h>#include<linux/uaccess.h>#define GLOBALMEM_SIZE 0x1000#define MEM_CLEAR 0x1#define GLOBALMEM_MAJOR 230                                         //设备号
static int globalmem_major = GLOBALMEM_MAJOR;module_param(globalmem_major,int,S_IRUGO);struct globalmem_dev{        struct cdev cdev;                                                           //描述字符设备的结构体        unsigned char mem[GLOBALMEM_SIZE];};
struct globalmem_dev* globalmem_devp;static int globalmem_open(struct inode* inode,struct file* filp){        filp->private_data = globalmem_devp;        return 0;}static int globalmem_release(struct inode* inode,struct file* filp){        return 0;}
static long globalmem_ioctl(struct file* filp,unsigned int cmd,unsigned long arg){        struct globalmem_dev* dev = filp->private_data;        switch(cmd)        {                case MEM_CLEAR:                        memset(dev->mem,0,GLOBALMEM_SIZE);                        printk(KERN_INFO"globalmem is set to zero\n");                        break;                default:                        return -EINVAL;        }        return 0;}
static ssize_t globalmem_read(struct file* filp,char __user* buf,size_t size,loff_t* ppos){        unsigned long p = *ppos;        unsigned int count = size;        int ret = 0;        struct globalmem_dev* dev = filp->private_data;        if(p >= GLOBALMEM_SIZE)                return 0;        if(count> GLOBALMEM_SIZE - p)                count = GLOBALMEM_SIZE - p;        if(copy_to_user(buf,dev->mem + p,count) )                ret = -EFAULT;        else        {                *ppos = *ppos + count;                ret = count;                printk(KERN_INFO"read %u bytes(s) from %lu\n",count,p);        }        return ret;}
static ssize_t globalmem_write(struct file* filp,const char __user* buf,size_t size,loff_t * ppos){        unsigned long p =  *ppos;        unsigned int count = size;        int ret = 0;        struct globalmem_dev* dev = filp->private_data;        if(p >= GLOBALMEM_SIZE)                return 0;        if(count > GLOBALMEM_SIZE - p)                count = GLOBALMEM_SIZE - p;        if( copy_from_user(dev->mem+p, buf, count) )                ret = -EFAULT;        else        {                *ppos += count;                ret = count;                printk(KERN_INFO"write %u bytes(s) from %lu\n",count,p);        }        return ret;}
static const struct file_operations globalmem_fops = {        .owner = THIS_MODULE,        .read = globalmem_read,        .write = globalmem_write,        .unlocked_ioctl = globalmem_ioctl,        .open = globalmem_open,        .release = globalmem_release,};static void globalmem_setup_cdev(struct globalmem_dev* dev,int index){        int err, devno = MKDEV(globalmem_major,index);        cdev_init(&dev->cdev,&globalmem_fops);        //初始化字符设备        dev->cdev.owner = THIS_MODULE;                            err = cdev_add(&dev->cdev,devno,1);              //向系统添加一个cdev        if(err)        {                printk(KERN_NOTICE"Error  %d adding globalmem%d",err,index);        }}
static int __init globalmem_init(void){        int ret;        dev_t devno = MKDEV(globalmem_major,0);        if(globalmem_major)        {                ret = register_chrdev_region(devno,1,"globalmem");  //申请设备号(知道起始设备号)        }
        else        {                ret = alloc_chrdev_region(&devno,0,1,"gobalmem");//申请设备号(不知道起始设备号)                globalmem_major = MAJOR(devno);        }        if(ret < 0)                return ret;        globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);//为设备申请空间        if(!globalmem_devp)        {                ret = -ENOMEM;                goto fail_malloc;        }        globalmem_setup_cdev(globalmem_devp,0);        return 0;        fail_malloc:
        unregister_chrdev_region(devno,1);        return ret;}module_init(globalmem_init);
static void __exit globalmem_exit(void){        cdev_del(&globalmem_devp->cdev);            //释放cdev        kfree(globalmem_devp);        unregister_chrdev_region( MKDEV(globalmem_major,0), 1);   //释放设备号}module_exit(globalmem_exit);MODULE_AUTHOR("liwanchao");
MODULE_LICENSE("GPL");
Makefile如下
ifneq ($(KERNELRELEASE),)param-objs:= chardevice.oobj-m := chardevice.oelseKDIR := /lib/modules/$(shell uname -r)/buildall:        make -C $(KDIR) M=$(PWD) modulesclean:        rm -f *.ko *.o *.mod.o *.mod.c *.symversendif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
如何把这个模块加载到内核呢?
Makefile写好了之后,直接make,这时候可以看到生成了chardevice.ko文件。
运用insmod chardevice.ko,就加载进内核了。(root用户)
这个时候vim  /proc/devices就可以看到刚才那个设备的设备号和设备名字:(root用户)
设备号为230,名字为globalmem
然后运用卸载工具rmmod chardevice 就卸载了这个设备。(root)

1 0
原创粉丝点击