首先需要知道的是,设备跟驱动是分开的。设备通过struct device来定义,也可以自己将结构体封装到自己定义的device结构体中:

例如:struct platform_device:

 在include/linux/platform_device.h文件中:          //这个位置还是个盲点,我一直没找到这个位置在哪里
struct platform_device {
const char * name
u32 id
struct device dev
u32 num_resources
struct resource * resource
}

platform_device通过向内核注册struct device dev这个结构体来告诉内核加载这个设备,方法就是  device_register(&platform_device->dev)。

驱动通过struct driver这个结构体来定义,与struct device一致,你也可以用自己的结构体去封装:例如,struct platform_driver。

 include/linux/platform_device.h文件中:
struct platform_driver {
int (*probe)(struct platform_device *)
int (*remove)(struct platform_device *)
void (*shutdown)(struct platform_device *)
int (*suspend)(struct platform_device *, pm_message_t state)
int (*suspend_late)(struct platform_device *, pm_message_t state)
int (*resume_early)(struct platform_device *)
int (*resume)(struct platform_device *)
struct device_driver driver
}

Platform设备驱动匹配基本流程:Platform 设备先被注册然后platfrom驱动加载时会调用驱动程序中的probe()入口函数,扫描系统中已注册的设备,通过。Name域找到匹配设备后将驱动和设备绑定。一个驱动可以对应多个设备,但是一个设备只对一个驱动。

驱动和设备的结构体成员有差别,但是用来匹配识别的name域必须是相同的。只有这样才能实现互相匹配,实现驱动找设备,设备找驱动。(系统为platform总线定义了一个bus_type(总线类型)的实例platform_bus_type,在此结构体中有一个成员函数:.match,系统通过这个函数完成相关匹配)。

在接下来的代码中我们定义的platform_device与plateform_driver分别为:

 static struct platform_device s3c_led_device = {
.name = "s3c_led",
.id = ,
.dev =
{
.platform_data = &s3c_led_data,
.release = platform_led_release,
},
}; static struct platform_driver s3c_led_driver = {
.probe = s3c_led_probe,
.remove = s3c_led_remove,
.driver = {
.name = "s3c_led",
.owner = THIS_MODULE,
},
};

platform_device由platform_device_register注册函数调用:

platform_device结构体中定义了name域id以及设备dev信息的函数指针;dev指针指向包含platform_data的指针与release的函数,platform_data指向设备存放的数据,数据存放在结构体类型为s3c_led_platform_data的结构体变量s3c_led_data中;s3c_led_data包含leds的指针与nleds的整型变量,nleds中保存的是s3c_led_info的结构体数组。

platform_driver由platform_driver_register注册函数调用:

platform_driver结构体中定义了指向s3c_led_probe的probe指针与remove指针以及driver指针,通过s3c_led_probe驱动找到platform_device设备,然后初始化led设备,更改led灯gpio口的状态以及通过判断主设备号静态或动态获取设备号,初始化cdev结构体并且将fops结构体与之绑定,最后字符设备驱动注册。

其次在led生成设备节点的时候可以利用mknod手动创建,也可以利用Linux内核为我们提供的一组函数来在内核加载的时候自动在/dev目录下创建相应的设备节点。

device_creat这个函数可以为我们自动创建设备节点。这是利用在驱动中加入了对mdev的支持所完成的。而要支持mdev:在驱动初始化的代码里调用class_create()为该设备创建一个class,再为每个设备调用device_create()创建对应的设备( 在2.6较早的内核中用class_device_create)。内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的mdev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

接下来看代码:

 #include "s3c_driver.h"

 #define DRV_DESC                  "S3C24XX LED driver"

 /* Driver version*/
