ImageVerifierCode 换一换
格式:DOC , 页数:14 ,大小:150.50KB ,
资源ID:9010427      下载积分:10 金币
验证码下载
登录下载
邮箱/手机:
图形码:
验证码: 获取验证码
温馨提示:
支付成功后,系统会自动生成账号(用户名为邮箱或者手机号,密码是验证码),方便下次登录下载和查询订单;
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

开通VIP
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.zixin.com.cn/docdown/9010427.html】到电脑端继续下载(重复下载【60天内】不扣币)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

开通VIP折扣优惠下载文档

            查看会员权益                  [ 下载后找不到文档?]

填表反馈(24小时):  下载求助     关注领币    退款申请

开具发票请登录PC端进行申请。


权利声明

1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:4009-655-100;投诉/维权电话:18658249818。

注意事项

本文(linux内核I2C总线驱动实现.doc)为本站上传会员【pc****0】主动上传,咨信网仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知咨信网(发送邮件至1219186828@qq.com、拔打电话4009-655-100或【 微信客服】、【 QQ客服】),核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载【60天内】不扣币。 服务填表

linux内核I2C总线驱动实现.doc

1、linux内核I2C总线驱动实现 谈到在linux系统下编写I2C驱动,目前主要有两种方式,一种是把I2C设备当作一个普通的字符设备来处理,另一种是利用linux I2C驱动体系结构来完成。下面比较下这两种驱动。 第一种方法的好处(对应第二种方法的劣势)有:         ●    思路比较直接,不需要花时间去了解linux内核中复杂的I2C子系统的操作方法。 第一种方法问题(对应第二种方法的好处)有:         ●    要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器操作;         ●    要求工程师对I2C的设备器及I2C的设备操作方法都

2、比较熟悉,最重要的是写出的程序可移植性差;         ●    对内核的资源无法直接使用。因为内核提供的所有I2C设备器及设备驱动都是基于I2C子系统的格式。I2C适配器的操作简单还好,如果遇到复杂的I2C适配器(如:基于PCI的I2C适配器),工作量就会大很多。 本文针对的对象是熟悉I2C协议,并且想使用linux内核子系统的开发人员。网络和一些书籍上有介绍I2C子系统的源码结构。但发现很多开发人员看了这些文章后,还是不清楚自己究竟该做些什么。究其原因还是没弄清楚I2C子系统为我们做了些什么,以及我们怎样利用I2C子系统。本文首先要解决是如何利用现有内核支持的I2C适配器,完成对I

3、2C设备的操作,然后再过度到适配器代码的编写。本文主要从解决问题的角度去写,不会涉及特别详细的代码跟踪。 I2C的通信肯定至少要有2个芯片完成,所以它的驱动是由2大部分组成:主芯片的i2c的驱动、从芯片的i2c的驱动。   注:万一选的都不支持怎么办?(只能2个芯片的驱动都得实现了,不过过程差不多) 1 分析linux内核中I2C驱动框架 1 .1主芯片的I2C的驱动 首先要查看linux内核是否支持主芯片中i2c驱动器,如果支持就配置一下就ok了,否则要编写主控芯片的i2c驱动器。 编写方法: (1)要有i2c总线驱动(首先要查查内核i2c文件是否支持这种总线驱动,一

4、般都有支持,如果没有只好自己写了) (2)i2c设备驱动(主控芯片的地址等等信息) 这个过程都是差不多的,以后在分析。一般的主控芯片的i2c控制器linux内核基本上支持的很好,如:2410的i2c驱动器的支持。 1.2从芯片的I2C的驱动 下面主要分析从芯片的I2C驱动(也有2种方式,第一个是利用内核提供的i2c-dev.c来构建,另一个是自己写) 主要分析第一种方式: 利用系统给我们提供的i2c-dev.c来实现一个i2c适配器的设备文件。然后通过在应用层操作i2c适配器来控制i2c设备。 i2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read()、write

