gpio子系统和pinctrl子系统(上)
前言
随着内核的发展,linux驱动框架在不断的变化。很早很早以前,出现了gpio子系统,后来又出现了pinctrl子系统。在网上很难看到一篇讲解这类子系统的文章。就拿gpio操作来说吧,很多时候都是简单的调用gpio子系统提供的api,然后根据sdk说明文档写明的gpio号传参数,至于里面的工作过程对于驱动工程师而言就像个黑盒子。当我们自己设计的板子和demo板有很大变动时,问题就出现了。首先遇到的是怎么配置pin(是基于设备树还是不基于设备树,基于设备树的话,怎么修改设备树关于pinctrl部分的内容,里面各个字段什么意思,怎么改),然后是在哪里配置pin(内核部分有哪些需要相应修改,还是不需要一点修改呢),接着就是怎么调试等等。我想只有清楚了尽量多的gpio子系统和pinctrl子系统细节,才会更快更好的完成这些工作。有些平台的实现没有使用内核提供的pinctrl子系统,而是继续采用在内核提供pinctrl子系统前自己实现的那套机制来pinmux操作,如omap,有些平台则基于pinctrl子系统来实现pinmux、pinconf的控制。本文以gpio子系统为入口慢慢深入,最后分析pinctrl子系统。
如果有错误的地方,欢迎大家直接指出
gpio子系统
gpio子系统帮助我们管理整个系统gpio的使用情况,同时通过sys文件系统导出了调试信息和应用层控制接口。它内部实现主要提供了两类接口,一类给bsp工程师,用于注册gpio chip(也就是所谓的gpio控制器驱动),另一部分给驱动工程师使用,为驱动工程师屏蔽了不同gpio chip之间的区别,驱动工程师调用的api的最终操作流程会导向gpio对应的gpio chip的控制代码,也就是bsp的代码。
gpio子系统核心实现分析
gpio子系统的内容在drivers/gpio文件夹下,主要文件有:
devres.c
gpiolib.c
gpiolib-of.c
gpiolib-acpi.c
gpio-xxx.c
devres.c是针对gpio api增加的devres机制的支持,devres机制讲解请参考另一篇博文,gpiolib.c是gpio子系统的核心实现,gpiolib-of.c是对设备树的支持,gpiolib-acpi.c和acpi相关,不分析(acpi还未深入了解_),最后情景分析的时候,会找一个平台的gpio-xxx.c来分析。
从驱动工程师使用的api开始分析吧!也分两代,legacy的api主要会用到的接口有(现在推荐采用新的,基于描述符的api):
gpio_request
、gpio_free
gpio_direction_input
、gpio_direction_output
gpio_to_irq
gpio_export
一般的流程:
//请求一个/一组gpio
gpio_request/devm_gpio_request、gpio_request_one/devm_gpio_request_one、gpio_request_array ---------<1>
...
//设置gpio方向为输入/输出
gpio_direction_input或者gpio_direction_output ---------<2>
...
//将该gpio通过sys文件系统导出,应用层可以通过文件操作gpio
gpio_export ---------<3>
...
//如果gpio为输入,获取gpio值,如果gpio为输出,可以设置gpio高低电平
gpio_get_value、gpio_set_value ---------<4>
...
//将gpio转为对应的irq,然后注册该irq的中断handler
request_irq(gpio_to_irq(gpio_num)...) ---------<5>
...
//释放请求的一个或者一组gpio
gpio_free/devm_gpio_free、gpio_free_array ---------<6>
...
下面一个个来分析吧!
<1> 以gpio_request
为例,gpio_request_one
、gpio_request_array
是它的扩展,devm_
为前缀的是gpio devres机制的实现。
int gpio_request(unsigned gpio, const char *label)
参数为gpio号和为该gpio指定的表签名,具体gpio号是多少,可以通过sdk开发包说明文档查看,或者查看设备树文件,再或者基于bank数量推算(当然,这样可能不准),实在没办法的话,瞄一眼gpio chip驱动的代码吧!gpio_request
主要做了以下动作:
- 检查是否已经被申请,没有的话,标记为已申请
- 填充label到该pin数据结构,用于debug
- 如果chip driver提供了request回调,调用它
- 如果chip driver提供了get_direction回调,调用它,通过它更新pin数据结构,标明gpio方向
gpio_request_one
多一个flags参数,通过该参数,可以指定GPIOF_OPEN_DRAIN
、GPIOF_OPEN_SOURCE
、GPIOF_DIR_IN
、GPIOF_EXPORT
等标志,如果指定了GPIOF_DIR_IN
,那么后面就不需要自己再额外调用gpio_direction_input
或者gpio_direction_output
了,如果指定了GPIOF_EXPORT
,后面就不需要自己调用gpio_export
了。
gpio_request_array
是对gpio_request_one
的封装,用于处理同时申请多个gpio的情形。
<2> gpio_direction_input
或者gpio_direction_output
用来设置该gpio为输入还是输出,它们主要是回调gpio chip driver提供的direction_input
或者direction_output
来设置该gpio寄存器为输入、输出。
<3> gpio_export
主要用于调试,它会将该gpio的信息通过sys文件系统导出,这样应用层可以直接查看状态、设置状态等。
<4> gpio_get_value
或者gpio_set_value
和input、output类似,如果为输入,获取该gpio的值,如果为输出,设置该gpio的值,内部也是调用gpio chip driver提供的get、set。
<5> gpio_to_irq
用于获取该gpio对应的中断号,这个需要设备树里的该gpio节点描述使用哪个中断号(并不是所有的gpio都可以触发中断的)。它里面的实现就是回调gpio chip driver提供的to_irq
。
<6> gpio_free
就不用说了啦,gpio_request
的逆操作。
要使用以上接口,需要#include <linux/gpio.h>,且还有一组用于允许睡眠场景的api没有给出,更多相关的说明可以参考Documentation/gpio/gpio-legacy.txt
上面的分析没有深入的代码层,之所以没有深入分析,是因为打算放到后面分析基于描述符api时啦!其实,legacy gpio 大部分api就是基于描述符api来实现的。最新的,基于描述符一般的流程:
...
//请求第一个/指定某一个gpio desc,该返回值用于后面的操作
gpiod_get/devm_gpiod_get、gpiod_get_index/devm_gpiod_get_index ---------<1>
...
//设置gpio方向为输入/输出
gpiod_direction_input或者gpiod_get_direction ---------<2>
...
//将该gpio通过sys文件系统导出,应用层可以通过文件操作gpio
gpiod_export ---------<3>
...
//如果gpio为输入,获取gpio值,如果gpio为输出,可以设置gpio高低电平
gpiod_get_value或者gpiod_set_value ---------<4>
...
//将gpio转为对应的irq,然后注册该irq的中断handler
request_irq(gpiod_to_irq(gpio_desc)...) ---------<5>
...
//释放请求的一个或者一组gpio
gpiod_put/devm_gpiod_put ---------<6>
...
还是下面一个个来分析吧!
<1> gpiod_get
内部的处理和gpio_request
一样,不过输入参数变为char *con_id,这个需要从设备树文件里查看到,除此之外,我们还可以在设备树文件里添加参数(GPIO_ACTIVE_LOW
、GPIO_OPEN_DRAIN
、GPIO_OPEN_SOURCE
)来触发该接口内部设置gpio,具体的参数格式和具体的gpio chip driver有关,一般可以在/Documentation/devicetree/bindings/gpio里找到对应平台的。举个例子:
row-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH /* Bank1, pin25 */
&gpio1 26 GPIO_ACTIVE_HIGH /* Bank1, pin26 */
&gpio1 27 GPIO_ACTIVE_HIGH>; /* Bank1, pin27 */
struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id)
对应上面的设备树描述,con_id就是row了,获取的也会是第一个设置,即gpio1 25 GPIO_ACTIVE_HIGH
,如果想获取第二个设置,我们得通过gpiod_get_index
,并将输入参数idx设置为1。
<2> gpiod_direction_input
和gpio_direction_input
功能一样,实际上gpio_direction_input
仅仅简单封装了gpiod_direction_input
,不再描述。
<3> gpiod_export
同上
<4> gpiod_get_value
同上
<5> gpiod_to_irq
同上
<6> gpiod_put
同上
要使用以上接口,需要#include <linux/gpio/consumer.h>,且还有一组用于允许睡眠场景的api没有给出,更多相关的说明可以参考Documentation/gpio/consumer.txt
下面简单分析下gpio子系统内部实现:
1> gpio chip driver的初始化
gpio子系统提供了两层接口,一层给上层驱动工程师调用,一层给下层bsp工程师调用。上层使用前,当然先得bsp工程师完成对应的动作。先看一张网上的截图:
bsp工程师通过gpiochip_add将gpio chip添加到gpio子系统中,下面就分析它:
int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = 0;
unsigned id;
int base = chip->base;
//如果指定了base,也就是指定了启示gpio号,需要校验下chip的所有gpio是否有效
//这里会用到ARCH_NR_GPIOS宏,它可以在配置的时候,通过CONFIG_ARCH_NR_GPIO修改,
//否则采用默认值256
if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
&& base >= 0) {
status = -EINVAL;
goto fail;
}
spin_lock_irqsave(&gpio_lock, flags);
//如果没有指定base,那么需要基于该chip的gpio数量在系统支持的gpio范围里找一段区间给该chip
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
goto unlock;
}
chip->base = base;
}
//到这里的时候,说明一切正常,把它加入到全局的gpiochip链表中去吧,注意,加入的时候会基于base排序
//这也保证了gpiochip_find_base的实现
status = gpiochip_add_to_list(chip);
//如果加入成功,最后一步就是初始化该chip对应的那些gpio了
if (status == 0) {
chip->desc = &gpio_desc[chip->base];
for (id = 0; id < chip->ngpio; id++) {
struct gpio_desc *desc = &chip->desc[id];
//将该chip对应的那些gpio对应的数据结构desc初始化,指向拥有它的chip
desc->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,
* and in case chip->get_direction is not set,
* we may expose the wrong direction in sysfs.
*/
//如果chip driver没有指定chip->direction_input,意味着不是输入,那就设置为输出咯
desc->flags = !chip->direction_input
? (1 << FLAG_IS_OUT)
: 0;
}
}
spin_unlock_irqrestore(&gpio_lock, flags);
#ifdef CONFIG_PINCTRL
//这里在配置了pinctrl的时候,会初始化它,后面会用到
INIT_LIST_HEAD(&chip->pin_ranges);
#endif
//初始化设备树相关的信息,后面会详细讲一下这部分
of_gpiochip_add(chip);
//acpi方式的先忽略吧
acpi_gpiochip_add(chip);
if (status)
goto fail;
//将该gpiochip导出到sys,用于调试和应用层直接操作
status = gpiochip_export(chip);
if (status)
goto fail;
pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return 0;
unlock:
spin_unlock_irqrestore(&gpio_lock, flags);
fail:
/* failures here can mean systems won't boot... */
pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return status;
}
还是以zynq平台为例,看看输入参数struct gpio_chip *chip的初始化过程:
chip->label = "zynq_gpio";
chip->owner = THIS_MODULE;
chip->dev = &pdev->dev;
chip->get = zynq_gpio_get_value;
chip->set = zynq_gpio_set_value;
chip->request = zynq_gpio_request;
chip->free = zynq_gpio_free;
chip->direction_input = zynq_gpio_dir_in;
chip->direction_output = zynq_gpio_dir_out;
chip->to_irq = zynq_gpio_to_irq;
chip->dbg_show = NULL;
chip->base = 0; /* default pin base */
chip->ngpio = ZYNQ_GPIO_NR_GPIOS;
chip->can_sleep = 0;
get、set、request、free、direction_input
、direction_output
、to_irq这几个回调应该很清楚了,也知道是哪几个接口调用的它们了吧,另外几个关键的参数base和ngpio在上面的gpiochip_add
里应该也已经清楚了它们的重要作用了吧!
of_gpiochip_add
会处理gpio chip设备树相关的东西:
void of_gpiochip_add(struct gpio_chip *chip)
{
if ((!chip->of_node) && (chip->dev))
chip->of_node = chip->dev->of_node;
if (!chip->of_node)
return;
//如果没有指定of_xlate,那给一个默认的吧!of_xlate用于解析设备树里gpio属性
//不同的soc可能需要不同的解析方法,但是如果没有什么特别,那就用默认的解析吧
//当前设备树的节点里支持两种格式的属性,一种是xxx-gpios,另一种就直接是gpios
//如前文中用到的row-gpios就是一种
if (!chip->of_xlate) {
chip->of_gpio_n_cells = 2;
chip->of_xlate = of_gpio_simple_xlate;
}
//这一步就是与pinctrl子系统打交道啦!后面会简单分析下它都做了什么
//等到讲pinctrl子系统的时候就更加清楚了
of_gpiochip_add_pin_range(chip);
//增加该节点引用计数
of_node_get(chip->of_node);
}
关于of_gpiochip_add_pin_range
,看起来很复杂,其实也就那样啦_:
static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
{
struct device_node *np = chip->of_node;
struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
int index = 0, ret;
const char *name;
static const char group_names_propname[] = "gpio-ranges-group-names";
struct property *group_names;
if (!np)
return;
//查找该gpiochip里的gpio-ranges-group-names属性
group_names = of_find_property(np, group_names_propname, NULL);
for (;; index++) {
//提取该gpiochip设备树信息里的gpio-ranges属性,按3个字段为一组解析,解析后
//放到pinspec中,这个gpio-ranges可能存在多组,因此用index来控制,一组一组来处理
//举个例子:
//gpio0: gpio@ffc40000 {
// compatible = "renesas,gpio-r8a7778", "renesas,gpio-rcar";
// reg = <0xffc40000 0x2c>;
// interrupt-parent = <&gic>;
// interrupts = <0 103 IRQ_TYPE_LEVEL_HIGH>;
// #gpio-cells = <2>;
// gpio-controller;
// gpio-ranges = <&pfc 0 0 32>;
// #interrupt-cells = <2>;
// interrupt-controller;
//};
//gpio-ranges属性是gpio子系统规定的属性,更详细的信息参考
//Documentation/devicetree/bindings/gpio/gpio.txt
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3,
index, &pinspec);
if (ret)
break;
//获取到pinspec.np(也就是pinctrl对应的节点)对应的pctldev
//注意,这个时候的gpiochip属于pinctrl的一个client
pctldev = of_pinctrl_get(pinspec.np);
if (!pctldev)
break;
//这部分的解析也是Documentation/devicetree/bindings/gpio/gpio.txt里有详细
//说明的,如果最后一个参数为0,表示代表一个gpio group,否则,第一个参数意思是gpio号起始值
//第二个参数意思是与之对应的pin号的起始值,最后一个参数表示连续多少各
if (pinspec.args[2]) {
if (group_names) {//这里仅仅是校验,如果不是表示gpio group,那么对应的
//gpio-ranges-group-names属性里对应的字段应该为""
ret = of_property_read_string_index(np,
group_names_propname,
index, &name);
if (strlen(name)) {
pr_err("%s: Group name of numeric GPIO ranges must be the empty string.\n",
np->full_name);
break;
}
}
/* npins != 0: linear range */
//到这里说明确定不是gpio group啦,那就将它们加入到pinctrl子系统管理起来吧
//,这里后面分析pinctrl子系统的时候再回过头来分析它
ret = gpiochip_add_pin_range(chip,
pinctrl_dev_get_devname(pctldev),
pinspec.args[0],
pinspec.args[1],
pinspec.args[2]);
if (ret)
break;
} else {
/* npins == 0: special range */
//校验,为pin gourp时,gpio 子系统要求第一个参数必须要0
if (pinspec.args[1]) {
pr_err("%s: Illegal gpio-range format.\n",
np->full_name);
break;
}
//如果是pin group,那么gpio-ranges-group-names属性必须要有,通过它来指定
//那个group
if (!group_names) {
pr_err("%s: GPIO group range requested but no %s property.\n",
np->full_name, group_names_propname);
break;
}
//获取gpio-ranges-group-names属性里第index对应的字串(也就是组名)
ret = of_property_read_string_index(np,
group_names_propname,
index, &name);
if (ret)
break;
if (!strlen(name)) {
pr_err("%s: Group name of GPIO group range cannot be the empty string.\n",
np->full_name);
break;
}
//一切ok,按group将它们加入到pinctrl子系统管理起来吧,这里后面分析pinctrl子系统的
//时候再回过头来分析它
ret = gpiochip_add_pingroup_range(chip, pctldev,
pinspec.args[0], name);
if (ret)
break;
}
}
}
总结一下,gpiochip_add总共做了以下主要事情:
- 将chip添加到全局的gpio chip链表中,用于gpio chip冲突处理和gpio管理
- 将该gpio chip对应的那段gpio都初始化
- 初始化设备树相关的信息,用于后面的属性解析及向pinctrl子系统同步下
- 导出到sys
还有一点需要补充,从这里我们也会发现pinctrl由gpio子系统调用了,驱动工程师以及bsp工程师都不用关心,多好啊!后面会再次看到,大部分都是内核的通用部分代码处理了pinctrl,驱动工程师以及bsp工程师仍然不用关心,只需要关心设备树里pinctrl相关的部分,不过我们也得知其所以然啊,不然改动那些和pinctrl相关的总是心理没底啊!!!和bsp驱动工程师相关的部分就一个函数,没其他的了。不过准备chip里面的那些字段也够bsp工程师忙一阵子了吧_
下面开始分析驱动工程师调用的gpio_request
的过程,它的核心实现是调用gpiod_request
,gpiod_get
和gpiod_get_index
的核心实现处理也调用了gpiod_request
,但还做了一些其他事情,如解析gpios或者xxx-gpios属性获取设备树里指定的flags以及通过指定的gpio号获取到对应的desc(在上面的gpiochip_add
过程中,我们有看到了desc的初始化),当然解析的过程会用到gpiochip_add
里说过的of_xlate
if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) {
dev_dbg(dev, "using device tree for GPIO lookup\n");
desc = of_find_gpio(dev, con_id, idx, &flags);
} else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) {
dev_dbg(dev, "using ACPI for GPIO lookup\n");
desc = acpi_find_gpio(dev, con_id, idx, &flags);
}
以及根据设备树里指定的flags设置desc,比如是否低电平有效,是否开漏输出等
if (flags & GPIO_ACTIVE_LOW)
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
if (flags & GPIO_OPEN_DRAIN)
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
if (flags & GPIO_OPEN_SOURCE)
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
gpio_request
里面做的事情前面已经说的很清楚了,下面结合代码再回顾下:
static int gpiod_request(struct gpio_desc *desc, const char *label)
{
int status = -EPROBE_DEFER;
struct gpio_chip *chip;
//检查desc是否有效,gpio_request会根据传入的gpio号在全局的desc里定位到desc
//gpiod_get和gpiod_get_index则是通过解析设备树信息,提取里面的gpio号,然后再转换
if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}
//获取该desc的拥有者,即gpio chip(这个初始化在gpiochip_add里已经分析过了)
chip = desc->chip;
if (!chip)
goto done;
if (try_module_get(chip->owner)) {//增加下chip的引用
status = __gpiod_request(desc, label);//核心动作都是在__gpiod_request里完成,就不再跟进去分析了^_^
if (status < 0)
module_put(chip->owner);
}
done:
if (status)
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}
总结一下,驱动工程师用gpio_request
等api请求指定的gpio,这些gpio其实都是由bsp工程师调用gpiochip_add
添加的,gpio_request
会标记它,防止被不同的模块重复引用该gpio,当然也会告诉下gpio chip(如果chip想要被通知的话)
2> gpio_to_irq
/gpiod_to_irq
的过程分析
通过前面的分析,应该对gpio子系统有了一个比较完全的了解吧!其他的api应该也都能理解(猜测到会做什么),这里在对和gpio相关的中断分析下
gpio_to_irq
只是简单的对gpiod_to_irq
进行封装,主要看gpiod_to_irq
:
int gpiod_to_irq(const struct gpio_desc *desc)
{
struct gpio_chip *chip;
int offset;
if (!desc)
return -EINVAL;
chip = desc->chip;
//获取该gpio号对应于该chip的offset,由于chip的起始号不一定就开始与系统全局desc的起点
//而该chip的处理又都是基于0开始的,所以得转一下啦
offset = gpio_chip_hwgpio(desc);
//调用芯片驱动提供的to_irq,如果chip driver不支持中断,那么to_irq应该就是空咯,说明不支持
//从这里应该也清楚的知道了gpio号与中断号的对应关系是由chip driver处理的
//驱动工程师用的gpio号都是全局的,bsp工程师用的gpio号都是局部的
return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO;
}
还是看一下zynq的to_irq的实现吧!说到这里,会牵涉到中断子系统(貌似我还没写过中断子系统的文章),如果不太清楚,就跳过吧!
static int zynq_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
return irq_find_mapping(irq_domain, offset);
}
看起来貌似很简单吧!其实这也要归功于中断子系统的功劳啦!一般有中断控制器功能的设备会在该设备驱动里调用irq_domain_add_xxx
接口来注册一个irq domain
,然后会调用irq_create_mapping
来创建irq号与硬件号的对应关系,里面会分配期望的irq desc,分配的时候,该中断控制器对应的中断号就确定了,然后会绑定该irq号与传入的硬件号。这就为后面的irq_find_mapping
提供了支持,通过硬件号获取对应的irq号。这里再多说一句,大部分gpio chip同时也是一个中断控制器,写bsp的苦逼们不仅要gpiochip_add
还要irq_domain_add
等等操作,于是乎,gpio子系统解救他们来了,提供了一个接口gpiochip_irqchip_add
,这接口完成后和gpio中断相关的所有事情,于是乎,bsp驱动工程师们也只需要调用两个接口了_多么美好!
未完,待续!
2015年7月
gpio子系统和pinctrl子系统(上)的更多相关文章
- gpio子系统和pinctrl子系统(下)
情景分析 打算从两个角度来情景分析,先从bsp驱动工程师的角度,然后是驱动工程师的角度,下面以三星s3c6410 Pinctrl-samsung.c为例看看pinctrl输入参数的初始化过程(最开始的 ...
- Linux驱动之GPIO子系统和pinctrl子系统
前期知识 1.如何编写一个简单的Linux驱动(一)--驱动的基本框架 2.如何编写一个简单的Linux驱动(二)--设备操作集file_operations 3.如何编写一个简单的Lin ...
- gpio子系统和pinctrl子系统(中)
pinctrl子系统核心实现分析 pinctrl子系统的内容在drivers/pinctrl文件夹下,主要文件有(建议先看看pinctrl内核文档Documentation/pinctrl.txt): ...
- 【转】Linux內核驅動之GPIO子系統(一)GPIO的使用 _蝸牛
原文网址:http://tc.chinawin.net/it/os/article-2512b.html 一 概述 Linux內核中gpio是最簡單,最常用的資源(和interrupt ,dma,ti ...
- Pinctrl子系统之一了解基础概念【转】
转自:https://blog.csdn.net/u012830148/article/details/80609337 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请 ...
- 适用于Linux 2的Windows子系统上的CUDA
适用于Linux 2的Windows子系统上的CUDA Announcing CUDA on Windows Subsystem for Linux 2 为了响应大众的需求,微软在2020年5月的构建 ...
- Linux驱动架构之pinctrl子系统分析(一)
1.前言在嵌入式系统中,许多SoC的内部都包含了pin控制器,通过芯片内部的pin控制器,我们可以配置一个或者一组引脚的状态和功能特性,Linux内核为了统一各SoC厂商的引脚管理,提供了pinctr ...
- 下一代大数据系统和4S标准
大数据行业发展到今天,它创造的价值和带来的社会效应,大家已经看得很明白,同时很多问题和不足也暴露出来,特别是hadoop能够提供的数据处理能力,现在已经挖掘到极限,但是现在各行业对数据的存储和计算需求 ...
- 操作系统和Python的发展历程
一:操作系统的发展历史: 操作系统:什么是操作系统?我们首先想到的是电脑,,也就是所谓的Windows8,Windows7,或者XP系统和Windows10,当然也包括我们手机的安卓系统或者IPhon ...
随机推荐
- jmeter接口测试--参数化
接口测试时遇到一些属性不能重复时,可以使用Random 随机函数,除此之外,也可以用用户参数 一..随机参数化 1.在jmeter工具,菜单-选项-函数助手对话框,输入数值,属性,点击生成: 2.在相 ...
- python 基础篇 15 内置函数和匿名函数
------------------------>>>>>>>>>>>>>>>内置函数<<< ...
- Python——数据类型之dict
字典,相当于一个列表,不过列表的索引是数字,字典的索引是数字或者字符串. 1.字典的访问 字典是典型的key-value结构,一个key对应着一个value,key就是索引,value就是要保存的值 ...
- Daily Scrum02 11.29
今天大家都已经开始了进行第二轮迭代的工作!相比第一轮迭代,每个人都已经有了一定开发经验,这次做起来顺手很多.薛神和肖犇的挑战最大,他们需要实现好友功能,手机间的通信.服务器的搭建都是难点,但他们的热情 ...
- 简单java采集程序一
[目标任务]通过该网站采集全国的手机号码段至数据库表中 [完成过程] 1.初涉正则表达式,学会写简单的正则表达式 2.获取单个网页内容,学会java中基本的IO流 3.将获取数据插入mysql数据库表 ...
- Spring定时器调用Hibernate方法无法获得SessionFactory的解决办法
由于在Spring定时器中无法通过注解的方式获取bean,因此需要通过原生的方式获取.获取session的方式如下: WebApplicationContext wac = ContextLoader ...
- lincode-58-四数之和
58-四数之和 给一个包含n个数的整数数组S,在S中找到所有使得和为给定整数target的四元组(a, b, c, d). 注意事项 四元组(a, b, c, d)中,需要满足a <= b &l ...
- ubuntu16.04 装了一天的gitlab
1.安装gitlab baidu安装完了[后续再自己写安装过程] 2.遇到的问题 访问gitlab http://ip 可以进入页面也能操作但是console报404无法加载js和css,这样很不方便 ...
- 基于log4j的消息流的实现之二消息传递
在“基于log4j的消息流的实现之一消息获取”中获取日志消息的部分,修改如下: import org.apache.commons.collections.map.HashedMap; import ...
- 【bzoj1391】[Ceoi2008]order 网络流最小割
原文地址:http://www.cnblogs.com/GXZlegend/p/6796937.html 题目描述 有N个工作,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序 ...