【经验】Silicon Labs的蓝牙SoC EFR32BG22如何进行在线的OTA升级?

2020-06-07 世强
蓝牙SoC,EFR32BG,EFR32BG22,Silicon Labs 蓝牙SoC,EFR32BG,EFR32BG22,Silicon Labs 蓝牙SoC,EFR32BG,EFR32BG22,Silicon Labs 蓝牙SoC,EFR32BG,EFR32BG22,Silicon Labs

SILICON LABS蓝牙SoC EFR32BG目前已经能够支持到蓝牙5.2,工艺升级后的EFR32BG22的功耗能够达到接收4.1mA,0dbm发射功耗3.6mA,休眠功耗低至1.4uA,如此低的功耗,能够大幅延长蓝牙设备的电池续航时间。


目前SiliconLabs提供的默认的OTA升级方式是In-place方式,流程为:

1、  APP发起OTA,显示连接断开,搜索OTA设备。

2、  EFR32BG22断开连接并重新启动;

3、  EFR32BG22 重启后以”OTA”广播;

4、  APP连接OTA并开始OTA升级;

5、  升级完成后EFR32BG22重启,升级成功。


这种方式占用的空间最小,但是可能在实际应用时,有些情况大家更喜欢传统的在线升级,链接不断开。本文介绍下方法。


首先需要选择一个Bootloader,本文以Internal Flash Bootloader为例,在ThunderBoard EFR32BG22的平台上进行操作:



然后,以SOC-Empty为例:

然后,复制Bootloader的API文件到工程中:

C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v2.7\platform\bootloader\api\*

 

使用如下代码替换工程的app.c

//////////////////////////////////分割线////////////////////////////////////////////////

/***********************************************************************************************//**

 * \file   app.c

 * \brief  This example shows how to do Bluetooth OTA update under application control.

 *

 *  For more details about the Bluetooth OTA, refer to application note AN1086.

 *

 ***************************************************************************************************

 * <b> (C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>

 ***************************************************************************************************

 * This file is licensed under the Silabs License Agreement. See the file

 * "Silabs_License_Agreement.txt" for details. Before using this software for

 * any purpose, you must agree to the terms of that agreement.

 **************************************************************************************************/

 

/* Bluetooth stack headers */

#include "bg_types.h"

#include "native_gecko.h"

#include "gatt_db.h"

 

#include "app.h"

 

#if DEBUG_LEVEL

/* printf debugging is enabled */

#include "retargetserial.h"

#include <stdio.h>

#define uart_flush RETARGET_SerialFlush

 

#else

/* debug prints are DISABLED - replace printf etc. with empty implementation */

#define printf(fmt, ...) (0)

#define uart_flush() (0)

 

#endif

 

#include "btl_interface.h"

#include "btl_interface_storage.h"

 

static void bootMessage(struct gecko_msg_system_boot_evt_t *bootevt);

 

static BootloaderInformation_t bldInfo;

static BootloaderStorageSlot_t slotInfo;

 

/* OTA variables */

static uint32 ota_image_position = 0;

static uint8 ota_in_progress = 0;

static uint8 ota_image_finished = 0;

static uint16 ota_time_elapsed = 0;

 

static int32_t get_slot_info()

{

    int32_t err;

 

    bootloader_getInfo(&bldInfo);

    printf("Gecko bootloader version: %u.%u\r\n", (bldInfo.version & 0xFF000000) >> 24, (bldInfo.version & 0x00FF0000) >> 16);

 

    err = bootloader_getStorageSlotInfo(0, &slotInfo);

 

    if(err == BOOTLOADER_OK)

    {

       printf("Slot 0 starts @ 0x%8.8x, size %u bytes\r\n", slotInfo.address, slotInfo.length);

    }

    else

    {

       printf("Unable to get storage slot info, error %x\r\n", err);

    }

 

    return(err);

}

 

static void erase_slot_if_needed()