5、)和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的i2c设备的存储空间或寄存器,并控制I2C设备的工作方式。 需要特别注意的是:i2c-dev.c的read()、write()方法都只适合于如下方式的数据格式(可查看内核相关源码) 图1 单开始信号时序 所以不具有太强的通用性,如下面这种情况就不适用(通常出现在读目标时)。 图2 多开始信号时序 但是read和write方法适用性有限,只适用用于适配器支持i2c算法的情况,如: static const struct i2c_algorithm s3c24xx_i2c_algorithm = {    

6、         .master_xfer = s3c24xx_i2c_xfer,             .functionality = s3c24xx_i2c_func,         }; 而不适合适配器只支持smbus算法的情况,如:         static const struct i2c_algorithm smbus_algorithm = {             .smbus_xfer = i801_access,             .functionality = i801_func,         }; 基于以上原因,一般都不会使用

7、i2c-dev.c的read()、write()方法。最常用的是ioctl()方法。ioctl()方法可以实现上面所有的情况(两种数据格式、以及I2C算法和smbus算法)。  针对i2c的算法,需要熟悉struct i2c_rdwr_ioctl_data 、struct i2c_msg。使用的命令是I2C_RDWR。         struct i2c_rdwr_ioctl_data      {              struct i2c_msg __user *msgs; /* pointers to i2c_msgs */               __u32 n

8、msgs; /* number of i2c_msgs */           };         struct i2c_msg {             _ _u16 addr; /* slave address */             _ _u16 flags; /* 标志(读、写) */              _ _u16 len; /* msg length */             _ _u8 *buf; /* pointer to msg data */         }; 针对smbus算法,需要熟悉struct i2c_smbus_ioct

9、l_data。使用的命令是I2C_SMBUS。对于smbus算法,不需要考虑“多开始信号时序”问题。         struct i2c_smbus_ioctl_data {             __u8 read_write; //读、写             __u8 command; //命令             __u32 size; //数据长度标识             union i2c_smbus_data __user *data; //数据         }; 下面以一个实例讲解操作的具体过程。通过S3C2410操作AT24C02 e2pr

10、om。实现在AT24C02中任意位置的读、写功能。 首先在内核中已经包含了对s3c2410 中的i2c控制器(总线驱动)驱动的支持。提供了i2c算法(非smbus类型的,所以后面的ioctl的命令是I2C_RDWR)         static const struct i2c_algorithm s3c24xx_i2c_algorithm = {             .master_xfer = s3c24xx_i2c_xfer,             .functionality = s3c24xx_i2c_func,         };   另外一方面需要确定

11、为了实现对AT24C02 e2prom的操作,需要确定从机芯片的地址及读写访问时序。 ●      AT24C02地址的确定 原理图上将A2、A1、A0都接地了,所以地址是0x50。 ●        AT24C02任意地址字节写的时序 可见此时序符合前面提到的“单开始信号时序” ●        AT24C02任意地址字节读的时序 可见此时序符合前面提到的“多开始信号时序” 下面开始具体代码的分析(代码在2.6.22内核上测试通过):         /*i2c_test.c         * hongtao_liu         *

12、/         #include         #include         #include         #include         #include         #include         #include         #include         #define I2C_RETRIES 0x0701         #define I2C_T

13、IMEOUT 0x0702         #define I2C_RDWR 0x0707          /*********定义struct i2c_rdwr_ioctl_data和struct i2c_msg,要和内核一致*******/      #define I2C_M_TEN 0x0010         #define I2C_M_RD 0x0001   struct i2c_msg         {                 unsigned short addr;                 unsigned short flag

14、s;                 unsigned short len;                 unsigned char *buf;         };   struct i2c_rdwr_ioctl_data         {                 struct i2c_msg *msgs;                 int nmsgs;         /* nmsgs这个数量决定了有多少开始信号,对于“单开始时序”,取1*/         };    /***********主程序***********/         

