6. [mmc subsystem] mmc core(第六章)——mmc core主模塊

来源:https://www.cnblogs.com/linhaostudy/archive/2019/05/05/10811296.html
-Advertisement-
Play Games

一、說明 1、mmc core概述 mmc core主模塊是mmc core的實現核心。也是本章的重點內容。 對應代碼位置 。 其主要負責如下功能: mmc core初始化,包括註冊mmc bus、mm host class等等 mmc host的管理和維護,包括為其他模塊提供mmc_host的操作 ...


一、說明

1、mmc core概述

mmc core主模塊是mmc core的實現核心。也是本章的重點內容。

對應代碼位置drivers/mmc/core/core.c

其主要負責如下功能:

  • mmc core初始化,包括註冊mmc bus、mm host class等等
  • mmc host的管理和維護,包括為其他模塊提供mmc_host的操作介面,如下
    • host的啟動和停止
    • host的占用和釋放
    • host電源狀態的保存和恢復
    • host匯流排操作集的綁定和解綁
    • host上卡狀態檢測
  • 為其他模塊提供mmc_card的操作介面,如下
    • card的喚醒和休眠
    • card擦除
    • card屬性的獲取
  • 為其他模塊提供匯流排io setting的介面
  • 為其他模塊提供mmc請求介面
  • card檢測介面
  • bkops操作介面
  • regulator操作介面
  • clock操作介面
  • mmc core電源管理操作介面

2、操作集說明

在mmc_host中有兩個操作集成員,需要理解一下,以免在代碼中產生誤會:

  • mmc_host->struct mmc_host_ops *ops,這個是host的操作集,由host controller驅動決定。對於sdhci類host來說,就是sdhci_ops(sdhci.c中設置)。
  • mmc_host->struct mmc_bus_ops *bus_ops,這個是mmc匯流排的操作集(也可以理解為host的mmc bus handler,host的匯流排處理方法),由匯流排上的card type決定。對於mmc card type來說,就是mmc_ops_unsafe或者mmc_ops(mmc_attach_bus_ops中設置)。

二、API總覽

1、mmc core初始化相關

  • mmc_init & mmc_exit (模塊內使用)

    2、mmc host的管理和維護相關

  • mmc_claim_host & mmc_try_claim_host & mmc_release_host (模塊內使用)
  • mmc_power_up & mmc_power_off
  • mmc_start_host & mmc_stop_host
  • mmc_power_save_host & mmc_power_restore_host
  • mmc_resume_host & mmc_suspend_host
  • mmc_pm_notify

3、mmc card的操作相關(包括card狀態的獲取)

  • mmc_hw_reset & mmc_hw_reset_check &
  • mmc_card_awake & mmc_card_sleep
  • mmc_card_is_prog_state
  • mmc_can_erase
  • mmc_can_trim
  • mmc_can_discard
  • mmc_can_sanitize
  • mmc_can_secure_erase_trim
  • mmc_erase_group_aligned

4、匯流排io setting相關

  • mmc_set_ios
  • mmc_set_chip_select
  • mmc_set_clock
  • mmc_set_bus_mode
  • mmc_set_bus_width
  • mmc_select_voltage
  • mmc_set_signal_voltage(特殊)
  • mmc_set_timing
  • mmc_set_driver_type
  • mmc_get_max_frequency & mmc_get_min_frequency

5、host的mmc匯流排相關

  • mmc_resume_bus
  • mmc_attach_bus & mmc_detach_bus

6、mmc請求相關

  • mmc_request_done
  • mmc_wait_for_req
  • mmc_wait_for_cmd
  • mmc_set_data_timeout
  • mmc_align_data_size

7、card檢測相關

mmc_detect_change
mmc_rescan
mmc_detect_card_removed

8、bkops操作相關

  • mmc_blk_init_bkops_statistics
  • mmc_start_delayed_bkops
  • mmc_start_bkops & mmc_stop_bkops
  • mmc_start_idle_time_bkops
  • mmc_read_bkops_status

9、regulator操作相關

  • mmc_regulator_get_ocrmask
  • mmc_regulator_set_ocr
  • mmc_regulator_get_supply

10、card擦除操作相關

  • mmc_init_erase
  • mmc_erase

11、clock操作介面

  • mmc_init_clk_scaling & mmc_exit_clk_scaling
  • mmc_can_scale_clk
  • mmc_disable_clk_scaling

12、mmc core電源管理操作

  • mmc_rpm_hold & mmc_rpm_release

二、介面代碼說明——mmc core初始化相關

1、mmc_init實現

負責初始化整個mmc core。

  • 主要工作:

    • 分配一個workqueue,用於專門處理mmc core的執行的工作
    • 註冊mmc bus
    • 註冊mmc host class

代碼如下:

static int __init mmc_init(void)
{
    int ret;

/* 分配一個workqueue,用於專門處理mmc core的執行的工作 */
    workqueue = alloc_ordered_workqueue("kmmcd", 0);

/* 註冊mmc bus */
    ret = mmc_register_bus();    // 調用mmc_register_bus註冊mmc bus,具體參考《mmc core——bus模塊說明》
        // 會生成/sys/bus/mmc目錄

/* 註冊mmc host class */
    ret = mmc_register_host_class();    // 調用mmc_register_host_class註冊mmc host class,具體參考《mmc core——host模塊說明》
        // 會生成/sys/class/mmc_host目錄

/* 註冊sdio bus */
    ret = sdio_register_bus();

    return 0;

}

subsys_initcall(mmc_init);

三、介面代碼說明——mmc host的管理和維護相關

1、mmc_claim_host & mmc_try_claim_host & mmc_release_host

host被使能之後就不能再次被使能,並且只能被某個進程獨自占用。

可以簡單地將host理解為一種資源,同時只能被一個進程獲取,但是在占用進程裡面可以重覆占用。

