Page MenuHomeFreeBSD

D12762.id34239.diff
No OneTemporary

D12762.id34239.diff

Index: sys/cam/mmc/mmc_da.c
===================================================================
--- sys/cam/mmc/mmc_da.c
+++ sys/cam/mmc/mmc_da.c
@@ -115,6 +115,13 @@
char card_sn_string[16];/* Formatted serial # for disk->d_ident */
/* Determined from CSD + is highspeed card*/
uint32_t card_f_max;
+
+ /* MMC partitions support */
+ uint32_t part_time; /* Partition switch timeout [us] */
+ off_t enh_base; /* Enhanced user data area slice base ... */
+ off_t enh_size; /* ... and size [bytes] */
+ int log_count;
+ struct timeval log_time;
};
#define ccb_bp ppriv_ptr1
@@ -136,6 +143,20 @@
static cam_status sdda_hook_into_geom(struct cam_periph *periph);
static void sdda_start_init(void *context, union ccb *start_ccb);
static void sdda_start_init_task(void *context, int pending);
+static void sdda_process_mmc_partitions(struct cam_periph *periph, union ccb *start_ccb);
+static uint32_t sdda_get_host_caps(struct cam_periph *periph, union ccb *ccb);
+
+static inline uint32_t mmc_get_sector_size(struct cam_periph *periph) {return MMC_SECTOR_SIZE;}
+
+/* TODO: actually issue GET_TRAN_SETTINGS to get R/O status */
+static inline bool sdda_get_read_only(struct cam_periph *periph, union ccb *start_ccb) {
+ return false;
+}
+
+static uint32_t mmc_get_spec_vers(struct cam_periph *periph);
+static uint64_t mmc_get_media_size(struct cam_periph *periph);
+static void sdda_add_part(struct cam_periph *periph, u_int type,
+ off_t media_size, bool ro);
static struct periph_driver sddadriver =
{
@@ -873,9 +894,6 @@
err = cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL);
if (err != 0)
return err;
- if (!(ccb->mmcio.cmd.resp[0] & R1_APP_CMD))
- return MMC_ERR_FAILED;
-
return MMC_ERR_NONE;
}
@@ -931,6 +949,18 @@
}
+static uint32_t
+mmc_get_spec_vers(struct cam_periph *periph) {
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+ return softc->csd.spec_vers;
+}
+
+static uint64_t
+mmc_get_media_size(struct cam_periph *periph) {
+ struct sdda_softc *softc = (struct sdda_softc *)periph->softc;
+ return softc->mediasize;
+}
+
static int
mmc_sd_switch(struct cam_periph *periph, union ccb *ccb,
uint8_t mode, uint8_t grp, uint8_t value,
@@ -1082,6 +1112,26 @@
xpt_action(ccb);
}
+static inline const char *part_type(u_int type) {
+ switch (type) {
+ case EXT_CSD_PART_CONFIG_ACC_RPMB:
+ return "RPMB";
+ case EXT_CSD_PART_CONFIG_ACC_DEFAULT:
+ return "default";
+ case EXT_CSD_PART_CONFIG_ACC_BOOT0:
+ return "boot0";
+ case EXT_CSD_PART_CONFIG_ACC_BOOT1:
+ return "boot1";
+ case EXT_CSD_PART_CONFIG_ACC_GP0:
+ case EXT_CSD_PART_CONFIG_ACC_GP1:
+ case EXT_CSD_PART_CONFIG_ACC_GP2:
+ case EXT_CSD_PART_CONFIG_ACC_GP3:
+ return "general purpose";
+ default:
+ return "(unknown type)";
+ }
+}
+
static inline const char *bus_width_str(enum mmc_bus_width w) {
switch (w) {
case bus_width_1:
@@ -1093,6 +1143,22 @@
}
}
+static uint32_t sdda_get_host_caps(struct cam_periph *periph, union ccb *ccb) {
+ struct ccb_trans_settings_mmc *cts;
+ cts = &ccb->cts.proto_specific.mmc;
+
+ ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+ ccb->ccb_h.flags = CAM_DIR_NONE;
+ ccb->ccb_h.retry_count = 0;
+ ccb->ccb_h.timeout = 100;
+ ccb->ccb_h.cbfcnp = NULL;
+ xpt_action(ccb);
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP)
+ panic("Cannot get host caps");
+ return cts->host_caps;
+}
+
static void
sdda_start_init(void *context, union ccb *start_ccb) {
struct cam_periph *periph;
@@ -1114,10 +1180,16 @@
if (mmcp->card_features & CARD_FEATURE_MMC) {
mmc_decode_csd_mmc(mmcp->card_csd, &softc->csd);
mmc_decode_cid_mmc(mmcp->card_cid, &softc->cid);
- if (softc->csd.spec_vers >= 4)
+ if (mmc_get_spec_vers(periph) >= 4) {
err = mmc_send_ext_csd(periph, start_ccb,
(uint8_t *)&softc->raw_ext_csd,
sizeof(softc->raw_ext_csd));
+ if (err != 0) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Cannot read EXT_CSD, err %d", err));
+ return;
+ }
+ }
} else {
mmc_decode_csd_sd(mmcp->card_csd, &softc->csd);
mmc_decode_cid_sd(mmcp->card_cid, &softc->cid);
@@ -1127,7 +1199,7 @@
softc->mediasize = softc->csd.capacity;
/* MMC >= 4.x have EXT_CSD that has its own opinion about capacity */
- if (softc->csd.spec_vers >= 4) {
+ if (mmc_get_spec_vers(periph) >= 4) {
uint32_t sec_count = softc->raw_ext_csd[EXT_CSD_SEC_CNT] +
(softc->raw_ext_csd[EXT_CSD_SEC_CNT + 1] << 8) +
(softc->raw_ext_csd[EXT_CSD_SEC_CNT + 2] << 16) +
@@ -1208,7 +1280,7 @@
}
}
- if (mmcp->card_features & CARD_FEATURE_MMC && softc->csd.spec_vers >= 4) {
+ if (mmcp->card_features & CARD_FEATURE_MMC && mmc_get_spec_vers(periph) >= 4) {
if (softc->raw_ext_csd[EXT_CSD_CARD_TYPE]
& EXT_CSD_CARD_TYPE_HS_52)
softc->card_f_max = MMC_TYPE_HS_52_MAX;
@@ -1261,8 +1333,156 @@
if (err != MMC_ERR_NONE)
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("Cannot switch card to high-speed mode"));
}
+
softc->state = SDDA_STATE_NORMAL;
sdda_hook_into_geom(periph);
+
+ /* MMC partitions support */
+ if (mmcp->card_features & CARD_FEATURE_MMC && mmc_get_spec_vers(periph) >= 4) {
+ sdda_process_mmc_partitions(periph, start_ccb);
+ }
+}
+
+/* TODO: actually add partitions to the device tree and GEOM */
+static void
+sdda_add_part(struct cam_periph *periph, u_int type,
+ off_t media_size, bool ro) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("Partition type '%s', size %ju %s\n",
+ part_type(type),
+ media_size,
+ ro ? "(read-only)" : ""));
+
+}
+
+static void
+sdda_process_mmc_partitions(struct cam_periph *periph, union ccb *ccb) {
+ struct sdda_softc *sc = (struct sdda_softc *)periph->softc;
+ struct mmc_params *mmcp = &periph->path->device->mmc_ident_data;
+
+ const uint8_t *ext_csd;
+ off_t erase_size, sector_size, size, wp_size;
+ __unused uintmax_t bytes;
+ int i;
+ __unused uint32_t quirks;
+ uint8_t rev;
+ bool comp, ro;
+
+ ext_csd = sc->raw_ext_csd;
+
+ /*
+ * Enhanced user data area and general purpose partitions are only
+ * supported in revision 1.4 (EXT_CSD_REV == 4) and later, the RPMB
+ * partition in revision 1.5 (MMC v4.41, EXT_CSD_REV == 5) and later.
+ */
+ rev = ext_csd[EXT_CSD_REV];
+
+ /*
+ * Ignore user-creatable enhanced user data area and general purpose
+ * partitions partitions as long as partitioning hasn't been finished.
+ */
+ comp = (ext_csd[EXT_CSD_PART_SET] & EXT_CSD_PART_SET_COMPLETED) != 0;
+
+ /*
+ * Add enhanced user data area slice, unless it spans the entirety of
+ * the user data area. The enhanced area is of a multiple of high
+ * capacity write protect groups ((ERASE_GRP_SIZE + HC_WP_GRP_SIZE) *
+ * 512 KB) and its offset given in either sectors or bytes, depending
+ * on whether it's a high capacity device or not.
+ * NB: The slicer and its slices need to be registered before adding
+ * the disk for the corresponding user data area as re-tasting is
+ * racy.
+ */
+ sector_size = mmc_get_sector_size(periph);
+ size = ext_csd[EXT_CSD_ENH_SIZE_MULT] +
+ (ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) +
+ (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16);
+ if (rev >= 4 && comp == TRUE && size > 0 &&
+ (ext_csd[EXT_CSD_PART_SUPPORT] &
+ EXT_CSD_PART_SUPPORT_ENH_ATTR_EN) != 0 &&
+ (ext_csd[EXT_CSD_PART_ATTR] & (EXT_CSD_PART_ATTR_ENH_USR)) != 0) {
+ erase_size = ext_csd[EXT_CSD_ERASE_GRP_SIZE] * 1024 *
+ MMC_SECTOR_SIZE;
+ wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ size *= erase_size * wp_size;
+ if (size != mmc_get_media_size(periph) * sector_size) {
+ sc->enh_size = size;
+ sc->enh_base = (ext_csd[EXT_CSD_ENH_START_ADDR] +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24)) *
+ ((mmcp->card_features & CARD_FEATURE_SDHC) ? 1: MMC_SECTOR_SIZE);
+ } else
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("enhanced user data area spans entire device"));
+ }
+
+ /*
+ * Add default partition. This may be the only one or the user
+ * data area in case partitions are supported.
+ */
+ ro = sdda_get_read_only(periph, ccb);
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_DEFAULT,
+ mmc_get_media_size(periph) * sector_size, ro);
+
+ if (mmc_get_spec_vers(periph) < 3)
+ return;
+
+ /* Belatedly announce enhanced user data slice. */
+ if (sc->enh_size != 0) {
+ CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH,
+ ("enhanced user data area off 0x%jx size %ju bytes\n",
+ sc->enh_base, sc->enh_size));
+ }
+
+
+ /*
+ * Determine partition switch timeout (provided in units of 10 ms)
+ * and ensure it's at least 300 ms as some eMMC chips lie.
+ */
+ sc->part_time = max(ext_csd[EXT_CSD_PART_SWITCH_TO] * 10 * 1000,
+ 300 * 1000);
+
+ /* Add boot partitions, which are of a fixed multiple of 128 KB. */
+ size = ext_csd[EXT_CSD_BOOT_SIZE_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE;
+ if (size > 0 && (sdda_get_host_caps(periph, ccb) & MMC_CAP_BOOT_NOACC) == 0) {
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_BOOT0,
+ size,
+ ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] &
+ EXT_CSD_BOOT_WP_STATUS_BOOT0_MASK) != 0));
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_BOOT1,
+ size,
+ ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] &
+ EXT_CSD_BOOT_WP_STATUS_BOOT1_MASK) != 0));
+ }
+
+ /* Add RPMB partition, which also is of a fixed multiple of 128 KB. */
+ size = ext_csd[EXT_CSD_RPMB_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE;
+ if (rev >= 5 && size > 0)
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_RPMB,
+ size, ro);
+
+ if (rev <= 3 || comp == FALSE)
+ return;
+
+ /*
+ * Add general purpose partitions, which are of a multiple of high
+ * capacity write protect groups, too.
+ */
+ if ((ext_csd[EXT_CSD_PART_SUPPORT] & EXT_CSD_PART_SUPPORT_EN) != 0) {
+ erase_size = ext_csd[EXT_CSD_ERASE_GRP_SIZE] * 1024 *
+ MMC_SECTOR_SIZE;
+ wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ for (i = 0; i < MMC_PART_GP_MAX; i++) {
+ size = ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3] +
+ (ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3 + 1] << 8) +
+ (ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3 + 2] << 16);
+ if (size == 0)
+ continue;
+ sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_GP0 + i,
+ size * erase_size * wp_size, ro);
+ }
+ }
}
/* Called with periph lock held! */

File Metadata

Mime Type
text/plain
Expires
Thu, Apr 23, 8:23 AM (7 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32019606
Default Alt Text
D12762.id34239.diff (10 KB)

Event Timeline