15、int main()         {                 int fd,ret;                 struct i2c_rdwr_ioctl_data e2prom_data;                 fd=open("/dev/i2c-0",O_RDWR);      /* 为什么是i2c-0呢??那就要到内核里看啦,等会再说。 open底层调用了i2c_get_adapter(int id)函数,这个函数很重要,他可以识别占用了哪个i2c总线使用地0个i2c控制器 /dev/i2c-0是在注册i2c-dev.c后产生的,代表一个可操作的适配

16、器。如果不使用i2c-dev.c(这里说啦上面的为什么) 的方式,就没有,也不需要这个节,i2c_driver结构体中有attach_adapter方法:里面用device_create(i2c_dev_class, &adap->dev,MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d",adap->nr);I2C_MAJOR=89,即i2c-dev.c针对每个i2c适配器生成一个主设备号位89的设备文件,次设备要自己定义。  /dev/i2c-0是在注册i2c-dev.c后产生的,代表一个可操作的适配器。如果不使用i2c-dev.c 的方式,就没有,也不需

17、要这个节点。          */                 if(fd<0)                 {                         perror("open error");                 }                 e2prom_data.nmsgs=2;         /*         *因为操作时序中,最多是用到2个开始信号(字节读操作中),所以此将         *e2prom_data.nmsgs配置为2         */                 e2prom_data.ms

18、gs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));                 if(!e2prom_data.msgs)                 {                         perror("malloc error");                         exit(1);                 }                 ioctl(fd,I2C_TIMEOUT,1);/*超时时间*/                 ioc

19、tl(fd,I2C_RETRIES,2);/*重复次数*/                 /***write data to e2prom**/ /**/                 e2prom_data.nmsgs=1;                 (e2prom_data.msgs[0]).len=2; //1个 e2prom 写入目标的地址和1个数据                 (e2prom_data.msgs[0]).addr=0x50;//e2prom 设备地址                 (e2prom_data.msgs[0]).flags=0

20、 //write                 (e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);                 (e2prom_data.msgs[0]).buf[0]=0x10;// e2prom 写入目标的地址                 (e2prom_data.msgs[0]).buf[1]=0x58;//the data to write              ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);            

21、     if(ret<0)                 {                         perror("ioctl error1");                 }                 sleep(1);         /******read data from e2prom*******/                 e2prom_data.nmsgs=2;                 (e2prom_data.msgs[0]).len=1; //e2prom 目标数据的地址                 (e2prom

22、data.msgs[0]).addr=0x50; // e2prom 设备地址                 (e2prom_data.msgs[0]).flags=0;//write                 (e2prom_data.msgs[0]).buf[0]=0x10;//e2prom数据地址                 (e2prom_data.msgs[1]).len=1;//读出的数据                 (e2prom_data.msgs[1]).addr=0x50;// e2prom 设备地址                 (e2pr

23、om_data.msgs[1]).flags=I2C_M_RD;//read                 (e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。                 (e2prom_data.msgs[1]).buf[0]=0;//初始化读缓冲             ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);                 if(ret<0)                 {            

24、             perror("ioctl error2");                 }                 printf("buff[0]=%x/n",(e2prom_data.msgs[1]).buf[0]); /***打印读出的值,没错的话,就应该是前面写的0x58了***/                  close(fd);       i2c_put_adapter(client->adapter);//释放i2c总线 return 0;          } 以上讲述了一种比较常用的利用i2c-dev.c操作i2c设备的方法,

25、这种方法可以说是在应用层完成了对具体i2c设备的驱动工作。 计划下一篇总结以下几点: (1)在内核里写i2c设备驱动的两种方式: ●    Probe方式(new style),如:                 static struct i2c_driver pca953x_driver = {                         .driver = {                                 .name = "pca953x",                         },                         

26、probe = pca953x_probe,                         .remove = pca953x_remove,                         .id_table = pca953x_id,                 }; ●    Adapter方式(LEGACY),如:                 static struct i2c_driver pcf8575_driver = {                         .driver = {                               

27、  .owner = THIS_MODULE,                                 .name = "pcf8575",                         },                         .attach_adapter = pcf8575_attach_adapter,                         .detach_client = pcf8575_detach_client,                 }; (2)适配器驱动编写方法 (3)分享一些项目中遇到的问题         希望大家