在對host進行操作之前(包括發起mmc請求),必須使用mmc_claim_host和mmc_release_host來進行獲取和釋放。

  • 變數說明

    • mmc_host->claimed用來表示host是否被占用
    • mmc_host->claimer用來表示host的占用者(進程)
    • mmc_host->claim_cnt用來表示host的占用者的占用計數,為0時則會釋放這個host
  • 代碼如下

static inline void mmc_claim_host(struct mmc_host *host)
{
    __mmc_claim_host(host, NULL);·// 調用__mmc_claim_host來獲取host
}

int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{
/////只考慮abort為NULL的情況,在mmc core中的mmc_claim_host也是將其設置為NULL
    DECLARE_WAITQUEUE(wait, current);    
    unsigned long flags;
    int stop;

    might_sleep();        // 說明這個函數可能導致進程休眠

    add_wait_queue(&host->wq, &wait);    // 把當前進程加入到等待隊列中

    spin_lock_irqsave(&host->lock, flags);
    while (1) {    // 以下嘗試獲取host,如果host正在被占用,會進入休眠
        set_current_state(TASK_UNINTERRUPTIBLE);    // 設置進程狀態為TASK_UNINTERRUPTIBLE狀態
        stop = abort ? atomic_read(abort) : 0;
        if (stop || !host->claimed || host->claimer == current)    // 當host的占用標誌claimed為0,或者占用者是當前進程的時候,說明可以占用了,退出
            break;
        spin_unlock_irqrestore(&host->lock, flags);
        schedule();    // 否則,進行調度進入休眠
        spin_lock_irqsave(&host->lock, flags);
    }
    set_current_state(TASK_RUNNING);    // 設置進程為運行狀態
    if (!stop) {
        host->claimed = 1;    // 設置占用標誌claimed 
        host->claimer = current;    // 設置占用者為當前進程
        host->claim_cnt += 1;    // 占用計數加1
    } else
        wake_up(&host->wq);
    spin_unlock_irqrestore(&host->lock, flags);
    remove_wait_queue(&host->wq, &wait);    // 將當前進程從等待隊列中退出
    if (host->ops->enable && !stop && host->claim_cnt == 1)
        host->ops->enable(host);  // 調用host操作集中的enable方法來占用該host,對應sdhci類host即為sdhci_enable
    return stop;
}

void mmc_release_host(struct mmc_host *host)
{
    unsigned long flags;

    WARN_ON(!host->claimed);

    if (host->ops->disable && host->claim_cnt == 1)    // 當前claim_cnt為1(馬上要變為0),調用釋放host了
        host->ops->disable(host);    // 調用host操作集中的disable方法來釋放該host,對應sdhci類host即為sdhci_disable

    spin_lock_irqsave(&host->lock, flags);
    if (--host->claim_cnt) {
        /* Release for nested claim */
        spin_unlock_irqrestore(&host->lock, flags);    // 如果減一之後計數還不為0,說明當前進程需要繼續占用該host,不做其他操作
    } else {    // 以下需要釋放該host
        host->claimed = 0;    // 設置占用標誌claimed為0
        host->claimer = NULL;    // 清空占用者(進程)
        spin_unlock_irqrestore(&host->lock, flags);
        wake_up(&host->wq);    // 喚醒host的等待隊列,讓那些調用mmc_claim_host睡眠等待host資源的進程被喚醒
    }
}

int mmc_try_claim_host(struct mmc_host *host) 
{
// 和mmc_claim_host的主要區別在於進程不會休眠,獲取失敗直接返回
    int claimed_host = 0;
    unsigned long flags;

    spin_lock_irqsave(&host->lock, flags);
    if (!host->claimed || host->claimer == current) {
        host->claimed = 1;
        host->claimer = current;
        host->claim_cnt += 1;
        claimed_host = 1;
    }
    spin_unlock_irqrestore(&host->lock, flags);
    if (host->ops->enable && claimed_host && host->claim_cnt == 1)
        host->ops->enable(host);
    return claimed_host;
}

會調用mmc_host->struct mmc_host_ops->enable和mmc_host->struct mmc_host_ops->disable來使能和禁用host。

對於sdhci類的host,相應就是sdhci_enable和sdhci_disable。

2、mmc_power_up & mmc_power_off

mmc host的上電操作和關電操作。

  • mmc的power狀態

    • MMC_POWER_OFF:掉電狀態
    • MMC_POWER_UP:正在上電的狀態
    • MMC_POWER_ON:供電正常的狀態
  • 主要工作
    主要工作就是初始化host的匯流排設置、匯流排時鐘以及工作電壓、信號電壓。

代碼如下

void mmc_power_up(struct mmc_host *host)
{
    int bit;

/* 判斷是否已經處於MMC_POWER_ON,是的話不進行後續操作 */
    if (host->ios.power_mode == MMC_POWER_ON)
        return;

/* 第一階段,先設置對應的io setting使host處於MMC_POWER_UP的狀態(匯流排工作頻率沒有設置) */
    mmc_host_clk_hold(host);    // 先獲取host時鐘

    /* If ocr is set, we use it */
    if (host->ocr)
        bit = ffs(host->ocr) - 1;    // 選擇一個ocr配置設置為host的工作電壓
    else
        bit = fls(host->ocr_avail) - 1;
    host->ios.vdd = bit;
    if (mmc_host_is_spi(host))
        host->ios.chip_select = MMC_CS_HIGH;
    else {
        host->ios.chip_select = MMC_CS_DONTCARE;
        host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;    // 設置匯流排模式
    }
    host->ios.power_mode = MMC_POWER_UP;
    host->ios.bus_width = MMC_BUS_WIDTH_1;    // 設置匯流排寬度為1
    host->ios.timing = MMC_TIMING_LEGACY;    // 串口時序
    mmc_set_ios(host);    // 調用mmc_set_ios設置匯流排的io setting,後面會說明

    /*
     * This delay should be sufficient to allow the power supply
     * to reach the minimum voltage.
     */
    mmc_delay(10);

/* 第二階段,以host的初始化工作頻率再次設置io setting,使host處於MMC_POWER_ON狀態 */
    host->ios.clock = host->f_init;    // 設置匯流排的時鐘頻率
    host->ios.power_mode = MMC_POWER_ON;
    mmc_set_ios(host);   // 調用mmc_set_ios設置匯流排的io setting,後面會說明

    /*
     * This delay must be at least 74 clock sizes, or 1 ms, or the
     * time required to reach a stable voltage.
     */
    mmc_delay(10);

/* 設置信號的電壓 */
    /* Set signal voltage to 3.3V */
    __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);

    mmc_host_clk_release(host);    // 釋放host時鐘
}

