Linux驱动开发-①platform平台②MISC字符驱动框架③input框架
- 一,platform
- 1.1 platform框架(设备树下)
- 1.2 platform框架(配置设备函数)
- 二,MISC字符驱动框架
- 三,input框架

一,platform
platform框架是一种管理平台设备(Platform Device)和平台驱动(Platform Driver)的机制,分为总线,驱动和设备,主要用于处理那些不依赖于传统总线(如 PCI、USB、I2C 等)的设备,①SoC(系统芯片)类型,②内部的外设(如 GPIO、定时器、UART 等)。好处:让设备信息和驱动信息分开,提供一个标准化的接口(probe,remove),不用管总线协议,方便驱动开发。其次支持设备树,支持设备的热插拔(驱动在内核配置好后,比如有设备连接上,通过compatible属性,驱动能够自动的控制设备),并且一个驱动能控制多个设备。
1.1 platform框架(设备树下)
驱动和设备的匹配方式,常用的有两种,一种通过name匹配,一种是利用of类型匹配,比较设备的compatible属性和of_match_table表中的所用成员,是否有相同的,有即匹配成功。(在设备树情况下,虽然用不到name,但是还是要在结构体函数里面定义一下name变量,目前不定义会报错)。驱动和设备匹配成功后,probe函数执行,当设备找不到或者卸载后,remove函数执行。
设备树:
led {
compatible = "gpio-leds";
pinctrl-0 = <&pinctrl_led>;
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
platform驱动框架:
......top函数相关
static int led_probe(struct platform_device *dev)
{
.......初始化工作
还是要找AAAled节点的
return 0;
}
static int led_remove(struct platform_device *dev)
{
........
return 0;
}
const struct of_device_id led_match_table[]={
{.compatible = "gpio-leds"},
{}
};
struct platform_driver led_platform={
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "led_driver",
.of_match_table = led_match_table,
},
};
static int __init led_init(void)
{
return platform_driver_register(&led_platform);
}
static void __exit led_exit(void)
{
platform_driver_unregister(&led_platform);
}
/*驱动入口*/
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");
1.2 platform框架(配置设备函数)
即不使用设备树,直接将设备用.c文件表达出,然后编译出device.ko,和驱动文件一起放到内核中去,这个设备先于驱动,或者后于驱动配置都行,当驱动检测到就会自动运行了,设备函数:
#include <linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/fs.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/types.h>
#include<linux/cdev.h>
#include<linux/platform_device.h>
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
#define CELL 0X04
static void leddevice_release(struct device *dev)//这个函数定义完有用不? 试试
{
printk("release \r\n");
}
static struct resource leddevice_resource[]={
[0]={
.start = CCM_CCGR1_BASE,
.end = CCM_CCGR1_BASE + CELL -1,
.flags = IORESOURCE_MEM,
},
[1]={
.start = SW_MUX_GPIO1_IO03_BASE,
.end = SW_MUX_GPIO1_IO03_BASE + CELL -1,
.flags = IORESOURCE_MEM,
},
[2]={
.start = SW_PAD_GPIO1_IO03_BASE,
.end = SW_PAD_GPIO1_IO03_BASE + CELL -1,
.flags = IORESOURCE_MEM,
},
[3]={
.start = GPIO1_DR_BASE,
.end = GPIO1_DR_BASE + CELL -1,
.flags = IORESOURCE_MEM,
},
[4]={
.start = GPIO1_GDIR_BASE,
.end = GPIO1_GDIR_BASE + CELL -1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device leddevice ={
.name = "LED",
.id = -1,//此设备无ID
.dev = {
.release = leddevice_release,
},
.num_resources = ARRAY_SIZE(leddevice_resource),
.resource = leddevice_resource,
};
static int __init leddevice_init(void)
{
return platform_device_register(&leddevice);
}
static void __exit leddevice_exit(void)
{
platform_device_unregister(&leddevice);
}
module_init(leddevice_init);
module_exit(leddevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");
总体驱动函数:
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h> // 包含 register_chrdev_region 的定义
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include<linux/platform_device.h>
#define LED_NAME "pgled"
#define LED_ON 0
#define LED_OFF 1
/*虚拟地址指针*/
static void __iomem *CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
struct led_dev{
struct class *class;
struct device *device;
struct device_node *nd;
struct cdev cdev;
dev_t led_hao;
int major;//主设备号
int minor;//次设备号
int led_gpio;//led的gpio
};
struct led_dev led;
static int pgled_open(struct inode *innode,struct file *filp)
{
filp->private_data = &led;//将led结构体数据设为私有数据
return 0;
}
static int pgled_release(struct inode *innode,struct file *filp)
{
return 0;
}
static ssize_t pgled_read(struct file *filp, char __user *buf,size_t cnt,loff_t *offt)
{
return 0;
}
static void led_choice(unsigned char in)
{
static int register_led = 0;
if(in==0)//turn on led
{
/*控制亮*/
register_led = readl(GPIO1_DR);
register_led &=~(1<<3);
writel(register_led,GPIO1_DR);
}else if(in==1)
{
register_led = readl(GPIO1_DR);
register_led |=(1<<3);
writel(register_led,GPIO1_DR);
}
}
static ssize_t pgled_write(struct file *filp, const char __user *buf,size_t cnt,loff_t *offt)
{
int ret;
unsigned char databuf[1];
unsigned char let_status;
ret = copy_from_user(databuf,buf,cnt);
let_status = databuf[0];
if(let_status == 0)
{
led_choice(LED_ON);//开灯
}else if(let_status == 1)
{
led_choice(LED_OFF);//关灯
}
return 0;
}
static struct file_operations pgled_fops={
.owner=THIS_MODULE,
.read=pgled_read,
.write=pgled_write,
.open=pgled_open,
.release=pgled_release,
};
static int led_probe(struct platform_device *led_device)
{
int i=0,ret = 0;
int register_result = 0;
struct resource *led_gpio[5] ={0};//数组里面放五个指针
for(i=0;i<5;i++)
{
led_gpio[i]= platform_get_resource(led_device,IORESOURCE_MEM,i);//从设备中获取寄存器地址
if(led_gpio[i] == NULL)
{
printk("get resource error \r\n");
return -1;
}
}
printk("platform probe true\r\n");
/*1.将物理地址*_BASE和虚拟地址联系起来*/
CCM_CCGR1 = ioremap(led_gpio[0]->start,resource_size(led_gpio[0]));
//右边是实际物理地址,左边是虚拟地址 映射地址长度4(字节),因为32位寄存器
SW_MUX_GPIO1_IO03 = ioremap(led_gpio[1]->start,resource_size(led_gpio[0]));
SW_PAD_GPIO1_IO03 = ioremap(led_gpio[2]->start,resource_size(led_gpio[0]));
GPIO1_DR = ioremap(led_gpio[3]->start,resource_size(led_gpio[0]));
GPIO1_GDIR = ioremap(led_gpio[4]->start,resource_size(led_gpio[0]));
/*2.初始化*/
/*2.1 时钟初始化*/
register_result = readl(CCM_CCGR1);
register_result |=(3<<26);
writel(register_result,CCM_CCGR1);
/*2.2复用初始化*/
writel(5,SW_MUX_GPIO1_IO03);
/*2.3电器属性初始化*/
writel(0x10b0,SW_PAD_GPIO1_IO03);
/*2.4设置为输出模式*/
register_result = readl(GPIO1_GDIR);
register_result |= (1<<3);
writel(register_result,GPIO1_GDIR);
/*2.5控制亮*/
register_result = readl(GPIO1_DR);
register_result &=~(1<<3);
writel(register_result,GPIO1_DR);
/*注册*/
/*1.设备号*/
if(led.major)
{
led.led_hao = MKDEV(led.major,0);
register_chrdev_region(led.led_hao, 1, LED_NAME);//主动注册
}else{
alloc_chrdev_region(&led.led_hao, 0, 1, LED_NAME);//自动注册
}
printk("major = %d,minor = %d",MAJOR(led.led_hao),MINOR(led.led_hao));
/*2.注册函数*/
led.cdev.owner = THIS_MODULE;
cdev_init(&led.cdev,&pgled_fops);
cdev_add(&led.cdev,led.led_hao,1);
/*3.节点申请*/
led.class = class_create(THIS_MODULE,LED_NAME);
led.device = device_create(led.class, NULL,led.led_hao, NULL,LED_NAME);
return 0;
}
static int led_remove(struct platform_device *led_device)
{
int register_result = 0;
/*控制灭*/
register_result = readl(GPIO1_DR);
register_result |=(1<<3);
writel(register_result,GPIO1_DR);
/*取消虚拟地址映射*/
iounmap(CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
printk("exit in linux\r\n");
gpio_free(led.led_gpio);
cdev_del(&led.cdev);//先删除设备
unregister_chrdev_region(led.led_hao,1);//删除设备号
device_destroy(led.class,led.led_hao);//先删除和设备关系
class_destroy(led.class);//再删除类
return 0;
}
const struct of_device_id led_of_match[]={
{.compatible = "alpha-gpiokey"},
{ /*sentinel*/ },
};
static struct platform_driver scoop_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "LED",//1.无设备树,匹配名字
.of_match_table = led_of_match,//2.有设备树,直接利用设备树中compatible属性
},
};
static int __init pgled_init(void)
{
return platform_driver_register(&scoop_driver);
}
static void __exit pgled_exit(void)
{
platform_driver_unregister(&scoop_driver);
}
/*驱动入口和出口*/
module_init(pgled_init);
module_exit(pgled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");
二,MISC字符驱动框架
MISC字符驱动框架目的就是为了让字符设备少占用点主设备号,让字符设备的主设备号为10,不同的字符设备用的次设备号不同。适合简单的字符设备,不适合归类到标准框架(input,tty),功能单一,无复杂子系统支持的设备。注册函数和注销函数为int misc_register(struct miscdevice * misc) 和int misc_deregister(struct miscdevice *misc)
,这俩函数省去了这些函数的操作,1 cdev_del(); /* 删除 cdev */ 2 unregister_chrdev_region(); /* 注销设备号 */ 3 device_destroy(); /* 删除设备 */ 4 class_destroy(); /* 删除类 */
。
驱动实现:
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h> // 包含 register_chrdev_region 的定义
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include<linux/platform_device.h>
#include<linux/miscdevice.h>
struct beep_dev{
struct device_node *beep_nd;
int beep_gpio;//led的gpio
};
struct beep_dev beepdev;
static int beep_open (struct inode *node, struct file *filp)
{
// filp->private_data = &beep_misc;
return 0;
};
static int beep_release (struct inode *node, struct file *filp)
{
return 0;
};
static ssize_t beep_write (struct file *filp, const char __user *buf, size_t count , loff_t *ppos)
{
int ret =0;
unsigned char result = 0;
ret = __copy_from_user(&result,buf,count);
if(ret<0)
{
printk("__copy_from_user error !!\r\n");
return -1;
}
gpio_set_value(beepdev.beep_gpio,result);//开关
return 0;
};
const struct file_operations beep_fops ={
.owner = THIS_MODULE,
.write = beep_write,
.open = beep_open,
.release = beep_release,
};
static struct miscdevice beep_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "beep_driver",
.fops = &beep_fops,
};
static int beep_probe(struct platform_device *dev)
{
int ret = 0;
ret = misc_register(&beep_misc);
if(ret<0)
{
printk("misc_register error !!\r\n");
return -1;
}
beepdev.beep_nd = of_find_node_by_path("/BEEP");
if (!beepdev.beep_nd) {
printk("of_find_node_by_path error !!\r\n");
return -ENODEV;
}
printk("of_find_node_by_path yes !!\r\n");
beepdev.beep_gpio = of_get_named_gpio(beepdev.beep_nd,"beep-gpio",0);
if (beepdev.beep_gpio < 0) {
printk("of_get_named_gpio error !!\r\n");
return -1;
}
ret = gpio_request(beepdev.beep_gpio,"beep-aaa");
if(ret<0)
{
printk("gpio requst error !\r\n");
}
ret = gpio_direction_output(beepdev.beep_gpio,1);
if(ret < 0)
{
gpio_free(beepdev.beep_gpio);
printk("gpio_direction_output error! \r\n");
return -1;
}
printk("probe yes !!\r\n");
return 0;
}
static int beep_remove(struct platform_device *dev)
{
int ret = 0;
gpio_free(beepdev.beep_gpio);
ret = misc_deregister(&beep_misc);
if(ret<0)
{
printk("misc_register error !!\r\n");
return -1;
}
return 0;
}
const struct of_device_id beep_match_table[]={
{.compatible = "atkalpha-beep"},
{}
};
struct platform_driver beep_platform={
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.name = "beep_driver",
.of_match_table = beep_match_table,
},
};
static int __init beep_init(void)
{
return platform_driver_register(&beep_platform);
}
static void __exit beep_exit(void)
{
platform_driver_unregister(&beep_platform);
}
/*驱动入口*/
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");
三,input框架
input框架适用于输入的子系统,针对一类设备而创建的框架,比如按键、键盘、鼠标、触摸屏,和这个misc感觉上也差不多,input适用的更多,能省去创建主设备号次设备号和节点的创建工作(主设备号已经定了,子系统的所有设备主设备号都为 13)。具体实现:这个input通过他特定的函数input_event,将数据从驱动中传出去,应用程序再定义一个特定的结构体(好巧名字也叫input_event),把通过read函数将数据读给用户层。
步骤包括:①定义一个input_dev类型结构体变量a,用input_allocate_device申请出这个结构体定义的值a②初始化这个a,主要包括初始化事件类型(evbit,比如按键类型,led类型等)和事件值(keybit,比如把按键键值设置为5),③利用注册函数注册,④卸载时候,先注销unregister再free释放a。
input采用的阻塞方式,这个它的子系统框架已经创建好了,所有输入设备通过 /dev/input/eventX 提供统一接口。
驱动:
/*3.24中断和定时器下input框架*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include<linux/platform_device.h>
#include<linux/input.h>
#include<linux/interrupt.h>
#include <linux/timer.h>
/*设备结构体*/
struct key_dev{
struct device_node *key_node;
int key_gpio;
struct input_dev *key_input;
struct timer_list timer;
};
/*中断结构体*/
struct irq_key{
int irq;
unsigned char key_irq_name[10];
irqreturn_t (*handler)(int,void *);
};
struct key_dev key;
struct irq_key key_irq;
/*中断处理函数*/
static irqreturn_t key_irq_handler(int irq,void *dev)
{
key.timer.data = (unsigned long)dev;
mod_timer(&key.timer,jiffies+msecs_to_jiffies(15));
return IRQ_HANDLED;
}
static void key_timer_function(unsigned long arg)
{
static int key_retsult = 0;
struct key_dev *key_timer_dev =(struct key_dev *)arg;
key_retsult = gpio_get_value(key_timer_dev->key_gpio);
if(key_retsult == 0)//按下
{
input_event(key.key_input, EV_KEY, KEY_8, 1);
input_sync(key.key_input);
}else { //松开
input_event(key.key_input, EV_KEY, KEY_8, 0);
input_sync(key.key_input);
}
}
static int __init key_init(void)
{
int ret = 0;
printk("00000000000\r\n");
/*get node*/
key.key_node = of_find_node_by_path("/key");
if(key.key_node==NULL)//未注册成功
{
ret = -1;
}
key.key_gpio = of_get_named_gpio(key.key_node,"key-gpio",0);
ret = gpio_request(key.key_gpio,"key_gpio");
if(ret<0)
{
printk("gpio requst error !\r\n");
}
ret = gpio_direction_input(key.key_gpio);//设置输入
if(ret < 0)
{
printk(" gpio_direction_input error!\r\n");
ret = -1;
goto gpio_error;
}
printk("111111111111111\r\n");
/*irq init*/
key_irq.irq = gpio_to_irq(key.key_gpio);
strncpy( key_irq.key_irq_name, "key_irq_0", sizeof(key_irq.key_irq_name));
key_irq.handler = key_irq_handler;
ret = request_irq(key_irq.irq,key_irq.handler,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,key_irq.key_irq_name,&key);
if(ret < 0)//未注册成功
{
ret = -1;
goto irq_error;
}
printk("2222222222222\r\n");
/*input init*/
key.key_input = input_allocate_device();//申请key_input
__set_bit(EV_KEY,key.key_input->evbit);//设置按键事件
__set_bit(EV_REP,key.key_input->evbit);//设置重复事件
__set_bit(KEY_8,key.key_input->keybit);//设置按键值
ret = input_register_device(key.key_input);
if(ret < 0)//未注册成功
{
ret = -1;
goto ragister_error;
}
printk("33333333333\r\n");
/*timer init */
init_timer(&key.timer);
key.timer.function = key_timer_function;
printk("6666666666666\r\n");
return 0;
ragister_error:
input_free_device(key.key_input);
irq_error:
free_irq(key_irq.irq,&key);
gpio_error:
gpio_free(key.key_gpio);
return ret;
}
static void __exit key_exit(void)
{
del_timer_sync(&key.timer);
gpio_free(key.key_gpio);
free_irq(key_irq.irq,&key);
/*input框架注销处理*/
input_unregister_device(key.key_input);
input_free_device(key.key_input);
}
/*in out*/
module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");
应用程序:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <linux/input.h>
static struct input_event key_input;
int main(unsigned char argc,unsigned char *argv[])
{
int rel = 0,fd = 0;
unsigned char *filename;
filename = argv[1];
printf("111\r\n");
fd = open(filename,O_RDWR);
if(fd<0) printf("open file error\r\n");
while(1)
{
printf("get in !\r\n");
rel = read(fd,&key_input,sizeof(key_input));
if(rel>0)
{
switch (key_input.type)
{
case EV_KEY:
if(key_input.value==1)
{
printf("按键按下\r\n");
}else printf("按键释放\r\n");
break;
default:
break;
}
}else {
printf("读取 error \r\n");
}
}
rel = close(fd);
if(rel<0) printf("close in APP error\r\n");
return 0;
}