当前位置: 首页 > news >正文

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

        内核中,针对每种驱动都设计了一套成熟的、标准的、典型的驱动框架,实现将相同部分实现,不同部分留出接口给工程师自行发挥。具有以下特点:①简单化;②标准化;③统一管控系统资源;④特定化接口函数与数据结构。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_TRIGGERSinit_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_TRIGGERSled_trigger_set_default(led_cdev);
#endifprintk(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_PMu32			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_PMif (chip->pm != NULL) {if (!chip->pm->save || !chip->pm->resume)printk(KERN_ERR "gpio: %s has missing PM functions\n",gc->label);} elseprintk(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.mrgr.cn/news/64174.html

相关文章:

  • 友思特应用 | FantoVision边缘计算:多模态传感+AI算法=新型非接触式医疗设备
  • 网易博客旧文----开发常用工具和软件列表
  • 【WPF】BackgroundWorker类
  • 如何有效提升MySQL大表分页查询效率(本文以一张900万条数据体量的表为例进行详细解读)
  • 小夜灯语音识别芯片,灯具声控方案,NRK3301
  • Windows 命令提示符(cmd)中输入 mysql 并收到错误消息“MySQL不是内部或外部命令,也不是可运行的程序或批处理文件?
  • 一篇文章带你快速理解MySQL中的内连接和外连接
  • 如何避免使用锁时出现的死锁问题?
  • leetcode35.搜索插入位置
  • 锁原理和使用
  • Python自动化运维:技能掌握与快速入门指南
  • 绿色积分如何结合商家联盟?打造线上线下消费生态
  • MMSegmentation测试阶段推理速度非常慢的一种可能原因
  • 优先级队列(PriorityQueue)
  • Visual Studio 2019下载安装使用教程
  • Php实现钉钉OA一级审批,二级审批
  • 河南省教育厅办公室关于举办2024年河南省高等职业教育技能大赛的通知
  • electron + vue 打包完成后,运行提示 electrion-updater 不存在
  • 最小支撑树MST
  • 数据结构-复杂度
  • phcharm贪吃蛇小游戏后续一(代码1,2,3前文已发)
  • CesiumJS 案例 P18:检测文本、删除所有文本、隐藏与显示文本、改变文本
  • 二维码中怎么存入文件?文件二维码活码的3步制作技巧
  • CAD图纸防泄密|哪些措施可以加密公司图纸?五个宝藏方法分享,2024必读!
  • 无人机维护保养、部件修理更换技术详解
  • Python 列表的定义语法