{

    uint32_t offset = 0;

    uint8_t buffer[256];

    int i;

    int dirty = 0;

    int32_t err = BOOTLOADER_OK;

    int num_blocks = 0;

 

    /* check the download area content by reading it in 256-byte blocks */

 

    num_blocks = slotInfo.length / 256;

 

    while((dirty == 0) && (offset < 256*num_blocks) && (err == BOOTLOADER_OK))

    {

       err = bootloader_readStorage(0, offset, buffer, 256);

       if(err == BOOTLOADER_OK)

       {

           i=0;

           while(i<256)

           {

              if(buffer[i++] != 0xFF)

              {

                  dirty = 1;

                  break;

              }

           }

           offset += 256;

       }

       printf(".");

    }

 

    if(err != BOOTLOADER_OK)

    {

       printf("error reading flash! %x\r\n", err);

    }

    else if(dirty)

    {

       printf("download area is not empty, erasing...\r\n");

       bootloader_eraseStorageSlot(0);

       printf("done\r\n");

    }

    else

    {

       printf("download area is empty\r\n");

    }

 

    return;

}

 

static void print_progress()

{

    // estimate transfer speed in kbps

    int kbps = ota_image_position*8/(1024*ota_time_elapsed);

 

    printf("pos: %u, time: %u, kbps: %u\r\n", ota_image_position, ota_time_elapsed, kbps);

}

 

void appMain(gecko_configuration_t *pconfig)

{

 

#if DISABLE_SLEEP > 0

    pconfig->sleep.flags = 0;

#endif

 

#if DEBUG_LEVEL

    RETARGET_SerialInit();

#endif

 

      // Initialize stack

      gecko_init(pconfig);

 

      while (1) {

        /* Event pointer for handling events */

        struct gecko_cmd_packet* evt;

 

#if DEBUG_LEVEL

        /* if there are no events pending then the next call to gecko_wait_event() may cause

         * device go to deep sleep. Make sure that debug prints are flushed before going to sleep */

        if (!gecko_event_pending()) {

            uart_flush();

        }

#endif

 

        /* Check for stack event. */

        evt = gecko_wait_event();

 

        /* Handle events */

        switch (BGLIB_MSG_ID(evt->header)) {

          /* This boot event is generated when the system boots up after reset.

           * Do not call any stack commands before receiving the boot event.

           * Here the system is set to start advertising immediately after boot procedure. */

          case gecko_evt_system_boot_id:

#if DEBUG_LEVEL

           bootMessage(&(evt->data.evt_system_boot));

 

#endif

           /* 1 second soft timer, used for performance statistics during OTA file upload */

           gecko_cmd_hardware_set_soft_timer(32768, 0, 0);

 

            /* set advertising interval to 100 ms */

            gecko_cmd_le_gap_set_advertise_timing(0, 160, 160, 0, 0);

 

            /* Start general advertising and enable connections. */

            printf("boot event - starting advertising\r\n");

            gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_connectable_scannable);

 

            /* bootloader init must be called before calling other bootloader_xxx API calls */

            bootloader_init();

 

            /* read slot information from bootloader */

            if(get_slot_info() == BOOTLOADER_OK)

            {

             /* the download area is erased here (if needed), prior to any connections are opened */

             erase_slot_if_needed();

            }

            else

            {

             printf("Check that you have installed correct type of Gecko bootloader!\r\n");

            }

            break;

 

          case gecko_evt_le_connection_opened_id:

           printf("connection opened\r\n");

           break;

 

          case gecko_evt_le_connection_closed_id:

           printf("connection closed, reason: 0x%2.2x\r\n", evt->data.evt_le_connection_closed.reason);

 

           if (ota_image_finished) {

              printf("Installing new image\r\n");

#if DEBUG_LEVEL

              uart_flush();

#endif

              bootloader_setImageToBootload(0);

              bootloader_rebootAndInstall();

           } else {

              /* Restart advertising after client has disconnected */

              gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_connectable_scannable);

           }

           break;

 

          case gecko_evt_hardware_soft_timer_id:

           if(ota_in_progress)

           {

              ota_time_elapsed++;

              print_progress();

           }

           break;

 

           /* write operations to ota_data, ot_control characteristics are handled here :*/

          case gecko_evt_gatt_server_user_write_request_id:

          {

           uint32_t connection = evt->data.evt_gatt_server_user_write_request.connection;

           uint32_t characteristic = evt->data.evt_gatt_server_user_write_request.characteristic;

           if(characteristic == ota_control)

           {

              switch(evt->data.evt_gatt_server_user_write_request.value.data[0])

              {

              case 0://Erase and use slot 0

                 // NOTE: download are is NOT erased here, because the long blocking delay would result in supervision timeout

                 //bootloader_eraseStorageSlot(0);

                 ota_image_position=0;

                 ota_in_progress=1;

                 break;

              case 3://END OTA process

                 //wait for connection close and then reboot

                 ota_in_progress=0;

                 ota_image_finished=1;

                 printf("upload finished. received file size %u bytes\r\n", ota_image_position);

                 uart_flush();

                 break;

              default:

                 break;

              }

           } else if(characteristic == ota_data)

           {

              if(ota_in_progress)

              {

                 bootloader_writeStorage(0,//use slot 0

                        ota_image_position,

                           evt->data.evt_gatt_server_user_write_request.value.data,

                           evt->data.evt_gatt_server_user_write_request.value.len);

                 ota_image_position+=evt->data.evt_gatt_server_user_write_request.value.len;

              }

           }

           gecko_cmd_gatt_server_send_user_write_response(connection,characteristic,0);

          }

           break;

 

          default:

            break;

        }

      }

 

}

 

