一步一步学习 Linux 驱动之 platform 机制 (tiny210 LED 驱动)

时间:2022-08-24 15:49:15

1、概述

在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:1:实现platform驱动 2:实现platform设备,然而在实现这两个工作的过程中还需要实现其他的很多小工作,在后面介绍。platform模型驱动的实现过程核心架构就很简单,如下所示。

platform驱动模型三个对象:platform总线、platform设备、platform驱动。
platform总线对应的内核结构:struct bus_type-->它包含的最关键的函数:match()
platform设备对应的内核结构:struct platform_device-->注册:platform_device_register(unregiste)
platform驱动对应的内核结构:struct platform_driver-->注册:platform_driver_register(unregiste)

简单介绍下platform驱动的工作过程:设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备)( http://blog.csdn.net/xy010902100449/article/details/45700523 前面我们讲了platform设备驱动的match自动匹配过程), 如果存在则将双方绑定;如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。

2、代码


/**
* @Author: ZP1015
*
* @Copyright:SCUT.
*
* @Function:Platform device driver for led
*
* @Creat:2015-06-10
*
* @Modify:2015-12-22
**/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>

static void Myled_platform_device_release(struct device * dev)
{
return ;
}
static struct resource Myled_resource[] = {
[0] = {
.start = 0xe0200280,
.end = 0xe0200280 + 12,
.flags = IORESOURCE_MEM
},
};
static struct platform_device Myledplatform_device_led = {
.name = "Myled_platform_device_driver",
.id = -1,
.num_resources = ARRAY_SIZE(Myled_resource),
.resource = Myled_resource,
.dev = {
.release = Myled_platform_device_release,
},
};
static int __init Myled_platform_device_init(void)
{
printk("Myled_platform_device add ok!\n");
return platform_device_register(&Myledplatform_device_led);
}
static void __exit Myled_platform_device_exit(void)
{
printk("Myled_platform_device remove ok!\n");
platform_device_unregister(&Myledplatform_device_led);
}
MODULE_AUTHOR("ZP1015");
MODULE_LICENSE("GPL");
module_init(Myled_platform_device_init);
module_exit(Myled_platform_device_exit);
//////////////////////////////

/*** @Author: ZP1015 ** @Copyright:SCUT.* * @Function:Platform  driver for led * * @Creat:2015-06-10** @Modify:2015-12-22**/#include <linux/init.h>#include <linux/module.h>#include <linux/fs.h>#include <asm/io.h>#include <asm/uaccess.h>#include <linux/device.h>#include <linux/cdev.h>#include <linux/platform_device.h>#define GLOBAL_LED_MAJOR  250static unsigned int global_led_major = GLOBAL_LED_MAJOR; static struct cdev *led_cdev = NULL; static struct class *led_class = NULL;volatile unsigned long *gpbcon = NULL;volatile unsigned long *gpbdat = NULL; volatile unsigned long *gpbup = NULL; static int Myled_open(struct inode * inode,struct file * file){	return 0;}static ssize_t Myled_read(struct file * file,char __user * in,size_t size,loff_t * off){	return 0;}static ssize_t Myled_write(struct file * file,const char __user * in,size_t size,loff_t * off){	return 0;}static void myled_configure(void){		*gpbcon &= ~((0x3<<(0*4)) | (0x3<<(1*4)) | (0x3<<(2*4)) | (0x3<<(3*4)));	*gpbcon |=  ((0x1<<(0*4)) | (0x1<<(1*4)) | (0x1<<(2*4)) | (0x1<<(3*4)));}static void myled_on(void){	*gpbdat &= ~((1<<0) | (1<<1) | (1<<2) | (1<<3));// 点灯}static void myled_off(void){	*gpbdat |= (1 << 0)|(1 << 1)|(1 << 2)|(1 << 3);// 灭灯	}struct file_operations led_fops = {	.owner = THIS_MODULE,	.open  = Myled_open,	.read  = Myled_read,	.write = Myled_write,};static int __devinit Myled_probe(struct platform_device *pdev){	int ret;	int err;	dev_t devno;	struct resource *pIORESOURCE_MEM = NULL;		printk(KERN_ALERT"Myled_probe!\n");		/*1.初始化cdev*/	led_cdev = cdev_alloc();//分配cdev	cdev_init(led_cdev,&led_fops);//初始化cdev	led_cdev->owner = THIS_MODULE;		/*2.获取字符设备号*/	devno = MKDEV(global_led_major,0);//将主设备号和次设备号转换成dev_t类型	if (devno) {		ret = register_chrdev_region(devno,1,"Myled_platfor_driver");//注册一系列设备号	} 	else {		ret = alloc_chrdev_region(&devno,0,1,"Myled_platfor_driver");//未知主设备号注册设备号		global_led_major = MAJOR(devno);	}		if (ret < 0) {		return ret;	}		/*3.注册字符设备*/	err = cdev_add(led_cdev,devno,1);//注册cdev	if (err) {		printk(KERN_NOTICE"Error %d adding led_cdev",err);		return -1;	} else {		printk(KERN_NOTICE"Myled_platfor_driver init ok!\n");		}		/*4.注册platform机制,在/sys/class/下创建类目录*/	led_class = class_create(THIS_MODULE,"Myled_platfor_driver");//自动创建设备节点	device_create(led_class,NULL,devno,NULL,"platfor_driver_for_Myled");	pIORESOURCE_MEM = platform_get_resource(pdev,IORESOURCE_MEM,0);		gpbcon = ioremap(pIORESOURCE_MEM->start,pIORESOURCE_MEM->end - pIORESOURCE_MEM->start);	gpbdat = gpbcon + 1;	gpbup  = gpbcon + 2;		myled_configure();	myled_on();		printk(KERN_NOTICE"platfor_driver_for_Myled init ok!\n");	return 0;}static int __devexit Myled_remove(struct platform_device *pdev){	dev_t devno;	devno = MKDEV(global_led_major,0);//将主设备号和次设备号转换成dev_t类型	printk("Myled_remove!\n");	myled_off();	cdev_del(led_cdev);	iounmap(gpbcon);	unregister_chrdev_region(devno,1);	device_destroy(led_class,devno);	class_destroy(led_class);		return 0;}static struct platform_driver Myled_platform_driver = {	.probe  = Myled_probe,	.remove = __devexit_p(Myled_remove),	.driver = {		.name = "Myled_platform_device_driver",		.owner = THIS_MODULE,	}};static int __init Myled_platform_driver_init(void){    printk("platform_driver_for_Myled init\n");	return platform_driver_register(&Myled_platform_driver);}static void __exit Myled_platform_driver_exit(void){    printk("platform_driver_for_Myled exit\n");	platform_driver_unregister(&Myled_platform_driver);}MODULE_AUTHOR("ZP1015");MODULE_LICENSE("GPL");module_param(global_led_major,int,S_IRUGO);module_init(Myled_platform_driver_init);module_exit(Myled_platform_driver_exit);///////////////////////


3、 实现结果

先注册设备,然后注册设备驱动

insmod 一下灯全亮,rmmod 一下等全灭。

4、 系统调用方式

/**
* @Author: ZP1015
*
* @Copyright:SCUT.
*
* @Function:Platform driver for led
*
* @Creat:2015-06-10
*
* @Modify:2015-12-22
**/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>

#define GLOBAL_LED_MAJOR 250
#define DEVICE_NAME "Myled_platfor_driver"

static unsigned int global_led_major = GLOBAL_LED_MAJOR;
static struct cdev *led_cdev = NULL;
static struct class *led_class = NULL;

volatile unsigned long *gpbcon = NULL;
volatile unsigned long *gpbdat = NULL;
volatile unsigned long *gpbup = NULL;

static void myled_configure(void)
{
*gpbcon &= ~((0x3<<(0*4)) | (0x3<<(1*4)) | (0x3<<(2*4)) | (0x3<<(3*4)));
*gpbcon |= ((0x1<<(0*4)) | (0x1<<(1*4)) | (0x1<<(2*4)) | (0x1<<(3*4)));
}

static void myled_on(void)
{
*gpbdat &= ~((1<<0) | (1<<1) | (1<<2) | (1<<3));// 点灯
}

static void myled_off(void)
{
*gpbdat |= (1 << 0)|(1 << 1)|(1 << 2)|(1 << 3);// 灭灯
}

static int Myled_open(struct inode * inode,struct file * file)
{
myled_configure();

return 0;
}

static ssize_t Myled_read(struct file * file,char __user * in,size_t size,loff_t * off)
{
return 0;
}

static ssize_t Myled_write(struct file * file,const char __user * in,size_t size,loff_t * off)
{
char val[1];

if(copy_from_user(val, in, size)) {//用户到内核空间
return -EFAULT;
}

printk("[%s,%d]val:%c %d\n",__func__,__LINE__,val[0],val[0]);

switch(val[0]) {
case '0':
myled_on();
break;
case '1':
myled_off();
break;
}

return 0;
}

struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = Myled_open,
.read = Myled_read,
.write = Myled_write,
};
static int __devinit Myled_probe(struct platform_device *pdev)
{
int ret;
int err;
dev_t devno;
struct resource *pIORESOURCE_MEM = NULL;

printk(KERN_ALERT"Myled_probe!\n");

/*1.初始化cdev*/
led_cdev = cdev_alloc();//分配cdev
cdev_init(led_cdev,&led_fops);//初始化cdev
led_cdev->owner = THIS_MODULE;

/*2.获取字符设备号*/
devno = MKDEV(global_led_major,0);//将主设备号和次设备号转换成dev_t类型
if (devno) {
ret = register_chrdev_region(devno,1,DEVICE_NAME);//注册一系列设备号
}
else {
ret = alloc_chrdev_region(&devno,0,1,DEVICE_NAME);//未知主设备号注册设备号
global_led_major = MAJOR(devno);
}

if (ret < 0) {
return ret;
}

/*3.注册字符设备*/
err = cdev_add(led_cdev,devno,1);//注册cdev
if (err) {
printk(KERN_NOTICE"Error %d adding led_cdev",err);
return -1;
} else {
printk(KERN_NOTICE"Myled_platfor_driver init ok!\n");
}

/*4.自动创建设备节点,在/sys/class/下创建类目录*/
led_class = class_create(THIS_MODULE,"myled_class");//自动创建设备节点
device_create(led_class,NULL,devno,NULL,"myled");

pIORESOURCE_MEM = platform_get_resource(pdev,IORESOURCE_MEM,0);

gpbcon = ioremap(pIORESOURCE_MEM->start,pIORESOURCE_MEM->end - pIORESOURCE_MEM->start);
gpbdat = gpbcon + 1;
gpbup = gpbcon + 2;

printk(KERN_NOTICE"Myled_platfor_driver init ok!\n");

return 0;
}

