Linux驱动开发之字符设备

字符设备是驱动开发中最简单的一种设备(通过字符设备来写寄存器,从而起到一个驱动的作用),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.o
obj-m := chardevice.o
else
KDIR := /lib/modules/$(shell uname -r)/build
all:
        make -C $(KDIR) M=$(PWD) modules
clean:
        rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
如何把这个模块加载到内核呢?
Makefile写好了之后,直接make,这时候可以看到生成了chardevice.ko文件。
运用insmod chardevice.ko,就加载进内核了。(root用户)
这个时候vim  /proc/devices就可以看到刚才那个设备的设备号和设备名字:(root用户)

设备号为230,名字为globalmem
然后运用卸载工具rmmod chardevice 就卸载了这个设备。(root)




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值