Linux内核gpiolib注册建立过程
1、相关的数据结构
struct s3c_gpio_chip { // 这个结构体是三星在移植gpiolib时封装的一个结构体 用来描述一组gpio端口信息
struct gpio_chip chip;
struct s3c_gpio_cfg *config; // 三星封装的用来配置一个gpio端口的结构体 主要是上下拉模式配置
struct s3c_gpio_pm *pm; // 这个是电源管理相关的数据结构
void __iomem *base; // gpio相关寄存器的虚拟基地址
int eint_offset;
spinlock_t lock;
#ifdef CONFIG_PM
u32 pm_save[];
#endif
};
struct gpio_chip { // 内核提供的用来描述一组gpio端口信息的结构体
const char *label; // 一个标号 也就是gpio端口的名字
struct device *dev; // device指针指向这个gpio端口设备
struct module *owner;
int (*request)(struct gpio_chip *chip, // 请求gpio 申请
unsigned offset);
void (*free)(struct gpio_chip *chip, // 释放
unsigned offset);
int (*direction_input)(struct gpio_chip *chip, // 用于将gpio配置为输入模式
unsigned offset);
int (*get)(struct gpio_chip *chip, // 获取gpio电平状态
unsigned offset);
int (*direction_output)(struct gpio_chip *chip, // 用于将gpio配置为输出模式
unsigned offset, int value);
int (*set_debounce)(struct gpio_chip *chip, // 用于消抖
unsigned offset, unsigned debounce);
void (*set)(struct gpio_chip *chip, // 设置gpio的电平
unsigned offset, int value);
int (*to_irq)(struct gpio_chip *chip, // 如果该gpio是一个外部中断源,则使用这个函数来获取对应的中断号
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int base; // 这组gpio端口的 基准编号 (这个编号是内核设计的)
u16 ngpio; // 这组端口的gpio数量
const char *const *names;
unsigned can_sleep:;
unsigned exported:;
};
struct s3c_gpio_cfg {
unsigned int cfg_eint; // 用于外部中断源时的一个配置值
s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs); // 读取下拉状态时gpio的电流
int (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs, // 设置下拉状态gpio电流
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); // 获取gpio的当前配置
int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs, // 设置gpio的当前配置
unsigned config);
};
2、函数调用关系图
smdkc110_map_io
s5pv210_gpiolib_init
samsung_gpiolib_add_4bit_chips
samsung_gpiolib_add_4bit
s3c_gpiolib_add
gpiochip_add // 这个函数就是内核的gpiolib驱动框架提供的用来注册gpiolib的函数
3、函数详解
smdkc110_map_io:
static void __init smdkc110_map_io(void)
{
s5p_init_io(NULL, , S5P_VA_CHIPID); // 静态物理地址到虚拟地址的映射初始化
s3c24xx_init_clocks(); // 系统时钟初始化
s5pv210_gpiolib_init(); // gpiolib管理器初始化
s3c24xx_init_uarts(smdkc110_uartcfgs, ARRAY_SIZE(smdkc110_uartcfgs));
s5p_reserve_bootmem(smdkc110_media_devs, ARRAY_SIZE(smdkc110_media_devs));
#ifdef CONFIG_MTD_ONENAND
s5pc110_device_onenand.name = "s5pc110-onenand";
#endif
#ifdef CONFIG_MTD_NAND
s3c_device_nand.name = "s5pv210-nand";
#endif
s5p_device_rtc.name = "smdkc110-rtc";
}
s5pv210_gpiolib_init:
__init int s5pv210_gpiolib_init(void)
{
struct s3c_gpio_chip *chip = s5pv210_gpio_4bit; // 4bit表示的是一个gpio使用4位来配置描述 s5pv210_gpio_4bit是一个struct s3c_gpio_chip数组,是三星移植时写好的
int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit); // 获取端口数量(注意一组端口和一个具体的gpio)
int i = ; for (i = ; i < nr_chips; i++, chip++) {
if (chip->config == NULL) // 如果我们的gpio端口没有 配置方法 则使用 gpio_cfg 进行默认配置
chip->config = &gpio_cfg;
if (chip->base == NULL) // 如果我们的gpio端口结构体中没有填充 基准编号 则使用下面进行填充
chip->base = S5PV210_BANK_BASE(i);
} samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips); // 添加gpiolib return ;
}
samsung_gpiolib_add_4bit_chips:
void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip,
int nr_chips)
{
for (; nr_chips > ; nr_chips--, chip++) { // 对于每一个gpio端口组都调用下面的两个函数
samsung_gpiolib_add_4bit(chip); // 给gpio端口配置输入输出模式配置方法
s3c_gpiolib_add(chip); // 最终是通过这个函数去添加gpiolib
}
} void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip)
{
chip->chip.direction_input = samsung_gpiolib_4bit_input; // 给gpio端口加入配置gpio为输入模式的方法
chip->chip.direction_output = samsung_gpiolib_4bit_output; // 给gpio端口加入配置gpio为输出模式的方法
chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); // 电源管理相关
}
s3c_gpiolib_add:
__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
{
struct gpio_chip *gc = &chip->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); // 最终又是通过这个函数去注册我们的gpiolib管理器 这个函数就是内核gpiolib驱动框架提供的了
if (ret >= )
s3c_gpiolib_track(chip);
}

