本节书摘来自异步社区《Android深度探索(卷1):HAL与驱动开发》一书中的第1章,第1.7节见识一下什么叫Linux驱动:LED,作者李宁,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.7 见识一下什么叫Linux驱动:LED
Android深度探索(卷1):HAL与驱动开发
Linux驱动这个家伙到现在为止仍然是只见其声,未见其人,不过在本节会向读者展示一下Linux驱动到底是个什么东西。如果读者看到Linux驱动的代码感到头晕,这属于正常现象。因为如果一看就明白的话,那就没有阅读本书的必要了。本节的目的只为向读者展示Linux驱动程序的结构,以及使读者对Linux驱动有一个大致的印象,读者无须理解其中的细节。当读者阅读完本书时,自然会对这些细节部分了如指掌。
下面给出一个简单的Linux驱动的核心代码(用C语言实现),这个驱动的作用就是控制S3C6410开发板上的4个LED(关于开发板的使用方法将在后面详细介绍)。我们姑且将其称为LED驱动。LED驱动属于字符设备驱动,核心代码如下:
#include <linux/miscdevice.h>
… …
//此处包含了多个头文件
// 定义了设备名,驱动程序会在/dev目录下建立一个leds设备文件,通过访问该设备文件可以访问LED驱动
#define DEVICE_NAME "leds"
// 向LED发送数据及从LED读取数据
static long s3c6410_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
switch (cmd)
{
unsigned tmp;
case 0:
case 1:
if (arg > 4)
{
return -EINVAL;
}
tmp = readl(S3C64XX_GPMDAT);
if (cmd == 0) // 关闭LED
{
tmp &= (~(1 << arg));
}
else // 打开LED
{
tmp |= (1 << arg);
}
// 向LED设备写数据
writel(tmp, S3C64XX_GPMDAT);
// 输出调试信息
printk(DEVICE_NAME"_lining: %d %d\n", arg, cmd);
return 0;
default:
return -EINVAL;
}
}
// 描述设备文件的操作和相关数据的结构体
static struct file_operations dev_fops =
{ .owner = THIS_MODULE, .unlocked_ioctl = s3c6410_leds_ioctl, };
static struct miscdevice misc =
{ .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, };
// 驱动的初始化函数
static int _init dev_init(void)
{
int ret;
unsigned tmp;
//gpm0-3 pull up
tmp = readl(S3C64XX_GPMPUD);
tmp &= (~0xFF);
tmp |= 0xaa;
writel(tmp,S3C64XX_GPMPUD);
//gpm0-3 output mode
tmp =readl(S3C64XX_GPMCON);
tmp &= (~0xFFFF);
tmp |= 0x1111;
writel(tmp,S3C64XX_GPMCON);
//gpm0-3 output 0
tmp = _raw_readl(S3C64XX_GPMDAT);
tmp |= 0x10;
writel(tmp,S3C64XX_GPMDAT);
ret = misc_register(&misc);
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
static void _exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init( dev_init);
module_exit( dev_exit);
// 指定了当前驱动在哪个协议下发布,在这里是GPL协议
MODULE_LICENSE("GPL");
LED驱动的代码涉及了很多系统的函数和结构体,如readl、writel、printk、miscdevice 、module_exit 、file_operations、miscdevice等。读者目前并不需要了解这些函数和结构体的作用和使用方法。只要知道任何的Linux驱动都有一个装载函数(装载驱动时调用)和一个卸载函数(卸载驱动时调用)即可。装载函数和卸载函数分别通过mobule_init和module_exit宏指定。