基于linux platform总线的LED驱动

在linux 2.6版本的设备驱动中,主要关心总线、设备和驱动者三个实体,总线将设备和驱动绑定。在系统没注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

一个现实的linux设备和驱动通常都需要接在一种总线上,对于本身依附于PCI、USB、I2C、SPI等设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统集成的独立外设控制器、挂接在SoC内存空间的外设等,却不依附于此类总线。基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver。

由以上可知,我们需要基于platform编写LED驱动,那么我们主要关注两个部分即可:第一、platform 设备部分;第二、platform驱动部分,下面就分别进行介绍

在介绍之前,首先明确一个观点,编写platform总线驱动platform设备名字和platform总线名字必须一致,否则驱动无法找到设备,设备无法找到驱动

1、platform 设备部分

设备部分主要包括了一个平台设备数据结构struct platform_device,然后剩下的就是platform的register和unregister了,具体代码如下所示

#include <linux/module.h>
#include <linux/init.h>

#include <linux/platform_device.h>

#include <asm/arch/regs-gpio.h>
#include <asm/arch/leds-gpio.h>

static void led_dev_release(struct device *dev)
{
	printk("<kernel> release\n");
}

static struct s3c24xx_led_platdata s3c2416_pdata_led4 = {	//LED控制数据
	.gpio		= S3C2410_GPK5,
	.flags		= S3C24XX_LEDF_ACTLOW,
	.name		= "led4"
};

static struct platform_device s3c_led_dev = {			//定义一个LED平台设备
	.name = "s3c2416_led",					//平台设备名字,此名字很重要
	.id   = 0,
	.dev = {
		.platform_data = &s3c2416_pdata_led4,		
		.release = led_dev_release,			//平台设备撤销的时候会调用,如果没有则会出错,可以试试
	},
};


static int __init led_device_init(void)				//模块初始化,主要注册该设备
{
	int ret;
	
	ret = platform_device_register(&s3c_led_dev);
	if (ret) {
		printk("device register failed\n");
		return ret;
	}

	printk("led device init\n");
	return 0;
}

static void __exit led_device_exit(void)			
{
	platform_device_unregister(&s3c_led_dev);
	printk("led device bye!\n");
}

module_init(led_device_init);
module_exit(led_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("laneyu<yulane.lang@gmail.com>");

2、platform 驱动部分

平台驱动部分主要包括了一个驱动数据结构strut platform_driver,以及平台驱动注册和撤销,还有就是最重要的probe和remove了,具体代码如下所示

#include <linux/module.h>
#include <linux/init.h>

#include <linux/platform_device.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/leds-gpio.h>

int led_driver_probe(struct platform_device *pdev)
{
	struct s3c24xx_led_platdata *pdata = pdev->dev.platform_data;	
	
	s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_setpin(pdata->gpio, 0);

	printk("led on\n");
	return 0;
}

int led_driver_remove(struct platform_device *pdev)
{
	struct s3c24xx_led_platdata *pdata = pdev->dev.platform_data;	

	s3c2410_gpio_setpin(pdata->gpio, 1);

	printk("led off\n");
	return 0;
}

struct platform_driver s3c_led_drv = {
	.probe  = led_driver_probe,
	.remove = led_driver_remove,
	.driver = {
		.name = "s3c2416_led",				//平台驱动名字,注意此名字必须和平台设备名字一致
	},
};

static int __init plat_led_init(void)
{
	int ret;

	ret = platform_driver_register(&s3c_led_drv);
	if (ret) {
		printk("led register failed!\n");
		return ret;
	}

	printk("led dirver init\n");
	return 0;
}

static void __exit plat_led_exit(void)
{
	platform_driver_unregister(&s3c_led_drv);
	printk("led driver exit");
}

module_init(plat_led_init);
module_exit(plat_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("laneyu<yulane.lang@gmail.com>");
3、编译

Makefile如下所示

.PHONY: all clean

obj-m += device.o driver.o

KDIR := /home/kernel2416

all: 
	make ARCH=arm CROSS_COMPILE=arm-linux- -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
	make ARCH=arm CROSS_COMPILE=arm-linux- -C $(KDIR) SUBDIRS=$(PWD) clean

4、测试

将编写好的driver.ko device.ko加载到内核里面去,设备和设备驱动加载没有先后顺序要求,此处,我们先加载设备,然后加载驱动,如下所示

加载设备 insmod device.ko,如下图所示

此处,我们可以看到执行完了insmod device.ko以后,打印出了一句led device init以及查看/sys/devices/platform下面多了一个s3c2416_led.0的设备名字

接下来,我们才用同样的方法注册平台驱动 ,即insmod,如下图所示

此处,我们可以看到打印出来我们想打印的两句话,并且可以看到开发板的一个LED灯以及亮了

接下来我们开始撤销设备和设备驱动,首先撤销设备驱动,如下所示

rmmod driver.ko,结果如下

注意:撤销设备驱动以后,我们可以发现开发板上面的LED灯灭了

接下来撤销设备,操作结果如下

rmmod device.ko,结果如下

5、platform调试技巧

platform总线驱动调试最重要的技巧就是,查看probe函数有没有执行,如果probe函数没有执行那么问题肯定就是平台设备名和平台驱动名不一致导致的,因此,只需要记住这一点就可以编写好platform驱动了。

此处,是动态加载设备,后面还可以修改为静态加载设备

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * 基于双向链表实现双端队列结构 */ package dsa; public class Deque_DLNode implements Deque { protected DLNode header;//指向头节点(哨兵) protected DLNode trailer;//指向尾节点(哨兵) protected int size;//队列中元素的数目 //构造函数 public Deque_DLNode() { header = new DLNode(); trailer = new DLNode(); header.setNext(trailer); trailer.setPrev(header); size = 0; } //返回队列中元素数目 public int getSize() { return size; } //判断队列是否为空 public boolean isEmpty() { return (0 == size) ? true : false; } //取首元素(但不删除) public Object first() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return header.getNext().getElem(); } //取末元素(但不删除) public Object last() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return trailer.getPrev().getElem(); } //在队列前端插入新节点 public void insertFirst(Object obj) { DLNode second = header.getNext(); DLNode first = new DLNode(obj, header, second); second.setPrev(first); header.setNext(first); size++; } //在队列后端插入新节点 public void insertLast(Object obj) { DLNode second = trailer.getPrev(); DLNode first = new DLNode(obj, second, trailer); second.setNext(first); trailer.setPrev(first); size++; } //删除首节点 public Object removeFirst() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = header.getNext(); DLNode second = first.getNext(); Object obj = first.getElem(); header.setNext(second); second.setPrev(header); size--; return(obj); } //删除末节点 public Object removeLast() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = trailer.getPrev(); DLNode second = first.getPrev(); Object obj = first.getElem(); trailer.setPrev(second); second.setNext(trailer); size--; return(obj); } //遍历 public void Traversal() { DLNode p = header.getNext(); while (p != trailer) { System.out.print(p.getElem()+" "); p = p.getNex
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值