前言
本文主要介绍了基于JZ2440开发板的LED灯驱动程序的介绍和驱动的安装和测试。
驱动程序的介绍
驱动程序需要的函数主要由以下几个:
first_drv_open函数
这个函数与测试程序的open函数对应,当测试程序调用open函数时,会进入内核寻找first_drv_open函数,本例中的函数内容如下:
static int first_drv_open(struct inode *inode, struct file *file)
{
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
return 0;
}
本例中,first_drv_open函数中完成的功能是,对LED灯对应的寄存器位置写1,所以当测试程序调用open函数后,对就对LED灯进行了一个初始化的操作。
first_drv_write函数
这个函数与测试程序的write函数对应,当测试程序调用write函数时,会进入内核寻找first_drv_write函数,本例中的函数内容如下:
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
copy_from_user(&val, buf, count); //copy_to_user();
if (val == 1)
{
// 点灯
*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
}
else
{
// 灭灯
*gpfdat |= (1<<4) | (1<<5) | (1<<6);
}
return 0;
}
该函数通过形参const char __user *buf进行测试程序和驱动程序的数据传递,由于该函数是测试程序向驱动程序里写数据,所以需要调用copy_from_user(&val, buf, count);进行数据的拷贝。通过判断测试程序对驱动程序的写0和写1,控制LED灯的亮灭。
first_drv_init函数
这个函数是装载驱动的函数,该函数的目的是把相应的驱动函数信息告诉内核。写这个函数之前,需要首先定义一些信息。
static struct class *firstdrv_class; //#1
static struct class_device *firstdrv_class_dev; //#2
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.write = first_drv_write,
};
结构体file_operations存储了驱动的所要用到的函数,将其打包成一个结构体。下面是first_drv_init函数内容:
int major;
static int first_drv_init(void)
{
major = register_chrdev(0, "first_drv", &first_drv_fops); // #1
firstdrv_class = class_create(THIS_MODULE, "firstdrv"); //#2
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* #3 /dev/xyz */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); //#4
gpfdat = gpfcon + 1; //#5
return 0;
}
- #1: 将结构体
file_operations存储的信息告诉内核,并让内核自动为其分配主设备号,存放至major中。 - #2和#3:在装载驱动后,根据驱动信息自动生成设备结点,即
/dev/xyz文件夹。若没有这两句,在装载完驱动后,需要根据设备信息手动创建设备结点和文件夹,即mknod /dev/xyz c 111(主设备号) 0(副设备号) - #4和#5:对LED等的配置寄存器和数据寄存器进行定义。
first_drv_exit()函数
该函数在卸载驱动时调用,函数内容如下:
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸载
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
iounmap(gpfcon);
}
该函数主要是将first_drv_init函数注册进内核的信息给删除掉。
有了first_drv_init函数和first_drv_exit()函数后,需要调用以下语句才能成功:
module_init(first_drv_init);
module_exit(first_drv_exit);
测试程序及测试结果
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR); //#1
if (fd < 0)
{
printf("can't open!\n");
}
if (argc != 2)
{
printf("Usage :\n");
printf("%s <on|off>\n", argv[0]);
return 0;
}
if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}
write(fd, &val, 4); //#2
return 0;
}
- #1: 通过
open函数进入驱动的first_drv_open函数,并返回fd作为该调用该驱动的标识。 - #4:通过
write函数进入驱动first_drv_write函数,将参数命令传给驱动,通过传入的命令控制LED灯的亮灭。
测试过程及结果
- 输入指令:insmod first_drv.ko 然后输入:cat /proc/devices
可以看到在字符设备列表里有我们刚写的驱动
first_drv,以及自动为它分配的主设备号252。 - 输入指令:./firstdrvtest on 可观察到板子上的LED灯亮起;输入指令:./firstdrvtest off 可观察到板子上的LED灯灭掉。
- 输入指令:rmmod first_drv.ko 可卸载驱动。