platform驱动的简单介绍
前几篇介绍了最简单的LED驱动写法,在linux2.6中提出了platform驱动,具体为什么要这么做后面博客再解释吧。
这里的platform驱动只是一个框架,并没有体现platform驱动的意义,因为在driver中没有使用device定义的资源。后面会再写一个真正的platfom。本篇算是入门练手,体会下吧。
pltform机制本身使用并不复杂,由两部分组成:platform_device和platform_driver。通过platform机制开发底层驱动的大致流程为:定义platform_deive->注册platform_device->定义platform_driver->注册platform_driver
好了,按照要求我们如果想编写一个platform驱动就需要编写两个模块了。一个设备模块dev。一个驱动模块driver。另外再编写一个应用程序来验证下。
platform_device
//my2416PlatformLedDev.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 参考arch/arm/plat-s3c24xx/devs.c */
/*1. 根据芯片手册来获取资源*/
static struct resource led_resource[] = {
[0] = {
.start = S3C2410_GPBCON,//使用2416开发板的GPB1,暂时我只是定义了资源,但是在driver中并没有使用定义的资源(因为第一次写还不熟悉,后面会再写一个led的platform驱动来使用device定义的资源,这样才能发挥platform驱动的真正意义:platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性(这些标准接口是安全的))
.end = S3C2410_GPBDAT,
.flags = IORESOURCE_MEM,
},
//[1] = {
// .start = 5,
// .end = 5,
// .flags = IORESOURCE_IRQ,
//},
};
void led_release(struct device *dev)
{
}
/*1.构建平台设备结构体,将平台资源加入进来*/
struct platform_device led_device = {
.name = "myplatformled", /* 使用名为"myplatformled"的平台驱动 ,加载驱动之后在sys/bus/platform/devices/目录下生成myplatformled设备*/
.id = -1,
.dev = {
.release = led_release,
},
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
};
/*2。把我们的设备资源挂在到虚拟总线的设备连表中去*/
int led_dev_init(void)
{
platform_device_register(&led_device);
return 0;
}
void led_dev_exit(void)
{
platform_device_unregister(&led_device);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
对应的makefile文件为
#Makefile for .c
ARCH=arm
CROSS_COMPILE=arm-linux-
ifneq ($(KERNELRELEASE),)
obj-m := my2416PlatformLedDev.o
else
#bbblack kernel
KERNELDIR ?= /home/zyd/soft/s3c2416/20140409_HELPER2416/Helper2416/source/s3c-linux.jyx
PWD := $(shell pwd)
modules:
make -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
modules_install:
make -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules_install
app: app.c
$(CROSS_COMPILE)gcc -o app app.c
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
endif
playform driver
//my2416PlatformLedDriver.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "myleds"//加载驱动之后会在/dev/目录下发现myleds,应用程序可以使用
#define LED_ON 0 //根据原理图,0点亮led,1熄灭led
#define LED_OFF 1
//定义GPIO管脚
static unsigned long led_table [] =
{
S3C2410_GPB(1), //不能是S3C2410_GPB5; 因为没有这样定义,可以通过#define S3C2410_GPB5 S3C2410_GPB(5)
//S3C2410_GPF(1),
//S3C2410_GPF(2),
//S3C2410_GPF(3),
};
//设置管脚模式
static unsigned int led_cfg_table [] =
{
S3C2410_GPIO_OUTPUT, //随内核版本中定义类型的变化,在arch/arm/mach-sc2410/include/mach/Regs-gpio.h文件中定义
//S3C2410_GPIO_OUTPUT,
//S3C2410_GPIO_OUTPUT,
//S3C2410_GPIO_OUTPUT,
};
static int my2416_leds_ioctl(struct file* filp, unsigned int cmd,unsigned long arg)
{
switch(cmd)
{
case LED_ON:
s3c2410_gpio_setpin(S3C2410_GPB(1), LED_ON);
break;
case LED_OFF:
//s3c2410_gpio_setpin(led_table[arg], !cmd);
s3c2410_gpio_setpin(S3C2410_GPB(1), LED_OFF);
break;
default:
printk("LED control:no cmd\n");
printk("LED control are LED_ON or LED_OFF\n");
return(-EINVAL);
}
return 0;
}
//dev_fops操作指令集
static struct file_operations my2416Led_fops =
{
.owner =THIS_MODULE,
.unlocked_ioctl =my2416_leds_ioctl,//这里必须是unlocked_ioctl而不是ioctl。
};
//第三步:混杂设备定义
static struct miscdevice my2416Ledmisc =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,//加载驱动之后会在/dev/目录下发现myleds,应用程序可以使用
.fops = &my2416Led_fops,
};
/*3。实现probe函数*/
static int led_probe(struct platform_device *dev)
{
int ret;
int i;
////这里只定义了一个io口GPB1
for (i = 0; i < 1; i++)
{
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 0);
}
ret = misc_register(&my2416Ledmisc);
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
int led_remove(struct platform_device *dev)
{
misc_deregister(&my2416Ledmisc);
}
/*1。构建平台驱动结构体,不知道的时候可以看别人的*/
static struct platform_driver led_driver = {
.probe = led_probe, /* 平台总线下增加一个平台设备时,调用枚举函数 */
.remove = led_remove, /* 平台总线下去掉一个平台设备时,调用remove函数 */
.driver = {
.name = "myplatformled", /* 能支持名为"myplatformled"的平台设备 */
.owner = THIS_MODULE,
},
};
/*2。注册,把我们的驱动加入到平台设备驱动连表中去*/
static int led_drv_init(void)
{
int ret;
//网上的例子有 通常采用函数platform_device *platform_device_alloc(const char *name, int id)动态申请,通常name就是需要申请的设备名,而id为-1。然后采用int platform_device_add(struct platform_device *pdev)或者int platform_device_register(struct platform_device *pdev)
//这里只使用了platform_device_register也成功了,具体为什么以后再分析。
注册定义好的设备即可。
ret=platform_driver_register(&led_driver);
return ret;
}
static void __exit led_drv_exit(void)
{
platform_driver_unregister(&led_driver);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
对应的makefile文件如下
#Makefile for .c
ARCH=arm
CROSS_COMPILE=arm-linux-
ifneq ($(KERNELRELEASE),)
obj-m := my2416PlatformLedDriver.o
else
#bbblack kernel
KERNELDIR ?= /home/zyd/soft/s3c2416/20140409_HELPER2416/Helper2416/source/s3c-linux.jyx
PWD := $(shell pwd)
modules:
make -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
modules_install:
make -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules_install
app: app.c
$(CROSS_COMPILE)gcc -o app app.c
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
endif
测试应用程序
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "/dev/myleds"
#define LED_ON 0
#define LED_OFF 1
int main()
{
int fd;
int ret;
char *i;
char count=0;
printf("My2416 led dev test!\n");
fd=open(DEVICE_NAME,O_RDWR);
printf("fd=%d\n",fd);
if(fd==-1)
{
printf("open device %s error\n",DEVICE_NAME);
}
else
{
for(count=0;count<10;count++)
{
ioctl(fd,LED_OFF);
sleep(1);
ioctl(fd,LED_ON);
sleep(1);
}
ret=close(fd);
printf("ret =%d\n",ret);
printf("Close ledapp succeed!\n");
}
return 0;
}
驱动加载的说明
首先加载设备,这里编译出来的是my2416PlatformLedDev.ko,加载之后在/sys/bus/platform/device目录下生成myplatformled。也就是和.name = “myplatformled”名称一致。
然后加载驱动,这里编译出来是my2416PlatformLedDriver.ko,加载之后在/sys/bus/platform/driver目录下生成myplatformled。也就是和.name = “myplatformled”名称一致。同时在/dev目录下生成myleds。应用程序就是使用的/dev/myleds。来操作设备的。
这里只是一个简单的platform驱动,只是在device中定义了资源,但是再driver中并没有使用资源(没有调用platform_get_resource函数。)因此这里并没有体现出platform的意义。后面会修改程序,做一个真正的platform驱动来。
下面几篇先介绍一下platform的基本知识,为写一个真正的platform驱动作个理论基础吧。
关键字:2416 platform 驱动之LED『本文转载自网络,版权归原作者所有,如有侵权请联系删除』