《[arm驱动]Platform设备驱动》涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板零个,可参考的相关应用程序或内核驱动一个


一、关键是device程序中resource结构体

结构体一)resource结构体

struct resource {
        resource_size_t start;      //定义资源的起始地址
        resource_size_t end;       //定义资源的结束地址
        const char *name;                   //定义资源的名称
        unsigned long flags;           //定义资源的类型,例如MEM, IO ,IRQ, DMA类型
        struct resource *parent, *sibling, *child;     //资源链表指针
};


函数一)driver程序中获取资源

struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)

参数:
dev: 资源所属的设备
type: 获取的资源类型  IORESOURCE_IO IORESOURCE_MEM IORESOURCE_IRQ IORESOURCE_DMA
num: 获取的资源数
例:platform_get_resource(pdev, IORESOURCE_IRQ, 0)获取第一个中断号

内核源码一)platform_get_resource内核源码


platform_get_resource(struct platform_device *dev, unsigned int type,
              unsigned int num)
{
    int i;
    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];
        //从下面两个if中看出num = 0并不等同于要取resource[0],而是获取第一个flag为type的resource
        if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
                 IORESOURCE_IRQ|IORESOURCE_DMA))
            == type)
            if (num-- == 0)
                return r;
    }
    return NULL;
}

函数二)driver中获取第num+1个中断资源

platform_get_irq(struct platform_device * dev, unsigned int num)

内核源码二)platform_get_irq内核源码

int platform_get_irq(struct platform_device *dev, unsigned int num)
{
    struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);
    //调用了platform_get_resource
    return r ? r->start : -ENXIO;
}


实例一)实现一个s3c2440的platform led流水灯

   platform_led_dev.c

/*代码代换:platform_led_dev_, "platform_led"*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/fs.h>//文件系统相关的函数和头文件
#include <linux/device.h>
#include <linux/cdev.h> //cdev结构的头文件包含<linux/kdev_t.h>
static struct resource platform_led_dev_resource[] = {
    /*
      参考s3c2440芯片手册
      GPFCON 0x56000050//本驱动使用,注意unsigned long 为4字节(4*8 bit),相当与ff
      GPFDAT 0x56000054//本驱动使用
      GPFUP 0x56000058//本驱动不使用,上拉电阻在驱动中不使用到,
    */
    [0] = {
        .start = 0x56000050,
        .end   = 0x56000050 + 8 - 1,
        .flags = IORESOURCE_MEM,
    },
                                                                                                                                            
};
static void platform_led_dev_release(struct device * dev){
    printk("device say the device is release\n");
return;
}
static struct platform_device platform_led_dev_device = {  //添加设备结构体
    .name = "platform_led",
    .id = -1,
    .num_resources    = ARRAY_SIZE(platform_led_dev_resource),//一定要加,因为platform_get_resource中要用到
    .resource = platform_led_dev_resource,
    .dev = {
    .release = platform_led_dev_release,//解决"Device 'platform_dev' does not have a release() function“问题
    }
};
static int  __init platform_led_dev_init(void){
    platform_device_register(&platform_led_dev_device);  //注册设备到内核
    return 0;
}
static void platform_led_dev_exit(void){
     platform_device_unregister(&platform_led_dev_device);  //卸载设备
    printk(KERN_ALERT "good bye\n");
}
module_init(platform_led_dev_init);
module_exit(platform_led_dev_exit);
MODULE_LICENSE("GPL");

   platform_led_drv.c

   

