SD卡检测

文章目录

  • SD卡检测
    • 前言
    • 目录
    • 代码分析
    • 问题追踪
    • 所思

前言

   最近公司拿到了Android P的代码,需要在原有的项目基础上将Android 8.1升级为Android 9.0;最为一个职场新人,独挑大梁是不现实的,不过帮大佬"打杂"还是可以的,在配置SD卡的时候,过程比较有意思,学到了一些东西,在此做个记录。

目录

   对于一个Android项目来说,SD卡应该是属于比较简单的一个模块,需要配置的地方也不多,如下:

1、kernel-4.9-lc/arch/arm/boot/dts/[project].dts
2、kernel-4.9-lc/arch/arm/boot/dts/mtxxx.dts
3、kernel-4.9-lc/arch/arm/boot/dts/cust_mtxxxx_msdc.dtsi
4、kernel-4.9-lc/drivers/misc/mediatek/dws/mt6580/[project].dws
5、vendor/mediatek/proprietary/bootable/bootloader/lk/target/[project]/dct/dct/codegen.dws
6、vendor/mediatek/proprietary/bootable/bootloader/preloader/custom/[project]/dct/dct/codegen.dws

代码分析

  
从上面所列代码路径来看,相关的dts文件有三个,[project].dts只作用于特定项目,SD相关代码如下:

&msdc1 {index = /bits/ 8 <1>;clk_src = /bits/ 8 <MSDC1_CLKSRC_187MHZ>;bus-width = <4>;max-frequency = <200000000>;sd_need_power;  //是否配置这条属性决定了当mmc_rescan函数没有检测到SD卡时是否下电,就是因为这个属性,让我兜兜转转转了一圈,后面详细说;cap-sd-highspeed;sd-uhs-sdr12;sd-uhs-sdr25;sd-uhs-sdr50;sd-uhs-sdr104;sd-uhs-ddr50;no-mmc;   //这两条标明不能作为mmc和sdio;no-sdio;pinctrl-names = "default","insert_cfg";pinctrl-0 = <&mmc1_pins_insert_default>; //insert和中断相关,即检测脚相关pinctrl-1 = <&mmc1_pins_insert_cfg>;pinctl = <&msdc1_pins_default>;pinctl_sdr104 = <&msdc1_pins_sdr104>;pinctl_sdr50 = <&msdc1_pins_sdr50>;pinctl_ddr50 = <&msdc1_pins_ddr50>;register_setting = <&msdc1_register_setting_default>;host_function = /bits/ 8 <MSDC_SD>;  //表明被用作sd;cd_level = /bits/ 8 <MSDC_CD_HIGH>;  //如果被设置为0,表示低有效(即正确识卡的时候检测脚为低电平);反之则高电平有效;这个由软件和硬件上共同决定cd-gpios = <&pio 14 0>;  //设置中断脚为GPIO14;vmmc-supply = <&mt_pmic_vmch_ldo_reg>;  //供电,代码中会用到vmmc-supply和vqmmc-supply;vqmmc-supply = <&mt_pmic_vmc_ldo_reg>;
}#include <miki8321p2_9709/cust.dtsi>   //这个文件和dws相关
/*End of this file, DO NOT ADD ANYTHING HERE*/

  
mtxxxx.dts和cust_mtxxxx_msdc.dtsi这两个文件和平台相关,当在项目文件中也定义了相同的内容,最后编译的时候项目中的定义会覆盖掉平台目录下的;所以在此我们只关注项目相关的dts文件;dts文件里面只是一些属性的配置,至于这些属性怎么用?在哪里用?这就得去看代码了,当然,看代码也是要讲究方法的,稍微对SD有了解的童鞋都应该知道,SD卡的代码架构分三层:core、host和card,想要在短时间内完全弄懂肯定是不现实的,所以,首先要明确自己想要从代码里面知道什么,然后再找到一个方向切入进去;在我准备看代码之前,通过对dts文件的配置,冷启动已经可以识别到卡了,但是热插拔无论怎么配置dts都不行,那么,就需要先去了解热插拔的原理。

热插拔实现原理可以从两个方向来说:

1、当硬件底层检测到SD卡状态发生变化时,某个GPIO中断脚产生中断,中断处理函数调用mmc_detect_change函数进行中断脚的检测;

2、host要求轮询sd card插入状态的情况下,所进行的轮询操作(sd card插入状态监控),在mmc_rescan函数中有所体现;

  
首先,先去看_mmc_detect_change函数里面都干了哪些事情,往下看:

static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,bool cd_irq)
{if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&device_can_wakeup(mmc_dev(host)))pm_wakeup_event(mmc_dev(host), 5000);host->detect_change = 1;    //将host->detect_change赋值为1,第一次扫描的时候需要                   mmc_schedule_delayed_work(&host->detect, delay); //唤醒host->detect
}

那么这个host_>detect是在哪里被设置的呢?在mmc_alloc_host函数中,如下:

struct mmc_host *mmc_alloc_host(int extra, struct device *dev){……INIT_DELAYED_WORK(&host->detect, mmc_rescan ); //将host_>detect初始化为mmc_rescan,并加入工作队列,等待以后调用……
}

即在_mmc_detect_change函数中会去调用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->rescan_disable)return;    //rescan_disable表示初始化还没完成,不能进行扫描的操作,直接返回/* If there is a non-removable card registered, only scan once */if (!mmc_card_is_removable(host) && host->rescan_entered)return;    //当non-removable属性被设置时,表示只需要进行一次扫描就OK了host->rescan_entered = 1;/**省略部分代码***//*SD卡检测相关,代码走到这儿说明之前卡槽上是没有卡的,此时,调用get_cd函数去检测是否有卡,若是,返回1;若不是,返回0;*/if (mmc_card_is_removable(host) && host->ops->get_cd && host->ops->get_cd(host) == 0) {mmc_power_off(host);mmc_release_host(host);goto out;   //若没有卡,跳转到out,实现循环检测}/**省略部分代码***/out:if (host->caps & MMC_CAP_NEEDS_POLL)mmc_schedule_delayed_work(&host->detect, HZ); //间隔指定时间循环检测卡的状态是否有变化
}

问题追踪

  总的来说,在代码中会不停的调用mmc_rescan这个函数去扫描mmc总线以检测SD卡的状态是否发生变化,然后做出相应的操作。通过在函数中加调试信息,打印log分析,在热插拔的过程中,mmc_rescan函数执行到SD卡检测那里就挂掉了,而且没有循环去跑这个函数,通过进一步分析,get_cd的返回值为0,就是说没有检测到有卡插入,get_cd的代码如下:

static int msdc_ops_get_cd(struct mmc_host *mmc){int level = 1;/*省略部分代码*/if (mmc->caps & MMC_CAP_NONREMOVABLE) {host->card_inserted = 1;   //若是配置为不可热插拔,直接将host->card_insert设置为1;goto end;} else {#ifdef CONFIG_GPIOLIBlevel = __gpio_get_value(cd_gpio); //获取中断引脚的值#endifhost->card_inserted = (host->hw->cd_level == level) ? 1 : 0; //host->hw->cd_level为dts里面设置的cd_level,若中断引脚的电平与cd_level相同,则将card_insert的值设为1,反之为0;}/*省略部分代码*/return host->card_inserted; //返回card_insert的值}

  在此次项目中,规定为高有效,即插卡后中断检测脚应该为高电平,而此时从代码中来看检测脚的电平为低……此时此刻万用表该上场了,毕竟不会使用万用表的驱动工程师是假的驱动工程师,量了检测脚的电压,果然为0,硬件的同事说再量量供电脚的电压,量了一下,也为0!难怪,没有电压还要要求检测脚正常工作,典型的又要让马儿跑又不给马儿吃草,我估计没有循环的去跑mmc_rescan函数也是因为没有电压,可是为什么没有电压呢?冷启动可以正常识卡说明肯定是有给SD卡上电的,难道是在检测到没卡的时候方下电了,于把下电的功能暂时去掉,编译,刷机,验证:供电引脚一直保持有电压,插拔卡时中断检测脚有电平变化,现在可以明确的定位是下电这里有问题,可以在mmc_rescan函数中看到当没有检测到卡时会调用mmc_power_off函数,函数功能如下,省略部分,直接看重点:

void mmc_power_off(struct mmc_host *host){if (host->ios.power_mode == MMC_POWER_OFF)return;  //若已经处于MMC_POWER_OFF模式,直接返回......host->ios.power_mode = MMC_POWER_OFF; //设置为MMC_POWER_OFF模式......
}

设置为这个模式是为哪里做准备呢?在msdc_ops_set_ios会被用到,如下,还是直接看重点:

void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){.......switch (ios->power_mode) {case MMC_POWER_OFF:spin_unlock(&host->lock);msdc_set_power_mode(host, ios->power_mode);spin_lock(&host->lock);break;case MMC_POWER_UP:spin_unlock(&host->lock);msdc_init_hw(host);msdc_set_power_mode(host, ios->power_mode);spin_lock(&host->lock);break;case MMC_POWER_ON:if (host->hw->host_function == MSDC_SD)msdc_sd_clock_run(host);break;default:break;}...........}

从上面代码中可以看出来,无论是MMC_POWER_OFF模式还是MMC_POWER_ON模式,都调用了msdc_set_power_mode,看看这个函数里面干了什么?看重点:

static void msdc_set_power_mode(struct msdc_host *host, u8 mode){....../*无论上下电都调用到了host->power_control这个函数*/if (host->power_mode == MMC_POWER_OFF && mode != MMC_POWER_OFF){.....if (host->power_control) host->power_control(host, 1);.....}else if (host->power_mode != MMC_POWER_OFF && mode == MMC_POWER_OFF){.....if (host->power_control)  host->power_control(host, 0);   ......
}

到现在都没有看到和vmmc或者vqmmc相关的代码,继续往下看,在msdc_set_host_power_control函数中将msdc_sd_power赋值给host->power_control,所以,在msdc_set_power_mode函数中实际上调用的是msdc_sd_powe函数,如下:

void msdc_set_host_power_control(struct msdc_host *host)
{if (host->hw->host_function == MSDC_EMMC) {host->power_control = msdc_emmc_power;} else if (host->hw->host_function == MSDC_SD) { //在dts里面已经将host_function赋值为了MSDC_SDhost->power_control = msdc_sd_power;host->power_switch = msdc_sd_power_switch;/*省略多余代码*/}/*省略多余代码*/
}void msdc_sd_power(struct msdc_host *host, u32 on)
{
#if !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS)u32 card_on = on;switch (host->id) {case 1:msdc_set_driving(host, &host->hw->driving);msdc_set_tdsel(host, MSDC_TDRDSEL_3V, 0);msdc_set_rdsel(host, MSDC_TDRDSEL_3V, 0);if (host->hw->flags & MSDC_SD_NEED_POWER)card_on = 1;/* VMCH VOLSEL */msdc_ldo_power(card_on, host->mmc->supply.vmmc, VOL_3300,&host->power_flash);msdc_ldo_power(on, host->mmc->supply.vqmmc, VOL_3300,&host->power_io);if (on)msdc_set_sr(host, 0, 0, 0, 0, 0);break;default:break;   }
#endif#ifdef MTK_MSDC_BRINGUP_DEBUGmsdc_dump_ldo_sts(NULL, 0, NULL, host);
#endif
}

  在msdc_sd_power函数中终于看到心心念念的vmmc和vqmmc了,从dts的配置中看出来,vmmc-supply = <&mt_pmic_vmch_ldo_reg>;硬件的同事说供电脚的供电源带了vmch的字样,从代码中可以看到msdc_ldo_power这个函数根据card_on的值来决定是否上电,card_on的值又由是否具有MSDC_SD_NEED_POWER这个标志决定。最后确定是由于缺少MSDC_SD_NEED_POWER这个东西,导致SD卡下电,然后无法检测中断引脚实现热插拔。

  在dts节点里面加上sd_need_power;这个属性,然后在msdc_of_parse函数中添加如下代码:

int msdc_of_parse(struct platform_device *pdev, struct mmc_host *mmc){int len;......if (of_find_property(np, "sd_need_power", &len))host->hw->flags |= MSDC_SD_NEED_POWER;......}

问题到此解决,分析也到此结束……

所思

  最后的解决方法很简单,加起来总共三行代码,但是其实过程还是挺曲折、挺磨人的,在dts上面花费了两天的时间,想破脑袋也想不出来,最开始也加过sd_need_power这个属性,但可能因为编译方式的问题导致没有生效,再加上没有代码做依据就没有在这个属性上面坚持,导致后面又绕了不少圈子,不过也学到了一些东西。以后再遇到类似的问题,首选看代码,看似费时间,实际上是最快的解决方式,一切的问题,始于代码,也终于代码。

MTK平台--Android P SD卡检测相关推荐

  1. Android区分SD卡和U盘

    写过这个功能的童鞋应该很理解我,当初找了大量的资料也只能做到在两者都插入时分辨出哪个是哪个,只插入其中一方,则一脸无奈,当初我甚至在界面上写"检测U盘或SD卡插入,总容量为xx,可用容量为x ...

  2. MTK平台Android 安全中secure boot机制

    一.相关名词解释 1.公钥:通俗来说,公钥就是公开的密钥,是私钥拥有者公开的,公钥通常用于加密会话密钥.验证数字签名,或加密可以用相应的私钥解密的数据. 2.私钥:私有的钥匙,不会公开,私钥加密又称为 ...

  3. 关于Android读取SD卡存储的动态申请

    关于Android读取SD卡存储的动态申请 介绍 Android的目录结构 数据的主要存储方式 疑惑 原来的代码:MainActivity.java 修改后代码: 介绍 这篇文章主要关于我学习SD卡的 ...

  4. Android 判断SD卡是否存在及容量查询

    转载:http://blog.csdn.net/xinzheng_wang/article/details/7827775 Android 判断SD卡是否存在及容量查询的简单方法如下: 首先要在And ...

  5. Android 获取SD卡路径和判断SD卡是否存在.

    android获取sd卡路径方法: public String getSDPath(){ File sdDir = null; boolean sdCardExist =Environment.get ...

  6. Android 系统(138 )---Mtk平台 Android 打包解包*.img ,修改system.img 参数

    Mtk平台 Android 打包解包*.img ,修改system.img 参数 MTK 升级包文件如下: 若存在软件版本号存在错误或需要修改,重新编译则需要几个小时,或者要几天的测试 若可以直接修改 ...

  7. 菜刀工具连接不上_sdspeed for Mac(SD卡检测工具) v3.0.1

    sdspeed Mac版是一款运行在MacOS平台上的SD卡检测工具.sdspeed可以帮助您检查测试SD卡在Mac上读写速度和稳定性,不仅可以测试SD(安全数字)卡,还可以测试USB闪存驱动器,甚至 ...

  8. android读写sd卡代码,android读写SD卡中的文件 demo

    有时,我们需要将更大的文件保存下来,就不能用手机内置的存储空间,毕竟是有限的,所以将文件保存在SD卡中. 要读写SD卡,首先要知道手机上是否有SD卡,且是否可读写 String str = " ...

  9. Android读写SD卡

    Android读写SD卡 0. 参考 解决各版本安卓读写SD卡的问题-java.io.IOException: Operation not permitted问题(兼容到android13) 1. 安 ...

最新文章

  1. 斯坦福大学深度学习与自然语言处理第三讲:高级的词向量表示
  2. Python练习-基于socket的FTPServer
  3. 内存中原码,反码,补码,查看数值范围,,或,异或,取反,左右移位操作
  4. 算法导论之python实现插入排序
  5. 技术管理规划-如何规划团队的架构
  6. gtk linux 升级_Linux包系列的知识(附:Ubuntu16.04升级到18.04的案例)
  7. linux gcc编译器误用-MM导致出现linker input file unused because linking not done
  8. onvif学习笔记6:onvif的OSD坐标小记
  9. html中点击 checkbox (radio也可以) 隐藏tr 或 展现tr
  10. cad放大_dwg文件怎么打开?CAD看图,360°精确识别CAD图块,细节见真章
  11. 操作 神通数据库_国产神通数据库教程
  12. 在家如何下载nature中的文献
  13. 知识付费资源变现小程序源码+可开流量主/带教程
  14. Google快讯 - UTStarcom
  15. android P adb shell dumpsys battery 使用
  16. 清空计算机网络缓存,如何清除DNS缓存 使用命令清理DNS缓存方法
  17. 游戏热更新:游戏客户端热更新那点事
  18. “荣光医院”急救中心 73 问答
  19. ios9系统无法下载应用问题分析与解决
  20. tableau数据分析

热门文章

  1. Unity shader 护盾shield的简单实现
  2. 考研、考公还是找工作?别在大学因为迷茫这个问题浪费时间了
  3. 2018春招京东实习编程题解
  4. MACS磁珠标记细胞分选技术
  5. 横河变送器EJA430E-JCS4G-917DB
  6. Grammar API
  7. 站群服务器金手指排名稳定,黑帽seo手法使用金手指:黑帽SEO,常见的SEO作弊手...
  8. Linux 目录初识
  9. 海豚调度器(dolphinscheduler)再次使用,添加钉钉预警
  10. coso js 魔窗