存储卡具有体积小巧、携带方便、使用简单等优点,在嵌入式系统当中,一般作为独立的存储介质。Linux内核已经支持s3c2416的hsmmc控制器,可以支持mmc卡、sd卡等的读写。

1. hsmmc设备

hsmmc设备包含了名字、独有的资源等等一些驱动程序的硬件或自定义信息。通过platform_add_devices(platform_device_register)函数将定义的平台设备注册到内核中,用于匹配设备驱动。

内核在drivers\mmc\host\sdhci-s3c.c中实现了s3c2416 hsmmc驱动,hsmmc设备的平台代码如下。

  1. #ifdef CONFIG_S3C_DEV_HSMMC
  2. static struct resources3c_hsmmc_resource[] = {
  3. [0]= DEFINE_RES_MEM(S3C_PA_HSMMC0, SZ_4K),
  4. [1]= DEFINE_RES_IRQ(IRQ_HSMMC0),
  5. };
  6. struct s3c_sdhci_platdatas3c_hsmmc0_def_platdata = {
  7. .max_width = 4,
  8. .host_caps = (MMC_CAP_4_BIT_DATA |
  9. MMC_CAP_MMC_HIGHSPEED |MMC_CAP_SD_HIGHSPEED),
  10. };
  11. struct platform_device s3c_device_hsmmc0= {
  12. .name = "s3c-sdhci",
  13. .id = 0,
  14. .num_resources = ARRAY_SIZE(s3c_hsmmc_resource),
  15. .resource = s3c_hsmmc_resource,
  16. .dev = {
  17. .dma_mask = &samsung_device_dma_mask,
  18. .coherent_dma_mask = DMA_BIT_MASK(32),
  19. .platform_data = &s3c_hsmmc0_def_platdata,
  20. },
  21. };
  22. void s3c_sdhci0_set_platdata(structs3c_sdhci_platdata *pd)
  23. {
  24. s3c_sdhci_set_platdata(pd,&s3c_hsmmc0_def_platdata);
  25. }
  26. #endif /* CONFIG_S3C_DEV_HSMMC */
  27. #ifdef CONFIG_S3C_DEV_HSMMC1
  28. static struct resources3c_hsmmc1_resource[] = {
  29. [0]= DEFINE_RES_MEM(S3C_PA_HSMMC1, SZ_4K),
  30. [1]= DEFINE_RES_IRQ(IRQ_HSMMC1),
  31. };
  32. struct s3c_sdhci_platdatas3c_hsmmc1_def_platdata = {
  33. .max_width = 4,
  34. .host_caps = (MMC_CAP_4_BIT_DATA |
  35. MMC_CAP_MMC_HIGHSPEED |MMC_CAP_SD_HIGHSPEED),
  36. };
  37. struct platform_device s3c_device_hsmmc1= {
  38. .name = "s3c-sdhci",
  39. .id = 1,
  40. .num_resources = ARRAY_SIZE(s3c_hsmmc1_resource),
  41. .resource = s3c_hsmmc1_resource,
  42. .dev = {
  43. .dma_mask = &samsung_device_dma_mask,
  44. .coherent_dma_mask = DMA_BIT_MASK(32),
  45. .platform_data = &s3c_hsmmc1_def_platdata,
  46. },
  47. };
  48. void s3c_sdhci1_set_platdata(structs3c_sdhci_platdata *pd)
  49. {
  50. s3c_sdhci_set_platdata(pd,&s3c_hsmmc1_def_platdata);
  51. }
  52. #endif /* CONFIG_S3C_DEV_HSMMC1 */
  53. static struct s3c_sdhci_platdatahome2416_hsmmc0_pdata __initdata = {
  54. .max_width = 4,
  55. .cd_type = S3C_SDHCI_CD_NONE,
  56. };
  57. static struct s3c_sdhci_platdatahome2416_hsmmc1_pdata __initdata = {
  58. .max_width = 4,
  59. .cd_type = S3C_SDHCI_CD_GPIO,
  60. .ext_cd_gpio = S3C2410_GPF(3),
  61. .ext_cd_gpio_invert = 1,
  62. };

在板级初始化函数home2416_machine_init ()中加入hsmmc平台数据s3c_sdhci0_set_platdata(&home2416_hsmmc0_pdata)和s3c_sdhci1_set_platdata(&home2416_hsmmc1_pdata)。在static struct platform_device *home2416_devices[]板级平台设备列表中加入&s3c_device_hsmmc0和&s3c_device_hsmmc1,使hcmmc0、hcmmc1设备能够注册到内核中。

