aspeed2600 GPIO分析与适配ipmitool power status, ipmitool power on/off

news2024/9/30 4:55:50

1.说明

本节以x86-power-control/src/power_control.cpp为基础,分析整个GPIO的调用流程,实现简单的ipmitool power on/off,ipmitool power status的管理。

  • 1.资源:x86-power-control:https://github.com/openbmc/x86-power-control
  • 2.相关文件: meta-phosphor/recipes-x86/chassis/x86-power-control_git.bb
  • 3.本节涉及到的代码可以参考: https://gitee.com/wit_yuan/sdk_v09.01_ast2600/blob/yuan_support_power_status

备注:
为了加速编译过程,在文件build/ast2600-default/conf/local.conf中添加参数:

BB_NUMBER_THREADS ='16'
PARALLEL_MAKE ='-j 4'
BB_NO_NETWORK="1"

2.分析x86-power-control

在文件:power_control.cpp中可以看到调用流程:

build/ast2600-default/workspace/sources/x86-power-control/src/power_control.cpp
main()
---> loadConfigValues()
	 ---> 读取文件: "/usr/share/x86-power-control/power-config-host" + power_control::node + ".json"
     ---> 读取 tempGpioData->lineName = gpioConfig["LineName"];
---> // Request PS_PWROK GPIO events
---> requestGPIOEvents(powerOkConfig.lineName, psPowerOKHandler,psPowerOKLine, psPowerOKEvent)
		---> gpioLine = gpiod::find_line(name);
		---> gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}});

因此,需要关注2个函数:

gpioLine = gpiod::find_line(name)
gpioLine.request(...)

3.分析libgpiod

下载好libgpiod库,实际跟踪文件:build/ast2600-default/workspace/sources/libgpiod/bindings/cxx/line.cpp中的函数:

line find_line(const ::std::string& name)
{
	line ret;

	for (auto& it: make_chip_iter()) {
		ret = it.find_line(name);
		if (ret)
			break;
	}

	return ret;
}

调用流程为:

build/ast2600-default/workspace/sources/libgpiod/bindings/cxx/line.cpp
line find_line(const ::std::string& name)
	---> for (auto& it: make_chip_iter()) {
		---> ret = it.find_line(name); // = line chip::find_line(const ::std::string& name) const
			---> ::gpiod_line* handle = ::gpiod_chip_find_line(this->_m_chip.get(), name.c_str());
			---> return handle ? line(handle, *this) : line();

build/ast2600-default/workspace/sources/libgpiod/bindings/cxx/iter.cpp
chip_iter make_chip_iter(void)
---> ::gpiod_chip_iter* iter = ::gpiod_chip_iter_new();
---> return chip_iter(iter);
		
chip_iter::chip_iter(::gpiod_chip_iter *iter) : _m_iter(iter, chip_iter_deleter)
---> ::gpiod_chip* first =  ::gpiod_chip_iter_next_noclose(this->_m_iter.get());
---> this->_m_current = chip(first);

build/ast2600-default/workspace/sources/libgpiod/lib/iter.c
struct gpiod_chip_iter *gpiod_chip_iter_new(void)
---> num_chips = scandir("/dev", &dirs, dir_filter, alphasort);
---> iter->num_chips = num_chips;
---> iter->offset = 0;
---> iter->chips[i] = gpiod_chip_open_by_name(dirs[i]->d_name);

根据上面的内容,实际上C++调用到C,有几个函数:

build/ast2600-default/workspace/sources/libgpiod/lib/helpers.c
struct gpiod_line *gpiod_chip_find_line(struct gpiod_chip *chip, const char *name)
---> iter = gpiod_line_iter_new(chip);
	---> iter->num_lines = gpiod_chip_num_lines(chip);
	---> iter->lines[i] = gpiod_chip_get_line(chip, i);
		build/ast2600-default/workspace/sources/libgpiod/lib/core.c
		---> gpiod_line_update(line);  //!!!!重要!!!
			---> struct gpioline_info info;
			---> rv = ioctl(line->chip->fd, GPIO_GET_LINEINFO_IOCTL, &info);
			---> strncpy(line->name, info.name, sizeof(line->name));
			---> strncpy(line->consumer, info.consumer, sizeof(line->consumer));
---> gpiod_foreach_line(iter, line) {
	---> tmp = gpiod_line_name(line);
	---> if (tmp && strcmp(tmp, name) == 0) {  //找到了 line.
		...
	---> ...

注意留意函数:gpiod_line_update()里面使用了内核结构体:

build/ast2600-default/workspace/sources/linux-aspeed/include/uapi/linux/gpio.h
struct gpioline_info {
	__u32 line_offset;
	__u32 flags;
	char name[GPIO_MAX_NAME_SIZE];
	char consumer[GPIO_MAX_NAME_SIZE];
};

应用层用到了line->name. 这个line_name又是从驱动/内核传递而来。应用层在build/ast2600-default/workspace/sources/x86-power-control/config/power-config-host0.json中定义了一个!!LineName!!.

函数gpiod_chip_open_by_name():

build/ast2600-default/workspace/sources/libgpiod/lib/helpers.c
struct gpiod_chip *gpiod_chip_open_by_name(const char *name)
---> rv = asprintf(&path, "/dev/%s", name);
---> chip = gpiod_chip_open(path);

函数gpiod_chip_open()调用的是:

build/ast2600-default/workspace/sources/libgpiod/lib/core.c
struct gpiod_chip *gpiod_chip_open(const char *path)
---> fd = open(path, O_RDWR | O_CLOEXEC);
---> if (!is_gpiochip_cdev(path)) goto err_close_fd;
---> rv = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
---> chip->fd = fd;
---> chip->num_lines = info.lines;
---> strncpy(chip->name, info.name, sizeof(chip->name));
---> ...

根据ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info)需要找到内核层。

4.分析内核层

4.1 分析dts

当前文件build/ast2600-default/workspace/sources/linux-aspeed/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi中写的gpio为:

			gpio0: gpio@1e780000 {
				#gpio-cells = <2>;
				gpio-controller;
				compatible = "aspeed,ast2600-gpio";
				reg = <0x1e780000 0x400>;
				interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
				gpio-ranges = <&pinctrl 0 0 208>;
				ngpios = <208>;
				clocks = <&syscon ASPEED_CLK_APB2>;
				interrupt-controller;
				#interrupt-cells = <2>;
			};

根据aspeed,ast2600-gpio可以定位到文件build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpio-aspeed.c.

4.2 gpio驱动

4.2.1 gpiolib.c

文件:build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib.c作为实际的入口调用,可以看到函数gpiolib_dev_init():

static int __init gpiolib_dev_init(void)
{
...
---> ret = bus_register(&gpio_bus_type);
---> ret = driver_register(&gpio_stub_drv);
---> ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME);
---> gpiochip_setup_devs();
}
core_initcall(gpiolib_dev_init);

以及函数static void gpiochip_setup_devs(void)

static void gpiochip_setup_devs(void)
---> ret = gpiochip_setup_dev(gdev);

函数gpiochip_setup_dev():

static int gpiochip_setup_dev(struct gpio_device *gdev)
---> ret = gcdev_register(gdev, gpio_devt);
---> ret = gpiochip_sysfs_register(gdev);

比较重要的是一个宏定义:

#ifdef CONFIG_GPIO_CDEV
#define gcdev_register(gdev, devt)	gpiolib_cdev_register((gdev), (devt))
#define gcdev_unregister(gdev)		gpiolib_cdev_unregister((gdev))
#else
/*
 * gpiolib_cdev_register() indirectly calls device_add(), which is still
 * required even when cdev is not selected.
 */
#define gcdev_register(gdev, devt)	device_add(&(gdev)->dev)
#define gcdev_unregister(gdev)		device_del(&(gdev)->dev)
#endif

根据该宏定义定义的函数gpiolib_cdev_register(),实际就跳到文件:build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib-cdev.c中。

4.2.2 gpiolib-cdev.c

文件build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib-cdev.c作为整个gpio字符驱动的核心入口.

函数gpiolib_cdev_register():

int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
---> cdev_init(&gdev->chrdev, &gpio_fileops);

结构体gpio_fileops定义如下:

static const struct file_operations gpio_fileops = {
	.release = gpio_chrdev_release,
	.open = gpio_chrdev_open,
	.poll = lineinfo_watch_poll,
	.read = lineinfo_watch_read,
	.owner = THIS_MODULE,
	.llseek = no_llseek,
	.unlocked_ioctl = gpio_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = gpio_ioctl_compat,
#endif
};

对于gpio_ioctl:

static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
---> call_ioctl_locked(file, cmd, arg, cdev->gdev,gpio_ioctl_unlocked);

实际gpio_ioctl_unlocked():

static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
---> case GPIO_GET_CHIPINFO_IOCTL:
	---> return chipinfo_get(cdev, ip);

这就是前面函数struct gpiod_chip *gpiod_chip_open(const char *path)实际调用的内核函数。
另外,函数chipinfo_get()内容如:

static int chipinfo_get(struct gpio_chardev_data *cdev, void __user *ip)
---> strscpy(chipinfo.name, dev_name(&gdev->dev), sizeof(chipinfo.name));
---> strscpy(chipinfo.label, gdev->label, sizeof(chipinfo.label));
---> chipinfo.lines = gdev->ngpio;
---> copy_to_user(ip, &chipinfo, sizeof(chipinfo))

如此,可以获取到gpio chip包含的gpio line数量。

4.2.3 gpio-aspeed.c

dts中配置了:compatible = "aspeed,ast2600-gpio";之后,会经过一系列流程之后,调用到文件:
build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpio-aspeed.c中的static int __init aspeed_gpio_probe(struct platform_device *pdev):

static int __init aspeed_gpio_probe(struct platform_device *pdev)
---> err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio);
---> rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);

