linux驱动——平台总线驱动 platform_driver_linux platform driver

int val;
int ret;

printk("--------------%s----------\n",__FUNCTION__);

ret = copy_from_user(&val, buf, count);
if(ret > 0)
{
	printk("copy_from_user error\n");

	return -EFAULT;
}

if(val)   //灭灯
{
	writel(readl(samsung_led->reg_base+4)|(1<<5), samsung_led->reg_base+4);  //一个寄存器4个字节 data寄存器为con寄存器+4
}
else  //亮灯
{
	
	writel(readl(samsung_led->reg_base+4)&~(1<<5), samsung_led->reg_base+4);  //一个寄存器4个字节 data寄存器为con寄存器+4
}


return 0;

}

int pled_open(struct inode *inode, struct file *filp)
{
printk(“--------------%s----------\n”,FUNCTION);

return 0;

}

int pled_close (struct inode *ionde, struct file *filp)
{
printk(“--------------%s----------\n”,FUNCTION);

return 0;

}

static struct file_operations pled_fops = {

.open = pled_open ,
.release = pled_close ,
.write = pled_write ,

};


 


## 参考别人的笔记:



struct platform_device {
const char * name; //设备名,可以用来和驱动进行匹配
int id; //作用?
struct device dev; // platform_device相当于是从dev继承派生而来的
u32 num_resources; //资源数目
struct resource * resource; //资源数组首地址

struct   platform_device_id  *id_entry;            //和驱动匹配后用来记录下此设备属于驱动idtable中的哪一个

/*   arch  specific   additions   */
struct   pdev_archdata          archdata;

};
//平台驱动
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 (resume)(struct platform_device );
struct device_driver driver;//匹配的设备的所有信息,platform_driver相当于是从driver继承派生而来的
struct platform_device_id id_table; //所有可以匹配的设备的idtable
};
/
***********/转载请注明原文地址 http://blog.csdn.net/oyhb_1992/article/details/77477112
struct device_driver {
const char *name; //匹配的设备名
struct bus_type *bus;

struct   module               *owner;
const   char            *mod_name;   /*   used   for   built-in   modules  */

bool   suppress_bind_attrs;  /*   disables   bind/unbind  via   sysfs   */

int   (*probe)  (struct   device   *dev);
int   (*remove)  (struct   device   *dev);
void   (*shutdown)  (struct   device   *dev);
int   (*suspend)  (struct   device   *dev,  pm_message_t   state);
int   (*resume)  (struct   device   *dev);
const   struct  attribute_group   **groups;

const   struct  dev_pm_ops   *pm;

struct   driver_private   *p;

//匹配算法:打开driver\base\platform.c文件,有个匹配函数
方法一:用id_table进行匹配
static int platform_match(struct device *dev, struct device_driver *drv)
{
//由前面介绍platform_device相当于是从dev继承派生而来的,platform_driver相当于是从driver继承派生而来的,这里由结构体中的成员变量可以反推出整个结构体变量的首地址
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);

	 /*   match  against   the   id   table   first  */
	 if   (pdrv->id_table)//如果id_table不为空,则用id进行匹配,然后直接返回不再调用name匹配算法
		 return   platform_match_id(pdrv->id_table,   pdev)  !=   NULL;
	 //匹配方法二
	 /*   fall-back  to   driver   name  match   */
	 return   (strcmp(pdev->name,   drv->name)   ==   0);
 }

 static  const   struct   platform_device_id   *platform_match_id(
						 struct   platform_device_id   *id,
						 struct   platform_device   *pdev)
 {
	 while   (id->name[0])   {        //id_table数组没有到尾
		 if   (strcmp(pdev->name,   id->name)   ==   0)   {     //本质上还是还是通过名字对应来匹配,但是匹配的名字被列在一个表中,这样一个驱动就可以匹配多个不同name的驱动了,只要设备的pdev->name在id_table数组列表的name中就可以匹配。
			 pdev->id_entry   =   id;   //和设备匹配后用来记录下此设备属于驱动idtable中的哪一个
			 return   id;
		 }
		 id++;      //指向id_table数组的下一个数组成员
	 }
	 return   NULL;
 }

 方法二:用设备名和驱动名进行匹配,这样就只能一个驱动匹配一个设备


	 源码举例1:
	 /drivers/mtd/nand/S3C2410.c
	 static   struct  platform_device_id   s3c24xx_driver_ids[]   =   {
		 {
			 .name                =   "s3c2410-nand",
				 .driver_data     =   TYPE_S3C2410,
		 },   {
			 .name                =   "s3c2440-nand",
				 .driver_data     =   TYPE_S3C2440,
		 },   {
			 .name                =   "s3c2412-nand",
				 .driver_data     =   TYPE_S3C2412,
			 },   {
				 .name                =   "s3c6400-nand",
					 .driver_data     =   TYPE_S3C2412,   /*   compatible   with  2412   */
			 },
			 {   }
 };

 static  struct   platform_driver   s3c24xx_nand_driver   =   {
	 .probe               =   s3c24xx_nand_probe,
	 .remove            =  s3c24xx_nand_remove,
	 .suspend  =   s3c24xx_nand_suspend,
	 .resume             =  s3c24xx_nand_resume,
	 .id_table  =   s3c24xx_driver_ids,//这个驱动支持的设备id表
	 .driver                =   {
		 .name       =  "s3c24xx-nand",
		 .owner     =   THIS_MODULE,
	 },
 };

