1:本文只是简单的通过gpio来控制亮灯的程序,我不是大神,但我在努力,请各位多多指教
首先我们要写的是一个驱动,而驱动又必须编译进内核才可以,那怎么样内核才知道你的程序是驱动而不是一般程序呢?没错,我们有现成的宏来干这个事。
-
module_init();
-
module_exit();
如果你问我这些宏是怎么来的,我只能说它们来自内核,而它们的目的就是告诉内核这是个驱动程序,装载是调用module_init();所指定的函数,而卸载是调用module_exit();所指定的函数,同时它们还可以验证函数的加载方式等其它功能。
既然我们使用的是gpio引脚,自然需要它们的地址咯!
-
volatile unsigned long *gpfcon = NULL;
-
volatile unsigned long *gpfdat = NULL;
Volatile:这个关键字的意思就是禁止编译器的优化,我们知道计算机在存储的时候为了存储的速度快,会把某些地址的值拷贝到寄存器中,这样以后直接读取寄存器的值速度会加快,但是有时候我们需要的值变化特别快,或者会在我们不知道的情况下改变它的值,而寄存器的值和内存地址的值可能不同步,那时我们就希望编译器不要优化,直接去读取内存地址的值,这时我们就用到了这个关键字。
上面说到module_init();会指定一个函数作为驱动的入口函数,可是内核没有提供入口函数,所以要我们自己去写
加入入口函数是:static int first_drv_init(void);
此时宏module_init();改为module_init(first_drv_init);这样驱动就知道从哪里开始执行了,
那么入口函数里面我们都做什么呢?
看看内核就会知道,内核给我们提供了一个结构体file_operations,是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。我们用的是简单的gpio点灯,所以我们用的成员不多,如下只有open和write
-
static struct file_operations first_drv_fops = {
-
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
-
.open = first_drv_open,
-
.write = first_drv_write,
-
};
既然是函数指针,那么函数 first_drv_open就得我们自己去写啦!
-
static int first_drv_open(struct inode *inode, struct file *file)
-
{
-
//printk("first_drv_open\n");
-
/* 配置GPF4,5,6为输出 */
-
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
-
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
-
return 0;
-
}
在open函数中我们就是配置了gpio的配置引脚让他们具有对应的功能,具体应该参照原理图来写,这里我们用的是三星的s3c2440的芯片,
接下来就是write操作
-
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
-
{
-
int val;
-
//printk("first_drv_write\n");
-
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;
-
}
这里主要是对gpio引脚的数据寄存器放入1,0,来控制对应引脚是否为高点平,低电平,
上面说了内核给了我们module_init();module_exit();和file_operations,前面的宏自然不用多说,内核自然回去识别,可是我们的file_operations结构体有了我们自己取的名字,内核还认识吗?这里我们就要使用另一个函数了
-
register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
这个函数的功能就是告诉内核,我这个驱动的file_operations结构体是first_drv_fops,驱动名字是first_drv,0表示自动分配一个设备号,
当然在卸载驱动是我们也要卸载这个结构体
-
unregister_chrdev(major, "first_drv"); // 卸载
光这样的话还不行,我们最好自己去主动创建节点,不然每次加载驱动都要创建,太麻烦了
-
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
-
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
在卸载的时候自然需要销毁节点
-
class_device_unregister(firstdrv_class_dev);
-
class_destroy(firstdrv_class);
细心的人肯定发现了,我们的gpio引脚目前还没有地址呢?来映射一下吧!
-
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
-
gpfdat = gpfcon + 1;
这样我们最简单的驱动就搞定啦,以下是完整驱动和它的测试程序
A:驱动
-
#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 <asm/arch/regs-gpio.h>
-
#include <asm/hardware.h>
-
static struct class *firstdrv_class;
-
static struct class_device *firstdrv_class_dev;
-
volatile unsigned long *gpfcon = NULL;
-
volatile unsigned long *gpfdat = NULL;
-
static int first_drv_open(struct inode *inode, struct file *file)
-
{
-
//printk("first_drv_open\n");
-
/* 配置GPF4,5,6为输出 */
-
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
-
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
-
return 0;
-
}
-
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
-
{
-
int val;
-
//printk("first_drv_write\n");
-
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;
-
}
-
static struct file_operations first_drv_fops = {
-
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
-
.open = first_drv_open,
-
.write = first_drv_write,
-
};
-
int major;
-
static int first_drv_init(void)
-
{
-
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
-
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;
-
return 0;
-
}
-
static void first_drv_exit(void)
-
{
-
unregister_chrdev(major, "first_drv"); // 卸载
-
class_device_unregister(firstdrv_class_dev);
-
class_destroy(firstdrv_class);
-
iounmap(gpfcon);
-
}
-
module_init(first_drv_init);
-
module_exit(first_drv_exit);
-
MODULE_LICENSE("GPL");
B:测试
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <fcntl.h>
-
#include <stdio.h>
-
/* firstdrvtest on
-
* firstdrvtest off
-
*/
-
int main(int argc, char **argv)
-
{
-
int fd;
-
int val = 1;
-
fd = open("/dev/xyz", O_RDWR);
-
if (fd < 0)
-
{
-
printf("can't open!\n");
-
}
-
if (argc != 2)
-
{
-
printf("Usage :\n");
-
printf("%s \n", argv[0]);
-
return 0;
-
}
-
if (strcmp(argv[1], "on") == 0)
-
{
-
val = 1;
-
}
-
else
-
{
-
val = 0;
-
}
-
write(fd, &val, 4);
-
return 0;
-
}
阅读(2724) | 评论(0) | 转发(0) |