嵌入式Linux驱动复习(4)分层思想写imx6ull的LED驱动程序

本节的重点是我们要理解如何分层的实现驱动程序

 对于LED驱动,我们想要什么样的接口?

用分层的思想写驱动程序,才能使得驱动程序支持多个板子

1. 把驱动拆分为通用的框架(leddrv.c)、具体的硬件操作(board_X.c):

 

2. 以面向对象的思想,改进代码:

抽象出一个结构体:

 

 每个单板相关的board_X.c实现自己的led_operations结构体,供上层的leddrv.c调用:

 

led_opr.h

#ifndef _LED_OPR_H
#define _LED_OPR_H

//函数指针???反正是面向对象思想的写法 
struct led_operations {
	int (*init) (int which); /* 初始化LED, which-哪个LED */       
	int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void); //定义了一个结构体指针函数


#endif

board_demo.c

#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 "led_opr.h"

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */	   
{
	
	printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
	return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
	printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
	return 0;
}

static struct led_operations board_demo_led_opr = {
	.init = board_demo_led_init,
	.ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)//leddrv中调用,让主函数能调用上面定义的函数
{
	return &board_demo_led_opr;
}

leddrv.c

#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 "led_opr.h"

#define LED_NUM 2

/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)

/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);   //从file里面的到inode
	int minor = iminor(inode);  //得出次设备号
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* 2. 定义自己的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,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{
	int err;
	int i;
	
	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, "100ask_led");
		return -1;
	}

	for (i = 0; i < LED_NUM; i++)
		device_create(led_class, NULL, MKDEV(major, i), NULL, "100ask_led%d", i); /* /dev/100ask_led0,1,... */

	p_led_opr = get_board_led_opr();   //的到具体单板的指针,可以用来调用init和ctrl函数 
	
	return 0;
}

/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
	int i;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	for (i = 0; i < LED_NUM; i++)
		device_destroy(led_class, MKDEV(major, i)); /* /dev/100ask_led0,1,... */

	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	unregister_chrdev(major, "100ask_led");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 IMX6uLL LED 闪烁的驱动程序的例子: ```c #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> /* GPIO register offsets */ #define GPIO_DR_OFFSET 0x0 #define GPIO_GDIR_OFFSET 0x4 #define GPIO_PSR_OFFSET 0x8 #define GPIO_ICR1_OFFSET 0x10 #define GPIO_ICR2_OFFSET 0x14 /* LED GPIO pin number */ #define LED_GPIO_PIN 10 /* Module information */ MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("LED Blink Driver"); MODULE_LICENSE("GPL"); /* GPIO base address */ static void __iomem *gpio_base; /* Module initialization function */ static int led_blink_probe(struct platform_device *pdev) { int ret; u32 gpio_val; /* Get the GPIO base address from the device tree */ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); gpio_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(gpio_base)) { dev_err(&pdev->dev, "Failed to map GPIO registers\n"); return PTR_ERR(gpio_base); } /* Configure the GPIO direction */ gpio_val = readl(gpio_base + GPIO_GDIR_OFFSET); gpio_val |= (1 << LED_GPIO_PIN); writel(gpio_val, gpio_base + GPIO_GDIR_OFFSET); /* Blink the LED */ while (!kthread_should_stop()) { gpio_val = readl(gpio_base + GPIO_DR_OFFSET); gpio_val ^= (1 << LED_GPIO_PIN); writel(gpio_val, gpio_base + GPIO_DR_OFFSET); msleep(500); } return 0; } /* Module exit function */ static int led_blink_remove(struct platform_device *pdev) { return 0; } /* Device tree match table */ static const struct of_device_id led_blink_of_match[] = { { .compatible = "led-blink-gpio", }, {}, }; MODULE_DEVICE_TABLE(of, led_blink_of_match); /* Platform driver structure */ static struct platform_driver led_blink_driver = { .probe = led_blink_probe, .remove = led_blink_remove, .driver = { .name = "led-blink-gpio", .of_match_table = led_blink_of_match, }, }; /* Module initialization function */ static int __init led_blink_init(void) { /* Register the platform driver */ return platform_driver_register(&led_blink_driver); } /* Module exit function */ static void __exit led_blink_exit(void) { /* Unregister the platform driver */ platform_driver_unregister(&led_blink_driver); } /* Module entry and exit points */ module_init(led_blink_init); module_exit(led_blink_exit); ``` 这个驱动程序使用了平台设备和设备树来获取 LED GPIO 的地址和配置信息,并使用 GPIO 控制 LED 的闪烁。在这个例子中,我们使用了 GPIO1_IO10 作为 LED 的控制引脚,然后使用 `msleep()` 函数来控制闪烁的频率。注意,驱动程序中的代码仅供参考,需要针对具体的硬件进行修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值