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