《linux设备驱动》笔记3——字符驱动实例

虽然可以照着书上写一个字符驱动,但是技术太渣,所以还是网上找了一段代码:
https://www.cnblogs.com/Charles-Zhang-Blog/archive/2013/12/02/3454382.html

#include <linux/module.h> //模块所需的大量符号和函数定义
#include <linux/init.h> //指定初始化和清除函数
#include <linux/fs.h> //文件系统相关的函数和头文件
#include <linux/cdev.h> //cdev结构的头文件
#include <asm/uaccess.h> //在内核和用户空间中移动数据的函数
#include <linux/slab.h>
#include <linux/device.h>

MODULE_LICENSE("GPL"); //指定代码使用的许可证

//文件操作函数的声明
int my74hc595_open(struct inode *, struct file *);
int my74hc595_release(struct inode *, struct file *);
ssize_t my74hc595_read(struct file *, char *, size_t, loff_t *);
ssize_t my74hc595_write(struct file *, const char *, size_t, loff_t *);

int dev_major = 1222; //指定主设备号,这个改了也是253?
int dev_minor = 0; //指定次设备号

static struct class *firstdrv_class;
static struct device *firstdrv_class_dev;

struct cdev *my74hc595_cdev; //内核中表示字符设备的结构
int *gp_testdata;//测试用数据

struct file_operations my74hc595_fops =
{
    owner: THIS_MODULE,
    open:my74hc595_open,
    read:my74hc595_read,
    release:my74hc595_release,
    write: my74hc595_write
};

static void __exit my74hc595_exit(void) //退出模块时的操作
{
    dev_t devno=MKDEV(dev_major, dev_minor); //dev_t是用来表示设备编号的结构

    cdev_del(my74hc595_cdev); //从系统中移除一个字符设备
    kfree(my74hc595_cdev); //释放自定义的设备结构
    kfree(gp_testdata);
    unregister_chrdev_region(devno, 1); //注销已注册的驱动程序

    device_unregister(firstdrv_class_dev); //删除/dev下对应的字符设备节点
    class_destroy(firstdrv_class);

    printk("my74hc595 unregister success\n");
}

static int __init my74hc595_init(void) //初始化模块的操作
{printk("my74hc595_init\n");
    int ret, err;
    dev_t devno;
    //操作了两种方法来分配设备号,最好是使用动态分配
#if 0 
    //动态分配设备号,次设备号已经指定。函数返回dev_t结构体用以保存设备号
    ret=alloc_chrdev_region(&devno, dev_minor, 1, "my74hc595");
    //保存动态分配的主设备号
    dev_major=MAJOR(devno);

#else
        //根据期望值分配设备号
    devno=MKDEV(dev_major, dev_minor);
    ret=register_chrdev_region(devno, 1, "my74hc595");
#endif

    if(ret<0)
    {
        printk("my74hc595 register failure\n");
        //my74hc595_exit(); //如果注册设备号失败就退出系统
        return ret;
    }
    else
    {
        printk("my74hc595 register success\n");
    }

    gp_testdata = kmalloc(sizeof(int), GFP_KERNEL);
    //给要存储的数据分配内存
#if 0//两种初始化字符设备信息的方法
    my74hc595_cdev = cdev_alloc();//调试时,此中方法在rmmod后会出现异常,原因未知
    //调试并未发现异常
    my74hc595_cdev->ops = &my74hc595_fops;
#else
    my74hc595_cdev = kmalloc(sizeof(struct cdev), GFP_KERNEL);
    cdev_init(my74hc595_cdev, &my74hc595_fops);
#endif

    my74hc595_cdev->owner = THIS_MODULE; //初始化cdev中的所有者字段

    err=cdev_add(my74hc595_cdev, devno, 1); //向内核添加这个cdev结构的信息
    if(err<0)
    printk("add device failure\n"); //如果添加失败打印错误消息
//建立类
    firstdrv_class = class_create(THIS_MODULE, "my74hc595");
//建立类下面的设备
    firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(dev_major, 0), NULL,"my74hc595-%d", 1);//在/dev下创建节点

    printk("register my74hc595 dev OK\n");

    return 0;
}
//打开设备文件系统调用对应的操作
int my74hc595_open(struct inode *inode, struct file *filp)
{printk("my74hc595_open\n");
    //将file结构中的private_data字段指向已分配的设备结构
    filp->private_data = gp_testdata;
    printk("open my74hc595 dev OK\n");
    return 0;
}
//关闭设备文件系统调用对应的操作
int my74hc595_release(struct inode *inode, struct file *filp)
{printk("my74hc595_release\n");
    printk("close my74hc595 dev OK\n");
    return 0;
}
//读设备文件系统调用对应的操作
ssize_t my74hc595_read(struct file *filp, char *buf, size_t len, loff_t *off)
{printk("my74hc595_read\n");
    //获取指向已分配数据的指针
    unsigned int *p_testdata = filp->private_data;
    //将设备变量值复制到用户空间
    if(copy_to_user(buf, p_testdata, sizeof(int)))
    {
    return -EFAULT;
    }
    printk("read my74hc595 dev OK\n");
    return sizeof(int); //返回读取数据的大小
}
//写设备文件系统调用对应的操作
ssize_t my74hc595_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{printk("my74hc595_write\n");
    //获取指向已分配数据的指针
    unsigned int *p_testdata = filp->private_data;
    //从用户空间复制数据到内核中的设备变量
    if(copy_from_user(p_testdata, buf, sizeof(int)))
    {
    return -EFAULT;
    }
    printk("write my74hc595 dev OK\n");
    return sizeof(int); //返回写数据的大小
    }

module_init(my74hc595_init); //模块被装载时调用my74hc595_init
module_exit(my74hc595_exit); //模块被卸载时调用my74hc595_exit

这段代码写的很详细了,而且也有备注。只需要自己写个Makefile和改动driver的Makefile就可以用了;自行百度。

我的理解就是创建了一个字符设备,然后给了字符设备几个操作函数。这应该就是字符设备驱动干的事情;
首先想一下应用程序要干什么事情:
1、打开字符设备
2、写进去值
3、读出来值
实现方法:
1、open
2、write
3、read
以上都是有函数提供的——不带用户缓存的文件操作函数
那么驱动的作用就是要实现用户程序在内核中的实现:
1、创建一个字符设备
2、打开这个设备
3、写入
4、读取
接下来分别说明:
1、创建一个字符设备(init函数):
首先你需要给设备一个dev_t(主次编号)两种方法,要么随机的动态分发,要么自己指定;
然后你要把已经得到设备号的设备注册进内核;
最后有一部在《linux设备驱动》的字符驱动那一章没有说就是建立class和class_dev
这一步是要在./dev下面建立设备节点,必不可少。
现在为止你已经得到了一个可以操作的字符设备了,你可以在/dev下面看到这个设备,在sys/class下面可以看到你建立的类和设备
(顺便说一下 有时候rmmod系统会崩)
2、然后建立一个open函数 write函数 read函数。我都是直接函数里面return 0;亲测可用;但是书里面写的很清楚,open该干嘛,所以各位该干嘛干嘛

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值