1 /* $NetBSD: mmemcard.c,v 1.26 2015/04/26 15:15:19 mlelstv 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.26 2015/04/26 15:15:19 mlelstv 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_discard = nodiscard, 199 .d_flag = D_DISK 200 }; 201 202 const struct cdevsw mmem_cdevsw = { 203 .d_open = mmemopen, 204 .d_close = mmemclose, 205 .d_read = mmemread, 206 .d_write = mmemwrite, 207 .d_ioctl = mmemioctl, 208 .d_stop = nostop, 209 .d_tty = notty, 210 .d_poll = nopoll, 211 .d_mmap = nommap, 212 .d_kqfilter = nokqfilter, 213 .d_discard = nodiscard, 214 .d_flag = D_DISK 215 }; 216 217 CFATTACH_DECL_NEW(mmem, sizeof(struct mmem_softc), 218 mmemmatch, mmemattach, mmemdetach, NULL); 219 220 struct dkdriver mmemdkdriver = { 221 .d_strategy = mmemstrategy 222 }; 223 224 static int 225 mmemmatch(device_t parent, cfdata_t cf, void *aux) 226 { 227 struct maple_attach_args *ma = aux; 228 229 return ma->ma_function == MAPLE_FN_MEMCARD ? MAPLE_MATCH_FUNC : 0; 230 } 231 232 static void 233 mmemattach(device_t parent, device_t self, void *aux) 234 { 235 struct mmem_softc *sc = device_private(self); 236 struct maple_attach_args *ma = aux; 237 int i; 238 union { 239 uint32_t v; 240 struct mmem_funcdef s; 241 } funcdef; 242 243 sc->sc_dev = self; 244 sc->sc_parent = parent; 245 sc->sc_unit = ma->ma_unit; 246 sc->sc_devinfo = ma->ma_devinfo; 247 248 funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_MEMCARD); 249 printf(": Memory card\n"); 250 printf("%s: %d part, %d bytes/block, ", 251 device_xname(self), 252 sc->sc_npt = funcdef.s.pt + 1, 253 sc->sc_bsize = (funcdef.s.bb + 1) << 5); 254 if ((sc->sc_wacc = funcdef.s.wa) == 0) 255 printf("no write, "); 256 else 257 printf("%d acc/write, ", sc->sc_wacc); 258 if ((sc->sc_racc = funcdef.s.ra) == 0) 259 printf("no read\n"); 260 else 261 printf("%d acc/read\n", sc->sc_racc); 262 263 /* 264 * start init sequence 265 */ 266 sc->sc_stat = MMEM_INIT; 267 bufq_alloc(&sc->sc_q, "disksort", BUFQ_SORT_RAWBLOCK); 268 269 /* check consistency */ 270 if (sc->sc_wacc != 0) { 271 sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc; 272 if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) { 273 printf("%s: write access isn't equally divided\n", 274 device_xname(self)); 275 sc->sc_wacc = 0; /* no write */ 276 } else if (sc->sc_waccsz > MMEM_MAXACCSIZE) { 277 printf("%s: write access size is too large\n", 278 device_xname(self)); 279 sc->sc_wacc = 0; /* no write */ 280 } 281 } 282 if (sc->sc_racc != 0) { 283 sc->sc_raccsz = sc->sc_bsize / sc->sc_racc; 284 if (sc->sc_bsize != sc->sc_raccsz * sc->sc_racc) { 285 printf("%s: read access isn't equally divided\n", 286 device_xname(self)); 287 sc->sc_racc = 0; /* no read */ 288 } else if (sc->sc_raccsz > MMEM_MAXACCSIZE) { 289 printf("%s: read access size is too large\n", 290 device_xname(self)); 291 sc->sc_racc = 0; /* no read */ 292 } 293 } 294 if (sc->sc_wacc == 0 && sc->sc_racc == 0) { 295 printf("%s: device doesn't support read nor write\n", 296 device_xname(self)); 297 return; 298 } 299 300 /* per-part structure */ 301 sc->sc_pt = malloc(sizeof(struct mmem_pt) * sc->sc_npt, M_DEVBUF, 302 M_WAITOK|M_ZERO); 303 304 for (i = 0; i < sc->sc_npt; i++) { 305 snprintf(sc->sc_pt[i].pt_name, sizeof(sc->sc_pt[i].pt_name), 306 "%s.%d", device_xname(self), i); 307 } 308 309 maple_set_callback(parent, sc->sc_unit, MAPLE_FN_MEMCARD, 310 mmem_intr, sc); 311 312 /* 313 * get capacity (start from partition 0) 314 */ 315 sc->sc_reqm.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 316 sc->sc_reqm.pt = 0; 317 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 318 MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0); 319 } 320 321 static int 322 mmemdetach(device_t self, int flags) 323 { 324 struct mmem_softc *sc = device_private(self); 325 struct buf *bp; 326 int i; 327 int minor_l, minor_h; 328 329 sc->sc_stat = MMEM_DETACH; /* just in case */ 330 331 /* 332 * kill pending I/O 333 */ 334 if ((bp = sc->sc_bp) != NULL) { 335 bp->b_error = EIO; 336 bp->b_resid = bp->b_bcount; 337 biodone(bp); 338 } 339 while ((bp = bufq_get(sc->sc_q)) != NULL) { 340 bp->b_error = EIO; 341 bp->b_resid = bp->b_bcount; 342 biodone(bp); 343 } 344 bufq_free(sc->sc_q); 345 346 /* 347 * revoke vnodes 348 */ 349 #ifdef __HAVE_OLD_DISKLABEL 350 #error This code assumes DISKUNIT() is contiguous in minor number. 351 #endif 352 minor_l = MMEM_DISKMINOR(device_unit(self), 0, 0); 353 minor_h = MMEM_DISKMINOR(device_unit(self), sc->sc_npt - 1, 354 MAXPARTITIONS - 1); 355 vdevgone(bdevsw_lookup_major(&mmem_bdevsw), minor_l, minor_h, VBLK); 356 vdevgone(cdevsw_lookup_major(&mmem_cdevsw), minor_l, minor_h, VCHR); 357 358 /* 359 * free per-partition structure 360 */ 361 if (sc->sc_pt) { 362 /* 363 * detach disks 364 */ 365 for (i = 0; i < sc->sc_npt; i++) { 366 if (sc->sc_pt[i].pt_flags & MMEM_PT_OK) { 367 disk_detach(&sc->sc_pt[i].pt_dk); 368 disk_destroy(&sc->sc_pt[i].pt_dk); 369 } 370 } 371 free(sc->sc_pt, M_DEVBUF); 372 } 373 374 return 0; 375 } 376 377 /* fake disklabel */ 378 static void 379 mmem_defaultlabel(struct mmem_softc *sc, struct mmem_pt *pt, 380 struct disklabel *d) 381 { 382 383 memset(d, 0, sizeof *d); 384 385 #if 0 386 d->d_type = DKTYPE_FLOPPY; /* XXX? */ 387 #endif 388 strncpy(d->d_typename, sc->sc_devinfo->di_product_name, 389 sizeof d->d_typename); 390 strcpy(d->d_packname, "fictitious"); 391 d->d_secsize = sc->sc_bsize; 392 d->d_ntracks = 1; /* XXX */ 393 d->d_nsectors = d->d_secpercyl = 8; /* XXX */ 394 d->d_secperunit = pt->pt_info.maxblk - pt->pt_info.minblk + 1; 395 d->d_ncylinders = d->d_secperunit / d->d_secpercyl; 396 d->d_rpm = 1; /* when 4 acc/write */ 397 398 d->d_npartitions = RAW_PART + 1; 399 d->d_partitions[RAW_PART].p_size = d->d_secperunit; 400 401 d->d_magic = d->d_magic2 = DISKMAGIC; 402 d->d_checksum = dkcksum(d); 403 } 404 405 /* 406 * called back from maple bus driver 407 */ 408 static void 409 mmem_intr(void *arg, struct maple_response *response, int sz, int flags) 410 { 411 struct mmem_softc *sc = arg; 412 struct mmem_response_read_data *r = (void *) response->data; 413 struct mmem_response_media_info *rm = (void *) response->data; 414 struct buf *bp; 415 int part; 416 struct mmem_pt *pt; 417 char pbuf[9]; 418 int off; 419 420 switch (sc->sc_stat) { 421 case MMEM_INIT: 422 /* checking part geometry */ 423 part = sc->sc_reqm.pt; 424 pt = &sc->sc_pt[part]; 425 switch ((maple_response_t) response->response_code) { 426 case MAPLE_RESPONSE_DATATRF: 427 pt->pt_info = rm->info; 428 format_bytes(pbuf, sizeof(pbuf), 429 (uint64_t) 430 ((pt->pt_info.maxblk - pt->pt_info.minblk + 1) 431 * sc->sc_bsize)); 432 printf("%s: %s, blk %d %d, inf %d, fat %d %d, dir %d %d, icon %d, data %d\n", 433 pt->pt_name, 434 pbuf, 435 pt->pt_info.maxblk, pt->pt_info.minblk, 436 pt->pt_info.infpos, 437 pt->pt_info.fatpos, pt->pt_info.fatsz, 438 pt->pt_info.dirpos, pt->pt_info.dirsz, 439 pt->pt_info.icon, 440 pt->pt_info.datasz); 441 442 disk_init(&pt->pt_dk, pt->pt_name, &mmemdkdriver); 443 disk_attach(&pt->pt_dk); 444 445 mmem_defaultlabel(sc, pt, pt->pt_dk.dk_label); 446 447 /* this partition is active */ 448 pt->pt_flags = MMEM_PT_OK; 449 450 break; 451 default: 452 printf("%s: init: unexpected response %#x, sz %d\n", 453 pt->pt_name, be32toh(response->response_code), sz); 454 break; 455 } 456 if (++part == sc->sc_npt) { 457 #if 1 458 /* 459 * XXX Read a block and discard the contents (only to 460 * turn off the access indicator on Visual Memory). 461 */ 462 pt = &sc->sc_pt[0]; 463 sc->sc_reqr.func_code = 464 htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 465 sc->sc_reqr.pt = 0; 466 sc->sc_reqr.block = htobe16(pt->pt_info.minblk); 467 sc->sc_reqr.phase = 0; 468 maple_command(sc->sc_parent, sc->sc_unit, 469 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 470 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 471 sc->sc_stat = MMEM_INIT2; 472 #else 473 sc->sc_stat = MMEM_IDLE; /* init done */ 474 #endif 475 } else { 476 sc->sc_reqm.pt = part; 477 maple_command(sc->sc_parent, sc->sc_unit, 478 MAPLE_FN_MEMCARD, MAPLE_COMMAND_GETMINFO, 479 sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0); 480 } 481 break; 482 483 case MMEM_INIT2: 484 /* XXX just discard */ 485 sc->sc_stat = MMEM_IDLE; /* init done */ 486 break; 487 488 case MMEM_READ: 489 bp = sc->sc_bp; 490 491 switch ((maple_response_t) response->response_code) { 492 case MAPLE_RESPONSE_DATATRF: /* read done */ 493 off = sc->sc_raccsz * sc->sc_reqr.phase; 494 memcpy(sc->sc_iobuf + off, r->data + off, 495 sc->sc_raccsz); 496 497 if (++sc->sc_reqr.phase == sc->sc_racc) { 498 /* all phase done */ 499 pt = &sc->sc_pt[sc->sc_reqr.pt]; 500 mmemdone(sc, pt, 0); 501 } else { 502 /* go next phase */ 503 maple_command(sc->sc_parent, sc->sc_unit, 504 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 505 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 506 } 507 break; 508 case MAPLE_RESPONSE_FILEERR: 509 mmem_printerror(sc->sc_pt[sc->sc_reqr.pt].pt_name, 510 1, bp->b_rawblkno, 511 r->func_code /* XXX */); 512 mmemstart_bp(sc); /* retry */ 513 break; 514 default: 515 printf("%s: read: unexpected response %#x %#x, sz %d\n", 516 sc->sc_pt[sc->sc_reqr.pt].pt_name, 517 be32toh(response->response_code), 518 be32toh(r->func_code), sz); 519 mmemstart_bp(sc); /* retry */ 520 break; 521 } 522 break; 523 524 case MMEM_WRITE1: /* read before write / verify after write */ 525 bp = sc->sc_bp; 526 527 switch ((maple_response_t) response->response_code) { 528 case MAPLE_RESPONSE_DATATRF: /* read done */ 529 off = sc->sc_raccsz * sc->sc_reqr.phase; 530 if (memcmp(r->data + off, sc->sc_iobuf + off, 531 sc->sc_raccsz)) { 532 /* 533 * data differ, start writing 534 */ 535 mmemstart_write2(sc); 536 } else if (++sc->sc_reqr.phase == sc->sc_racc) { 537 /* 538 * all phase done and compared equal 539 */ 540 pt = &sc->sc_pt[sc->sc_reqr.pt]; 541 mmemdone(sc, pt, 0); 542 } else { 543 /* go next phase */ 544 maple_command(sc->sc_parent, sc->sc_unit, 545 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 546 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 547 } 548 break; 549 case MAPLE_RESPONSE_FILEERR: 550 mmem_printerror(sc->sc_pt[sc->sc_reqr.pt].pt_name, 551 1, bp->b_rawblkno, 552 r->func_code /* XXX */); 553 mmemstart_write2(sc); /* start writing */ 554 break; 555 default: 556 printf("%s: verify: unexpected response %#x %#x, sz %d\n", 557 sc->sc_pt[sc->sc_reqr.pt].pt_name, 558 be32toh(response->response_code), 559 be32toh(r->func_code), sz); 560 mmemstart_write2(sc); /* start writing */ 561 break; 562 } 563 break; 564 565 case MMEM_WRITE2: /* write */ 566 bp = sc->sc_bp; 567 568 switch ((maple_response_t) response->response_code) { 569 case MAPLE_RESPONSE_OK: /* write done */ 570 if (sc->sc_reqw.phase == sc->sc_wacc) { 571 /* all phase done */ 572 mmemstart_bp(sc); /* start verify */ 573 } else if (++sc->sc_reqw.phase == sc->sc_wacc) { 574 /* check error */ 575 maple_command(sc->sc_parent, sc->sc_unit, 576 MAPLE_FN_MEMCARD, MAPLE_COMMAND_GETLASTERR, 577 2 /* no data */ , &sc->sc_reqw, 578 MAPLE_FLAG_CMD_PERIODIC_TIMING); 579 } else { 580 /* go next phase */ 581 memcpy(sc->sc_reqw.data, sc->sc_iobuf + 582 sc->sc_waccsz * sc->sc_reqw.phase, 583 sc->sc_waccsz); 584 maple_command(sc->sc_parent, sc->sc_unit, 585 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BWRITE, 586 MMEM_SIZE_REQW(sc) / 4, &sc->sc_reqw, 587 MAPLE_FLAG_CMD_PERIODIC_TIMING); 588 } 589 break; 590 case MAPLE_RESPONSE_FILEERR: 591 mmem_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name, 592 0, bp->b_rawblkno, 593 r->func_code /* XXX */); 594 mmemstart_write2(sc); /* retry writing */ 595 break; 596 default: 597 printf("%s: write: unexpected response %#x, %#x, sz %d\n", 598 sc->sc_pt[sc->sc_reqw.pt].pt_name, 599 be32toh(response->response_code), 600 be32toh(r->func_code), sz); 601 mmemstart_write2(sc); /* retry writing */ 602 break; 603 } 604 break; 605 606 default: 607 break; 608 } 609 } 610 611 static void 612 mmem_printerror(const char *head, int rd, int blk, uint32_t code) 613 { 614 615 printf("%s: error %sing blk %d:", head, rd? "read" : "writ", blk); 616 NTOHL(code); 617 if (code & 1) 618 printf(" PT error"); 619 if (code & 2) 620 printf(" Phase error"); 621 if (code & 4) 622 printf(" Block error"); 623 if (code & 010) 624 printf(" Write error"); 625 if (code & 020) 626 printf(" Length error"); 627 if (code & 040) 628 printf(" CRC error"); 629 if (code & ~077) 630 printf(" Unknown error %#x", code & ~077); 631 printf("\n"); 632 } 633 634 int 635 mmemopen(dev_t dev, int flags, int devtype, struct lwp *l) 636 { 637 int diskunit, unit, part, labelpart; 638 struct mmem_softc *sc; 639 struct mmem_pt *pt; 640 641 diskunit = DISKUNIT(dev); 642 unit = MMEM_UNIT(diskunit); 643 part = MMEM_PART(diskunit); 644 labelpart = DISKPART(dev); 645 if ((sc = device_lookup_private(&mmem_cd, unit)) == NULL 646 || sc->sc_stat == MMEM_INIT 647 || sc->sc_stat == MMEM_INIT2 648 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0) 649 return ENXIO; 650 651 switch (devtype) { 652 case S_IFCHR: 653 pt->pt_dk.dk_copenmask |= (1 << labelpart); 654 break; 655 case S_IFBLK: 656 pt->pt_dk.dk_bopenmask |= (1 << labelpart); 657 break; 658 } 659 660 return 0; 661 } 662 663 int 664 mmemclose(dev_t dev, int flags, int devtype, struct lwp *l) 665 { 666 int diskunit, unit, part, labelpart; 667 struct mmem_softc *sc; 668 struct mmem_pt *pt; 669 670 diskunit = DISKUNIT(dev); 671 unit = MMEM_UNIT(diskunit); 672 part = MMEM_PART(diskunit); 673 sc = device_lookup_private(&mmem_cd, unit); 674 pt = &sc->sc_pt[part]; 675 labelpart = DISKPART(dev); 676 677 switch (devtype) { 678 case S_IFCHR: 679 pt->pt_dk.dk_copenmask &= ~(1 << labelpart); 680 break; 681 case S_IFBLK: 682 pt->pt_dk.dk_bopenmask &= ~(1 << labelpart); 683 break; 684 } 685 686 return 0; 687 } 688 689 void 690 mmemstrategy(struct buf *bp) 691 { 692 int diskunit, unit, part, labelpart; 693 struct mmem_softc *sc; 694 struct mmem_pt *pt; 695 daddr_t off, nblk, cnt; 696 697 diskunit = DISKUNIT(bp->b_dev); 698 unit = MMEM_UNIT(diskunit); 699 part = MMEM_PART(diskunit); 700 if ((sc = device_lookup_private(&mmem_cd, unit)) == NULL 701 || sc->sc_stat == MMEM_INIT 702 || sc->sc_stat == MMEM_INIT2 703 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0) 704 goto inval; 705 706 #if 0 707 printf("%s: mmemstrategy: blkno %d, count %ld\n", 708 pt->pt_name, bp->b_blkno, bp->b_bcount); 709 #endif 710 711 if (bp->b_flags & B_READ) { 712 if (sc->sc_racc == 0) 713 goto inval; /* no read */ 714 } else if (sc->sc_wacc == 0) { 715 bp->b_error = EROFS; /* no write */ 716 goto done; 717 } 718 719 if (bp->b_blkno & ~(~(daddr_t)0 >> (DEV_BSHIFT + 1 /* sign bit */)) 720 || (bp->b_bcount % sc->sc_bsize) != 0) 721 goto inval; 722 723 cnt = howmany(bp->b_bcount, sc->sc_bsize); 724 if (cnt == 0) 725 goto done; /* no work */ 726 727 off = bp->b_blkno * DEV_BSIZE / sc->sc_bsize; 728 729 /* offset to disklabel partition */ 730 labelpart = DISKPART(bp->b_dev); 731 if (labelpart == RAW_PART) { 732 nblk = pt->pt_info.maxblk - pt->pt_info.minblk + 1; 733 } else { 734 off += 735 nblk = pt->pt_dk.dk_label->d_partitions[labelpart].p_offset; 736 nblk += pt->pt_dk.dk_label->d_partitions[labelpart].p_size; 737 } 738 739 /* deal with the EOF condition */ 740 if (off + cnt > nblk) { 741 if (off >= nblk) { 742 if (off == nblk) 743 goto done; 744 goto inval; 745 } 746 cnt = nblk - off; 747 bp->b_resid = bp->b_bcount - (cnt * sc->sc_bsize); 748 } 749 750 bp->b_rawblkno = off; 751 752 /* queue this transfer */ 753 bufq_put(sc->sc_q, bp); 754 755 if (sc->sc_stat == MMEM_IDLE) 756 mmemstart(sc); 757 758 return; 759 760 inval: bp->b_error = EINVAL; 761 done: bp->b_resid = bp->b_bcount; 762 biodone(bp); 763 } 764 765 /* 766 * start I/O operations 767 */ 768 static void 769 mmemstart(struct mmem_softc *sc) 770 { 771 struct buf *bp; 772 struct mmem_pt *pt; 773 int s; 774 775 if ((bp = bufq_get(sc->sc_q)) == NULL) { 776 sc->sc_stat = MMEM_IDLE; 777 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, 778 MAPLE_FN_MEMCARD, 1); 779 return; 780 } 781 782 sc->sc_bp = bp; 783 sc->sc_cnt = howmany(bp->b_bcount - bp->b_resid, sc->sc_bsize); 784 KASSERT(sc->sc_cnt); 785 sc->sc_iobuf = bp->b_data; 786 sc->sc_retry = 0; 787 788 pt = &sc->sc_pt[MMEM_PART(DISKUNIT(bp->b_dev))]; 789 s = splbio(); 790 disk_busy(&pt->pt_dk); 791 splx(s); 792 793 /* 794 * I/O access will fail if the removal detection (by maple driver) 795 * occurs before finishing the I/O, so disable it. 796 * We are sending commands, and the removal detection is still alive. 797 */ 798 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 0); 799 800 mmemstart_bp(sc); 801 } 802 803 /* 804 * start/retry a specified I/O operation 805 */ 806 static void 807 mmemstart_bp(struct mmem_softc *sc) 808 { 809 struct buf *bp; 810 int diskunit, part; 811 struct mmem_pt *pt; 812 813 bp = sc->sc_bp; 814 diskunit = DISKUNIT(bp->b_dev); 815 part = MMEM_PART(diskunit); 816 pt = &sc->sc_pt[part]; 817 818 /* handle retry */ 819 if (sc->sc_retry++ > MMEM_MAXRETRY) { 820 /* retry count exceeded */ 821 mmemdone(sc, pt, EIO); 822 return; 823 } 824 825 /* 826 * Start the first phase (phase# = 0). 827 */ 828 /* start read */ 829 sc->sc_stat = (bp->b_flags & B_READ) ? MMEM_READ : MMEM_WRITE1; 830 sc->sc_reqr.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 831 sc->sc_reqr.pt = part; 832 sc->sc_reqr.block = htobe16(bp->b_rawblkno); 833 sc->sc_reqr.phase = 0; /* first phase */ 834 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 835 MAPLE_COMMAND_BREAD, sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 836 } 837 838 static void 839 mmemstart_write2(struct mmem_softc *sc) 840 { 841 struct buf *bp; 842 int diskunit, part; 843 struct mmem_pt *pt; 844 845 bp = sc->sc_bp; 846 diskunit = DISKUNIT(bp->b_dev); 847 part = MMEM_PART(diskunit); 848 pt = &sc->sc_pt[part]; 849 850 /* handle retry */ 851 if (sc->sc_retry++ > MMEM_MAXRETRY - 2 /* spare for verify read */) { 852 /* retry count exceeded */ 853 mmemdone(sc, pt, EIO); 854 return; 855 } 856 857 /* 858 * Start the first phase (phase# = 0). 859 */ 860 /* start write */ 861 sc->sc_stat = MMEM_WRITE2; 862 sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 863 sc->sc_reqw.pt = part; 864 sc->sc_reqw.block = htobe16(bp->b_rawblkno); 865 sc->sc_reqw.phase = 0; /* first phase */ 866 memcpy(sc->sc_reqw.data, sc->sc_iobuf /* + sc->sc_waccsz * phase */, 867 sc->sc_waccsz); 868 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 869 MAPLE_COMMAND_BWRITE, MMEM_SIZE_REQW(sc) / 4, &sc->sc_reqw, 870 MAPLE_FLAG_CMD_PERIODIC_TIMING); 871 } 872 873 static void 874 mmemdone(struct mmem_softc *sc, struct mmem_pt *pt, int err) 875 { 876 struct buf *bp = sc->sc_bp; 877 int s; 878 int bcnt; 879 880 KASSERT(bp); 881 882 if (err) { 883 bcnt = (char *)sc->sc_iobuf - (char *)bp->b_data; 884 bp->b_resid = bp->b_bcount - bcnt; 885 886 /* raise error if no block is read */ 887 if (bcnt == 0) { 888 bp->b_error = err; 889 } 890 goto term_xfer; 891 } 892 893 sc->sc_iobuf += sc->sc_bsize; 894 if (--sc->sc_cnt == 0) { 895 term_xfer: 896 /* terminate current transfer */ 897 sc->sc_bp = NULL; 898 s = splbio(); 899 disk_unbusy(&pt->pt_dk, 900 (char *)sc->sc_iobuf - (char *)bp->b_data, 901 sc->sc_stat == MMEM_READ); 902 biodone(bp); 903 splx(s); 904 905 /* go next transfer */ 906 mmemstart(sc); 907 } else { 908 /* go next block */ 909 bp->b_rawblkno++; 910 sc->sc_retry = 0; 911 mmemstart_bp(sc); 912 } 913 } 914 915 int 916 mmemread(dev_t dev, struct uio *uio, int flags) 917 { 918 919 return physio(mmemstrategy, NULL, dev, B_READ, minphys, uio); 920 } 921 922 int 923 mmemwrite(dev_t dev, struct uio *uio, int flags) 924 { 925 926 return physio(mmemstrategy, NULL, dev, B_WRITE, minphys, uio); 927 } 928 929 int 930 mmemioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 931 { 932 int diskunit, unit, part; 933 struct mmem_softc *sc; 934 struct mmem_pt *pt; 935 936 diskunit = DISKUNIT(dev); 937 unit = MMEM_UNIT(diskunit); 938 part = MMEM_PART(diskunit); 939 sc = device_lookup_private(&mmem_cd, unit); 940 pt = &sc->sc_pt[part]; 941 942 switch (cmd) { 943 case DIOCGDINFO: 944 *(struct disklabel *)data = *pt->pt_dk.dk_label; /* XXX */ 945 break; 946 947 default: 948 /* generic maple ioctl */ 949 return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data, 950 flag, l); 951 } 952 953 return 0; 954 } 955