static void bootMessage(struct gecko_msg_system_boot_evt_t *bootevt)

{

    bd_addr local_addr;

    int i;

 

    printf("stack version: %u.%u.%u\r\n", bootevt->major, bootevt->minor, bootevt->patch);

    local_addr = gecko_cmd_system_get_bt_address()->address;

 

    printf("local BT device address: ");

    for(i=0;i<5;i++)

    {

       printf("%2.2x:", local_addr.addr[5-i]);

    }

    printf("%2.2x\r\n", local_addr.addr[0]);

 

}

 

 

//////////////////////////////////分割线/////////////////////////////////////////////////

 

使用如下替代gatt.xml

////////////////////////////////////分割线////////////////////////////////////

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<!--Custom BLE GATT-->

<gatt gatt_caching="false" generic_attribute_service="false" name="Custom BLE GATT" prefix="">

 

  <!--Generic Access-->

  <service advertise="false" name="Generic Access" requirement="mandatory" sourceId="org.bluetooth.service.generic_access" type="primary" uuid="1800">

    <informativeText>Abstract:  The generic_access service contains generic information about the device. All available Characteristics are readonly.  </informativeText>

   

    <!--Device Name-->

    <characteristic name="Device Name" sourceId="org.bluetooth.characteristic.gap.device_name" uuid="2A00">

      <informativeText/>

      <value length="16" type="utf-8" variable_length="false">EFR32WaitForOTA</value>

      <properties const="true" const_requirement="optional" read="true" read_requirement="mandatory"/>

    </characteristic>

   

    <!--Appearance-->

    <characteristic name="Appearance" sourceId="org.bluetooth.characteristic.gap.appearance" uuid="2A01">

      <informativeText>Abstract:  The external appearance of this device. The values are composed of a category (10-bits) and sub-categories (6-bits).  </informativeText>

      <value length="2" type="hex" variable_length="false">0000</value>

      <properties const="true" const_requirement="optional" read="true" read_requirement="mandatory"/>

    </characteristic>

  </service>

 

  <!--Silicon Labs OTA-->

  <service advertise="false" name="Silicon Labs OTA" requirement="mandatory" sourceId="com.silabs.service.ota" type="primary" uuid="1D14D6EE-FD63-4FA1-BFA4-8F47B42119F0">

    <informativeText>Abstract:  The Silicon Labs OTA Service enables over-the-air firmware update of the device.  </informativeText>

   

    <!--Silicon Labs OTA Control-->

    <characteristic id="ota_control" name="Silicon Labs OTA Control" sourceId="com.silabs.characteristic.ota_control" uuid="F7BF3564-FB6D-4E53-88A4-5E37E0326063">

      <informativeText>Abstract:  Silicon Labs OTA Control.  </informativeText>

      <value length="1" type="user" variable_length="false"/>

      <properties write="true" write_requirement="mandatory"/>

    </characteristic>

   

    <!--Silicon Labs OTA Data-->

    <characteristic id="ota_data" name="Silicon Labs OTA Data" sourceId="com.silabs.characteristic.ota_data" uuid="984227F3-34FC-4045-A5D0-2C581F81A153">

      <informativeText>Abstract:  Silicon Labs OTA Data.  </informativeText>

      <value length="255" type="user" variable_length="true"/>

      <properties write="true" write_no_response="true" write_no_response_requirement="mandatory" write_requirement="excluded"/>

    </characteristic>

  </service>

