当前位置: 首页 > news >正文

如何在网站上添加qq深圳做分销网站的公司

如何在网站上添加qq,深圳做分销网站的公司,进一步加强舆情管控,vue seo 优化方案记录一种在内核空间向用户空间通知中断的方法 0.前言1.代码实现1)内核设备驱动实现2)消息通知实现3)测试程序 2.解析 参考文章#xff1a;Linux驱动实践#xff1a;中断处理函数如何【发送信号】给应用层#xff1f; 0.前言 最近在项目中遇到一个需求#xff0c;需要将一个… 记录一种在内核空间向用户空间通知中断的方法 0.前言1.代码实现1)内核设备驱动实现2)消息通知实现3)测试程序 2.解析 参考文章Linux驱动实践中断处理函数如何【发送信号】给应用层 0.前言 最近在项目中遇到一个需求需要将一个设备的中断状态通知到用户态的一个指定程序中该设备的整体架构如下   CPU通过SPI与设备通信在设备中存在一个ISR控制单元当设备发生中断时会在ISR单元中给对应的寄存器置位CPU可以通过轮询这些寄存器单元来判断这些中断是否发生。但这样做就失去了中断的意义内核依旧是通过轮询方式来扫描中断。   在原先的实现方案中SPI device通过内核通用接口挂载成了一个内核设备spidev笔者在此设备基础上重新在内核中创建了一个影子设备用来实现用户态程序的读写接口包括open()、release()、ioctl()接口等但笔者在后续尝试为这段ISR寄存器地址申请内核中断时发现官方驱动似乎存在一些问题会出现设备无法挂载或挂载后无法正常感知中断的结果。   所以笔者在同事的帮助下重新做了一个解决方案SPI device依旧通过内核通用接口挂载成内核设备但不创建影子设备用户态直接操作原设备此外为这段ISR地址单独实现一个platform driver将地址重映射到内存地址中这样当中断发生时通过内核的netlink函数通知指定的端口。 1.代码实现 1)内核设备驱动实现 spidev.h: #ifndef __SPIDEV_H__ #define __SPIDEV_H__#include linux/mutex.h #include linux/slab.h #include linux/compat.h #include linux/of.h #include linux/of_device.h #include linux/acpi.h #include linux/spi/spi.h #include linux/spi/spidev.h #include linux/uaccess.h #include linux/phy.h #include net/dsa.hstruct spidev_switch{dev_t devid;struct cdev cdev;struct class *class;struct device *device;struct device *parent;void __iomem * vir_base_addr;unsigned long spi_base_addr;unsigned long mem_size;struct spi_device *spidev;uint32_t speed_hz;struct mutex spi_api_lock;struct mii_bus* stmmac_mii;int irq;struct gpio_irq_dev *gpioDev;};typedef struct{uint32_t addr;uint32_t *data;uint32_t regCnt;uint32_t burstType;uint32_t crcFlag;int32_t ret; }switch_reg_t;struct gpio_irq_dev {struct gpio_desc *gpiod;int irq;char irq_name[10];char irq_drv_name[20]; }gpio_irq_dev_t;#define SWI_DEVICE_NAME spi-dev #define SWI_DEVICE_CNT 1 #define SWI_IOC_MAGIC k#define DRV_NAME 2404-spi #define DRV_VERSION 0.1.1 #define GPIO_INTR_NUM 3#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \| SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \| SPI_RX_QUAD | SPI_RX_OCTAL)#define WITCH_IOC_SYS_BUS_REG_RD _IOWR(SWI_IOC_MAGIC, 8, switch_reg_t) #define WITCH_IOC_SYS_BUS_REG_WR _IOWR(SWI_IOC_MAGIC, 9, switch_reg_t) #define WITCH_IOC_SPI_INNER_REG_RD _IOWR(SWI_IOC_MAGIC, 12, switch_reg_t) #define WITCH_IOC_SPI_INNER_REG_WR _IOWR(SWI_IOC_MAGIC, 13, switch_reg_t) #define WITCH_IOC_SPI_BURST_REG_RD _IOWR(SWI_IOC_MAGIC, 14, switch_reg_t) #define WITCH_IOC_SPI_BURST_REG_WR _IOWR(SWI_IOC_MAGIC, 15, switch_reg_t) #define SWITCH_IOC_GPIO_ISR_EN _IOWR(SWI_IOC_MAGIC, 16, switch_reg_t) #endif spidev.c: #include spidev.h #include my-netlink.h #include linux/of.h #include linux/of_gpio.h #include linux/irq.hstatic int spi_module_open (struct inode *node, struct file *filp) {struct cdev* cdev_t;cdev_t node-i_cdev;filp-private_data container_of(cdev_t, struct spidev_switch, cdev);return 0; }static ssize_t spi_module_read(struct file *filp, char *buff, size_t count, loff_t *offp) {return 0; }static ssize_t spi_module_write(struct file *filp, const char __user *buf, size_t count, loff_t *off) {return 0; }static int spi_module_release(struct inode *node, struct file *filp) {return 0; }static long int spi_module_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {int32_t retval 0;int32_t len;switch_reg_t switch_reg;uint32_t g_mem_reg_data[256] {0};struct spidev_switch* kgsw file-private_data;spidev_dbg_printf(spidev_LOG_DEBUG,%s:cmd:%x,arg:%x\n,__func__,cmd,arg);switch (cmd) {case WITCH_IOC_SYS_BUS_REG_RD:len _IOC_SIZE(cmd);retval copy_from_user(switch_reg, (const void __user *)arg, len);spidev_dbg_printf(spidev_LOG_DEBUG,%s:retval:%x\n,__func__,retval);if (retval 0){switch_reg.ret spidev_apb_reg_read32(kgsw,switch_reg.addr, \switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);retval copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));spidev_dbg_printf(spidev_LOG_DEBUG,%s:retval:%x\n,__func__,retval);} break;case WITCH_IOC_SYS_BUS_REG_WR:len _IOC_SIZE(cmd);retval copy_from_user(switch_reg, (const void __user *)arg, len);if (retval 0){retval copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));if (retval 0){switch_reg.ret spidev_apb_reg_write32(kgsw,switch_reg.addr, \switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);spidev_dbg_printf(spidev_LOG_DEBUG,%s:retval:%x\n,__func__,retval);}} break;case WITCH_IOC_SPI_INNER_REG_RD:len _IOC_SIZE(cmd);retval copy_from_user(switch_reg, (const void __user *)arg, len);if (retval 0){switch_reg.ret spi_inner_reg_read32(kgsw,switch_reg.addr,g_mem_reg_data);retval copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));}break;case WITCH_IOC_SPI_INNER_REG_WR:len _IOC_SIZE(cmd);retval copy_from_user(switch_reg, (const void __user *)arg, len);if (retval 0){retval copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));if (retval 0){switch_reg.ret spi_inner_reg_write32(kgsw,switch_reg.addr,g_mem_reg_data);}}break;case WITCH_IOC_SPI_BURST_REG_RD:len _IOC_SIZE(cmd);retval copy_from_user(switch_reg, (const void __user *)arg, len);if (retval 0){switch_reg.ret spi_burst_reg_read32(kgsw,switch_reg.addr, \switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);retval copy_to_user((void __user *)switch_reg.data, g_mem_reg_data, switch_reg.regCnt*sizeof(uint32_t));}break;case WITCH_IOC_SPI_BURST_REG_WR:len _IOC_SIZE(cmd);retval copy_from_user(switch_reg, (const void __user *)arg, len);if (retval 0){retval copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));if (retval 0){switch_reg.ret spi_burst_reg_write32(kgsw,switch_reg.addr, \switch_reg.regCnt,switch_reg.burstType,g_mem_reg_data);}}break;case SWITCH_IOC_GPIO_ISR_EN:len _IOC_SIZE(cmd);retval copy_from_user(switch_reg, (const void __user *)arg, len);if (retval 0){retval copy_from_user(g_mem_reg_data, (const void __user *)switch_reg.data, switch_reg.regCnt*sizeof(uint32_t));if (retval 0){switch_reg.ret gpio_irq_enable(kgsw, switch_reg.addr);}}break;default:spidev_dbg_printf(spidev_LOG_ERROR, %s nuknow cmd type\n,__func__);break;}return retval; }static struct file_operations switch_module_opts {.owner THIS_MODULE,.open spi_module_open,.read spi_module_read,.write spi_module_write,.unlocked_ioctl spi_module_ioctl,.release spi_module_release, };static int is_dev_creat 0; int spidev_dev_creat(struct spidev_switch* kgsw) {int major;int ret;if(is_dev_creat)return 0;/*alloc devid*/alloc_chrdev_region(kgsw-devid, 0, SWI_DEVICE_CNT, SWI_DEVICE_NAME);major MAJOR(kgsw-devid);/*register char dev*/cdev_init(kgsw-cdev, switch_module_opts);ret cdev_add(kgsw-cdev, kgsw-devid, SWI_DEVICE_CNT);if (ret) {goto del_unrigister;}/*creat device node*/kgsw-class class_create(THIS_MODULE, SWI_DEVICE_NAME);if (IS_ERR(kgsw-class)) {goto del_cdev;} kgsw-device device_create(kgsw-class, NULL, kgsw-devid, NULL, SWI_DEVICE_NAME);if (IS_ERR(kgsw-device)) {goto destroy_class;}find_spi_dev(kgsw);of_parse_spidev_reg_addr(kgsw);is_dev_creat 1;return 0;destroy_class:device_destroy(kgsw-class, kgsw-devid); del_cdev:cdev_del(kgsw-cdev); del_unrigister:unregister_chrdev_region(kgsw-devid, SWI_DEVICE_CNT);is_dev_creat 0;return -EIO; }static irqreturn_t switch_2404_isr_func(int irq, void *dev_id) {//printk(KERN_NOTICE Enter switch 2404 irq(%d) function\n, irq);struct spidev_switch* kgsw (struct spidev_switch*)dev_id;netlink_msg_t nlg;memset(nlg, 0, sizeof(netlink_msg_t));void __iomem* vir_addr2 0x30800068 - kgsw-spi_base_addr kgsw-vir_base_addr;writel(1,vir_addr2);nlg.cmd_type REPORT_IRQ_TO_USER;nlg.irq_type SWITCH_INTERRUPT_TYPE;nlg.irq_number irq;printk(KERN_NOTICE Kernel netlink send to user: irq_type(0x%x) irq_number(%x)\n, nlg.irq_type, nlg.irq_number);myspi_send_to_usr_msg(nlg);return IRQ_HANDLED; }static irqreturn_t myspi_gpio_isr_func(int irq, void *dev_id) {netlink_msg_t nlg;memset(nlg, 0, sizeof(netlink_msg_t));//disable_irq_nosync(irq);nlg.cmd_type REPORT_IRQ_TO_USER;nlg.irq_type GPIO_INTR_TYPE;nlg.irq_number irq;printk(KERN_NOTICE Kernel netlink send to user: irq_type(0x%x) irq_number(%x)\n, nlg.irq_type, nlg.irq_number);myspi_send_to_usr_msg(nlg);return IRQ_HANDLED; }static int myspi_gpio_irq_init(struct spidev_switch* kgsw) {int rc;struct gpio_irq_dev *gpio_dev NULL;struct device *dev kgsw-parent;int index 0;gpio_dev devm_kzalloc(dev, sizeof(*gpio_dev) * GPIO_INTR_NUM, GFP_KERNEL);if (!gpio_dev){dev_err(dev, no memory.\n);return -ENOMEM;}for (index 0; index GPIO_INTR_NUM; index){sprintf(gpio_dev[index].irq_name, %d, index);sprintf(gpio_dev[index].irq_drv_name, %d-gpio-irq, index);gpio_dev[index].gpiod devm_gpiod_get(dev, gpio_dev[index].irq_name, GPIOD_IN);if (IS_ERR(gpio_dev[index].gpiod)) {rc PTR_ERR(gpio_dev[index].gpiod);if (rc ! -EPROBE_DEFER)dev_err(dev, error getting gpio (%d)\n, rc);goto irq_err;}gpio_dev[index].irq gpiod_to_irq(gpio_dev[index].gpiod);if (gpio_dev[index].irq 0){goto irq_err;} rc devm_request_irq(dev, gpio_dev[index].irq, myspi_gpio_isr_func, IRQF_TRIGGER_RISING, gpio_dev[index].irq_drv_name, gpio_dev[index]); if (rc){dev_err(dev, unable to claim IRQ %d\n, gpio_dev[index].irq);goto irq_err;}else{printk(KERN_NOTICE gpio-irq%d %s request success!\n, gpio_dev[index].irq, gpio_dev[index].irq_drv_name);}}kgsw-gpioDev gpio_dev;return 0;irq_err:devm_kfree(dev, gpio_dev);return rc; }static int spidev_probe(struct platform_device *pdev) {struct spidev_switch* kgsw;int ret 0;kgsw devm_kzalloc(pdev-dev, sizeof(struct spidev_switch), GFP_KERNEL);kgsw-parent pdev-dev;kgsw-irq platform_get_irq(pdev, 0);if (kgsw-irq 0){dev_err(pdev-dev, irq number invalid\n);goto err_out;}ret spidev_dev_creat(kgsw);if (ret 0) {dev_err(pdev-dev, device create fail!\n);goto err_out;} ret request_irq(kgsw-irq, switch_2404_isr_func, IRQF_SHARED, dev_name(pdev-dev), kgsw-devid);if (ret 0 ret ! -ENOTCONN) {dev_err(pdev-dev, can not get IRQ\n);goto err_out;}platform_set_drvdata(pdev, kgsw);myspi_gpio_irq_init(kgsw);myspi_netlink_init();spidev_dbg_printf(spidev_LOG_INFO, %s probe done\n, DRV_NAME);return 0;err_out:return -ENXIO; }static int spidev_remove(struct platform_device *pdev) {struct spidev_switch* kgsw dev_get_drvdata(pdev-dev);printk(KERN_NOTICE Delete device files\n);device_destroy(kgsw-class, kgsw-devid);printk(KERN_NOTICE Delete class files\n);class_destroy(kgsw-class);printk(KERN_NOTICE Deregister the character device driver\n);cdev_del(kgsw-cdev);//Indicates device numbers for deregistering applicationsunregister_chrdev_region(kgsw-devid, SWI_DEVICE_CNT);myspi_netlink_exit();free_irq(kgsw-irq, kgsw-devid);is_dev_creat 0;return 0; }static const struct of_device_id spidev_switch_of_match[] {{ .compatible my-spidev },{ }, };MODULE_DEVICE_TABLE(of, spidev_switch_of_match);static struct platform_driver spidev_driver {.probe spidev_probe,.remove spidev_remove,.driver {.name spidev switch driver,.of_match_table spidev_switch_of_match,}, };static int __init spidev_switch_init(void) {printk(KERN_NOTICE DRV_NAME : version %s loaded\n, DRV_VERSION);return platform_driver_register(spidev_switch_driver); }static void __exit spidev_switch_exit(void) {printk(KERN_NOTICE DRV_NAME : version %s unloaded\n, DRV_VERSION);platform_driver_unregister(spidev_switch_driver); }module_init(spidev_switch_init); module_exit(spidev_switch_exit);MODULE_DESCRIPTION(Driver for myspi switch); MODULE_VERSION(DRV_VERSION); MODULE_LICENSE(GPL v2); 2)消息通知实现 my_netlink.h: #ifndef __MYSPI_SWITCH_NETLINK_H__ #define __MYSPI_SWITCH_NETLINK_H__#define NETLINK_IRQ_REPORT_USER (MAX_LINKS-1) #define NETLINK_DEFAULT_PORT_ID 0 #define NETLINK_DEFAULT_SEQ 0 #define NETLINK_DEFAULT_FLAGS 0 #define NETLINK_USER_PORT 100#define INTERRUPT_TYPE 0x24040000 #define PHY_GPIO_INTR_FMC0_TYPE 0x24040001 #define PHY_GPIO_INTR_FMC1_TYPE 0x24040002 #define PHY_GPIO_INTR_FMC2_TYPE 0x24040003enum {ASYNC_READ_CMD 1,ASYNC_WRITE_CMD 2,SHARE_MEM_REG_UPDATE 3,SHARE_MEM_REG_UPDATE_STOP 4,REPORT_IRQ_TO_USER 5, };typedef struct netlink_msg {int cmd_type;int irq_type;int irq_number; }netlink_msg_t;int myspi_netlink_init(void); void myspi_netlink_exit(void); int myspi_send_to_usr_msg(netlink_msg_t* reg); #endifmy_netlink.c: #include linux/init.h #include linux/module.h #include linux/types.h #include net/sock.h #include linux/netlink.h #include myspi_switch_netlink.hstatic struct sock *nlsk NULL;int myspi_send_to_usr_msg(netlink_msg_t* msg) {struct sk_buff *nl_skb;struct nlmsghdr *nlh;int ret;//creat sk_buffnl_skb nlmsg_new(sizeof(netlink_msg_t), GFP_ATOMIC);//what time to free;if(!nl_skb){printk(netlink alloc failure\n);return -1;}// set netlink head msg// 填充nlmsg报文头并最终将报文填充到sk_buff发送出去nlh nlmsg_put(nl_skb, NETLINK_DEFAULT_PORT_ID, NETLINK_DEFAULT_SEQ, NETLINK_IRQ_REPORT_USER, sizeof(netlink_msg_t), NETLINK_DEFAULT_FLAGS); if(nlh NULL){printk(nlmsg_put failaure \n);nlmsg_free(nl_skb);return -1;}//send msg; nl_skb will free automaticmemcpy(nlmsg_data(nlh), msg, sizeof(netlink_msg_t));ret netlink_unicast(nlsk, nl_skb, NETLINK_USER_PORT, MSG_DONTWAIT); // 指定端口号端口号是唯一识别目的地址的标识广播非阻塞式发送return ret; }static void netlink_rcv_msg(struct sk_buff *skb) {struct nlmsghdr *nlh NULL;netlink_msg_t *reg_data NULL;if(skb-len nlmsg_total_size(0)){nlh nlmsg_hdr(skb); // 取出报文头reg_data NLMSG_DATA(nlh); // 取出报文内容if(reg_data){//print_regdata(__func__,__LINE__,reg_data);}} }static struct netlink_kernel_cfg cfg { .input netlink_rcv_msg, /* set recv callback */ };int myspi_netlink_init(void) {/* create netlink socket */printk( Kernel netlink create);nlsk (struct sock *)netlink_kernel_create(init_net, NETLINK_IRQ_REPORT_USER, cfg);if(nlsk NULL){ printk(Kernel netlink create error!\n);return -1; } return 0; }void myspi_netlink_exit(void) {printk( Kernel netlink exit!\n);if (nlsk){netlink_kernel_release(nlsk); /* release ..*/nlsk NULL;} }3)测试程序 test.c #include stdio.h #include stdlib.h #include sys/socket.h #include string.h #include linux/netlink.h #include stdint.h #include unistd.h #include errno.h#define NETLINK_REG_ASYNC_RW (MAX_LINKS-1) //31 #define NETLINK_USER_PORT 100enum {ASYNC_READ_CMD 1,ASYNC_WRITE_CMD 2,SHARE_MEM_REG_UPDATE 3,SHARE_MEM_REG_UPDATE_STOP 4,REPORT_IRQ_TO_USER 5, };typedef struct netlink_msg {struct nlmsghdr hdr;int cmd;int irq_type;int irq_number; }netlink_msg_t;typedef struct netlink_handle {int skfd;struct sockaddr_nl saddr;struct sockaddr_nl daddr; } netlink_handle_t;static netlink_handle_t async_netlink_handle;netlink_handle_t* get_async_netlink_handle(void) {return async_netlink_handle; }int switch_netlink_init(void) {int skfd;struct sockaddr_nl* saddr; //saddr 表示源端口地址daddr表示目的端口地址struct sockaddr_nl* daddr;netlink_handle_t* handle;handle get_async_netlink_handle();saddr (handle-saddr);daddr (handle-daddr);/* 创建NETLINK socket */skfd socket(AF_NETLINK, SOCK_RAW, NETLINK_REG_ASYNC_RW);if(skfd -1){perror(create socket error\n);return -1;}memset(saddr, 0, sizeof(struct sockaddr_nl));saddr-nl_family AF_NETLINK; //AF_NETLINKsaddr-nl_pid NETLINK_USER_PORT; //端口号(port ID) saddr-nl_groups 0;if(bind(skfd, (struct sockaddr *)saddr, sizeof(struct sockaddr_nl)) ! 0){perror(bind() error\n);close(skfd);return -1;}memset(daddr, 0, sizeof(struct sockaddr_nl));daddr-nl_family AF_NETLINK;daddr-nl_pid 0; // to kernel daddr-nl_groups 0;handle-skfd skfd;return 0; }int switch_recv_reg_msg(void) {int ret;netlink_msg_t recv_msg;socklen_t len;netlink_handle_t* handle;handle get_async_netlink_handle();memset(recv_msg, 0, sizeof(netlink_msg_t));len sizeof(struct sockaddr_nl);ret recvfrom(handle-skfd, recv_msg, sizeof(netlink_msg_t), 0, (struct sockaddr *)(handle-daddr), len);if(!ret){printf(%s,%d, err\n,__func__,__LINE__);return -1;}switch(recv_msg.cmd){case REPORT_IRQ_TO_USER:printf(User netlink recv from kernel: irq_type(%x), irq_number(%d)\n,recv_msg.irq_type, recv_msg.irq_number);break;default:printf(Unknown command\n);break;}return 0; }int main(int argc, char *argv[]){if (fork() 0){printf(Create a child process, pid %d\n, getpid());int ret 0;ret switch_netlink_init();if (ret){return ret;}while(1){switch_recv_reg_msg();}printf(main end\n);}else{printf(Father process, pid %d\n, getpid());return;} }2.解析 在设备驱动文件中实现了一个platform子系统的设备驱动通过指定的compatible来匹配并实现了设备的probe、remove、ioctl操作等接口并且在创建内核设备节点时为设备申请了spi设备中断和gpio中断并初始化了一个netlink通信端口。当设备发生中断时会触发回调函数中的netlink接口向指定的用户端口发送中断号以及中断类型这样就可以在用户程序中进行后续操作。 测试小程序中主要是测试netlink的接收在while(1)循环中一直监听等待内核驱动的信息。 注此文中仅为大致的实现框架仅供参考具体设备的详细实现流程需根据实际情况进行修改。
http://www.dnsts.com.cn/news/21560.html

