linux MMC framework(2) - sdhci host driver

假如想象 提交于 2020-01-22 08:27:00
  • 了解 sdhci host driver.

1.概述

  The MultiMediaCard (MMC)/ Secure Digital (SD)/ Secure Digital Input Output (SDIO) host driver implements a standard Linux driver interface to the ultra MMC/SD host controller (microSDHC). The host driver is part of the Linux kernel MMC framework.

1.1.Kernel configuration

You can manage the MMC driver support through the kernel configuration options:

  • MMC/SD/SDIO (CONFIG_MMC)
  • MMC block (CONFIG_MMC_BLOCK)
  • Secure Digital Host Controller Interface support (CONFIG_MMC_SDHCI)
  • SDHCI support on the platform-specific bus (CONFIG_MMC_SDHCI_PLTFM)

2.数据结构

2.1.struct sdhci_host

struct sdhci_host {
    /* Data set by hardware interface driver */
    const char *hw_name;    /* Hardware bus name */      // 名称
    unsigned int quirks;    /* Deviations from spec. */         // 癖好,可以理解为硬件sdhci controller和标准sdhci规范不符合的地方。
    unsigned int quirks2;   /* More deviations from spec. */   // 癖好2,可以理解为硬件sdhci controller和标准sdhci规范不符合的地方。

    int irq;        /* Device IRQ */      // sdhci的中断
    void __iomem *ioaddr;   /* Mapped address */   // sdhci寄存器的基地址
    const struct sdhci_ops *ops;    /* Low level hw interface */      // 底层硬件的操作接口

    struct regulator *vmmc;     /* Power regulator (vmmc) */       // sdhci core的LDO
    struct regulator *vqmmc;    /* Signaling regulator (vccq) */      // 给sdhci io供电的LDO

    /* Internal data */
    struct mmc_host *mmc;   /* MMC structure */      // struct mmc_host,用于注册到mmc subsystem中
    u64 dma_mask;       /* custom DMA mask */

    spinlock_t lock;    /* Mutex */      // 自旋锁
    int flags;      /* Host attributes */   // sdhci的一些标识
    unsigned int version;   /* SDHCI spec. version */   // 当前sdhci的硬件版本
    unsigned int max_clk;   /* Max possible freq (MHz) */   // 该sdhci支持的最大电压
    unsigned int timeout_clk;   /* Timeout freq (KHz) */   // 超时频率
    unsigned int clk_mul;   /* Clock Muliplier value */   // 当前倍频值
    unsigned int clock; /* Current clock (MHz) */      // 当前工作频率
    u8 pwr;         /* Current voltage */   // 当前工作电压
    bool runtime_suspended; /* Host is runtime suspended */      // 是否处于runtime suspend状态
    struct mmc_request *mrq;    /* Current request */      // 当前正在处理的请求
    struct mmc_command *cmd;    /* Current command */   // 当前的命令请求
    struct mmc_data *data;  /* Current data request */      // 当前的数据请求
    unsigned int data_early:1;  /* Data finished before cmd */   // 表示在CMD处理完成前,data已经处理完成

    struct sg_mapping_iter sg_miter;    /* SG state for PIO */
    unsigned int blocks;    /* remaining PIO blocks */
    int sg_count;       /* Mapped sg entries */
    u8 *adma_desc;      /* ADMA descriptor table */
    u8 *align_buffer;   /* Bounce buffer */
    unsigned int adma_desc_sz; /* ADMA descriptor table size */
    unsigned int adma_desc_line_sz; /* ADMA descriptor line size */
    unsigned int align_buf_sz; /* Bounce buffer size */
    unsigned int align_bytes; /* Alignment bytes (4/8 for 32-bit/64-bit) */
    unsigned int adma_max_desc; /* Max ADMA descriptos (max sg segments) */
    dma_addr_t adma_addr;   /* Mapped ADMA descr. table */
    dma_addr_t align_addr;  /* Mapped bounce buffer */

