找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 3910|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

platform驅(qū)動(dòng)模型編程總結(jié)(基于mini2440平臺(tái)的LED驅(qū)動(dòng))

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:60266 發(fā)表于 2014-8-18 02:24 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
sysfs與platform的相關(guān)基礎(chǔ)介紹可以參考博文【 sysfs   platform總線 】。
platform模型驅(qū)動(dòng)編程,需要實(shí)現(xiàn)platform_device(設(shè)備)與platform_driver(驅(qū)動(dòng))在platform(虛擬總線)上的注冊(cè)、匹配,相互綁定,然后再做為一個(gè)普通的字符設(shè)備進(jìn)行相應(yīng)的應(yīng)用,總之如果編寫(xiě)的是基于字符設(shè)備的platform驅(qū)動(dòng),在遵循并實(shí)現(xiàn)platform總線上驅(qū)動(dòng)與設(shè)備的特定接口的情況下,最核心的還是字符設(shè)備的核心結(jié)構(gòu):cdev、 file_operations(他包含的操作函數(shù)接口)、dev_t(設(shè)備號(hào))、設(shè)備文件(/dev)等,因?yàn)橛胮latform機(jī)制編寫(xiě)的字符驅(qū)動(dòng),它的本質(zhì)是字符驅(qū)動(dòng)。

在一般情況下,2.6內(nèi)核中已經(jīng)初始化并掛載了一條platform總線在sysfs文件系統(tǒng)中。那么我們編寫(xiě)platform模型驅(qū)動(dòng)時(shí),需要完成兩個(gè)工作:1:實(shí)現(xiàn)platform驅(qū)動(dòng) 2:實(shí)現(xiàn)platform設(shè)備,然而在實(shí)現(xiàn)這兩個(gè)工作的過(guò)程中還需要實(shí)現(xiàn)其他的很多小工作,在后面介紹。platform模型驅(qū)動(dòng)的實(shí)現(xiàn)過(guò)程核心架構(gòu)就很簡(jiǎn)單,如下所示。



platform驅(qū)動(dòng)模型三個(gè)對(duì)象:platform總線、platform設(shè)備、platform驅(qū)動(dòng)。
platform總線對(duì)應(yīng)的內(nèi)核結(jié)構(gòu):struct bus_type-->它包含的最關(guān)鍵的函數(shù):match()
platform設(shè)備對(duì)應(yīng)的內(nèi)核結(jié)構(gòu):struct platform_device-->注冊(cè):platform_device_register(unregiste)
platform驅(qū)動(dòng)對(duì)應(yīng)的內(nèi)核結(jié)構(gòu):struct platform_driver-->注冊(cè):platform_driver_register(unregiste)


簡(jiǎn)單介紹下platform驅(qū)動(dòng)的工作過(guò)程:設(shè)備(或驅(qū)動(dòng))注冊(cè)的時(shí)候,都會(huì)引發(fā)總線調(diào)用自己的match函數(shù)來(lái)尋找目前platform總線是否掛載有與該設(shè)備(或驅(qū)動(dòng))名字匹配的驅(qū)動(dòng)(或設(shè)備),如果存在則將雙方綁定;如果先注冊(cè)設(shè)備,驅(qū)動(dòng)還沒(méi)有注冊(cè),那么設(shè)備在被注冊(cè)到總線上時(shí),將不會(huì)匹配到與自己同名的驅(qū)動(dòng),然后在驅(qū)動(dòng)注冊(cè)到總線上時(shí),因?yàn)樵O(shè)備已注冊(cè),那么總線會(huì)立即匹配與綁定這時(shí)的同名的設(shè)備與驅(qū)動(dòng),再調(diào)用驅(qū)動(dòng)中的probe函數(shù)等;如果是驅(qū)動(dòng)先注冊(cè),同設(shè)備驅(qū)動(dòng)一樣先會(huì)匹配失敗,匹配失敗將導(dǎo)致它的probe函數(shù)暫不調(diào)用,而是要等到設(shè)備注冊(cè)成功并與自己匹配綁定后才會(huì)調(diào)用。




接下來(lái)講解如下實(shí)現(xiàn)platform驅(qū)動(dòng)與設(shè)備的詳細(xì)過(guò)程。
實(shí)現(xiàn)platform驅(qū)動(dòng)的詳細(xì)過(guò)程



1:定義驅(qū)動(dòng)實(shí)例 mini2440_led_platform_driver
static struct platform_driver mini2440_led_platform_driver = {
.probe  = mini2440_led_probe,
.remove = __devexit_p(mini2440_led_remove),
.driver = {
.name = "mini2440_led_platform_device_driver",
.owner = THIS_MODULE,
}
};