相关文章:

  • 哪家做网站比较好wordpress镜像是什么意思
  • 成都电子商务网站建设公司网站基础建设
  • 招生网站建设的意义免费公众号模板编辑器
  • h5页面怎么生成链接河南网站seo推广
  • 一键生成100个原创视频百度关键词优化快速排名软件
  • 企业网站制作公司排名电子简历手机版免费
  • 工程建设举报网有哪些网站注册企业邮箱需要什么
  • 聊城手机网站建设公司阿里云域名备案流程详细
  • php网站搬家教程伽师网站建设
  • 视频网站设计苏州模板网站建站
  • 怎么建网站新手入门网站与网页的区别与联系
  • 企业服务网站建设方案上海高端模板建站
  • 国外网站托管桥的设计网站建设
  • 做电影网站赚钱吗适响应式网站弊端
  • 格尔木市建设局网站电商开店流程及费用
  • 汕头网站制作哪家强华为网站建设建议
  • 上海互联网网站建设公司crm系统免费
  • 怎么利用自媒体做网站优化wordpress网站是什么
  • 品牌网站建设还来大蝌蚪网站的功能规范
  • 送网站建设wordpress页脚小工具导航
  • 做360手机网站优化排seo网站源码
  • 企业网站怎么查济南哪个网站建设最好
  • 购物网站设计理念君隆网站建设
  • 自己弄一个网站要多少钱专做hiphop的网站
  • 东莞网上做公司网站常州设计网站
  • h5做招聘网站可以吗温州优化网站
  • 网站网页开发公司如何提高wordpress访问速度
  • 常见的网站建设技术长沙有哪些大型工厂
  • 网站板块设计移动商城touch版h5页面
  • 辞职做网站抖音seo推荐算法