28、多提意见,多多交流。 2 I2C驱动编写 下面具体分析如何写第一部分: 2.1 主芯片的I2C驱动编写 主控芯片的i2c驱动分为2个步骤: 2.1.1写总线驱动 选了个主控芯片,比如:S3C8900(自己瞎选的芯片),在driver/i2c/busses/i2c-s3c2410.c中没有找到这个芯片的I2C支持(总线驱动支持)。(倒霉了,没有选好芯片,也可能是最新型号的,linux内核没跟上)。 在此之前先分析i2c-s3c2410.c中完成的工作(总线驱动): l 设计对应于i2c_adapter_xxx_init()模板的s3c8900的模块加载函数和对应于i2c_

29、adapter_xxx_exit()函数模板的模块卸载函数。 l 设计对应于i2c_adapter_xxx_xfer()模板的 s3c8900适配器的通信方法函数,针对 s3c24xx、64xx、s5pc1XX、s5p64xx处理器functionality()函数s3c24xx_i2c-func()只需简单的返回I2C_FUNC_I2C|I2C_FUNC_SMBUS_EMUL|I2C_FUNC_PROTOCOL_MANGLING表明其支持的功能 话说没找到总线驱动支持,(这倒霉孩子)那就得编写个类似的i2c-s3c8900.c的总线驱动支持,嘿嘿,照着上面的功能写吧,反正是总线驱动。

30、Ø I2C适配器驱动加载与卸载  1. 初始化i2c适配器所使用的硬件资源,如申请I/O地址、中断号等; 2. 通过i2c_add_adapter添加i2c_adapter数据结构,当然这个数据结构的成员已经被xxx适配器的相应的函数指针所初始化; 3. i2c总线卸载模块与装载相反,是否i2c适配器使用的硬件资源,通过i2c_del_adapter删除i2c_adapter的数据结构。         模板如下:                 static int __init i2c_adapter_xxx_init(void)                 {  

31、                 xxx_adapter_hw_init();//初始化硬件资源                     i2c_add_adapter(&xxx_adapter);                 }                     static void __init i2c_adapter_xxx_exit(void)                         {         xxx_adapter_hw_free();//释放硬件资源                             i2c_del_adapter(&

32、xxx_adapter);                             }             具体CPU具体分析,有的用platform做的,可以参考6410的做法 2.1.2 I2C总线的通信方法 我们需要为特定的i2c适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函数和functionality()函数。functionality函数很简单,用于返回algorithm所支持的通信协议,如:I2C_FUCN-_I2C, I2C_FUNC_10BIT_ADDR,I2C_FUNC_SMBUS_READ_BYTE,I2C_FUNC_S

33、MBUS_write_byte等 master_xfer函数在i2c适配器上完成传递给他的i2c_msg数组中的每个i2c消息。              模板如下:             static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)                 {                     ......                     for(i = 0; i

34、      i2c_adapter_xxx_start(); //产生开始位                         //如果是读消息                         if(msg[i]->falgs &I2C_M_RD){                         i2c_adapter_xxx_setaddr((msg->addr<<1)|1); //发送从设备读地址                          i2c_adapter_xxx_wait_ack();//获取从设备的ack信息                          i

35、2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len);//读取msg[i]->len长的数据到msg[i]->buf里                             }    else{    //是写消息                               i2c_adapter_xxx_setaddr((msg->addr<<1)|1); //发送从设备写地址                              i2c_adapter_xxx_wait_ack();//获取从设备的ack信息         

36、                     i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len);//读取msg[i]->len长的数据到msg[i]->buf里                                 }                                     }                         i2c_adapter_xxx_stop(); //产生停止位      }   好啦,完成了装载和卸载,又完成了通信方法这两个重要的东东,那么总线驱动结构已经完成啦,累死了!  

