怎么写驱动程序?
看原理图
确定引脚
看芯片手册,确定怎么操作这些引脚
写驱动代码
为什么写驱动?
驱动的作用起一个封装作用,应用程序需要操作文件或硬件是一套标准的APP接口,open某个设备文件,read得到设备状态,write设备状态,或各种ioctl,相应的在驱动程序中就有相应的drv_open drv_read drv_write drv_ioctl
怎么写?
- 分配一个file_operations结构体,里面封装了各种函数,例如点灯。
- 设置结构体中的函数:.open = led_open 做什么:把led引脚配置为输出引脚。.read = led_read 做什么:根据APP传入的值设置引脚的状态。
- 注册(告诉内核): 把这个register_chrdev(主设备号,file_operations结构体,名字)结构体放入内核的一个数组中,放入数组中的哪一项也就是主设备号
- 入口:用来调用register_chrdev
- 出口:用来调用unregister_chrdev
那怎么在驱动中指定引脚?
- 传统方法:在代码中写死,led_drv.c(分配、注册fileopetation结构体)
- 总线设备驱动模型:分为led_drv.c(分配、注册fileopetation结构体)和led_dev.c,在led_dev.c中指定引脚
- 使用设备数指明引脚:led_drv.c(分配、注册fileopetation结构体)和imax7.dts,在imax7.dts中指定引脚
- 总结驱动写法:核心写法不变,差别在于如何指定硬件资源。
led_drv.c
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/uaccess.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/of.h>
#include<linux/of_device.h>
#include<linux/of_platform.h>
static int major;
static struct class *led_class;
static unsigned int gpio_base[] = {
0x56000000,//GPACON
0x56000010,//GPBCON
0x56000020,//GPCCON
0x56000030,//GPDCON
0x56000040,//GPECON
0x56000050,//GPFCON
0x56000060,//GPGCON
0x56000070,//GPHCON
0 ,//GPICON
0x560000D0,//GPJCON
};
#define S3C2440_GPA(n) (0<<16 | n)
#define S3C2440_GPB(n) (1<<16 | n)
#define S3C2440_GPC(n) (2<<16 | n)
#define S3C2440_GPD(n) (3<<16 | n)
#define S3C2440_GPE(n) (4<<16 | n)
#define S3C2440_GPF(n) (5<<16 | n)
#define S3C2440_GPG(n) (6<<16 | n)
#define S3C2440_GPH(n) (7<<16 | n)
#define S3C2440_GPI(n) (8<<16 | n)
#define S3C2440_GPJ(n) (9<<16 | n)
static int led_pin = S3C2440_GPF(5);
static volatile unsigned int *gpio_con;
static volatile unsigned int *gpio_dat;
/*1.分配file_operations结构体
*2.设置file_operations结构体
*3.注册file_operations结构体
*4.入口
*5.出口
*/
static int led_release(struct inode *node,struct file *filp)
{
printk("iounmap(0x%x)\n",gpio_con);
iounmap(gpio_con);
return 0;
}
static struct file_operations myled_oprs = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
};
static int led_open(struct inode *node,struct file *filp)
{
//把LED引脚配置为输出引脚
//例如引脚GPF5 基地址0x56000050
//linux驱动中,不能直接操作物理地址,需要映射到虚拟地址。
int bank = led_pin >> 16;
int base = gpio_base[bank];
int pin = led_pin & 0xffff;
//映射
gpio_con = ioremap(base,8);
if(gpio_con)
{
printk("ioremap(0x%x) = 0x%x\n",base,gpio_con);
}
else
{
return -EINVAL;
}
gpio_dat = gpio_con + 1;
//先清0
*gpio_con &= ~(3<<(pin * 2));
//置1
*gpio_con |= (1<<(pin * 2));
return 0;
}
static ssize_t led_write(struct file *filp,const char __user *buf,size_t size,loff_t *off)
{
//根据应用程序APP传入的值来设置LED引脚
//怎么传给内核
unsigned char val;
int pin = led_pin & 0xffff;
copy_from_user(&val,buf,1);
if(val)
{
//点灯
*gpio_dat &= ~(1<<pin);
}
else
{
//灭灯
*gpio_dat |= (1<<pin);
}
return 1; //已经写入一个数据
}
static int myled_init(void)
{
major = register_chrdev(0,"myled",&myled_oprs);
//自动分配设备号
led_class = class_create(THIS_MODULE,“myled”);
device_create(led_class,NULL,MKDEV(major,0),NULL,"led");// /dev/led
//此后就可以open/dev/led来设置灯。
return 0;
}
static int myled_exit(void)
{
unregister_chrdev(major,"myled");
device_destroy(led_class,MKDEV(major,0));
class_destroy(led_class);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
led_test.c
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
//ledtest on
//ledtest off
int main(int argc,char *argv[])
{
int fd;
unsigned char val = 1;
fd = open("dev/led",O_RDWR);
if(fd < 0)
{
printf("cant't error!\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,1);
return 0;
}
Makefile
KERN_DIR = /work/system/ccclinux
all:
make -C $(KERN_DIR) M = 'pwd' modules
clean:
make -C $(KERN_DIR) M = 'pwd' modules clean
rm -rf modules.order
obj_m +=led_drv.o