笔记整理—linux驱动开发部分(4)驱动框架

news2024/11/8 23:46:12

        内核中,针对每种驱动都设计了一套成熟的、标准的、典型的驱动框架,实现将相同部分实现,不同部分留出接口给工程师自行发挥。具有以下特点:①简单化;②标准化;③统一管控系统资源;④特定化接口函数与数据结构。eg:led_class.c led_core.c 去实现自己的LED_xxx.c,其中xxx为SOC厂商,产品商又会以厂商代码做出产品,对soc厂商代码做移植与调试。

        模块分析方法:从下往上进行分析。驱动框架:实现了一个类的通用class。

        内核驱动开发以级别定启动顺序与段(n=1~7s,可选1~7s,但实际为0~7s)。内核启动时按段启动顺序执行。

#define __define_initcall(level,fn,id) \
	static initcall_t __initcall_##fn##id __used \
	__attribute__((__section__(".initcall" level ".init"))) = fn

   attribute,对应于/sys/class/xxx/目录中的内容,一般为文件或文件夹,是sysfs给应用层的一些接口,类似/dev/下的设备文件。

        驱动操作硬件:①file_operation;②attribute。

class_create:在/sys/class下创建一个类
device_create:创建属于一个类的一个设备,本质是注册一个设备,是驱动框架的注册接口
file_operations:用于register_chrdev注册

驱动模型分为三种
platform:平台设备
system:系统设备
virtual:虚拟设备

        echo 1>led1 可操作led1,因为在led1的ston中有方法支持(X210开发板)。

        基于LED class实现LED驱动,关键点在于led_classdev_register,这是厂商已经写好的,自己不用在写一次。

int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
				      "%s", led_cdev->name);
	if (IS_ERR(led_cdev->dev))
		return PTR_ERR(led_cdev->dev);

#ifdef CONFIG_LEDS_TRIGGERS
	init_rwsem(&led_cdev->trigger_lock);
#endif
	/* add to the list of leds */
	down_write(&leds_list_lock);
	list_add_tail(&led_cdev->node, &leds_list);
	up_write(&leds_list_lock);

	if (!led_cdev->max_brightness)
		led_cdev->max_brightness = LED_FULL;

	led_update_brightness(led_cdev);

#ifdef CONFIG_LEDS_TRIGGERS
	led_trigger_set_default(led_cdev);
#endif

	printk(KERN_DEBUG "Registered led device: %s\n",
			led_cdev->name);

	return 0;
}

        当注册成功在/sys/class/led/下会出现相关信息。

static int __init s5pv210_led_init(void);//注册
static void __exit s5pv210_led_exit(void);//注销

struct led_classdev mydev;//led_classdev 是内核类设备

led_classdev_register(NULL,&mydev);
led_classdev_unregister(&mydev);

mydev.name="mydev";
mydev.brightness=0;
mydev.brightness_set=s5pv210_led_set;

        store 方法对应echo写法;show方法对应cat(show)读方法。

        show方法读硬件信息,返回到应用层,但因为驱动框架提供的方法无法直接提取到信息,因为没有特殊性的去细化每一个设备,只是一个类的方法,所以在show和store之间使struct led_classdev结构体中相对应的细化方法去实现。struct led_classdev结构体中的方法要自己去写。

struct led_classdev {
	const char		*name;
	int			 brightness;
	int			 max_brightness;
	int			 flags;

	/* Lower 16 bits reflect status */
#define LED_SUSPENDED		(1 << 0)
	/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME	(1 << 16)

	/* Set LED brightness level */
	/* Must not sleep, use a workqueue if needed */
	void		(*brightness_set)(struct led_classdev *led_cdev,
					  enum led_brightness brightness);
	/* Get LED brightness level */
	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);

	/* Activate hardware accelerated blink, delays are in
	 * miliseconds and if none is provided then a sensible default
	 * should be chosen. The call can adjust the timings if it can't
	 * match the values specified exactly. */
	int		(*blink_set)(struct led_classdev *led_cdev,
				     unsigned long *delay_on,
				     unsigned long *delay_off);

	struct device		*dev;
	struct list_head	 node;			/* LED Device list */
	const char		*default_trigger;	/* Trigger to use */

#ifdef CONFIG_LEDS_TRIGGERS
	/* Protects the trigger data below */
	struct rw_semaphore	 trigger_lock;

	struct led_trigger	*trigger;
	struct list_head	 trig_list;
	void			*trigger_data;
