微软交流社区

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 84|回复: 0

树莓派点灯驱动程序----基于总线设备驱动模型

[复制链接]

3

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-12-8 01:31:58 | 显示全部楼层 |阅读模式
摘要:一开始我们编写点灯程序,是把设备和驱动在一个文件中实现的。我们会发现,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                        = "myled",
        .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("GPL");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 = "led_drv";

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("led on\n");

                *gpset0 |= (0x1 << pin);

        }

        else

        {

                //led off

                printk("led off\n");

                *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, "myled" );

        leddrv_class_dev = device_create( leddrv_class, NULL, devno, NULL, module_name );

        printk("led_probe, found led\n");
        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("led_remove, remove led\n");
        return 0;
}


// allocate set and register a platform_driver
static struct platform_driver led_drv = {
        .probe  = led_probe,
        .remove = led_remove,
        .driver = {
                .name = "myled",
        }
};



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("GPL");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("Usage: \n");
                printf("%s: <on|off>\n", argv[0]);     //<> must have on | off
                return 0;
        }
       
        fd = open("/dev/led_drv",O_RDWR);
        if( fd < 0 )
        {
                printf("can not open\n");
        }
       
        if( strcmp(argv[1],"on") == 0 )
        {
                val = 1;
                printf("led on\n");
        }
        else if( strcmp(argv[1],"off") == 0 )
        {
                val = 0;
                printf("led off\n");
        }
        write(fd, &val, 4);
}测试运行:
1. 交叉编译
sudo make arm-linux-gnueabihf-gcc led_test.c -o led_test2. 测试
./led_test on     //开灯
./led_test off    //关灯这是我基于总线设备驱动写的一份基于树莓派GPIO0的点灯程序。大家如果有问题,可以留言,欢迎一起讨论。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|微软交流社区

GMT+8, 2025-1-7 05:03 , Processed in 0.068511 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表