十八、platform模型

一、概念

platform是一个驱动管理模型,它不是从设计驱动的角度考虑,而是从管理驱动的角度考虑

  • platform device:描述硬件信息的部分(硬件不通,信息或资源不同)。
  • platform driver:描述软件信息的部分(对硬件资源的操作,保持相对稳定,较少修改)。
  • platform bus:是platform driver和platform device的管理者(管理着两条链表)。

二、platform模型的优点

  • 方便驱动的移植(争取做到移植时,只修改device,而driver部分基本不变)。
  • 内核中绝大部分设备驱动都是基于platform驱动模型的,,方便分析内核驱动源码,更便于理解驱动程序的框架。
  • 当platform driver和platform device匹配时,调用driver->probe函数
  • platform driver和platform device一般在不同的源文件中。

三、platform dirver实现步骤

1、platform driver的结构体

struct platform_driver {
	int (*probe)(struct platform_device *);-----在platform device 和platform driver 的driver的name匹配时会调用该函数
	int (*remove)(struct platform_device *);----是probe的反函数
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	const struct platform_device_id *id_table;
};

2、platform driver的注册

int platform_driver_register(struct platform_driver *drv)

3、platform driver的注销

void platform_driver_unregister(struct platform_driver *drv)

4、资源的获取

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

功能:获取platform device的资源,

最常用的资源类型有下面几种:

#define IORESOURCE_IO		0x00000100            //GPIO资源
#define IORESOURCE_MEM		0x00000200        //IO内存资源
#define IORESOURCE_IRQ		0x00000400       //中断号资源 
#define IORESOURCE_DMA		0x00000800            //DMA资源 

返回值:成功返回有效的资源结构体地址,失败返回NULL  

 

四、platform模型驱动实例

以内核源码看门狗驱动为例

1、plafrom device 

(1)注册platform device

大部分的platform device都会在nxp_cpu_devs_register中使用platform_device_register进行注册

#if defined(CONFIG_NXP_WDT)
    printk("mach: add device watchdog\n");
    platform_device_register(&nxp_device_wdt);
#endif
(2) platform结构体
struct platform_device nxp_device_wdt = {
        .name           = DEV_NAME_WDT,         //设备名,资源通过设备名和platform driver匹配,匹配成功后会调用驱动中的probe进行初始化
        .id             = -1,
        .num_resources  = ARRAY_SIZE(nxp_wdt_resource),
        .resource       = nxp_wdt_resource,      //看门狗的资源
};
(3) resource
static struct resource nxp_wdt_resource[] = {
        [0] = DEFINE_RES_MEM(PHY_BASEADDR_WDT, SZ_1K),   //内存资源
        [1] = DEFINE_RES_IRQ(IRQ_PHY_WDT),               //中断资源   
};
(4)platform device的注销
void platform_device_unregister(struct platform_device *pdev)

 

2、platform driver

nxp_wdt.c (drivers\watchdog)

static struct platform_driver nxp_wdt_driver = {-------platform driver
	.probe		= nxp_wdt_probe,-----------------基于某个驱动模型的初始化函数
	.remove		= __devexit_p(nxp_wdt_remove),----相当于probe函数的反函数
	.shutdown	= nxp_wdt_shutdown,
	.suspend	= nxp_wdt_suspend,
	.resume		= nxp_wdt_resume,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= DEV_NAME_WDT,-----------------用于注册时跟device的name比较
		.of_match_table	= nxp_wdt_match,
	},
};


static int __init watchdog_init(void)
{
	pr_info("NXP Watchdog Timer, (c) 2014 SLsiAP\n");

	return platform_driver_register(&nxp_wdt_driver);----------注册platform driver
}

static void __exit watchdog_exit(void)
{
	platform_driver_unregister(&nxp_wdt_driver);-------注销platform driver
	
}

static int __devinit nxp_wdt_probe(struct platform_device *pdev)
{
	1.获取platform device 的资源(IO内存资源、中断号资源)
	2.申请IO内存
	3.获取/使能时钟
	4.具体硬件相关的操作,停止看门狗、设置定时时间
	5.注册看门狗中断
	6.其它的资源操作的代码

}		 

 

五、platform实例 

使用platform模型实现蜂鸣器打开和关闭。

1、beep_dev.c

 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>  
#include <linux/types.h>
#include <linux/ioctl.h>
#include <mach/platform.h>
#include <mach/soc.h>
#include <mach/devices.h>

static void beep_release(struct device *dev)
{
	    printk(KERN_INFO"beep_release\n");
}


//platform device拥有的资源
//定义了两种资源类型,可以使用IO口,也可以使用io内存
static struct resource beep_res[] =
{
	{
		.start =0xC001C000,
		.end = 0xC001C000+0x64+3,   //加3是因为最后一个寄存器还有四位
		.name = "GPIOC_IOMEM",
		.flags = IORESOURCE_MEM,
	},
	{
		.start =PAD_GPIO_C+14,
		.end = PAD_GPIO_C+14,
		.name = "GPIOC_IONUM",
		.flags = IORESOURCE_IO,
	},
		

};

static struct platform_device beep_pdev =
{
	.name 			= "beep_pdev_pdrv",  //和驱动匹配的设备名
	.id   				= -1,
	.num_resources 	= ARRAY_SIZE(beep_res), //得到的是数组元素的个数
	.resource    		= beep_res,
	.dev				=
					{
						.release = beep_release,
					},
};

