Step:
1.设备驱动程序通常需要一个入口函数(通俗理解就是加载此驱动程序模块就会执行的函数,通常做一些初始化操作),用module_init(firstdrv_init)修饰。
2.同样的需要一个出口函数(在模块卸载时调用,通常做与初始化相反的卸载工作。用module_exit(firstdrv_exit)修饰。
3.定义初始化函数.
static int firstdrv_init(void)
{
...
}
所做的事情通常为:
a.为一个字符驱动获取一个或多个设备编号。
int register_chrdev_region(dev_t from, unsigned count, const char *name)
int alloc_chrdev_region(dev_t * dev, unsigned baseminor, unsigned count, const char * name)
两者的区别是,前者为静态分配后者为动态分配。
b.初始化字符设备结构体,通常要定义相应的文件操作结构体(稍后介绍),以使它们关联,使用之前必须定义cdev结构体。
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
c.添加字符设备到系统
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
d.为驱动注册一个类,需要先定义这个类结构体,在2.6.32内核里的定义应区别与更早更早的版本
class_register(class);
static struct class xxx_class = {
.name = "xxx",
...
};
e.创建类下的设备
struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...)
上面五个为基本步骤,在卸载函数里应该做相反的动作,稍后介绍。
因为本程序旨在写一个LED字符驱动程序,所以在初始化函数里应该将
将LED所在IO地址空间映射到内核的虚拟地址空间上去,便于访问,调用void *ioremap(unsigned long phys_addr, unsigned long size)
4.定义出口函数.
static void firstdrv_exit(void)
{
...
}
通常做与注册相反的操作:
a.去除类下的设备
void device_destroy(struct class *class, dev_t devt)
b.去除类
void class_destroy(struct class *cls)
c.字符设备删除
void cdev_del(struct cdev *p)
d.释放原先申请的设备号
void unregister_chrdev_region(dev_t from, unsigned count)
一般的操作结束之后,本驱动还要去除io映射
void iounmap(*io_addr)
5.定义与字符设备相关联的操作函数结构体
static struct file_operations firstdrv_ops = {
.owner = THIS_MODULE,
.open = xxx_open,
.write = xxx_write,
.read = xxx_read,
...
};
6.实现各xxx函数,其中包括设备文件被打开时会调用的xxx_open函数,被关闭时会调用的xxx_release函数,被读/写数据时的xxx_read/xxx_write函数等。
本驱动程序在打开时,需要配置寄存器为输出模式(实现xxx_open函数),参照数据手册:
因为LED被配置为输入模式,只需要接受用户空间传到内核空间的数据,所以还需要实现xxx_write函数。根据不同的值设置寄存器即可点亮或者关闭LED。其中会用到函数
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
在像用户空间传数据时还会用到
static inline long copy_to_user(void __user *to, const void *from, unsigned long n)。
7.包含必要的头文件,遵循GPL协议
MODULE_LICENSE("GPL");
具体实现代码如下:
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <asm/uaccess.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #define FIRST_DEV MKDEV(250, 0)
- volatile unsigned long *gpbcon = NULL;
- volatile unsigned long *gpbdat = NULL;
- static struct cdev firstdrv_cdev;
- static struct class_device *firstdrv_class_dev;
- static int firstdrv_open(struct inode *inode, struct file *file)
- {
- printk(KERN_NOTICE "Device opened!\n");
- /* 配置为输出 */
- *gpbcon &= ~((0x3<<(5*2)) |(0x3<<(6*2)) |(0x3<<(7*2)) |(0x3<<(8*2)));
- *gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
- return 0;
- }
- static ssize_t firstdrv_write(struct file *file, const char __user *userbuf,
- size_t bytes, loff_t *off)
- {
- int val;
- copy_from_user(&val, userbuf, bytes);
- if (1 == val) {
- *gpbdat &= ~((0x1<<5) | (0x1<<6) | (0x1<<7) | (0x1<<8));
- } else {
- *gpbdat |= ((0x1<<5) | (0x1<<6) | (0x1<<7) | (0x1<<8));
- }
-
- return 0;
- }
- static struct class firstdrv_class = {
- .name = "firstdrv_class",
- };
- static struct file_operations firstdrv_ops = {
- .owner = THIS_MODULE,
- .open = firstdrv_open,
- .write = firstdrv_write,
- };
- static int firstdrv_init(void)
- {
- int ret;
- ret = register_chrdev_region(FIRST_DEV, 1, "firstdrv");
- if (ret) {
- printk(KERN_ERR "Unable to register firstdrv\n");
- goto err_reg;
- }
- cdev_init(&firstdrv_cdev, &firstdrv_ops);
- ret = cdev_add(&firstdrv_cdev, FIRST_DEV, 1);
- if (ret) {
- printk(KERN_ERR "Unable to add cdev\n");
- goto err_add_cdev;
- }
- class_register(&firstdrv_class);
-
- /* /dev/first */
- device_create(&firstdrv_class, NULL, FIRST_DEV, NULL, "first");
- gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);
- gpbdat = gpbcon + 1;
-
- return 0;
-
- err_add_cdev:
- cdev_del(&firstdrv_cdev);
- err_reg:
- unregister_chrdev_region(FIRST_DEV, 1);
- return 0;
- }
- static void firstdrv_exit(void)
- {
- device_destroy(&firstdrv_class, FIRST_DEV);
- class_destroy(&firstdrv_class);
- cdev_del(&firstdrv_cdev);
- unregister_chrdev_region(FIRST_DEV, 1);
- iounmap(gpbcon);
- }
- module_init(firstdrv_init);
- module_exit(firstdrv_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Angrad Young");
文章说明:
此博文只为总结分享学习经验,许多内容不能也并未详细描述,如每个函数的参数、返回值,何为内核空间、用户空间,怎么操作寄存器等等,这些知识点都在相关资料中能得到更专业、详细的解答。
阅读(2470) | 评论(1) | 转发(2) |