    struct tasklet_struct card_tasklet; /* Tasklet structures */      // card tasklet,用于处理card的插入或者拔出事件
    struct tasklet_struct finish_tasklet;      // finsh tasklet,用来通知上层一个请求处理完成(包括出错的情况)

    struct timer_list timer;    /* Timer for timeouts */   // 超时定时器链表

    u32 caps;       /* Alternative CAPABILITY_0 */   // 表示该sdhci controller的属性
    u32 caps1;      /* Alternative CAPABILITY_1 */   // 表示该sdhci controller的属性

    unsigned int            ocr_avail_sdio; /* OCR bit masks */   // 在该sdhci controller上可用的sdio card的ocr值掩码(代表了其可用电压)
    unsigned int            ocr_avail_sd;   // 在该sdhci controller上可用的sd card的ocr值掩码(代表了其可用电压) 
    unsigned int            ocr_avail_mmc;   /// 在该sdhci controller上可用的mmc card的ocr值掩码(代表了其可用电压) 

/* 以下和mmc的tuning相关 */
    wait_queue_head_t   buf_ready_int;  /* Waitqueue for Buffer Read Ready interrupt */
    unsigned int        tuning_done;    /* Condition flag set when CMD19 succeeds */
    unsigned int        tuning_count;   /* Timer count for re-tuning */
    unsigned int        tuning_mode;    /* Re-tuning mode supported by host */
#define SDHCI_TUNING_MODE_1 0
    struct timer_list   tuning_timer;   /* Timer for tuning */

/* 以下和sdhci的qos相关 */
    struct sdhci_host_qos host_qos[SDHCI_QOS_MAX_POLICY];
    enum sdhci_host_qos_policy last_qos_policy;
    bool host_use_default_qos;  
    unsigned int pm_qos_timeout_us;         /* timeout for PM QoS request */
    struct device_attribute pm_qos_tout;
    struct delayed_work pm_qos_work;

    struct sdhci_next next_data;
    ktime_t data_start_time;
    struct mutex ios_mutex;
    enum sdhci_power_policy power_policy;

    bool irq_enabled; /* host irq status flag */      // 表示中断是否使能?
    bool async_int_supp;  /* async support to rxv int, when clks are off */
    bool disable_sdio_irq_deferred; /* status of disabling sdio irq */
    u32 auto_cmd_err_sts;
    struct ratelimit_state dbg_dump_rs;
    int reset_wa_applied; /* reset workaround status */
    ktime_t reset_wa_t; /* time when the reset workaround is applied */
    int reset_wa_cnt; /* total number of times workaround is used */

    unsigned long private[0] ____cacheline_aligned;      // 私有数据指针
};
  • 癖好1(sdhci_host->quirks)各个位意义如下:
/* Controller doesn't honor resets unless we touch the clock register */
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET            (1<<0)
/* Controller has bad caps bits, but really supports DMA */
#define SDHCI_QUIRK_FORCE_DMA                (1<<1)
/* Controller doesn't like to be reset when there is no card inserted. */
#define SDHCI_QUIRK_NO_CARD_NO_RESET            (1<<2)
/* Controller doesn't like clearing the power reg before a change */
#define SDHCI_QUIRK_SINGLE_POWER_WRITE            (1<<3)
/* Controller has flaky internal state so reset it on each ios change */
#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS        (1<<4)
/* Controller has an unusable DMA engine */
#define SDHCI_QUIRK_BROKEN_DMA                (1<<5)
/* Controller has an unusable ADMA engine */
#define SDHCI_QUIRK_BROKEN_ADMA                (1<<6)
/* Controller can only DMA from 32-bit aligned addresses */
#define SDHCI_QUIRK_32BIT_DMA_ADDR            (1<<7)
/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
#define SDHCI_QUIRK_32BIT_DMA_SIZE            (1<<8)
/* Controller can only ADMA chunks that are a multiple of 32 bits */
#define SDHCI_QUIRK_32BIT_ADMA_SIZE            (1<<9)
/* Controller needs to be reset after each request to stay stable */
#define SDHCI_QUIRK_RESET_AFTER_REQUEST            (1<<10)
/* Controller needs voltage and power writes to happen separately */
#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER        (1<<11)
/* Controller provides an incorrect timeout value for transfers */
#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL            (1<<12)
/* Controller has an issue with buffer bits for small transfers */
#define SDHCI_QUIRK_BROKEN_SMALL_PIO            (1<<13)
/* Controller does not provide transfer-complete interrupt when not busy */
#define SDHCI_QUIRK_NO_BUSY_IRQ                (1<<14)
/* Controller has unreliable card detection */
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION        (1<<15)
/* Controller reports inverted write-protect state */
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT        (1<<16)
/* Controller has nonstandard clock management */
#define SDHCI_QUIRK_NONSTANDARD_CLOCK            (1<<17)
/* Controller does not like fast PIO transfers */
#define SDHCI_QUIRK_PIO_NEEDS_DELAY            (1<<18)
/* Controller losing signal/interrupt enable states after reset */
#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET        (1<<19)
/* Controller has to be forced to use block size of 2048 bytes */
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048            (1<<20)
/* Controller cannot do multi-block transfers */
#define SDHCI_QUIRK_NO_MULTIBLOCK            (1<<21)
/* Controller can only handle 1-bit data transfers */
#define SDHCI_QUIRK_FORCE_1_BIT_DATA            (1<<22)
/* Controller needs 10ms delay between applying power and clock */
#define SDHCI_QUIRK_DELAY_AFTER_POWER            (1<<23)
/* Controller uses SDCLK instead of TMCLK for data timeouts */
#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK        (1<<24)
/* Controller reports wrong base clock capability */
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN        (1<<25)
/* Controller cannot support End Attribute in NOP ADMA descriptor */
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC        (1<<26)
/* Controller is missing device caps. Use caps provided by host */
#define SDHCI_QUIRK_MISSING_CAPS            (1<<27)
/* Controller uses Auto CMD12 command to stop the transfer */
#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12        (1<<28)
/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
#define SDHCI_QUIRK_NO_HISPD_BIT            (1<<29)
/* Controller treats ADMA descriptors with length 0000h incorrectly */
#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC        (1<<30)
/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT            (1<<31)
  • 癖好2(sdhci_host->quirks2)各个位意义如下:
#define SDHCI_QUIRK2_HOST_OFF_CARD_ON           (1<<0)
#define SDHCI_QUIRK2_HOST_NO_CMD23          (1<<1)
/* The system physically doesn't support 1.8v, even if the host does */
#define SDHCI_QUIRK2_NO_1_8_V               (1<<2)
#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN        (1<<3)
/*
 * Read Transfer Active/ Write Transfer Active may be not
 * de-asserted after end of transaction. Issue reset for DAT line.
 */
#define SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT         (1<<4)
/*
 * Slow interrupt clearance at 400KHz may cause
 * host controller driver interrupt handler to
 * be called twice.
 */
#define SDHCI_QUIRK2_SLOW_INT_CLR           (1<<5)
/*
 * If the base clock can be scalable, then there should be no further
 * clock dividing as the input clock itself will be scaled down to
 * required frequency.
 */
#define SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK      (1<<6)
/*
 * Dont use the max_discard_to in sdhci driver so that the maximum discard
 * unit gets picked by the mmc queue. Otherwise, it takes a long time for
 * secure discard kind of operations to complete.
 */
#define SDHCI_QUIRK2_USE_MAX_DISCARD_SIZE       (1<<7)
/*
 * Ignore data timeout error for R1B commands as there will be no
 * data associated and the busy timeout value for these commands
 * could be lager than the maximum timeout value that controller
 * can handle.
 */