3、mmc_start_host & mmc_stop_host

mmc_start_host 用來啟動一個host,mmc_stop_host用來停止一個host。

當底層host controller調用mmc_add_host來註冊host時,在mmc_add_host中就會調用mmc_start_host來啟動一個host了。具體參考《mmc core——host模塊說明》。

相對應的,會在mmc_remove_host中調用mmc_stop_host停止host。

void mmc_start_host(struct mmc_host *host)
{
    mmc_claim_host(host);    // 因為上電操作涉及到對host的使用和設置,需要先占用host

    host->f_init = max(freqs[0], host->f_min);    // 通過最小頻率要設置初始化頻率
    host->rescan_disable = 0;    // 設置rescan_disable標誌為0,說明已經可以進行card檢測了
    if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)    // 如果mmc屬性設置了MMC_CAP2_NO_PRESCAN_POWERUP,也就是在rescan前不需要進行power up操作時,則進行關電
        mmc_power_off(host);
    else
        mmc_power_up(host);    // 否則,調用mmc_power_up對host進行上電操作。這裡也是mmc core中啟動host的核心函數。

    mmc_release_host(host);    // 完成上電操作,釋放host

/* 到這裡host已經可以工作了,可以開始進行後續的card操作了 */
    mmc_detect_change(host, 0);    // 調用mmc_detect_change檢測card變化,後續會繼續說明
}

四、介面代碼說明——card檢測相關

1、mmc_detect_change

在上述中我們知道在啟動host的函數mmc_start_host 中最後調用了mmc_detect_change來開始檢測card(也就是檢測mmc卡槽的狀態變化情況)。

其實mmc_detect_change是在driver發現mmc卡槽狀態發生變化時,調用mmc_detect_change來進行確認和處理。

void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
#ifdef CONFIG_MMC_DEBUG
    unsigned long flags;
    spin_lock_irqsave(&host->lock, flags);
    WARN_ON(host->removed);
    spin_unlock_irqrestore(&host->lock, flags);
#endif
    host->detect_change = 1;    // 檢測到card狀態發生變化的標識

    mmc_schedule_delayed_work(&host->detect, delay);    // 間隔delay jiffies之後調用host->detect的工作
}

在《host模塊說明》已經知道了在mmc_alloc_host中預設將host->detect工作設置為mmc_rescan(card重新掃描)函數, INIT_DELAYED_WORK(&host->detect, mmc_rescan)。
當然,host也可以自己另外設置,但是一般都是使用mmc core提供的mmc_rescan作為detect工作來搜索card。下麵說明。

2、mmc_rescan

用於檢測host的卡槽狀態,並對狀態變化做相應的操作。

有card插入時,重新掃描mmc card。

void mmc_rescan(struct work_struct *work)
{
    struct mmc_host *host =
        container_of(work, struct mmc_host, detect.work);
    bool extend_wakelock = false;

/* 如果rescan_disable被設置,說明host此時還禁止rescan */
    if (host->rescan_disable)
        return;

/* 對於設備不可移除的host來說,只能rescan一次 */
    /* If there is a non-removable card registered, only scan once */
    if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
        return;
    host->rescan_entered = 1;

    mmc_bus_get(host);    // 獲取host對應的bus
    mmc_rpm_hold(host, &host->class_dev);    // 使host處於rpm resume的狀態

/* 以下判斷原來的card是否已經被移除,移除了則需要做相應的操作 */
    /*
     * if there is a _removable_ card registered, check whether it is
     * still present
     */
    if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
        && !(host->caps & MMC_CAP_NONREMOVABLE))
        host->bus_ops->detect(host);    
        // host->bus_ops存在的話說明之前是有card插入的狀態
        // 需要調用host->bus_ops->detect檢測card是否被移除,是的話在host->bus_ops->detect中做相應的處理
        // 對於mmc type card來說,對應就是mmc_detect,具體參考《card相關模塊》

    host->detect_change = 0;
    /* If the card was removed the bus will be marked
     * as dead - extend the wakelock so userspace
     * can respond */
    if (host->bus_dead)
        extend_wakelock = 1;    // 需要設置一個wakelock鎖,使用戶空間可以及時做出相應

    /*
     * Let mmc_bus_put() free the bus/bus_ops if we've found that
     * the card is no longer present.
     */
    mmc_bus_put(host);    
       // 因為在這個函數的前面已經獲取了一次host,可能導致host->bus_ops->detect中檢測到card拔出之後,沒有真正釋放到host的bus,所以這裡先put一次
        // host bus的計數(bus_refs)為0的時候,會調用__mmc_release_bus清空host bus的信息
    mmc_bus_get(host);
        // 再獲取host bus

    /* if there still is a card present, stop here */
    if (host->bus_ops != NULL) {    // 說明此時還有card插入,退出後續的操作
        mmc_rpm_release(host, &host->class_dev);
        mmc_bus_put(host);
        goto out;
    }

    mmc_rpm_release(host, &host->class_dev);    

    /*
     * Only we can add a new handler, so it's safe to
     * release the lock here.
     */
    mmc_bus_put(host);

