收藏 分销(赏)

PCI总线、PCI设备、驱动知识点.doc

上传人:xrp****65 文档编号:7665998 上传时间:2025-01-11 格式:DOC 页数:40 大小:621.50KB 下载积分:10 金币
下载 相关 举报
PCI总线、PCI设备、驱动知识点.doc_第1页
第1页 / 共40页
PCI总线、PCI设备、驱动知识点.doc_第2页
第2页 / 共40页


点击查看更多>>
资源描述
  PCI设备驱动知识点 分类: linux编程2010-12-31 16:55 132人阅读 评论(0) 收藏 举报      申明:此文章并非本人所写,只是加以修改和备注 原帖请看: 那位大大可是个大牛人,上面有不少好东东!  一、PCI简介      PCI是一种外设总线规范。我们先来看一下什么是总线:总线是一种传输信号的路径或信道。典型情况是,总线是连接于一个或多个导体的电气连线,总线上连接的所有设备可在同一时间收到所有的传输内容。总线由电气接口和编程接口组成。本文讨论Linux 下的设备驱动,所以,重点关注编程接口。     PCI是Peripheral Component Interconnect(外围设备互联)的简称,是普遍使用在桌面及更大型的计算机上的外设总线。PCI架构被设计为ISA标准的替代品,他有三个主要目标:获得在计算机和外设之间传输数据时更好的性能;尽可能的平台无关;简化往系统中添加和删除外设的工作。 二、PCI寻址     从目前开始,我想尽可能通过一些实际的例子来说明问题,而减少理论方面的问题的描述,因为,相关的理论的东西,能在其他地方找到。         我们先来看一个例子,我的计算机装有1G的RAM,1G以后的物理内存地址空间都是外部设备IO在系统内存地址空间上的映射。/proc/iomem描述了系统中所有的设备I/O在内存地址空间上的映射。我们来看地址从1G开始的第一个设备在/proc/iomem中是怎么描述的:             40000000-400003ff : 0000:00:1f.1          这是个PCI设备,40000000-400003ff是他所映射的内存地址空间,占据了内存地址空间的1024bytes的位置,而0000:00:1f.1则是个PCI外设的地址,以冒号和逗号分隔为4个部分: 第一个16位表示域; 第二个8位表示一个总线编号,2^8-256,故每个域最多能有256个总线; 第三个5位表示一个设备号,每个总线最多能挂载32个设备; 最后是3位,表示功能号,每个设备最多能有8种功能,也就是最多能够对应8个逻辑设备,每种功能都唯一的对应一个pci_dev结构体。     注:因为PCI规范允许单个系统拥有高达256个总线,但对于大型系统而言,这是不够的,所以,引入了域的概念。       由此,我们能得出上述的PCI设备的地址是0号域0号总线上的31号设备上的1号功能。那上述的这个PCI设备到底是什么呢?下面是我的计算机上的lspci命令的输出:     00:00.0 Host bridge: Intel Corporation 82845 845 (Brookdale) Chipset Host Bridge (rev 04)     00:01.0 PCI bridge: Intel Corporation 82845 845 (Brookdale) Chipset AGP Bridge(rev 04)     00:1d.0 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #1) (rev 02)     00:1d.1 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #2) (rev 02)     00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 42)     00:1f.0 ISA bridge: Intel Corporation 82801CAM ISA Bridge (LPC) (rev 02)     00:1f.1 IDE interface: Intel Corporation 82801CAM IDE U100 (rev 02)     00:1f.3 SMBus: Intel Corporation 82801CA/CAM SMBus Controller (rev 02)     00:1f.5 Multimedia audio controller:Intel Corporation 82801CA/CAM AC’97 Audio Controller (rev 02)     00:1f.6 Modem: Intel Corporation 82801CA/CAM AC’97 Modem Controller (rev 02)     01:00.0 VGA compatible controller: nVidia Corporation NV17 [GeForce4 420 Go](rev a3)     02:00.0 FireWire (IEEE 1394): VIA Technologies, Inc. IEEE 1394 Host Controller(rev 46)     02:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+(rev 10)     02:04.0 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)     02:04.1 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)     lspci没有标明域,但对于一台PC而言,一般只有一个域,即0号域。通过这个输出我们能看到他是个IDE interface。由上述的输出能看到,我的计算机上共有3个PCI总线(0号,1号,2号)。在单个系统上,插入多个总线是通过桥(bridge)来完成的,桥是一种用来连接总线的特别PCI外设。所以,PCI系统的整体布局组织为树型,我们能通过上面的lspci输出,来画出我的计算机上的PCI系统的树型结构: 0号总线 (主桥)--00:01(PCI桥)                      |--00:1d(USB号控制器,提供了0、1这两个逻辑设备,即功能)                                |--00:1e:0(PCI桥)                      |--00:1f.多功能卡(提供了0 ISA bridge、1 IDE interface、3SMBus、5 Multimedia audio controller、6 Modem这5个逻辑设备,即功能) 1号总线(主桥)--01:00.0 VGA compatible controller  2号总线(主桥)--02:00.0 IEEE1394                            |--02:01.0 8139网卡                              |--02:04 CardBus桥(提供了桥0、1两个逻辑设备,即功能)      由上图能得出,我的计算机上共有8个PCI设备,其中0号总线上(主桥)上连有4个,1号总线上连有1个,2号总线上连有3个。00:1f是个连有5个功能的多功能板卡。每一个PCI设备都有他映射的内存地址空间和他的I/O区域,这点是比较容易理解的。除此之外,PCI设备还有有他的设置寄存器。有了设置寄存器,PCI的驱动程式就不必探测就能访问设备。设置寄存器的布局是标准化的,设置空间的4个字节含有一个独一无二的功能ID,因此,驱动程式可通过查询外设的特定ID来识别其设备。所以,PCI接口标准在ISA之上的主要创新在于设置地址空间。      在系统引导阶段,PCI硬件设备保持未激活状态,但每个PCI主板均配备有能够处理PCI的固件,固件通过读写PCI控制器中的寄存器,提供了对设备设置地址空间的访问。设置地址空间的前64字节是标准化的,他提供了厂商号,设备号,版本号等信息,唯一标识一个PCI设备。同时,他也提供了最多可多达6个的I/O地址区域,每个区域能是内存也能是I/O地址。这几个I/O地址区域是驱动程式找到设备映射到内存和I/O空间的具体位置的唯一途径。有了这两点,PCI驱动程式就完成了相当于探测的功能。关于这64个字节的设置空间的周详情况,可参阅《Linux设备驱动程式第三版》P306,图12-2,不再详述。     下面,我们来看一下8139too网卡设备的设置空间的周详情况。在2.6内核的系统中,能在目录/sys/bus/pci/devices/下看到非常 多以PCI设备名命名的目录,但不是说这些设备都存在于你的系统中。我们进入8139too目录,其中有一个以他的设备地址0000:02:01.0命名,的目录。在这个目录下能找到该网卡设备相关的非常多信息。其中resource记录了他的6个I/O地址区域。内容如下:         0x0000000000003400 0x00000000000034ff   0x0000000000000101         0x00000000e0000800 0x00000000e00008ff   0x0000000000000200         0x0000000000000000 0x0000000000000000 0x0000000000000000         0x0000000000000000 0x0000000000000000 0x0000000000000000         0x0000000000000000 0x0000000000000000 0x0000000000000000         0x0000000000000000 0x0000000000000000 0x0000000000000000         0x0000000000000000 0x0000000000000000 0x0000000000000000     由该文件能看出,8139too设备使用了两个I/O地址区域,第一个是他映射的I/O端口范围,第二个是他映射的内存地址空间。关于这两个值能在/proc/iomem和/proc/ioport中得到验证。为了能看到实际的运行效果,我们选择8139too网卡作为示例,从该网卡的linux驱动程式中裁剪相关代码。     一个PCI设备的驱动程式必须要向内核中的PCI核心描述自己。同时,他也必须告诉PCI核心自己能够驱动哪些设备。下面,就介绍两个相关的重要数据结构。   /* 用于定义该驱动程序支持的不同类型的PCI设备列表*/     struct pci_device_id {         __u32 vendor;         __u32  device;       /* 指定设备的PCI厂商和设备ID,如驱动程序可以处理任何厂商或设备ID,可使用值PCI_ANY_ID*/         __u32 subvendor;         __u32 subdevice; /* 指定设备的PCI子系统厂商和设备ID,如驱动程序可处理任何子系统厂商或设备ID,可使用PCI_ANY_ID*/         __u32 class;         __u32 class_mask;    /* 可使驱动程序指定一种PCI类(class)设备,如果可以处理任何类型,则使用PCI_ANY_ID */         kernel_ulong_t driver_data; /* 如果需要,则用来保存PCI驱动程序用于区分不同设备的信息*/     };   /* 用于向PCI核心描述PCI驱动程序*/          struct pci_driver {         struct list_head node;          char *name;      /* 在内核的所有PCI驱动程序的名字必须唯一,通常设置为和驱动程序模块名相同的名字*/         struct module *owner;         const struct pci_device_id *id_table; //驱动所能操纵的设备id列表。         int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); //指向PCI驱动程序中的探测函数,用于插入新设备         void (*remove)(struct pci_dev *dev);   //移除设备         int (*suspend)(struct pci_dev *dev, pm_message_t state);   //指向挂起函数,挂起状态以state传递,该函数可选         int (*resume)(struct pci_dev *dev);    //指向恢复函数,总是在被挂起之后调用,该函数也可选         int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);   //使能唤事件         void (*shutdown) (struct pci_dev *dev);         struct device_driver    driver;         struct pci_dynids dynids;     };     pci_device_id唯一标识一个PCI设备。他的几个成员依次分别表示:厂商号、设备号、子厂商号、子设备号、类别、类别掩码(类可分为基类、子类)、私有数据。每一个PCI设备的驱动程式都有一个pci_device_id的数组,用于告诉PCI核心自己能够驱动哪些设备。8139too的驱动程式定义他的pci_device_id数组如下:         static struct pci_device_id rtl8139_pci_tbl[];     该数组被初始化为8139系列的一组网卡,当PCI核心得到这个数组后,会拿数组中的每一项跟从PCI设置空间中读取到的数据进行比对,从而为该驱动程式找到正确的设备。而pci_driver代表一个pci驱动程序。成员id_talbe即是指向pci_device_id数组的指针。name是驱动程序的名字,probe完成探测工作,即拿pci_device_id数组和内核中的数据进行比对。remove完成驱动程式的移除工作。关键的成员就这几 个。     驱动程式通过pci_module_init向内核注册自己(我们有时会看到pci_register_driver函数,其实他们是同一个,在内核代码中会看到,只是个简单的#define):             pci_module_init(&pci_driver);     调用函数后,如果pci_device_id数组中标识的设备存在于系统中,并且该设备恰好还没有驱动程式,则该驱动程式会被安装。下面我们来看从8139too驱动代码中裁剪的pci设备初始化代码: pci_driver.h: /* pci_driver.h * helinqiang@ * 2006-3-5 */ #ifndef PCI_DRIVER_H #define PCI_DRIVER_H #include   //for struct pci_device_id #include   //for MODULE_DEVICE_TABLE #include    //for struct pci_driver #define DRV_NAME    "8139too" #define DRV_VERSION "0.9.27" #define RTL8139_DRIVER_NAME   DRV_NAME " Fast Ethernet driver " DRV_VERSION typedef enum{     RTL8139 = 0,     RTL8129, }board_t; static struct pci_device_id rtl8139_pci_tbl[] = {     {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },     {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, #ifdef CONFIG_SH_SECUREEDGE5410     /* Bogus 8139 silicon reports 8129 without external PROM :-( */     {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, #endif #ifdef CONFIG_8139TOO_8129     {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, #endif /* some crazy cards report invalid vendor ids like      * 0x0001 here.  The other ids are valid and constant,      * so we simply don’t match on the main vendor id.      */     {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },     {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },     {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },     {0,} }; MODULE_DEVICE_TABLE(pci, rtl8139_pci_tbl); static int __devinit rtl8139_init_one(struct pci_dev *pdev, const struct pci_device_id *id); static void __devexit rtl8139_remove_one(struct pci_dev *pdev); static struct pci_driver rtl8139_pci_driver = {     .name       = DRV_NAME,     .id_table   = rtl8139_pci_tbl,     .probe      = rtl8139_init_one,     .remove     = __devexit_p(rtl8139_remove_one), }; #endif //PCI_DRIVER_H pci_driver.c: /* pci_driver.c * helinqiang@ * 2006-3-5 */ #include "pci_driver.h" #include  MODULE_AUTHOR("Linqiang He, Hangzhou China"); MODULE_LICENSE("Dual BSD/GPL"); static int __init rtl8139_init_module(void) {     /* when we’re a module, we always print a version message,      * even if no 8139 board is found.      */ #ifdef MODULE     printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); #endif     return pci_module_init(&rtl8139_pci_driver); } static void __exit rtl8139_cleanup_module (void) {     pci_unregister_driver(&rtl8139_pci_driver); } module_init(rtl8139_init_module); module_exit(rtl8139_cleanup_module); int __devinit rtl8139_init_one(struct pci_dev *pdev, const struct pci_device_id *id) {     //这里可插入各种调试代码,下文会有专门描述。     return 0; } void __devexit rtl8139_remove_one (struct pci_dev *pdev) { }     注册驱动程式成功后,rtl8139_init_one会被调用,在这个函数中,我们能通过插入一些打印输出语句看到PCI的设置地址空间和I/O地址区域的一些情况。     首先,插入以下语句:             u16 vendor, device;             pci_read_config_word(pdev, 0, &vendor);             pci_read_config_word(pdev, 2, &device);             printk(KERN_INFO "%x, %x\n", vendor, device);     这段代码读取了网卡设备的设置地址空间的前四位,他正好是设备的厂商号和设备号。下面是输出:             10ec, 8139     10ec和8139就是我的网卡的厂商号和设备号了。     再插入下列代码:             u32 addr1,addr2,addr3, addr4,addr5,addr6;             pci_read_config_dword(pdev, 16, &addr1);             pci_read_config_dword(pdev, 20, &addr2);             pci_read_config_dword(pdev, 24, &addr3);             pci_read_config_dword(pdev, 28, &addr4);             pci_read_config_dword(pdev, 32, &addr5);             pci_read_config_dword(pdev, 36, &addr6);             printk(KERN_INFO "%x,%x,%x,%x,%x,%x\n",addr1, addr2, addr3,addr4,addr5,addr6);     这段代码读取网卡设备的6个I/O地址区域的址始位置。下面是输出:    3401,e0000800,0,0,0,0     可见,该设备只使用了前两个I/O地址区域,分别标识他的I/O端口区域和内存地址空间。     另外,在这里,还可直接打印出网卡的MAC地址。不再详述。     我们能在rtl8139_init_one中插入一些不同的调试代码,观察设备驱动模块在内核中的一些动作。8139too 网卡设备的设备内存的头6个字节存放的是该网卡的48位的MAC地址,我们能通过访问设备内存得到这个MAC地址。下面通过在rtl8139_init_one在插入代码,以四种不同方式访问设备内存。第一种是通过访问I/O内存实现,后三种则是通过访问I/O端口的形式实现。 第一种: unsigned long mmio_start, addr1, addr2; void __iomem *ioaddr; mmio_start = pci_resource_start( pdev, 1); ioaddr = pci_iomap(pdev, 1, 0); addr1 = ioread32( ioaddr ); addr2 = ioread32( ioaddr + 4 ); printk(KERN_INFO "mmio start: %lX\n", mmio_start); printk(KERN_INFO "ioaddr: %p\n", ioaddr); printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lX\n", (addr1) & 0xFF, (addr1 >> 8) & 0xFF, (addr1 >> 16 ) & 0xFF, (addr1 >> 24 ) & 0xFF, (addr2) & 0xFF, (addr2 >> 8) & 0xFF ); 运行结果:   mmio start: E0000800   ioaddr: f8aa6800   00.02.3F.AC.41.9D 第二种: unsigned long pio_start, pio_len, addr1, addr2; void __iomem *ioaddr; pio_start = pci_resource_start( pdev, 0); pio_len = pci_resource_len (pdev, 0); ioaddr = ioport_map(pio_start, pio_len); addr1 = ioread32( ioaddr ); addr2 = ioread32( ioaddr + 4 ); printk(KERN_INFO "pio start: %lX\n", pio_start); printk(KERN_INFO "ioaddr: %p\n", ioaddr); printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lX\n", (addr1) & 0xFF, (addr1 >> 8) & 0xFF, (addr1 >> 16 ) & 0xFF, (addr1 >> 24 ) & 0xFF, (addr2) & 0xFF, (addr2 >> 8) & 0xFF ); 运行结果:    pio start: 3400    ioaddr: 00013400    00.02.3F.AC.41.9D 第三种: unsigned long pio_start, addr1, addr2; pio_start = pci_resource_start( pdev, 0 ); addr1 = inl( pio_start ); addr2 = inl( pio_start + 4 ); printk(KERN_INFO "port io start: %lX\n", pio_start); printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lX\n", (addr1) & 0xFF, (addr1 >> 8) & 0xFF, (addr1 >> 16) & 0xFF, (addr1 >> 24) & 0xFF, (addr2) & 0xFF, (addr2 >> 8) & 0xFF ); 运行结果:    port io start: 3400    00.02.3F.AC.41.9D 第四种: unsigned long pio_start; u8 addr1, addr2, addr3, addr4, addr5, addr6; pio_start = pci_resource_start( pdev, 0 ); addr1 = inb( pio_start ); addr2 = inb( pio_start + 1 ); addr3 = inb( pio_start + 2 ); addr4 = inb( pio_start + 3 ); addr5 = inb( pio_start + 4 ); addr6 = inb( pio_start + 5 ); printk(KERN_INFO "port io start: %lX\n", pio_start); printk(KERN_INFO "%02X.%02X.%02X.%02X.%02X.%02X\n", addr1, addr2, addr3, addr4, addr5, addr6 ); 运行结果:    port io start: 3400    00.02.3F.AC.41.9D   pci驱动的注册和初始化 分类: linux编程2011-01-13 10:30 222人阅读 评论(1) 收藏 举报     大家好,最近在看网络部分的代码,目前看到了网卡的初始化部分。书上讲到的内容主要是网卡驱动程序对网卡自身的初始化部分,即网卡驱动的probe函数是如何执行的,而很少讲到网卡是如何注册到系统中去的这一部分。     现在的网卡大部分都是连接到PCI总线上的。因此,网卡驱动是如何连接到PCI总线,又是如何与网卡设备联系起来,网卡在注册的最后又是如何调用到该网卡的probe函数的,这一个过程将在后面的文章中进行描述。整个文章分成两个部分,第一部分是讲解总线、设备以及驱动三者的联系,为第二部分具体讲解PCI总线、网卡设备和驱动做一点铺垫。     由于我在这方面也是初学,之所以想总结出来是想到在总结的过程中对自己的学习也是一个梳理的过程。所以有什么地方写得不好的,还请各位多多指正,非常感谢!也希望能在这里结识更多的朋友。     在总结的过程中参考了下面一些资料,在此表示感谢: [1] <Understanding Linux Network Internals> [2] <Linux那些事儿之我是U盘> [3] <Essential Linux Device Drivers> [4] <Linux Device Driver> 3rd Edition.  1. 总线、设备和驱动 1.1 简单介绍         Linux设备模型中三个很重要的概念就是总线、设备和驱动,即bus,device和driver。它们分别对应的数据结构分别为struct bus_type,struct device和struct device_driver。         总线是处理器与一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连。在最底层,Linux系统中的每一个设备都用device结构的一个实例来表示。而驱动则是使总线上的设备能够完成它应该完成的功能。         在系统中有多种总线,如PCI总线、SCSI总线等。系统中的多个设备和驱动是通过总线让它们联系起来的。在bus_type中两个很重要的成员就是struct kset drivers和struct kset devices。它分别代表了连接在这个总线上的两个链,一个是设备链表,另一个则是设备驱动链表。也就是说,通过一个总线描述符,就可以找到挂载到这条总线上的设备,以及支持该总线的不同的设备驱动程序。 1.2 总线、设备与驱动的绑定         在系统启动时,它会对每种类型的总线创建一个描述符,并将使用该总线的设备链接到该总线描述符的devices链上来。也即是说在系统初始化时,它会扫描连接了哪些设备,并且为每个设备建立一个struce device变量,然后将该变量链接到这个设备所连接的总线的描述符上去。另一方面,每当加载了一个设备驱动,则系统也会准备一个struct device_driver结构的变量,然后再将这个变量也链接到它所在总线的描述符的drivers链上去。                 对于设备来说,在结构体struct device中有两个重要的成员,一个是struct bus_type *bus,另一个是struct device_driver *driver。bus成员就表示该设备是链接到哪一个总线上的,而driver成员就表示当前设备是由哪个
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 教育专区 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服