linux平臺匯流排驅動裝置模型之點亮LED

lwj103862095發表於2014-01-07

上一節中,我們詳細分析了平臺驅動裝置模型的原始碼,懂得了框架是如何構成的。

上一節文章連結: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,這樣一來就實現了,穩定部分不用修改,只需要修改硬體易變部分,並且應用程式不需要任何更改。

相關文章