天天看點

一步一步學習 Linux 驅動之 platform 機制 (tiny210 LED 驅動)

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  250

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 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;
}
           

繼續閱讀