注意,devm_gpiochip_add_data()在文件:build/ast2600-default/workspace/sources/linux-aspeed/include/linux/gpio/driver.h中定义,定义如:

#ifdef CONFIG_LOCKDEP
#define gpiochip_add_data(gc, data) ({		\
		static struct lock_class_key lock_key;	\
		static struct lock_class_key request_key;	  \
		gpiochip_add_data_with_key(gc, data, &lock_key, \
					   &request_key);	  \
	})
#define devm_gpiochip_add_data(dev, gc, data) ({ \
		static struct lock_class_key lock_key;	\
		static struct lock_class_key request_key;	  \
		devm_gpiochip_add_data_with_key(dev, gc, data, &lock_key, \
					   &request_key);	  \
	})
#else
#define gpiochip_add_data(gc, data) gpiochip_add_data_with_key(gc, data, NULL, NULL)
#define devm_gpiochip_add_data(dev, gc, data) \
	devm_gpiochip_add_data_with_key(dev, gc, data, NULL, NULL)
#endif /* CONFIG_LOCKDEP */

只需要关注函数devm_gpiochip_add_data_with_key():

build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib-devres.c
devm_gpiochip_add_data_with_key()
	build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib.c
---> gpiochip_add_data_with_key(gc, data, lock_key, request_key);
	---> ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
	---> ret = gpiochip_get_ngpios(gc, &gdev->dev);
	---> ret = gpiochip_set_desc_names(gc);
	---> ret = gpiochip_set_names(gc);
		---> count = device_property_string_array_count(dev, "gpio-line-names");
		---> ret = device_property_read_string_array(dev, "gpio-line-names",...
		---> gdev->descs[i].name = names[chip->offset + i];
---> devm_add_action_or_reset(dev, devm_gpio_chip_release, gc);

对于应用层调用ioctl(line->chip->fd, GPIO_GET_LINEINFO_IOCTL, &info);,流程如:

build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib-cdev.c
static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
---> case GPIO_GET_LINEINFO_IOCTL:
	---> return lineinfo_get_v1(cdev, ip, false);
		---> struct gpio_desc *desc;
		---> desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset);
		---> gpio_desc_to_lineinfo(desc, &lineinfo_v2);
			//struct gpio_desc *desc
			---> info->offset = gpio_chip_hwgpio(desc);
			---> if (desc->name)  strscpy(info->name, desc->name, sizeof(info->name));
		---> gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
			---> memcpy(info_v1->name, info_v2->name, sizeof(info_v1->name));
			---> memcpy(info_v1->consumer, info_v2->consumer, sizeof(info_v1->consumer));
		---> copy_to_user(ip, &lineinfo, sizeof(lineinfo)

从以上代码可以看出,应用层用的是LineName, 驱动用的是:gpio-line-names。因此,适配的时候,注意将这2个名字匹配好。

因此,文件build/ast2600-default/workspace/sources/x86-power-control/config/power-config-host0.json中定义如下:

    "gpio_configs": [
        {
            "Name": "IdButton",
            "LineName": "ID_BUTTON",
            "Type": "GPIO",
            "Polarity": "ActiveLow"
        },
        {
            "Name": "PowerOk",
            "LineName": "PS_PWROK",
            "Type": "GPIO",
            "Polarity": "ActiveLow"
        },
        {
            "Name": "PowerOut",
            "LineName": "POWER_BUTTON",
            "Type": "GPIO",
            "Polarity": "ActiveLow"
        }
    ],

对应,文件build/ast2600-default/workspace/sources/linux-aspeed/arch/arm/boot/dts/aspeed/aspeed-ast2600-evb.dts内容定义如下:

&gpio0 {
	status = "okay";
	gpio-line-names = 
	/*A0~A7*/"A0","A1","A2","","","","ID_BUTTON","",
	/*B0~B7*/"POWER_BUTTON","B1","B2","","","","","",
	/*C0~C7*/"C0","C1","C2","","","","","",
	/*D0~D7*/"D0","D1","D2","","","","","",
	/*E0~E7*/"E0","E1","E2","","","","","",
	/*F0~F7*/"F0","F1","PS_PWROK","","","","","",
	/*G0~G7*/"G0","G1","G2","","","","","",
	/*H0~H7*/"H0","","","","","","","",
	/*I0~7*/"I0","","I2","","","","","",
	/*J0~7*/"J0","J1","","","","","","",
	/*K0~7*/"K0","K1","","","","","","",
	/*L0~7*/"","","","","","","","",
	/*M0~7*/"","","","","","","","",
	/*N0~7*/"","","","","","","","",
	/*O0~7*/"","","","","","","","",
	/*P0~7*/"","","","","","","","",
	/*Q0~7*/"","","","","","","","",
	/*R0~7*/"","","","","","","","",
	/*S0~7*/"","","","","","","","",
	/*T0~7*/"","","","","","","","",
	/*U0~7*/"","","","","","","","",
	/*V0~7*/"","","","","","","","",
	/*W0~7*/"","","","","","","","",
	/*X0~7*/"","","","","","","","",
	/*Y0~7*/"","","","","","","","",
	/*Z0~7*/"","","","","","","","";
};

注意,将gpio-line-names的数量控制在208或者以内即可。

5.适配power status,power on/off

5.1 修改dts

在适配中,发现在文件:build/ast2600-default/workspace/sources/x86-power-control/src/power_control.cpp中报错,报错点为:gpioLine.request({appName, gpiod::line_request::EVENT_BOTH_EDGES, {}});,报错内容为:

#  journalctl | grep power
Failed to request events for PS_PWROK: error requesting GPIO lines: Invalid argument

实际追踪下来,发现具体是在文件build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib-cdev.c中的函数lineevent_create()报错,具体点为函数gpiod_request_user():

build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib-cdev.c
static int lineevent_create(struct gpio_device *gdev, void __user *ip)
---> copy_from_user(&eventreq, ip, sizeof(eventreq))
---> desc = gpiochip_get_desc(gdev->chip, offset);
---> ret = gpiod_request_user(desc, le->label);

其中,函数gpiod_request_user()定义如下:

build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib.h
static inline int gpiod_request_user(struct gpio_desc *desc, const char *label)
---> ret = gpiod_request(desc, label);

其中,函数gpiod_request()定义如下:

build/ast2600-default/workspace/sources/linux-aspeed/drivers/gpio/gpiolib.c
int gpiod_request(struct gpio_desc *desc, const char *label)
---> int ret = -EPROBE_DEFER;
---> if (try_module_get(desc->gdev->owner)) {
		---> ret = gpiod_request_commit(desc, label);
			---> if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
				---> desc_set_label(desc, label ? : "?");
				else
				---> ret = -EBUSY;
				---> goto out_free_unlock;
			---> }
			---> if (gc->request) {
				---> if (gpiochip_line_is_valid(gc, offset))
					---> ret = gc->request(gc, offset); //aspeed_gpio_request
				---> else
					----> ...
			--->}
---> }

