1 /* $NetBSD: mmemcard.c,v 1.22 2014/03/26 16:08:45 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by ITOH Yasufumi. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: mmemcard.c,v 1.22 2014/03/26 16:08:45 christos Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/buf.h> 37 #include <sys/bufq.h> 38 #include <sys/device.h> 39 #include <sys/disklabel.h> 40 #include <sys/disk.h> 41 #include <sys/kernel.h> 42 #include <sys/malloc.h> 43 #include <sys/proc.h> 44 #include <sys/stat.h> 45 #include <sys/systm.h> 46 #include <sys/vnode.h> 47 #include <sys/conf.h> 48 49 #include <dreamcast/dev/maple/maple.h> 50 #include <dreamcast/dev/maple/mapleconf.h> 51 52 #include "ioconf.h" 53 54 #define MMEM_MAXACCSIZE 1012 /* (255*4) - 8 = 253*32 / 8 */ 55 56 struct mmem_funcdef { /* XXX assuming little-endian structure packing */ 57 unsigned unused : 8, 58 ra : 4, /* number of access / read */ 59 wa : 4, /* number of access / write */ 60 bb : 8, /* block size / 32 - 1 */ 61 pt : 8; /* number of partition - 1 */ 62 }; 63 64 struct mmem_request_read_data { 65 uint32_t func_code; 66 uint8_t pt; 67 uint8_t phase; 68 uint16_t block; 69 }; 70 71 struct mmem_response_read_data { 72 uint32_t func_code; /* function code (big endian) */ 73 uint32_t blkno; /* 512byte block number (big endian) */ 74 uint8_t data[MMEM_MAXACCSIZE]; 75 }; 76 77 struct mmem_request_write_data { 78 uint32_t func_code; 79 uint8_t pt; 80 uint8_t phase; /* 0, 1, 2, 3: for each 128 byte */ 81 uint16_t block; 82 uint8_t data[MMEM_MAXACCSIZE]; 83 }; 84 #define MMEM_SIZE_REQW(sc) ((sc)->sc_waccsz + 8) 85 86 struct mmem_request_get_media_info { 87 uint32_t func_code; 88 uint32_t pt; /* pt (1 byte) and unused 3 bytes */ 89 }; 90 91 struct mmem_media_info { 92 uint16_t maxblk, minblk; 93 uint16_t infpos; 94 uint16_t fatpos, fatsz; 95 uint16_t dirpos, dirsz; 96 uint16_t icon; 97 uint16_t datasz; 98 uint16_t rsvd[3]; 99 }; 100 101 struct mmem_response_media_info { 102 uint32_t func_code; /* function code (big endian) */ 103 struct mmem_media_info info; 104 }; 105 106 struct mmem_softc { 107 device_t sc_dev; 108 109 device_t sc_parent; 110 struct maple_unit *sc_unit; 111 struct maple_devinfo *sc_devinfo; 112 113 enum mmem_stat { 114 MMEM_INIT, /* during initialization */ 115 MMEM_INIT2, /* during initialization */ 116 MMEM_IDLE, /* init done, not in I/O */ 117 MMEM_READ, /* in read operation */ 118 MMEM_WRITE1, /* in write operation (read and compare) */ 119 MMEM_WRITE2, /* in write operation (write) */ 120 MMEM_DETACH /* detaching */ 121 } sc_stat; 122 123 int sc_npt; /* number of partitions */ 124 int sc_bsize; /* block size */ 125 int sc_wacc; /* number of write access per block */ 126 int sc_waccsz; /* size of a write access */ 127 int sc_racc; /* number of read access per block */ 128 int sc_raccsz; /* size of a read access */ 129 130 struct mmem_pt { 131 int pt_flags; 132 #define MMEM_PT_OK 1 /* partition is alive */ 133 struct disk pt_dk; /* disk(9) */ 134 struct mmem_media_info pt_info; /* geometry per part */ 135 136 char pt_name[16 /* see device.h */ + 4 /* ".255" */]; 137 } *sc_pt; 138 139 /* write request buffer (only one is used at a time) */ 140 union { 141 struct mmem_request_read_data req_read; 142 struct mmem_request_write_data req_write; 143 struct mmem_request_get_media_info req_minfo; 144 } sc_req; 145 #define sc_reqr sc_req.req_read 146 #define sc_reqw sc_req.req_write 147 #define sc_reqm sc_req.req_minfo 148 149 /* pending buffers */ 150 struct bufq_state *sc_q; 151 152 /* current I/O access */ 153 struct buf *sc_bp; 154 int sc_cnt; 155 char *sc_iobuf; 156 int sc_retry; 157 #define MMEM_MAXRETRY 12 158 }; 159 160 /* 161 * minor number layout (mmemdetach() depends on this layout): 162 * 163 * 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 164 * |---------------------| |---------------------| |---------| 165 * unit part disklabel partition 166 */ 167 #define MMEM_PART(diskunit) ((diskunit) & 0xff) 168 #define MMEM_UNIT(diskunit) ((diskunit) >> 8) 169 #define MMEM_DISKMINOR(unit, part, disklabel_partition) \ 170 DISKMINOR(((unit) << 8) | (part), (disklabel_partition)) 171 172 static int mmemmatch(device_t, cfdata_t, void *); 173 static void mmemattach(device_t, device_t, void *); 174 static void mmem_defaultlabel(struct mmem_softc *, struct mmem_pt *, 175 struct disklabel *); 176 static int mmemdetach(device_t, int); 177 static void mmem_intr(void *, struct maple_response *, int, int); 178 static void mmem_printerror(const char *, int, int, uint32_t); 179 static void mmemstart(struct mmem_softc *); 180 static void mmemstart_bp(struct mmem_softc *); 181 static void mmemstart_write2(struct mmem_softc *); 182 static void mmemdone(struct mmem_softc *, struct mmem_pt *, int); 183 184 dev_type_open(mmemopen); 185 dev_type_close(mmemclose); 186 dev_type_read(mmemread); 187 dev_type_write(mmemwrite); 188 dev_type_ioctl(mmemioctl); 189 dev_type_strategy(mmemstrategy); 190 191 const struct bdevsw mmem_bdevsw = { 192 .d_open = mmemopen, 193 .d_close = mmemclose, 194 .d_strategy = mmemstrategy, 195 .d_ioctl = mmemioctl, 196 .d_dump = nodump, 197 .d_psize = nosize, 198 .d_flag = D_DISK 199 }; 200 201 const struct cdevsw mmem_cdevsw = { 202 .d_open = mmemopen, 203 .d_close = mmemclose, 204 .d_read = mmemread, 205 .d_write = mmemwrite, 206 .d_ioctl = mmemioctl, 207 .d_stop = nostop, 208 .d_tty = notty, 209 .d_poll = nopoll, 210 .d_mmap = nommap, 211 .d_kqfilter = nokqfilter, 212 .d_flag = D_DISK 213 }; 214 215 CFATTACH_DECL_NEW(mmem, sizeof(struct mmem_softc), 216 mmemmatch, mmemattach, mmemdetach, NULL); 217 218 struct dkdriver mmemdkdriver = { mmemstrategy }; 219 220 static int 221 mmemmatch(device_t parent, cfdata_t cf, void *aux) 222 { 223 struct maple_attach_args *ma = aux; 224 225 return ma->ma_function == MAPLE_FN_MEMCARD ? MAPLE_MATCH_FUNC : 0; 226 } 227 228 static void 229 mmemattach(device_t parent, device_t self, void *aux) 230 { 231 struct mmem_softc *sc = device_private(self); 232 struct maple_attach_args *ma = aux; 233 int i; 234 union { 235 uint32_t v; 236 struct mmem_funcdef s; 237 } funcdef; 238 239 sc->sc_dev = self; 240 sc->sc_parent = parent; 241 sc->sc_unit = ma->ma_unit; 242 sc->sc_devinfo = ma->ma_devinfo; 243 244 funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_MEMCARD); 245 printf(": Memory card\n"); 246 printf("%s: %d part, %d bytes/block, ", 247 device_xname(self), 248 sc->sc_npt = funcdef.s.pt + 1, 249 sc->sc_bsize = (funcdef.s.bb + 1) << 5); 250 if ((sc->sc_wacc = funcdef.s.wa) == 0) 251 printf("no write, "); 252 else 253 printf("%d acc/write, ", sc->sc_wacc); 254 if ((sc->sc_racc = funcdef.s.ra) == 0) 255 printf("no read\n"); 256 else 257 printf("%d acc/read\n", sc->sc_racc); 258 259 /* 260 * start init sequence 261 */ 262 sc->sc_stat = MMEM_INIT; 263 bufq_alloc(&sc->sc_q, "disksort", BUFQ_SORT_RAWBLOCK); 264 265 /* check consistency */ 266 if (sc->sc_wacc != 0) { 267 sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc; 268 if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) { 269 printf("%s: write access isn't equally divided\n", 270 device_xname(self)); 271 sc->sc_wacc = 0; /* no write */ 272 } else if (sc->sc_waccsz > MMEM_MAXACCSIZE) { 273 printf("%s: write access size is too large\n", 274 device_xname(self)); 275 sc->sc_wacc = 0; /* no write */ 276 } 277 } 278 if (sc->sc_racc != 0) { 279 sc->sc_raccsz = sc->sc_bsize / sc->sc_racc; 280 if (sc->sc_bsize != sc->sc_raccsz * sc->sc_racc) { 281 printf("%s: read access isn't equally divided\n", 282 device_xname(self)); 283 sc->sc_racc = 0; /* no read */ 284 } else if (sc->sc_raccsz > MMEM_MAXACCSIZE) { 285 printf("%s: read access size is too large\n", 286 device_xname(self)); 287 sc->sc_racc = 0; /* no read */ 288 } 289 } 290 if (sc->sc_wacc == 0 && sc->sc_racc == 0) { 291 printf("%s: device doesn't support read nor write\n", 292 device_xname(self)); 293 return; 294 } 295 296 /* per-part structure */ 297 sc->sc_pt = malloc(sizeof(struct mmem_pt) * sc->sc_npt, M_DEVBUF, 298 M_WAITOK|M_ZERO); 299 300 for (i = 0; i < sc->sc_npt; i++) { 301 snprintf(sc->sc_pt[i].pt_name, sizeof(sc->sc_pt[i].pt_name), 302 "%s.%d", device_xname(self), i); 303 } 304 305 maple_set_callback(parent, sc->sc_unit, MAPLE_FN_MEMCARD, 306 mmem_intr, sc); 307 308 /* 309 * get capacity (start from partition 0) 310 */ 311 sc->sc_reqm.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 312 sc->sc_reqm.pt = 0; 313 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 314 MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0); 315 } 316 317 static int 318 mmemdetach(device_t self, int flags) 319 { 320 struct mmem_softc *sc = device_private(self); 321 struct buf *bp; 322 int i; 323 int minor_l, minor_h; 324 325 sc->sc_stat = MMEM_DETACH; /* just in case */ 326 327 /* 328 * kill pending I/O 329 */ 330 if ((bp = sc->sc_bp) != NULL) { 331 bp->b_error = EIO; 332 bp->b_resid = bp->b_bcount; 333 biodone(bp); 334 } 335 while ((bp = bufq_get(sc->sc_q)) != NULL) { 336 bp->b_error = EIO; 337 bp->b_resid = bp->b_bcount; 338 biodone(bp); 339 } 340 bufq_free(sc->sc_q); 341 342 /* 343 * revoke vnodes 344 */ 345 #ifdef __HAVE_OLD_DISKLABEL 346 #error This code assumes DISKUNIT() is contiguous in minor number. 347 #endif 348 minor_l = MMEM_DISKMINOR(device_unit(self), 0, 0); 349 minor_h = MMEM_DISKMINOR(device_unit(self), sc->sc_npt - 1, 350 MAXPARTITIONS - 1); 351 vdevgone(bdevsw_lookup_major(&mmem_bdevsw), minor_l, minor_h, VBLK); 352 vdevgone(cdevsw_lookup_major(&mmem_cdevsw), minor_l, minor_h, VCHR); 353 354 /* 355 * free per-partition structure 356 */ 357 if (sc->sc_pt) { 358 /* 359 * detach disks 360 */ 361 for (i = 0; i < sc->sc_npt; i++) { 362 if (sc->sc_pt[i].pt_flags & MMEM_PT_OK) { 363 disk_detach(&sc->sc_pt[i].pt_dk); 364 disk_destroy(&sc->sc_pt[i].pt_dk); 365 } 366 } 367 free(sc->sc_pt, M_DEVBUF); 368 } 369 370 return 0; 371 } 372 373 /* fake disklabel */ 374 static void 375 mmem_defaultlabel(struct mmem_softc *sc, struct mmem_pt *pt, 376 struct disklabel *d) 377 { 378 379 memset(d, 0, sizeof *d); 380 381 #if 0 382 d->d_type = DTYPE_FLOPPY; /* XXX? */ 383 #endif 384 strncpy(d->d_typename, sc->sc_devinfo->di_product_name, 385 sizeof d->d_typename); 386 strcpy(d->d_packname, "fictitious"); 387 d->d_secsize = sc->sc_bsize; 388 d->d_ntracks = 1; /* XXX */ 389 d->d_nsectors = d->d_secpercyl = 8; /* XXX */ 390 d->d_secperunit = pt->pt_info.maxblk - pt->pt_info.minblk + 1; 391 d->d_ncylinders = d->d_secperunit / d->d_secpercyl; 392 d->d_rpm = 1; /* when 4 acc/write */ 393 394 d->d_npartitions = RAW_PART + 1; 395 d->d_partitions[RAW_PART].p_size = d->d_secperunit; 396 397 d->d_magic = d->d_magic2 = DISKMAGIC; 398 d->d_checksum = dkcksum(d); 399 } 400 401 /* 402 * called back from maple bus driver 403 */ 404 static void 405 mmem_intr(void *arg, struct maple_response *response, int sz, int flags) 406 { 407 struct mmem_softc *sc = arg; 408 struct mmem_response_read_data *r = (void *) response->data; 409 struct mmem_response_media_info *rm = (void *) response->data; 410 struct buf *bp; 411 int part; 412 struct mmem_pt *pt; 413 char pbuf[9]; 414 int off; 415 416 switch (sc->sc_stat) { 417 case MMEM_INIT: 418 /* checking part geometry */ 419 part = sc->sc_reqm.pt; 420 pt = &sc->sc_pt[part]; 421 switch ((maple_response_t) response->response_code) { 422 case MAPLE_RESPONSE_DATATRF: 423 pt->pt_info = rm->info; 424 format_bytes(pbuf, sizeof(pbuf), 425 (uint64_t) 426 ((pt->pt_info.maxblk - pt->pt_info.minblk + 1) 427 * sc->sc_bsize)); 428 printf("%s: %s, blk %d %d, inf %d, fat %d %d, dir %d %d, icon %d, data %d\n", 429 pt->pt_name, 430 pbuf, 431 pt->pt_info.maxblk, pt->pt_info.minblk, 432 pt->pt_info.infpos, 433 pt->pt_info.fatpos, pt->pt_info.fatsz, 434 pt->pt_info.dirpos, pt->pt_info.dirsz, 435 pt->pt_info.icon, 436 pt->pt_info.datasz); 437 438 disk_init(&pt->pt_dk, pt->pt_name, &mmemdkdriver); 439 disk_attach(&pt->pt_dk); 440 441 mmem_defaultlabel(sc, pt, pt->pt_dk.dk_label); 442 443 /* this partition is active */ 444 pt->pt_flags = MMEM_PT_OK; 445 446 break; 447 default: 448 printf("%s: init: unexpected response %#x, sz %d\n", 449 pt->pt_name, be32toh(response->response_code), sz); 450 break; 451 } 452 if (++part == sc->sc_npt) { 453 #if 1 454 /* 455 * XXX Read a block and discard the contents (only to 456 * turn off the access indicator on Visual Memory). 457 */ 458 pt = &sc->sc_pt[0]; 459 sc->sc_reqr.func_code = 460 htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 461 sc->sc_reqr.pt = 0; 462 sc->sc_reqr.block = htobe16(pt->pt_info.minblk); 463 sc->sc_reqr.phase = 0; 464 maple_command(sc->sc_parent, sc->sc_unit, 465 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 466 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 467 sc->sc_stat = MMEM_INIT2; 468 #else 469 sc->sc_stat = MMEM_IDLE; /* init done */ 470 #endif 471 } else { 472 sc->sc_reqm.pt = part; 473 maple_command(sc->sc_parent, sc->sc_unit, 474 MAPLE_FN_MEMCARD, MAPLE_COMMAND_GETMINFO, 475 sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0); 476 } 477 break; 478 479 case MMEM_INIT2: 480 /* XXX just discard */ 481 sc->sc_stat = MMEM_IDLE; /* init done */ 482 break; 483 484 case MMEM_READ: 485 bp = sc->sc_bp; 486 487 switch ((maple_response_t) response->response_code) { 488 case MAPLE_RESPONSE_DATATRF: /* read done */ 489 off = sc->sc_raccsz * sc->sc_reqr.phase; 490 memcpy(sc->sc_iobuf + off, r->data + off, 491 sc->sc_raccsz); 492 493 if (++sc->sc_reqr.phase == sc->sc_racc) { 494 /* all phase done */ 495 pt = &sc->sc_pt[sc->sc_reqr.pt]; 496 mmemdone(sc, pt, 0); 497 } else { 498 /* go next phase */ 499 maple_command(sc->sc_parent, sc->sc_unit, 500 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 501 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 502 } 503 break; 504 case MAPLE_RESPONSE_FILEERR: 505 mmem_printerror(sc->sc_pt[sc->sc_reqr.pt].pt_name, 506 1, bp->b_rawblkno, 507 r->func_code /* XXX */); 508 mmemstart_bp(sc); /* retry */ 509 break; 510 default: 511 printf("%s: read: unexpected response %#x %#x, sz %d\n", 512 sc->sc_pt[sc->sc_reqr.pt].pt_name, 513 be32toh(response->response_code), 514 be32toh(r->func_code), sz); 515 mmemstart_bp(sc); /* retry */ 516 break; 517 } 518 break; 519 520 case MMEM_WRITE1: /* read before write / verify after write */ 521 bp = sc->sc_bp; 522 523 switch ((maple_response_t) response->response_code) { 524 case MAPLE_RESPONSE_DATATRF: /* read done */ 525 off = sc->sc_raccsz * sc->sc_reqr.phase; 526 if (memcmp(r->data + off, sc->sc_iobuf + off, 527 sc->sc_raccsz)) { 528 /* 529 * data differ, start writing 530 */ 531 mmemstart_write2(sc); 532 } else if (++sc->sc_reqr.phase == sc->sc_racc) { 533 /* 534 * all phase done and compared equal 535 */ 536 pt = &sc->sc_pt[sc->sc_reqr.pt]; 537 mmemdone(sc, pt, 0); 538 } else { 539 /* go next phase */ 540 maple_command(sc->sc_parent, sc->sc_unit, 541 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 542 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 543 } 544 break; 545 case MAPLE_RESPONSE_FILEERR: 546 mmem_printerror(sc->sc_pt[sc->sc_reqr.pt].pt_name, 547 1, bp->b_rawblkno, 548 r->func_code /* XXX */); 549 mmemstart_write2(sc); /* start writing */ 550 break; 551 default: 552 printf("%s: verify: unexpected response %#x %#x, sz %d\n", 553 sc->sc_pt[sc->sc_reqr.pt].pt_name, 554 be32toh(response->response_code), 555 be32toh(r->func_code), sz); 556 mmemstart_write2(sc); /* start writing */ 557 break; 558 } 559 break; 560 561 case MMEM_WRITE2: /* write */ 562 bp = sc->sc_bp; 563 564 switch ((maple_response_t) response->response_code) { 565 case MAPLE_RESPONSE_OK: /* write done */ 566 if (sc->sc_reqw.phase == sc->sc_wacc) { 567 /* all phase done */ 568 mmemstart_bp(sc); /* start verify */ 569 } else if (++sc->sc_reqw.phase == sc->sc_wacc) { 570 /* check error */ 571 maple_command(sc->sc_parent, sc->sc_unit, 572 MAPLE_FN_MEMCARD, MAPLE_COMMAND_GETLASTERR, 573 2 /* no data */ , &sc->sc_reqw, 574 MAPLE_FLAG_CMD_PERIODIC_TIMING); 575 } else { 576 /* go next phase */ 577 memcpy(sc->sc_reqw.data, sc->sc_iobuf + 578 sc->sc_waccsz * sc->sc_reqw.phase, 579 sc->sc_waccsz); 580 maple_command(sc->sc_parent, sc->sc_unit, 581 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BWRITE, 582 MMEM_SIZE_REQW(sc) / 4, &sc->sc_reqw, 583 MAPLE_FLAG_CMD_PERIODIC_TIMING); 584 } 585 break; 586 case MAPLE_RESPONSE_FILEERR: 587 mmem_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name, 588 0, bp->b_rawblkno, 589 r->func_code /* XXX */); 590 mmemstart_write2(sc); /* retry writing */ 591 break; 592 default: 593 printf("%s: write: unexpected response %#x, %#x, sz %d\n", 594 sc->sc_pt[sc->sc_reqw.pt].pt_name, 595 be32toh(response->response_code), 596 be32toh(r->func_code), sz); 597 mmemstart_write2(sc); /* retry writing */ 598 break; 599 } 600 break; 601 602 default: 603 break; 604 } 605 } 606 607 static void 608 mmem_printerror(const char *head, int rd, int blk, uint32_t code) 609 { 610 611 printf("%s: error %sing blk %d:", head, rd? "read" : "writ", blk); 612 NTOHL(code); 613 if (code & 1) 614 printf(" PT error"); 615 if (code & 2) 616 printf(" Phase error"); 617 if (code & 4) 618 printf(" Block error"); 619 if (code & 010) 620 printf(" Write error"); 621 if (code & 020) 622 printf(" Length error"); 623 if (code & 040) 624 printf(" CRC error"); 625 if (code & ~077) 626 printf(" Unknown error %#x", code & ~077); 627 printf("\n"); 628 } 629 630 int 631 mmemopen(dev_t dev, int flags, int devtype, struct lwp *l) 632 { 633 int diskunit, unit, part, labelpart; 634 struct mmem_softc *sc; 635 struct mmem_pt *pt; 636 637 diskunit = DISKUNIT(dev); 638 unit = MMEM_UNIT(diskunit); 639 part = MMEM_PART(diskunit); 640 labelpart = DISKPART(dev); 641 if ((sc = device_lookup_private(&mmem_cd, unit)) == NULL 642 || sc->sc_stat == MMEM_INIT 643 || sc->sc_stat == MMEM_INIT2 644 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0) 645 return ENXIO; 646 647 switch (devtype) { 648 case S_IFCHR: 649 pt->pt_dk.dk_copenmask |= (1 << labelpart); 650 break; 651 case S_IFBLK: 652 pt->pt_dk.dk_bopenmask |= (1 << labelpart); 653 break; 654 } 655 656 return 0; 657 } 658 659 int 660 mmemclose(dev_t dev, int flags, int devtype, struct lwp *l) 661 { 662 int diskunit, unit, part, labelpart; 663 struct mmem_softc *sc; 664 struct mmem_pt *pt; 665 666 diskunit = DISKUNIT(dev); 667 unit = MMEM_UNIT(diskunit); 668 part = MMEM_PART(diskunit); 669 sc = device_lookup_private(&mmem_cd, unit); 670 pt = &sc->sc_pt[part]; 671 labelpart = DISKPART(dev); 672 673 switch (devtype) { 674 case S_IFCHR: 675 pt->pt_dk.dk_copenmask &= ~(1 << labelpart); 676 break; 677 case S_IFBLK: 678 pt->pt_dk.dk_bopenmask &= ~(1 << labelpart); 679 break; 680 } 681 682 return 0; 683 } 684 685 void 686 mmemstrategy(struct buf *bp) 687 { 688 int diskunit, unit, part, labelpart; 689 struct mmem_softc *sc; 690 struct mmem_pt *pt; 691 daddr_t off, nblk, cnt; 692 693 diskunit = DISKUNIT(bp->b_dev); 694 unit = MMEM_UNIT(diskunit); 695 part = MMEM_PART(diskunit); 696 if ((sc = device_lookup_private(&mmem_cd, unit)) == NULL 697 || sc->sc_stat == MMEM_INIT 698 || sc->sc_stat == MMEM_INIT2 699 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0) 700 goto inval; 701 702 #if 0 703 printf("%s: mmemstrategy: blkno %d, count %ld\n", 704 pt->pt_name, bp->b_blkno, bp->b_bcount); 705 #endif 706 707 if (bp->b_flags & B_READ) { 708 if (sc->sc_racc == 0) 709 goto inval; /* no read */ 710 } else if (sc->sc_wacc == 0) { 711 bp->b_error = EROFS; /* no write */ 712 goto done; 713 } 714 715 if (bp->b_blkno & ~(~(daddr_t)0 >> (DEV_BSHIFT + 1 /* sign bit */)) 716 || (bp->b_bcount % sc->sc_bsize) != 0) 717 goto inval; 718 719 cnt = howmany(bp->b_bcount, sc->sc_bsize); 720 if (cnt == 0) 721 goto done; /* no work */ 722 723 off = bp->b_blkno * DEV_BSIZE / sc->sc_bsize; 724 725 /* offset to disklabel partition */ 726 labelpart = DISKPART(bp->b_dev); 727 if (labelpart == RAW_PART) { 728 nblk = pt->pt_info.maxblk - pt->pt_info.minblk + 1; 729 } else { 730 off += 731 nblk = pt->pt_dk.dk_label->d_partitions[labelpart].p_offset; 732 nblk += pt->pt_dk.dk_label->d_partitions[labelpart].p_size; 733 } 734 735 /* deal with the EOF condition */ 736 if (off + cnt > nblk) { 737 if (off >= nblk) { 738 if (off == nblk) 739 goto done; 740 goto inval; 741 } 742 cnt = nblk - off; 743 bp->b_resid = bp->b_bcount - (cnt * sc->sc_bsize); 744 } 745 746 bp->b_rawblkno = off; 747 748 /* queue this transfer */ 749 bufq_put(sc->sc_q, bp); 750 751 if (sc->sc_stat == MMEM_IDLE) 752 mmemstart(sc); 753 754 return; 755 756 inval: bp->b_error = EINVAL; 757 done: bp->b_resid = bp->b_bcount; 758 biodone(bp); 759 } 760 761 /* 762 * start I/O operations 763 */ 764 static void 765 mmemstart(struct mmem_softc *sc) 766 { 767 struct buf *bp; 768 struct mmem_pt *pt; 769 int s; 770 771 if ((bp = bufq_get(sc->sc_q)) == NULL) { 772 sc->sc_stat = MMEM_IDLE; 773 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, 774 MAPLE_FN_MEMCARD, 1); 775 return; 776 } 777 778 sc->sc_bp = bp; 779 sc->sc_cnt = howmany(bp->b_bcount - bp->b_resid, sc->sc_bsize); 780 KASSERT(sc->sc_cnt); 781 sc->sc_iobuf = bp->b_data; 782 sc->sc_retry = 0; 783 784 pt = &sc->sc_pt[MMEM_PART(DISKUNIT(bp->b_dev))]; 785 s = splbio(); 786 disk_busy(&pt->pt_dk); 787 splx(s); 788 789 /* 790 * I/O access will fail if the removal detection (by maple driver) 791 * occurs before finishing the I/O, so disable it. 792 * We are sending commands, and the removal detection is still alive. 793 */ 794 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 0); 795 796 mmemstart_bp(sc); 797 } 798 799 /* 800 * start/retry a specified I/O operation 801 */ 802 static void 803 mmemstart_bp(struct mmem_softc *sc) 804 { 805 struct buf *bp; 806 int diskunit, part; 807 struct mmem_pt *pt; 808 809 bp = sc->sc_bp; 810 diskunit = DISKUNIT(bp->b_dev); 811 part = MMEM_PART(diskunit); 812 pt = &sc->sc_pt[part]; 813 814 /* handle retry */ 815 if (sc->sc_retry++ > MMEM_MAXRETRY) { 816 /* retry count exceeded */ 817 mmemdone(sc, pt, EIO); 818 return; 819 } 820 821 /* 822 * Start the first phase (phase# = 0). 823 */ 824 /* start read */ 825 sc->sc_stat = (bp->b_flags & B_READ) ? MMEM_READ : MMEM_WRITE1; 826 sc->sc_reqr.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 827 sc->sc_reqr.pt = part; 828 sc->sc_reqr.block = htobe16(bp->b_rawblkno); 829 sc->sc_reqr.phase = 0; /* first phase */ 830 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 831 MAPLE_COMMAND_BREAD, sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 832 } 833 834 static void 835 mmemstart_write2(struct mmem_softc *sc) 836 { 837 struct buf *bp; 838 int diskunit, part; 839 struct mmem_pt *pt; 840 841 bp = sc->sc_bp; 842 diskunit = DISKUNIT(bp->b_dev); 843 part = MMEM_PART(diskunit); 844 pt = &sc->sc_pt[part]; 845 846 /* handle retry */ 847 if (sc->sc_retry++ > MMEM_MAXRETRY - 2 /* spare for verify read */) { 848 /* retry count exceeded */ 849 mmemdone(sc, pt, EIO); 850 return; 851 } 852 853 /* 854 * Start the first phase (phase# = 0). 855 */ 856 /* start write */ 857 sc->sc_stat = MMEM_WRITE2; 858 sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 859 sc->sc_reqw.pt = part; 860 sc->sc_reqw.block = htobe16(bp->b_rawblkno); 861 sc->sc_reqw.phase = 0; /* first phase */ 862 memcpy(sc->sc_reqw.data, sc->sc_iobuf /* + sc->sc_waccsz * phase */, 863 sc->sc_waccsz); 864 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 865 MAPLE_COMMAND_BWRITE, MMEM_SIZE_REQW(sc) / 4, &sc->sc_reqw, 866 MAPLE_FLAG_CMD_PERIODIC_TIMING); 867 } 868 869 static void 870 mmemdone(struct mmem_softc *sc, struct mmem_pt *pt, int err) 871 { 872 struct buf *bp = sc->sc_bp; 873 int s; 874 int bcnt; 875 876 KASSERT(bp); 877 878 if (err) { 879 bcnt = (char *)sc->sc_iobuf - (char *)bp->b_data; 880 bp->b_resid = bp->b_bcount - bcnt; 881 882 /* raise error if no block is read */ 883 if (bcnt == 0) { 884 bp->b_error = err; 885 } 886 goto term_xfer; 887 } 888 889 sc->sc_iobuf += sc->sc_bsize; 890 if (--sc->sc_cnt == 0) { 891 term_xfer: 892 /* terminate current transfer */ 893 sc->sc_bp = NULL; 894 s = splbio(); 895 disk_unbusy(&pt->pt_dk, 896 (char *)sc->sc_iobuf - (char *)bp->b_data, 897 sc->sc_stat == MMEM_READ); 898 biodone(bp); 899 splx(s); 900 901 /* go next transfer */ 902 mmemstart(sc); 903 } else { 904 /* go next block */ 905 bp->b_rawblkno++; 906 sc->sc_retry = 0; 907 mmemstart_bp(sc); 908 } 909 } 910 911 int 912 mmemread(dev_t dev, struct uio *uio, int flags) 913 { 914 915 return physio(mmemstrategy, NULL, dev, B_READ, minphys, uio); 916 } 917 918 int 919 mmemwrite(dev_t dev, struct uio *uio, int flags) 920 { 921 922 return physio(mmemstrategy, NULL, dev, B_WRITE, minphys, uio); 923 } 924 925 int 926 mmemioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 927 { 928 int diskunit, unit, part; 929 struct mmem_softc *sc; 930 struct mmem_pt *pt; 931 932 diskunit = DISKUNIT(dev); 933 unit = MMEM_UNIT(diskunit); 934 part = MMEM_PART(diskunit); 935 sc = device_lookup_private(&mmem_cd, unit); 936 pt = &sc->sc_pt[part]; 937 938 switch (cmd) { 939 case DIOCGDINFO: 940 *(struct disklabel *)data = *pt->pt_dk.dk_label; /* XXX */ 941 break; 942 943 default: 944 /* generic maple ioctl */ 945 return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data, 946 flag, l); 947 } 948 949 return 0; 950 } 951