#endif
};
static void s5pv210_led_set(struct led_classdev *led_cdev,emum led_brightness value)
{
    if(LED_OFF==value)
    {
        write(0xxxxxxxxx,GPJ0CON);
        //方法
        write(0xxxxxxxxx,GPJ0DAT);
    }else if(LED_ON==value)
    {
        //方法
    }
}

        硬件驱动的机制与策略/机制——怎么实现操作方法,也即是驱动;策略——如何使硬件按照自己的要求工作,也就是应用。

        机制不应该去提供策略方法,操作LED的方法就是想灭就灭,想关就关,机制只应该提供开关的接口,不应该去管要亮几个LED,等应用层要LED亮就亮就行了。

        读改写保证开关一个LED:

writel(((readl(GPJ0DAT)|(1<<3),GPJ0DAT);//灭
writel(((readl(GPJ0DAT)&~(1<<3),GPJ0DAT);//亮

        GPIOLIB是先申请,再使用的。大部分硬件都使用GPIO工作以及复用(同GPIO工作不同硬件);同一个GPIO被两个驱动同时控制会出现BUG;内核提供gpiolib进行统一管理系统中所有gpio(只要一方不释放,别人就别想用);gpiolib本质属于驱动框架的一部分(/kernel/drivers/gpio)。

        GPIO的使用方法:申请——>使用——>释放。

        gpio_chip结构体,gpio操作方法框架。

struct gpio_chip {
	const char		*label;
	struct device		*dev;
	struct module		*owner;

	int			(*request)(struct gpio_chip *chip,
						unsigned offset);
	void			(*free)(struct gpio_chip *chip,
						unsigned offset);

	int			(*direction_input)(struct gpio_chip *chip,
						unsigned offset);
	int			(*get)(struct gpio_chip *chip,
						unsigned offset);
	int			(*direction_output)(struct gpio_chip *chip,
						unsigned offset, int value);
	int			(*set_debounce)(struct gpio_chip *chip,
						unsigned offset, unsigned debounce);

	void			(*set)(struct gpio_chip *chip,
						unsigned offset, int value);

	int			(*to_irq)(struct gpio_chip *chip,
						unsigned offset);

	void			(*dbg_show)(struct seq_file *s,
						struct gpio_chip *chip);
	int			base;
	u16			ngpio;
	const char		*const *names;
	unsigned		can_sleep:1;
	unsigned		exported:1;
};

        xxx_gpio_cfg:xxxgpio配置。xxx_gpio_pm,xxx电源管理;__iomem是虚拟地址基地址。

struct s3c_gpio_chip {
	struct gpio_chip	chip;
	struct s3c_gpio_cfg	*config;
	struct s3c_gpio_pm	*pm;
	void __iomem		*base;
	int			eint_offset;
	spinlock_t		 lock;
#ifdef CONFIG_PM
	u32			pm_save[7];
#endif
};

struct s3c_gpio_cfg {
	unsigned int	cfg_eint;

	s3c_gpio_pull_t	(*get_pull)(struct s3c_gpio_chip *chip, unsigned offs);
	int		(*set_pull)(struct s3c_gpio_chip *chip, unsigned offs,
				    s3c_gpio_pull_t pull);

	int		(*set_pin)(struct s3c_gpio_chip *chip, unsigned offs,
				    s3c_gpio_pull_t level);

	unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs);
	int	 (*set_config)(struct s3c_gpio_chip *chip, unsigned offs,
			       unsigned config);
};

struct s3c_gpio_pm {
	void (*save)(struct s3c_gpio_chip *chip);
	void (*resume)(struct s3c_gpio_chip *chip);
};

        端口与IO口,一个端口包含多个IO口,如GPA0是一个端口,GPA0_0是其中的一个IO口,每一个端口与相接端口相差0x20。        

 

        内核中为每个IO分配唯一一个连续编号,编号可用让程序识别,每一个GPIO label(是给人看的,本质上是对于申请的GPIO_n所起的别名,在后面可用该别名进行端口申请情况查找),base是每个GPIO的起始编码。

        内核中也对终端号进行了统一管控。

static struct s3c_gpio_chip s5pv210_gpio_4bit[]

        实现了对s5pv210开发板上的端口信息概括:

ARRAY_SIZE(s5pv210_gpio_4bit);//实现端口数计算
void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip,
					   int nr_chips)
{
	for (; nr_chips > 0; nr_chips--, chip++) {
		samsung_gpiolib_add_4bit(chip);
		s3c_gpiolib_add(chip);
	}
}
//这是三星注册gpio的方法。

int gpiochip_add(struct gpio_chip *chip)
{
	unsigned long	flags;
	int		status = 0;
	unsigned	id;
	int		base = chip->base;

	if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
			&& base >= 0) {
		status = -EINVAL;
		goto fail;
	}

	spin_lock_irqsave(&gpio_lock, flags);

	if (base < 0) {
		base = gpiochip_find_base(chip->ngpio);
		if (base < 0) {
			status = base;
			goto unlock;
		}
		chip->base = base;
	}

	/* these GPIO numbers must not be managed by another gpio_chip */
	for (id = base; id < base + chip->ngpio; id++) {
		if (gpio_desc[id].chip != NULL) {
			status = -EBUSY;
			break;
		}
	}
	if (status == 0) {
		for (id = base; id < base + chip->ngpio; id++) {
			gpio_desc[id].chip = chip;

			/* REVISIT:  most hardware initializes GPIOs as
			 * inputs (often with pullups enabled) so power
			 * usage is minimized.  Linux code should set the
			 * gpio direction first thing; but until it does,
			 * we may expose the wrong direction in sysfs.
			 */
			gpio_desc[id].flags = !chip->direction_input
				? (1 << FLAG_IS_OUT)
				: 0;
		}
	}

unlock:
	spin_unlock_irqrestore(&gpio_lock, flags);
	if (status == 0)
		status = gpiochip_export(chip);
fail:
	/* failures here can mean systems won't boot... */
	if (status)
		pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",
			chip->base, chip->base + chip->ngpio - 1,
			chip->label ? : "generic");
	return status;
}
//进行gpiochip注册,这是通用的

