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