2:實(shí)現(xiàn)驅(qū)動(dòng)實(shí)例成員函數(shù):probe  mini2440_led_probe
probe函數(shù)中要實(shí)現(xiàn)的功能包括:設(shè)備號(hào)的申請(qǐng),字符設(shè)備內(nèi)核對(duì)象cdev的定義、初始化、綁定file_operations,注冊(cè)字符設(shè)備內(nèi)核對(duì)象cdev,在/dev下動(dòng)態(tài)創(chuàng)建設(shè)備文件。
3:platform模型驅(qū)動(dòng)寫(xiě)字符設(shè)備仍要實(shí)現(xiàn)字符設(shè)備的核心結(jié)構(gòu)file_operations,第一步的cdev要用到
將file_operations結(jié)構(gòu)體中open、write、read等要用到的接口函數(shù)實(shí)現(xiàn)。
4:實(shí)現(xiàn)驅(qū)動(dòng)實(shí)例成員函數(shù):remove mini2440_led_remove
remove函數(shù)中要實(shí)現(xiàn)的功能與probe中的相反,進(jìn)行相關(guān)設(shè)備與資源的注銷。
【probe與remove函數(shù)其實(shí)對(duì)應(yīng)前面用非platform機(jī)制寫(xiě)驅(qū)動(dòng)的時(shí)候所實(shí)現(xiàn)的模塊加載與卸載函數(shù),而在platform機(jī)制中,模塊的加載與卸載函數(shù)調(diào)用的是 設(shè)備或驅(qū)動(dòng)的注冊(cè)函數(shù)】
5:實(shí)現(xiàn)驅(qū)動(dòng)的加載與卸載函數(shù):
在加載函數(shù)中,調(diào)用驅(qū)動(dòng)的注冊(cè)函數(shù),platform_driver_register(...);
在卸載函數(shù)中,調(diào)用驅(qū)動(dòng)的卸載函數(shù),platform_driver_unregister(...);
實(shí)現(xiàn)platform設(shè)備的詳細(xì)過(guò)程

platform設(shè)備的實(shí)現(xiàn)有兩種方法:
     1:最笨的一種:直接在內(nèi)核源代碼里面添加相關(guān)的資源代碼,\arch\arm\mach-s3c2440\mach-mini2440.c
     2:編寫(xiě)設(shè)備模塊,用insmod命令加載該設(shè)備模塊到platform總線上。
當(dāng)然用第二種了,不過(guò)這兩種方法的原理與要寫(xiě)的代碼都是一樣的,但是第一種不用自己注冊(cè),因?yàn)橄到y(tǒng)初始化的時(shí)候會(huì)將mach-mini2440.c中struct platform_device *mini2440_devices[] __initdata 設(shè)備數(shù)組中包含的所有設(shè)備注冊(cè)到總線上。而第二種手動(dòng)注冊(cè),一個(gè)字活。
1:定義設(shè)備與資源實(shí)例
static struct resource mini2440_led_resource[] = {
        [0] = {
                .start = 0x56000010,
                .end   = 0x56000010 + 12,
                .flags = IORESOURCE_MEM
        },
};
static struct platform_device mini2440_platform_device_led = {
        .name           = " mini2440_led_platform_device_driver ",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(mini2440_led_resource),
        .resource       =  mini2440_led_resource ,
        .dev            = {
              .release  = mini2440_led_platform_device_release,
        },
};

2:實(shí)現(xiàn)設(shè)備的成員函數(shù)release  
static void mini2440_led_platform_device_release(struct device * dev)
{
    return ;
}
3:實(shí)現(xiàn)設(shè)備的加載與卸載函數(shù):
在加載函數(shù)中,調(diào)用設(shè)備的注冊(cè)函數(shù),platform_device_register(...);
在卸載函數(shù)中,調(diào)用設(shè)備的卸載函數(shù),platform_device_unregister(...);

多個(gè)設(shè)備的同時(shí)注冊(cè):platform_add_devices(struct platform_devices **devs,int num);
struct platform_devices **devs設(shè)備數(shù)組,num包含的設(shè)備數(shù)目

驅(qū)動(dòng)與設(shè)備是否成功注冊(cè),我們都可以在/sys/bus/platform/devices(drivers)/下查看

下面是代碼清單:
//////////////////////////////
設(shè)備模塊:
#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 mini2440_led_platform_device_release(struct device * dev)
{
    return ;
}
static struct resource mini2440_led_resource[] = {
        [0] = {
                .start = 0x56000010,
                .end   = 0x56000010 + 12,
                .flags = IORESOURCE_MEM
        },
};
static struct platform_device mini2440_platform_device_led = {
        .name           = "mini2440_led_platform_device_driver",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(mini2440_led_resource),
        .resource       = mini2440_led_resource,
        .dev            = {
              .release  = mini2440_led_platform_device_release,
        },
};
static int __init mini2440_led_platform_device_init(void)
{
    printk("mini2440_led_platform_device add ok!\n");
return platform_device_register(&mini2440_platform_device_led);
}
static void __exit mini2440_led_platform_device_exit(void)
{
    printk("mini2440_led_platform_device remove ok!\n");
platform_device_unregister(&mini2440_platform_device_led);
}
MODULE_AUTHOR("xxxx");
MODULE_LICENSE("GPL");
module_init(mini2440_led_platform_device_init);
module_exit(mini2440_led_platform_device_exit);
//////////////////////////////
驅(qū)動(dòng)模塊:
#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;