static struct platform_device_id s3c_rtc_driver_ids[] = {
{
.name = “s3c2410-rtc”,
.driver_data = TYPE_S3C2410,
}, {
.name = “s3c2416-rtc”,
.driver_data = TYPE_S3C2416,
}, {
.name = “s3c2443-rtc”,
.driver_data = TYPE_S3C2443,
}, {
.name = “s3c64xx-rtc”,
.driver_data = TYPE_S3C64XX,
},
{ }
};

static struct platform_device_id s3c_rtc_driver_ids[] = {
{
.name = “s3c2410-rtc”,
.driver_data = TYPE_S3C2410,
}, {
.name = “s3c2416-rtc”,
.driver_data = TYPE_S3C2416,
}, {
.name = “s3c2443-rtc”,
.driver_data = TYPE_S3C2443,
}, {
.name = “s3c64xx-rtc”,
.driver_data = TYPE_S3C64XX,
},
{ }
};

上面就是platform_device_id,可以看到这个驱动支持的设备不只一种,包括2410、2416等等。而Platform_driver定义如下:

static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = __devexit_p(s3c_rtc_remove),
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.id_table = s3c_rtc_driver_ids,
.driver = {
.name = “s3c-rtc”,
.owner = THIS_MODULE,
.of_match_table = s3c_rtc_dt_match,
},
};

2410的platfrom_device定义如下:

struct platform_device s3c_device_rtc = {
.name = “s3c2410-rtc”,
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
};

通过id_table这种方式有什么好处呢,如果只是简单的比较驱动里的.driver.name字段和设备里的.name是否相同,那么一个驱动只能支持特定的一个设备,而如果通过id_table的方式呢,一个驱动.id_table可以和多个驱动的设备里的.name匹配是否相同,支持很多个设备,而它们只是name字段不同而已。


## 实验代码:


### plat\_ded\_pdrv.c



#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>

//probe方法实现步骤:
/*
1、注册设备号,
2、创建设备节点
3、初始化硬件
4、实现各种io接口

*/

struct led_dev{
int dev_major; //主设备号
struct class *cls; //设备类
struct class_device *dev; //设备节点
struct resource *res; //获取到的资源
void *reg_base; //物理地址映射后的虚拟地址
};

struct led_dev *samsung_led;

ssize_t pled_write(struct file *filp, const char __user *buf, size_t count, loff_t *fops)
{
int val;
int ret;

printk("--------------%s----------\n",__FUNCTION__);

ret = copy_from_user(&val, buf, count);
if(ret > 0)
{
	printk("copy_from_user error\n");

	return -EFAULT;
}

if(val)   //灭灯
{
	writel(readl(samsung_led->reg_base+4)|(1<<5), samsung_led->reg_base+4);  //一个寄存器4个字节 data寄存器为con寄存器+4
}
else  //亮灯
{
	
	writel(readl(samsung_led->reg_base+4)&~(1<<5), samsung_led->reg_base+4);  //一个寄存器4个字节 data寄存器为con寄存器+4
}


return 0;

}

int pled_open(struct inode *inode, struct file *filp)
{
printk(“--------------%s----------\n”,FUNCTION);

return 0;

}

int pled_close (struct inode *ionde, struct file *filp)
{
printk(“--------------%s----------\n”,FUNCTION);

return 0;

}

static struct file_operations pled_fops = {

.open = pled_open ,
.release = pled_close ,
.write = pled_write ,

};