static int __devexit Myled_remove(struct platform_device *pdev)
{
dev_t devno;
devno = MKDEV(global_led_major,0);//将主设备号和次设备号转换成dev_t类型

printk("Myled_remove!\n");
myled_off();

cdev_del(led_cdev);
iounmap(gpbcon);
unregister_chrdev_region(devno,1);
device_destroy(led_class,devno);
class_destroy(led_class);

return 0;
}
static struct platform_driver Myled_platform_driver = {
.probe = Myled_probe,
.remove = __devexit_p(Myled_remove),
.driver = {
.name = "Myled_platform_device_driver",
.owner = THIS_MODULE,
}
};
static int __init Myled_platform_driver_init(void)
{
printk("platform_driver_for_Myled init\n");
return platform_driver_register(&Myled_platform_driver);
}

static void __exit Myled_platform_driver_exit(void)
{
printk("platform_driver_for_Myled exit\n");
platform_driver_unregister(&Myled_platform_driver);
}
MODULE_AUTHOR("ZP1015");
MODULE_LICENSE("GPL");
module_param(global_led_major,int,S_IRUGO);
module_init(Myled_platform_driver_init);
module_exit(Myled_platform_driver_exit);
///////////////////////
测试程序:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h> // open() close()
#include <unistd.h> // read() write()

int main(void)
{
int fd;

fd = open("/dev/myled", O_RDWR|O_NDELAY);
if (fd < 0) {
printf("open device /lib/module/2.6.32.2/myled error!\n");
}
else {
while(1) {
printf("led is on!\n");
write(fd,"1",1);
sleep(1);//等待1秒再做下一步操作
printf("led is off!\n");
write(fd,"0",1);
sleep(1);
}
close(fd);
}
return 0;
}