/* 檢測當前卡槽狀態,根據卡槽狀態做相應的操作 */
    if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
        // 調用host->ops->get_cd來判斷host的卡槽的當前card插入狀態
        // 對應sdhci類型的host來說,就是sdhci_get_cd
        // 為0的時候,表示沒有card插入,對應host進行power off操作之後進行退出
        // 為1的時候,表示當前有card插入,跳到後續的操作
        mmc_claim_host(host);
        mmc_power_off(host);
        mmc_release_host(host);
        goto out;
    }

    mmc_rpm_hold(host, &host->class_dev);
    mmc_claim_host(host);
    if (!mmc_rescan_try_freq(host, host->f_min))    // 調用mmc_rescan_try_freq,以支持的最低頻率作為工作頻率嘗試搜索card,後續繼續說明
        extend_wakelock = true;
    mmc_release_host(host);
    mmc_rpm_release(host, &host->class_dev);
 out:
    /* only extend the wakelock, if suspend has not started yet */
    if (extend_wakelock && !host->rescan_disable)    
        wake_lock_timeout(&host->detect_wake_lock, HZ / 2);    // 占用wakelock,使系統在HZ/2的時間內不會休眠

    if (host->caps & MMC_CAP_NEEDS_POLL)
        mmc_schedule_delayed_work(&host->detect, HZ);    
               // 當host設置了MMC_CAP_NEEDS_POLL屬性時,需要每隔HZ的時間輪詢檢測host的卡槽狀態,
                // 調度了host->detect工作,對應就是mmc_rescan
}

會調用host->ops->get_cd來判斷host的卡槽的當前card插入狀態,對應sdhci類型的host就是sdhci_get_cd。

會調用host->bus_ops->detect檢測card是否被移除,是的話在host->bus_ops->detect中做相應的處理。對於mmc type card,就是mmc_detect。

3、mmc_rescan_try_freq

以一定頻率搜索host bus上的card。

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
    host->f_init = freq;

    mmc_power_up(host);    // 給host做上電操作

    mmc_hw_reset_for_init(host);    // 硬體複位和初始化
    mmc_go_idle(host);

    mmc_send_if_cond(host, host->ocr_avail);    // 獲取card的可用頻率,存儲到host->ocr_avail中

    /* Order's important: probe SDIO, then SD, then MMC */
/* 用於綁定card到host bus上(也就是card和host的綁定)。 */
    if (!mmc_attach_sdio(host))    // 先假設card是sdio type card,嘗試綁定到host bus上,失敗則說明不是sdio type card,繼續後面的操作,否則返回
        return 0;
    if (!mmc_attach_sd(host))  // 先假設card是sd type card,嘗試綁定到host bus上,失敗則說明不是sd type card,繼續後面的操作,否則返回
        return 0;
    if (!mmc_attach_mmc(host))  // 先假設card是mmc type card,嘗試綁定到host bus上,失敗則說明不是mmc type card,繼續後面的操作,否則返回
        // mmc_attach_mmc通過mmc_host獲取mmc type card信息,初始化mmc_card,併進行部分驅動,最後將其註冊到mmc_bus上。
        // 具體參考《card相關模塊說明》
        return 0;

    mmc_power_off(host);
    return -EIO;
}

五、介面代碼說明——匯流排io setting相關

0、mmc_ios說明

struct mmc_ios 由mmc core定義的規範的結構,用來維護mmc匯流排相關的一些io setting。如下:

struct mmc_ios {
    unsigned int    clock;          /* clock rate */ // 當前工作頻率
    unsigned int    old_rate;       /* saved clock rate */    // 上一次的工作頻率
    unsigned long   clk_ts;         /* time stamp of last updated clock */    // 上一次更新工作頻率的時間戳
    unsigned short  vdd;/* vdd stores the bit number of the selected voltage range from below. */   // 支持的電壓表
    unsigned char   bus_mode;       /* command output mode */    // 匯流排輸出模式,包括開漏模式和上拉模式
    unsigned char   chip_select;        /* SPI chip select */    // spi片選
    unsigned char   power_mode;     /* power supply mode */    // 電源狀態模式
    unsigned char   bus_width;      /* data bus width */    // 匯流排寬度
    unsigned char   timing;         /* timing specification used */    // 時序類型
    unsigned char   signal_voltage;     /* signalling voltage (1.8V or 3.3V) */    // 信號的工作電壓
    unsigned char   drv_type;       /* driver type (A, B, C, D) */    // 驅動類型
};

在設置匯流排io setting的過程中,就是要設置mmc_host->mmc_ios中的這些成員。

然後通過調用mmc_set_ios進行統一設置。後續會繼續說明。

1、mmc_set_ios

統一設置mmc匯流排的io設置(io setting)。

void mmc_set_ios(struct mmc_host *host)
{
    struct mmc_ios *ios = &host->ios;

    if (ios->clock > 0)
        mmc_set_ungated(host);    // 關閉clock的門控
    host->ops->set_ios(host, ios);    // 調用host->ops->set_ios來對mmc匯流排的io setting進行設置,核心函數
        // 對於sdhci類型的host,對應就是sdhci_set_ios
}

會調用host->ops->set_ios來對mmc匯流排的io setting進行設置,核心函數。對於sdhci類型的host,對應就是sdhci_set_ios

2、mmc_set_bus_mode & mmc_set_bus_width

  • mmc_set_bus_mode用於設置匯流排模式,有如下模式
    • MMC_BUSMODE_OPENDRAIN(開漏模式)
    • MMC_BUSMODE_PUSHPULL(上拉模式)
  • mmc_set_bus_width用於設置匯流排寬度,有如下模式
    • MMC_BUS_WIDTH_1
    • MMC_BUS_WIDTH_4
    • MMC_BUS_WIDTH_8

代碼如下:

void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
    mmc_host_clk_hold(host);
    host->ios.bus_mode = mode;
    mmc_set_ios(host);
    mmc_host_clk_release(host);
}

void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
    mmc_host_clk_hold(host);
    host->ios.bus_width = width;
    mmc_set_ios(host);
    mmc_host_clk_release(host);
}

其他設置io setting的函數類似,不多說明。

六、介面代碼說明——host的mmc匯流排相關