#define DRV_MAJOR_VER 1
#define DRV_MINOR_VER 0
#define DRV_REVER_VER 0 #define DEV_NAME DEV_LED_NAME //#define DEV_MAJOR DEV_LED_MAJOR
#ifndef DEV_MAJOR
#define DEV_MAJOR 0 /* dynamic major by default */
#endif #define TIMER_TIMEOUT 40 static int debug = DISABLE;
static int dev_major = DEV_MAJOR;
static int dev_minor = ; /* ============================ Platform Device part ===============================*/
/* LED hardware informtation structure*/
struct s3c_led_info //定义led结构体信息
{
unsigned char num; /* The LED number */
unsigned int gpio; /* Which GPIO the LED used */
unsigned char active_level; /* The GPIO pin level(HIGHLEVEL or LOWLEVEL) to turn on or off */
unsigned char status; /* Current LED status: OFF/ON */
unsigned char blink; /* Blink or not */
}; /* The LED platform device private data structure */
struct s3c_led_platform_data
{
struct s3c_led_info *leds;
int nleds;
}; /* LED hardware informtation data*/
static struct s3c_led_info s3c_leds[] = { //定义各个灯的信息
[] = {
.num = ,
.gpio = S3C2410_GPB(),
.active_level = LOWLEVEL,
.status = OFF,
.blink = ENABLE,
},
[] = {
.num = ,
.gpio = S3C2410_GPB(),
.active_level = LOWLEVEL,
.status = OFF,
.blink = DISABLE,
},
[] = {
.num = ,
.gpio = S3C2410_GPB(),
.active_level = LOWLEVEL,
.status = OFF,
.blink = DISABLE,
},
[] = {
.num = ,
.gpio = S3C2410_GPB(),
.active_level = LOWLEVEL,
.status = OFF,
.blink = DISABLE,
},
}; /* The LED platform device private data */
static struct s3c_led_platform_data s3c_led_data = { //定义led灯的结构体信息
.leds = s3c_leds, //每个灯的信息
.nleds = ARRAY_SIZE(s3c_leds), //灯的数量
}; struct led_device //定义一个led_device的结构体
{
struct s3c_led_platform_data *data;
struct cdev cdev;
struct class *dev_class;
struct timer_list blink_timer;
} led_device; static void platform_led_release(struct device * dev) //将所有灯关掉
{
int i;
struct s3c_led_platform_data *pdata = dev->platform_data; //pdata指向led灯的结构体 dbg_print("%s():%d\n", __FUNCTION__,__LINE__); //调试打印 /* Turn all LED off */
for(i=; i<pdata->nleds; i++)
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
}
} static struct platform_device s3c_led_device = { //设备节点结构体
.name = "s3c_led",
.id = ,
.dev =
{
.platform_data = &s3c_led_data,
.release = platform_led_release,
},
}; /* ===================== led device driver part ===========================*/ void led_timer_handler(unsigned long data) //实现led的亮灭
{
int i;
struct s3c_led_platform_data *pdata = (struct s3c_led_platform_data *)data; for(i=; i<pdata->nleds; i++)
{
if(ON == pdata->leds[i].status) //如果要求是ON则转换成ON
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);
}
else //如果要求是OFF则转换成OFF
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
} if(ENABLE == pdata->leds[i].blink ) /* LED should blink */ //如果要求是闪,则让它闪
{
/* Switch status between 0 and 1 to turn LED ON or off */
pdata->leds[i].status = pdata->leds[i].status ^ 0x01;
}
//更新计时器,jiffies是个全局变量,cpu每来一个时钟脉冲就自加,一个jiffes=10ms,利用这种方法就不需要单独的一个进程
mod_timer(&(led_device.blink_timer), jiffies + TIMER_TIMEOUT);
}
} static int led_open(struct inode *inode, struct file *file) //连接自static struct file_operations led_fops的结构体
{
struct led_device *pdev ;
struct s3c_led_platform_data *pdata; pdev = container_of(inode->i_cdev,struct led_device, cdev);
//container_of是通过一个结构变体量中一个成员的地址找到这个结构体变量的首地址。
pdata = pdev->data; file->private_data = pdata;
//这里为什么不直接用file->private_data = pdev->data; return ;
} static int led_release(struct inode *inode, struct file *file)
{
return ;
} static void print_led_help(void) //定义help结构体
{
printk("Follow is the ioctl() command for LED driver:\n");
printk("Enable Driver debug command: %u\n", SET_DRV_DEBUG);
printk("Get Driver verion command : %u\n", GET_DRV_VER);
printk("Turn LED on command : %u\n", LED_ON);
printk("Turn LED off command : %u\n", LED_OFF);
printk("Turn LED blink command : %u\n", LED_BLINK);
} /* compatible with kernel version >=2.6.38*/
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct s3c_led_platform_data *pdata = file->private_data; switch (cmd)
{
case SET_DRV_DEBUG:
dbg_print("%s driver debug now.\n", DISABLE == arg ? "Disable" : "Enable");
debug = (==arg) ? DISABLE : ENABLE;
break;
case GET_DRV_VER:
print_version(DRV_VERSION);
return DRV_VERSION; case LED_OFF:
if(pdata->nleds <= arg)
{
printk("LED%ld doesn't exist\n", arg);
return -ENOTTY;
}
pdata->leds[arg].status = OFF;
pdata->leds[arg].blink = DISABLE;
break; case LED_ON:
if(pdata->nleds <= arg)
{
printk("LED%ld doesn't exist\n", arg);
return -ENOTTY;
}
pdata->leds[arg].status = ON;
pdata->leds[arg].blink = DISABLE;
break; case LED_BLINK:
if(pdata->nleds <= arg)
{
printk("LED%ld doesn't exist\n", arg);
return -ENOTTY;
}
pdata->leds[arg].blink = ENABLE;
pdata->leds[arg].status = ON;
break; default:
dbg_print("%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);
print_led_help();
return -EINVAL; }
return ;
} static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl, /* compatible with kernel version >=2.6.38*/
}; static int s3c_led_probe(struct platform_device *dev)
/*由内核将platform_device的结构体传给probe函数,当设备找到驱动或者驱动找到设备的时候会调probe函数。
一旦找到,内核就会通过platform_device_register找到s3c_led_device,然后传给probe函数,这里的*dev就指向
了platform_device定义的s3c_led_device*/
{
struct s3c_led_platform_data *pdata = dev->dev.platform_data;
//*pdata就获取到了static struct s3c_led_platform_data定义的s3c_led_data的地址
int result = ;
int i;
dev_t devno; /* Initialize the LED status */
for(i=; i<pdata->nleds; i++)
{
s3c2410_gpio_cfgpin(pdata->leds[i].gpio, S3C2410_GPIO_OUTPUT); //s3c2410_gpio_cfgpin配置端口的GPIO的功能
/*void s3c2410_gpio_cfgpin(unsigned int pin,unsigned int function)
第一个参数pin 是对应的io引脚(这里用宏S3C2410_GPB(5),5不是固定的,看你需要引用的引脚而定)
第二个引脚是设置该引脚的功能的,(由S3C2410_GPIO_INPUT,S3C2410_GPIO_OUTPUT,S3C2410_GPIO_SFN2,S3C2410_GPIO_SFN3这4个宏进行定义)
例如:s3c2410_gpio_cfgpin(S3C2410_GPB(5),S3C2410_GPIO_INPUT)
设置GPB5引脚为输入。*/
if(ON == pdata->leds[i].status)
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level); //设置led管脚的亮
}
else
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level); //设置led管脚灭
}
} /* Alloc the device for driver */
if ( != dev_major)
{
devno = MKDEV(dev_major, dev_minor);
result = register_chrdev_region(devno, , DEV_NAME); //分配主次设备号
}
else
{
result = alloc_chrdev_region(&devno, dev_minor, , DEV_NAME); //动态分配主次设备号
dev_major = MAJOR(devno);
} /* Alloc for device major failure */
if (result < )
{
printk("%s driver can't get major %d\n", DEV_NAME, dev_major);
return result;
} /* Initialize button structure and register cdev*/
memset(&led_device, , sizeof(led_device)); //
led_device.data = dev->dev.platform_data;
cdev_init (&(led_device.cdev), &led_fops);
led_device.cdev.owner = THIS_MODULE; result = cdev_add (&(led_device.cdev), devno , );
if (result)
{
printk (KERN_NOTICE "error %d add %s device", result, DEV_NAME);
goto ERROR;
} led_device.dev_class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(led_device.dev_class))
{
printk("%s driver create class failture\n",DEV_NAME);
result = -ENOMEM;
goto ERROR;
} #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
device_create(led_device.dev_class, NULL, devno, NULL, DEV_NAME);
#else
device_create (led_device.dev_class, NULL, devno, DEV_NAME);
#endif /* Initial the LED blink timer */ //异步不会阻塞?
init_timer(&(led_device.blink_timer)); //初始化时钟
led_device.blink_timer.function = led_timer_handler; //时间到了之后执行的函数
led_device.blink_timer.data = (unsigned long)pdata; //传给函数的参数
led_device.blink_timer.expires = jiffies + TIMER_TIMEOUT; //定义闹钟间隔
add_timer(&(led_device.blink_timer)); printk("S3C %s driver version %d.%d.%d initiliazed.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER); return ; ERROR:
printk("S3C %s driver version %d.%d.%d install failure.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER);
cdev_del(&(led_device.cdev)); unregister_chrdev_region(devno, );
return result; } static int s3c_led_remove(struct platform_device *dev)
{
dev_t devno = MKDEV(dev_major, dev_minor); del_timer(&(led_device.blink_timer)); cdev_del(&(led_device.cdev));
device_destroy(led_device.dev_class, devno);
class_destroy(led_device.dev_class); unregister_chrdev_region(devno, );
printk("S3C %s driver removed\n", DEV_NAME); return ;
} static struct platform_driver s3c_led_driver = {
.probe = s3c_led_probe,
.remove = s3c_led_remove,
.driver = {
.name = "s3c_led",
.owner = THIS_MODULE,
},
}; static int __init s3c_led_init(void)
{
int ret = ; ret = platform_device_register(&s3c_led_device);
if(ret)
{
printk(KERN_ERR "%s:%d: Can't register platform device %d\n", __FUNCTION__,__LINE__, ret);
goto fail_reg_plat_dev;
}
dbg_print("Regist S3C LED Platform Device successfully.\n"); ret = platform_driver_register(&s3c_led_driver);
if(ret)
{
printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__,__LINE__, ret);
goto fail_reg_plat_drv;
}
dbg_print("Regist S3C LED Platform Driver successfully.\n"); return ; fail_reg_plat_drv:
platform_driver_unregister(&s3c_led_driver);
fail_reg_plat_dev:
return ret;
} static void s3c_led_exit(void)
{
dbg_print("%s():%d remove LED platform drvier\n", __FUNCTION__,__LINE__);
platform_driver_unregister(&s3c_led_driver);
dbg_print("%s():%d remove LED platform device\n", __FUNCTION__,__LINE__);
platform_device_unregister(&s3c_led_device);
} module_init(s3c_led_init);
module_exit(s3c_led_exit); module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);
module_param(dev_minor, int, S_IRUGO); MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:S3C24XX_led");
下面是一个小小的应用程序:
 /*********************************************************************************
* Copyright: (C) 2016 2013dianxin_3
* All rights reserved.
*
* Filename: ledapp.c
* Description: This file
*
* Version: 1.0.0(06/06/2016)
* Author: xiaohexiansheng <1392195453@qq.com>
* ChangeLog: 1, Release initial version on "06/06/2016 05:21:50 PM"
*
********************************************************************************/
#include "plat_ioctl.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h> int main(int argc, char **argv)
{
int fd; fd = open("/dev/led", O_RDWR);
ioctl(fd,LED_ON,);
ioctl(fd,LED_ON,);
ioctl(fd,LED_ON,);
ioctl(fd,LED_BLINK,);
close(fd);
return ;
}

