历史上的今天
返回首页

历史上的今天

今天是:2024年11月19日(星期二)

正在发生

2019年11月19日 | platform驱动模型编程总结(基于mini2440平台的LED驱动)

2019-11-19 来源:51hei

sysfs与platform的相关基础介绍可以参考博文【 sysfs   platform总线 】。
platform模型驱动编程,需要实现platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定,然后再做为一个普通的字符设备进行相应的应用,总之如果编写的是基于字符设备的platform驱动,在遵循并实现platform总线上驱动与设备的特定接口的情况下,最核心的还是字符设备的核心结构:cdev、 file_operations(他包含的操作函数接口)、dev_t(设备号)、设备文件(/dev)等,因为用platform机制编写的字符驱动,它的本质是字符驱动。

在一般情况下,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总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。




接下来讲解如下实现platform驱动与设备的详细过程。

实现platform驱动的详细过程




1:定义驱动实例 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:实现驱动实例成员函数:probe  mini2440_led_probe
probe函数中要实现的功能包括:设备号的申请,字符设备内核对象cdev的定义、初始化、绑定file_operations,注册字符设备内核对象cdev,在/dev下动态创建设备文件。
3:platform模型驱动写字符设备仍要实现字符设备的核心结构file_operations,第一步的cdev要用到
将file_operations结构体中open、write、read等要用到的接口函数实现。
4:实现驱动实例成员函数:remove mini2440_led_remove
remove函数中要实现的功能与probe中的相反,进行相关设备与资源的注销。
【probe与remove函数其实对应前面用非platform机制写驱动的时候所实现的模块加载与卸载函数,而在platform机制中,模块的加载与卸载函数调用的是 设备或驱动的注册函数】
5:实现驱动的加载与卸载函数:
在加载函数中,调用驱动的注册函数,platform_driver_register(...);
在卸载函数中,调用驱动的卸载函数,platform_driver_unregister(...);

实现platform设备的详细过程


platform设备的实现有两种方法:
     1:最笨的一种:直接在内核源代码里面添加相关的资源代码,archarmmach-s3c2440mach-mini2440.c
     2:编写设备模块,用insmod命令加载该设备模块到platform总线上。
当然用第二种了,不过这两种方法的原理与要写的代码都是一样的,但是第一种不用自己注册,因为系统初始化的时候会将mach-mini2440.c中struct platform_device *mini2440_devices[] __initdata 设备数组中包含的所有设备注册到总线上。而第二种手动注册,一个字活。
1:定义设备与资源实例
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:实现设备的成员函数release  
static void mini2440_led_platform_device_release(struct device * dev)
{
    return ;
}
3:实现设备的加载与卸载函数:
在加载函数中,调用设备的注册函数,platform_device_register(...);
在卸载函数中,调用设备的卸载函数,platform_device_unregister(...);

多个设备的同时注册:platform_add_devices(struct platform_devices **devs,int num);
struct platform_devices **devs设备数组,num包含的设备数目

驱动与设备是否成功注册,我们都可以在/sys/bus/platform/devices(drivers)/下查看

下面是代码清单:
//////////////////////////////
设备模块:
#include
#include
#include
#include
#include
#include
#include
#include

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);
//////////////////////////////
驱动模块:
#include
#include
#include
#include
#include
#include
#include
#include

#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 =%dn",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 initn");
return platform_driver_register(&mini2440_led_platform_driver);
}