__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
{
	struct gpio_chip *gc = &chip->chip;
	int ret;

	BUG_ON(!chip->base);
	BUG_ON(!gc->label);
	BUG_ON(!gc->ngpio);

	spin_lock_init(&chip->lock);

	if (!gc->direction_input)
		gc->direction_input = s3c_gpiolib_input;
	if (!gc->direction_output)
		gc->direction_output = s3c_gpiolib_output;
	if (!gc->set)
		gc->set = s3c_gpiolib_set;
	if (!gc->get)
		gc->get = s3c_gpiolib_get;

#ifdef CONFIG_PM
	if (chip->pm != NULL) {
		if (!chip->pm->save || !chip->pm->resume)
			printk(KERN_ERR "gpio: %s has missing PM functions\n",
			       gc->label);
	} else
		printk(KERN_ERR "gpio: %s has no PM function\n", gc->label);
#endif

	/* gpiochip_add() prints own failure message on error. */
	ret = gpiochip_add(gc);
	if (ret >= 0)
		s3c_gpiolib_track(chip);
}
//其中提供了chip的input、output、set、get四种方法

        同一个虚拟地址不可关联多个chip。

        gpiochip_add是将封装的一个GPIO端口所有信息变量挂载到内核gpiolib模块定义的一个gpio_desc数组中。gpiochip_add是给厂商用来注册gpio接口的,在正常开发过程中是用不上的。gpio_request给驱动工程师使用gpiolib的接口,也是正常开发使用的接口。

        驱动过程中,使用gpio接口,应先调用gpio_request去申请gpio接口,gpio_free用于释放申请的gpio接口。

int gpio_request_array(struct gpio *array, size_t num);可以一次申请多个gpio
void gpio_free_array(struct gpio *array, size_t num);一次释放多个gpio
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);申请一个gpio,支持flag调试模式
const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset);查看gpio是否被申请
int gpio_direction_input/output(unsigned gpio);设置gpio输入/输出模式(只是在soc方法做了封装)本质上指向了samsung_gpiolib_4bit_output/input.

        框架就是给不同soc留有接口去定制专项方法。 

        在gpiochip n文件夹中有base、lable、ngpio方法。

        echo n>export可导出相关gpio信息。

        gpiolib使用方法:①申请gpio_request;②设置输入输出模式gpio_direction_input/output;③设置输入输入的值gpio_get_value、gpio_set_value。

        在mach/gpio中可看到用gpio与申请方法:

#defile GPIO_LED1    s5pv210_GPJ0(3);
#defile GPIO_LED2    s5pv210_GPJ0(4);
#defile GPIO_LED3    s5pv210_GPJ0(5);

if(gpio_request(GPIO_LED1,"gpio_3"))
{
    //err
}else{
    gpio_direction_output(GPIO_LED1,1);
}

//释放
gpio_free(GPIO_LED1);

//gpio设置高低电平
gpio_set_value(GPIO_LED1,0);


//申请多个gpio口
gpio_request_array(struct gpio,size);

        查看某个gpio释放被使用 debugfs虚拟文件系统:①mount -t dugfs debugfs /tmp;②cat /tmp/gpio 可查看gpio信息;③umount /tmp卸载debugfd。

        自己写的驱动应该放在文件夹中的/eds/下,后修改MAKEFILE、修改kconfig文件。在make menuconfig后可进行配置,在.config中可查看配置结果,make就可进行内核编译。编译成为模块的文件在同文件夹下可见.ko文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2236183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

靠谷歌广告赚了100美刀,程序员可以照这个思路去干

复制网站盈利尝试&#xff1a;谷歌广告收入之路的挑战与反思 背景介绍 在互联网的浩瀚海洋中&#xff0c;网站复制现象屡见不鲜。近期&#xff0c;我尝试复制了一个名为网站B的核心代码&#xff0c;并成功发布了自己的网站。通过谷歌搜索引擎的优化&#xff08;SEO&#xff0…

Windows 10/11 设置锁屏密码的方法以及设置PIN密码

Windows 10/11 设置锁屏密码的方法 一、打开设置&#xff1a; 按 Win I 快捷键打开“设置”。 二、进入账户设置&#xff1a; 在设置窗口中点击“账户”。 三、选择登录选项&#xff1a; 在左侧菜单中选择“登录选项”。 四、添加密码&#xff1a; …

Python并发编程库:Asyncio的异步编程实战

Python并发编程库&#xff1a;Asyncio的异步编程实战 在现代应用中&#xff0c;并发和高效的I/O处理是影响系统性能的关键因素之一。Python的asyncio库是专为异步编程设计的模块&#xff0c;提供了一种更加高效、易读的并发编程方式&#xff0c;适用于处理大量的I/O密集型任务…

当软件质量遇上计划性报废:测试行业该如何应对?

那天&#xff0c;我像往常一样开车在路上&#xff0c;车窗外的风景飞快掠过。就在这时&#xff0c;我在听的一档播客里&#xff0c;突然提到了一个让我不得不停下来思考的词——“计划性报废”。这个词让我愣了一下&#xff0c;伴随着车轮的转动&#xff0c;我的思绪也随之转了…

【Seed-Labs】SQL Injection Attack Lab

Overview SQL 注入是一种代码注入技术&#xff0c;利用的是网络应用程序与数据库服务器之间接口的漏洞。当用户输入的信息在发送到后端数据库服务器之前没有在网络应用程序中进行正确检查时&#xff0c;就会出现这种漏洞。 许多网络应用程序从用户那里获取输入&#xff0c;然…

linux笔记(DNS)

一、概念 DNS&#xff08;Domain Name System&#xff09;DNS 是一种分布式网络目录服务&#xff0c;主要用于将人类易于记忆的域名&#xff08;如 www.example.com&#xff09;转换为计算机可识别的 IP 地址&#xff08;如 192.168.1.1&#xff09;。它就像是互联网的电话簿&a…

【计网】实现reactor反应堆模型 --- 框架搭建

没有一颗星&#xff0c; 会因为追求梦想而受伤&#xff0c; 当你真心渴望某样东西时&#xff0c; 整个宇宙都会来帮忙。 --- 保罗・戈埃罗 《牧羊少年奇幻之旅》--- 实现Reactor反应堆模型 1 前言2 框架搭建3 准备工作4 Reactor类的设计5 Connection连接接口6 回调方法 1 …

minikube 的 Kubernetes 入门教程--(五)

本文记录 Minikube 在 Kubernetes 上安装 WordPress 和 MySQL。 这两个应用都使用 PersistentVolumes 和 PersistentVolumeClaims 保存数据。 在深入这些步骤之前&#xff0c;先分享来自kubernetes.io教程。 链接>>使用持久卷部署 WordPress 和 MySQL | Kubernetes 获…