1、mmc_attach_bus & mmc_detach_bus

  • 主要功能
    • mmc_attach_bus用於將分配一個mmc匯流排操作集給host。
    • mmc_detach_bus用於釋放和host相關聯的mmc匯流排操作集。
  • 一些變數
    • mmc_host->bus_ops,表示host的mmc匯流排操作集
    • mmc_host->bus_refs,表示host的mmc匯流排的使用者計數
    • mmc_host->bus_dead,表示host的mmc匯流排是否被激活,如果設置了bus_ops,那麼就會被激活了
  • 代碼如下
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{
    unsigned long flags;

    spin_lock_irqsave(&host->lock, flags);

    BUG_ON(host->bus_ops);    // 不允許重覆設置host的mmc匯流排操作集
    BUG_ON(host->bus_refs);    // 當mmc匯流排的使用者計數還存在時,不允許設置host的mmc匯流排操作集

    host->bus_ops = ops;    // 設置host的mmc匯流排操作集
    host->bus_refs = 1;    // host的mmc匯流排的使用者計數設置為1,相當於調用了mmc_bus_get
    host->bus_dead = 0;    // 匯流排被激活了

    spin_unlock_irqrestore(&host->lock, flags);
}

void mmc_detach_bus(struct mmc_host *host)
{
    unsigned long flags;
    spin_lock_irqsave(&host->lock, flags);
    host->bus_dead = 1;    // host的mmc匯流排設置為dead狀態
    spin_unlock_irqrestore(&host->lock, flags);
    mmc_bus_put(host);    // 調用mmc_bus_put釋放host的mmc匯流排,也就是對host的mmc匯流排的使用者計數-1
}

在《card相關模塊》中可以看到mmc_attach_mmc->mmc_attach_bus_ops調用mmc_attach_bus來綁定了host的mmc匯流排操作集為mmc_ops_unsafe或者mmc_ops

2、mmc_bus_get & mmc_bus_put

static inline void mmc_bus_get(struct mmc_host *host)
{
    unsigned long flags;

    spin_lock_irqsave(&host->lock, flags);
    host->bus_refs++;    // 對host的mmc匯流排的使用者計數+1
    spin_unlock_irqrestore(&host->lock, flags);
}

static inline void mmc_bus_put(struct mmc_host *host)
{
    unsigned long flags;

    spin_lock_irqsave(&host->lock, flags);
    host->bus_refs--;    // 對host的mmc匯流排的使用者計數-1
    if ((host->bus_refs == 0) && host->bus_ops)    // 說明host的mmc匯流排當前並沒有使用,調用__mmc_release_bus進行實際的釋放操作
        __mmc_release_bus(host);
    spin_unlock_irqrestore(&host->lock, flags);
}

static void __mmc_release_bus(struct mmc_host *host)
{
    host->bus_ops = NULL; // 清空host的mmc匯流排操作集
}

七、介面代碼說明——mmc請求相關

分成同步的mmc請求和非同步的mmc請求。差別如下:

1、流程上的差別:
(1)會阻塞的處理流程:
mmc_wait_for_req
——》__mmc_start_req // 發起請求
————》init_completion(&mrq->completion);  
————》mrq->done = mmc_wait_done
————》mmc_start_request(host, mrq);   // 實際發起請求的操作
——》mmc_wait_for_req_done   // 阻塞等待請求處理完成
——》返回

(2)不阻塞等待該命令的處理流程:
(註意:並不是說調用這個介面並不會阻塞,而是不會為了等待當前請求處理完成而阻塞,但是可能會等待上一次請求處理完成而阻塞)
mmc_start_req
——》mmc_wait_for_data_req_done   // 阻塞等待上一次的請求處理
——》__mmc_start_data_req   // 發起非同步請求
————》mrq->done = mmc_wait_data_done
————》mmc_start_request   // 實際發起請求的操作
——》返回

最後都是調用了mmc_start_request使host向MMC發起請求。

0、數據結構說明

一個mmc請求分成兩部分內容,分別是命令部分和數據部分。

  • mmc_command
struct mmc_command {
    u32            opcode;    // 命令的操作碼,如MMC_GO_IDLE_STATE、MMC_SEND_OP_COND等等
    u32            arg;    // 命令的參數
    u32            resp[4];    // response值
    unsigned int        flags;        /* expected response type */    // 期待的response的類型
#define mmc_resp_type(cmd)    ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))

/*
* These are the command types.
*/
#define mmc_cmd_type(cmd)    ((cmd)->flags & MMC_CMD_MASK)

    unsigned int        retries;    /* max number of retries */    // 失敗時的重覆嘗試次數
    unsigned int        error;        /* command error */    // 命令的錯誤碼

/*
* Standard errno values are used for errors, but some have specific
* meaning in the MMC layer:
*
 * ETIMEDOUT    Card took too long to respond
 * EILSEQ       Basic format problem with the received or sent data
 *              (e.g. CRC check failed, incorrect opcode in response
 *              or bad end bit)
 * EINVAL       Request cannot be performed because of restrictions
 *              in hardware and/or the driver
 * ENOMEDIUM    Host can determine that the slot is empty and is
 *              actively failing requests
*/

    unsigned int        cmd_timeout_ms;    /* in milliseconds */    // 命令執行的等待超時事件

    struct mmc_data        *data;        /* data segment associated with cmd */    // 和該命令關聯在一起的數據段
    struct mmc_request    *mrq;        /* associated request */    // 該命令關聯到哪個request
};
  • mmc_data
struct mmc_data {
    unsigned int        timeout_ns; /* data timeout (in ns, max 80ms) */   // 超時時間,以ns為單位
    unsigned int        timeout_clks;   /* data timeout (in clocks) */   // 超時時間,以clock為單位
    unsigned int        blksz;      /* data block size */   // 塊大小
    unsigned int        blocks;     /* number of blocks */   // 塊數量
    unsigned int        error;      /* data error */   // 傳輸的錯誤碼
    unsigned int        flags;   // 傳輸標識

#define MMC_DATA_WRITE  (1 << 8)
#define MMC_DATA_READ   (1 << 9)
#define MMC_DATA_STREAM (1 << 10)