/*
 *代码代换:platform_led_drv_, "platform_led"
 */
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/platform_device.h>
    static struct class *platform_led_drv_cls;
    static volatile unsigned long *gpiof_con;//注意unsigned long 为4字节(4*8 bit),相当与ff
    static volatile unsigned long *gpiof_dat;
    static int major;
                                                                                                                       
                                                                                                                       
    static int platform_led_drv_open(struct inode *inode, struct file *file)
    {
        printk("driver\tplatform_led open\n");
        /* 配置为输出 */
        *gpiof_con &= ~((0x3 << (4*2)) | (0x3 << (5*2)) | (0x3 << (6*2)));
        *gpiof_con |= ((0x1 << (4*2)) | (0x1 << (5*2)) | (0x1 << (6*2) ));
        return 0;  
    }
                                                                                                                       
                                                                                                                       
    static ssize_t platform_led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
    int val;
    int on;
    copy_from_user(&val, buf, count);
    on = val % 10;
    val = val / 10 + 4;
    if(on == 0){
    *gpiof_dat &= ~(1 << val);   
    }else{
    *gpiof_dat |= (1 << val);
    }
    return 0;
    }
                                                                                                                       
    static struct file_operations platform_led_drv_fops = {
        .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open   =   platform_led_drv_open,    
        .write  =   platform_led_drv_write,   
    };
    static int platform_led_drv_probe(struct platform_device *dev)//
    {
                                                                                                                           
        struct resource     *res;
        //获得GPFCON,GPFDAT
        res = platform_get_resource(dev, IORESOURCE_MEM, 0);
        printk("driver say the driver found the device\n");
        gpiof_con = (volatile unsigned long *)ioremap(res->start, res->end - res->start + 1);//将0x56000050-0x56000057进行IO映射,gpiof_con等于ioremap的首地址,占4个字节
        gpiof_dat = gpiof_con + 1;//相当与0x56000050 + 4
        major = register_chrdev(0, "platform_led", &platform_led_drv_fops);  /*/proc/devices*/
        platform_led_drv_cls = class_create(THIS_MODULE, "platform_led");/* /sys/class/platform_led */
        class_device_create(platform_led_drv_cls, NULL, MKDEV(major, 0), NULL, "platform_led"); /* /dev/platform_led */
        printk("driver say the driver probe ok\n");
        return 0;
    }
    static int platform_led_drv_remove(struct platform_device *dev)
    {
        printk("driver say the device is polled out\n");
        /* 卸载字符设备驱动程序 */
    /* iounmap */
        class_device_destroy(platform_led_drv_cls, MKDEV(major, 0));
        class_destroy(platform_led_drv_cls);
        unregister_chrdev(major, "platform_led");
        iounmap(gpiof_con);
        return 0;
    }
//platform 总线相关文件挂载/sys/bus/platform/路径下
    static struct platform_driver platform_led_drv_driver = {//driver是驱动的意思,相关文件在/sys/bus/platform/drivers
        .probe = platform_led_drv_probe,//注册时要执行的函数
        .remove = platform_led_drv_remove,//注销时会执行的函数
        .driver = {
            .owner = THIS_MODULE,
            .name = "platform_led",//会在"/sys/bus/platform/drivers"下创建platform_dev文件夹
        },
    };
    static int __init platform_led_drv_driver_init(void)
    {
            /*注册平台驱动*/
        return platform_driver_register(&platform_led_drv_driver);
    }
    static void platform_led_drv_driver_exit(void)
    {
        platform_driver_unregister(&platform_led_drv_driver);
    }
    module_init(platform_led_drv_driver_init);
    module_exit(platform_led_drv_driver_exit);
    MODULE_LICENSE("GPL");

   上面两个c文件对于的Makefile

   

KERN_DIR = /workspacearm/linux-2.6.2.6
#platform_led_dev.ko
#platform_led_drv.ko
all:
    make -C $(KERN_DIR) M=`pwd` modules
    cp platform_led_dev.ko /opt/fsmini/
    cp platform_led_drv.ko /opt/fsmini/
clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order
    rm -rf Module.symvers
obj-m   += platform_led_dev.o
obj-m   += platform_led_drv.o

   应用测试程序

   

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
//myled
int main(int argc, char **argv)
{
    int fd;
    int val = 1;
    fd = open("/dev/platform_led", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
    }
    while(1){      
    if(val > 10){
    val = val - 10;
    write(fd, &val, 4);
    val = val + 10;
    }else{
    val = 21;
    write(fd, &val, 4);
    val = 1;
    }
    val -=1;       
    write(fd, &val, 4);
    sleep(1);
    val += 11;
    if(val > 22 )val = 1;
    }
    //write(fd, &val, 4);
    close(fd);
    return 0;
}

   

   应用程序对应的Makefile

   

objs := $(patsubst %c, %o, $(shell ls *.c))
myarmgcc := /workspacearm/armlinuxgcc2626/bin/arm-linux-gcc
myled.bin:$(objs)
    $(myarmgcc) -o $@ $^
    cp *.bin /opt/fsmini/
%.o:%.c
    $(myarmgcc) -c -o $@ $<
clean:
    rm -f  *.bin *.o


  总结:可以看出,将device可drive分开写,驱动(driver)可以达到平台(device)无关性,这也是总线的初衷