Linux平台总线设备驱动

1. 平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备(没有挂到真实总线的设备)与驱动进行了管理,这样提高了程序的可移植性。

2. 平台总线开发设备驱动流程

(1)定义平台设备

(2)注册平台设备

(3)定义平台驱动

(4)注册平台驱动

3. 平台设备使用struct platform_device来描述

struct platform_device {
    const char    * name;
    int        id;
    struct device    dev;
    u32        num_resources;
    struct resource    * resource;
};

① name:设备名

② id:设备ID:配合设备名使用,以区分不同设备

③ dev:

④ num_resources:硬件资源个数

⑤ resource:硬件资源

struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};


(1) 注册平台设备:int platform_device_register(struct platform_device *pdev);

(2) 注销平台设备:void platform_device_unregister(struct platform_device *pdev);

4. 平台驱动使用struct platform_driver描述

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*suspend_late)(struct platform_device *, pm_message_t state);
    int (*resume_early)(struct platform_device *);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
};


(1)平台驱动注册: int platform_driver_register(struct platform_driver *)

(2)平台驱动注销:void platform_driver_unregister(struct platform_driver *)

5. 简单示例

① PlatformKeyDevice.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>

MODULE_LICENSE("GPL");

#define GPGCON 0x56000060

void key_release(struct device *dev)
{
    printk("dev->init_name = %s release!
", dev->init_name);
}

static struct resource key_resource[] = {
    [0] = {
        .start = GPGCON,
        .end   = GPGCON + 8,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_EINT8,
        .end   = IRQ_EINT11,
        .flags = IORESOURCE_IRQ,
    },
    [2] = {
        .start = IRQ_EINT13,
        .end   = IRQ_EINT14,
        .flags = IORESOURCE_IRQ,
    },
    [3] = {
        .start = IRQ_EINT15,
        .end   = IRQ_EINT19,
        .flags = IORESOURCE_IRQ,
    },
};

struct platform_device key_device = {
    .name = "my-key",
    .id = 0,
    .dev = {
        .init_name = "init_key",
        .release = key_release,
    },
    .num_resources = ARRAY_SIZE(key_resource),
    .resource = key_resource,
};

static int button_init(void)
{
    platform_device_register(&key_device);
     
    return 0;
}

static void button_exit(void)
{       
    platform_device_unregister(&key_device);
}

module_init(button_init);
module_exit(button_exit);


② PlatformKeyDriver.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>

MODULE_LICENSE("GPL");

struct timer_list buttons_timer;

unsigned int key_num = 0;

wait_queue_head_t key_wait_queue;

unsigned int *key_base = NULL;


void buttons_timer_function(unsigned long data)  
{
    printk("keys_timer_function
");
    
    key_num = 0;

    printk("data = %lx
", data);
    
    switch(data)
    {
        case IRQ_EINT8:
            key_num = 1;
            break;
            
        case IRQ_EINT11:
            key_num = 2;
            break;
            
        case IRQ_EINT13:
            key_num = 3;
            break;
            
        case IRQ_EINT14:
            key_num = 4;
            break;
        
        case IRQ_EINT15:
            key_num = 5;
            break;
        
        case IRQ_EINT19:
            key_num = 6;
            break;            
        
        default:
            break;
    }

    printk("key_num = %d
", key_num);
    
    wake_up(&key_wait_queue);
} 

irqreturn_t key_int(int irq, void *dev_id)
{
    //1. 检测是否发生了按键中断
    
    //2. 清除已经发生的按键中断
    
    //3. 提交下半部
    buttons_timer.data = irq;
    mod_timer(&buttons_timer, jiffies + (HZ /10));     
  
    //return 0;
    return IRQ_HANDLED;     
}

void key_hw_init(void)
{
    unsigned int config_data;
    
    if(key_base != NULL)
    {
        config_data = readl(key_base);
        config_data &= 0b001111110000001100111100;
        config_data |= 0b100000001010100010000010;

        writel(config_data, key_base);
    }
}

int key_open(struct inode *node,struct file *filp)
{
    return 0;    
}

ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{ 
    wait_event(key_wait_queue, key_num);
        
    copy_to_user(buf, &key_num, 4);
    
    key_num = 0;
  
    return 4;
}

struct file_operations key_fops = 
{
    .open = key_open,
    .read = key_read,    
};

struct miscdevice key_miscdev = {
    .minor = 200,
    .name = "key",
    .fops = &key_fops,    
};

int key_probe(struct platform_device *pdev)
{
    int ret, size;
    
    struct resource *resources;
    struct resource *resources_irq;
    
    ret = misc_register(&key_miscdev);
    
    if (ret != 0)
        printk("register fail!
");
    
    //注册中断处理程序
    resources_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    
    request_irq(resources_irq->start, key_int, IRQF_TRIGGER_LOW, "key1", 0);
    request_irq(resources_irq->end, key_int, IRQF_TRIGGER_LOW, "key2", 0);
    
    resources_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
    
    request_irq(resources_irq->start, key_int, IRQF_TRIGGER_LOW, "key3", 0);
    request_irq(resources_irq->end, key_int, IRQF_TRIGGER_LOW, "key4", 0);
    
    resources_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
    
    request_irq(resources_irq->start, key_int,IRQF_TRIGGER_LOW, "key5", 0);
    request_irq(resources_irq->end, key_int,IRQF_TRIGGER_LOW, "key6", 0);
    
    
    //按键初始化
    resources = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    size = (resources->end - resources->start) + 1;
    key_base = ioremap(resources->start, size);
    
    key_hw_init();
    
    /* 初始化定时器 */  
    init_timer(&buttons_timer);   
    buttons_timer.function  = buttons_timer_function;  
    
    /* 向内核注册一个定时器 */  
    add_timer(&buttons_timer);  
    
    /*初始化等待队列*/
    init_waitqueue_head(&key_wait_queue);
    
    return 0;
}

int key_remove(struct platform_device *pdev)
{
    struct resource *resources_irq;
    int i = 0;
    
    printk("PlatformKeyDriver: key_remove!
");
    
    for(i = 0; i <= 2; i++)
    {
        resources_irq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
        
        free_irq(resources_irq->start, 0);
        free_irq(resources_irq->end, 0);
    }
    
    iounmap(key_base);
    misc_deregister(&key_miscdev);
    
    return 0;
}

static struct platform_driver key_driver = {
    .probe        = key_probe,
    .remove        = key_remove,
    .driver        = {
        .owner    = THIS_MODULE,
        .name    = "my-key",
    },
};

static int button_init(void)
{
    return platform_driver_register(&key_driver);
}

static void button_exit(void)
{       
    platform_driver_unregister(&key_driver);
}

module_init(button_init);
module_exit(button_exit);
原文地址:https://www.cnblogs.com/wulei0630/p/9543394.html