</gatt>

 

////////////////////////////////分割线/////////////////////////////////////////////

在图形界面导入gatt.xml,然后重新生成一下,如有需要可以添加一些:

重新生成后编译即可。


这样就可以按照原先的OTA方法,进行在线升级了。


授权代理商:世强先进(深圳)科技股份有限公司
技术资料,数据手册,3D模型库,原理图,PCB封装文件,选型指南来源平台:世强硬创平台www.sekorm.com
现货商城,价格查询,交期查询,订货,现货采购,在线购买,样品申请渠道:世强硬创平台电子商城www.sekorm.com/supply/
概念,方案,设计,选型,BOM优化,FAE技术支持,样品,加工定制,测试,量产供应服务提供:世强硬创平台www.sekorm.com
集成电路,电子元件,电子材料,电气自动化,电机,仪器全品类供应:世强硬创平台www.sekorm.com
  • +1 赞 0
  • 收藏
  • 评论 0

本文由Song提供,版权归世强硬创平台所有,非经授权,任何媒体、网站或个人不得转载,授权转载时须注明“来源:世强硬创平台”。

评论

   |   

提交评论

全部评论(0

暂无评论

相关推荐

【经验】STUDIO V5中蓝牙SoC EFR32BG22添加串口LOG打印的方法

在调试Silicon Labs蓝牙SoC EFR32BG22时,一般需要添加LOG打印信息,通过串口的方式来判断代码运行是否正常。由于使用RTT功能在打印信息时无法再次在线调试和代码下载,所以直接采用串口的方式更加便捷。

设计经验    发布时间 : 2021-06-30

探索蓝牙新安全功能,基于证书的身份验证和配对 (CBAP)

“基于证书的身份验证和配对(CBAP)”有助于简化低功耗蓝牙(Bluetooth LE)设备的身份验证和配对过程。芯科科技是低功耗蓝牙解决方案的行业领导者,可以帮助您实施基于证书的身份验证和配对,以确保设备具有最高安全性,而无需任何用户交互。目前,Secure Vault-High 和 Secure Vault-Mid 设备上支持基于证书的身份验证和配对。

设计经验    发布时间 : 2024-07-10

【经验】芯科科技EFR32BG22系列蓝牙SOC修改自定义广播包的思路与具体实例

在蓝牙的实际应用中,往往需要自定义的蓝牙广播包来适应不同的应用环境,本文主要介绍基于SILICON LABS的EFR32BG22蓝牙SOC实现自定义广播数据包的方法。

设计经验    发布时间 : 2023-03-16

芯科EFR32BG27蓝牙SoC用在CGM中,支持3v或者1.5v电池供电,可以做到21天的超长续航时间

EFR32BG27蓝牙SoC由世界知名半导体厂商Silicon Labs推出,基于ARM Cortex-M33处理器内核,拥有超低功耗、超小体积和超大资源等优势,成为了CGM设备的理想选择。

应用方案    发布时间 : 2023-06-13

选对了针对医疗和可穿戴设备的小型蓝牙芯片,设计毫不费力!

芯科科技一直为医疗设备和可穿戴设备制造商提供小型、专为应用而优化的蓝牙解决方案。以下展示了芯科科技提供的八款小型蓝牙芯片以及快速比较表,可以帮助您为应用程序选择最佳蓝牙解决方案!

应用方案    发布时间 : 2024-02-04

芯科科技EFR32BG27蓝牙SoC助力持续性血糖监测CGM设备长达21天续航时间

在当今医疗领域,持续性血糖监测(CGM)成为人们关注的焦点,特别是对于糖尿病患者而言,实时了解血糖水平变化对于治疗和生活质量的提升具有举足轻重的意义。Silicon Labs提供的极小型、超低功耗EFR32BG27(BG27)蓝牙SoC在CGM中的应用显得尤为突出。本文将详细介绍BG27蓝牙SoC在CGM中的应用及其突出特点。

应用方案    发布时间 : 2024-07-12

汇顶科技(GOODIX)低功耗蓝牙Soc选型表(ON型号)

目录- 蓝牙SoC   

型号- GR5515I0ND,GR5515IENDU,GR5515RGBD,GR5513BEND,GR5515GGBD,GR5515IGND,GR5515I0NDA

选型指南  -  汇顶科技  - 2022/4/14 PDF 中文 下载

【应用】EFR32BG22蓝牙SoC用做CGM连续血糖仪主控,单芯片集成MCU和蓝牙5.2

CGM连续血糖仪为了用户体验更好,要做到小而轻,续航时间长,可以通过手机蓝牙连接获取血糖测量数据。Silicon Labs的蓝牙SoC EFR32BG22在单芯片中集成了MCU和蓝牙5.2,可以作为CGM的主控芯片并实现蓝牙数据收发,有丰富的外设。

应用方案    发布时间 : 2021-07-20

低功耗、高性能:探究流行蓝牙SOC的成功秘诀

随着无线技术的飞速发展,蓝牙SOC已成为当今市场上极为流行的无线通信解决方案。蓝牙SOC集成了蓝牙通信功能以及微处理器等其他功能,为现代智能设备提供了高效、稳定的连接体验。

技术探讨    发布时间 : 2024-05-17

芯科科技提供多款无线连接和控制芯片产品及解决方案,BG2x系列蓝牙SoC成就多样医疗物联网用例,

Silicon Labs(亦称“芯科科技”)的无线SoC和MCU助力全球客户的医疗物联网应用创新,持续打造更智能、高效、安全和便捷的健康监测设备。智能和网联技术近年来一直是医疗和健康保障领域内的热门技术,许多厂商都在利用医疗物联网(IoMT)技术开发更加智能和互联的健康监测设备,以利用物联网、云计算、人工智能和可穿戴等新一代信息通信技术,来帮助用户时刻监控自己的健康状况、降低医疗费用和就医麻烦。

厂牌及品类    发布时间 : 2023-06-29

【技术】蓝牙5与蓝牙MESH,增加蓝牙传输距离并将蓝牙设备进行组网通讯

Silicon Labs公司作为世界顶级的物联网芯片、软件、解决方案供应商,推出支持蓝牙5.0的SOC芯片EFR32BG系列和通过认证、集成天线、远距离传输、小封装的蓝牙模块BGM系列。Silicon Labs公公司针对蓝牙产品,提供整套完善的开发工具,蓝牙Mesh网络开发包,例程和手机APP代码。运用Silicon Labs公司提供的丰富资源,可以提高设计产品的稳定性,加快上市时间。

新技术    发布时间 : 2018-05-18

AIoT与蓝牙SOC:智能生活的无缝连接

未来,随着5G、云计算、边缘计算等技术的不断发展,蓝牙SOC将会在AIoT领域发挥更加重要的作用。它不仅能够实现设备之间的无缝连接和数据交换,还能够为AI算法提供更加丰富的数据源,推动人工智能技术的不断创新和发展。

技术探讨    发布时间 : 2024-05-17

【经验】EFR32BG22系列蓝牙SOC电池电压与VDD供电电压检测ADC的方法

EFR32BG22作为低功耗蓝牙SOC方案,经常应用于电池供电的方案中,一般采用内部的ADC作为采集通道,内部的输入源作为输入接口,来测试VDD供电电压。本文将讲解电池电压与VDD供电电压检测ADC的方法。

设计经验    发布时间 : 2020-11-28

Silicon Labs EFR32BG21 蓝牙SoC RCP模式测试-扫描设备

本文介绍测试EFR32BG21 蓝牙RCP模式工作在主模式,用于扫描蓝牙从设备,建立蓝牙连接和收发数据。首先需要准备一台电脑并且已经安装好了linux系统。也可以是windows系统下安装的linux虚拟机。并且已经安装好了蓝牙BlueZ协议栈。

设计经验    发布时间 : 2024-03-01

蓝牙SOC:高度集成的无线通信解决方案

蓝牙SOC(System on a Chip,系统级芯片)技术,作为现代无线通信领域的一项重要技术,正日益受到人们的关注和追捧。SOC,即将多个功能集成到单一芯片上,它大大提升了设备的集成度和性能,同时也降低了功耗和成本。在蓝牙技术中,SOC的应用使得蓝牙设备的制造更为简便,功能更为强大。

技术探讨    发布时间 : 2024-05-13

展开更多

电子商城

查看更多

只看有货

品牌:SILICON LABS

品类:Wireless Gecko SoC

价格:¥8.1764

现货: 111,255

品牌:SILICON LABS

品类:评估工具

价格:¥111.8652

现货: 1

品牌:SILICON LABS

品类:Bluetooth SoCs

价格:

现货: 0

品牌:SILICON LABS

品类:Bluetooth SoCs

价格:

现货: 0

品牌:SILICON LABS

品类:Bluetooth SoCs

价格:

现货: 0

品牌:SILICON LABS

品类:Bluetooth SoCs

价格:

现货: 0

品牌:SILICON LABS

品类:开发工具

价格:¥331.7858

现货: 0

品牌:芯海

品类:蓝牙芯片

价格:¥3.3334

现货: 77,830

品牌:芯海

品类:低功耗蓝牙SOC芯片

价格:¥3.5334

现货: 7,470

品牌:芯海

品类:低功耗蓝牙SOC芯片

价格:¥6.1334

现货: 5,951

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

现货市场

查看更多

品牌:SILICON LABS

品类:Switch Hall Effect Magnetic Position Sensor

价格:¥2.2924

现货:150,000

品牌:SILICON LABS

品类:Light Sensor

价格:¥20.3400

现货:28,003

品牌:SILICON LABS

品类:Position Sensor

价格:¥3.0897

现货:12,568

品牌:SILICON LABS

品类:8位MCU

价格:¥4.9000

现货:12,000

品牌:SILICON LABS

品类:Mixed-Signal MCU

价格:¥10.1700

现货:10,000

品牌:SILICON LABS

品类:Gecko MCU

价格:¥15.6000

现货:5,353

品牌:SILICON LABS

品类:8 BIT MCU

价格:¥3.7900

现货:3,451

品牌:SILICON LABS

品类:Wireless SoC

价格:¥15.1400

现货:1,455

品牌:SILICON LABS

品类:Mixed-Signal MCU

价格:¥11.1200

现货:1,201

品牌:SILICON LABS

品类:8 BIT MCU

价格:¥16.8500

现货:550

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

品牌:

品类:

价格:

现货:

服务

查看更多

蓝牙射频及通信协议测试

根据用户的蓝牙模块,使用Bluetooth 蓝牙测试装置MT8852B,测试蓝牙1.0至5.1,包括传输速率、功率、频率、调制和接收机灵敏度,生成测试报告。支持到场/视频直播测试,资深专家全程指导。

实验室地址: 深圳 提交需求>

语音/录音芯片定制

提供语音芯片、MP3芯片、录音芯片、音频蓝牙芯片等IC定制,语音时长:40秒~3小时(外挂flash),可以外挂TF卡或U盘扩容。

最小起订量: 1pcs 提交需求>

世强和原厂的技术专家将在一个工作日内解答,帮助您快速完成研发及采购。
我要提问

954668/400-830-1766(工作日 9:00-18:00)

service@sekorm.com

研发客服
商务客服
服务热线

联系我们

954668/400-830-1766(工作日 9:00-18:00)

service@sekorm.com

投诉与建议

E-mail:claim@sekorm.com

商务合作

E-mail:contact@sekorm.com

收藏
收藏当前页面