Linux驱动开发-①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;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值