之前写的字符驱动,安装驱动之后还需要自己mknod创建文件节点、而且设备和驱动都在一个驱动文件里,嵌入式的产品那么多,如果对应的硬件变了,那整个驱动代码就不可重用了,可移植性很差,下面说说怎么解决这些问题。
一、自动创建设备文件结点
实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev,udev根据系统信息创建设备结点、mdev是busybox提供的一个工具,用在嵌入式系统中,相当于简化版的udev,作用是在系统启动和热插拔或动态加载驱动程序时, 自动创建设备节点。文件系统中的/dev目录下的设备节点都是由mdev创建的。
[wuyujun@wuyujunlocalhost etc]$ cat inittab
# Use mdev to auto generate device nod and auto mount SD card and USB storage
::sysinit:/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug
::sysinit:/sbin/mdev -s
当驱动程序调用device_create()创建设备节点时内核产生hotplug事件,通知应用程序mdev创建设备程序,mdev最后调用make_device来创建节点,而make_device最终也还是会调用mknod函数去创建节点,详细的分析mdev参考下面博客
参考:https://blog.csdn.net/qq_33160790/article/details/79266306
自动创建文件结点需要用到的函数:
static struct class * ;
struct class *class_create(struct module *owner, const char *name)
class_create - create a struct class structure
@owner: pointer to the module that is to "own" this struct class
@name: pointer to a string for the name of this class.
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
class_destroy(struct class *cls);
device_destroy(struct class *class,dev_t devt);
在驱动初始化的代码里调用class_create为该设备创建一个类,再为每个设备调用 device_create创建对应的设备。
下面是增加了自动创建设备文件结点init函数
#include <linux/device.h> /*class_create()*/
#include <linux/version.h>//获取Linux内核版本号
struct class *dev_class ; //定义全局变量,结构体struct class 类型的指针dev_class方便后面使用;
static int __init s3c_led_init(void)
{
int result;
dev_t devno;
char dev_name[16] ;
int i ;
if( 0 != s3c_hw_init() )
{
printk(KERN_ERR "s3c2440 LED hardware initialize failure.\n");
return -ENODEV;
}
/* Alloc the device for driver */
if (0 != dev_major) /* Static */
{
devno = MKDEV(dev_major, 0);
result = register_chrdev_region (devno, dev_count, DEV_NAME);
}
else
{
result = alloc_chrdev_region(&devno, dev_minor, dev_count, DEV_NAME);
dev_major = MAJOR(devno);
}
/* Alloc for device major failure */
if (result < 0)
{
printk(KERN_ERR "S3C %s driver can't use major %d\n", DEV_NAME, dev_major);
return -ENODEV;
}
printk(KERN_DEBUG "S3C %s driver use major %d\n", DEV_NAME, dev_major);
if(NULL == (led_cdev=cdev_alloc()) )
{
printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
unregister_chrdev_region(devno, dev_count);
return -ENOMEM;
}
led_cdev->owner = THIS_MODULE;
cdev_init(led_cdev, &led_fops);
result = cdev_add(led_cdev, devno, dev_count);
if (0 != result)
{
printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result);
goto ERROR;
}
dev_class = class_create(THIS_MODULE, DEV_NAME) ;
if(IS_ERR(dev_class))
{
printk("%s driver create class failture\n",DEV_NAME);
result = -ENOMEM;
goto ERROR;
}
memset(dev_name,0,sizeof(dev_name)) ;
for(i=0;i<LED_NUM;i++)
{
devno = MKDEV(dev_major, i);
snprintf(dev_name,sizeof(dev_name),"%s%d",DEV_NAME,i) ;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
device_create(dev_class, NULL, devno, NULL, dev_name);
#else
device_create (dev_class, NULL, devno, dev_name);
#endif
}
printk(KERN_ERR "S3C %s driver[major=%d] version 1.0.0 installed successfully!\n", DEV_NAME, dev_major);
return 0;
ERROR:
printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);
cdev_del(led_cdev);
unregister_chrdev_region(devno, dev_count);
return result;
}
在static void __exit s3c_led_exit(void)函数退出还要destroy销毁掉,防止占用空间
static void __exit s3c_led_exit(void)
{
int i ;
dev_t devno = MKDEV(dev_major, dev_minor);
s3c_hw_term();
cdev_del(led_cdev);
unregister_chrdev_region(devno, dev_count);
for(i=0;i<LED_NUM;i++)
{
devno = MKDEV(dev_major, i);
device_destroy(dev_class, devno);
}
class_destroy(dev_class);
printk(KERN_ERR "S3C %s driver version 1.0.0 removed!\n", DEV_NAME);
return ;
}
二、Platform总线驱动
从Linux 2.6起引入了一套新的驱动管理和注册机制,platform_device和platform_driver,Linux中大部分的设备驱动都可以使用这套机制。platform是一条虚拟总线。设备用platform_device表示,驱动用platform_driver进行注册,linux platform driver机制和传统的device driver机制(通过driver_register进行注册)相比,一个明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性(这些标准接口是安全的)。
先分析一下platform的工作原理,platform先被内核注册,设备挂接到总线上时,与总线上的所有驱动进行匹配(用bus_type.match进行匹配),如果匹配成功,则调用bus_type.probe或者driver.probe初始化该设备,挂接到总线上,如果匹配失败,则只是将该设备挂接到总线上。
驱动挂接到总线上时,与总线上的所有设备进行匹配(用bus_type.match进行匹配),如果匹配成功,则调用bus_type.probe或者driver.probe初始化该设备;如果匹配失败,则只是将该驱动挂接到总线上。需要重点关注的是总线的匹配函数match(),驱动的初始化函数probe()。
platform的注册在linux-3.0/driver/base/platform.c里
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus); //注册platform设备
if (error)
return error;
error = bus_register(&platform_bus_type); //注册platform总线
if (error)
device_unregister(&platform_bus);
return error;
}
/platform设备声明
struct device platform_bus = {
.bus_id = "platform",
};
EXPORT_SYMBOL_GPL(platform_bus);
//platform总线设备声明
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //设备与匹配用到的函数
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
platform总线下的匹配函数,platform_match函数
static int platform_match(struct device *dev, struct device_driver *drv) //总线下的设备与设备驱动的匹配函数
{
struct platform_device *pdev = to_platform_device(dev); //通过device 变量获取到 platform_device
struct platform_driver *pdrv = to_platform_driver(drv); //通过driver获取 platform_driver
/* match against the id table first */
if (pdrv->id_table) //如果pdrv中的id_table 表存在
return platform_match_id(pdrv->id_table, pdev) != NULL; //匹配id_table
/* fall-back to driver name match */ // 第二个就是指直接匹配 pdev->name drv->name 名字是否形同
return (strcmp(pdev->name, drv->name) == 0);
}
static const struct platform_device_id *platform_match_id(
const struct platform_device_id *id,
struct platform_device *pdev)
{
while (id->name[0]) { // 循环去比较id_table数组中的各个id名字是否与pdev->name 相同
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id; // 将id_table数组中的名字匹配上的这个数组项指针赋值给 pdev->id_entry
return id; // 返回这个指针
}
id++;
}
return NULL;
}
platform总线下设备与设备驱动是通过名字进行匹配的,先去匹配platform_driver中的id_table表中的各个名字与platform_device->name,不管是先注册设备还是先注册设备驱动都会进行一次设备与设备驱动的匹配过程,匹配成功之后就会调用probe函数,匹配的原理就是去遍历总线下的相应的链表来找到挂接在他下面的设备或者设备驱动
注册platform之后就将设备挂到总线上
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]); //轮询的去注册设备
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
设备注册函数
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
接着看 platform_device_add()函数
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus; //设置父设备是platform_bus
pdev->dev.bus = &platform_bus_type; //设置类型是platform_bus_type
if (pdev->id != -1)
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); //设置设备名字
else
dev_set_name(&pdev->dev, "%s", pdev->name);
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = dev_name(&pdev->dev);
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d\n",
dev_name(&pdev->dev), i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
ret = device_add(&pdev->dev);//注册一个设备到内核
if (ret == 0)
return ret;
failed:
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
release_resource(r);
}
return ret;
}
device_add具体确定该设备的父设备总线等,调用bus_probe_device()...之后的就不再分析了,感兴趣的可以自己看源码
device_add()-->bus_probe_device()-->device_attch()-->__device_attch()-->really_probe()-->probe()。
和上面分析的一样device_add注册之后会到总线上面去找相对应的驱动,如果找到了最终调用probe()函数。
注册驱动函数
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type; //总线类型初始化为platform_bus_type
if (drv->probe) //注册probe函数
drv->driver.probe = platform_drv_probe;
if (drv->remove)//注册remove函数
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
实现driver_register的代码在/drivers/base/driver.c里
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus); //查找驱动防止重复注册
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv); //根据总线类型添加驱动
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
bus_add_driver()里有driver_attach()函数同样也是查找相对应的设备:
bus_add_driver()-->driver_attch()-->__driver_attach()-->driver_probe_device-->really_probe()-->probe()
找到了相对应的设备最终调用probe()函数。最后来看一下really_probe(),在linux-3.0/drivers/base/dd.c里有really_probe()函数
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
如果dev->bus->probe也就是platform总线上有probe函数优先执行dev->bus->probe(dev);,如果没有就执行驱动下面的probe函数drv->probe(dev);
使用platfrom总线驱动用到的函数:
int platform_driver_register(struct platform_driver *); // 注册设备驱动
void platform_driver_unregister(struct platform_driver *); // 卸载设备驱动
int platform_device_register(struct platform_device *); // 注册设备
void platform_device_unregister(struct platform_device *); // 卸载设备
这里有两个重要的结构体struct platform_driver和struct platform_device
定义结构体的头文件platform_device.h在linux-3.0/include/linux/路径下面
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
其中最重要的成员就是struct resource * resource;
struct resource {
resource_size_t start; /* 这段资源的起始 */
resource_size_t end; /* 这段资源的结束 */
const char *name; /* 这个资源的名字,方便用户查看 */
unsigned long flags; /* 标记属于那种资源 */
struct resource *parent, *sibling, *child; /* 作为树的节点,链入树中 */
};
驱动struct platform_driver结构体同样在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 (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
对gpio的操作的函数
为了更加优化代码,对gpio的操作可以使用内核给的函数,在linux内核源代码的/arch/arm/plat_s3c24xx/gpio.c中实现,这些函数的具体内容就不在这里介绍了,可以通过源代码进行查看,这里对这些函数的用法进行解读:
1.void s3c2410_gpio_cfgpin(unsigned int pin,unsigned int function)
第一个参数pin 是对应的io引脚
第二个引脚是设置该引脚的功能的
(由S3C2410_GPIO_INPUT,S3C2410_GPIO_OUTPUT,S3C2410_GPIO_SFN2,S3C2410_GPIO_SFN3这4个宏进行定义)
例如:s3c2410_gpio_cfgpin(S3C2410_GPB(5),S3C2410_GPIO_INPUT)
设置GPB5引脚为输入。
2.unsigned int s3c2410_gpio_gecfg(unsigned int pin)
作用:返回对应的GPIO的配置情况
例如:pin=s3c2410_GPB5返回GPB5的配置情况
3.void s3c2410_gpio_pullup(unsigned int pin,unsigned int to)
作用:设置相应的的GPIO的上拉电阻。
第一个参数:相应的引脚,和1里面的用法一致。
第二个参数:设置为1或者0,1表示上拉,0表示不上拉。
4.void s3c2410_gpio_setpin(unsigned int pin,unsigned int to)
作用:将相应的引脚输出为1或者0。
第一个参数:相应的引脚宏
第二个参数:1或者0
例子:s3c2410_gpio_setpin(S3C2410_GPB(5),1)将引脚GPB5输出为1
5.unsigned int s3c2410_gpin_getpin(unsigned int pin)
功能:获取输入值
参数:相应的引脚
6.unsigned int s3c2410_modify_misccr(unsigned int clear ,unsigned int change)
7.int s3c2410_gpio_getirq(unsigned pin)
Platform总线LED驱动代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include "platform_led.h"
#define DEV_NAME "led"
#define DEV_MAJOR 251
#ifndef DEV_MAJOR
#define DEV_MAJOR 0 /* dynamic major by default */
#endif
#define PLATDRV_MAGIC 0x60
#define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON _IO (PLATDRV_MAGIC, 0x19)
#define DEV_NUM 1
static int dev_major = DEV_MAJOR;
struct class *dev_class ;
/* LED hardware informtation data*/
static struct s3c_led_info s3c_leds[] = { //在"platform_led.h"头文件定义的结构体
[0] = {
.num = 1,
.gpio = S3C2410_GPB(5),
.active_level = LOWLEVEL,
.status = OFF,
},
[1] = {
.num = 2,
.gpio = S3C2410_GPB(6),
.active_level = LOWLEVEL,
.status = OFF,
},
[2] = {
.num = 3,
.gpio = S3C2410_GPB(8),
.active_level = LOWLEVEL,
.status = OFF,
},
[3] = {
.num = 4,
.gpio = S3C2410_GPB(10),
.active_level = LOWLEVEL,
.status = OFF,
},
};
/* The LED platform device private data */
static struct s3c_led_platform_data s3c_led_data = {
.leds = s3c_leds,
.nleds = ARRAY_SIZE(s3c_leds),
};
struct led_device
{
struct s3c_led_platform_data *data;
struct cdev cdev;
}led_device;
static void platform_led_release(struct device * dev)//platform设备结构体里声明的remove函数
{
int i;
struct s3c_led_platform_data *pdata = dev->platform_data;
for(i=0; i<pdata->nleds; i++)
{
/* Turn all LED off */
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
}
}
static void print_led_help(void)
{
printk("Follow is the ioctl() command for LED driver:\n");
printk("Turn LED on command : %u\n", LED_ON);
printk("Turn LED off command : %u\n", LED_OFF);
}
static int led_open(struct inode *inode, struct file *file) //file_operations里定义的open()函数
{
struct led_device *pdev ;
struct s3c_led_platform_data *pdata;
pdev = container_of(inode->i_cdev,struct led_device, cdev);
pdata = pdev->data;
file->private_data = pdata;
return 0;
}
static int led_release(struct inode *inode, struct file *file)//file_operations里定义release函数,应用层调用closes时调用到它
{
return 0;
}
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)//file_operations里定义的的ioctl()函数
{
struct s3c_led_platform_data *pdata = file->private_data;
switch (cmd)
{
case LED_OFF:
if(pdata->nleds <= arg) /* 通过传进来的arg判断对哪个LED灯控制,
如果传进来要控制的LED编号大于led的总数,出错返回*/
{
printk("LED%ld doesn't exist\n", arg);
return -ENOTTY;
}
s3c2410_gpio_setpin(pdata->leds[arg].gpio, ~pdata->leds[arg].active_level); //关灯
pdata->leds[arg].status = OFF;
break;
case LED_ON:
if(pdata->nleds <= arg)
{
printk("LED%ld doesn't exist\n", arg);
return -ENOTTY;
}
pdata->leds[arg].status = ON;
s3c2410_gpio_setpin(pdata->leds[arg].gpio, pdata->leds[arg].active_level); //开灯
break;
default:
printk("%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);
print_led_help();
return -EINVAL;
}
return 0;
}
struct file_operations led_fops = {
.open = led_open ,
.unlocked_ioctl = led_ioctl ,
.release = led_release ,
};
static int s3c_led_probe(struct platform_device *dev)//设备与驱动匹配成功进入probe函数
{
struct s3c_led_platform_data *pdata = dev->dev.platform_data;
int result = 0;
int i;
dev_t devno;
/* Initialize the LED status */
for(i=0; i<pdata->nleds; i++) //初始化LED状态
{
s3c2410_gpio_cfgpin(pdata->leds[i].gpio, S3C2410_GPIO_OUTPUT);
if(ON == pdata->leds[i].status)
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);
}
else
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
}
}
/* Alloc the device for driver */
if (0 != dev_major)
{
devno = MKDEV(dev_major, 0);
result = register_chrdev_region(devno, 1, DEV_NAME); //注册主次设备号
}
else
{
result = alloc_chrdev_region(&devno, 0, 1, DEV_NAME); //动态注册主次设备号
dev_major = MAJOR(devno);
}
/* Alloc for device major failure */
if (result < 0)
{
printk("%s driver can't get major %d\n", DEV_NAME, dev_major);
return result;
}
/* Initialize button structure and register cdev*/
memset(&led_device, 0, 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, 1); //绑定主次设备号,并注册给Linux内核
if (0 != result)
{
printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result);
goto ERROR;
}
dev_class = class_create(THIS_MODULE, DEV_NAME); //创建类
if(IS_ERR(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(dev_class, NULL, devno, NULL, DEV_NAME); //创建设备结点
#else
device_create(dev_class, NULL, devno, DEV_NAME);
#endif
printk("S3C %s driver version 1.0.0 initiliazed.\n", DEV_NAME);
return 0;
ERROR:
printk("S3C %s driver version 1.0.0 install failure.\n", DEV_NAME);
cdev_del(&(led_device.cdev));
unregister_chrdev_region(devno, 1);
return result;
}
static int s3c_led_remove(struct platform_device *dev) //platform驱动结构体声明的remove函数
{
dev_t devno = MKDEV(dev_major, 0);
cdev_del(&(led_device.cdev));
device_destroy(dev_class, devno);
class_destroy(dev_class);
unregister_chrdev_region(devno, 1);
printk("S3C %s driver removed\n", DEV_NAME);
return 0;
}
struct platform_device s3c_led_platdev = { //platform设备结构体
.name = "plat_led" ,
.id = 1 ,
.dev = {
.platform_data = &s3c_led_data,
.release = platform_led_release,
}
};
static struct platform_driver s3c_led_platdrv = { //platform驱动结构体
.probe = s3c_led_probe,
.remove = s3c_led_remove,
.driver = {
.name = "plat_led",
.owner = THIS_MODULE,
},
};
static int __init s3c_led_init(void)
{
int rv ;
rv = platform_device_register(&s3c_led_platdev); //注册设备
if(rv)
{
printk(KERN_ERR "%s:%d: Can't register platform device %d\n", __FUNCTION__,__LINE__, rv);
return rv;
}
printk("Regist S3C LED Platform Device successfully.\n");
rv =platform_driver_register(&s3c_led_platdrv) ;//注册设备驱动
if(rv)
{
printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__,__LINE__, rv);
return rv;
}
printk("Regist S3C LED Platform Driver successfully.\n");
return 0;
}
static void s3c_led_exit(void)
{
dev_t devno = MKDEV(dev_major, 0);
cdev_del(&(led_device.cdev));
device_destroy(dev_class, devno);
class_destroy(dev_class);
unregister_chrdev_region(devno, 1); //卸载字符设备
class_destroy(dev_class);
printk("%s():%d remove LED platform device\n", __FUNCTION__,__LINE__);
platform_device_unregister(&s3c_led_platdev) ; //卸载设备
printk("%s():%d remove LED platform drvier\n", __FUNCTION__,__LINE__);
platform_driver_unregister(&s3c_led_platdrv) ;//卸载设备驱动
}
/* These two functions defined in <linux/init.h> */
module_init(s3c_led_init);
module_exit(s3c_led_exit);
module_param(dev_major, int, S_IRUGO);
MODULE_AUTHOR("WuYujun<540726307@qq.com>");
MODULE_DESCRIPTION("FL2440 LED linux lowlevel platform device");
MODULE_LICENSE("GPL");
platform_led.h头文件
#ifndef _PLATDEV_LED_H_
#define _PLATDEV_LED_H_
#include <linux/platform_device.h>
#include <linux/version.h>
#include <mach/regs-gpio.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#include <mach/hardware.h>
#include <mach/gpio.h>
#include <asm/irq.h>
#else
#include <asm-arm/irq.h>
#include <asm/arch/gpio.h>
#include <asm/arch/hardware.h>
#endif
#define ENPULLUP 1
#define DISPULLUP 0
#define HIGHLEVEL 1
#define LOWLEVEL 0
#define INPUT 1
#define OUTPUT 0
#define OFF 0
#define ON 1
#define ENABLE 1
#define DISABLE 0
/* LED hardware informtation structure*/
struct s3c_led_info
{
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;
};
#endif /* ----- #ifndef _PLATDEV_LED_H_ ----- */
安装platform驱动
测试驱动代码:
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define DEV_NUM 4
#define PLATDRV_MAGIC 0x60
#define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON _IO (PLATDRV_MAGIC, 0x19)
int main(int argc, char **argv)
{
int i ;
int led_fd = -1;
char dev_name[16] ;
memset(dev_name,0,sizeof(dev_name)) ;
snprintf(dev_name, sizeof(dev_name), "/dev/led", i);
led_fd = open(dev_name,O_RDWR) ;
if(led_fd < 0)
{
printf("open %s failed:%s\n",dev_name, strerror(errno)) ;
goto clean ;
}
while(1)
{
for(i=0;i<DEV_NUM;i++)
{
ioctl(led_fd, LED_ON,i) ;
sleep(1) ;
ioctl(led_fd,LED_OFF,i) ;
sleep(1) ;
}
}
clean:
if(led_fd>0)
{
close(led_fd) ;
}
return 0;
}
同样是实现跑马灯功能