linux设备驱动模型之平台总线实践环节(一)

1、首先回顾下之前写的驱动和数据在一起的led驱动代码,代码如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <asm/io.h>	//ioremap和iounmap的头文件	writel等
/**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表**********************************/
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#define 	GPJ0CON		S5PV210_GPJ0CON	//这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,
										//以此类推。
#define 	GPJ0DAT		S5PV210_GPJ0DAT

static struct led_classdev myled1;
static struct led_classdev myled2;
static struct led_classdev myled3;

//这个函数绑定到了struct led_classdev这个结构体类型的myled变量中的brightness_set成员,当我们用户写这个led硬件的时候,因为是用led驱动框架
//写的led驱动,所以我们是通过led驱动框架中的brightness这个文件,去写或这个读led硬件的,应为这个文件在驱动框架中具有了可读可写的属性,是在
//struct //device_attribute结构中拥有的,并且最终绑定了到了这个设备类中。所以最后我们用户去通过brightness这个属性文件去写led硬件的时候,会去执行led_br//ightness_store这个方法,这个方法中,又
//通过调用led_set_brightness这个函数,这个函数中通过led_cdev->brightness_set(led_cdev, value);这条语句,最终对应到了struct //led_classdev结构体中的brightness_set这个函数指针变量,而这个函数指针变量在我们填充那个结构体
//struct led_classdev中的brightness_set函数指针时,就已经绑定好了我们写的那个写操作硬件的函数了。所以这一条线就打通了.value就是用户要写的值
//value是枚举,是enum led_brightness类型的枚举,这个枚举有三个值,分别是LED_OFF = 0  LED_XXX = 122  LED_FULL = 255,

static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value)
{
	//printk(KERN_INFO "whyx210_led_set\n");

	writel(0x11111111, GPJ0CON);

	if (value == LED_OFF) {	//用户输入0时灭		对应用户输入的是echo 0 > brightness

		writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT);

	}else if (value == LED_FULL){	//用户输入255时亮		对应用户输入的是echo 255 > brightness

		writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT);	//这样操作保持其他位值不变

	}
}

static void whyx210_led2_set(struct led_classdev *led_cdev, enum led_brightness value)
{
	//printk(KERN_INFO "whyx210_led_set\n");

	writel(0x11111111, GPJ0CON);

	if (value == LED_OFF) {	//用户输入0时灭		对应用户输入的是echo 0 > brightness

		writel(readl(GPJ0DAT) | (1 << 4), GPJ0DAT);

	}else if (value == LED_FULL){	//用户输入255时亮		对应用户输入的是echo 255 > brightness

		writel(readl(GPJ0DAT) & ~(1 << 4), GPJ0DAT);	//这样操作保持其他位值不变

	}
}

static void whyx210_led3_set(struct led_classdev *led_cdev, enum led_brightness value)
{
	//printk(KERN_INFO "whyx210_led_set\n");

	writel(0x11111111, GPJ0CON);

	if (value == LED_OFF) {	//用户输入0时灭		对应用户输入的是echo 0 > brightness

		writel(readl(GPJ0DAT) | (1 << 5), GPJ0DAT);

	}else if (value == LED_FULL){	//用户输入255时亮		对应用户输入的是echo 255 > brightness

		writel(readl(GPJ0DAT) & ~(1 << 5), GPJ0DAT);	//这样操作保持其他位值不变

	}
}

static int __init whyx210_led_init(void)
{
	int ret = -1;
	//led1
	//填充我们要注册的struct led_classdev类型的结构体
	myled1.name = "led1";		//将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
	myled1.brightness = 255;
	myled1.brightness_set = whyx210_led1_set;

	//去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
	//在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
	ret = led_classdev_register(NULL, &myled1);

	if (ret < 0) {

		printk(KERN_ERR "led_classdev_register errro\n");
		return ret;
	}
	//printk(KERN_INFO "led_classdev_register success %s\n", myled.name);
/*********************************************************************************************/
	//led2
	//填充我们要注册的struct led_classdev类型的结构体
	myled2.name = "led2";		//将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
	myled2.brightness = 255;
	myled2.brightness_set = whyx210_led2_set;

	//去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
	//在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
	ret = led_classdev_register(NULL, &myled2);

	if (ret < 0) {

		printk(KERN_ERR "led_classdev_register errro\n");
		return ret;
	}
	//printk(KERN_INFO "led_classdev_register success %s\n", myled.name);
/*********************************************************************************************/
	//led3
	//填充我们要注册的struct led_classdev类型的结构体
	myled3.name = "led3";		//将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
	myled3.brightness = 255;
	myled3.brightness_set = whyx210_led3_set;

	//去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
	//在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
	ret = led_classdev_register(NULL, &myled3);

	if (ret < 0) {

		printk(KERN_ERR "led_classdev_register errro\n");
		return ret;
	}
	//printk(KERN_INFO "led_classdev_register success %s\n", myled.name);

	return 0;
}

