驱动的加载与卸载函数
驱动加载服务函数
int major;
static int first_drv_init(void)
{
major = register_chrdev(0, "first_drv",
&first_drv_fops);
// 注册驱动,其中第一个参数为0,则表示采用系统动态分配的主设备号
//第二个参数是其注册的设备名
//第三个很重要的参数,就是我们配置驱动函数的结构
//(具体见上一篇对该函数的解析)
firstdrv_class = class_create(THIS_MODULE,
"firstdrv");
//创建一个驱动类
firstdrv_class_dev =
class_device_create(firstdrv_class, NULL,
MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
//用于自动的在系统的目录下创建一个字符设备的节点
gpfcon = (volatile unsigned long
*)ioremap(0x56000050, 16);
//通过内存映射到真实地址
gpfdat = gpfcon + 1;
//配置GPIO为输出
return 0;
}
驱动卸载服务函数
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv");
//告诉内核,卸载该驱动
//major是程序的主设备号
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
//这两句用于删除在文件系统中创建的节点
iounmap(gpfcon);
//取消GPIO的映射
}
最后别忘了,把这两个函数注册进去
module_init(first_drv_init);
module_exit(first_drv_exit);
重要的结构体
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
/* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
//说是与神秘高级特性有关,不懂,先这么写
.open = first_drv_open,
.ioctl = first_drv_ioctl,
};
用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址
驱动函数
open()函数
static int first_drv_open(struct inode *inode, struct
file *file)
{
printk("first_drv_open\n");
/* 配置GPF4,5,6为输出 */
......
return 0;
}
ioctl()函数
这个函数比较重要,用于应用程序到驱动程序的参数传递
int ioctl(int fd, ind cmd, …);
//最多可以传递三个参数
static int first_dev_ioctl(struct inode *inode,
struct file *file, int cmd, int arg)
{
printk("first_drv_ioctl\n");
......
}
证书
因为Linux遵循GNU/GPL ,所以要在内核做出声明
(要不,内核会傲娇…)
MODULE_LICENSE("GPL");
就此,一个简单的led的驱动程序就是分析完了,下面开始说说,驱动是如何被使用的。
在应用层,和硬件层之间的中间层,就是驱动所在的位置那么我们的驱动程序,一定是要通过应用程序调用的
驱动测试程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0) {
printf("can't open!\n");
}
...
ioctl(fd, strtol(argv[2]));
return 0;
}
一套驱动就算完成了,
长路漫漫,这这只是最简单的第一步