再次查看BMC日志,发现如下内容:

[   72.729136] aspeed-g6-pinctrl 1e6e2000.syscon:pinctrl: pin D23 already requested by 1e740100.sdhci; cannot claim for 1e780000.gpio:554
[   72.742714] aspeed-g6-pinctrl 1e6e2000.syscon:pinctrl: pin-42 (1e780000.gpio:554) status -22

在这里插入图片描述
对比build/ast2600-default/workspace/sources/linux-aspeed/arch/arm/boot/dts/aspeed/aspeed-ast2600-evb.dts文件,发现是GPIO被占用了。

以上,设置完成之后,通过ipmitool power status查看系统状态:

# ipmitool power status
Chassis Power is on

在这里插入图片描述

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

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

相关文章

【redis 第八篇章】链表结构

一、数组和链表 1、数组 数组会在内存中开辟一块连续的空间存储数据&#xff0c;这种存储方式有利也有弊端。当获取数据的时候&#xff0c;直接通过下标值就可以获取到对应的元素&#xff0c;时间复杂度为 O(1)。但是如果新增或者删除数据会移动大量的数据&#xff0c;时间复…

AI辅助教育:九章大模型的数学辅导功能解析

1.简介 九章大模型是学而思为学习研发的模型&#xff0c;该模型对于数学做了很多专门的训练&#xff0c;在题目推荐方面做得比较好。 同时&#xff0c;这个模型也能支持上传图片&#xff0c;对图片内容进行分析&#xff0c;然后针对内容进行校对&#xff0c;推荐相识题目。 支…