static void __init whyx210_led_exit(void)
{
	led_classdev_unregister(&myled1);
	led_classdev_unregister(&myled2);
	led_classdev_unregister(&myled3);
	//printk(KERN_INFO "led_classdev_unregister success.\n");
}

module_init(whyx210_led_init);
module_exit(whyx210_led_exit);

MODULE_AUTHOR("why <[email protected]>");
MODULE_DESCRIPTION("whyx210 LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("whyx210_led");

现在先将led2和led3的代码去掉,只留下led1,并且我们要知道我们怎么去用platform平台总线的方法去实现led的驱动,我们要有probe函数,和remove函数,分别对应的是驱动和设备匹配上后执行的probe函数,以及设备和驱动分离时的remove函数也就是卸载驱动的函数,我们要将数据部分的代码写入到platform的platform_data中,在驱动代码的probe函数中,用参数的方式,将设备的数据部分得到,实现驱动和设备数据的代码的分离逻辑框架。

上面的代码只留下led1的驱动代码后,代码如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <asm/io.h>	//ioremap和iounmap的头文件	writel等
/**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表**********************************/
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#define 	GPJ0CON		S5PV210_GPJ0CON	//这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,
										//以此类推。
#define 	GPJ0DAT		S5PV210_GPJ0DAT

static struct led_classdev myled1;

//这个函数绑定到了struct led_classdev这个结构体类型的myled变量中的brightness_set成员,当我们用户写这个led硬件的时候,因为是用led驱动框架
//写的led驱动,所以我们是通过led驱动框架中的brightness这个文件,去写或这个读led硬件的,应为这个文件在驱动框架中具有了可读可写的属性,是在
//struct //device_attribute结构中拥有的,并且最终绑定了到了这个设备类中。所以最后我们用户去通过brightness这个属性文件去写led硬件的时候,会去执行led_br//ightness_store这个方法,这个方法中,又
//通过调用led_set_brightness这个函数,这个函数中通过led_cdev->brightness_set(led_cdev, value);这条语句,最终对应到了struct //led_classdev结构体中的brightness_set这个函数指针变量,而这个函数指针变量在我们填充那个结构体
//struct led_classdev中的brightness_set函数指针时,就已经绑定好了我们写的那个写操作硬件的函数了。所以这一条线就打通了.value就是用户要写的值
//value是枚举,是enum led_brightness类型的枚举,这个枚举有三个值,分别是LED_OFF = 0  LED_XXX = 122  LED_FULL = 255,

static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value)
{
	//printk(KERN_INFO "whyx210_led_set\n");

	writel(0x11111111, GPJ0CON);

	if (value == LED_OFF) {	//用户输入0时灭		对应用户输入的是echo 0 > brightness

		writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT);

	}else if (value == LED_FULL){	//用户输入255时亮		对应用户输入的是echo 255 > brightness

		writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT);	//这样操作保持其他位值不变

	}
}

static int __init whyx210_led_init(void)
{
	int ret = -1;
	//led1
	//填充我们要注册的struct led_classdev类型的结构体
	myled1.name = "led1";		//将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
	myled1.brightness = 255;
	myled1.brightness_set = whyx210_led1_set;

	//去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
	//在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
	ret = led_classdev_register(NULL, &myled1);

	if (ret < 0) {

		printk(KERN_ERR "led_classdev_register errro\n");
		return ret;
	}
	//printk(KERN_INFO "led_classdev_register success %s\n", myled.name);

	return 0;
}

static void __init whyx210_led_exit(void)
{
	led_classdev_unregister(&myled1);
}

module_init(whyx210_led_init);
module_exit(whyx210_led_exit);

