最新要闻
- 甲流吃退烧药不退烧怎么办_吃了一粒退烧药多久可以喂奶
- 环球今日报丨卢伟冰:Note系列全球销量破3.2亿 进入全球单品十强
- 头条:马斯克为何没做出ChatGPT?揭秘OpenAI创始人的权力斗争
- 日本推出佛祖版ChatGPT:已经为20多万人解决烦恼
- 观天下!合资车还咋玩!奇瑞艾瑞泽5 GT上市:起售价仅7.99万
- 每日快报!Redmi Note 12 Turbo搭载超细四窄边直屏:边框窄至1.42mm!
- 全球资讯:德创环保:宁波甬德拟以1.61亿元收购飞乐环保100%股权
- 每日讯息!12306回应免费坐高铁:积分存在有效期
- 环球即时看!甄子丹主演!《疾速追杀4》豆瓣8.2分:纯粹动作爽片 超越前作
- 男子求职竟被要求在杭州本地买房 网友:不是招员工是招客户
- 环球微动态丨试过网易的新AI后 我发现它很笨 但又很聪明
- 广汽传祺GS4追尾后定损巡航坏了?保险公司不想理赔
- 今日热议:南钢股份:钢材销量同比下降6.28%,2022年年归母净利润同比下滑48.59%,拟10派2.5元
- 快讯:《生化危机4:重制版》D加密惹争议 又导致性能问题了
- 全球观热点:女子地铁照被AI一键脱衣传播 网友:无下限的开发
- 每日焦点!网友准备冲Redmi Note 12 Turbo哈利·波特版:预算3900元
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
设备树的概念(四):平台设备驱动和设备树
平台驱动程序也与DTs一起工作。这是目前处理平台设备的推荐方式,不再需要使用板级文件,当设备的属性发生变化时也不需要重新编译内核。可以使用OF匹配表进行匹配,这是一种基于DT的匹配机制。下面让我们看看它是如何工作的。
(资料图片仅供参考)
OF匹配表方式
OF匹配表匹配是Platform核心执行的第一个匹配机制,目的是将设备与其驱动程序进行匹配。它使用一个设备树的compatible属性来匹配of_match_table中的设备条目,这是struct driver子结构的一个字段。每个设备节点都有一个compatible属性,它是一个字符串或字符串列表。任何声明compatible属性中列出的字符串之一的平台驱动程序都将触发匹配,并将看到其probe函数被执行。
DT匹配项在内核中被描述为struct of_device_id结构的实例,该结构在linux/mod_devictable .h中定义,如下所示:
// we are only interested in the two last elements of the structurestruct of_device_id { [...] char compatible[128]; const void *data;};
下面是结构中每个元素的含义:
- char compatible[128]: 用于匹配DT中设备节点的compatible属性的字符串。在发生匹配之前,它们必须是相同的。
- const void *data: 可以指向任何结构,可以用作每个设备类型的配置数据。
由于of_match_table是一个指针,你可以传递一个struct of_device_id的数组来让你的驱动程序兼容多个设备:
static const struct of_device_id imx_uart_dt_ids[] = { { .compatible = "fsl,imx6q-uart", }, { .compatible = "fsl,imx1-uart", }, { .compatible = "fsl,imx21-uart", }, { /* sentinel */ }};
一旦你填充了你的id数组,它必须被传递到你的平台驱动的of_match_table字段,在driver子结构中:
static struct platform_driver serial_imx_driver = { [...] .driver = { .name = "imx-uart", .of_match_table = imx_uart_dt_ids, [...] },};
在这一步,只有你的驱动程序知道你的of_device_id数组。为了让内核也得到通知(这样它就可以在platform核心维护的设备列表中存储你的id),你的数组必须在MODULE_DEVICE_TABLE中注册,如下:
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
就这些!我们的驱动程序是DT兼容的。回到DT中,让我们声明一个与驱动程序兼容的设备:
uart1: serial@02020000 { compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; reg = <0x02020000 0x4000>; interrupts = <0 26 IRQ_TYPE_LEVEL_HIGH>; [...]};
这里提供了两个compatible字符串。如果第一个字符串不匹配任何驱动程序,核心将使用第二个字符串继续进行匹配。
当匹配发生时,你的驱动程序的probe函数被调用,probe用一个struct platform_device结构体作为参数,它包含一个struct device dev字段,其中有一个struct device_node *of_node字段,对应于与我们的设备关联的节点,所以你可以使用它来提取设备设置:
static int serial_imx_probe(struct platform_device *pdev){ [...] struct device_node *np; np = pdev->dev.of_node; if (of_get_property(np, "fsl,dte-mode", NULL)) sport->dte_mode = 1; [...]}
你可以检查DT节点是否被设置为知道驱动程序作为一个of_match的响应被加载 ,或者从板子的init文件中实例化。然后你应该使用of_match_device函数,以选择产生匹配的struct *of_device_id条目,它可能包含你传递的特定数据:
static int my_probe(struct platform_device *pdev){ struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; match = of_match_device(imx_uart_dt_ids, &pdev->dev); if (match) { /* Devicetree, extract the data */ my_data = match->data; } else { /* Board init file */ my_data = dev_get_platdata(&pdev->dev); } [...]}
处理非设备树平台
在内核中通过CONFIG_OF选项启用了DT支持。当内核中没有启用DT API支持时,你可能希望避免使用它。实现这一点的方法是检查是否设置了CONFIG_OF。我们通常这样做:
#ifdef CONFIG_OFstatic const struct of_device_id imx_uart_dt_ids[] = { { .compatible = "fsl,imx6q-uart", }, { .compatible = "fsl,imx1-uart", }, { .compatible = "fsl,imx21-uart", }, { /* sentinel */ }};/* other device tree dependent code */[...]#endif
即使在缺少设备树支持时总是定义of_device_id数据类型,但包含在#ifdef CONFIG_OF…#endif中的代码将在构建过程中被忽略。这用于条件编译。这不是你唯一的选择;还有of_match_ptr宏,当OF被禁用时,它简单地返回NULL。在任何地方,你都需要传递你的of_match_table作为参数;它应该被包装在of_match_ptr宏中,这样当OF被禁用时它就会返回NULL。宏定义在include/linux/of.h中:
#define of_match_ptr(_ptr) (_ptr) /* When CONFIG_OF is enabled */#define of_match_ptr(_ptr) NULL /* When it is not */
我们可以这样使用它:
static int my_probe(struct platform_device *pdev){ const struct of_device_id *match; match = of_match_device(of_match_ptr(imx_uart_dt_ids), &pdev->dev); [...]}static struct platform_driver serial_imx_driver = { [...] .driver = { .name = "imx-uart", .of_match_table = of_match_ptr(imx_uart_dt_ids), },}
of_match_ptr这个宏避免了使用#ifdef,当OF被禁用时返回NULL。
支持多个硬件设备,每个设备都有特定的数据
有时,一个驱动程序可以支持不同的硬件,每种硬件都有其特定的配置数据。这些数据可以是专用的函数表、特定的寄存器值或每个设备特有的任何东西。下面的例子描述了一个通用方法:
首先让我们记住include/linux/mod_devictable.h中的struct of_device_id是什么样的:
/** Struct used for matching a device*/struct of_device_id { [...] char compatible[128]; const void *data;};
我们感兴趣的字段是const void *data,因此我们可以使用它来传递每个特定设备的任何数据。
假设我们拥有三个不同的设备,每个设备都有特定的私人数据。of_device_id.data将包含一个指向特定参数的指针。这个例子参考了drivers/tty/serial/imx.c。
首先,我们声明私有结构:
/* i.MX21 type uart runs on all i.mx except i.MX1 and i.MX6q */enum imx_uart_type { IMX1_UART, IMX21_UART, IMX6Q_UART,};/* device type dependent stuff */struct imx_uart_data { unsigned uts_reg; enum imx_uart_type devtype;};
然后,我们用特定于设备的数据填充数组:
static struct imx_uart_data imx_uart_devdata[] = { [IMX1_UART] = { .uts_reg = IMX1_UTS, .devtype = IMX1_UART, }, [IMX21_UART] = { .uts_reg = IMX21_UTS, .devtype = IMX21_UART, }, [IMX6Q_UART] = { .uts_reg = IMX21_UTS, .devtype = IMX6Q_UART, },};
每个兼容的条目都与一个特定的数组索引绑定:
static const struct of_device_id imx_uart_dt_ids[] = { { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], }, { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], }, { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], }, { /* sentinel */ }};MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);static struct platform_driver serial_imx_driver = { [...] .driver = { .name = "imx-uart", .of_match_table = of_match_ptr(imx_uart_dt_ids), },};
现在在probe函数中,无论匹配项是什么,它都会保存一个指向设备特定结构的指针:
static int imx_probe_dt(struct platform_device *pdev){ struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id = of_match_device(of_match_ptr(imx_uart_dt_ids), &pdev->dev); if (!of_id) /* no device tree device */ return 1; [...] sport->devdata = of_id->data; /* Get private data back */}
在上面的代码中,devdata是原始源代码中结构的一个元素,并声明为const struct imx_uart_data *devdata;我们可以在数组中存储任何特定的参数。
混合匹配方法
OF匹配表可以与任何其他匹配机制组合。在下面的例子中,我们混合了DT和设备ID匹配方法:
我们为设备ID匹配方法填充一个数组,每个设备都有它的数据:
static const struct platform_device_id sdma_devtypes[] = { { .name = "imx51-sdma", .driver_data = (unsigned long)&sdma_imx51, }, { .name = "imx53-sdma", .driver_data = (unsigned long)&sdma_imx53, }, { .name = "imx6q-sdma", .driver_data = (unsigned long)&sdma_imx6q, }, { .name = "imx7d-sdma", .driver_data = (unsigned long)&sdma_imx7d, }, { /* sentinel */ }};MODULE_DEVICE_TABLE(platform, sdma_devtypes);
我们对OF匹配表方法做同样的操作:
static const struct of_device_idsdma_dt_ids[] = { { .compatible = "fsl,imx6q-sdma", .data = &sdma_imx6q, }, { .compatible = "fsl,imx53-sdma", .data = &sdma_imx53, }, { .compatible = "fsl,imx51-sdma", .data = &sdma_imx51, }, { .compatible = "fsl,imx7d-sdma", .data = &sdma_imx7d, }, { /* sentinel */ }};MODULE_DEVICE_TABLE(of, sdma_dt_ids);
probe函数如下所示:
static int sdma_probe(structplatform_device *pdev){ const struct of_device_id *of_id = of_match_device(of_match_ptr(sdma_dt_ids), &pdev->dev); struct device_node *np = pdev->dev.of_node; /* If devicetree, */ if (of_id) drvdata = of_id->data; /* else, hard-coded */ else if (pdev->id_entry) drvdata = (void *)pdev->id_entry->driver_data; if (!drvdata) { dev_err(&pdev->dev, "unable to find driver data\n"); return -EINVAL; } [...]}
然后,我们声明平台驱动程序:
static struct platform_drivers dma_driver = { .driver = { .name = "imx-sdma", .of_match_table = of_match_ptr(sdma_dt_ids), }, .id_table = sdma_devtypes, .remove = sdma_remove, .probe = sdma_probe,};module_platform_driver(sdma_driver);
Platform资源和DTs
平台设备可以与支持DT的系统一起工作,而无需任何额外的修改。通过使用platform_xxx族函数,核心会遍历DT(使用of_xxx族函数)以查找所请求的资源。
反之则不成立,因为of_xxx族函数仅为DT预留。所有资源数据将以通常的方式提供给驱动程序。驱动程序现在知道这个设备是否在板级文件中使用硬编码参数进行了初始化。我们以uart设备节点为例:
uart1: serial@02020000 { compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; reg = <0x02020000 0x4000>; interrupts = <0 26 IRQ_TYPE_LEVEL_HIGH>; dmas = <&sdma 25 4 0>, <&sdma 26 4 0>; dma-names = "rx", "tx";};
以下摘录描述了其驱动程序的探测函数。在probe函数中,函数platform_get_resource()可以用来提取任何资源属性(内存区域,dma, irq),或者一个特定的函数,比如platform_get_irq(),它提取DT中interrupts属性提供的irq:
static int my_probe(struct platform_device *pdev){ struct iio_dev *indio_dev; struct resource *mem, *dma_res; struct xadc *xadc; int irq, ret, dmareq; /* irq */ irq = platform_get_irq(pdev, 0); if (irq<= 0) return -ENXIO; [...] /* memory region */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); xadc->base = devm_ioremap_resource(&pdev->dev, mem); /* * We could have used * devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); * too. */ if (IS_ERR(xadc->base)) return PTR_ERR(xadc->base); [...] /* second dma channel */ dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); dmareq = dma_res->start; [...]}
总而言之,对于dma、irq和mem这样的属性,在平台驱动程序中不需要做任何事情来匹配dtb。如果你还记得,该数据与你可以作为平台资源传递的数据是同一类型的。为了理解其中的原因,我们只需要看看这些函数的内部;我们会看到它们内部是如何处理DT函数的。下面是platform_get_irq函数的示例:
int platform_get_irq(struct platform_device *dev, unsigned int num){ [...] struct resource *r; if (IS_ENABLED(CONFIG_OF_IRQ) &&dev->dev.of_node) { int ret; ret = of_irq_get(dev->dev.of_node, num); if (ret > 0 || ret == -EPROBE_DEFER) return ret; } r = platform_get_resource(dev, IORESOURCE_IRQ, num); if (r && r->flags & IORESOURCE_BITS) { struct irq_data *irqd; irqd = irq_get_irq_data(r->start); if (!irqd) return -ENXIO; irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); } return r ? r->start : -ENXIO;}
您可能想知道platform_xxx函数如何从DT中提取资源。这应该是使用of_xxx函数族。对的,但是在系统boot期间,内核在每个设备节点上调用of_platform_device_create_pdata(),这将导致创建一个具有关联资源的平台设备,你可以在该设备上调用platform_xxx族函数。其原型如下:
static struct platform_device *of_platform_device_create_pdata( struct device_node *np, const char *bus_id, void *platform_data, struct device *parent)
Platform数据与DTs相比
如果你的驱动程序需要平台数据,你应该检查dev.platform_data指针。非空值意味着您的驱动程序已经在板级配置文件中以旧的方式实例化,并且DT不会进入其中。对于从DT实例化的驱动程序,dev.platform_data将为NULL,并且您的平台设备将在DT条目(节点)上获得一个指针,该指针在dev.of_node指针中对应于您的设备,你可以从中提取资源并使用OF API解析和提取应用程序数据。
还有一种混合方法,我们可以使用它将C文件中声明的平台数据与DT节点关联起来,但这仅适用于特殊情况:DMA、IRQ和内存。此方法仅在驱动程序只需要资源而不需要特定于应用程序的数据时使用。
我们可以将I2C控制器过时的声明转换为DT兼容节点,方法如下:
过时的板级声明:
#define SIRFSOC_I2C0MOD_PA_BASE 0xcc0e0000#define SIRFSOC_I2C0MOD_SIZE 0x10000#define IRQ_I2C0static struct resource sirfsoc_i2c0_resource[] = { { .start = SIRFSOC_I2C0MOD_PA_BASE, .end = SIRFSOC_I2C0MOD_PA_BASE + SIRFSOC_I2C0MOD_SIZE - 1, .flags = IORESOURCE_MEM, },{ .start = IRQ_I2C0, .end = IRQ_I2C0, .flags = IORESOURCE_IRQ, },};
DT节点:
i2c0: i2c@cc0e0000 { compatible = "sirf,marco-i2c"; reg = <0xcc0e0000 0x10000>; interrupt-parent = <&phandle_to_interrupt_controller_node> interrupts = <0 24 0>; #address-cells = <1>; #size-cells = <0>; status = "disabled";};
从hardcode设备配置切换到DTs的时刻已经到来。上述提供了处理DT所需的全部内容。现在我们已经具备了定制或添加任何DT节点和属性的必要技能,并从驱动程序中提取它们。
关键词:
-
天天最资讯丨Apache iotdb-web-workbench 认证绕过漏洞(CVE-2023-24829)
漏洞主要来自于iotdb-web-workbenchIoTDB-Workbench是IoTDB的可视化管理工具,可对IoTDB的数据进行增...
来源: -
Halcon学习教程(一) 之提取十字线中心 图像分割
原文作者:aircraft原文链接:https: www cnblogs com DOMLX p 17266405 html废话不多说,因为...
来源: 设备树的概念(四):平台设备驱动和设备树
天天最资讯丨Apache iotdb-web-workbench 认证绕过漏洞(CVE-2023-24829)
Halcon学习教程(一) 之提取十字线中心 图像分割
甲流吃退烧药不退烧怎么办_吃了一粒退烧药多久可以喂奶
环球今日报丨卢伟冰:Note系列全球销量破3.2亿 进入全球单品十强
头条:马斯克为何没做出ChatGPT?揭秘OpenAI创始人的权力斗争
日本推出佛祖版ChatGPT:已经为20多万人解决烦恼
观天下!合资车还咋玩!奇瑞艾瑞泽5 GT上市:起售价仅7.99万
每日快报!Redmi Note 12 Turbo搭载超细四窄边直屏:边框窄至1.42mm!
热消息:一篇文章带你了解面积图
京沪杭等地近期明确将有序放开设摊、允许外摆
全球资讯:德创环保:宁波甬德拟以1.61亿元收购飞乐环保100%股权
每日讯息!12306回应免费坐高铁:积分存在有效期
环球即时看!甄子丹主演!《疾速追杀4》豆瓣8.2分:纯粹动作爽片 超越前作
男子求职竟被要求在杭州本地买房 网友:不是招员工是招客户
环球微动态丨试过网易的新AI后 我发现它很笨 但又很聪明
广汽传祺GS4追尾后定损巡航坏了?保险公司不想理赔
记录--vue刷新当前页面
全球关注:借助 mperf 进行矩阵乘法极致优化
MySQL学习笔记-存储引擎
当前看点!低代码起势,开发者可以早日脱离996了?
今日热议:南钢股份:钢材销量同比下降6.28%,2022年年归母净利润同比下滑48.59%,拟10派2.5元
快讯:《生化危机4:重制版》D加密惹争议 又导致性能问题了
全球观热点:女子地铁照被AI一键脱衣传播 网友:无下限的开发
每日焦点!网友准备冲Redmi Note 12 Turbo哈利·波特版:预算3900元
头条:男子乘错出租车 起步1秒被强制收费6元:走了不到10米
【世界新视野】内存掉入无底洞:没有最便宜 只有更便宜
环球观天下!32、K8S-配置管理之Configmap
世界看点:Python中21道个程序小练习
css设置超过固定长度以省略号显示
全球看点:乒乓球技巧训练这就是高手的水准_乒乓球技巧
全球简讯:瑞银:看好亚股增长潜力 偏好亚洲投资级债券
Twitter将上线离谱新规:非会员甚至无法参与投票
热头条丨九识自动驾驶物流车被曝闯红灯 网友:该怎么记分处罚
速读:“网红”威震天在北京环球影城摔了一跤 客服回应:后续演出正常
【环球新要闻】一个时代结束了!Wii U和3DS在线商店已正式关闭
实时:透明外壳设计!Nothing Ear(2)无线耳机上市 999元
热资讯![HTML]表单标签(form表单域、input输入表单、label标签、select下拉表单、textarea文本域)
C#List的3种排序方法
环球观焦点:韩国有意解禁福岛水产品?在野党呼吁韩总统表态
全球新资讯:收评:两市震荡走弱沪指跌0.19% 人工智能概念冲高回落
全球热头条丨想玩必须升级!Steam将于2024年终止对Win7/8/8.1系统支持
中国科学家发现月球水库 估计蓄水量多达2700亿吨
父母做生意给儿子取名为顾客 当事人:家里有个“顾客”能带来更多顾客
世界最高安全标准 我国自研核电华龙一号西部首堆全面建成
【时快讯】249元 TP-LINK推出新款玄鸟AX3000路由器:3000Mbps满血Wi-Fi 6
观热点:Rust 备忘清单_开发速查表分享
即时焦点:郑州大学河南先进技术研究院2023年硕士研究生拟调剂信息公告已公布
天天热点评!国服一步步被掏空 暴雪国际服支持微信支付宝:价格全面上涨
vivo X Fold2将在博鳌亚洲论坛首秀:采用“天圆地方·两仪万象”设计
每日资讯:网信办:全面清理虚假摆拍短视频
环球观热点:如何应对上手英文工具站的 8 大误区
小程序的车载场景应用
【Visual Leak Detector】配置项 ReportEncoding
数据建模
世界要闻:在.NET7使用NPOI读取Excel如此简单
每日简讯:西门子医疗Syngo Carbon助力贵安医院打破数据孤岛,加速智慧医院建设
每日快播:谁是更好的甜点处理器?锐龙5 7600、酷睿i5-13400对比评测:游戏差距大到不敢相信
终于结束了 特斯拉“退一赔三”案二审败诉后申请再审:法院驳回
笔记本电脑排风扇声音大是怎么回事?笔记本电脑排风扇声音大怎么解决?
nova6最严重的缺点是什么?nova6参数详细价格
哈尔滨有几个电脑城?哈尔滨电脑城地址
折旧率是什么意思?折旧率计算公式
全球今头条!Go语言:通过TDD驱动测试开发为同事写的程序优化提速——初次接触并发与channel
【世界时快讯】非看不可的Redis持久化
选择排序
速递!【Visual Leak Detector】配置项 MaxTraceFrames
flash8怎么下载到电脑上?Flash8的序列号是多少?
大连地铁敞门行驶 线路刚开12天:客服称“原因正在调查”
快资讯丨00后女生毕业当猪场保育员:她认为这行业很有潜力
世界快资讯丨微软反戈一击 反垄断战火引至日本本土
当前头条:东南亚出游降温:机票降幅达40%
环球新动态:机器学习基础06DAY
数据密集型应用存储与检索设计
当前头条:范畴论:迷人的数学花园
环球观天下!智能存储重磅上线:低成本闲时转码
【天天快播报】促消费,振内需,康佳加速业务高质量发展
世界第一名宇航员!尤里·加加林坠机遇难地点照片首次公开
每日看点!真的可以免费坐高铁了!用12306积分兑换:官方教程收好
焦点速递!杭州地铁现打工人专线 网友:通勤都不让闲着
环球观察:《指环王:力量之戒》片场马匹死亡 动保组织谴责:剥削动物
魅族20系列大杯曝光:120Hz E6直屏、12GB+512GB组合
焦点快报!实验一 密码引擎-2-电子钥匙功能测试
CNStack 虚拟化服务:实现虚拟机和容器资源的共池管理
每日视讯:ChatGPT软件技术栈解密
天天微速讯:具有FTP、FTPS和sftp功能的文本编辑器——EditPlus功能介绍
今日热搜:【解答】MySQL MTR的实现原理与优势
核磁共振增强剂的危害_核磁共振增强剂的危害
环球热文:高德、阿里云发布全新车路协同方案 夜间开车像开了上帝视角
世界热文:瑞幸咖啡被吐槽喝完3口剩下全是冰 客服:门店按配方比例制作
世界快消息!育碧正式宣布“单飞”:E3展会再失重要参展商
独此一家!真我GT Neo5 SE梦幻续航组合:100W快充+5500mAh电池
每日速递:迪士尼《小美人鱼》真人电影确认引进内地:或5月上映
世界短讯!关于ChatGPT的一些信息,我画了一张思维导图
当前头条:前端设计模式——解释器模式
世界热点!各部队加强即将退役人员保密教育的一组见闻
焦点速递!男子2天喝4顿后死亡 起诉店家获赔:医生称如此喝酒很伤身
全球实时:价格相差数倍!智能手表比智能手环强在哪里?
昔日明星掌机落幕:任天堂正式关闭3DS游戏商店
最新消息:李想:攻破燃油车 需3步