static int __init beep_dev_init(void)
{
	printk(KERN_INFO"beep_dev_init\n");
	//注册平台设备
	platform_device_register(&beep_pdev);
	return 0;
}

static void __exit beep_dev_exit(void)
{
	//销毁平台设备
	printk(KERN_INFO"beep_dev_exit\n");
	platform_device_unregister(&beep_pdev);
}


module_init(beep_dev_init);
module_exit(beep_dev_exit);
MODULE_AUTHOR("yqf");
MODULE_DESCRIPTION("beep device program");
MODULE_LICENSE("GPL");

 

2、beep_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>  
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>


static struct resource *beep_mem_res  = NULL;
static void __iomem *GPIOCBASE = NULL;
static void __iomem *GPIOCOUT = NULL;
static void __iomem *GPIOCOUTENB= NULL;
static void __iomem *GPIOCALTFN0 = NULL;
static void __iomem *GPIOCALTFN1 = NULL;


#define BEEP_ON  _IO('B',0)
#define BEEP_OFF   _IO('B',1)

static  int beep_open(struct inode* inode,struct file *filp)
{
    printk(KERN_INFO"beep_open\n");
    return 0;
}

static  int beep_close(struct inode* inode,struct file *filp)
{
    printk(KERN_INFO"beep_close\n");
    return 0;
}

static ssize_t beep_write(struct file *filp, const char __user *user, size_t  size, loff_t *oft)
{

    printk(KERN_INFO"beep_write\n");
     return size;
}

static long beep_ioctl (struct file *filp, unsigned int cmd, unsigned long  arg)
{
      switch(cmd)
      {
      		case BEEP_ON:
				writel(readl(GPIOCOUT)|((0x01<<14)),GPIOCOUT);
				break;
		case BEEP_OFF:
				writel(readl(GPIOCOUT)&(~(0x01<<14)),GPIOCOUT);
		default:
			return -ENOIOCTLCMD;			
      }
	 return 0; 
}
struct file_operations beep_fops=
{
	.open = beep_open,
	.release = beep_close,
	.write = beep_write,
	.unlocked_ioctl = beep_ioctl,
};

static struct miscdevice beep_misc=
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = "beep_misc",
	.fops = &beep_fops,
};
static int  beep_probe(struct platform_device *pdev)
{
	int ret;
	printk(KERN_INFO"beep_probe\n");
	misc_register(&beep_misc);
	if(ret < 0)
	{
		printk(KERN_INFO"beep misc register fail\n");
		goto misc_register_err;
	}
	//获取IO内存资源
	beep_mem_res = platform_get_resource(pdev,IORESOURCE_MEM,0);
	if(beep_mem_res==NULL)
	{
		printk(KERN_INFO"platform_get_resource failed\n");
		ret = -ENODEV;
		goto platform_get_resource_err;
	}
	//映射到虚拟地址
	GPIOCBASE = ioremap(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1);
	if(GPIOCBASE==NULL)
	{
		printk(KERN_INFO"ioremap failed");
		ret = -EBUSY;
		goto ioremap_err;
	}
	GPIOCOUT = GPIOCBASE+0x00;
	GPIOCOUTENB = GPIOCBASE+0x04;
	GPIOCALTFN0 = GPIOCBASE+0x20;
	GPIOCALTFN1 = GPIOCBASE+0x24;
	//beep
	writel(readl(GPIOCOUTENB)|(0x01<<14),GPIOCOUTENB);
	writel(readl(GPIOCALTFN0) &(~(0x03<<28)),GPIOCALTFN0);
	writel(readl(GPIOCALTFN0) |(1<<28),GPIOCALTFN0);
	writel(readl(GPIOCOUT)&(~(0x01<<14)),GPIOCOUT);
	return 0;
ioremap_err:
	release_mem_region(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1);
platform_get_resource_err:
	misc_deregister(&beep_misc);
misc_register_err:
		return 0;	
}

static int  beep_remove(struct platform_device *pdev)

{
    
	printk(KERN_INFO"beep_remove\n");
	iounmap(GPIOCBASE);
	release_mem_region(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1);
	misc_deregister(&beep_misc);	
	return 0;
}

static struct platform_driver beep_drv={
	.probe 	= beep_probe,
	.remove	= beep_remove,
	.driver 	={
				.owner = THIS_MODULE,
				.name = "beep_pdev_pdrv",	
			   },
	};

static int  __init  beep_init(void)
{
       printk(KERN_INFO"beep_init\n");
	platform_driver_register(&beep_drv);   
	return 0;
}

static void __exit beep_exit(void)

{
   
	printk(KERN_INFO"beep_exit\n");
	platform_driver_unregister(&beep_drv);
}

module_init(beep_init);

module_exit(beep_exit);

MODULE_AUTHOR("yqf");

MODULE_DESCRIPTION("beep platform driver");
MODULE_LICENSE("GPL");  

3、main.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>

#define BEEP_ON _IO('B',0)
#define BEEP_OFF _IO('B',1)

int main()
{
    int fd;
    char buff[2]={0};
    fd = open("/dev/beep_misc",O_RDWR);
    if(fd<0)
    {
        perror("open bepp misc error!");        
    }
    while(1)
    {
        
        ioctl(fd,BEEP_ON);
        sleep(1);
        ioctl(fd,BEEP_OFF);
         sleep(1);
    }
    
     close(fd);
}

  

  

 

  

 

posted @ 2022-01-08 11:40  轻轻的吻  阅读(183)  评论(0编辑  收藏  举报