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_OFF 1
#define X210_LED_ON 0

//从platform_device的指针得到s5pv210_gpio_led的指针
static inline struct s5pv210_gpio_led *pdev_to_gpio(struct platform_device *dev)
{
	return platform_get_drvdata(dev);
}

//定义一个大的结构体框架,包含led_classdev和设备的数据部分的结构体
struct s5pv210_gpio_led {
	struct led_classdev		 cdev;
	struct s5pv210_led_platdata	*pdata;
};

//为了让这个操作led的驱动方法具有一般性,所以函数可以通过参数struct led_classdev类型的指针反推得到s5pv210_gpio_led的指针
//从而通过这个指针知道s5pv210_led_platdata这个成员,这个类型的成员是我们自己定义的设备的数据部分。
static void whyx210_led_set(struct led_classdev *led_cdev, enum led_brightness value)
{
    printk(KERN_INFO "whyx210_led_set\n");
     
    struct s5pv210_gpio_led *p = to_gpio(led_cdev);    //从led_classdev指针反推得到s5pv210_gpio_led的指针
     
    if (value == LED_OFF) { //用户输入0时灭       对应用户输入的是echo 0 > brightness
     
        //writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT);
        gpio_set_value(p->pdata->gpio, X210_LED_OFF);    //通过s5pv210_gpio_led中的pdata来知道gpio号是多少,pdata是设备数据部分,我们写
                                                            //platform_device时已经填充好
         
    }else if (value == LED_FULL){   //用户输入255时亮     对应用户输入的是echo 255 > brightness
     
         
        //writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT); //这样操作保持其他位值不变
        gpio_set_value(p->pdata->gpio, X210_LED_ON);
    }
}

static int why_led_probe(struct platform_device *dev)
{
    int ret = -1;
    struct s5pv210_led_platdata *pdata = dev->platform_data;    //得到匹配上的设备的数据部分
    struct s5pv210_gpio_led *led; //上一章博客中说道brightness绑定的函数不能写死,所以使用这种设计逻辑,这个结构体中包含了led_classdev结构体
                                    //同时也包含了我们定义的设备的数据部分的结构体
    
    led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);
    if (led == NULL) {
            dev_err(&dev->dev, "No memory for device\n");
	    return -ENOMEM;
	}

    platform_set_drvdata(dev, led);//这个函数的功能是将led这个含有led_classdev和设备数据部分的结构体变量的指针让其driver中的数据部分的指针指
                                    //向,就是将led结构体被driver中能指向任意类型的指针指向,将led这个代表设备数据部分的东西传递给驱动。
    
    //填充我们要注册的struct led_classdev类型的结构体
    //myled.name = pdata->name; //当前匹配上的led设备的名字
    //myled.brightness = 255;
    //myled.brightness_set = whyx210_led1_set;//这个myled的brightness_set方法绑定的其实就不能是固定的whyx210_led1_set这个方法了,因为我们
                                                //的驱动和设备的匹配不是固定是led1的,有可能led2的设备也和这个驱动匹配上了,总不能led2的
                                                //操作方法使用的还是led1的方法吧
     
     //因为led中包含了led_classdev,所以上面的代码改变如下,用这样的方法将设备的数据填充到led中,同时因为led已经在上面被drvier成员中的指针
     //指向,所以等于为driver填充数据。
     led->cdev.name = pdata->name;    //led中的成员cdev就是上面的myled
     led->cdev.brightness = 255;
     led->cdev.brightness_set = whyx210_led_set;    //这个set函数就不能写死了,要根据实际设备硬件数据的变化来去进行操作led灯的亮灭。所以
                                                     //从新写这个函数,名字改为why210_led_set,用一个框架更大的思路去操作led,通过实时获取设
                                                       //备硬件数据的方法,来操作led,这样这个操作led的驱动方法就具有一般性,以后只需要改动设
                                                        //备的数据,就可以达到控制不同的led。
     
    //去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
    //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
    
    ret = led_classdev_register(&dev->dev, &led->cdev);
    
    led->pdata = pdata;    //将从设备中得到数据部分,填充到这个led结构体中的pdata成员,led因为前面已经被driver成员中的指针指向,所以等于将
                           //数据传递到驱动中
     
    if (ret < 0) {
         
        printk(KERN_ERR "led_classdev_register errro\n");
        return ret;
    }
     
    return 0;
}

static int why_led_remove(struct platform_device *dev)
{
	struct s5pv210_gpio_led *led = pdev_to_gpio(dev);    //从platform_device类型的指针反推得到s5pv210_gpio_led的指针

	led_classdev_unregister(&led->cdev);    //注销这个设备
	kfree(led);

	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"

将具有一般性的驱动代码实现完毕后,进行make编译,加载到系统中后,设备和驱动是可以匹配上的,内核代码中写了几个led的设备,那么驱动就会匹配上几个led设备,echo 0或1 到/sys/class/leds/ledx/brightness文件也能控制led的亮灭,证明驱动是工作了。

想要在class目录下的leds目录中出现led设备文件并使用,就需要利用led_classdev_register函数将led设备和驱动数据结构体进行注册。

想要实时获取设备的数据部分传递给驱动,并且能给让驱动操作led的操作方法具有一般性,就需要使用上面的s5pv210_gpio_led这个结构体创造框架进行实现。

这个代码是通过三星内核中的/driver/leds/leds-s3c24xx.c代码进行仿造来完成的,几乎是一样的.

代码中有一些细节部分,没有做处理,只为框架思路上作下记录,使自己对platform总线理解的能够更深入些。

时间: 2024-12-22 02:21:42

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

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

1.首先回顾下之前写的驱动和数据在一起的led驱动代码,代码如下: #include <linux/module.h> #include <linux/init.h> #include <linux/leds.h> #include <asm/io.h> //ioremap和iounmap的头文件 writel等 /**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表************

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