Linux内核MMC里的轮询机制

从这篇文章你能学到如何使用MMC框架里的轮询机制做探卡检测 , 十分简单 。
1 前言最近遇到客户提的一个问题 , 大概意思是他们的SDIO Wi-Fi在卸载Wi-Fi驱动后再加载就检测不到Wi-Fi设备了 。从他的问题看大概是热插拔有问题 。
想要支持Wi-Fi复位后能重新扫描到Wi-Fi设备 , 需要host设置MMC_CAP_NEEDS_POLL 。
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */2 如何使用MMC里的轮询机制做探卡检测?先说方法 , 后面再分析 。
方法一:修改dts , 在对应的节点增加字段broken-cd , 同时 , 如果有non-removable字段 , 必须去掉该字段 。

Linux内核MMC里的轮询机制

文章插图
 
方法二:通过其他手段设置host->caps |= MMC_CAP_NEEDS_POLL
3 MMC里的轮询机制剖析3.1 在dts设置broken-cd字段 , 代码在哪里解析?在mmc_of_parse函数 , 路径是driversmmccorecore.c , of_property_read_bool函数读broken-cd字段 , 如果读到 , 就给host设置MMC_CAP_NEEDS_POLL能力 。
int mmc_of_parse(struct mmc_host *host){/* 省略 */ /** Configure CD and WP pins. They are both by default active low to* match the SDHCI spec. If GPIOS are provided for CD and / or WP, the* mmc-gpio helpers are used to attach, configure and use them. If* polarity inversion is specified in DT, one of MMC_CAP2_CD_ACTIVE_HIGH* and MMC_CAP2_RO_ACTIVE_HIGH capability-2 flags is set. If the* "broken-cd" property is provided, the MMC_CAP_NEEDS_POLL capability* is set. If the "non-removable" property is found, the* MMC_CAP_NONREMOVABLE capability is set and no card-detection* configuration is performed.*/ /* Parse Card Detection */ if (of_property_read_bool(np, "non-removable")) {host->caps |= MMC_CAP_NONREMOVABLE;if (of_property_read_bool(np, "cd-post"))host->caps2 |= MMC_CAP2_CD_POST; } else {cd_cap_invert = of_property_read_bool(np, "cd-inverted");if (of_property_read_bool(np, "broken-cd"))host->caps |= MMC_CAP_NEEDS_POLL;ret = mmc_gpiod_request_cd(host, "cd", 0, true,0, &cd_gpio_invert);if (!ret)dev_info(host->parent, "Got CD GPIOn");else if (ret != -ENOENT && ret != -ENOSYS)return ret;/** There are two ways to flag that the CD line is inverted:* through the cd-inverted flag and by the GPIO line itself* being inverted from the GPIO subsystem. This is a leftover* from the times when the GPIO subsystem did not make it* possible to flag a line as inverted.** If the capability on the host AND the GPIO line are* both inverted, the end result is that the CD line is* not inverted.*/if (cd_cap_invert ^ cd_gpio_invert)host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; }/* 省略 */}3.2 探卡检测流程mmc_alloc_host函数会创建一个工作队列 , mmc_rescan与host->detect绑定 。
INIT_DELAYED_WORK(&host->detect, mmc_rescan);mmc_rescan就是扫描卡的函数
void mmc_rescan(struct work_struct *work){ struct mmc_host *host =container_of(work, struct mmc_host, detect.work); int i; if (host->trigger_card_event && host->ops->card_event) {host->ops->card_event(host);host->trigger_card_event = false; } if (host->rescan_disable)return; /* 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); /** if there is a _removable_ card registered, check whether it is* still present*/ if (host->bus_ops && !host->bus_dead&& !(host->caps & MMC_CAP_NONREMOVABLE))host->bus_ops->detect(host); host->detect_change = 0; /** Let mmc_bus_put() free the bus/bus_ops if we've found that* the card is no longer present.*/ mmc_bus_put(host); mmc_bus_get(host); /* if there still is a card present, stop here */ if (host->bus_ops != NULL) {mmc_bus_put(host);goto out; } /** Only we can add a new handler, so it's safe to* release the lock here.*/ mmc_bus_put(host); if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&host->ops->get_cd(host) == 0) {mmc_claim_host(host);mmc_power_off(host);mmc_release_host(host);goto out; } mmc_claim_host(host); for (i = 0; i < ARRAY_SIZE(freqs); i++) {if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))break;if (freqs[i] <= host->f_min)break; } mmc_release_host(host); out: if (host->caps & MMC_CAP_NEEDS_POLL)mmc_schedule_delayed_work(&host->detect, HZ);}看到最后两行 , 判断host的能力 , 如果设置了MMC_CAP_NEEDS_POLL , 也就是轮询机制 , 就会每隔HZ(这是个宏)时间执行一次host->detect , 也就是mmc_rescan 。


推荐阅读