用于完成个人搜索的反向图像搜索工具

简介&#xff1a; Infringement.report 提供了一个强大的反向图像搜索工具&#xff0c;称为 Raider。这对于网络安全人员和渗透测试人员来说&#xff0c;是一个不可或缺的工具。 主要功能&#xff1a; 反向图像搜索&#xff1a; 该工具允许用户通过图像进行搜索&#xff0c…

Bash Shell 脚本中的循环语句

文章目录 Bash Shell 脚本中的循环语句一、for 循环1.1 列表循环1.2 不带列表循环&#xff08;C 风格的 for 循环&#xff09; 二、案例示例2.1 打印 1-5 的数字2.2 打印 5 次 "hello world"2.3 打印 abcde2.4 输出 0-50 之间的偶数 三、应用技巧3.1 使用花括号和 se…

自注意力和位置编码

一、自注意力 1、给定一个由词元组成的输入序列x1,…,xn&#xff0c; 其中任意xi∈R^d&#xff08;1≤i≤n&#xff09;。 该序列的自注意力输出为一个长度相同的序列 y1,…,yn&#xff0c;其中&#xff1a; 2、自注意力池化层将xi当作key&#xff0c;value&#xff0c;query来…

【Nuxt】资源导入

public 通常用于存放静态资源。 assets 通常用于存放样式表、字体或者 svg 的资源。 图片资源 alias 推荐使用 ~。 <img src"/avatar1.png" alt"avatar1"/> <img src"/assets/images/unnamed.jpg" alt"unnamed"/><te…

(STM32笔记)九、RCC时钟树与时钟 第二部分

我用的是正点的STM32F103来进行学习&#xff0c;板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话&#xff0c;用的也是这个板子和教程。 九、RCC时钟树与时钟 九、RCC时钟树与时钟2、时钟配置函数时钟初始化思路(72M)复位时钟至默认状态使能HSE&#xff0c;并等待…

第128天:内网安全-横向移动IPCATSC 命令Impacket 套件CS 插件全自动

环境部署 案例一&#xff1a; 域横向移动-IPC-命令版-at&schtasks 首先是通过外网web访问到win2008&#xff0c;获得了win2008的权限&#xff0c;这一步不做演示 因为里面的主机都不出网&#xff0c;所以只能利用win2008进行正向或者反向连接 信息收集 域内用户信息&…

【Git学习笔记】零基础入门学习Git

1. 学习目标 掌握Git企业应用开发的基本操作以及背后原理&#xff0c;掌握工作区、暂存区、版本库的区别掌握Git的版本管理&#xff0c;例如版本回退、撤销、修改等操作掌握Git的分支管理&#xff0c;例如创建分支、合并分支、删除分支掌握本地仓库与远程仓库之间的区别&#…

数据结构实验报告-链表

