存储卡具有体积小巧、携带方便、使用简单等优点,在嵌入式系统当中,一般作为独立的存储介质。Linux内核已经支持s3c2416的hsmmc控制器,可以支持mmc卡、sd卡等的读写。
1. hsmmc设备
hsmmc设备包含了名字、独有的资源等等一些驱动程序的硬件或自定义信息。通过platform_add_devices(platform_device_register)函数将定义的平台设备注册到内核中,用于匹配设备驱动。
内核在drivers\mmc\host\sdhci-s3c.c中实现了s3c2416 hsmmc驱动,hsmmc设备的平台代码如下。
#ifdef CONFIG_S3C_DEV_HSMMC
static struct resources3c_hsmmc_resource[] = {
[0]= DEFINE_RES_MEM(S3C_PA_HSMMC0, SZ_4K),
[1]= DEFINE_RES_IRQ(IRQ_HSMMC0),
};
struct s3c_sdhci_platdatas3c_hsmmc0_def_platdata = {
.max_width = 4,
.host_caps = (MMC_CAP_4_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED |MMC_CAP_SD_HIGHSPEED),
};
struct platform_device s3c_device_hsmmc0= {
.name = "s3c-sdhci",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_hsmmc_resource),
.resource = s3c_hsmmc_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s3c_hsmmc0_def_platdata,
},
};
void s3c_sdhci0_set_platdata(structs3c_sdhci_platdata *pd)
{
s3c_sdhci_set_platdata(pd,&s3c_hsmmc0_def_platdata);
}
#endif /* CONFIG_S3C_DEV_HSMMC */
#ifdef CONFIG_S3C_DEV_HSMMC1
static struct resources3c_hsmmc1_resource[] = {
[0]= DEFINE_RES_MEM(S3C_PA_HSMMC1, SZ_4K),
[1]= DEFINE_RES_IRQ(IRQ_HSMMC1),
};
struct s3c_sdhci_platdatas3c_hsmmc1_def_platdata = {
.max_width = 4,
.host_caps = (MMC_CAP_4_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED |MMC_CAP_SD_HIGHSPEED),
};
struct platform_device s3c_device_hsmmc1= {
.name = "s3c-sdhci",
.id = 1,
.num_resources = ARRAY_SIZE(s3c_hsmmc1_resource),
.resource = s3c_hsmmc1_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &s3c_hsmmc1_def_platdata,
},
};
void s3c_sdhci1_set_platdata(structs3c_sdhci_platdata *pd)
{
s3c_sdhci_set_platdata(pd,&s3c_hsmmc1_def_platdata);
}
#endif /* CONFIG_S3C_DEV_HSMMC1 */
static struct s3c_sdhci_platdatahome2416_hsmmc0_pdata __initdata = {
.max_width = 4,
.cd_type = S3C_SDHCI_CD_NONE,
};
static struct s3c_sdhci_platdatahome2416_hsmmc1_pdata __initdata = {
.max_width = 4,
.cd_type = S3C_SDHCI_CD_GPIO,
.ext_cd_gpio = S3C2410_GPF(3),
.ext_cd_gpio_invert = 1,
};
在板级初始化函数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卡。
#include <linux/mmc/slot-gpio.h>// add
if(pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
host->mmc->caps= MMC_CAP_NONREMOVABLE;
//add
if(pdata->cd_type == S3C_SDHCI_CD_GPIO) {
if(gpio_is_valid(pdata->ext_cd_gpio)) {
ret= mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio,
0);
if(ret) {
dev_err(mmc_dev(host->mmc),
"failedto allocate card detect gpio\n");
gotoerr_req_regs;
}
}
}
在sdhci_s3c_ops结构体中加入读sd卡写保护的实现.get_ro = sdhci_s3c_get_ro,本例sd卡没有硬件写保护。
// add
static unsigned intsdhci_s3c_get_ro(struct sdhci_host *host)
{
return 0; // write enable
}
static struct sdhci_ops sdhci_s3c_ops ={
.get_ro = sdhci_s3c_get_ro, // add
.get_max_clock = sdhci_s3c_get_max_clk,
.set_clock = sdhci_s3c_set_clock,
.get_min_clock = sdhci_s3c_get_min_clock,
.set_bus_width = sdhci_s3c_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
2. 内核配置
Linux内核配置支持hsmmc设备驱动,选中Device Drivers->MMC/SD/SDIO cardsupport->SDHCI support on Samsung S3C SoC。
sd卡使用fat32文件系统,配置内核支持vfat文件系统。File systems->DOS/FAT/NT Filesystems->VFAT (Windows-95) fssupport。
3. sd卡测试
cat/proc/devices可以知道hsmmc1的主设备好为179,次设备号为0,在/dev/block目录中创建mmcblk1设备文件。
mknod /dev/mmcblk1 b 179 0
cat/proc/partitions查看sd卡分区,先对sd卡格式化成vfat文件系统。
mkfs.vfat /dev/mmcblk1
挂载mmcblk1设备。
mkdir /mnt/sd
mount /dev/mmcblk1 /mnt/sd
对sd卡读写文件。
echo This is file test. > /mnt/sd/test.txt
cat /mnt/sd/test.txt
4. 应用编程
应用程序可以通过设备文件访问sd卡,hsmmc应用测试代码hsmmc_test.c如下。
#include "fcntl.h"
#include "unistd.h"
#include "sys/types.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int main(void)
{
intret;
intfd;
charwrite_buf[64] = "This is file test.";
charread_buf[64];
fd= open("/mnt/sd/test.txt", O_RDWR|O_CREAT);
if(fd == -1) {
printf("Openfile failed\n");
exit(1);
}
ret= write(fd, write_buf, strlen(write_buf));
if(ret == -1) {
printf("Writefile failed\n");
close(fd);
exit(1);
}
close(fd);
printf("write:%s\n", write_buf);
fd= open("/mnt/sd/test.txt", O_RDONLY);
ret= read(fd, read_buf, strlen(write_buf));
if(ret == -1) {
printf("Readfile failed\n");
close(fd);
exit(1);
}
read_buf[strlen(write_buf)]= 0;
printf("read:%s\n", read_buf);
close(fd);
return0;
}
用arm-linux-gcc静态编译,使之生成arm cpu可执行的指令,并且可脱离任何库独立运行,arm-linux-gcc -static -o hsmmc_test hsmmc_test.c,生成hsmmc_test可执行文件。复制可执行文件到根文件系统,目标板启动后在目录输入./hsmmc_test即可执行。