    unsigned int        bytes_xfered;

    struct mmc_command  *stop;      /* stop command */   // 結束傳輸的命令
    struct mmc_request  *mrq;       /* associated request */   // 該命令關聯到哪個request

    unsigned int        sg_len;     /* size of scatter list */
    struct scatterlist  *sg;        /* I/O scatter list */
    s32         host_cookie;    /* host private data */
    bool            fault_injected; /* fault injected */
};
  • mmc_request

struct mmc_request是mmc core向host controller發起命令請求的處理單位。

struct mmc_request {
    struct mmc_command    *sbc;        /* SET_BLOCK_COUNT for multiblock */    // 設置塊數量的命令,怎麼用的後續再補充
    struct mmc_command    *cmd;    // 要傳輸的命令
    struct mmc_data        *data;    // 要傳輸的數據
    struct mmc_command    *stop;    // 結束命令,怎麼用的後續再補充

    struct completion    completion; // 完成量
    void            (*done)(struct mmc_request *);/* completion function */ // 傳輸結束後的回調函數
    struct mmc_host        *host;    // 所屬host
};
  • mmc_async_req
struct mmc_async_req {
    /* active mmc request */
    struct mmc_request  *mrq;
    unsigned int cmd_flags; /* copied from struct request */

    /*
     * Check error status of completed mmc request.
     * Returns 0 if success otherwise non zero.
     */
    int (*err_check) (struct mmc_card *, struct mmc_async_req *);
    /* Reinserts request back to the block layer */
    void (*reinsert_req) (struct mmc_async_req *);
    /* update what part of request is not done (packed_fail_idx) */
    int (*update_interrupted_req) (struct mmc_card *,
            struct mmc_async_req *);
};

1、mmc_wait_for_req

發起mmc_request請求並且等待其處理完成。由其他需要發起mmc請求的模塊調用。

可以結合後面的mmc_request_done來看。

void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
    if (mmc_bus_needs_resume(host))
        mmc_resume_bus(host);
#endif
    __mmc_start_req(host, mrq);    // 開始發起mmc_request請求
    mmc_wait_for_req_done(host, mrq);    // 等待mmc_request處理完成
}

//-----------------------------------__mmc_start_req說明,開始發起mmc_request請求
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
/* 發起mmc_request前的一些初始化工作,包括完成量和處理完成的回調函數的設置 */
    init_completion(&mrq->completion);    // 初始化完成量,在mmc_wait_for_req_done中會去等待這個完成量
    mrq->done = mmc_wait_done; 
        // 設置mmc_request處理完成的回調函數,會調用complete(&mrq->completion);來設置完成量
        // host controller會調用mmc_request_done來執行這個回調函數,具體在後面分析
    if (mmc_card_removed(host->card)) {    // 檢測card是否存在
        mrq->cmd->error = -ENOMEDIUM;
        complete(&mrq->completion);
        return -ENOMEDIUM;
    }

/* 調用mmc_start_request發起mmc請求 */
    mmc_start_request(host, mrq);    // 開始處理mmc_request請求
    return 0;
}

static void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{

    WARN_ON(!host->claimed);

/* 以下對mmc_request的各個成員,包括cmd、data、stop做驗證操作和關聯操作 */
    mrq->cmd->error = 0;
    mrq->cmd->mrq = mrq;
    if (mrq->data) {
        BUG_ON(mrq->data->blksz > host->max_blk_size);
        BUG_ON(mrq->data->blocks > host->max_blk_count);
        BUG_ON(mrq->data->blocks * mrq->data->blksz >
            host->max_req_size);
        mrq->cmd->data = mrq->data;      // 也就是說mmc_request的data和其cmd中的data是一一樣的
        mrq->data->error = 0;
        mrq->data->mrq = mrq;
        if (mrq->stop) {
            mrq->data->stop = mrq->stop;
            mrq->stop->error = 0;
            mrq->stop->mrq = mrq;
        }
#ifdef CONFIG_MMC_PERF_PROFILING
        if (host->perf_enable)
            host->perf.start = ktime_get();
#endif
    }

/* 獲取時鐘 */
    mmc_host_clk_hold(host);

/* 調用host controller的request方法來處理mmc_request請求 */
    host->ops->request(host, mrq);    
       // host->ops->request也就是host controller的request方法,對於sdhci類型的host來說,就是sdhci_request
}

//-----------------------------------mmc_wait_for_req_done說明,等待mmc_request處理完成
static void mmc_wait_for_req_done(struct mmc_host *host,
                  struct mmc_request *mrq)
{
    struct mmc_command *cmd;

    while (1) {
        wait_for_completion_io(&mrq->completion);   // 在這裡休眠,等待mrq->completion完成量,在__mmc_start_req中初始化的

        cmd = mrq->cmd;   // 獲取對應的command

        /*
         * If host has timed out waiting for the commands which can be
         * HPIed then let the caller handle the timeout error as it may
         * want to send the HPI command to bring the card out of
         * programming state.
         */
        if (cmd->ignore_timeout && cmd->error == -ETIMEDOUT)
            break;

        if (!cmd->error || !cmd->retries || mmc_card_removed(host->card))
                // 如果command正常處理完成,或者失敗重覆嘗試次數為0,或者card被移除了,直接退出迴圈返回
            break;

                // 以下處理失敗重覆嘗試的情況
        pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
             mmc_hostname(host), cmd->opcode, cmd->error);
        cmd->retries--;
        cmd->error = 0;
        host->ops->request(host, mrq);
    }
}

會調用host->ops->request來對mmc_request進行處理,對於sdhci類型的host,對應就是sdhci_request。

這個方法就是mmc_request實際被處理的核心。

2、mmc_request_done

通知mmc core某個mmc_request已經處理完成,由host controller調用。

