From: Pierre Ossman <drzeus-list@drzeus.cx>

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>
---

 drivers/mmc/mmc.c        |   14 +++++++++++++-
 drivers/mmc/mmc_block.c  |    9 +++++++--
 include/linux/mmc/card.h |    3 +++
 include/linux/mmc/host.h |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

diff -puN drivers/mmc/mmc_block.c~sd-read-only-switch drivers/mmc/mmc_block.c
--- devel/drivers/mmc/mmc_block.c~sd-read-only-switch	2005-08-22 00:51:20.000000000 -0700
+++ devel-akpm/drivers/mmc/mmc_block.c	2005-08-22 00:51:20.000000000 -0700
@@ -95,6 +95,10 @@ static int mmc_blk_open(struct inode *in
 		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 @@ static int mmc_blk_probe(struct mmc_card
 	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);
diff -puN drivers/mmc/mmc.c~sd-read-only-switch drivers/mmc/mmc.c
--- devel/drivers/mmc/mmc.c~sd-read-only-switch	2005-08-22 00:51:20.000000000 -0700
+++ devel-akpm/drivers/mmc/mmc.c	2005-08-22 00:51:43.000000000 -0700
@@ -726,8 +726,20 @@ static void mmc_discover_cards(struct mm
 			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;
+
+				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;
diff -puN include/linux/mmc/card.h~sd-read-only-switch include/linux/mmc/card.h
--- devel/include/linux/mmc/card.h~sd-read-only-switch	2005-08-22 00:51:20.000000000 -0700
+++ devel-akpm/include/linux/mmc/card.h	2005-08-22 00:51:20.000000000 -0700
@@ -48,6 +48,7 @@ struct mmc_card {
 #define MMC_STATE_DEAD		(1<<1)		/* device no longer in stack */
 #define MMC_STATE_BAD		(1<<2)		/* unrecognised device */
 #define MMC_STATE_SDCARD	(1<<3)		/* is an SD card */
+#define MMC_STATE_READONLY	(1<<4)		/* card is read-only */
 	u32			raw_cid[4];	/* raw card CID */
 	u32			raw_csd[4];	/* raw card CSD */
 	struct mmc_cid		cid;		/* card identification */
@@ -58,11 +59,13 @@ struct mmc_card {
 #define mmc_card_dead(c)	((c)->state & MMC_STATE_DEAD)
 #define mmc_card_bad(c)		((c)->state & MMC_STATE_BAD)
 #define mmc_card_sd(c)		((c)->state & MMC_STATE_SDCARD)
+#define mmc_card_readonly(c)	((c)->state & MMC_STATE_READONLY)
 
 #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_dead(c)	((c)->state |= MMC_STATE_DEAD)
 #define mmc_card_set_bad(c)	((c)->state |= MMC_STATE_BAD)
 #define mmc_card_set_sd(c)	((c)->state |= MMC_STATE_SDCARD)
+#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
 
 #define mmc_card_name(c)	((c)->cid.prod_name)
 #define mmc_card_id(c)		((c)->dev.bus_id)
diff -puN include/linux/mmc/host.h~sd-read-only-switch include/linux/mmc/host.h
--- devel/include/linux/mmc/host.h~sd-read-only-switch	2005-08-22 00:51:20.000000000 -0700
+++ devel-akpm/include/linux/mmc/host.h	2005-08-22 00:51:20.000000000 -0700
@@ -56,6 +56,7 @@ struct mmc_ios {
 struct mmc_host_ops {
 	void	(*request)(struct mmc_host *host, struct mmc_request *req);
 	void	(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
+	int	(*get_ro)(struct mmc_host *host);
 };
 
 struct mmc_card;
_