11.3编写Linux串口驱动

编写串口驱动主要步骤

  1. 构建并初始化 struct console 对象,若串口无需支持 console 可省略此步骤
//UART驱动的console
static struct uart_driver virt_uart_drv;
static struct console virt_uart_console = {//console 的名称,配合index字段使用,如果name为“ttyVIRT”,且index字段为小于0,则可以和“console=ttyVIRT“(n=0,1,2…)来确定index字段的值.name = "ttyVIRT",//操作函数集合.device = virt_uart_console_device,.write = virt_uart_console_write,.setup = virt_uart_console_setup,//CON_PRINTBUFFER表示从buffer中的第一行log开始打印//CON_CONSDEV表示从earlycon没有打印的log开始打印.flags = CON_PRINTBUFFER,//index小于0时通过bootargs参数确定其值.index = -1,//console私有数据,这里用于记录拥有此console的串口驱动.data = &virt_uart_drv,
};
  1. 构建并初始化 struct uart_driver 对象
//UART驱动
static struct uart_driver virt_uart_drv = {.owner = THIS_MODULE,//驱动名称,在dev文件系统中以此为前缀生成设备文件名.driver_name = "VIRT_UART",//设备名称.dev_name = "ttyVIRT",//主设备号和次设备号起始值.major = 0,.minor = 0,//只有一个端口.nr = 1,//UART的console.cons = &virt_uart_console,
};
  1. 使用 uart_register_driver 函数注册串口驱动
	//注册串口驱动result = uart_register_driver(&virt_uart_drv);if(result < 0){printk("register uart driver failed\r\n");return result;}
  1. 构建并初始化 struct uart_port 对象
//UART端口
static struct uart_port virt_port = {};//设置端口virt_port.line = 0;//端口所属设备virt_port.dev = &pdev->dev;//串口寄存器物理基地址,iobase、mapbase、membase不能全部为0,否则在初始化时不会执行端口配置操作virt_port.mapbase = 1;//端口类型,不能为PORT_UNKNOWNvirt_port.type = PORT_8250;//io访问方式virt_port.iotype = UPIO_MEM;//串口的中断号virt_port.irq = 0;//串口端口发送FIFO大小virt_port.fifosize = 32;//操作函数集合virt_port.ops = &virt_uart_ops;//RS485配置函数virt_port.rs485_config = NULL;//执行自动配置,但不探测UART类型virt_port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE;
  1. 使用 uart_add_one_port 函数在 uart_driver 添加串口端口
	//在串口驱动下添加端口result = uart_add_one_port(&virt_uart_drv, &virt_port);if(result < 0){printk("add uart port failed\n");return result;}

编写串口驱动

这里以一个虚拟串口为例来介绍串口驱动的编写,它在 proc 文件系统中创建了一个文件,通过向这个文件写入数据来模拟串口硬件的接收(写入数据时先将数据写入到虚拟串口接收FIFO中,然后调度工作队列,用以模拟串口中断处理函数来处理写入到虚拟串口接收FIFO中的数据),通过串口发送的数据会写入虚拟串口发送FIFO中,然后可以通过这个文件来读取虚拟串口发送FIFO中的数据,用于模拟串口硬件发送。

编写设备树

在顶层设备树根节点中加入如下节点:

	virtual_uart: virtual_uart_controller {compatible = "atk,virtual_uart";};

用make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs -j8编译设备树(arm-none-linux-gnueabihf-是编译器前缀),然后用新的.dtb文件启动系统

驱动代码编写

驱动代码的要点前面已经介绍过了,这里给出驱动程序的完整代码:

#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/rational.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/proc_fs.h>
#include <linux/kfifo.h>
#include <linux/of_irq.h>
#include <linux/sched.h>//定义一个FIFO,用于模拟串口的接收FIFO
static DEFINE_KFIFO(rx_fifo, char, 128);
//接收标志,非0表示开启接收
uint8_t rx_flag = 0;
//模拟串口接收完成中断的工作队列
static void rx_isr_work(struct work_struct *w);
DECLARE_WORK(rx_work, rx_isr_work);//定义一个FIFO,用于模拟串口的发送FIFO
static DEFINE_KFIFO(tx_fifo, char, 128);
//发送标志,非0表示开启发送
uint8_t tx_flag = 0;
//模拟串口发送完成中断的工作队列
static void tx_isr_work(struct work_struct *w);
DECLARE_WORK(tx_work, tx_isr_work);//UART端口
static struct uart_port virt_port = {};//proc文件,用于模拟串口硬件收发数据
static struct proc_dir_entry *proc_file;//从虚拟串口的发送FIFO中读取数据,用于模拟串口硬件发送数据
static ssize_t proc_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{unsigned int copied;//从虚拟串口的发送FIFO中读取数据,模拟串口硬件发送数据kfifo_to_user(&tx_fifo, buf, size, &copied);//调度工作队列,模拟产生发送中断schedule_work(&tx_work);return copied;
}//向虚拟串口的接收FIFO写入数据,用于模拟串口硬件接收到数据
static ssize_t proc_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{unsigned int copied;//将数据写入虚拟串口的接收FIFO,模拟串口硬件接收到数据kfifo_from_user(&rx_fifo, buf, size, &copied);//调度工作队列,模拟产生接收中断schedule_work(&rx_work);return copied;
}//proc文件操作函数集合
static const struct file_operations proc_fops = {.read = proc_read,.write = proc_write,
};//虚拟串口接收完成中断
static void rx_isr_work(struct work_struct *w)
{int rx_count;int result;unsigned long flags;uint8_t buffer[16];struct tty_port *ttyport = &virt_port.state->port;if(rx_flag){while(1){//从虚拟串口接收FIFO中读取数据rx_count = kfifo_out(&rx_fifo, buffer, sizeof(buffer));if(rx_count <= 0)break;//获取自旋锁spin_lock_irqsave(&virt_port.lock, flags);//将接收的数据写入行规程result = tty_insert_flip_string(ttyport, buffer, rx_count);//更新统计数据virt_port.icount.rx += result;//释放自旋锁spin_unlock_irqrestore(&virt_port.lock, flags);//通知行规程进行处理tty_flip_buffer_push(ttyport);}}
}//虚拟串口发送完成中断
static void tx_isr_work(struct work_struct *w)
{int one;int two;int count;int tx_count;unsigned long flags;struct circ_buf *xmit = &virt_port.state->xmit;//获取自旋锁spin_lock_irqsave(&virt_port.lock, flags);tx_count = 0;//获取环形缓冲区的长度count = uart_circ_chars_pending(xmit);if(count > 0){//将端口环形缓冲区的数据写入虚拟串口的发送FIFOif (xmit->tail < xmit->head) {//一次完成拷贝tx_count = kfifo_in(&tx_fifo, &xmit->buf[xmit->tail], count);}else{//分两次拷贝one = UART_XMIT_SIZE - xmit->tail;if (one > count)one = count;two = count - one;tx_count = kfifo_in(&tx_fifo, &xmit->buf[xmit->tail], one);if((two > 0) && (tx_count >= one))tx_count += kfifo_in(&tx_fifo, &xmit->buf[0], two);}//更新环形缓冲区xmit->tail = (xmit->tail + tx_count) & (UART_XMIT_SIZE - 1);//更新统计数据virt_port.icount.tx += tx_count;}elsetx_flag = 0;//释放自旋锁spin_unlock_irqrestore(&virt_port.lock, flags);
}//发送是否空闲
static unsigned int virt_uart_tx_empty(struct uart_port *port)
{/* 因为要发送的数据瞬间存入buffer */return (!tx_flag) ? 1 : 0;
}//配置流控
static void virt_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
}//获取流控配置
static unsigned int virt_uart_get_mctrl(struct uart_port *port)
{return 0;
}//停止发送
static void virt_uart_stop_tx(struct uart_port *port)
{tx_flag = 0;
}//启动发送
static void virt_uart_start_tx(struct uart_port *port)
{int one;int two;int count;int tx_count;struct circ_buf *xmit = &virt_port.state->xmit;tx_count = 0;//设置发送忙标志tx_flag = 1;//获取环形缓冲区的长度count = uart_circ_chars_pending(xmit);if(count > 0){//将端口环形缓冲区的数据写入虚拟串口的发送FIFOif(xmit->tail < xmit->head) {//一次完成拷贝tx_count = kfifo_in(&tx_fifo, &xmit->buf[xmit->tail], count);}else{//分两次拷贝one = UART_XMIT_SIZE - xmit->tail;if (one > count)one = count;two = count - one;tx_count = kfifo_in(&tx_fifo, &xmit->buf[xmit->tail], one);if((two > 0) && (tx_count >= one))tx_count += kfifo_in(&tx_fifo, &xmit->buf[0], two);}//更新环形缓冲区xmit->tail = (xmit->tail + tx_count) & (UART_XMIT_SIZE - 1);//更新统计数据virt_port.icount.tx += tx_count;}elsetx_flag = 0;
}//停止接收
static void virt_uart_stop_rx(struct uart_port *port)
{rx_flag = 0;
}//传输控制中断信号
static void virt_uart_break_ctl(struct uart_port *port, int break_state)
{
}//启动串口
static int virt_uart_startup(struct uart_port *port)
{//复位接收FIFOkfifo_reset(&rx_fifo);//启动接收rx_flag = 1;return 0;
}//关闭串口
static void virt_uart_shutdown(struct uart_port *port)
{//终止接收发送rx_flag = 0;tx_flag = 0;
}//刷新输出缓冲区
static void virt_uart_flush_buffer(struct uart_port *port)
{
}//配置端口时序
static void virt_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old)
{
}//获取端口类型
static const char *virt_uart_type(struct uart_port *port)
{return (port->type == PORT_8250) ? "VIRTUAL_UART" : NULL;
}//释放端口
static void virt_uart_release_port(struct uart_port *port)
{
}//请求端口
static int virt_uart_request_port(struct uart_port *port)
{return 0;
}//配置端口
static void virt_uart_config_port(struct uart_port *port, int flags)
{if (flags & UART_CONFIG_TYPE)port->type = PORT_8250;
}//验证端口
static int virt_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
{return -EINVAL;
}//console发送函数
static void virt_uart_console_write(struct console *co, const char *s, unsigned int count)
{//将数据写入发送FIFOkfifo_in(&tx_fifo, s, count);
}//获取console所属的tty_driver
struct tty_driver *virt_uart_console_device(struct console *co, int *index)
{struct uart_driver *p = co->data;*index = co->index;return p->tty_driver;
}//配置console
static int virt_uart_console_setup(struct console *co, char *options)
{return 0;
}//UART端口操作函数集合
static const struct uart_ops virt_uart_ops = {.tx_empty = virt_uart_tx_empty,.set_mctrl = virt_uart_set_mctrl,.get_mctrl = virt_uart_get_mctrl,.stop_tx = virt_uart_stop_tx,.start_tx = virt_uart_start_tx,.stop_rx = virt_uart_stop_rx,.break_ctl = virt_uart_break_ctl,.startup = virt_uart_startup,.shutdown = virt_uart_shutdown,.flush_buffer = virt_uart_flush_buffer,.set_termios = virt_uart_set_termios,.type = virt_uart_type,.release_port = virt_uart_release_port,.request_port = virt_uart_request_port,.config_port = virt_uart_config_port,.verify_port = virt_uart_verify_port,
};//UART驱动的console
static struct uart_driver virt_uart_drv;
static struct console virt_uart_console = {//console 的名称,配合index字段使用,如果name为“ttyVIRT”,且index字段为小于0,则可以和“console=ttyVIRT“(n=0,1,2…)来确定index字段的值.name = "ttyVIRT",//操作函数集合.device = virt_uart_console_device,.write = virt_uart_console_write,.setup = virt_uart_console_setup,//CON_PRINTBUFFER表示从buffer中的第一行log开始打印//CON_CONSDEV表示从earlycon没有打印的log开始打印.flags = CON_PRINTBUFFER,//index小于0时通过bootargs参数确定其值.index = -1,//console私有数据,这里用于记录拥有此console的串口驱动.data = &virt_uart_drv,
};//UART驱动
static struct uart_driver virt_uart_drv = {.owner = THIS_MODULE,//驱动名称,在dev文件系统中以此为前缀生成设备文件名.driver_name = "VIRT_UART",//设备名称.dev_name = "ttyVIRT",//主设备号和次设备号起始值.major = 0,.minor = 0,//只有一个端口.nr = 1,//UART的console.cons = &virt_uart_console,
};//设备和驱动匹配成功执行
static int virtual_uart_probe(struct platform_device *pdev)
{int result;printk("%s\r\n", __FUNCTION__);//设置端口virt_port.line = 0;//端口所属设备virt_port.dev = &pdev->dev;//串口寄存器物理基地址,iobase、mapbase、membase不能全部为0,否则在初始化时不会执行端口配置操作virt_port.mapbase = 1;//端口类型,不能为PORT_UNKNOWNvirt_port.type = PORT_8250;//io访问方式virt_port.iotype = UPIO_MEM;//串口的中断号virt_port.irq = 0;//串口端口发送FIFO大小virt_port.fifosize = 32;//操作函数集合virt_port.ops = &virt_uart_ops;//RS485配置函数virt_port.rs485_config = NULL;//执行自动配置,但不探测UART类型virt_port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE;//在串口驱动下添加端口result = uart_add_one_port(&virt_uart_drv, &virt_port);if(result < 0){printk("add uart port failed\n");return result;}//创建proc文件,用于模拟串口硬件的发送和接收proc_file = proc_create("virt_uart", 0, NULL, &proc_fops);if (!proc_file){uart_remove_one_port(&virt_uart_drv, &virt_port);printk("create proc file failed\n");return -ENOMEM;}return 0;
}//设备或驱动卸载时执行
static int virtual_uart_remove(struct platform_device *pdev)
{printk("%s\r\n", __FUNCTION__);//删除proc文件proc_remove(proc_file);//移除端口return uart_remove_one_port(&virt_uart_drv, &virt_port);
}//匹配列表,用于设备树和平台驱动匹配
static const struct of_device_id virtual_uart_of_match[] = {{.compatible = "atk,virtual_uart"},{ /* Sentinel */ }
};
//平台驱动
static struct platform_driver virtual_uart_drv = {.driver = {.name = "virtual_uart",.owner = THIS_MODULE,.pm = NULL,.of_match_table = virtual_uart_of_match,},.probe = virtual_uart_probe,.remove = virtual_uart_remove,
};static int __init virtual_uart_init(void)
{int result;printk("%s\r\n", __FUNCTION__);//注册串口驱动result = uart_register_driver(&virt_uart_drv);if(result < 0){printk("register uart driver failed\r\n");return result;}//注册平台驱动result = platform_driver_register(&virtual_uart_drv);if(result < 0){uart_unregister_driver(&virt_uart_drv);printk("register platform driver failed\r\n");return result;}return 0;
}static void __exit virtual_uart_exit(void)
{printk("%s\r\n", __FUNCTION__);//注销平台驱动platform_driver_unregister(&virtual_uart_drv);//注销串口驱动uart_unregister_driver(&virt_uart_drv);
}module_init(virtual_uart_init);
module_exit(virtual_uart_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("virtual_uart_dev");

串口测试程序

测试程序可以使用11.1Linux串口应用程序开发中编写的串口回环用于程序。

上机实验

  1. 修改设备树,在顶层设备树根节点中加入描述虚拟串口的设备节点,然后编译设备树,用新的设备树启动目标板
  2. 从这里下载测试程序,并进行编译,然后拷贝到目标板根文件系统的root目录
  3. 从这里下载驱动程序并进行编译,然后拷贝到目标板根文件系统的root目录
  4. 执行命令 insmod virtual_uart.ko 加载驱动程序
    在这里插入图片描述
  5. 执行命令 ./uart_teat.out /dev/ttyVIRT0 运行测试程序
  6. 另开一个终端,在终端中执行命令 echo 123456 > /proc/virt_uart 模拟串口硬件接收数据,执行命令 cat /proc/virt_uart 模拟串口硬件发送数据,在执行命令过程中 ./uart_teat.out 程序也会打印它收到的字节数。
    在这里插入图片描述
    在这里插入图片描述

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

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

相关文章

unity C#中Array、Stack、Queue、Dictionary、HashSet优缺点和使用场景总结

文章目录 数组 (Array)列表 (List<T>)栈 (Stack<T>)队列 (Queue<T>)链表 (LinkedList<T>)哈希表 (Dictionary<TKey, TValue>) 或 HashSet<T>集合 (Collection<T>) 数组 (Array) 优点&#xff1a; 高效访问&#xff1a;通过索引可以…

1-04C语言执行过程

一、概述 本小节主要讲解一个C程序从源代码到最终执行的过程&#xff0c;这个过程又可以细分为两部分&#xff1a; 源代码到可执行文件的过程可执行文件在内存中执行 本小节是C语言基础当中&#xff0c;比较容易被初学者忽视的知识点。而实际上&#xff1a; 熟悉C程序从源文…

前端超好玩的小游戏合集来啦--周末两天用html5做一个3D飞行兔子萝卜小游戏

文章目录 💖飞行兔子萝卜小游戏💟效果展示💟代码展示源码获取💖飞行兔子萝卜小游戏 💟效果展示 💟代码展示 <body> <script src=

如何选猫粮:买主食冻干猫粮需要注意什么?

随着养猫的人越来越多&#xff0c;铲屎官们对猫咪的饮食也越来越注重。除了猫粮&#xff0c;很多铲屎官还会给猫咪准备小零食。那么&#xff0c;猫咪是不是除了猫粮就没有其他可吃的了呢&#xff1f;答案当然不是。猫咪还有猫冻干、冻干猫粮、猫条等可以选择。每个铲屎官都希望…

【MySQL】索引基础

文章目录 1. 索引介绍2. 创建索引 create index…on…2.1 explain2.2 创建索引create index … on…2.3 删除索引 drop index … on 表名 3. 查看索引 show indexes in …4. 前缀索引4.1 确定最佳前缀长度&#xff1a;索引的选择性 5. 全文索引5.1 创建全文索引 create fulltex…

Vue3.4更新 “Slam Dunk“发布!!!

Announcing Vue 3.4 | The Vue Point. vue3.4更新官方文档 在vue2即将结束更新的时候&#xff0c;vue3迎来了一个重要的更新。代号为“&#x1f3c0; Slam Dunk”&#xff0c;即"灌篮高手"。这个版本进行了很多显著的内部改进&#xff0c;最重要的是模版解析的底层逻…

Github 2024-01-08开源项目周报 Top14

根据Github Trendings的统计&#xff0c;本周(2024-01-08统计)共有14个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目5TypeScript项目3C项目2Dart项目1QML项目1Go项目1Shell项目1Rust项目1JavaScript项目1C#项目1 免费…

IO进程线程Day5

1> 将互斥机制代码重新实现一遍 #include<myhead.h>char buf[128]; //临界资源pthread_mutex_t mutex; //创建锁资源//分支线程 void* task(void* arg) {while(1){//获取锁资源pthread_mutex_lock(&mutex);printf("这里是分支线程:%s\n",buf);st…

多线程模板应用实现(实践学习笔记)

出处&#xff1a;B站码出名企路 个人笔记&#xff1a;因为是跟着b站的教学视频以及文档初步学习&#xff0c;可能存在诸多的理解有误&#xff0c;对大家仅供借鉴&#xff0c;参考&#xff0c;然后是B站up阳哥的视频&#xff0c;我是跟着他学。大家有兴趣的可以到b站搜索。加油…

CAD安装教程

CAD安装教程 目录 一&#xff0e; 下载CAD二&#xff0e; 安装CAD 一&#xff0e; 下载CAD 如果需要CAD安装包请私信。 二&#xff0e; 安装CAD 解压压缩包AutoCAD2022中文版&#xff0c;以管理员身份运行AutoCAD_2022_Simplified_Chinese_Win_64bit_dlm.sfx。 选择解压路径。…

【sklearn练习】datasets的使用

一、数据集分类 1、fetch类的数据集&#xff1a; 以 "fetch" 开头的数据集&#xff0c;这些数据集通常不包含在 scikit-learn 的标准安装中&#xff0c;需要从远程服务器上下载。这些数据集通常比标准数据集更大&#xff0c;因此在使用它们之前&#xff0c;需要通过…

Spring MVC中@ExceptionHandler注解的智能处理机制——无需显示指定异常类型

概述 在深入探讨Spring MVC框架时&#xff0c;我们经常会遇到异常处理的相关场景。其中&#xff0c;ExceptionHandler注解是一个非常重要的工具&#xff0c;它允许我们声明一个方法来专门处理特定类型的异常。有趣的是&#xff0c;Spring容器具备智能化的异常类型关联功能&…

自动驾驶:低阶可部署的单目测距算法-基于YOLO与透视变换

一、开发环境 部署平台&#xff1a;英伟达的Jetson Nano 环境&#xff1a;Linux ROS 语言&#xff1a;C 设备&#xff1a;1920*1080像素的摄像头、开发板。 模型&#xff1a;yolo-v8s 二、单目测距实现思路 0、标定相机和车辆&#xff08;假设已经标定完成&#xff09; 1、通…

06-微服务-SpringAMQP

SpringAMQP SpringAMQP是基于RabbitMQ封装的一套模板&#xff0c;并且还利用SpringBoot对其实现了自动装配&#xff0c;使用起来非常方便。 SpringAmqp的官方地址&#xff1a;https://spring.io/projects/spring-amqp SpringAMQP提供了三个功能&#xff1a; 自动声明队列、交…

[论文阅读] Revisiting Feature Propagation and Aggregation in Polyp Segmentation

[论文地址] [代码] [MICCAI 23] Abstract 息肉的准确分割是筛查过程中有效诊断结直肠癌的关键步骤。 由于能够有效捕获多尺度上下文信息&#xff0c;普遍采用类似UNet 的编码器-解码器框架。 然而&#xff0c;两个主要限制阻碍了网络实现有效的特征传播和聚合。 首先&#xff…

基于SSM的企业员工管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

[AutoSar]基础部分 RTE 04 数据类型的定义及使用

目录 关键词平台说明一、数据类型分类二、Adt三、Idt四、Base 数据类型五、units六、compu methods七、data constraint 关键词 嵌入式、C语言、autosar、Rte 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C&#xff0c;C编译器HighTec (GCC) 一、数据…

python自动化测试面试题与答案汇总

对于机器学习算法工程师而言,Python是不可或缺的语言,它的优美与简洁令人无法自拔,下面这篇文章主要给大家介绍了关于30道python自动化测试面试题与答案汇总的相关资料,需要的朋友可以参考下 1、什么项目适合做自动化测试&#xff1f; 关键字&#xff1a;不变的、重复的、规范…

ts axios 指定返回值类型,返回数据类型不确定该怎么办 typescript

ts axios 指定返回值类型&#xff0c;返回数据类型不确定该怎么办 typescript 转到 ts 以来&#xff0c;一直有个问题困扰着我&#xff0c;就是每次用 axios 获取数据时&#xff0c;返回值 res 的类型都不能确定&#xff0c;这就导致编辑器一直提示我&#xff1a; 原因 原因是…

SPON世邦 IP网络对讲广播系统 多处文件上传漏洞复现

0x01 产品简介 SPON世邦IP网络对讲广播系统是一种先进的通信解决方案,旨在提供高效的网络对讲和广播功能。 0x02 漏洞概述 SPON世邦IP网络对讲广播系统 addscenedata.php、uploadjson.php、my_parser.php等接口处存在任意文件上传漏洞,未经身份验证的攻击者可利用此漏洞上…