static int led_pdrv_probe(struct platform_device * pdev)
{
printk(“--------------%s----------\n”,FUNCTION);

samsung_led = kmalloc(sizeof(struct led_dev), GFP_KERNEL);
if(samsung_led==NULL)
{
	printk("samsung_led kmalloc error\n");

	return -ENOMEM;
}

samsung_led->dev_major = register_chrdev(0, "led_drv", &pled_fops); //动态申请

samsung_led->cls = class_create(THIS_MODULE, "led_class") ;

samsung_led->dev = class_device_create(samsung_led->cls, NULL, 
					MKDEV(samsung_led->dev_major, 0), NULL, "led0");

//获取资源
//参数1、从哪个pdev 获取资源
//参数2、资源类型

/*参数3、表示获取同种资源第几个 ,一定要注意是同种资源的排序(即先看同种资源,再看数组下标),
而不是光看数组下标
*/

samsung_led->res = platform_get_resource(pdev,IORESOURCE_MEM, 0);

samsung_led->reg_base = ioremap(samsung_led->res->start, 
					samsung_led->res->end-samsung_led->res->start+1);

if(samsung_led->reg_base == NULL)
{
	printk("ioremap error \n");

	return -ENOMEM;

}


//配置寄存器输出,writel对某个寄存器写数据;

writel((readl(samsung_led->reg_base)&~(3<<10))|(1<<10), samsung_led->reg_base);


return 0;

}

static int led_pdrv_remove(struct platform_device * pdev)
{
printk(“--------------%s----------\n”,FUNCTION);

iounmap(samsung_led->reg_base);    //释放io资源
class_device_destroy(samsung_led->cls, MKDEV(samsung_led->dev_major, 0));
class_destroy(samsung_led->cls);
unregister_chrdev(samsung_led->dev_major, "led_drv");
kfree(samsung_led);

return 0;

}

//const struct platform_device_id *id_table 老版本的没有;

//构建一个platform_driver对象

struct platform_driver led_pdrv = {
.probe = led_pdrv_probe , //匹配成功调用
.remove = led_pdrv_remove ,
.driver = {
.name = “myled” , //可以用来作匹配,也可以用id匹配 ,
//在driver目录下会生成myled驱动

}

};

static int __init plat_led_drv_init(void)
{
int ret;

printk("--------------%s----------\n",__FUNCTION__);

ret = platform_driver_register(&led_pdrv);
if(ret < 0)
{
	printk("driver register error!\n");
	return ret;

}

return 0;

}

static void __exit plat_led_drv_exit(void)
{
printk(“--------------%s----------\n”,FUNCTION);

platform_driver_unregister(&led_pdrv);

}

module_init(plat_led_drv_init);
module_exit(plat_led_drv_exit);
MODULE_LICENSE(“GPL”);


### plat\_led\_pdev.c



#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>

//led GPF 4 5 6

struct resource led_res[]=
{
[0] = {
.start = 0x56000050 , //这个是起始地址 GPFCON
.end = 0x56000050+8-1 , //GPFUP终止地址 0x56000058-1
.flags = IORESOURCE_MEM , //说明是什么类型的资源 这里是IO内存资源
},

[1] = {

	.start = 5 ,             //start和end是一样的,中断号为5
	.end =   5 , 		    
	.flags =  IORESOURCE_IRQ ,              //中断资源
},

};

struct platform_device led_pdev = {

.name = "myled" ,  //名称和driver里面的保持一致
.id = -1 ,
.num_resources = ARRAY_SIZE(led_res),  //资源数量
.resource = led_res ,

};

static int __init plat_led_dev_init(void)
{
int ret;

printk("--------------%s----------\n",__FUNCTION__);

ret = platform_device_register(&led_pdev);
if(ret < 0)
{
	printk("platform_device_register error!\n");
	return ret;

}

return 0;

}

static void __exit plat_led_dev_exit(void)
{
printk(“--------------%s----------\n”,FUNCTION);

platform_device_unregister(&led_pdev);

}

module_init(plat_led_dev_init);
module_exit(plat_led_dev_exit);
MODULE_LICENSE(“GPL”);


### test.c



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

int main(int argc, char *argv[])
{

int fd;
int on;

fd = open("/dev/led0", O_RDWR);

if(fd < 0)
{
	printf("open error\n");
	exit(1);
}


//控制灯的亮灭

while(1)
{
	on = 0 ;  //亮灯
	write(fd, &on, 4);
	sleep(1);

	on = 1;  //灭灯
	write(fd,&on,4);
	sleep(1);

}



close(fd);

return 0 ;

}


## 总结:



> 
> 容易出错的地方是:创建设备节点用错函数,device\_create  应该用 class\_device\_create
> 
> 
> 调式方法:多加几个printk函数打印
> 
> 
> 


 


 


 


 


 


 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值