fl2440 platform总线led字符设备驱动的更多相关文章

  1. fl2440 platform总线button字符设备驱动

    驱动程序: #include "s3c_driver.h" #define DRV_DESC "S3C24XX button driver" /* Driver ...

  2. Tiny6410 LED字符设备驱动

    1.查看用户手册 led1.led2.led3.led4 连接的分别是 GPK4.GPK5.GPK6.GPK7 2.查询6410芯片手册 下面还需要3个步骤: 1.设置GPIO为OUTPUT. 将GP ...

  3. LED字符设备驱动实例及测试代码

    驱动代码如下: #include <linux/kernel.h>//内核头文件 #include <linux/init.h>//__init等 #include <l ...

  4. Linux LED字符设备驱动

    // 申请IO资源 int gpio_request(unsigned gpio, const char *label); // 释放IO资源 void gpio_free(unsigned gpio ...

  5. 深入理解Linux字符设备驱动

    文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...

  6. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  7. linux设备驱动之字符设备驱动模型(2)

    在上一篇中我们已经了解了字符设备驱动的原理,也了解了应用层调用内核函数的机制,但是我们每次操作设备,都必须首先通过mknod命令创建一个设备文件名,比如说我们要打开u盘,硬盘等这些设备,难道我们还要自 ...

  8. [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

    转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...

  9. (57)Linux驱动开发之三Linux字符设备驱动

    1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是: ...

随机推荐

  1. stdafx.h、stdafx.cpp的作用

    这两个文件用于建立一个预编译的头文件".PCH"和一个预定义的类型文件"STDAFX.OBJ".由于MFC体系结构非常大,各个源文件中都包含许多头文件,如果每次 ...

  2. Codeforces 940F Machine Learning 带修改莫队

    题目链接 题意 给定一个长度为\(n\)的数组\(a\),\(q\)个操作,操作分两种: 对于区间\([l,r]\),询问\(Mex\{c_0,c_1,c_2,⋯,c_{10^9}\}\),其中\(c ...

  3. JS 判断某变量是否为某数组中的一个值 的几种方法

    1.正则表达式 js 中判断某个元素是否存在于某个 js 数组中,相当于 PHP 语言中的 in_array 函数. }; 用法如下: var arr=new Array([‘b’,2,‘a‘,4]) ...

  4. js监听不到组合键

    我在js文件中写代码,监听 ctrl + enter 组合键,但是一直监听不到.只能监听到单个键. 后来我将监听的代码放到html页面中去,就能监听到了. 这个问题困扰我很久,记录下!

  5. 安装cuda8.0时出现错误:Missing recommended library: libGLU.so Missing recommended library: libGL.so

    安装cuda8.0时出现错误: Missing recommended library: libGLU.soMissing recommended library: libGL.so Installi ...

  6. PYTHON设计模式学习(3):Singleton pattern

    参考了其他的博客:http://ghostfromheaven.iteye.com/blog/1562618 #-*- encoding=utf-8 -*- print '-------------- ...

  7. CURL命令的使用

    原文地址:http://blog.sina.com.cn/s/blog_4b9eab320100slyw.html 可以看作命令行浏览器 1.开启gzip请求curl -I http://www.si ...

  8. phython正则表达式 Python Re模块

    反斜杠问题 与大多数编程语言相同,正则表达式里使用”\”作为转义字符,这就可能造成反斜杠困扰.假如你需要匹配文本中的字符”\”, Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式 ...

  9. 清理tomcat缓存

    解决方案:删除work目录的内容,注意不能删除work目录,不然会出现404错误 rm -rf /usr/local/tomcat/work/*

  10. Spring:与Redis的集成

    一个月没写过博客了,一直想记录一下之前学习的Redis的有关知识,但是因为四月太过于慵懒和忙碌,所以一直没有什么机会,今天就来讲讲,如何使用Spring当中的Spring-data-redis去与Re ...