/* drivers/mmc/cprmdrv_samsung.c * * Copyright 2010 Samsung Electronics Co.Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include "cprmdrv_samsung.h" #include static int mmc_wait_busy(struct mmc_card *card) { int ret, busy; struct mmc_command cmd; busy = 0; do { memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SEND_STATUS; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ret = mmc_wait_for_cmd(card->host, &cmd, 0); if (ret) break; if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) { busy = 1; printk(KERN_INFO "%s: Warning: Host did not " "wait for busy state to end.\n", mmc_hostname(card->host)); } } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); return ret; } static int CPRM_CMD_SecureRW(struct mmc_card *card, unsigned int command, unsigned int dir, unsigned long arg, unsigned char *buff, unsigned int length) { int err; int i = 0; struct mmc_request mrq; struct mmc_command cmd; struct mmc_command stop; struct mmc_data data; struct scatterlist sg; if (command == SD_ACMD25_SECURE_WRITE_MULTI_BLOCK || command == SD_ACMD18_SECURE_READ_MULTI_BLOCK) { return -EINVAL; } memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_APP_CMD; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, 0); if (err) return (u32)-1; if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD)) return (u32)-1; printk("CPRM_CMD_SecureRW: 1, command : %d\n", command); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = command; if (command == SD_ACMD43_GET_MKB) cmd.arg = arg; else cmd.arg = 0; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; memset(&data, 0, sizeof(struct mmc_data)); data.timeout_ns = 100000000; data.timeout_clks = 0; #if defined(CONFIG_TARGET_LOCALE_NTT) data.timeout_ns = 100000000; data.timeout_clks = 0; #endif data.blksz = length; data.blocks = 1; data.flags = dir; data.sg = &sg; data.sg_len = 1; stop.opcode = MMC_STOP_TRANSMISSION; stop.arg = 0; stop.flags = MMC_RSP_R1B | MMC_CMD_AC; memset(&mrq, 0, sizeof(struct mmc_request)); mrq.cmd = &cmd; mrq.data = &data; if (data.blocks == 1) mrq.stop = NULL; else mrq.stop = &stop; printk(KERN_DEBUG "CPRM_CMD_SecureRW: 2\n"); sg_init_one(&sg, buff, length); printk(KERN_DEBUG "CPRM_CMD_SecureRW: 3\n"); mmc_wait_for_req(card->host, &mrq); printk(KERN_DEBUG "CPRM_CMD_SecureRW: 4\n"); i = 0; do { printk(KERN_DEBUG "%x", buff[i++]); if (i > 10) break; } while (i < length); printk(KERN_DEBUG "\n"); if (cmd.error) { printk(KERN_DEBUG "%s]cmd.error=%d\n ", __func__, cmd.error); return cmd.error; } if (data.error) { printk(KERN_DEBUG "%s]data.error=%d\n ", __func__, data.error); return data.error; } err = mmc_wait_busy(card); printk(KERN_DEBUG "CPRM_CMD_SecureRW: 5\n"); if (err) return err; return 0; } static int CPRM_CMD_SecureMultiRW(struct mmc_card *card, unsigned int command, unsigned int dir, unsigned long arg, unsigned char *buff, unsigned int length) { int err; struct mmc_request mrq; struct mmc_command cmd; struct mmc_command stop; struct mmc_data data; unsigned long flags; struct scatterlist sg; memset(&cmd, 0, sizeof(struct mmc_command)); memset(&stop, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_APP_CMD; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, 0); if (err) return (u32)-1; if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD)) return (u32)-1; printk(KERN_DEBUG "CPRM_CMD_SecureRW: 1\n"); memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = command; if (command == SD_ACMD43_GET_MKB) cmd.arg = arg; else cmd.arg = 0; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; memset(&data, 0, sizeof(struct mmc_data)); data.timeout_ns = 100000000; data.timeout_clks = 0; #if defined(CONFIG_TARGET_LOCALE_NTT) data.timeout_ns = 100000000; data.timeout_clks = 0; #endif data.blksz = 512; data.blocks = (length + 511) / 512; data.flags = dir; data.sg = &sg; data.sg_len = 1; stop.opcode = MMC_STOP_TRANSMISSION; stop.arg = 0; stop.flags = MMC_RSP_R1B | MMC_CMD_AC; memset(&mrq, 0, sizeof(struct mmc_request)); mrq.cmd = &cmd; mrq.data = &data; mrq.stop = &stop; printk(KERN_DEBUG "CPRM_CMD_SecureRW: 2\n"); sg_init_one(&sg, buff, length); if (dir == MMC_DATA_WRITE) { local_irq_save(flags); sg_copy_from_buffer(&sg, data.sg_len, buff, length); local_irq_restore(flags); } printk(KERN_DEBUG "CPRM_CMD_SecureRW: 3\n"); mmc_wait_for_req(card->host, &mrq); printk(KERN_DEBUG "CPRM_CMD_SecureRW: 4\n"); if (cmd.error) { printk(KERN_DEBUG "%s]cmd.error=%d\n", __func__, cmd.error); return cmd.error; } if (data.error) { printk(KERN_DEBUG "%s]data.error=%d\n", __func__, data.error); return data.error; } err = mmc_wait_busy(card); printk(KERN_DEBUG "CPRM_CMD_SecureRW: 5\n"); if (dir == MMC_DATA_READ) { local_irq_save(flags); sg_copy_to_buffer(&sg, data.sg_len, buff, length); local_irq_restore(flags); } if (err) return err; return 0; } int stub_sendcmd(struct mmc_card *card, unsigned int cmd, unsigned long arg, unsigned int len, unsigned char *buff) { int returnVal = -1; unsigned char *kbuffer = NULL; int direction = 0; int result = 0; if (card == NULL) { printk(KERN_DEBUG "stub_sendcmd: card is null error\n"); return -ENXIO; } kbuffer = kmalloc(len, GFP_KERNEL); if (kbuffer == NULL) { printk(KERN_DEBUG "malloc failed\n"); return -ENOMEM; } memset(kbuffer, 0x00, len); printk(KERN_DEBUG "%s]cmd=0x%x,len=%d\n ", __func__, cmd, len); mmc_claim_host(card->host); switch (cmd) { case ACMD43: direction = MMC_DATA_READ; returnVal = CPRM_CMD_SecureRW(card, SD_ACMD43_GET_MKB, direction, arg, kbuffer, len); printk(KERN_DEBUG "SD_ACMD43_GET_MKB:0x%x\n", returnVal); break; case ACMD44: direction = MMC_DATA_READ; returnVal = CPRM_CMD_SecureRW(card, SD_ACMD44_GET_MID, direction, 0, kbuffer, len); printk(KERN_DEBUG "SD_ACMD44_GET_MID:0x%x\n", returnVal); break; case ACMD45: direction = MMC_DATA_WRITE; result = copy_from_user((void *)kbuffer, (void *)buff, len); returnVal = CPRM_CMD_SecureRW(card, SD_ACMD45_SET_CER_RN1, direction, 0, kbuffer, len); printk(KERN_DEBUG "SD_ACMD45_SET_CER_RN1:0x%x\n", returnVal); break; case ACMD46: direction = MMC_DATA_READ; returnVal = CPRM_CMD_SecureRW(card, SD_ACMD46_GET_CER_RN2, direction, 0, kbuffer, len); printk(KERN_DEBUG "SD_ACMD46_GET_CER_RN2:0x%x\n", returnVal); break; case ACMD47: direction = MMC_DATA_WRITE; result = copy_from_user((void *)kbuffer, (void *)buff, len); returnVal = CPRM_CMD_SecureRW(card, SD_ACMD47_SET_CER_RES2, direction, 0, kbuffer, len); printk(KERN_DEBUG "SD_ACMD47_SET_CER_RES2:0x%x\n", returnVal); break; case ACMD48: direction = MMC_DATA_READ; returnVal = CPRM_CMD_SecureRW(card, SD_ACMD48_GET_CER_RES1, direction, 0, kbuffer, len); printk(KERN_DEBUG "SD_ACMD48_GET_CER_RES1:0x%x\n", returnVal); break; case ACMD25: direction = MMC_DATA_WRITE; result = copy_from_user((void *)kbuffer, (void *)buff, len); returnVal = CPRM_CMD_SecureMultiRW(card, SD_ACMD25_SECURE_WRITE_MULTI_BLOCK, direction, 0, kbuffer, len); printk(KERN_DEBUG "SD_ACMD25_SECURE_WRITE_MULTI_BLOCK[%d]=%d\n", len, returnVal); break; case ACMD18: direction = MMC_DATA_READ; returnVal = CPRM_CMD_SecureMultiRW(card, SD_ACMD18_SECURE_READ_MULTI_BLOCK, direction, 0, kbuffer, len); printk(KERN_DEBUG "SD_ACMD18_SECURE_READ_MULTI_BLOCK [%d]=%d\n", len, returnVal); break; case ACMD13: break; default: printk(KERN_DEBUG " %s ] : CMD [ %x ] ERROR", __func__, cmd); break; } if (returnVal == 0) { if (direction == MMC_DATA_READ) result = copy_to_user((void *)buff, (void *)kbuffer, len); result = returnVal; printk(KERN_DEBUG "stub_sendcmd SDAS_E_SUCCESS\n"); } else { printk(KERN_DEBUG "stub_sendcmd SDAS_E_FAIL\n"); result = -EIO; } mmc_release_host(card->host); kfree(kbuffer); return result; }