Add MMC password protection (lock/unlock) support V3
PATCH 2/5 Add MMC password protection (lock/unlock) support V3
Implement card lock/unlock card operation, using the MMC_LOCK_UNLOCK command.
Signed-off-by Anderson Briglia <[email protected]>
Signed-off-by Anderson Lizardo <[email protected]>
Signed-off-by Carlos Eduardo Aguiar <[email protected]>
drivers/mmc/mmc.c +131 -0
@@ -4,6 +4,8 @@* Copyright (C) 2003-2004 Russell King, All Rights Reserved. * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. * SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved. +* MMC password protection (C) 2005 Instituto Nokia de Tecnologia (INdT), +* All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as
@@ -20,6 +22,7 @@#include <linux/err.h> #include <asm/scatterlist.h> #include <linux/scatterlist.h> +#include <linux/key.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h>
@@ -1088,6 +1091,134 @@mmc_read_scrs (host); } +/* Calculate the minimal blksz_bits to hold x bytes. */ +static inline int blksz_bits (unsigned x) +{ +return fls (x-1 ); +} + +/** +* mmc_lock_unlock - send LOCK_UNLOCK command to a specific card. +* @card: card to which the LOCK_UNLOCK command should be sent +* @key: key containing the MMC password +* @mode: LOCK_UNLOCK mode +* +*/ +int mmc_lock_unlock (struct mmc_card *card,struct key *key,int mode) +{ +struct mmc_request mrq; +struct mmc_command cmd; +struct mmc_data data; +struct scatterlist sg; +struct mmc_key_payload *mpayload; +unsigned long erase_timeout; +int err, data_size; +u8 *data_buf; + + mpayload =NULL ; + data_size =1 ; +if (mode !=MMC_LOCK_MODE_ERASE ) { + mpayload =rcu_dereference (key->payload.data); + data_size =2 + mpayload->datalen; + } + + data_buf =kmalloc (data_size,GFP_KERNEL ); +if (!data_buf) +return -ENOMEM ; +memset (data_buf,0 , data_size); + + data_buf[0 ] = mode; +if (mode !=MMC_LOCK_MODE_ERASE ) { + data_buf[1 ] = mpayload->datalen; +memcpy (data_buf +2 , mpayload->data, mpayload->datalen); + } + + err =mmc_card_claim_host (card); +if (err !=MMC_ERR_NONE ) { +mmc_card_set_dead (card); + goto out; + } + +memset (&cmd,0 ,sizeof (struct mmc_command )); + + cmd.opcode =MMC_SET_BLOCKLEN ; + cmd.arg = data_size; + cmd.flags =MMC_RSP_R1 ; + err =mmc_wait_for_cmd (card->host, &cmd,CMD_RETRIES ); +if (err !=MMC_ERR_NONE ) { +mmc_card_set_dead (card); + goto error; + } + +memset (&cmd,0 ,sizeof (struct mmc_command )); + + cmd.opcode =MMC_LOCK_UNLOCK ; + cmd.arg =0 ; + cmd.flags =MMC_RSP_R1B ; + +memset (&data,0 ,sizeof (struct mmc_data )); + + data.timeout_ns = card->csd.tacc_ns *10 ; + data.timeout_clks = card->csd.tacc_clks *10 ; + data.blksz_bits =blksz_bits (data_size); + data.blocks =1 ; + data.flags =MMC_DATA_WRITE ; + data.sg = &sg; + data.sg_len =1 ; + +memset (&mrq,0 ,sizeof (struct mmc_request )); + + mrq.cmd = &cmd; + mrq.data = &data; + +sg_init_one (&sg, data_buf, data_size); + err =mmc_wait_for_req (card->host, &mrq); +if (err !=MMC_ERR_NONE ) { +mmc_card_set_dead (card); + goto error; + } + +memset (&cmd,0 ,sizeof (struct mmc_command )); + + cmd.opcode =MMC_SEND_STATUS ; + cmd.arg = card->rca <<16 ; + cmd.flags =MMC_RSP_R1 ; + +/* set timeout for forced erase operation to 3 min. (see MMC spec) */ + erase_timeout = jiffies +180 *HZ ; +do { +/* we cannot use "retries" here because the +* R1_LOCK_UNLOCK_FAILED bit is cleared by subsequent reads to +* the status register, hiding the error condition */ + err =mmc_wait_for_cmd (card->host, &cmd,0 ); +if (err !=MMC_ERR_NONE ) +break ; +/* the other modes don't need timeout checking */ +if (mode !=MMC_LOCK_MODE_ERASE ) +continue ; +if (time_after (jiffies, erase_timeout)) { +dev_dbg (&card->dev,"forced erase timed out\n" ); + err =MMC_ERR_TIMEOUT ; +break ; + } + }while (!(cmd.resp[0 ] &R1_READY_FOR_DATA )); +if (cmd.resp[0 ] &R1_LOCK_UNLOCK_FAILED ) { +dev_dbg (&card->dev,"LOCK_UNLOCK operation failed\n" ); + err =MMC_ERR_FAILED ; + } + +error: +mmc_check_cards (card->host); +mmc_deselect_cards (card->host); +mmc_card_release_host (card); +out: +kfree (data_buf); + +return err; +} + +EXPORT_SYMBOL (mmc_lock_unlock); +/** * mmc_detect_change - process change of state on a MMC socket
include/linux/mmc/card.h +5 -1
@@ -72,7 +72,7 @@#define mmc_card_readonly (c) ((c)->state & MMC_STATE_READONLY)#define mmc_card_locked (c) ((c)->state & MMC_STATE_LOCKED) -#define mmc_card_lockable (c) ((c)->csd.cmdclass & CCC_LOCK_CARD) +#define mmc_card_lockable (c) (((c)->csd.cmdclass & CCC_LOCK_CARD) && (c)->host->pwd_support)#define mmc_card_set_present (c) ((c)->state |= MMC_STATE_PRESENT)#define mmc_card_set_dead (c) ((c)->state |= MMC_STATE_DEAD)
@@ -109,4 +109,8 @@#define mmc_card_release_host (c) mmc_release_host((c)->host) +struct key ; + +extern int mmc_lock_unlock (struct mmc_card *card,struct key *key,int mode); +#endif
include/linux/mmc/protocol.h +8 -0
@@ -243,5 +243,13 @@#define SD_BUS_WIDTH_1 0#define SD_BUS_WIDTH_4 2 +/* +* MMC_LOCK_UNLOCK modes +*/ +#define MMC_LOCK_MODE_ERASE (1<<3) +#define MMC_LOCK_MODE_UNLOCK (0<<2) +#define MMC_LOCK_MODE_CLR_PWD (1<<1) +#define MMC_LOCK_MODE_SET_PWD (1<<0) +#endif /* MMC_MMC_PROTOCOL_H */
include/linux/mmc/host.h +1 -0
@@ -83,6 +83,7 @@u32 ocr_avail;unsigned long caps;/* Host capabilities */ +int pwd_support;/* MMC password support */ #define MMC_CAP_4_BIT_DATA (1 << 0)/* Can the host do 4 bit transfers */
Quoted 15 lines · Anderson Briglia
drivers/mmc/mmc.c +15
-0+ cmd.opcode =MMC_LOCK_UNLOCK ; + cmd.arg =0 ; + cmd.flags =MMC_RSP_R1B ; + +memset (&data,0 ,sizeof (struct mmc_data )); + + data.timeout_ns = card->csd.tacc_ns *10 ; + data.timeout_clks = card->csd.tacc_clks *10 ; + data.blksz_bits =blksz_bits (data_size); + data.blocks =1 ; + data.flags =MMC_DATA_WRITE ; + data.sg = &sg; + data.sg_len =1 ; + +memset (&mrq,0 ,sizeof (struct mmc_request ));
drivers/mmc/mmc.c line 1179
Quoted 10 lines
drivers/mmc/mmc.c +10
-0+ + mrq.cmd = &cmd; + mrq.data = &data; + +sg_init_one (&sg, data_buf, data_size); + err =mmc_wait_for_req (card->host, &mrq); +if (err !=MMC_ERR_NONE ) { +mmc_card_set_dead (card); + goto error; + }
If the command error is MMC_ERR_INVALID, this means the host doesn't support this operation, so we shouldn't set the card "dead".
Quoted 1 line
+
memset(&cmd, 0, sizeof(struct mmc_command));
The ugly formatting monster makes an appearance. Tabs please.
include/linux/mmc/host.h line 89
Quoted 12 lines
Index:
linux-2.6.15-rc4/include/linux/mmc/host.h
===================================================================include/linux/mmc/host.h +1
-0@@ -83,6 +83,7 @@u32 ocr_avail;unsigned long caps;/* Host capabilities */ +int pwd_support;/* MMC password support */ #define MMC_CAP_4_BIT_DATA (1 << 0)/* Can the host do 4 bit transfers */
And this means we don't need the "pwd_support" thing here. Even if we did, it should be a host capability, not an "int".
Signature · 3 lines
Russell King Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/ maintainer of: 2.6 Serial core