算法详解——链表的归并排序非递归解法

算法详解——链表的归并排序非递归解法 本文使用倍增法加上归并排序操作实现了对链表的快速排序&#xff0c;比起一般的递归式归并排序要节省空间并且实现要简单的多&#xff0c;比起一般的迭代式归并排序实现也要简单。 1. 题目假设 给定链表的头结点 head &#xff0c;请将其…

【网络-交换机】生成树协议、环路检测

路由优先级 路由优先级决定了在多种可达的路由类型中&#xff0c;哪种路由将被用来转发数据包。路由优先级值越低&#xff0c;对应路由的优先级越高&#xff0c;优先级值255表示对应的路由不可达。一般情况下&#xff0c;静态路由的优先级为1&#xff0c;OSPF路由优先级为110&a…

确定图像的熵和各向异性 Halcon entropy_gray 解析

1、图像的熵 1.1 介绍 图像熵&#xff08;image entropy&#xff09;是图像“繁忙”程度的估计值&#xff0c;它表示为图像灰度级集合的比特平均数&#xff0c;单位比特/像素&#xff0c;也描述了图像信源的平均信息量。熵指的是体系的混乱程度&#xff0c;对于图像而言&#…

数字后端零基础入门系列 | Innovus零基础LAB学习Day9

Module 16 Wire Editing 这个章节的学习目标是学习如何在innovus中手工画线&#xff0c;切断一根线&#xff0c;换孔&#xff0c;更改一条net shape的layer和width等等。这个技能是每个数字IC后端工程师必须具备的。因为项目后期都需要这些技能来修复DRC和做一些手工custom走线…

除草机器人算法以及技术详解!

算法详解 图像识别与目标检测算法 Yolo算法&#xff1a;这是目标检测领域的一种常用算法&#xff0c;通过卷积神经网络对输入图像进行处理&#xff0c;将图像划分为多个网格&#xff0c;每个网格生成预测框&#xff0c;并通过非极大值抑制&#xff08;NMS&#xff09;筛选出最…

ProCalun卡伦纯天然万用膏,全家的皮肤健康守护

受季节交替、生活环境变化、空气污染等方面因素的影响&#xff0c;加上作息不规律导致的免疫力降低&#xff0c;我们或多或少会出现一些如湿疹、痤疮、瘙痒之类的皮肤问题&#xff0c;且反复概率很高。很多人盲目用药&#xff0c;甚至诱发激素依赖性皮炎。所以近年来&#xff0…

Vue 自定义icon组件封装SVG图标

通过自定义子组件CustomIcon.vue使用SVG图标&#xff0c;相比iconfont下载文件、重新替换更节省时间。 子组件包括&#xff1a; 1. Icons.vue 存放所有SVG图标的path 2. CustomIcon.vue 通过icon的id索引对应的图标 使用的时候需要将 <Icons></Icons> 引到使用的…

wireshark工具使用

复制数据 1.右键展开整帧数据 2.复制“所有可见项目” mark标记数据 标记&#xff1a; 跳转&#xff1a; 保存成文件&#xff1a; 文件–>导出特定分组—>Marked packets only

【SpringCloud】SpringBoot集成Swagger 常用Swagger注解

概述&#xff1a;SpringBoot集成Swagger 常用Swagger注解 导语 相信无论是前端还是后端开发&#xff0c;都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力&#xff0c;经常来不及更新。其实无论是前…

Webserver(4.9)本地套接字的通信

目录 本地套接字 本地套接字 TCP\UDP实现不同主机、网络通信 本地套接字实现本地的进程间的通信&#xff0c;类似的&#xff0c;一般采用TCP的通信流程 生成套接字文件 #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h&…

[spring源码]spring配置类解析

解析配置类 在启动Spring时&#xff0c;需要传入一个AppConfig.class给ApplicationContext&#xff0c;ApplicationContext会根据AppConfig类封装为一个BeanDefinition&#xff0c;这种BeanDefinition我们把它称为配置类BeanDefinition AnnotationConfigApplicationContext a…

uni-app跨域set-cookie

set-cookie的值是作为一个权限控制的 首先&#xff0c;无论什么接口都会返回一个set-cookie&#xff0c;但未登录时&#xff0c;set-cookie是没有任何权限的 其次&#xff0c;登录接口请求时会修改set-cookie&#xff0c;并且在后续其他接口发起请求时&#xff0c;会在请求头…