#define SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD     (1<<8)
/*
 * The preset value registers are not properly initialized by
 * some hardware and hence preset value must not be enabled for
 * such controllers.
 */
#define SDHCI_QUIRK2_BROKEN_PRESET_VALUE        (1<<9)
/*
 * Some controllers define the usage of 0xF in data timeout counter
 * register (0x2E) which is actually a reserved bit as per
 * specification.
 */
#define SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT       (1<<10)
/*
 * This is applicable for controllers that advertize timeout clock
 * value in capabilities register (bit 5-0) as just 50MHz whereas the
 * base clock frequency is 200MHz. So, the controller internally
 * multiplies the value in timeout control register by 4 with the
 * assumption that driver always uses fixed timeout clock value from
 * capabilities register to calculate the timeout. But when the driver
 * uses SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK base clock frequency is directly
 * controller by driver and it's rate varies upto max. 200MHz. This new quirk
 * will be used in such cases to avoid controller mulplication when timeout is
 * calculated based on the base clock.
 */
#define SDHCI_QUIRK2_DIVIDE_TOUT_BY_4 (1 << 11)
/*
 * Some SDHC controllers are unable to handle data-end bit error in
 * 1-bit mode of SDIO.
 */
#define SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR             (1<<12)

/*
 * Some SDHC controllers do not require data buffers alignment, skip
 * the bounce buffer logic when preparing data
 */
#define SDHCI_QUIRK2_ADMA_SKIP_DATA_ALIGNMENT             (1<<13)
/* Some controllers doesn't have have any LED control */
#define SDHCI_QUIRK2_BROKEN_LED_CONTROL (1 << 14)
/* Use reset workaround in case sdhci reset timeouts */
#define SDHCI_QUIRK2_USE_RESET_WORKAROUND (1 << 15)
  • sdhci host的一些标识(sdhci_host->flags)如下:
#define SDHCI_USE_SDMA      (1<<0)  /* Host is SDMA capable */
#define SDHCI_USE_ADMA      (1<<1)  /* Host is ADMA capable */
#define SDHCI_REQ_USE_DMA   (1<<2)  /* Use DMA for this req. */
#define SDHCI_DEVICE_DEAD   (1<<3)  /* Device unresponsive */
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
#define SDHCI_NEEDS_RETUNING    (1<<5)  /* Host needs retuning */
#define SDHCI_AUTO_CMD12    (1<<6)  /* Auto CMD12 support */
#define SDHCI_AUTO_CMD23    (1<<7)  /* Auto CMD23 support */
#define SDHCI_PV_ENABLED    (1<<8)  /* Preset value enabled */
#define SDHCI_SDIO_IRQ_ENABLED  (1<<9)  /* SDIO irq enabled */
#define SDHCI_HS200_NEEDS_TUNING (1<<10)    /* HS200 needs tuning */
#define SDHCI_USING_RETUNING_TIMER (1<<11)  /* Host is using a retuning timer for the card */
#define SDHCI_HS400_NEEDS_TUNING (1<<12)    /* HS400 needs tuning */
#define SDHCI_USE_ADMA_64BIT     (1<<13)/* Host is 64-bit ADMA capable */

2.2.struct sdhci_ops (host driver要实现的核心内容)

  sdhci core只是提供了一些接口和符合mmc core的操作集方法给对应的host driver使用。由于各个host的硬件有所差异,所以实际和硬件交互的驱动部分还是在host driver中实现。 所以sdhci core要求host提供标准的访问硬件的一些方法。而这些方法就被定义在了struct sdhci_ops结构体内部。

struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS    
   // 表示host另外提供了一套访问寄存器的方法,没有定义的话,则说明使用通用的读写寄存器的方法
    u32        (*read_l)(struct sdhci_host *host, int reg);
    u16        (*read_w)(struct sdhci_host *host, int reg);
    u8        (*read_b)(struct sdhci_host *host, int reg);
    void        (*write_l)(struct sdhci_host *host, u32 val, int reg);
    void        (*write_w)(struct sdhci_host *host, u16 val, int reg);
    void        (*write_b)(struct sdhci_host *host, u8 val, int reg);