总结:
其实我们的gpiolib虽然说是一个gpio的管理者,同样他也是一个设备,在/sys目录下是能够去通过设备的属性方法对具体的gpio进行设置的,三星在移植gpiolib时是使用内核
提供的gpiolib驱动框架来实现的。
其实这个注册就是将我们的封装了一个GPIO端口的所有信息的chip结构体变量挂接到内核gpiolib模块定义的一个gpio_desc数组中的某一个格子中。
Linux内核gpiolib注册建立过程的更多相关文章
- 结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程
结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程 目录 结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程 一. 实验准备 二. 实验过程 I 分析中断上下文的切换 ...
- linux设备树的建立过程
为了阐明表示总线.设备和设备驱动程序的各个数据结构之间彼此的关联,它们的注册过程是很有必要的.顺序一定是如下:(1)注册总线---bus_register:(2)注册设备device_register ...
- Linux内核树的建立-基于ubuntu系统
刚看 O'REILLY 写的<LINUX 设备驱动程序>时.作者一再强调在编写驱动程序时必须 建立内核树.先前的内核只需要有一套内核头文件就够了,但因为2.6的内核模块吆喝内核源码树中的目 ...
- Xilinx-Zynq Linux内核源码编译过程
本文内容依据http://www.wiki.xilinx.com网址编写,编译所用操作系统为ubuntu 14 1.交叉编译环境的安装配置 1)http://www.wiki.xilinx.com/I ...
- 代码学习-Linux内核网卡收包过程(NAPI)【转】
转自:https://blog.csdn.net/crazycoder8848/article/details/46333761 版权声明:本文没有任何版权限制,任何人可以以任何方式使用本文. htt ...
- Linux内核源码树建立加载hello模块
在加载模块之前,书中说要先建立内核源码树,那么,如何建立内核源码树呢? 首先,要先知道你的OS的内核版本,用uname -r可以查得到 在/url/src/目录下可以看到对应的版本目录 如果没有可以用 ...
- Linux内核,文件系统移植过程中出现的一些问题与解决办法
1.bootm地址和load address一样 此种情况下,bootm不会对uImage header后的zImage进行memory move的动作,而会直接go到entry point开始执行. ...
- Linux 内核设备注册
通常的注册和注销函数在: int device_register(struct device *dev); void device_unregister(struct device *dev); 我们 ...
- Linux 内核总线注册
如同我们提过的, 例子源码包含一个虚拟总线实现称为 lddbus. 这个总线建立它的 bus_type 结构, 如下: struct bus_type ldd_bus_type = { .name = ...
随机推荐
- python使用set来去重碰到TypeError: unhashable type
新版:Python 的 unhashable type 错误分析及解决 python使用set来去重是一种常用的方法. 一般使用方法如下: # int a = [1, 2, 3, 4, 5, 1, 2 ...
- pyDay13
内容来自廖雪峰的官方网站. 1.把list.dict.str等Iterable变成Iterator可以使用iter()函数 >>> L = iter([1, 2, 3, 4, 5, ...
- Python面试题目之字典排序
按照字典的内的年龄排序 待排序的字典 d1 = [ {'name':'alice', 'age':38}, {'name':'bob', 'age':18}, {'name':'Carl', 'age ...
- STM32 DMA简述
STM32 DMA简述 DMA (Direct Memory Access) 直接内存存储器,在做数据传输时能够大大减轻CPU的负担. DMA的作用 DMA提供了一个关于数据的高数传输通道,这个通道不 ...
- Ubuntu安装配置rclone(Onedrive应用)
rclone安装 命令行安装 脚本安装 curl https://rclone.org/install.sh | sudo bash # 或者 curl https://rclone.org/inst ...
- duilib : 滑动显示的窗口实现以及 悬浮窗 (转载)
1. vc 判断窗口是否显示 BOOL IsWindowVisible(HWND hWnd); 2.悬浮窗 http://blog.csdn.net/lincyang/article/details ...
- k8s 学习笔记 etcd
1. Etcd Etcd是Kubernetes集群中的一个十分重要的组件,用于保存集群所有的网络配置和对象的状态信息.在后面具体的安装环境中,我们安装的etcd的版本是v3.1.5,整个kuberne ...
- Binary Tree Zigzag Level Order Traversal,z字形遍历二叉树,得到每层访问的节点值。
问题描述: Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from l ...
- shell fold限制文件行宽
将文本的行限制到特定的宽 这个用 echo 命令发送的文本用 -w 选项分解成块. 在这个例子中,我们设定了行宽为12个字符. 如果没有字符设置,默认是80. 增加的 -s 选项将让 fold 分解到 ...
- window 常用指令
1. window 快捷键 Win+D: / 点击右下角右下角 返回到桌面Win+E: 打开“资源管理器” / 调出磁盘窗口Win+L: 锁定当前用户. / 锁屏Win+R: 打开“运行”对话框.Wi ...