37、 2.2 写设备驱动 四部曲: Ø 构建i2c_driver Ø 注册i2c_driver Ø 构建i2c_client ( 第一种方法:注册字符设备驱动、第二种方法:通过板文件的i2c_board_info填充,然后注册) Ø 注销i2c_driver 具体如下: 2.2.1构建i2c_driver static struct i2c_driver pca953x_driver = {                 .driver = {                                     .name= "pca953x", //名称    

38、                             },                 .id= ID_PCA9555,//id号                 .attach_adapter= pca953x_attach_adapter, //调用适配器连接设备                 .detach_client= pca953x_detach_client,//让设备脱离适配器         }; 2.2.2注册i2c_driver   static int __init pca953x_init(void)         {           

39、      return i2c_add_driver(&pca953x_driver);         }         module_init(pca953x_init);   执行i2c_add_driver(&pca953x_driver)后,如果内核中已经注册了i2c适配器,则顺序调用这些适配器来连接我们的i2c设备。此过程是通过调用i2c_driver中的attach_adapter方法完成的。具体实现形式如下:   static int pca953x_attach_adapter(struct i2c_adapter *adapter)         {

40、                return i2c_probe(adapter, &addr_data, pca953x_detect);                 /*                 adapter:适配器                 addr_data:地址信息                 pca953x_detect:探测到设备后调用的函数                 */         }   地址信息addr_data是由下面代码指定的。         /* Addresses to scan */         sta

41、tic unsigned short normal_i2c[] = {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,I2C_CLIENT_END};         I2C_CLIENT_INSMOD;   注意:normal_i2c里的地址必须是你i2c芯片的地址。否则将无法正确探测到设备。而I2C_ CLIENT_INSMOD是一个宏,它会利用normal_i2c构建addr_data。 2.2.3构建i2c_client,并注册字符设备驱动 i2c_probe在探测到目标设备后,后调用pca953x_detect,并把当时的探测地址addres

42、s作为参数传入。 static int pca953x_detect(struct i2c_adapter *adapter, int address, int kind)         {                 struct i2c_client *new_client;                 struct pca953x_chip *chip; //设备结构体                 int err = 0,result;                 dev_t pca953x_dev=MKDEV(pca953x_major,0);//构建设备

43、号,根据具体情况设定,这里我只考虑了normal_i2c中只有一个地址匹配的情况。主次设备号来源                 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA| I2C_FUNC_SMBUS_WORD_DATA))//判定适配器能力                 goto exit;                 if (!(chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL))) {                       

44、  err = -ENOMEM;                         goto exit;                 }                 /****构建i2c-client****/                 chip->client=kzalloc(sizeof(struct i2c_client),GFP_KERNEL);                 new_client = chip->client;                 i2c_set_clientdata(new_client, chip);            

45、     new_client->addr = address;                 new_client->adapter = adapter;                 new_client->driver = &pca953x_driver;                 new_client->flags = 0;                 strlcpy(new_client->name, "pca953x", I2C_NAME_SIZE);                 if ((err = i2c_attach_client(new_clie

46、nt)))//注册i2c_client                 goto exit_kfree;                 if (err)                 goto exit_detach;                 if(pca953x_major)                 {                         result=register_chrdev_region(pca953x_dev,1,"pca953x");                 }                 else{        

47、                 result=alloc_chrdev_region(&pca953x_dev,0,1,"pca953x");                         pca953x_major=MAJOR(pca953x_dev);                 }                 if (result < 0) {                         printk(KERN_NOTICE "Unable to get pca953x region, error %d/n", result);                 

48、        return result;                 }                 pca953x_setup_cdev(chip,0); //注册字符设备,此处不详解                 return 0;                 exit_detach:                 i2c_detach_client(new_client);         exit_kfree:                 kfree(chip);         exit:                 return err

49、         } i2c_check_functionality用来判定设配器的能力,这一点非常重要。你也可以直接查看对应设配器的能力,如 static const struct i2c_algorithm smbus_algorithm = {                 .smbus_xfer= i801_access,                 .functionality= i801_func,         };         static u32 i801_func(struct i2c_adapter *adapter)         {                         return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |                                I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |                                I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK|(isich4 ? I2C_FUNC_SMBUS_HWPEC

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

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

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

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

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

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

客服