#endif

    void    (*set_clock)(struct sdhci_host *host, unsigned int clock);    // 设置时钟频率

    int        (*enable_dma)(struct sdhci_host *host);    // 使能DMA
    unsigned int    (*get_max_clock)(struct sdhci_host *host);    // 获取支持的最大时钟频率
    unsigned int    (*get_min_clock)(struct sdhci_host *host);    // 获取支持的最小时钟频率
    unsigned int    (*get_timeout_clock)(struct sdhci_host *host);
    int        (*platform_bus_width)(struct sdhci_host *host, int width);  
    void (*platform_send_init_74_clocks)(struct sdhci_host *host,
                         u8 power_mode);
    unsigned int    (*get_ro)(struct sdhci_host *host);    // 获取
    void    (*platform_reset_enter)(struct sdhci_host *host, u8 mask);    // 进入平台复位的方法
    void    (*platform_reset_exit)(struct sdhci_host *host, u8 mask);    // 退出平台复位的方法
    int    (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);    // 设置uhs方式
    void    (*hw_reset)(struct sdhci_host *host);    // 硬件复位的方法
    void    (*platform_suspend)(struct sdhci_host *host);    // 平台host的suspend方法
    void    (*platform_resume)(struct sdhci_host *host);    // 平台host的resume方法
    void    (*adma_workaround)(struct sdhci_host *host, u32 intmask);
    void    (*platform_init)(struct sdhci_host *host);    // 平台host的初始化方法
    void    (*check_power_status)(struct sdhci_host *host, u32 req_type);    // 检测总线的电源状态
#define REQ_BUS_OFF    (1 << 0)
#define REQ_BUS_ON    (1 << 1)
#define REQ_IO_LOW    (1 << 2)
#define REQ_IO_HIGH    (1 << 3)
    int    (*execute_tuning)(struct sdhci_host *host, u32 opcode);    // 执行tuning操作的的方法
    void    (*toggle_cdr)(struct sdhci_host *host, bool enable);
    unsigned int    (*get_max_segments)(void);
    void    (*platform_bus_voting)(struct sdhci_host *host, u32 enable);    // 平台总线投票的方法
    void    (*disable_data_xfer)(struct sdhci_host *host);
    void    (*dump_vendor_regs)(struct sdhci_host *host);
    int    (*config_auto_tuning_cmd)(struct sdhci_host *host,
                      bool enable,
                      u32 type);
    int    (*enable_controller_clock)(struct sdhci_host *host);
    void    (*reset_workaround)(struct sdhci_host *host, u32 enable);
};

2.3. struct mmc_host_ops sdhci_ops

static const struct mmc_host_ops sdhci_ops = {
     // post_req和pre_req是为了实现异步请求处理而设置的
     // 异步请求处理就是指,当另外一个异步请求还没有处理完成的时候,可以先准备另外一个异步请求而不必等待
    .pre_req    = sdhci_pre_req,  
    .post_req   = sdhci_post_req,
    .request    = sdhci_request,    // host处理mmc请求的方法,在mmc_start_request中会调用
    .set_ios    = sdhci_set_ios,   // 设置host的总线的io setting
    .get_cd     = sdhci_get_cd,   // 检测host的卡槽中card的插入状态
    .get_ro     = sdhci_get_ro,  // 获取host上的card的读写属性
    .hw_reset   = sdhci_hw_reset,  // 硬件复位
    .enable_sdio_irq = sdhci_enable_sdio_irq,
    .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,   // 切换信号电压的方法
    .execute_tuning         = sdhci_execute_tuning,   // 执行tuning操作,为card选择一个合适的采样点
    .card_event         = sdhci_card_event,
    .card_busy  = sdhci_card_busy,   // 用于检测card是否处于busy状态
};