以sdhci類型的host為例,處理完一個mmc_request之後,會執行sdhci_tasklet_finish,而在sdhci_tasklet_finish中會調用mmc_request_done來通知host某個mmc_request已經處理完成了。

void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
    struct mmc_command *cmd = mrq->cmd;
    int err = cmd->error;

    if (host->card)
        mmc_update_clk_scaling(host);

    if (err && cmd->retries && !mmc_card_removed(host->card)) {
                // command執行出錯,如果還需要重覆嘗試的話,這裡不釋放clock,只是通知mmc core
        if (mrq->done)
            mrq->done(mrq);   
               // 執行mmc_request的回調函數來通知mmc core,
               // 對於__mmc_start_req發起的request來說,就是mmc_wait_done,上面已經說明過了
               // 對於__mmc_start_data_req發起的request來說,就是mmc_wait_data_done,後面會說明
    } else {
        mmc_should_fail_request(host, mrq);   
                // 用於模擬data傳輸概率出錯的情況
                // 具體參考http://blog.csdn.net/luckywang1103/article/details/52224160

        if (mrq->done)
            mrq->done(mrq);
                // 執行mmc_request的回調函數來通知mmc core,對於__mmc_start_req發起的request來說,就是mmc_wait_done,上面已經說明過了

        mmc_host_clk_release(host);
    }
}

通過上述,mrq->done被調度,mmc_wait_done被執行,mrq->completion被設置。

然後等待mrq->completion的mmc_wait_for_req_done就會繼續往下執行。

3、mmc_wait_for_cmd

mmc_wait_for_cmd用於處理一個不帶數據請求的命令。

會被封裝到mmc_request中,通過調用mmc_wait_for_req來發起請求。

int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
    struct mmc_request mrq = {NULL};

    WARN_ON(!host->claimed);
    memset(cmd->resp, 0, sizeof(cmd->resp));   // 清空command的response
    cmd->retries = retries;   // 失敗時的重覆嘗試次數
    mrq.cmd = cmd;   // 封裝到mmc_request中
    cmd->data = NULL;   // 不帶數據包的命令,故清空data
    mmc_wait_for_req(host, &mrq);   // 調用mmc_wait_for_req發起mmc請求並且等待其處理完成

    return cmd->error;   // 返回錯誤碼
}

4、mmc_start_req(重要)

機制說明如下:mmc_start_req會先判斷上一次的asycn_req是否處理完成,如果沒有處理完成,則會等待其處理完成。

如果處理完成了,為當前要處理的asycn_req發起請求,但是並不會等待,而是直接返回。

註意:並不是說調用這個介面並不會阻塞,而是不會為了等待當前請求處理完成而阻塞,但是可能會等待上一次請求處理完成而阻塞。這樣,可以利用等待的一部分時間來做其他操作。

為了方便理解這個函數,需要看一下其函數註釋。

  • 要註意,在函數裡面有兩個非同步請求:
    • areq:表示新的非同步請求
    • host->areq:表示上一次發起的、正在處理、等待完成的非同步請求

代碼如下(為了方便理解,對代碼進行了簡化):

/**
 *  mmc_start_req - start a non-blocking request    // 該函數用來發起一個不阻塞的請求
 *  @host: MMC host to start command    // 要發起對應請求的host
 *  @areq: async request to start    // 要發起的非同步請求
 *  @error: out parameter returns 0 for success, otherwise non zero    // 返回值,返回0表示成功,返回非零表示失敗
 *
 *  Start a new MMC custom command request for a host.    // 為host發起的一個新的mmc命令請求
 *  If there is on ongoing async request wait for completion    // 如果host已經有一個正在處理、等待完成的非同步請求,那麼會等待這個請求完成!!!
 *  of that request and start the new one and return.    // 然後發起新的請求,然後返回!!!
 *  Does not wait for the new request to complete.    // 並不會等待這個新的請求完成!!!
 *
 *      Returns the completed request, NULL in case of none completed.    // 會返回被完成的mmc請求(而不是新的mmc請求。)空表示沒有mmc請求被完成。
 *  Wait for the an ongoing request (previoulsy started) to complete and
 *  return the completed request. If there is no ongoing request, NULL
 *  is returned without waiting. NULL is not an error condition.
// 等待上一次發起的mmc請求完成,然後把這個mmc請求返回。如果沒有mmc請求正在處理,那麼就直接返回而不會等待。空並不是錯誤條件。
 */
struct mmc_async_req *mmc_start_req(struct mmc_host *host,
                    struct mmc_async_req *areq, int *error)
{
    int err = 0;
    int start_err = 0;
    struct mmc_async_req *data = host->areq;
    unsigned long flags;
    bool is_urgent;

    /* Prepare a new request */
/* 為新的非同步請求做準備處理 */
    if (areq) {
        /*
         * start waiting here for possible interrupt
         * because mmc_pre_req() taking long time
         */
        mmc_pre_req(host, areq->mrq, !host->areq);
    }

/* 對上一次發起的、正在處理、等待完成的非同步請求進行處理、等待操作 */
    if (host->areq) {
        err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq);   // 在這裡等待正在處理的非同步請求處理完成
        //.......以下過濾了錯誤處理的部分
    }

/* 對新的非同步請求進行發起操作 */
    if (!err && areq) {
        /* urgent notification may come again */
        spin_lock_irqsave(&host->context_info.lock, flags);
        is_urgent = host->context_info.is_urgent;
        host->context_info.is_urgent = false;
        spin_unlock_irqrestore(&host->context_info.lock, flags);
        if (!is_urgent || (areq->cmd_flags & REQ_URGENT)) {
            start_err = __mmc_start_data_req(host, areq->mrq);    // 調用__mmc_start_data_req發起新的非同步請求
        } else {
            /* previous request was done */
            err = MMC_BLK_URGENT_DONE;
            if (host->areq) {
                mmc_post_req(host, host->areq->mrq, 0);
                host->areq = NULL;
            }
            areq->reinsert_req(areq);
            mmc_post_req(host, areq->mrq, 0);
            goto exit;
        }
    }

    if (host->areq)
        mmc_post_req(host, host->areq->mrq, 0);

     /* Cancel a prepared request if it was not started. */
    if ((err || start_err) && areq)
        mmc_post_req(host, areq->mrq, -EINVAL);

    if (err)
        host->areq = NULL;
    else
        host->areq = areq;