MODULE_AUTHOR("why <[email protected]>");
MODULE_DESCRIPTION("whyx210 LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("whyx210_led");

makefile的代码如下:

#ubuntu中的内核源码树目录
#KERN_VER = $(shell uname -r)
#KERN_DIR = /lib/modules/$(KERN_VER)/build

#我编译的九鼎内核的源码树目录
KERN_DIR = /root/xin_x210/kernel

obj-m	+= leds-why210.o

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	#arm-linux-gcc app.c -o app

cp:
	cp *.ko /root/rootfs/rootfs/driver_test/ -f
	#cp app /root/rootfs/rootfs/driver_test/ -f
.PHONY: clean
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
#	rm app -f 
	

makefile的代码不用动

之后我们在led的驱动代码中,修改并添加probe函数和led驱动需要的结构体。加入probe函数和remove函数,并且加入并填充led驱动需要的结构体,并将init函数也就是ismod加载驱动模块时执行的函数,和exit函数也就是卸载驱动模块时执行的函数,将这两个函数中的内容分别改为用platform提供的注册函数进行注册驱动(led驱动的结构体)和用platform提供的注销驱动(led驱动结构体)的方法。改动后的代码如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <asm/io.h>	//ioremap和iounmap的头文件	writel等
/**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表**********************************/
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/platform_device.h>

#define 	GPJ0CON		S5PV210_GPJ0CON	//这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,
										//以此类推。
#define 	GPJ0DAT		S5PV210_GPJ0DAT

static struct led_classdev myled1;

static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value)
{
	//printk(KERN_INFO "whyx210_led_set\n");

	writel(0x11111111, GPJ0CON);

	if (value == LED_OFF) {	//用户输入0时灭		对应用户输入的是echo 0 > brightness

		writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT);

	}else if (value == LED_FULL){	//用户输入255时亮		对应用户输入的是echo 255 > brightness

		writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT);	//这样操作保持其他位值不变

	}
}

static int why_led_remove(struct platform_device *dev)
{
	led_classdev_unregister(&myled1);

	return 0;
}

static int why_led_probe(struct platform_device *dev)
{
		int ret = -1;
	//led1
	//填充我们要注册的struct led_classdev类型的结构体
	myled1.name = "led1";		//将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
	myled1.brightness = 255;
	myled1.brightness_set = whyx210_led1_set;

	//去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
	//在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
	ret = led_classdev_register(NULL, &myled1);

	if (ret < 0) {

		printk(KERN_ERR "led_classdev_register errro\n");
		return ret;
	}

	return 0;
}

static struct platform_driver why_led_driver = {
	.probe		= why_led_probe,
	.remove		= why_led_remove,
	.driver		= {
		.name		= "why_led",
		.owner		= THIS_MODULE,
	},
};

static int __init whyx210_led_init(void)
{
	return platform_driver_register(&why_led_driver);
}

static void __init whyx210_led_exit(void)
{
	platform_driver_unregister(&why_led_driver);
}

module_init(whyx210_led_init);
module_exit(whyx210_led_exit);