static void __exit mini2440_led_platform_driver_exit(void)
{
    printk("platform_driver_for_mini2440_led exitn");
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

推荐阅读

史海拾趣

GHI Electronics公司的发展小趣事

为了满足不同客户的多样化需求,GHI Electronics不断拓展其产品线。除了传统的电子元件和模块外,公司还开始涉足智能家居、物联网等新兴领域。通过不断引入新技术、新产品,GHI Electronics成功实现了从单一产品向多元化产品线的转变。这种转变不仅丰富了公司的产品线结构,也提高了公司的综合竞争力。

Goodwork Semiconductor ( GW )公司的发展小趣事
由于可控硅的导通和截止过程非常迅速,能量损耗较小,因此整体效率较高。
Eastman Kodak Company公司的发展小趣事

经过破产保护和重组的洗礼后,伊士曼柯达公司更加注重创新和可持续发展。公司加强了与新兴科技公司的合作,积极探索新的业务模式和市场机会。同时,柯达还致力于研发更加环保、高效的影像产品和技术,以满足客户对高品质影像的需求。未来,伊士曼柯达公司将继续秉承创新、品质、服务的理念,不断推动影像行业的发展和进步。

Cypress(赛普拉斯)公司的发展小趣事

在发展历程中,Cypress经历了多次并购和业务整合。这些并购不仅扩大了公司的规模和业务范围,也带来了更多的技术和人才资源。通过并购和整合,Cypress能够更好地满足客户的需求,提供更加全面的解决方案。同时,公司还加强了内部管理和资源整合,提高了整体运营效率。

Dawn Electronics Inc公司的发展小趣事

随着市场的不断发展,Dawn Electronics Inc公司意识到技术创新的重要性。公司加大了研发投入,成立了专门的研发团队,积极探索新的技术方向。经过不懈的努力,公司成功研发出了一种新型的智能传感器,具有更高的精度和更低的功耗,这一创新技术迅速被市场接受,为公司带来了可观的收益。

长园维安(CYGWAYON)公司的发展小趣事

长园维安自成立以来,始终坚持技术创新为核心竞争力。在早期发展阶段,公司投入大量资源研发新型线路保护元器件,如PPTC、CPTC等。这些产品凭借其卓越的性能和稳定性,迅速在通讯、汽车电子等领域获得广泛应用。通过不断创新,长园维安在电子行业树立了技术领先的形象。

问答坊 | AI 解惑

USB控制器芯片按功能分类的特性比较

引   言         通用串行总线USB是以主机为中心的分层星型总线拓扑结构实现与外设的连接,软件比较复杂,对功率要求也比较高,所以在推出后的一段时期内,USB在基于PC的系统中得到了广泛应用,而在嵌入 ...…

查看全部问答>

经过本人近一个月的辛苦努力终于攻克了西门子200四级密码的破解难题,相信这

经过本人近一个月的辛苦努力终于攻克了西门子200四级密码的破解难题,相信这 也是让很多人头痛的问题!经过近百次的实验成功率100%,且不会伤害PLC .对于一 至三级就更容易了!同时还可破解现在世面上的大部分PLC,包括西门子300的解密. 如果有这方面 ...…

查看全部问答>

诚心请教open at 开发问题,请各位大侠进来看看。

     小女子刚接触无线通信,用Q24PL模块,做一些应用程序,手里有一本书,。在open at 的开发环境下编程,以前只会纯粹的C,现在想用VC编,生成目标文件下载到模块里实现,打电话、发短信、GPRS上网,等一些常用功能。   ...…

查看全部问答>

eboot~~~

在eboot里修改了BootloaderMain ();这个函数,然后重新编译Public的内容以及工程后,在串口输出的信息始终不变。 请问要怎么修改才会变?…

查看全部问答>

CAB安装包修改注册表问题

各位大虾,       小弟需要一个将一个.dll文件打包安装到设备,同时修改注册表.我的.inf文件已经写好了,能正确生成.cab文件, 现在就是不知道怎么才能修改注册表,那位兄弟能指点以下?谢了。…

查看全部问答>

自己做板子终于能连上DSP

自己做板子终于能连上DSP了。 先冷静下,再load程序,希望一切顺利。 这的代价是毁了一个DSP加两块板子。 …

查看全部问答>

卷和带封装的区别

有哪位能告诉我卷和带两种封装有什么区别,感激不尽。…

查看全部问答>

OFDM 详述

It contains information on the scheme behind the operation of OFDM modulation and demodulation. It also includes some simulation figures if 4QAM modulation is used. …

查看全部问答>

Buffer和Cache的区别

1. Cache:缓存区,是高速缓存,是位于CPU和主内存之间的容量较小但速度很快的存储器,因为CPU的速度远远高于主内存的速度,CPU从内存中读取数据需等待很长的时间,而  Cache保存着CPU刚用过的数据或循环使用的部分数据,这时从Cache中 ...…

查看全部问答>