sdmmc.c revision 1.1
1/* $NetBSD: sdmmc.c,v 1.1 2025/11/16 20:11:47 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2025 Jared McNeill <jmcneill@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 50static uint8_t sdmmc_buf[SDMMC_BLOCK_SIZE]; 51static struct disklabel sdmmc_disklabel; 52 53static void 54sdmmc_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 76static int 77sdmmc_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 106int 107sdmmc_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 166static int 167sdmmc_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 197int 198sdmmc_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 232int 233sdmmc_close(struct open_file *f) 234{ 235 return 0; 236} 237 238int 239sdmmc_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