Changeset View
Standalone View
sys/cam/mmc/mmc_da.c
Context not available. | |||||
} sdda_flags; | } sdda_flags; | ||||
typedef enum { | typedef enum { | ||||
SDDA_STATE_INIT, | SDDA_STATE_INIT, | ||||
SDDA_STATE_INVALID, | SDDA_STATE_INVALID, | ||||
SDDA_STATE_NORMAL | SDDA_STATE_NORMAL, | ||||
SDDA_STATE_PART_SWITCH, | |||||
} sdda_state; | } sdda_state; | ||||
#define SDDA_FMT_BOOT "sdda%dboot" | |||||
#define SDDA_FMT_GP "sdda%dgp" | |||||
#define SDDA_FMT_RPMB "sdda%drpmb" | |||||
#define SDDA_LABEL_ENH "enh" | |||||
#define SDDA_PART_NAMELEN (16 + 1) | |||||
struct sdda_softc; | |||||
struct sdda_part { | |||||
struct disk *disk; | |||||
struct bio_queue_head bio_queue; | |||||
int refcount; /* Active xpt_action() calls */ | |||||
sdda_flags flags; | |||||
struct sdda_softc *sc; | |||||
u_int cnt; | |||||
u_int type; | |||||
bool ro; | |||||
char name[SDDA_PART_NAMELEN]; | |||||
}; | |||||
struct sdda_softc { | struct sdda_softc { | ||||
struct bio_queue_head bio_queue; | |||||
int outstanding_cmds; /* Number of active commands */ | int outstanding_cmds; /* Number of active commands */ | ||||
int refcount; /* Active xpt_action() calls */ | |||||
sdda_state state; | sdda_state state; | ||||
sdda_flags flags; | |||||
struct mmc_data *mmcdata; | struct mmc_data *mmcdata; | ||||
struct cam_periph *periph; | |||||
// sdda_quirks quirks; | // sdda_quirks quirks; | ||||
struct task start_init_task; | struct task start_init_task; | ||||
struct disk *disk; | uint32_t raw_csd[4]; | ||||
uint32_t raw_csd[4]; | |||||
uint8_t raw_ext_csd[512]; /* MMC only? */ | uint8_t raw_ext_csd[512]; /* MMC only? */ | ||||
struct mmc_csd csd; | struct mmc_csd csd; | ||||
struct mmc_cid cid; | struct mmc_cid cid; | ||||
struct mmc_scr scr; | struct mmc_scr scr; | ||||
/* Calculated from CSD */ | /* Calculated from CSD */ | ||||
uint64_t sector_count; | uint64_t sector_count; | ||||
uint64_t mediasize; | uint64_t mediasize; | ||||
/* Calculated from CID */ | /* Calculated from CID */ | ||||
char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */ | char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */ | ||||
char card_sn_string[16];/* Formatted serial # for disk->d_ident */ | char card_sn_string[16];/* Formatted serial # for disk->d_ident */ | ||||
/* Determined from CSD + is highspeed card*/ | /* Determined from CSD + is highspeed card*/ | ||||
uint32_t card_f_max; | uint32_t card_f_max; | ||||
/* Generic switch timeout */ | |||||
uint32_t cmd6_time; | |||||
/* MMC partitions support */ | |||||
struct sdda_part *part[MMC_PART_MAX]; | |||||
uint8_t part_curr; /* Partition currently switched to */ | |||||
uint8_t part_requested; /* What partition we're currently switching to */ | |||||
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; | |||||
}; | }; | ||||
marius: style(9): "When declaring variables in structures, declare them sorted by use, then by size… | |||||
#define ccb_bp ppriv_ptr1 | #define ccb_bp ppriv_ptr1 | ||||
Context not available. | |||||
u_int32_t sense_flags); | u_int32_t sense_flags); | ||||
static uint16_t get_rca(struct cam_periph *periph); | static uint16_t get_rca(struct cam_periph *periph); | ||||
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(void *context, union ccb *start_ccb); | ||||
static void sdda_start_init_task(void *context, int pending); | 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 void sdda_init_switch_part(struct cam_periph *periph, union ccb *start_ccb, u_int part); | |||||
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) | |||||
{ | |||||
Done Inline Actionsstyle(9):
marius: style(9):
- "The function type should be on a line by itself preceding the function. The… | |||||
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 uint32_t mmc_get_cmd6_timeout(struct cam_periph *periph); | |||||
static void sdda_add_part(struct cam_periph *periph, u_int type, | |||||
const char *name, u_int cnt, off_t media_size, bool ro); | |||||
static struct periph_driver sddadriver = | static struct periph_driver sddadriver = | ||||
{ | { | ||||
Context not available. | |||||
static int | static int | ||||
sddaopen(struct disk *dp) | sddaopen(struct disk *dp) | ||||
{ | { | ||||
struct sdda_part *part; | |||||
struct cam_periph *periph; | struct cam_periph *periph; | ||||
struct sdda_softc *softc; | struct sdda_softc *softc; | ||||
int error; | int error; | ||||
periph = (struct cam_periph *)dp->d_drv1; | part = (struct sdda_part *)dp->d_drv1; | ||||
softc = part->sc; | |||||
Done Inline Actionsstyle(9): "Space after keywords (if, while, for, return, switch)." marius: style(9): "Space after keywords (if, while, for, return, switch)." | |||||
periph = softc->periph; | |||||
if (cam_periph_acquire(periph) != CAM_REQ_CMP) { | if (cam_periph_acquire(periph) != CAM_REQ_CMP) { | ||||
return(ENXIO); | return (ENXIO); | ||||
} | } | ||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
Context not available. | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddaopen\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddaopen\n")); | ||||
softc = (struct sdda_softc *)periph->softc; | part->flags |= SDDA_FLAG_OPEN; | ||||
softc->flags |= SDDA_FLAG_OPEN; | |||||
cam_periph_unhold(periph); | cam_periph_unhold(periph); | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
Context not available. | |||||
static int | static int | ||||
sddaclose(struct disk *dp) | sddaclose(struct disk *dp) | ||||
{ | { | ||||
struct sdda_part *part; | |||||
struct cam_periph *periph; | struct cam_periph *periph; | ||||
struct sdda_softc *softc; | struct sdda_softc *softc; | ||||
// union ccb *ccb; | |||||
// int error; | |||||
periph = (struct cam_periph *)dp->d_drv1; | part = (struct sdda_part *)dp->d_drv1; | ||||
softc = (struct sdda_softc *)periph->softc; | softc = part->sc; | ||||
softc->flags &= ~SDDA_FLAG_OPEN; | periph = softc->periph; | ||||
part->flags &= ~SDDA_FLAG_OPEN; | |||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddaclose\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddaclose\n")); | ||||
while (softc->refcount != 0) | while (part->refcount != 0) | ||||
cam_periph_sleep(periph, &softc->refcount, PRIBIO, "sddaclose", 1); | cam_periph_sleep(periph, &part->refcount, PRIBIO, "sddaclose", 1); | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
cam_periph_release(periph); | cam_periph_release(periph); | ||||
return (0); | return (0); | ||||
Context not available. | |||||
sddaschedule(struct cam_periph *periph) | sddaschedule(struct cam_periph *periph) | ||||
{ | { | ||||
struct sdda_softc *softc = (struct sdda_softc *)periph->softc; | struct sdda_softc *softc = (struct sdda_softc *)periph->softc; | ||||
struct sdda_part *part; | |||||
struct bio *bp; | |||||
int i; | |||||
/* Check if we have more work to do. */ | /* Check if we have more work to do. */ | ||||
if (bioq_first(&softc->bio_queue)) { | /* Find partition that has outstanding commands. Prefer current partition. */ | ||||
bp = bioq_first(&softc->part[softc->part_curr]->bio_queue); | |||||
if (bp == NULL) { | |||||
for (i = 0; i < MMC_PART_MAX; i++) { | |||||
if ((part = softc->part[i]) != NULL && | |||||
(bp = bioq_first(&softc->part[i]->bio_queue)) != NULL) | |||||
break; | |||||
} | |||||
} | |||||
if (bp != NULL) { | |||||
xpt_schedule(periph, CAM_PRIORITY_NORMAL); | xpt_schedule(periph, CAM_PRIORITY_NORMAL); | ||||
} | } | ||||
} | } | ||||
Context not available. | |||||
sddastrategy(struct bio *bp) | sddastrategy(struct bio *bp) | ||||
{ | { | ||||
struct cam_periph *periph; | struct cam_periph *periph; | ||||
struct sdda_part *part; | |||||
struct sdda_softc *softc; | struct sdda_softc *softc; | ||||
periph = (struct cam_periph *)bp->bio_disk->d_drv1; | part = (struct sdda_part *)bp->bio_disk->d_drv1; | ||||
softc = (struct sdda_softc *)periph->softc; | softc = part->sc; | ||||
periph = softc->periph; | |||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
Context not available. | |||||
/* | /* | ||||
* Place it in the queue of disk activities for this disk | * Place it in the queue of disk activities for this disk | ||||
*/ | */ | ||||
bioq_disksort(&softc->bio_queue, bp); | bioq_disksort(&part->bio_queue, bp); | ||||
impUnsubmitted Not Done Inline ActionsDo you have on your list to move to the cam I/O scheduler? imp: Do you have on your list to move to the cam I/O scheduler? | |||||
kibabAuthorUnsubmitted Not Done Inline ActionsYes, see https://github.com/kibab/freebsd/issues/6 kibab: Yes, see https://github.com/kibab/freebsd/issues/6
Although this has lower priority than… | |||||
/* | /* | ||||
* Schedule ourselves for performing the work. | * Schedule ourselves for performing the work. | ||||
Context not available. | |||||
sddadiskgonecb(struct disk *dp) | sddadiskgonecb(struct disk *dp) | ||||
{ | { | ||||
struct cam_periph *periph; | struct cam_periph *periph; | ||||
struct sdda_part *part; | |||||
periph = (struct cam_periph *)dp->d_drv1; | part = (struct sdda_part *)dp->d_drv1; | ||||
periph = part->sc->periph; | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddadiskgonecb\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddadiskgonecb\n")); | ||||
cam_periph_release(periph); | cam_periph_release(periph); | ||||
Context not available. | |||||
sddaoninvalidate(struct cam_periph *periph) | sddaoninvalidate(struct cam_periph *periph) | ||||
{ | { | ||||
struct sdda_softc *softc; | struct sdda_softc *softc; | ||||
struct sdda_part *part; | |||||
softc = (struct sdda_softc *)periph->softc; | softc = (struct sdda_softc *)periph->softc; | ||||
Context not available. | |||||
* with XPT_ABORT_CCB. | * with XPT_ABORT_CCB. | ||||
*/ | */ | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("bioq_flush start\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("bioq_flush start\n")); | ||||
bioq_flush(&softc->bio_queue, NULL, ENXIO); | for (int i = 0; i < MMC_PART_MAX; i++) { | ||||
if ((part = softc->part[i]) != NULL) { | |||||
bioq_flush(&part->bio_queue, NULL, ENXIO); | |||||
disk_gone(part->disk); | |||||
} | |||||
} | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("bioq_flush end\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("bioq_flush end\n")); | ||||
disk_gone(softc->disk); | |||||
} | } | ||||
static void | static void | ||||
sddacleanup(struct cam_periph *periph) | sddacleanup(struct cam_periph *periph) | ||||
{ | { | ||||
struct sdda_softc *softc; | struct sdda_softc *softc; | ||||
struct sdda_part *part; | |||||
int i; | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddacleanup\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddacleanup\n")); | ||||
softc = (struct sdda_softc *)periph->softc; | softc = (struct sdda_softc *)periph->softc; | ||||
Done Inline ActionsAccording to style(9), variable declarations within functions are accumulated at the beginning of that function, i. e. no C99/11-style variable declaration. marius: According to style(9), variable declarations within functions are accumulated at the beginning… | |||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
disk_destroy(softc->disk); | for (i = 0; i < MMC_PART_MAX; i++) { | ||||
if ((part = softc->part[i]) != NULL) { | |||||
disk_destroy(part->disk); | |||||
free(part, M_DEVBUF); | |||||
softc->part[i] = NULL; | |||||
} | |||||
} | |||||
free(softc, M_DEVBUF); | free(softc, M_DEVBUF); | ||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
} | } | ||||
Context not available. | |||||
case AC_ADVINFO_CHANGED: | case AC_ADVINFO_CHANGED: | ||||
{ | { | ||||
uintptr_t buftype; | uintptr_t buftype; | ||||
int i; | |||||
CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> AC_ADVINFO_CHANGED\n")); | CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> AC_ADVINFO_CHANGED\n")); | ||||
buftype = (uintptr_t)arg; | buftype = (uintptr_t)arg; | ||||
Done Inline ActionsDitto. marius: Ditto. | |||||
if (buftype == CDAI_TYPE_PHYS_PATH) { | if (buftype == CDAI_TYPE_PHYS_PATH) { | ||||
struct sdda_softc *softc; | struct sdda_softc *softc; | ||||
struct sdda_part *part; | |||||
softc = periph->softc; | softc = periph->softc; | ||||
disk_attr_changed(softc->disk, "GEOM::physpath", | for (i = 0; i < MMC_PART_MAX; i++) { | ||||
M_NOWAIT); | if ((part = softc->part[i]) != NULL) { | ||||
disk_attr_changed(part->disk, "GEOM::physpath", | |||||
M_NOWAIT); | |||||
} | |||||
} | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
Context not available. | |||||
static int | static int | ||||
Done Inline ActionsUsing a tab here instead of a single space isn't appropriate, also according to style(9): "When declaring variables in functions declare them sorted by size, then in alphabetical order; multiple ones per line are okay." marius: Using a tab here instead of a single space isn't appropriate, also according to style(9): "When… | |||||
sddagetattr(struct bio *bp) | sddagetattr(struct bio *bp) | ||||
{ | { | ||||
int ret; | |||||
struct cam_periph *periph; | struct cam_periph *periph; | ||||
struct sdda_softc *softc; | |||||
struct sdda_part *part; | |||||
int ret; | |||||
periph = (struct cam_periph *)bp->bio_disk->d_drv1; | part = (struct sdda_part *)bp->bio_disk->d_drv1; | ||||
softc = part->sc; | |||||
periph = softc->periph; | |||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute, | ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute, | ||||
periph->path); | periph->path); | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
if (ret == 0) | if (ret == 0) | ||||
bp->bio_completed = bp->bio_length; | bp->bio_completed = bp->bio_length; | ||||
return ret; | return (ret); | ||||
} | } | ||||
static cam_status | static cam_status | ||||
sddaregister(struct cam_periph *periph, void *arg) | sddaregister(struct cam_periph *periph, void *arg) | ||||
{ | { | ||||
struct sdda_softc *softc; | struct sdda_softc *softc; | ||||
// struct ccb_pathinq cpi; | |||||
struct ccb_getdev *cgd; | struct ccb_getdev *cgd; | ||||
// char announce_buf[80], buf1[32]; | |||||
// caddr_t match; | |||||
union ccb *request_ccb; /* CCB representing the probe request */ | union ccb *request_ccb; /* CCB representing the probe request */ | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddaregister\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddaregister\n")); | ||||
cgd = (struct ccb_getdev *)arg; | cgd = (struct ccb_getdev *)arg; | ||||
if (cgd == NULL) { | if (cgd == NULL) { | ||||
printf("sddaregister: no getdev CCB, can't register device\n"); | printf("sddaregister: no getdev CCB, can't register device\n"); | ||||
return(CAM_REQ_CMP_ERR); | return (CAM_REQ_CMP_ERR); | ||||
} | } | ||||
softc = (struct sdda_softc *)malloc(sizeof(*softc), M_DEVBUF, | softc = (struct sdda_softc *)malloc(sizeof(*softc), M_DEVBUF, | ||||
Context not available. | |||||
if (softc == NULL) { | if (softc == NULL) { | ||||
printf("sddaregister: Unable to probe new device. " | printf("sddaregister: Unable to probe new device. " | ||||
"Unable to allocate softc\n"); | "Unable to allocate softc\n"); | ||||
return(CAM_REQ_CMP_ERR); | return (CAM_REQ_CMP_ERR); | ||||
} | } | ||||
Done Inline Actionsstyle(9):
marius: style(9):
- "Routines returning void * should not have their return values cast to any pointer… | |||||
bioq_init(&softc->bio_queue); | |||||
softc->state = SDDA_STATE_INIT; | softc->state = SDDA_STATE_INIT; | ||||
softc->mmcdata = | softc->mmcdata = | ||||
(struct mmc_data *) malloc(sizeof(struct mmc_data), M_DEVBUF, M_NOWAIT|M_ZERO); | (struct mmc_data *)malloc(sizeof(struct mmc_data), M_DEVBUF, M_NOWAIT|M_ZERO); | ||||
periph->softc = softc; | periph->softc = softc; | ||||
softc->periph = periph; | |||||
request_ccb = (union ccb*) arg; | request_ccb = (union ccb*) arg; | ||||
xpt_schedule(periph, CAM_PRIORITY_XPT); | xpt_schedule(periph, CAM_PRIORITY_XPT); | ||||
Context not available. | |||||
return (CAM_REQ_CMP); | return (CAM_REQ_CMP); | ||||
} | } | ||||
static cam_status | |||||
sdda_hook_into_geom(struct cam_periph *periph) | |||||
{ | |||||
struct sdda_softc *softc; | |||||
struct ccb_pathinq cpi; | |||||
struct ccb_getdev cgd; | |||||
u_int maxio; | |||||
softc = (struct sdda_softc*) periph->softc; | |||||
bzero(&cpi, sizeof(cpi)); | |||||
xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE); | |||||
cpi.ccb_h.func_code = XPT_PATH_INQ; | |||||
xpt_action((union ccb *)&cpi); | |||||
bzero(&cgd, sizeof(cgd)); | |||||
xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NONE); | |||||
cpi.ccb_h.func_code = XPT_GDEV_TYPE; | |||||
xpt_action((union ccb *)&cgd); | |||||
/* | |||||
* Register this media as a disk | |||||
*/ | |||||
(void)cam_periph_hold(periph, PRIBIO); | |||||
cam_periph_unlock(periph); | |||||
softc->disk = disk_alloc(); | |||||
softc->disk->d_rotation_rate = 0; | |||||
softc->disk->d_devstat = devstat_new_entry(periph->periph_name, | |||||
periph->unit_number, 512, | |||||
DEVSTAT_ALL_SUPPORTED, | |||||
DEVSTAT_TYPE_DIRECT | | |||||
XPORT_DEVSTAT_TYPE(cpi.transport), | |||||
DEVSTAT_PRIORITY_DISK); | |||||
softc->disk->d_open = sddaopen; | |||||
softc->disk->d_close = sddaclose; | |||||
softc->disk->d_strategy = sddastrategy; | |||||
softc->disk->d_getattr = sddagetattr; | |||||
// softc->disk->d_dump = sddadump; | |||||
softc->disk->d_gone = sddadiskgonecb; | |||||
softc->disk->d_name = "sdda"; | |||||
softc->disk->d_drv1 = periph; | |||||
maxio = cpi.maxio; /* Honor max I/O size of SIM */ | |||||
if (maxio == 0) | |||||
maxio = DFLTPHYS; /* traditional default */ | |||||
else if (maxio > MAXPHYS) | |||||
maxio = MAXPHYS; /* for safety */ | |||||
softc->disk->d_maxsize = maxio; | |||||
softc->disk->d_unit = periph->unit_number; | |||||
softc->disk->d_flags = DISKFLAG_CANDELETE; | |||||
strlcpy(softc->disk->d_descr, softc->card_id_string, | |||||
MIN(sizeof(softc->disk->d_descr), sizeof(softc->card_id_string))); | |||||
strlcpy(softc->disk->d_ident, softc->card_sn_string, | |||||
MIN(sizeof(softc->disk->d_ident), sizeof(softc->card_sn_string))); | |||||
softc->disk->d_hba_vendor = cpi.hba_vendor; | |||||
softc->disk->d_hba_device = cpi.hba_device; | |||||
softc->disk->d_hba_subvendor = cpi.hba_subvendor; | |||||
softc->disk->d_hba_subdevice = cpi.hba_subdevice; | |||||
softc->disk->d_sectorsize = 512; | |||||
softc->disk->d_mediasize = softc->mediasize; | |||||
softc->disk->d_stripesize = 0; | |||||
softc->disk->d_fwsectors = 0; | |||||
softc->disk->d_fwheads = 0; | |||||
/* | |||||
* Acquire a reference to the periph before we register with GEOM. | |||||
* We'll release this reference once GEOM calls us back (via | |||||
* sddadiskgonecb()) telling us that our provider has been freed. | |||||
*/ | |||||
if (cam_periph_acquire(periph) != CAM_REQ_CMP) { | |||||
xpt_print(periph->path, "%s: lost periph during " | |||||
"registration!\n", __func__); | |||||
cam_periph_lock(periph); | |||||
return (CAM_REQ_CMP_ERR); | |||||
} | |||||
disk_create(softc->disk, DISK_VERSION); | |||||
cam_periph_lock(periph); | |||||
cam_periph_unhold(periph); | |||||
xpt_announce_periph(periph, softc->card_id_string); | |||||
/* | |||||
* Add async callbacks for bus reset and | |||||
* bus device reset calls. I don't bother | |||||
* checking if this fails as, in most cases, | |||||
* the system will function just fine without | |||||
* them and the only alternative would be to | |||||
* not attach the device on failure. | |||||
*/ | |||||
xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | | |||||
AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED, | |||||
sddaasync, periph, periph->path); | |||||
return(CAM_REQ_CMP); | |||||
} | |||||
static int | static int | ||||
mmc_exec_app_cmd(struct cam_periph *periph, union ccb *ccb, | mmc_exec_app_cmd(struct cam_periph *periph, union ccb *ccb, | ||||
struct mmc_command *cmd) { | struct mmc_command *cmd) { | ||||
Context not available. | |||||
err = cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL); | err = cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL); | ||||
if (err != 0) | if (err != 0) | ||||
return err; | return (err); | ||||
if (!(ccb->mmcio.cmd.resp[0] & R1_APP_CMD)) | return (MMC_ERR_NONE); | ||||
return MMC_ERR_FAILED; | |||||
return MMC_ERR_NONE; | |||||
} | } | ||||
static void | static void | ||||
Context not available. | |||||
scr->bus_widths = mmc_get_bits(raw_scr, 64, 48, 4); | scr->bus_widths = mmc_get_bits(raw_scr, 64, 48, 4); | ||||
} | } | ||||
Done Inline ActionsSee comment for sdda_get_read_only(). marius: See comment for sdda_get_read_only(). | |||||
static int | static inline void | ||||
mmc_switch(struct cam_periph *periph, union ccb *ccb, | mmc_switch_fill_mmcio(union ccb *ccb, | ||||
uint8_t set, uint8_t index, uint8_t value) | uint8_t set, uint8_t index, uint8_t value, u_int timeout) | ||||
{ | { | ||||
int arg = (MMC_SWITCH_FUNC_WR << 24) | | int arg = (MMC_SWITCH_FUNC_WR << 24) | | ||||
(index << 16) | | (index << 16) | | ||||
(value << 8) | | (value << 8) | | ||||
set; | set; | ||||
cam_fill_mmcio(&ccb->mmcio, | cam_fill_mmcio(&ccb->mmcio, | ||||
/*retries*/ 0, | /*retries*/ 0, | ||||
/*cbfcnp*/ NULL, | /*cbfcnp*/ NULL, | ||||
Not Done Inline ActionsThat timeout doesn't seem to get correctly handled, i. e. for sdhci(4), apparently it isn't used to set SDHCI_TIMEOUT_CONTROL as appropriate (the MMC layer not supplying bridge drivers with timeouts also is a nasty bug with !MCCAM, though). Also, MMC_CAP_WAIT_WHILE_BUSY and the maximum host timeout need to be taken into account (see mmc_switch() in mmc_subr.c). marius: That timeout doesn't seem to get correctly handled, i. e. for sdhci(4), apparently it isn't… | |||||
Context not available. | |||||
/*mmc_arg*/ arg, | /*mmc_arg*/ arg, | ||||
/*mmc_flags*/ MMC_RSP_R1B | MMC_CMD_AC, | /*mmc_flags*/ MMC_RSP_R1B | MMC_CMD_AC, | ||||
/*mmc_data*/ NULL, | /*mmc_data*/ NULL, | ||||
/*timeout*/ 0); | /*timeout*/ timeout); | ||||
Done Inline Actionsstyle(9): "Insert an empty line if the function has no local variables." marius: style(9): "Insert an empty line if the function has no local variables." | |||||
} | |||||
static int | |||||
mmc_switch(struct cam_periph *periph, union ccb *ccb, | |||||
uint8_t set, uint8_t index, uint8_t value, u_int timeout) | |||||
{ | |||||
mmc_switch_fill_mmcio(ccb, set, index, value, timeout); | |||||
cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL); | cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL); | ||||
if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) { | if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) { | ||||
if (ccb->mmcio.cmd.error != 0) { | if (ccb->mmcio.cmd.error != 0) { | ||||
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH, | CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH, | ||||
("%s: MMC command failed", __func__)); | ("%s: MMC command failed", __func__)); | ||||
return EIO; | return (EIO); | ||||
} | } | ||||
return 0; /* Normal return */ | return (0); /* Normal return */ | ||||
} else { | } else { | ||||
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH, | CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH, | ||||
("%s: CAM request failed\n", __func__)); | ("%s: CAM request failed\n", __func__)); | ||||
return EIO; | return (EIO); | ||||
} | } | ||||
Done Inline Actionsstyle(9): "Values in return statements should be enclosed in parentheses." marius: style(9): "Values in return statements should be enclosed in parentheses."
Also: Missing empty… | |||||
} | } | ||||
static uint32_t | |||||
mmc_get_spec_vers(struct cam_periph *periph) { | |||||
struct sdda_softc *softc = (struct sdda_softc *)periph->softc; | |||||
Done Inline ActionsDitto marius: Ditto | |||||
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 uint32_t | |||||
mmc_get_cmd6_timeout(struct cam_periph *periph) | |||||
{ | |||||
struct sdda_softc *softc = (struct sdda_softc *)periph->softc; | |||||
if (mmc_get_spec_vers(periph) >= 6) | |||||
return (softc->raw_ext_csd[EXT_CSD_GEN_CMD6_TIME] * 10); | |||||
return (500 * 1000); | |||||
} | |||||
static int | static int | ||||
mmc_sd_switch(struct cam_periph *periph, union ccb *ccb, | mmc_sd_switch(struct cam_periph *periph, union ccb *ccb, | ||||
uint8_t mode, uint8_t grp, uint8_t value, | uint8_t mode, uint8_t grp, uint8_t value, | ||||
Context not available. | |||||
u_char switch_res[64]; | u_char switch_res[64]; | ||||
int err; | int err; | ||||
uint8_t value; | uint8_t value; | ||||
struct sdda_softc *softc = (struct sdda_softc *)periph->softc; | |||||
struct mmc_params *mmcp = &periph->path->device->mmc_ident_data; | struct mmc_params *mmcp = &periph->path->device->mmc_ident_data; | ||||
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, | CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, | ||||
Context not available. | |||||
} | } | ||||
if (mmcp->card_features & CARD_FEATURE_MMC) { | if (mmcp->card_features & CARD_FEATURE_MMC) { | ||||
err = mmc_switch(periph, ccb, EXT_CSD_CMD_SET_NORMAL, | err = mmc_switch(periph, ccb, EXT_CSD_CMD_SET_NORMAL, | ||||
EXT_CSD_HS_TIMING, value); | EXT_CSD_HS_TIMING, value, softc->cmd6_time); | ||||
} else { | } else { | ||||
err = mmc_sd_switch(periph, ccb, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1, value, switch_res); | err = mmc_sd_switch(periph, ccb, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1, value, switch_res); | ||||
} | } | ||||
Context not available. | |||||
static void | static void | ||||
sdda_set_bus_width(struct cam_periph *periph, union ccb *ccb, int width) { | sdda_set_bus_width(struct cam_periph *periph, union ccb *ccb, int width) { | ||||
struct sdda_softc *softc = (struct sdda_softc *)periph->softc; | |||||
struct mmc_params *mmcp = &periph->path->device->mmc_ident_data; | struct mmc_params *mmcp = &periph->path->device->mmc_ident_data; | ||||
int err; | int err; | ||||
Context not available. | |||||
panic("Invalid bus width %d", width); | panic("Invalid bus width %d", width); | ||||
} | } | ||||
err = mmc_switch(periph, ccb, EXT_CSD_CMD_SET_NORMAL, | err = mmc_switch(periph, ccb, EXT_CSD_CMD_SET_NORMAL, | ||||
EXT_CSD_BUS_WIDTH, value); | EXT_CSD_BUS_WIDTH, value, softc->cmd6_time); | ||||
} else { | } else { | ||||
/* For SD cards we send ACMD6 with the required bus width in arg */ | /* For SD cards we send ACMD6 with the required bus width in arg */ | ||||
struct mmc_command cmd; | struct mmc_command cmd; | ||||
Done Inline ActionsSee comment for sdda_get_read_only(). marius: See comment for sdda_get_read_only(). | |||||
Context not available. | |||||
xpt_action(ccb); | xpt_action(ccb); | ||||
} | } | ||||
static inline const char *bus_width_str(enum mmc_bus_width w) { | 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"); | |||||
Not Done Inline Actionsstyle(9): ""The function type should be on a line by itself preceding the function." marius: style(9): ""The function type should be on a line by itself preceding the function." | |||||
case EXT_CSD_PART_CONFIG_ACC_GP0: | |||||
Done Inline ActionsMissing empty line after variable declaration in a function. marius: Missing empty line after variable declaration in a function. | |||||
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) { | switch (w) { | ||||
case bus_width_1: | case bus_width_1: | ||||
return "1-bit"; | return ("1-bit"); | ||||
case bus_width_4: | case bus_width_4: | ||||
return "4-bit"; | return ("4-bit"); | ||||
case bus_width_8: | case bus_width_8: | ||||
return "8-bit"; | return ("8-bit"); | ||||
} | } | ||||
} | } | ||||
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"); | |||||
impUnsubmitted Not Done Inline ActionsIs this really a panic? Is it a never can happen / programming error detection class event? imp: Is this really a panic? Is it a never can happen / programming error detection class event? | |||||
kibabAuthorUnsubmitted Not Done Inline ActionsThis should never happen, at least for SDHCI-based controllers. For non-SDHCI panic is appropriate because reply to GET_TRAN_SETTINGS absolutely has to be implemented, but there is no way to enforce it at compile-time... kibab: This should never happen, at least for SDHCI-based controllers. For non-SDHCI panic is… | |||||
return (cts->host_caps); | |||||
} | |||||
Not Done Inline Actionsstyle(9): "Second level indents are four spaces." marius: style(9): "Second level indents are four spaces." | |||||
static void | static void | ||||
sdda_start_init(void *context, union ccb *start_ccb) { | sdda_start_init(void *context, union ccb *start_ccb) | ||||
struct cam_periph *periph; | { | ||||
periph = (struct cam_periph *)context; | struct cam_periph *periph = (struct cam_periph *)context; | ||||
struct ccb_trans_settings_mmc *cts; | |||||
uint32_t host_caps; | |||||
uint32_t sec_count; | |||||
int err; | int err; | ||||
int host_f_max; | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdda_start_init\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdda_start_init\n")); | ||||
Done Inline ActionsThat's just the default; for spec_vers >= 6, the actual value should be read from EXT_CSD_GEN_CMD6_TIME. marius: That's just the default; for spec_vers >= 6, the actual value should be read from… | |||||
/* periph was held for us when this task was enqueued */ | /* periph was held for us when this task was enqueued */ | ||||
Context not available. | |||||
if (mmcp->card_features & CARD_FEATURE_MMC) { | if (mmcp->card_features & CARD_FEATURE_MMC) { | ||||
mmc_decode_csd_mmc(mmcp->card_csd, &softc->csd); | mmc_decode_csd_mmc(mmcp->card_csd, &softc->csd); | ||||
mmc_decode_cid_mmc(mmcp->card_cid, &softc->cid); | 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, | err = mmc_send_ext_csd(periph, start_ccb, | ||||
(uint8_t *)&softc->raw_ext_csd, | (uint8_t *)&softc->raw_ext_csd, | ||||
sizeof(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 { | } else { | ||||
mmc_decode_csd_sd(mmcp->card_csd, &softc->csd); | mmc_decode_csd_sd(mmcp->card_csd, &softc->csd); | ||||
mmc_decode_cid_sd(mmcp->card_cid, &softc->cid); | mmc_decode_cid_sd(mmcp->card_cid, &softc->cid); | ||||
Context not available. | |||||
softc->sector_count = softc->csd.capacity / 512; | softc->sector_count = softc->csd.capacity / 512; | ||||
softc->mediasize = softc->csd.capacity; | softc->mediasize = softc->csd.capacity; | ||||
softc->cmd6_time = mmc_get_cmd6_timeout(periph); | |||||
/* MMC >= 4.x have EXT_CSD that has its own opinion about 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] + | 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 + 1] << 8) + | ||||
(softc->raw_ext_csd[EXT_CSD_SEC_CNT + 2] << 16) + | (softc->raw_ext_csd[EXT_CSD_SEC_CNT + 2] << 16) + | ||||
(softc->raw_ext_csd[EXT_CSD_SEC_CNT + 3] << 24); | (softc->raw_ext_csd[EXT_CSD_SEC_CNT + 3] << 24); | ||||
if (sec_count != 0) { | if (sec_count != 0) { | ||||
softc->sector_count = sec_count; | softc->sector_count = sec_count; | ||||
softc->mediasize = softc->sector_count * 512; | softc->mediasize = softc->sector_count * 512; | ||||
Context not available. | |||||
} | } | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, | CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, | ||||
("Capacity: %"PRIu64", sectors: %"PRIu64"\n", | ("Capacity: %"PRIu64", sectors: %"PRIu64"\n", | ||||
softc->mediasize, | softc->mediasize, | ||||
softc->sector_count)); | softc->sector_count)); | ||||
mmc_format_card_id_string(softc, mmcp); | mmc_format_card_id_string(softc, mmcp); | ||||
/* Update info for CAM */ | /* Update info for CAM */ | ||||
device->serial_num_len = strlen(softc->card_sn_string); | device->serial_num_len = strlen(softc->card_sn_string); | ||||
device->serial_num = | device->serial_num = (u_int8_t *)malloc((device->serial_num_len + 1), | ||||
(u_int8_t *)malloc((device->serial_num_len + 1), | M_CAMXPT, M_NOWAIT); | ||||
M_CAMXPT, M_NOWAIT); | |||||
strlcpy(device->serial_num, softc->card_sn_string, device->serial_num_len); | strlcpy(device->serial_num, softc->card_sn_string, device->serial_num_len); | ||||
device->device_id_len = strlen(softc->card_id_string); | device->device_id_len = strlen(softc->card_id_string); | ||||
device->device_id = | device->device_id = (u_int8_t *)malloc((device->device_id_len + 1), | ||||
(u_int8_t *)malloc((device->device_id_len + 1), | M_CAMXPT, M_NOWAIT); | ||||
M_CAMXPT, M_NOWAIT); | |||||
strlcpy(device->device_id, softc->card_id_string, device->device_id_len); | strlcpy(device->device_id, softc->card_id_string, device->device_id_len); | ||||
strlcpy(mmcp->model, softc->card_id_string, sizeof(mmcp->model)); | strlcpy(mmcp->model, softc->card_id_string, sizeof(mmcp->model)); | ||||
/* Set the clock frequency that the card can handle */ | /* Set the clock frequency that the card can handle */ | ||||
struct ccb_trans_settings_mmc *cts; | |||||
cts = &start_ccb->cts.proto_specific.mmc; | cts = &start_ccb->cts.proto_specific.mmc; | ||||
/* First, get the host's max freq */ | /* First, get the host's max freq */ | ||||
Context not available. | |||||
if (start_ccb->ccb_h.status != CAM_REQ_CMP) | if (start_ccb->ccb_h.status != CAM_REQ_CMP) | ||||
panic("Cannot get max host freq"); | panic("Cannot get max host freq"); | ||||
int host_f_max = cts->host_f_max; | host_f_max = cts->host_f_max; | ||||
uint32_t host_caps = cts->host_caps; | host_caps = cts->host_caps; | ||||
if (cts->ios.bus_width != bus_width_1) | if (cts->ios.bus_width != bus_width_1) | ||||
panic("Bus width in ios is not 1-bit"); | panic("Bus width in ios is not 1-bit"); | ||||
Context not available. | |||||
} | } | ||||
} | } | ||||
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] | if (softc->raw_ext_csd[EXT_CSD_CARD_TYPE] | ||||
& EXT_CSD_CARD_TYPE_HS_52) | & EXT_CSD_CARD_TYPE_HS_52) | ||||
softc->card_f_max = MMC_TYPE_HS_52_MAX; | softc->card_f_max = MMC_TYPE_HS_52_MAX; | ||||
Done Inline ActionsProbably should be an "else if" as a device can't possibly be (e)MMC chip/card and SD card at the same time. marius: Probably should be an "else if" as a device can't possibly be (e)MMC chip/card and SD card at… | |||||
Done Inline ActionsThere's no need to wrap comments on margins shorter than the generic 80 columns maximum ... marius: There's no need to wrap comments on margins shorter than the generic 80 columns maximum ... | |||||
Context not available. | |||||
if (err != MMC_ERR_NONE) | if (err != MMC_ERR_NONE) | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("Cannot switch card to high-speed mode")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("Cannot switch card to high-speed mode")); | ||||
} | } | ||||
softc->state = SDDA_STATE_NORMAL; | 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) { | |||||
Done Inline Actions... but this line is too long. Also, the comment above this function apparently is at least outdated now. marius: ... but this line is too long. Also, the comment above this function apparently is at least… | |||||
sdda_process_mmc_partitions(periph, start_ccb); | |||||
} else if (mmcp->card_features & CARD_FEATURE_SD20) { | |||||
/* For SD[HC] cards, just add one partition that is the whole card */ | |||||
sdda_add_part(periph, 0, "sdda", | |||||
periph->unit_number, | |||||
mmc_get_media_size(periph), | |||||
sdda_get_read_only(periph, start_ccb)); | |||||
softc->part_curr = 0; | |||||
Not Done Inline ActionsThis should continue using the second level indent of 4 spaces below CAM_DEBUG(). marius: This should continue using the second level indent of 4 spaces below CAM_DEBUG(). | |||||
Not Done Inline ActionsIt was indented like that because this is a continuation inside the braces. But I will align everything with 4 spaces. kibab: It was indented like that because this is a continuation inside the braces. But I will align… | |||||
} | |||||
xpt_announce_periph(periph, softc->card_id_string); | |||||
/* | |||||
* Add async callbacks for bus reset and bus device reset calls. | |||||
* I don't bother checking if this fails as, in most cases, | |||||
* the system will function just fine without them and the only | |||||
* alternative would be to not attach the device on failure. | |||||
*/ | |||||
xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | | |||||
AC_GETDEV_CHANGED | AC_ADVINFO_CHANGED, | |||||
sddaasync, periph, periph->path); | |||||
impUnsubmitted Not Done Inline ActionsThis list seems overly long. SEND_BDR and BUS_RESET I thought were purely SCSI.... I don't see where they can be sent... imp: This list seems overly long. SEND_BDR and BUS_RESET I thought were purely SCSI.... I don't see… | |||||
kibabAuthorUnsubmitted Not Done Inline ActionsYeah, I checked the sources for possible mentions of SEND_BDR and BUS_RESET and it seems they are sent only in SCSI-specific code. I will remove this and related case blocks in sddaasync() kibab: Yeah, I checked the sources for possible mentions of SEND_BDR and BUS_RESET and it seems they… | |||||
} | |||||
Done Inline ActionsThis comment is misleading; eMMC RPMB partitions actually support all I/O operations that would be needed to put file systems onto them. However, apart form most likely requiring unlocking, due to the nature of RPMBs it doesn't seem to make much sense to actually do so. Instead, it would appear to be more appropriate to create a userland tool tailored to the actual use case or wrap around the RPMB partition support of e. g. mmc-utils. marius: This comment is misleading; eMMC RPMB partitions actually support all I/O operations that would… | |||||
static void | |||||
sdda_add_part(struct cam_periph *periph, u_int type, const char *name, | |||||
u_int cnt, off_t media_size, bool ro) | |||||
{ | |||||
struct sdda_softc *sc = (struct sdda_softc *)periph->softc; | |||||
struct sdda_part *part; | |||||
struct ccb_pathinq cpi; | |||||
u_int maxio; | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, | |||||
("Partition type '%s', size %ju %s\n", | |||||
part_type(type), | |||||
media_size, | |||||
ro ? "(read-only)" : "")); | |||||
part = sc->part[type] = malloc(sizeof(*part), M_DEVBUF, | |||||
M_WAITOK | M_ZERO); | |||||
part->cnt = cnt; | |||||
part->type = type; | |||||
part->ro = ro; | |||||
part->sc = sc; | |||||
snprintf(part->name, sizeof(part->name), name, periph->unit_number); | |||||
/* | |||||
* Due to the nature of RPMB partition it doesn't make much sense | |||||
* to add it as a disk. It would be more appropriate to create a | |||||
* userland tool to operate on the partition or leverage the existing | |||||
* tools from sysutils/mmc-utils. | |||||
*/ | |||||
if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) { | |||||
/* TODO: Create device, assign IOCTL handler */ | |||||
Done Inline ActionsHrm, why is that code block left comment out (apparently, the sdda_hook_into_geom() had it in). marius: Hrm, why is that code block left comment out (apparently, the sdda_hook_into_geom() had it in). | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, | |||||
("Don't know what to do with RPMB partitions yet\n")); | |||||
return; | |||||
} | |||||
bioq_init(&part->bio_queue); | |||||
bzero(&cpi, sizeof(cpi)); | |||||
xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NONE); | |||||
Not Done Inline ActionsSide-note: Prior to UHS-II, the maximum transfer size for SDHCI is limited by the 16-bit block count register (that maximum may even shrink further depending on the re-tuning mode a particular SDHCI controller implements, though). So for !MMCCAM, shdci(4) correctly hardcodes 65535 for MMCBR_IVAR_MAX_DATA in the !re-tuning case. However, in case of MMCCAM, shdci(4) currently incorrectly sets cpi->maxio to MAXPHYS for XPT_PATH_INQ. marius: Side-note: Prior to UHS-II, the maximum transfer size for SDHCI is limited by the 16-bit block… | |||||
cpi.ccb_h.func_code = XPT_PATH_INQ; | |||||
xpt_action((union ccb *)&cpi); | |||||
impUnsubmitted Done Inline ActionsMaybe I should create a wrapper for XPT_PATH_INQ... I'll talk to scott about it... But no action item... imp: Maybe I should create a wrapper for XPT_PATH_INQ... I'll talk to scott about it... But no… | |||||
kibabAuthorUnsubmitted Not Done Inline ActionsWhat would your new wrapper do? kibab: What would your new wrapper do? | |||||
/* | |||||
* Register this media as a disk | |||||
*/ | |||||
(void)cam_periph_hold(periph, PRIBIO); | |||||
Done Inline ActionsCurrently, MMCCAM doesn't seem to invoke a MMC_ERASE command anywhere so it appears that DISKFLAG_CANDELETE/BIO_DELETE isn't actually supported so far. Also, if it actually does, it seems that part->disk->d_delmaxsize would be needed to be set somewhere. marius: Currently, MMCCAM doesn't seem to invoke a MMC_ERASE command anywhere so it appears that… | |||||
Not Done Inline ActionsFair point. kibab: Fair point. | |||||
cam_periph_unlock(periph); | |||||
part->disk = disk_alloc(); | |||||
part->disk->d_rotation_rate = DISK_RR_NON_ROTATING; | |||||
part->disk->d_devstat = devstat_new_entry(part->name, | |||||
cnt, 512, | |||||
DEVSTAT_ALL_SUPPORTED, | |||||
DEVSTAT_TYPE_DIRECT | XPORT_DEVSTAT_TYPE(cpi.transport), | |||||
DEVSTAT_PRIORITY_DISK); | |||||
part->disk->d_open = sddaopen; | |||||
part->disk->d_close = sddaclose; | |||||
part->disk->d_strategy = sddastrategy; | |||||
part->disk->d_getattr = sddagetattr; | |||||
// sc->disk->d_dump = sddadump; | |||||
part->disk->d_gone = sddadiskgonecb; | |||||
part->disk->d_name = part->name; | |||||
part->disk->d_drv1 = part; | |||||
maxio = cpi.maxio; /* Honor max I/O size of SIM */ | |||||
if (maxio == 0) | |||||
maxio = DFLTPHYS; /* traditional default */ | |||||
else if (maxio > MAXPHYS) | |||||
maxio = MAXPHYS; /* for safety */ | |||||
part->disk->d_maxsize = maxio; | |||||
part->disk->d_unit = cnt; | |||||
part->disk->d_flags = 0; | |||||
strlcpy(part->disk->d_descr, sc->card_id_string, | |||||
MIN(sizeof(part->disk->d_descr), sizeof(sc->card_id_string))); | |||||
strlcpy(part->disk->d_ident, sc->card_sn_string, | |||||
MIN(sizeof(part->disk->d_ident), sizeof(sc->card_sn_string))); | |||||
part->disk->d_hba_vendor = cpi.hba_vendor; | |||||
part->disk->d_hba_device = cpi.hba_device; | |||||
part->disk->d_hba_subvendor = cpi.hba_subvendor; | |||||
part->disk->d_hba_subdevice = cpi.hba_subdevice; | |||||
part->disk->d_sectorsize = mmc_get_sector_size(periph); | |||||
part->disk->d_mediasize = media_size; | |||||
part->disk->d_stripesize = 0; | |||||
part->disk->d_fwsectors = 0; | |||||
part->disk->d_fwheads = 0; | |||||
Done Inline ActionsExcessive newline. marius: Excessive newline. | |||||
/* | |||||
* Acquire a reference to the periph before we register with GEOM. | |||||
* We'll release this reference once GEOM calls us back (via | |||||
* sddadiskgonecb()) telling us that our provider has been freed. | |||||
*/ | |||||
if (cam_periph_acquire(periph) != CAM_REQ_CMP) { | |||||
xpt_print(periph->path, "%s: lost periph during " | |||||
"registration!\n", __func__); | |||||
cam_periph_lock(periph); | |||||
return; | |||||
} | |||||
disk_create(part->disk, DISK_VERSION); | |||||
cam_periph_lock(periph); | |||||
cam_periph_unhold(periph); | |||||
} | |||||
/* | |||||
* For MMC cards, process EXT_CSD and add partitions that are supported by | |||||
* this device. | |||||
*/ | |||||
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; | |||||
off_t erase_size, sector_size, size, wp_size; | |||||
int i; | |||||
const uint8_t *ext_csd; | |||||
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 | |||||
Not Done Inline ActionsThough, actually doing something with extended user data area information e. g. by registering such partitions with geom_flashmap(4) is still missing. marius: Though, actually doing something with extended user data area information e. g. by registering… | |||||
* 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, "sdda", | |||||
periph->unit_number, mmc_get_media_size(periph), ro); | |||||
sc->part_curr = EXT_CSD_PART_CONFIG_ACC_DEFAULT; | |||||
Done Inline ActionsShould use 4 space second level indent relative to sdda_add_part(). marius: Should use 4 space second level indent relative to sdda_add_part(). | |||||
if (mmc_get_spec_vers(periph) < 3) | |||||
return; | |||||
/* Belatedly announce enhanced user data slice. */ | |||||
Done Inline ActionsDitto marius: Ditto | |||||
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, | |||||
SDDA_FMT_BOOT, 0, 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, | |||||
SDDA_FMT_BOOT, 1, 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, | |||||
SDDA_FMT_RPMB, 0, 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) { | |||||
Done Inline ActionsWrapping should make use of the 80 columns margin. marius: Wrapping should make use of the 80 columns margin. | |||||
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++) { | |||||
Done Inline ActionsShould use a single space after the type instead of a tab. marius: Should use a single space after the type instead of a tab. | |||||
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) | |||||
Not Done Inline ActionsWhat guarantees that the kernel copy of the EXT_CSD - specifically the upper bits of EXT_CSD_PART_CONFIG - isn't outdated as userland has changed the content? For mmcsd(4), mmcsd_ioctl_cmd() rereads EXT_CSD as necessary as that IOCTL handler is the only way userland can alter EXT_CSD (specifically, setting the partition to boot from in EXT_CSD_PART_CONFIG is a likely change). However, with CAM an additional way for userland to fiddle with these bits is via pass(4). So there appears to be a hook missing which rereads EXT_CSD also on accesses via the latter. marius: What guarantees that the kernel copy of the EXT_CSD - specifically the upper bits of… | |||||
Not Done Inline ActionsEverything that is happening through pass(4) is irrelevant here, the instance of sdda is not processing any pass requests. The user is advised that fiddling with camcontrol can have unwanted side effects. The only way to recover after direct manipulations with pass(4) is to initiate rescan and reattach. kibab: Everything that is happening through pass(4) is irrelevant here, the instance of sdda is not… | |||||
continue; | |||||
sdda_add_part(periph, EXT_CSD_PART_CONFIG_ACC_GP0 + i, | |||||
SDDA_FMT_GP, i, size * erase_size * wp_size, ro); | |||||
} | |||||
} | |||||
} | |||||
/* | |||||
* We cannot just call mmc_switch() since it will sleep, and we are in | |||||
* GEOM context and cannot sleep. Instead, create an MMCIO request to switch | |||||
* partitions and send it to h/w, and upon completion resume processing | |||||
* the I/O queue. | |||||
* This function cannot fail, instead check switch errors in sddadone(). | |||||
*/ | |||||
static void | |||||
sdda_init_switch_part(struct cam_periph *periph, union ccb *start_ccb, u_int part) { | |||||
struct sdda_softc *sc = (struct sdda_softc *)periph->softc; | |||||
uint8_t value; | |||||
sc->part_requested = part; | |||||
value = (sc->raw_ext_csd[EXT_CSD_PART_CONFIG] & | |||||
~EXT_CSD_PART_CONFIG_ACC_MASK) | part; | |||||
mmc_switch_fill_mmcio(start_ccb, EXT_CSD_CMD_SET_NORMAL, | |||||
EXT_CSD_PART_CONFIG, value, sc->part_time); | |||||
start_ccb->ccb_h.cbfcnp = sddadone; | |||||
sc->outstanding_cmds++; | |||||
cam_periph_unlock(periph); | |||||
xpt_action(start_ccb); | |||||
Done Inline ActionsAccording to style(9), two spaces are required after a period within a comment. marius: According to style(9), two spaces are required after a period within a comment. | |||||
cam_periph_lock(periph); | |||||
Done Inline ActionsAccording to style(9), variable declarations within functions are accumulated at the beginning of that function. marius: According to style(9), variable declarations within functions are accumulated at the beginning… | |||||
} | } | ||||
/* Called with periph lock held! */ | /* Called with periph lock held! */ | ||||
static void | static void | ||||
sddastart(struct cam_periph *periph, union ccb *start_ccb) | sddastart(struct cam_periph *periph, union ccb *start_ccb) | ||||
{ | { | ||||
struct bio *bp; | |||||
struct sdda_softc *softc = (struct sdda_softc *)periph->softc; | struct sdda_softc *softc = (struct sdda_softc *)periph->softc; | ||||
struct sdda_part *part; | |||||
struct mmc_params *mmcp = &periph->path->device->mmc_ident_data; | struct mmc_params *mmcp = &periph->path->device->mmc_ident_data; | ||||
int part_index; | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddastart\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sddastart\n")); | ||||
Not Done Inline ActionsJust as with the remainder of EXT_CSD, userland may change the currently selected partition so the kernel must not trust its last accessed partition to still be the active one if there was a write access to EXT_CSD by userland in between (which is something the current in-tree mmcsd_ioctl_cmd() also doesn't handle correctly, though). Apart from that, unnecessary partition switches should be avoiding of course. marius: Just as with the remainder of EXT_CSD, userland may change the currently selected partition so… | |||||
Not Done Inline ActionsWith regards to changes through pass(4) see my comment above. kibab: With regards to changes through pass(4) see my comment above.
"Unnecessary partitions switch"… | |||||
Not Done Inline ActionsYou can send suspend/resume events to e. g. sdhci(4) instances via devctl(8), which should even work on ARM (but doing so with the system booted from an SD card probably isn't a good idea for starters). marius: You can send suspend/resume events to e. g. sdhci(4) instances via devctl(8), which should even… | |||||
if (softc->state != SDDA_STATE_NORMAL) { | if (softc->state != SDDA_STATE_NORMAL) { | ||||
Done Inline ActionsLine exceeds 80 columns maximum. marius: Line exceeds 80 columns maximum. | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("device is not in SDDA_STATE_NORMAL yet")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("device is not in SDDA_STATE_NORMAL yet\n")); | ||||
xpt_release_ccb(start_ccb); | xpt_release_ccb(start_ccb); | ||||
return; | return; | ||||
} | } | ||||
struct bio *bp; | |||||
/* Run regular command. */ | /* Find partition that has outstanding commands. Prefer current partition. */ | ||||
bp = bioq_first(&softc->bio_queue); | part = softc->part[softc->part_curr]; | ||||
bp = bioq_first(&part->bio_queue); | |||||
if (bp == NULL) { | |||||
for (part_index = 0; part_index < MMC_PART_MAX; part_index++) { | |||||
if ((part = softc->part[part_index]) != NULL && | |||||
(bp = bioq_first(&softc->part[part_index]->bio_queue)) != NULL) | |||||
break; | |||||
} | |||||
} | |||||
if (bp == NULL) { | if (bp == NULL) { | ||||
xpt_release_ccb(start_ccb); | xpt_release_ccb(start_ccb); | ||||
return; | return; | ||||
} | } | ||||
bioq_remove(&softc->bio_queue, bp); | if (part_index != softc->part_curr) { | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, | |||||
("Partition %d -> %d\n", softc->part_curr, part_index)); | |||||
/* | |||||
* According to section "6.2.2 Command restrictions" of the eMMC | |||||
* specification v5.1, CMD19/CMD21 aren't allowed to be used with | |||||
* RPMB partitions. So we pause re-tuning along with triggering | |||||
* it up-front to decrease the likelihood of re-tuning becoming | |||||
* necessary while accessing an RPMB partition. Consequently, an | |||||
* RPMB partition should immediately be switched away from again | |||||
* after an access in order to allow for re-tuning to take place | |||||
* anew. | |||||
*/ | |||||
/* TODO: pause retune if switching to RPMB partition */ | |||||
softc->state = SDDA_STATE_PART_SWITCH; | |||||
sdda_init_switch_part(periph, start_ccb, part_index); | |||||
return; | |||||
} | |||||
bioq_remove(&part->bio_queue, bp); | |||||
switch (bp->bio_cmd) { | switch (bp->bio_cmd) { | ||||
case BIO_WRITE: | case BIO_WRITE: | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_WRITE\n")); | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_WRITE\n")); | ||||
softc->flags |= SDDA_FLAG_DIRTY; | part->flags |= SDDA_FLAG_DIRTY; | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case BIO_READ: | case BIO_READ: | ||||
{ | { | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_READ\n")); | struct ccb_mmcio *mmcio; | ||||
uint64_t blockno = bp->bio_pblkno; | uint64_t blockno = bp->bio_pblkno; | ||||
uint16_t count = bp->bio_bcount / 512; | uint16_t count = bp->bio_bcount / 512; | ||||
uint16_t opcode; | uint16_t opcode; | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("Block %"PRIu64" cnt %u\n", blockno, count)); | if (bp->bio_cmd == BIO_READ) | ||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("BIO_READ\n")); | |||||
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, | |||||
("Block %"PRIu64" cnt %u\n", blockno, count)); | |||||
/* Construct new MMC command */ | /* Construct new MMC command */ | ||||
if (bp->bio_cmd == BIO_READ) { | if (bp->bio_cmd == BIO_READ) { | ||||
Context not available. | |||||
start_ccb->ccb_h.retry_count = 0; | start_ccb->ccb_h.retry_count = 0; | ||||
start_ccb->ccb_h.timeout = 15 * 1000; | start_ccb->ccb_h.timeout = 15 * 1000; | ||||
start_ccb->ccb_h.cbfcnp = sddadone; | start_ccb->ccb_h.cbfcnp = sddadone; | ||||
struct ccb_mmcio *mmcio; | |||||
mmcio = &start_ccb->mmcio; | mmcio = &start_ccb->mmcio; | ||||
mmcio->cmd.opcode = opcode; | mmcio->cmd.opcode = opcode; | ||||
Context not available. | |||||
} | } | ||||
start_ccb->ccb_h.ccb_bp = bp; | start_ccb->ccb_h.ccb_bp = bp; | ||||
softc->outstanding_cmds++; | softc->outstanding_cmds++; | ||||
softc->refcount++; | part->refcount++; | ||||
cam_periph_unlock(periph); | cam_periph_unlock(periph); | ||||
xpt_action(start_ccb); | xpt_action(start_ccb); | ||||
cam_periph_lock(periph); | cam_periph_lock(periph); | ||||
softc->refcount--; | part->refcount--; | ||||
/* May have more work to do, so ensure we stay scheduled */ | /* May have more work to do, so ensure we stay scheduled */ | ||||
sddaschedule(periph); | sddaschedule(periph); | ||||
Context not available. | |||||
static void | static void | ||||
sddadone(struct cam_periph *periph, union ccb *done_ccb) | sddadone(struct cam_periph *periph, union ccb *done_ccb) | ||||
{ | { | ||||
struct bio *bp; | |||||
struct sdda_softc *softc; | struct sdda_softc *softc; | ||||
struct ccb_mmcio *mmcio; | struct ccb_mmcio *mmcio; | ||||
// struct ccb_getdev *cgd; | |||||
struct cam_path *path; | struct cam_path *path; | ||||
// int state; | uint32_t card_status; | ||||
int error = 0; | |||||
Done Inline ActionsAccording to style(9), variable declarations within functions are accumulated at the beginning of that function, but probably better yet, just use mmcio->cmd.resp[0] directly below so compilers won't complain about an unused/written-only variable if CAM_DEBUG is defined no nothing. marius: According to style(9), variable declarations within functions are accumulated at the beginning… | |||||
softc = (struct sdda_softc *)periph->softc; | softc = (struct sdda_softc *)periph->softc; | ||||
mmcio = &done_ccb->mmcio; | mmcio = &done_ccb->mmcio; | ||||
path = done_ccb->ccb_h.path; | path = done_ccb->ccb_h.path; | ||||
CAM_DEBUG(path, CAM_DEBUG_TRACE, ("sddadone\n")); | CAM_DEBUG(path, CAM_DEBUG_TRACE, ("sddadone\n")); | ||||
struct bio *bp; | |||||
int error = 0; | |||||
// cam_periph_lock(periph); | // cam_periph_lock(periph); | ||||
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | ||||
CAM_DEBUG(path, CAM_DEBUG_TRACE, ("Error!!!\n")); | CAM_DEBUG(path, CAM_DEBUG_TRACE, ("Error!!!\n")); | ||||
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) | if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) | ||||
cam_release_devq(path, | cam_release_devq(path, | ||||
/*relsim_flags*/0, | /*relsim_flags*/0, | ||||
/*reduction*/0, | /*reduction*/0, | ||||
/*timeout*/0, | /*timeout*/0, | ||||
/*getcount_only*/0); | /*getcount_only*/0); | ||||
error = 5; /* EIO */ | error = 5; /* EIO */ | ||||
} else { | } else { | ||||
Done Inline ActionsHrm, how does that guarantee that in case the switching fails, the outstanding deletes/reads/writes in the I/O queue also fail and won't go to the wrong partition? marius: Hrm, how does that guarantee that in case the switching fails, the outstanding… | |||||
Not Done Inline ActionsThe index of active partition is not changed in this case. So when the next BIO is processed and that BIO targets new partition, the code will attempt to switch the partition again. kibab: The index of active partition is not changed in this case. So when the next BIO is processed… | |||||
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) | if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) | ||||
Context not available. | |||||
error = 0; | error = 0; | ||||
} | } | ||||
card_status = mmcio->cmd.resp[0]; | |||||
CAM_DEBUG(path, CAM_DEBUG_TRACE, | |||||
("Card status: %08x\n", R1_STATUS(card_status))); | |||||
CAM_DEBUG(path, CAM_DEBUG_TRACE, | |||||
("Current state: %d\n", R1_CURRENT_STATE(card_status))); | |||||
/* Process result of switching MMC partitions */ | |||||
if (softc->state == SDDA_STATE_PART_SWITCH) { | |||||
CAM_DEBUG(path, CAM_DEBUG_TRACE, | |||||
("Compteting partition switch to %d\n", softc->part_requested)); | |||||
softc->outstanding_cmds--; | |||||
/* Complete partition switch */ | |||||
softc->state = SDDA_STATE_NORMAL; | |||||
if (error != MMC_ERR_NONE) { | |||||
/* TODO: Unpause retune if accessing RPMB */ | |||||
xpt_release_ccb(done_ccb); | |||||
xpt_schedule(periph, CAM_PRIORITY_NORMAL); | |||||
return; | |||||
} | |||||
softc->raw_ext_csd[EXT_CSD_PART_CONFIG] = | |||||
(softc->raw_ext_csd[EXT_CSD_PART_CONFIG] & | |||||
~EXT_CSD_PART_CONFIG_ACC_MASK) | softc->part_requested; | |||||
/* TODO: Unpause retune if accessing RPMB */ | |||||
softc->part_curr = softc->part_requested; | |||||
xpt_release_ccb(done_ccb); | |||||
/* Return to processing BIO requests */ | |||||
xpt_schedule(periph, CAM_PRIORITY_NORMAL); | |||||
return; | |||||
} | |||||
bp = (struct bio *)done_ccb->ccb_h.ccb_bp; | bp = (struct bio *)done_ccb->ccb_h.ccb_bp; | ||||
bp->bio_error = error; | bp->bio_error = error; | ||||
Context not available. | |||||
bp->bio_flags |= BIO_ERROR; | bp->bio_flags |= BIO_ERROR; | ||||
} | } | ||||
uint32_t card_status = mmcio->cmd.resp[0]; | |||||
CAM_DEBUG(path, CAM_DEBUG_TRACE, | |||||
("Card status: %08x\n", R1_STATUS(card_status))); | |||||
CAM_DEBUG(path, CAM_DEBUG_TRACE, | |||||
("Current state: %d\n", R1_CURRENT_STATE(card_status))); | |||||
softc->outstanding_cmds--; | softc->outstanding_cmds--; | ||||
xpt_release_ccb(done_ccb); | xpt_release_ccb(done_ccb); | ||||
biodone(bp); | biodone(bp); | ||||
Context not available. |
style(9): "When declaring variables in structures, declare them sorted by use, then by size (largest to smallest), and then in alphabetical order. The first category normally does not apply, but there are exceptions."