快速上手Linux系统总线设备驱动模型

什么是总线设备驱动模型

用一个流程图来概况:Bus/Dev/Drv模型

Bus
硬件资源
platform_device
驱动程序
platform_driver

该设计模型将硬件资源和驱动程序分离开,这样的程序方便扩展。

伪代码

以编写LED驱动程序为例理解总线设备驱动模型
第一步: 实现platform_device结构体。
将外设的一些引脚信息,单独放到一个同板卡相关同芯片不相关的.c文件中。

// 分配设置注册platform_device 结构体。在这里面定义资源,指定设备名字。
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>

#include "led_resource.h"

// 该函数在卸载 platform_device 时会被调用,如果不提供的话内核会打印警告信息。
static void led_dev_release(struct device *dev)
{
}

static struct resource resources[] = {
        {
                .start = GROUP_PIN(3,1),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
        {
                .start = GROUP_PIN(5,8),
                .flags = IORESOURCE_IRQ,
                .name = "100ask_led_pin",
        },
};


static struct platform_device board_A_led_dev = {
        .name = "100ask_led",
        .num_resources = ARRAY_SIZE(resources),
        .resource = resources,
        .dev = {
                .release = led_dev_release,
         },
};

/* 该.c文件作为一个可加载模块,里面也有入口函数、出口函数。
   将硬件信息注册到内核中
*/
static int __init led_dev_init(void)
{
    int err;
    // 向内核注册platform_device结构体
    err = platform_device_register(&board_A_led_dev);  
    return 0;
}

static void __exit led_dev_exit(void)
{
    platform_device_unregister(&board_A_led_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");

第二步:实现platform_driver结构体

同一个芯片对外设的初始化操作是类似的,比如LED0 LED1这些GPIO引脚、寄存器操作的操作同芯片的关系十分紧密,因此要将外设的初始化、控制等操作方法定义在同芯片相关同板卡不相关的.c文件中,同时platform_driver结构体也在这个.c中进行实现。

// 定义platform_driver结构体
static struct platform_driver chip_demo_gpio_driver = {
   .probe = chip_demo_gpio_probe, // 当有匹配的platform_device时,它的probe函数会调用
   .remove = chip_demo_gpio_remove,
   .driver = {
   .name = "100ask_led",
  },
 };

 /*
function:
1、从匹配的platform_device中获取资源,确定GPIO引脚。
2、把引脚记录下来,在操作硬件时使用。
3、新发现一个GPIO引脚,就调用上册驱动的代码创建设备节点。

*/
 static int chip_demo_gpio_probe(struct platform_device *pdev)
 {
	 struct resource *res;
	 int i = 0;

	 while (1)
	 {
		 res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
		 if (!res)
		 break;

		 g_ledpins[g_ledcnt] = res->start;
		 led_class_create_device(g_ledcnt);
		 g_ledcnt++;
	 }
	 return 0;
 }
/*
function:向内核注册platform_driver结构体,以及向上层驱动程序注册硬件操作方法的函数。
*/

static int __init chip_demo_gpio_drv_init(void)
{
    int err;
    
    err = platform_driver_register(&chip_demo_gpio_driver); 
    register_led_operations(&board_demo_led_opr);// 告知上层驱动程序,封装硬件操作方法的结构体
    return 0;
}

static void __exit lchip_demo_gpio_drv_exit(void)
{
    platform_driver_unregister(&chip_demo_gpio_driver);
}
// 硬件操作代码
 static int board_demo_led_init (int which) /* 初始化 LED, which-哪个 LED */
 static int board_demo_led_ctl (int which, char status) /* 控制 LED, which-哪个 LED, status:1-亮,0-灭 */
/*
  将操作硬件相关的代码放入结构体封装
*/
static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init,
    .ctl  = board_demo_led_ctl,
};

module_init(chip_demo_gpio_drv_init);
module_exit(lchip_demo_gpio_drv_exit);
MODULE_LICENSE("GPL");

第三步:定义并实现file_operation结构体

/*  定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};

/*
将file_operation结构体告诉内核。入口函数:安装驱动程序时,就回去调用这个入口函数。
*/
static int __init led_init(void)
{
	int err;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */
	led_class = class_create(THIS_MODULE, "100ask_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "led");
		return -1;
	}
	return 0;
}

/*  有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	class_destroy(led_class);
	unregister_chrdev(major, "100ask_led");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
快速上手Linux内核,您可以按照以下步骤进行: 1. 了解Linux内核的基本概念:学习Linux内核的基础知识,如进程管理、内存管理、文件系统设备驱动等。可以阅读相关的书籍、在线教程或参考官方文档。 2. 设置开发环境:在您的计算机上设置Linux开发环境。可以选择一个流行的Linux发行版(如Ubuntu、Fedora等),安装开发工具链和必要的软件包,以便进行内核编译和调试。 3. 下载并编译内核源代码:从Linux官方网站(https://www.kernel.org/)下载最新版本的内核源代码。按照官方提供的说明进行编译,生成可执行的内核映像文件。 4. 阅读和理解内核代码:开始阅读内核源代码,了解其结构和组织方式。可以从一些简单的模块或驱动程序开始,逐渐深入理解内核的不同部分。 5. 参与开发社区:加入Linux内核开发社区,参与讨论和交流,在邮件列表、论坛或IRC频道上与其他开发者互动。这将帮助您获得更多实践经验和指导。 6. 进行实际编程和调试:尝试编写简单的内核模块、驱动程序或补丁,并进行调试和测试。通过实际编程和解决实际问题,加深对内核的理解和掌握。 7. 学习内核调试技术:了解内核调试工具和技术,如GDB、Kprobes、Ftrace等。这些工具可以帮助您分析和调试内核代码,定位和修复问题。 8. 深入研究特定领域:根据自己的兴趣和需求,选择一个特定的领域进行深入研究,如网络协议栈、虚拟化、安全性等。探索Linux内核的各个子系统,并尝试在特定领域做出贡献。 请注意,学习Linux内核需要耐心和持续的努力。这只是一个快速上手的指南,真正的深入学习和掌握需要更多的时间和经验。建议在学习过程中保持对官方文档、书籍和其他资源的持续学习和参考。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值