注意:这里的sdhci_ops是一个变量名,和上述的struct sdhci_ops不是同一个概念

3.APIs

3.1.sdhci_host 分配和释放 APIs

 struct sdhci_host *sdhci_alloc_host(struct device *dev, size_t priv_size)
 void sdhci_free_host(struct sdhci_host *host)
  • sdhci_alloc_host:为host driver分配一个sdhci_host和mmc_host,并实现其初始化,以及sdhci_host和mmc_host的关联。
  • sdhci_free_host:释放一个sdhci_host。

3.1.1.sdhci_alloc_host

struct sdhci_host *sdhci_alloc_host(struct device *dev,
	size_t priv_size)
{
	struct mmc_host *mmc;
	struct sdhci_host *host;

	mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);

	host = mmc_priv(mmc);
	host->mmc = mmc;
	host->mmc_host_ops = sdhci_ops;
	mmc->ops = &host->mmc_host_ops;

	host->flags = SDHCI_SIGNALING_330;

	host->cqe_ier     = SDHCI_CQE_INT_MASK;
	host->cqe_err_ier = SDHCI_CQE_INT_ERR_MASK;

	host->tuning_delay = -1;
	host->tuning_loop_count = MAX_TUNING_LOOP;

	host->sdma_boundary = SDHCI_DEFAULT_BOUNDARY_ARG;
	host->adma_table_cnt = SDHCI_MAX_SEGS * 2 + 1;

	return host;
}

关键代码调用流程:
sdhci_alloc_host
  -->mmc_alloc_host:分配、设置mmc_host(卡检测的扫描工作队列)

3.2.sdhci_host 注册、卸载

int sdhci_add_host(struct sdhci_host *host);
void sdhci_remove_host(struct sdhci_host *host, int dead);
  • sdhci_add_host用于向sdhci core注册一个sdhci_host,会根据sdhci的寄存器以及部分标识设置其mmc_host,最终将mmc_host注册到mmc core中。 因此,在调用sdhci_add_host之前,必须准备好sdhci的所有硬件环境。
  • sdhci_free_host则用于从sdhci core中卸载一个sdhci_host,对应的mmc_host也会从mmc core中被卸载。

3.2.1.分析sdhci_add_host:

  • sdhci_setup_host:设置sdhci_host;
    • 设置host->flags;
    • 设置DMA;
    • 设置host clock;
    • 等等
  • __sdhci_add_host:注册sdhci_host。
    • 设置请求处理完成时调用的任务队列处理函数sdhci_tasklet_finish
    • 设置当前请求命令的响应定时处理函数sdhci_timeout_timer
    • 设置当前数据交互的响应定时sdhci_timeout_data_timer
    • 设置等待队列的缓冲区读准备中断
    • 设置sdhci_host
    • 设置外部中断(sdhci_set_default_irqs),并注册sdhci_irq和sdhci_thread_irq handler
    • 注册LED灯
    • 调用mmc_add_host
    • 使能卡检测sdhci_enable_card_detection

3.2.2.__sdhci_add_host:

int __sdhci_add_host(struct sdhci_host *host)
{
	struct mmc_host *mmc = host->mmc;
	int ret;

	tasklet_init(&host->finish_tasklet,
		sdhci_tasklet_finish, (unsigned long)host);

	setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
	setup_timer(&host->data_timer, sdhci_timeout_data_timer,
		    (unsigned long)host);

	init_waitqueue_head(&host->buf_ready_int);

	sdhci_init(host, 0);

	ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
				   IRQF_SHARED,	mmc_hostname(mmc), host);

	ret = sdhci_led_register(host);
	mmiowb();

	ret = mmc_add_host(mmc);

	sdhci_enable_card_detection(host);

	return 0;
}

refer to

  • https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842090/SD+controller
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!