|
摘要:一开始我们编写点灯程序,是把设备和驱动在一个文件中实现的。我们会发现,Linux的设备驱动并不是这样编写的,基于软件工程“高内聚,低耦合”的思想,设备和驱动往往是分离的。总线设备驱动模型就是这样一个思想。我们基于总线设备驱动模型,实现树莓派的点灯程序。
总线设备驱动模型:
在Linux2.6以后的设备驱动模型中,需关心总线、设备和驱动三个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动。相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
直接上代码,有问题的可以留言。
led_dev.c:负责device的代码
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <asm/io.h>
#include <linux/platform_device.h>
static struct resource led_rsrc[] = {
[0]={
.start = 0x3f200000,
.end = 0x3f200030,
.flags = IORESOURCE_MEM,
},
[1]={
.start = 0,
.end = 0,
.flags = IORESOURCE_IRQ,
}
};
static void led_release( struct device *dev )
{
}
// allocate set and register a platform_device
static struct platform_device led_dev = {
.name = &#34;myled&#34;,
.id = -1,
.num_resources = ARRAY_SIZE(led_rsrc),
.resource = led_rsrc,
.dev = { .release = led_release, },
};
static int __init led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
}
static void __exit led_dev_exit(void)
{
platform_device_unregister(&led_dev);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE(&#34;GPL&#34;);led_drv.c:负责driver的代码
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <asm/io.h>
#include <linux/platform_device.h>
static dev_t devno;
static int major = 231;
static int minor = 0;
static char *module_name = &#34;led_drv&#34;;
static struct class *leddrv_class;
static struct device *leddrv_class_dev;
volatile unsigned long *gpfsel0 = NULL;
volatile unsigned long *gpset0 = NULL;
volatile unsigned long *gpclr0 = NULL;
static int pin;
static int led_open(struct inode *inode, struct file *file)
{
*gpfsel0 &= ( ~( 0x6 << ( pin * 3 ) ) );
*gpfsel0 |= ( 0x1 << ( pin * 3 ) );
return 0;
}
//led_write函数
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int val = 0;
copy_from_user(&val, buf, count);
if( val == 1)
{
//led on
printk(&#34;led on\n&#34;);
*gpset0 |= (0x1 << pin);
}
else
{
//led off
printk(&#34;led off\n&#34;);
*gpclr0 |= (0x1 << pin);
}
return 0;
}
static struct file_operations leds_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
static int led_probe( struct platform_device *pdev )
{
int ret;
struct resource *res;
res = platform_get_resource( pdev, IORESOURCE_MEM, 0 );
gpfsel0 = (volatile unsigned long *)ioremap(res->start, 4 );
gpset0 = gpfsel0 + 7;
gpclr0 = gpfsel0 + 10;
res = platform_get_resource( pdev, IORESOURCE_IRQ, 0 );
pin = res->start;
devno = MKDEV(major,minor);
ret = register_chrdev( major, module_name, &leds_fops);
leddrv_class = class_create( THIS_MODULE, &#34;myled&#34; );
leddrv_class_dev = device_create( leddrv_class, NULL, devno, NULL, module_name );
printk(&#34;led_probe, found led\n&#34;);
return 0;
}
static int led_remove( struct platform_device *pdev )
{
device_destroy(leddrv_class,devno);
class_destroy(leddrv_class);
unregister_chrdev( major, module_name);
iounmap(gpfsel0);
printk(&#34;led_remove, remove led\n&#34;);
return 0;
}
// allocate set and register a platform_driver
static struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = &#34;myled&#34;,
}
};
static int __init led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
}
static void __exit led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE(&#34;GPL&#34;);Makefie:
KERN_DIR=/home/dr/raspberry_src/linux-rpi-4.14.y
#Kernel modules
obj-m += led_dev.o
obj-m += led_drv.o
build: kernel_modules
kernel_modules:
make -C $(KERN_DIR) M=`pwd` modules ARCH=arm CROSS_COMPILE=/home/dr/raspberry_src/tools-master/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
clean:
make -C $(KERN_DIR) M=`pwd` modules ARCH=arm CROSS_COMPILE=/home/dr/raspberry_src/tools-master/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf- clean
led_test.c:测试程序代码
#include <stdio.h>
#include <fcntl.h> //open
/*
* led_test on
* led_test off
*/
int main(int argc, char **argv)
{
int fd;
int val = 0;
if(argc != 2)
{
printf(&#34;Usage: \n&#34;);
printf(&#34;%s: <on|off>\n&#34;, argv[0]); //<> must have on | off
return 0;
}
fd = open(&#34;/dev/led_drv&#34;,O_RDWR);
if( fd < 0 )
{
printf(&#34;can not open\n&#34;);
}
if( strcmp(argv[1],&#34;on&#34;) == 0 )
{
val = 1;
printf(&#34;led on\n&#34;);
}
else if( strcmp(argv[1],&#34;off&#34;) == 0 )
{
val = 0;
printf(&#34;led off\n&#34;);
}
write(fd, &val, 4);
}测试运行:
1. 交叉编译
sudo make arm-linux-gnueabihf-gcc led_test.c -o led_test2. 测试
./led_test on //开灯
./led_test off //关灯这是我基于总线设备驱动写的一份基于树莓派GPIO0的点灯程序。大家如果有问题,可以留言,欢迎一起讨论。 |
|