linux平台总线驱动设备模型之点亮LED

上一节中,我们详细分析了平台驱动设备模型的源码,懂得了框架是如何构成的。

上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/17957637

这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备。

问:如何编写平台总线驱动设备这一套架构的设备驱动?

答:分为两个.c文件,一个是drv.c,另一个是dev.c;前者实现平台驱动,后者实现平台设备,平台总线不用我们自己实现。

问:编写平台驱动的核心内容有哪些?

答:分配、设置、注册一个platform_driver

问:如何注册平台驱动?

答:使用platform_driver_register(struct platform_driver *drv)函数,该函数的参数为platform_driver

问:如何定义platform_driver?

答:简单示例

static struct platform_driver led_driver = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "myled",
		.owner	= THIS_MODULE,
	}
};
问:probe函数什么时候被调用?

答:当系统中有同名的平台设备和平台驱动时,就会调用probe函数。

问:probe函数有什么作用?

答:该函数可以做什么由你决定,你可以只打印一条语句,也可以做很复杂的事情。例如,led_probe函数就做了获取资源,映射IO,注册字符设备。

led_drv.c源码参考:

#include <linux/module.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <asm/uaccess.h>   // copy_from_user
#include <asm/io.h>  // ioremap

static struct class *led_cls;

static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin;
static int major;

static int led_open(struct inode * inode, struct file * filp)
{
	*gpio_con &= ~(0x3<<(pin*2));
	*gpio_con |= (0x1<<(pin*2));
	return 0;
}

static ssize_t
led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	int val;
	copy_from_user(&val, buf, count);
	if(val == 1)
	{
		/* 点灯 */
		*gpio_dat  &= ~(1<<pin); 
	}
	else
	{
		/* 灭灯 */
		*gpio_dat  |= (1<<pin); 
	}
	return 0;
}


/* File operations struct for character device */
static const struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.open		= led_open,
	.write      = led_write,
};

static int __devinit led_probe(struct platform_device *pdev)
{
	struct resource *res;
	
	printk("led_probe, found led\n");
	/* 根据platform_device的资源进行ioremap */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	gpio_con =  ioremap(res->start, res->end - res->start + 1);
	gpio_dat = gpio_con + 1;

	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	pin =  res->start;

	/* 注册字符设备 */
	major = register_chrdev(0, "myled", &led_fops);
	
	led_cls = class_create(THIS_MODULE,"myled");
	device_create(led_cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
	return 0;
}
static int __devexit led_remove(struct platform_device *pdev)
{
	printk("led_remove, remove led\n");
	device_destroy(led_cls, MKDEV(major, 0));
	class_destroy(led_cls);
	unregister_chrdev(major, "myled");
	iounmap(gpio_con);
	return 0;
}

static struct platform_driver led_driver = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "myled",
		.owner	= THIS_MODULE,
	}
};

/* 分配/设置/注册一个platform_driver */
static int led_drv_init(void)
{
	return platform_driver_register(&led_driver);
}

static void led_drv_exit(void)
{
	platform_driver_unregister(&led_driver);
}

module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LWJ");
MODULE_DESCRIPTION("Just for Demo");


问:编写平台设备驱动的核心内容有哪些?

答:分配、设置、注册一个platform_device 

问:如何注册平台设备?

答:使用platform_device_register(struct platform_device *pdev)函数,该函数的参数为platform_device

问:如何定义platform_device?

答:简单示例:led_device

static struct platform_device led_device = {
    .id			= -1,
    .name		= "myled",  /* 与led_driver的name一致 */
    .resource		= led_resources,
    .num_resources	= ARRAY_SIZE(led_resources),
    .dev            ={
    	.release    = led_release,
    },
};
问:如何定义resource?

答:简单示例:

static struct resource led_resources[] = {
	[0] = {
		.start	= 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */
		.end	= 0x56000010 + 8 -1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= 5,		/* LED1 */
		.end	= 5,
		.flags	= IORESOURCE_IRQ,
	},
};
led_dev.c源码参考:

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

#include <linux/init.h>

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>

static struct resource led_resources[] = {
	[0] = {
		.start	= 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */
		.end	= 0x56000010 + 8 -1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= 5,		/* LED1 */
		.end	= 5,
		.flags	= IORESOURCE_IRQ,
	},
};

static void led_release(struct device * dev)
{
}
		
static struct platform_device led_device = {
    .id			= -1,
    .name		= "myled",  /* 与led_driver的name一致 */
    .resource		= led_resources,
    .num_resources	= ARRAY_SIZE(led_resources),
    .dev            ={
    	.release    = led_release,
    },
};

/* 分配/设置/注册一个platform_device */
static int led_dev_init(void)
{
	return platform_device_register(&led_device);
}

static void led_dev_exit(void)
{
	platform_device_unregister(&led_device);
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LWJ");
MODULE_DESCRIPTION("Just for Demo");
应用测试程序源码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* 9th_led_test on
 * 9th_led_test off
 */
int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/led", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", argv[0]);
		return 0;
	}

	if (strcmp(argv[1], "on") == 0)
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	
	write(fd, &val, 4);
	return 0;
}
测试步骤:

9th_led_test        first_drv.ko        sddisk
Qt                  first_test          second_drv.ko
TQLedtest           fourth_drv.ko       second_test
app_test            fourth_test         sixth_drv.ko
bin                 home                sixth_test
busybox             led_dev.ko          sixthdrvtest
buttons_all_drv.ko  led_drv.ko          sys
buttons_all_test    lib                 third_drv.ko
buttons_input.ko    linuxrc             third_test
dev                 mnt                 tmp
driver_test         opt                 udisk
etc                 proc                usr
fifth_drv.ko        root                var
fifth_test          sbin                web
[WJ2440]# insmod led_drv.ko 
[WJ2440]# insmod led_dev.ko 
led_probe, found led
[WJ2440]# rmmod led_dev
led_remove, remove led
rmmod: module 'led_dev' not found
[WJ2440]# lsmod
led_drv 2800 0 - Live 0xbf003000
[WJ2440]# insmod led_dev.ko 
led_probe, found led
[WJ2440]# lsmod
led_dev 1444 0 - Live 0xbf009000
led_drv 2800 0 - Live 0xbf003000
[WJ2440]# ls /dev/led -l 
crw-rw----    1 root     root      252,   0 Jan  2 07:44 /dev/led
[WJ2440]# ./9th_led_test 
Usage :
./9th_led_test <on|off>
[WJ2440]# ./9th_led_test off
[WJ2440]# ./9th_led_test on 
当执行./9th_led_test off时,led1被熄灭;当执行./9th_led_test on时  led1被点亮。如果你需要点亮led2,那么只需要修改led_dev的led_resources改为:
static struct resource led_resources[] = {
	[0] = {
		.start	= 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */
		.end	= 0x56000010 + 8 -1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= 6,		/* LED2 */
		.end	= 6,
		.flags	= IORESOURCE_IRQ,
	},
};
这样,应用程序不用更改,即可点亮led2,这样一来就实现了,稳定部分不用修改,只需要修改硬件易变部分,并且应用程序不需要任何更改。

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值