static volatile unsigned long *gpfcon = NULL;
static volatile unsigned long *gpfdat = NULL;
static volatile unsigned long *gpfup = NULL;

static int mini2440_led_open(struct inode * inode,struct file * file)
{
printk("mini2440_open[kernel_space]\n");
*gpfcon &=~((0x3<<0) | (0x3<<8) |(0x3<<10) |(0x3<<12)|(0x3<<14));
*gpfcon |= (0x1<<0) | (0x1<<8) |(0x1<<10) |(0x1<<12)|(0x1<<14);
return 0;
}
static ssize_t mini2440_led_read(struct file * file,const char __user * in,size_t size,loff_t * off)
{
printk("mini2440_read[kernel_space]\n");
return 0;
}
static ssize_t mini2440_led_write(struct file * file,const char __user * in,size_t size,loff_t * off)
{
    int ret;
char ker_buf;
printk("mini2440_write[kernel_space]\n");
ret = copy_from_user(&ker_buf,in,size);
printk("ker_buf =%d\n",ker_buf);
if(ker_buf)
{
*gpfdat &=~((0x1<<4)|(0x1<<5)|(0x1<<6)|(0x1<<7));
      
*gpfdat |= (0x1<<0);
}
else
{
*gpfdat |=(0x1<<4)|(0x1<<5)|(0x1<<6)|(0x1<<7);
*gpfdat &= ~(0x1<<0);
}
return 0;
}
struct file_operations led_fops = {
.owner = THIS_MODULE,
.open  = mini2440_led_open,
.read  = mini2440_led_read,
.write = mini2440_led_write,
};
static int __devinit mini2440_led_probe(struct platform_device *pdev)
{
int ret;
int err;
dev_t devno;
struct resource *pIORESOURCE_MEM;
devno = MKDEV(global_led_major,0);
printk(KERN_ALERT"mini2440_led_probe!\n");
if (devno) {
ret = register_chrdev_region(devno,1,"mini2440_led_platfor_driver");
} else {
ret = alloc_chrdev_region(&devno,0,1,"mini2440_led_platfor_driver");
global_led_major = MAJOR(devno);
}
if (ret < 0) {
return ret;
}
led_cdev = cdev_alloc();
cdev_init(led_cdev,&led_fops);
led_cdev->owner = THIS_MODULE;
err = cdev_add(led_cdev,devno,1);
led_class = class_create(THIS_MODULE,"mini2440_led_platfor_driver");
device_create(led_class,NULL,MKDEV(global_led_major,0),NULL,"platfor_driver_for_mini2440_led");
pIORESOURCE_MEM = platform_get_resource(pdev,IORESOURCE_MEM,0);
gpfcon = ioremap(pIORESOURCE_MEM->start,pIORESOURCE_MEM->end - pIORESOURCE_MEM->start);
gpfdat = gpfcon + 1;
gpfup  = gpfcon + 2;
if (err) {
printk(KERN_NOTICE"Error %d adding led_cdev",err);
return -1;
} else {
printk(KERN_NOTICE"platform_driver_for_mini2440_led init ok!\n");
return 0;
}
}




static int __devexit mini2440_led_remove(struct platform_device *pdev)
{
    printk("mini2440_led_remove!\n");
cdev_del(led_cdev);
iounmap(gpfcon);
unregister_chrdev_region(MKDEV(global_led_major,0),1);
device_destroy(led_class, MKDEV(global_led_major,0));
class_destroy(led_class);
return 0;
}
static struct platform_driver mini2440_led_platform_driver = {
.probe  = mini2440_led_probe,
.remove = __devexit_p(mini2440_led_remove),
.driver = {
.name = "mini2440_led_platform_device_driver",
.owner = THIS_MODULE,
}
};
static int __init mini2440_led_platform_driver_init(void)
{
    printk("platform_driver_for_mini2440_led init\n");
return platform_driver_register(&mini2440_led_platform_driver);
}

static void __exit mini2440_led_platform_driver_exit(void)
{
    printk("platform_driver_for_mini2440_led exit\n");
platform_driver_unregister(&mini2440_led_platform_driver);
}
MODULE_AUTHOR("xxx");
MODULE_LICENSE("GPL");
module_param(global_led_major,int,S_IRUGO);
module_init(mini2440_led_platform_driver_init);
module_exit(mini2440_led_platform_driver_exit);
///////////////////////
Makefiel

ifneq ($(KERNELRELEASE), )
obj-m  := mini2440_led_platform_driver.o
else
KDIR := /home/tools/linux-2.6.32.2
all:
make -C $(KDIR)  M=/linux_prg/platform_driver_device_module/driver_module/  modules  modules
clean:
rm -f *.ko  *.o  *.mod.o  *.mod.c  *.symvers
endif




分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表