实 验 二 报 告 一、实验目的 1.熟练掌握链表的结构类型定义、特点。 2.熟练掌握链表的基本操作算法的实现及其算法时间复杂度的分析。 3.掌握循环链表、双向链表的结构类型定义及其基本操作算法。掌握链表的应用。 二、实验内容 1&#xff0e;请编写一个完整的程序&…

基于RHEL7的服务器批量安装

目录 一、项目要求 二、实验环境 三、生成kickstart自动化安装脚本 四、搭建dhcp服务并测试kickstart脚本 五、搭建pxe网络安装环境实现服务器自动部署 ​编辑 六、测试 一、项目要求 1.使用kickstart编写自动化安装脚本 2.搭建dhcp服务并测试kickstart脚本 3.搭建px…

软件设计之HTML5

软件设计之HTML5 【狂神说Java】HTML5完整教学通俗易懂 学习内容&#xff1a; 软件开发技能点参照&#xff1a;软件开发&#xff0c;小白变大佬&#xff0c;这套学习路线让你少走弯路是认真的&#xff0c;欢迎讨论 软件开发技能点参照&#xff1a;Java学习完整路线&#xff…

【doghead】mac构建 2: player 端 clion构建

准备工作 【doghead】mac构建 1 【doghead】mac: clion2024.1启动崩溃 mbp的 uv 构建ok zhangbin@zhangbin-mbp-2  ~/tet/Fargo/zhb-bifrost/Bifrost-202403/worker/third_party/libuv   main clion使用lldb cmake构建 更

SQL注入:MySQL元数据库,外网实战手工SQL注入

MySQL元数据库 MySQL的元数据库是一组特殊的数据库&#xff0c;用于存储MySQL服务器的元数据信息&#xff0c;在sql注入中较为常用为以下两种元数据库&#xff1a; information_schema&#xff1a;这个数据库包含了MySQL服务器上所有其他数据库的元数据信息。例如数据库名、表…

7 WIFI

7 WIFI 1、ESP8266模块2、烧写固件3、调试工具4、使用库函数实现wifi4.1 实现串口3和DMA的初始化4.2 利用串口3实现wifi 1、ESP8266模块 ESP8266系列无线模块是安信可科技自主研发设计的一系列高性价比WIFI SOC模组。该系列模块支持标准的IEEE802.11 b/g/n协议&#xff0c;内置…

Unity UnityWebRequest封装类

简化api调用流程&#xff0c;非常奈斯。 RestWebClient.cs using System; using System.Collections; using UnityEngine; using UnityEngine.Networking;namespace MYTOOL.RestClient {/// <summary>/// UnityWebRequest封装类/// </summary>public class RestW…

基于R语言绘制GGE双标图1

参考资料&#xff1a; 严威凯等: 双标图分析在农作物品种多点试验中的应用【作物学报】 https://cran.r-project.org/web/packages/GGEBiplots/GGEBiplots.pdf 1、安装GGEBiplots包 目前搜索到的资料多数为“GGEBiplotGUI”安装包&#xff0c;但在安装时报错&#xff0c;如下…

【独家原创】基于APO-Transformer-LSTM多特征分类预测(多输入单输出)Matlab代码

【独家原创】基于APO-Transformer-LSTM多特征分类预测&#xff08;多输入单输出&#xff09;Matlab代码 目录 【独家原创】基于APO-Transformer-LSTM多特征分类预测&#xff08;多输入单输出&#xff09;Matlab代码分类效果基本描述程序设计参考资料 分类效果 基本描述 [24年最…

【初阶数据结构题目】11.相交链表

相交链表 点击链接做题 思路&#xff1a; 如何判断链表是否相交找相交链表的起始节点 遍历两个链表&#xff0c;若尾结点相同&#xff0c;则链表一定相交。两个链表节点个数相同&#xff1a;往后遍历&#xff0c;找到相交的位置两个链表节点个数不同&#xff1a; 找两个链表的…

End-to-End Object Detection with Transformers(Detection Transformer)翻译

摘要 我们提出了一种新方法&#xff0c;将目标检测视为直接的集合预测问题。我们的方法简化了检测流程&#xff0c;有效消除了对许多手工设计组件的需求&#xff0c;如非极大值抑制过程或锚框生成&#xff0c;这些组件显式编码了我们对任务的先验知识。新框架称为检测变换器&a…