MODULE_AUTHOR("why <[email protected]>");
MODULE_DESCRIPTION("whyx210 LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("whyx210_led");

分析,此时probe和remove函数的内容还不是正确的,用makefile编译能编译通过,生成一个leds-why210.ko驱动模块,我们将这个驱动模块拷贝到根文件系统中,执行insmod的时候,对应的whyx210_led_init函数会被执行,会将led的驱动结构体数据结构注册到内核中,此时我们在根文件系统的/sys/bus/platform/driver/目录下面会看到有一个why_led的文件,说明我们的驱动注册成功了,但是是用不了的,因为我们没有提供platform总线下需要的platform_device,probe函数和remove也是不对的,如果正确的话,我们提供platform_device后,将其进行注册后,当有led设备的时候,驱动也被加载的时候,那么match函数会将led的驱动和设备进行匹配,驱动的probe函数会得以执行,led的数据部分是在platform_device中的,在驱动的probe函数中将platform_device中的数据部分得到,最后在probe函数中会进行一些注册,这个时候,在根文件系统的/sys/bus/platform/driver/why_led目录下就会有其他东西,这个时候我们的led驱动就是可以使用的了。

时间: 2024-08-03 13:31:53

linux设备驱动模型之平台总线实践环节(一)的相关文章

linux设备驱动模型之平台总线实践环节(四)

//通过led_classdev类型的指针得到s5pv210_led_pladata类型的指针,这个s5pv210_led_pladata类型结构体是我们自己定义的设备数据部分. static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev) { return container_of(led_cdev, struct s5pv210_gpio_led, cdev); } #define X210_LED

linux设备驱动之platform平台总线工作原理(二)

5.5.5.platform平台总线工作原理2 5.5.5.1.平台总线体系的工作流程 (1)第一步:linux内核系统启动时在bus系统中注册platform. 1.什么叫做bus系统,操作系统中有一套管理总线的体系,内核里有一个子系统,就叫做总线子系统.就是内核来管理总线的.bus系统在内核启动时建立起来,比platform建立的时间还要早,bus系统的是由内核编写的人提供的,我们将来分析代码的时候不需要去分析他.在bus系统起来以后,就需要在bus系统中注册这个platform平台总线的b

linux设备驱动之platform平台总线工作原理(三)

设备为数据,驱动为加工着 1.以led-s3c24xx.c为例来分析platform设备和驱动的注册过程 其中关于led的驱动数据结构为: static struct platform_driver s3c24xx_led_driver = { .probe = s3c24xx_led_probe, .remove = s3c24xx_led_remove, .driver = { .name = "s3c24xx_led", .owner = THIS_MODULE, }, }; s

20150225 IMX257 总线设备驱动模型编程之总线篇

20150225 IMX257 总线设备驱动模型编程之总线篇 2015-02-25 19:40 李海沿 从现在开始,我们开始来实现 总线-设备-驱动模型中的总线.. 我们这个程序的目标是在 sysfs文件系统的/sys/bus/ 目录下面建立一个文件夹. 一.总线介绍 1. 总线数据结构bus_type struct bus_type 结构体的定义如下: struct bus_type { const char *name; --总线名 struct bus_attribute *bus_att

Linux 设备驱动模型

Linux系统将设备和驱动归一到设备驱动模型中了来管理 设备驱动程序功能: 1,对硬件设备初始化和释放 2,对设备进行管理,包括实参设置,以及提供对设备的统一操作接口 3,读取应用程序传递给设备文件的数据或回送应用程序请求的数据 4,检测或处理设备出现的错误 设备驱动模型提供了硬件的抽象包括: 1,电源管理 其实,电源管理就是一些设备不工作的时候,让它歇一会,休眠一会(最低消耗),达到省电的目的 它的一个重要的功能是: 省电模式下,使系统中的设备以一定的先后顺序挂起 在全速工作模式下,使系统的设

linux设备驱动之平台总线实践环节(二)

1.上一节中,我们将初步的驱动代码写完后编译后,放入到rootfs中进行insmod时,在/sys/bus/platform/drvier/目录中能够看到why_led这个目录,但是进入后只有一些基本的东西,却没有能使用这个led驱动的关键性东西,那是因为我们没有提供platform_device,并且驱动代码中的probe函数remove函数的代码内容也不对.这一节课中,做另一半,就是platform_device这一部分. 2.做platform_device这一半 参考mach-mini2

Linux设备驱动模型--分离分层思想

一.设备驱动的分层思想:以platform设备驱动.input设备驱动为例看看他们在分层思想上的体现 [1]在核心层:一类设备总线通用的功能特性,如果某一设备支持的总线比较特别可以不使用这些功能特性,在自己的设备总线结构体中 进行重载. 例(1)platform核心层:定义在drivers/base/platform.c文件 Linux内核对设备总线先进行了一次全局的抽象,即概括了所有设备驱动总线具有的功能特性: struct bus_type { //具备总线名.总线属性.总线上设备属性.总线

Linux设备驱动模型【转】

本文转载自:http://blog.csdn.net/xiahouzuoxin/article/details/8943863 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要. linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同

linux设备驱动模型

1.总线概念 总线是内核与一个或者多个设备之间的通道.在设备模型中,所有的设备都是通过总线连接 2.sysfs 作用: 一个用于内核对象表述的文件系统 sysfs是一个基于ramfs的内存文件系统. 描述内核数据结构,属性以及与它们用户空间的链接关系 sys文件夹内含的顶层文件夹(使用ls -l /sys可以查看) block/ bus/ :包含内核中所有总线类型的描述. 每一个总线文件夹下面都有下面两类文件夹 devices/ :内核中所有注册的设备,内含的设备都是指向sys目录下面devic