exit:
    if (error)
        *error = err;
    return data;    // 反正上一次正常處理的非同步請求
}

//-----------------------------------------------------------------------------------------------------------------------------
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
{
    mrq->done = mmc_wait_data_done;
        // 設置mmc_request處理完成的回調函數,會喚醒正在等待請求被完成的進程,後面說明
        // host controller會調用mmc_request_done來執行這個回調函數,具體前面分析過了

    mrq->host = host;
    mmc_start_request(host, mrq);    // 開始處理mmc_request請求,前面已經說明過了

    return 0;
}

static void mmc_wait_data_done(struct mmc_request *mrq)
{
    unsigned long flags;
    struct mmc_context_info *context_info = &mrq->host->context_info;

    spin_lock_irqsave(&context_info->lock, flags);
    mrq->host->context_info.is_done_rcv = true;    // 設置is_done_rcv標識
    wake_up_interruptible(&mrq->host->context_info.wait);    // 喚醒context_info上的等待進程
    spin_unlock_irqrestore(&context_info->lock, flags);
}

//-----------------------------------------------------------------------------------------------------------------------------
static int mmc_wait_for_data_req_done(struct mmc_host *host,
                      struct mmc_request *mrq,
                      struct mmc_async_req *next_req)
{
// struct mmc_request *mrq:表示正在等待完成的請求
// struct mmc_async_req *next_req:表示下一次要執行的非同步請求
    struct mmc_command *cmd;
    struct mmc_context_info *context_info = &host->context_info;
    bool pending_is_urgent = false;
    bool is_urgent = false;
    bool is_done_rcv = false;
    int err, ret;
    unsigned long flags;

    while (1) {
/* 在這裡等待正在進行的請求完成,會在mmc_wait_data_done中被喚醒 */
/* 有幾種情況會喚醒等待進程 */
        ret = wait_io_event_interruptible(context_info->wait,(context_info->is_done_rcv || context_info->is_new_req  || context_info->is_urgent));
        spin_lock_irqsave(&context_info->lock, flags);
        is_urgent = context_info->is_urgent;
        is_done_rcv = context_info->is_done_rcv;
        context_info->is_waiting_last_req = false;
        spin_unlock_irqrestore(&context_info->lock, flags);

/* 對請求處理完成的處理 */
        if (is_done_rcv) {
            context_info->is_done_rcv = false;
            context_info->is_new_req = false;
            cmd = mrq->cmd;


            if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) {
/* 請求正常處理完成,或者失敗但是不需要重覆嘗試的情況的處理 */
                err = host->areq->err_check(host->card, host->areq);
                //.......
                break; /* return err */
            } else {
/* 對請求處理出錯並且需要重覆嘗試的情況的處理 */
                //.......
            }
        }
    }
    return err;
}


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這裡以Centos7為例: 使用tree命令查看/目錄結構如下: 下麵我們主要探討如下主要目錄: /:根目錄不必多說,文件系統的最頂端,存放系統所有目錄。 bin:該目錄主要存放系統運行所需要的重要命令,和普通用戶可以使用的絕大部分命令。 sbin:主要存放系統管理員使用的系統管理的命令。 etc: ...
  • @ "TOC" quickLook插件是Mac上的快速瀏覽的一個功能,現在win10系統上也能安裝插件,這個插件可以快速瀏覽txt,doc,圖片,表格等文件如下圖: 我認為最方便的地方是你需要查看某一個文件內容不需要編輯的時候,又不想用wps打開時就可以用這個簡單的插件。 安裝方式也很簡單,但是需要 ...
  • 一、echo 1.顯示普通字元串: 這裡的雙引號可以省略。 2.顯示轉義字元: 3.顯示變數: read 命令從標準輸入中讀取一行,並把輸入行的每個欄位的值指定給 shell 變數 輸出: 4.顯示換行: 輸出: 5.顯示不換行: 輸出: 6.顯示定向至文件: 輸出: 7.原樣輸出字元串,不進行轉義 ...
  • [20190505]ts 命令在哪裡.txt--//在論壇問一下ts命令在哪裡?沒人解答,自己也google看了一下:https://unix.stackexchange.com/questions/272433/piping-into-moreutils-ts-with-nanosecond-pr ...
  • 一、sdhci core說明 1、sdhci說明 具體參考《host(第一章)——概述》 SDHC:Secure Digital(SD) Host Controller,是指一套sd host控制器的設計標準,其寄存器偏移以及意義都有一定的規範,並且提供了對應的驅動程式,方便vendor進行host ...
  • 由於linux最小單位為分,但是很多需求上需要按秒執行,如30秒請求一個URL地址之類的,思路很簡單就是修改計劃任務腳本用迴圈控制,代碼如下: 上述代碼中XXXXXX為你需要執行的URL地址 以上示例以寶塔下計劃任務為基礎所演示 ...
  • 一、前言 1、簡介 在上一篇UART詳解中,已經有了關於UART的詳細介紹了,也有關於如何使用STM32CubeMX來配置UART的操作了,而在該篇博客,主要會講解一下如何實現UART串口的發送功能。 2、UART簡介 嵌入式開發中,UART串口通信協議是我們常用的通信協議之一,全稱叫做通用非同步收發 ...
  • 一、host簡單說明 host,也可以理解為host controller,是指mmc匯流排上的主機端,mmc匯流排的控制器,每個host controller對應一條mmc匯流排。 host controller會控制命令線、數據線和時鐘線,從而實現mmc匯流排上的通訊。 上層發送mmc請求時,就是通過h ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...