9.6.platform_driver_register是怎么进行驱动注册的?
设备树需要uboot传递给内核,参考设备树专题的视频,大体的流程是uboot把设备树编译成的.dtb文件的地址传递给内核,内核提取.dtb的根节点的compatible属性来匹配machine_desc,解析chosen节点、memory节点、#address-cells和#size-cells属性,为设备树分配内存。再去遍历整个.dts文件的节点将其转换成device_nodes并将properties填充到各个nodes,最终构建出device-tree。再根据compatible的转换规定将某些device_nodes转化成platform_devices,而不能转化成device_nodes的节点内核提供专门的处理函数。到这里设备树到内核的转换过程已经清楚了,回到leddrv.c中。先沿着leddrv.c的执行过程一路看下去。
下午看了2篇连续的博客,写的很好!但是看的很是晕乎!自己把函数调用过程过了一遍就清楚了,整理出来一张执行过程。
有篇博文写的很好记录下:https://blog.csdn.net/Richard_LiuJH/article/details/45825333
上图也解决了问题7_probe函数的形参是谁给的?
9.7.gpiod_get函数是怎么找到设备树中的led节点?
在probe函数中主要做了4件事:
a.从leds_my节点中获取led-gpios属性;
b.注册file_operations结构体,创建主设备;
c.创建设备类;
d.创建次设备;
目前有1个相关文档:Linux-4.9.88-Docs-gpio-consumer.txt,依次看下程序是如何执行的?先追踪下gpiod_get()的执行过程:
gpiod_get()
--->gpiod_get_index()
----->of_find_gpio()
------->of_get_named_gpiod_flags()
--------->of_parse_phandle_with_args()
--------->of_find_gpiochip_by_xlate()
----------->gpiochip_find()
------------->list_for_each_entry()
--------->of_xlate_and_get_gpiod_flags()
----------->chip->of_xlate() //of_xlate回调函数
----------->gpiochip_get_desc() //Get the GPIO descriptor corresponding to the given hw number for this chip.
参考博文:https://blog.csdn.net/Guet_Kite/article/details/82263003,通过回调函数leds-gpios中的3个参数提取出来。
static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec,
u32 *flags)
{
int pin, base;
base = PINS_PER_BANK * gpiospec->args[0];
pin = base + gpiospec->args[1];
if (pin > gc->ngpio)
return -EINVAL;
if (flags)
*flags = gpiospec->args[2];
return pin;
}
通过上面的一路调用可以将定义的leds-gpios信息提取出来。哪怎么去修改gpio呢?
9.8.gpiod_direction_output是怎么实现gpio初始化的?
在led_drv_open()中先设置了gpiod_direction_output(),具体代码执行过程不分析了,需要知道它的作用就是把gpio设置为输出引脚就可以了。参考:https://blog.csdn.net/u012830148/article/details/80513693。
设置为输出之后,就是设置输出值了。
9.9.gpiod_set_value是怎么实现设置的gpio的?
__gpio_set_value
-->gpiod_set_raw_value
-->_gpiod_set_raw_value
-->chip->set(chip, gpio_chip_hwgpio(desc), value);//调用set回调函数
参考:https://blog.csdn.net/Guet_Kite/article/details/82263003
static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip,
unsigned offset, int value)
{
struct sunxi_pinctrl *pctl = gpiochip_get_data(chip);
u32 reg = sunxi_data_reg(offset);
u8 index = sunxi_data_offset(offset);
unsigned long flags;
u32 regval;
spin_lock_irqsave(&pctl->lock, flags);
regval = readl(pctl->membase + reg);
if (value)
regval |= BIT(index);
else
regval &= ~(BIT(index));
writel(regval, pctl->membase + reg);
spin_unlock_irqrestore(&pctl->lock, flags);
}
里面基本就是:先读取GPIO寄存器的值,修改值,再写回去!
gpio_direction_output vs gpio_set_value之间的使用关系?参考:https://blog.csdn.net/fu_shuwu/article/details/6123333
gpio_set_value(port_num,0/1)一般只是在这个GPIO口的寄存器上写上某个值,至于这个端口是否设置为输出,它就管不了!而gpio_direction_output (port_num,0/1),在某个GPIO口写上某个值之后,还会把这个端口设置为输出模式。因此,有人也许就会建议,把gpio_set_value这个函数直接去掉不用,是否可以,显然是可以的。
但是为什么系统还要用呢?我个人分析是,系统开发人员在要结合这两者来使用,以便提高效率。一般某个端口设置好了输入与输出模式后,最好不要经常变动。首先要调用gpio_direction_output(),以后要设置高低电平时,直接使用gpio_set_value()就可以了,这样可以省却再次调用设置输出模式的操作,从而提高运行效率!
9.10.register_chrdev怎么把100ask_led注册到内核中的?
probe函数中获取了.dts中的gpio之后进行register_chrdev,为什么要去注册字符串设备呢???设备和gpio是什么关系?在这个100sak_led中调用gpio资源去完成一些操作?
register_chrdev()
---->__register_chrdev(); //create and register a cdev occupying a range of minors
------>__register_chrdev_region(major, baseminor, count, name); //Register a single major with a specified minor range.
---------->kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
---------->major_to_index(major);
------>cdev = cdev_alloc(); //allocate a cdev structure
------>cdev_add(cdev, MKDEV(cd->major, baseminor), count); //add a char device to the system
有一篇很好的博文可以参考下,https://blog.csdn.net/lizuobin2/article/details/52695533###,就是申请主次设备号,申请cdev结构体,添加到内核中。
9.11为什么要创建设备类class?class_create是怎么在内核中创建的class?
创建了chrdev之后为什么还要创建class_create?是不是便于管理?先看下class_create的过程:
class_create();
---->__class_create(); //create a struct class structure
{
Kzalloc();
cls->name = name;
cls->owner = owner;
__class_register();
{
Kzalloc();
add_class_attrs()
{
class_get();
class_create_file();
}
class_put();
}
}
参考:https://www.cnblogs.com/LxwEmbedded/p/4854714.html
9.12.用register_chrdev要创建主设备,device_create创建次设备,怎么创建的?
内核同时提供了class_create(…) 函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。参考: https://blog.csdn.net/lizuobin2/article/details/52695533
device_create - creates a device and registers it with sysfs
{
device_create_vargs()
{
device_create_groups_vargs()
{
Kzalloc();
device_initialize();
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->groups = groups;
dev_set_drvdata();
kobject_set_name_vargs();
device_add();
put_device();
}
}
}
register_chrdev-->class_create--->device_create,
之前写的字符类设备驱动,没有自动创建设备节点,因为只使用了register_chrdev()函数,只是注册了这个设备。然后在系统启动后,就要自己创建设备节点mknod,这样虽然是可行的,但是比较麻烦。于是想在__init函数里面,自动创建设备节点。经过查阅资料,发现创建设备节点使用了两个函数 class_create()和class_device_create(),当然在__exit()函数里,要使用class_destory()和class_device_desotry()注销创建的设备节点!参考:https://www.jianshu.com/p/a99c278fc6e4
9.13.假如有2个led1/2呢?怎么去区分led0和led1的?
led1-gpio = <>;
led2-gpio = <>;
使用gpiod_get()分别获取!
居然就这么快速的结束了好吧!
前面信誓旦旦要写出好的博文,哈,发现自己那点水平读内核代码太吃力了,只有去借鉴别人写的博文了。期间参考了好多大牛的博文,不一一列写了,该加的参考链接都贴出来了,向他们学习!加油!
现在再来看这个图感觉思路更清晰了!
下面学习中断的内容,裸机手册也发布了,很多东西都可以学习了!加油!