[PATCH] sd: read-only switch

Support for the read-only switch on SD cards which must be enforced by the
host.

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 294961a..725c6ad 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -413,8 +413,7 @@
 		card->cid.month			= UNSTUFF_BITS(resp, 8, 4);
 
 		card->cid.year += 2000; /* SD cards year offset */
-	}
-	else {
+	} else {
 		/*
 		 * The selection of the format here is based upon published
 		 * specs from sandisk and from what people have reported.
@@ -494,8 +493,7 @@
 		csd->capacity	  = (1 + m) << (e + 2);
 
 		csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
-	}
-	else {
+	} else {
 		/*
 		 * We only understand CSD structure v1.1 and v1.2.
 		 * v1.2 has extra information in bits 15, 11 and 10.
@@ -738,10 +736,20 @@
 			err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
 			if (err != MMC_ERR_NONE)
 				mmc_card_set_dead(card);
-			else
+			else {
 				card->rca = cmd.resp[0] >> 16;
-		}
-		else {
+
+				if (!host->ops->get_ro) {
+					printk(KERN_WARNING "%s: host does not "
+						"support reading read-only "
+						"switch. assuming write-enable.\n",
+						mmc_hostname(host));
+				} else {
+					if (host->ops->get_ro(host))
+						mmc_card_set_readonly(card);
+				}
+			}
+		} else {
 			cmd.opcode = MMC_SET_RELATIVE_ADDR;
 			cmd.arg = card->rca << 16;
 			cmd.flags = MMC_RSP_R1;
@@ -833,24 +841,23 @@
 		int err;
 		u32 ocr;
 
-		host->mode = MMC_MODE_MMC;
+		host->mode = MMC_MODE_SD;
 
 		mmc_power_up(host);
 		mmc_idle_cards(host);
 
-		err = mmc_send_op_cond(host, 0, &ocr);
+		err = mmc_send_app_op_cond(host, 0, &ocr);
 
 		/*
-		 * If we fail to detect any cards then try
-		 * searching for SD cards.
+		 * If we fail to detect any SD cards then try
+		 * searching for MMC cards.
 		 */
-		if (err != MMC_ERR_NONE)
-		{
-			err = mmc_send_app_op_cond(host, 0, &ocr);
+		if (err != MMC_ERR_NONE) {
+			host->mode = MMC_MODE_MMC;
+
+			err = mmc_send_op_cond(host, 0, &ocr);
 			if (err != MMC_ERR_NONE)
 				return;
-
-			host->mode = MMC_MODE_SD;
 		}
 
 		host->ocr = mmc_select_voltage(host, ocr);
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index d4eee99..fa83f15 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -95,6 +95,10 @@
 		if (md->usage == 2)
 			check_disk_change(inode->i_bdev);
 		ret = 0;
+
+		if ((filp->f_mode & FMODE_WRITE) &&
+			mmc_card_readonly(md->queue.card))
+			ret = -EROFS;
 	}
 
 	return ret;
@@ -403,9 +407,10 @@
 	if (err)
 		goto out;
 
-	printk(KERN_INFO "%s: %s %s %dKiB\n",
+	printk(KERN_INFO "%s: %s %s %dKiB %s\n",
 		md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
-		(card->csd.capacity << card->csd.read_blkbits) / 1024);
+		(card->csd.capacity << card->csd.read_blkbits) / 1024,
+		mmc_card_readonly(card)?"(ro)":"");
 
 	mmc_set_drvdata(card, md);
 	add_disk(md->disk);