Home | History | Annotate | Line # | Download | only in wii
      1 /* $NetBSD: sdmmc.c,v 1.1 2025/11/16 20:11:47 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2025 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <lib/libsa/stand.h>
     30 #include <lib/libkern/libkern.h>
     31 
     32 #include <sys/bootblock.h>
     33 
     34 #define FSTYPENAMES
     35 #include <sys/disklabel.h>
     36 
     37 #include "sdmmc.h"
     38 #include "miniipc.h"
     39 #include "gpio.h"
     40 
     41 #if LABELSECTOR != 1
     42 #error bad LABELSECTOR
     43 #endif
     44 
     45 #define DEFAULT_DEVICE	"ld0"
     46 #define DEFAULT_PART	0
     47 
     48 #define	SDMMC_STATE_NEW_CARD		2
     49 
     50 static uint8_t sdmmc_buf[SDMMC_BLOCK_SIZE];
     51 static struct disklabel sdmmc_disklabel;
     52 
     53 static void
     54 sdmmc_print_label(void)
     55 {
     56 #ifdef SDMMC_DEBUG
     57 	struct disklabel *d = &sdmmc_disklabel;
     58 	int part;
     59 
     60 	for (part = 0; part < le16toh(d->d_npartitions); part++) {
     61 		struct partition *p = &d->d_partitions[part];
     62 
     63 		printf("partition %s%c ", DEFAULT_DEVICE, part + 'a');
     64 
     65 		if (p->p_fstype < __arraycount(fstypenames)) {
     66 			printf("(%s)", fstypenames[p->p_fstype]);
     67 		} else {
     68 			printf("(type %u)", p->p_fstype);
     69 		}
     70 
     71 		printf(" - %u %u\n", le32toh(p->p_offset), le32toh(p->p_size));
     72 	}
     73 #endif
     74 }
     75 
     76 static int
     77 sdmmc_read_label(daddr_t dblk, size_t size)
     78 {
     79 	struct disklabel d;
     80 	int error;
     81 
     82 	error = miniipc_sdmmc_read(dblk + LABELSECTOR, 1, sdmmc_buf);
     83 	if (error != 0) {
     84 		printf("SDMMC: failed to read disklabel: %d\n", error);
     85 		return error;
     86 	}
     87 	memcpy(&d, sdmmc_buf, sizeof(d));
     88 
     89 	if (le32toh(d.d_magic) != DISKMAGIC ||
     90 	    le32toh(d.d_magic2) != DISKMAGIC) {
     91 #ifdef SDMMC_DEBUG
     92 		printf("SDMMC: bad diskmagic 0x%x 0x%x\n", le32toh(d.d_magic),
     93 		    le32toh(d.d_magic2));
     94 #endif
     95 		return EINVAL;
     96 	}
     97 	if (le16toh(d.d_npartitions) > MAXPARTITIONS) {
     98 		printf("SDMMC: bad npartitions %u\n", le16toh(d.d_npartitions));
     99 		return EINVAL;
    100 	}
    101 
    102 	memcpy(&sdmmc_disklabel, sdmmc_buf, sizeof(sdmmc_disklabel));
    103 	return 0;
    104 }
    105 
    106 int
    107 sdmmc_init(void)
    108 {
    109 	struct mbr_sector mbr;
    110 	struct mbr_partition *mbr_part;
    111 	int error, n;
    112 	bool has_label = false;
    113 	uint32_t state, ack;
    114 
    115 	miniipc_sdmmc_state(&state);
    116 	if (state == SDMMC_STATE_NEW_CARD) {
    117 		miniipc_sdmmc_ack(&ack);
    118 	}
    119 
    120 	error = miniipc_sdmmc_read(0, 1, sdmmc_buf);
    121 	if (error != 0) {
    122 		printf("SDMMC: failed to read MBR: %d\n", error);
    123 		return error;
    124 	}
    125 	memcpy(&mbr, sdmmc_buf, sizeof(mbr));
    126 	if (le16toh(mbr.mbr_magic) != MBR_MAGIC) {
    127 		printf("SDMMC: bad MBR magic: 0x%x\n", le32toh(mbr.mbr_magic));
    128 		return ENXIO;
    129 	}
    130 
    131 	for (n = 0; n < MBR_PART_COUNT; n++) {
    132 		uint32_t start, size;
    133 
    134 		mbr_part = &mbr.mbr_parts[n];
    135 		size = le32toh(mbr_part->mbrp_size);
    136 		if (le32toh(mbr_part->mbrp_size) == 0) {
    137 			continue;
    138 		}
    139 		start = le32toh(mbr_part->mbrp_start);
    140 #ifdef SDMMC_DEBUG
    141 		printf("MBR part %u type 0x%x start %u size %u\n",
    142 		    n, mbr_part->mbrp_type, start, size);
    143 #endif
    144 
    145 		if (mbr_part->mbrp_type == MBR_PTYPE_NETBSD && !has_label) {
    146 			error = sdmmc_read_label(start, size);
    147 			if (error != 0) {
    148 				struct partition *p =
    149 				    &sdmmc_disklabel.d_partitions[0];
    150 
    151 				/* No label on disk, fake one. */
    152 				p->p_fstype = FS_BSDFFS;
    153 				p->p_size = htole32(size);
    154 				p->p_offset = htole32(start);
    155 				sdmmc_disklabel.d_npartitions = htole16(1);
    156 			}
    157 			has_label = true;
    158 		}
    159 	}
    160 
    161 	sdmmc_print_label();
    162 
    163 	return 0;
    164 }
    165 
    166 static int
    167 sdmmc_parse(const char *fname, int *part, char **pfile)
    168 {
    169 	const char *full_path;
    170 	char pathbuf[PATH_MAX];
    171 
    172 	if (strchr(fname, ':') == NULL) {
    173 		snprintf(pathbuf, sizeof(pathbuf), "%s%c:%s",
    174 		    DEFAULT_DEVICE, DEFAULT_PART + 'a', fname);
    175 		full_path = pathbuf;
    176 		*pfile = __UNCONST(fname);
    177 	} else {
    178 		full_path = fname;
    179 		*pfile = strchr(fname, ':') + 1;
    180 	}
    181 	if (*pfile[0] == '\0') {
    182 		*pfile = __UNCONST("/");
    183 	}
    184 
    185 	if (strncmp(full_path, DEFAULT_DEVICE, 3) != 0) {
    186 		return EINVAL;
    187 	}
    188 	if (full_path[3] < 'a' || full_path[3] >= 'a' + MAXPARTITIONS ||
    189 	    full_path[4] != ':') {
    190 		return EINVAL;
    191 	}
    192 	*part = full_path[3] - 'a';
    193 
    194 	return 0;
    195 }
    196 
    197 int
    198 sdmmc_open(struct open_file *f, ...)
    199 {
    200 	int error, n, part;
    201 	const char *fname;
    202 	va_list ap;
    203 	char **file;
    204 	char *path;
    205 
    206 	va_start(ap, f);
    207 	fname = va_arg(ap, const char *);
    208 	file = va_arg(ap, char **);
    209 	va_end(ap);
    210 
    211 	error = sdmmc_parse(fname, &part, &path);
    212 	if (error != 0) {
    213 		return error;
    214 	}
    215 
    216 	for (n = 0; n < ndevs; n++) {
    217 		if (strcmp(DEV_NAME(&devsw[n]), "sdmmc") == 0) {
    218 			f->f_dev = &devsw[n];
    219 			break;
    220 		}
    221 	}
    222 	if (n == ndevs) {
    223 		return ENXIO;
    224 	}
    225 
    226 	f->f_devdata = &sdmmc_disklabel.d_partitions[part];
    227 	*file = path;
    228 
    229 	return 0;
    230 }
    231 
    232 int
    233 sdmmc_close(struct open_file *f)
    234 {
    235 	return 0;
    236 }
    237 
    238 int
    239 sdmmc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf,
    240     size_t *rsize)
    241 {
    242 	struct partition *p = devdata;
    243 	int error;
    244 
    245 	if (rw != F_READ) {
    246 		return EROFS;
    247 	}
    248 	if ((size % SDMMC_BLOCK_SIZE) != 0) {
    249 		printf("I/O must be multiple of SDMMC_BLOCK_SIZE\n");
    250 		return EIO;
    251 	}
    252 
    253 	gpio_clear(GPIO_SLOT_LED);
    254 	error = miniipc_sdmmc_read(le32toh(p->p_offset) + dblk,
    255 	    size / SDMMC_BLOCK_SIZE, buf);
    256 	gpio_set(GPIO_SLOT_LED);
    257 	if (error == 0) {
    258 		*rsize = size;
    259 	}
    260 
    261 	return error;
    262 }
    263