修改并更正驱动文件sdhci-s3c.c。

在sdhci_s3c_probe()函数中,加入gpio检测sd卡。

  1. #include <linux/mmc/slot-gpio.h>// add
  2. if(pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
  3. host->mmc->caps= MMC_CAP_NONREMOVABLE;
  4. //add
  5. if(pdata->cd_type == S3C_SDHCI_CD_GPIO) {
  6. if(gpio_is_valid(pdata->ext_cd_gpio)) {
  7. ret= mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio,
  8. 0);
  9. if(ret) {
  10. dev_err(mmc_dev(host->mmc),
  11. "failedto allocate card detect gpio\n");
  12. gotoerr_req_regs;
  13. }
  14. }
  15. }

在sdhci_s3c_ops结构体中加入读sd卡写保护的实现.get_ro = sdhci_s3c_get_ro,本例sd卡没有硬件写保护。

  1. // add
  2. static unsigned intsdhci_s3c_get_ro(struct sdhci_host *host)
  3. {
  4. return 0; // write enable
  5. }
  6. static struct sdhci_ops sdhci_s3c_ops ={
  7. .get_ro = sdhci_s3c_get_ro, // add
  8. .get_max_clock = sdhci_s3c_get_max_clk,
  9. .set_clock = sdhci_s3c_set_clock,
  10. .get_min_clock = sdhci_s3c_get_min_clock,
  11. .set_bus_width = sdhci_s3c_set_bus_width,
  12. .reset = sdhci_reset,
  13. .set_uhs_signaling = sdhci_set_uhs_signaling,
  14. };

2. 内核配置

Linux内核配置支持hsmmc设备驱动,选中Device Drivers->MMC/SD/SDIO cardsupport->SDHCI support on Samsung S3C SoC。 13_HSMMC驱动 - 图1

sd卡使用fat32文件系统,配置内核支持vfat文件系统。File systems->DOS/FAT/NT Filesystems->VFAT (Windows-95) fssupport。 13_HSMMC驱动 - 图2

3. sd卡测试

cat/proc/devices可以知道hsmmc1的主设备好为179,次设备号为0,在/dev/block目录中创建mmcblk1设备文件。

  1. mknod /dev/mmcblk1 b 179 0

cat/proc/partitions查看sd卡分区,先对sd卡格式化成vfat文件系统。

  1. mkfs.vfat /dev/mmcblk1

挂载mmcblk1设备。

  1. mkdir /mnt/sd
  2. mount /dev/mmcblk1 /mnt/sd

对sd卡读写文件。

  1. echo This is file test. > /mnt/sd/test.txt
  2. cat /mnt/sd/test.txt

13_HSMMC驱动 - 图3

4. 应用编程

应用程序可以通过设备文件访问sd卡,hsmmc应用测试代码hsmmc_test.c如下。

  1. #include "fcntl.h"
  2. #include "unistd.h"
  3. #include "sys/types.h"
  4. #include "stdio.h"
  5. #include "stdlib.h"
  6. #include "string.h"
  7. int main(void)
  8. {
  9. intret;
  10. intfd;
  11. charwrite_buf[64] = "This is file test.";
  12. charread_buf[64];
  13. fd= open("/mnt/sd/test.txt", O_RDWR|O_CREAT);
  14. if(fd == -1) {
  15. printf("Openfile failed\n");
  16. exit(1);
  17. }
  18. ret= write(fd, write_buf, strlen(write_buf));
  19. if(ret == -1) {
  20. printf("Writefile failed\n");
  21. close(fd);
  22. exit(1);
  23. }
  24. close(fd);
  25. printf("write:%s\n", write_buf);
  26. fd= open("/mnt/sd/test.txt", O_RDONLY);
  27. ret= read(fd, read_buf, strlen(write_buf));
  28. if(ret == -1) {
  29. printf("Readfile failed\n");
  30. close(fd);
  31. exit(1);
  32. }
  33. read_buf[strlen(write_buf)]= 0;
  34. printf("read:%s\n", read_buf);
  35. close(fd);
  36. return0;
  37. }

用arm-linux-gcc静态编译,使之生成arm cpu可执行的指令,并且可脱离任何库独立运行,arm-linux-gcc -static -o hsmmc_test hsmmc_test.c,生成hsmmc_test可执行文件。复制可执行文件到根文件系统,目标板启动后在目录输入./hsmmc_test即可执行。 13_HSMMC驱动 - 图4