1 /* $NetBSD: mmemcard.c,v 1.7 2005/12/11 12:17:06 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 * 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.7 2005/12/11 12:17:06 christos 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_flags |= B_ERROR; 324 bp->b_resid = bp->b_bcount; 325 biodone(bp); 326 } 327 while ((bp = BUFQ_GET(sc->sc_q)) != NULL) { 328 bp->b_error = EIO; 329 bp->b_flags |= B_ERROR; 330 bp->b_resid = bp->b_bcount; 331 biodone(bp); 332 } 333 bufq_free(sc->sc_q); 334 335 /* 336 * revoke vnodes 337 */ 338 #ifdef __HAVE_OLD_DISKLABEL 339 #error This code assumes DISKUNIT() is contiguous in minor number. 340 #endif 341 minor_l = MMEM_DISKMINOR(self->dv_unit, 0, 0); 342 minor_h = MMEM_DISKMINOR(self->dv_unit, sc->sc_npt - 1, 343 MAXPARTITIONS - 1); 344 vdevgone(bdevsw_lookup_major(&mmem_bdevsw), minor_l, minor_h, VBLK); 345 vdevgone(cdevsw_lookup_major(&mmem_cdevsw), minor_l, minor_h, VCHR); 346 347 /* 348 * free per-partition structure 349 */ 350 if (sc->sc_pt) { 351 /* 352 * detach disks 353 */ 354 for (i = 0; i < sc->sc_npt; i++) { 355 if (sc->sc_pt[i].pt_flags & MMEM_PT_OK) 356 disk_detach(&sc->sc_pt[i].pt_dk); 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 pt->pt_dk.dk_driver = &mmemdkdriver; 430 pt->pt_dk.dk_name = pt->pt_name; 431 disk_attach(&pt->pt_dk); 432 433 mmem_defaultlabel(sc, pt, pt->pt_dk.dk_label); 434 435 /* this partition is active */ 436 pt->pt_flags = MMEM_PT_OK; 437 438 break; 439 default: 440 printf("%s: init: unexpected response %#x, sz %d\n", 441 pt->pt_name, be32toh(response->response_code), sz); 442 break; 443 } 444 if (++part == sc->sc_npt) { 445 #if 1 446 /* 447 * XXX Read a block and discard the contents (only to 448 * turn off the access indicator on Visual Memory). 449 */ 450 pt = &sc->sc_pt[0]; 451 sc->sc_reqr.func_code = 452 htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 453 sc->sc_reqr.pt = 0; 454 sc->sc_reqr.block = htobe16(pt->pt_info.minblk); 455 sc->sc_reqr.phase = 0; 456 maple_command(sc->sc_parent, sc->sc_unit, 457 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 458 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 459 sc->sc_stat = MMEM_INIT2; 460 #else 461 sc->sc_stat = MMEM_IDLE; /* init done */ 462 #endif 463 } else { 464 sc->sc_reqm.pt = part; 465 maple_command(sc->sc_parent, sc->sc_unit, 466 MAPLE_FN_MEMCARD, MAPLE_COMMAND_GETMINFO, 467 sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0); 468 } 469 break; 470 471 case MMEM_INIT2: 472 /* XXX just discard */ 473 sc->sc_stat = MMEM_IDLE; /* init done */ 474 break; 475 476 case MMEM_READ: 477 bp = sc->sc_bp; 478 479 switch ((maple_response_t) response->response_code) { 480 case MAPLE_RESPONSE_DATATRF: /* read done */ 481 off = sc->sc_raccsz * sc->sc_reqr.phase; 482 memcpy(sc->sc_iobuf + off, r->data + off, 483 sc->sc_raccsz); 484 485 if (++sc->sc_reqr.phase == sc->sc_racc) { 486 /* all phase done */ 487 pt = &sc->sc_pt[sc->sc_reqr.pt]; 488 mmemdone(sc, pt, 0); 489 } else { 490 /* go next phase */ 491 maple_command(sc->sc_parent, sc->sc_unit, 492 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 493 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 494 } 495 break; 496 case MAPLE_RESPONSE_FILEERR: 497 mmem_printerror(sc->sc_pt[sc->sc_reqr.pt].pt_name, 498 1, bp->b_rawblkno, 499 r->func_code /* XXX */); 500 mmemstart_bp(sc); /* retry */ 501 break; 502 default: 503 printf("%s: read: unexpected response %#x %#x, sz %d\n", 504 sc->sc_pt[sc->sc_reqr.pt].pt_name, 505 be32toh(response->response_code), 506 be32toh(r->func_code), sz); 507 mmemstart_bp(sc); /* retry */ 508 break; 509 } 510 break; 511 512 case MMEM_WRITE1: /* read before write / verify after write */ 513 bp = sc->sc_bp; 514 515 switch ((maple_response_t) response->response_code) { 516 case MAPLE_RESPONSE_DATATRF: /* read done */ 517 off = sc->sc_raccsz * sc->sc_reqr.phase; 518 if (memcmp(r->data + off, sc->sc_iobuf + off, 519 sc->sc_raccsz)) { 520 /* 521 * data differ, start writing 522 */ 523 mmemstart_write2(sc); 524 } else if (++sc->sc_reqr.phase == sc->sc_racc) { 525 /* 526 * all phase done and compared equal 527 */ 528 pt = &sc->sc_pt[sc->sc_reqr.pt]; 529 mmemdone(sc, pt, 0); 530 } else { 531 /* go next phase */ 532 maple_command(sc->sc_parent, sc->sc_unit, 533 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BREAD, 534 sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 535 } 536 break; 537 case MAPLE_RESPONSE_FILEERR: 538 mmem_printerror(sc->sc_pt[sc->sc_reqr.pt].pt_name, 539 1, bp->b_rawblkno, 540 r->func_code /* XXX */); 541 mmemstart_write2(sc); /* start writing */ 542 break; 543 default: 544 printf("%s: verify: unexpected response %#x %#x, sz %d\n", 545 sc->sc_pt[sc->sc_reqr.pt].pt_name, 546 be32toh(response->response_code), 547 be32toh(r->func_code), sz); 548 mmemstart_write2(sc); /* start writing */ 549 break; 550 } 551 break; 552 553 case MMEM_WRITE2: /* write */ 554 bp = sc->sc_bp; 555 556 switch ((maple_response_t) response->response_code) { 557 case MAPLE_RESPONSE_OK: /* write done */ 558 if (sc->sc_reqw.phase == sc->sc_wacc) { 559 /* all phase done */ 560 mmemstart_bp(sc); /* start verify */ 561 } else if (++sc->sc_reqw.phase == sc->sc_wacc) { 562 /* check error */ 563 maple_command(sc->sc_parent, sc->sc_unit, 564 MAPLE_FN_MEMCARD, MAPLE_COMMAND_GETLASTERR, 565 2 /* no data */ , &sc->sc_reqw, 566 MAPLE_FLAG_CMD_PERIODIC_TIMING); 567 } else { 568 /* go next phase */ 569 memcpy(sc->sc_reqw.data, sc->sc_iobuf + 570 sc->sc_waccsz * sc->sc_reqw.phase, 571 sc->sc_waccsz); 572 maple_command(sc->sc_parent, sc->sc_unit, 573 MAPLE_FN_MEMCARD, MAPLE_COMMAND_BWRITE, 574 MMEM_SIZE_REQW(sc) / 4, &sc->sc_reqw, 575 MAPLE_FLAG_CMD_PERIODIC_TIMING); 576 } 577 break; 578 case MAPLE_RESPONSE_FILEERR: 579 mmem_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name, 580 0, bp->b_rawblkno, 581 r->func_code /* XXX */); 582 mmemstart_write2(sc); /* retry writing */ 583 break; 584 default: 585 printf("%s: write: unexpected response %#x, %#x, sz %d\n", 586 sc->sc_pt[sc->sc_reqw.pt].pt_name, 587 be32toh(response->response_code), 588 be32toh(r->func_code), sz); 589 mmemstart_write2(sc); /* retry writing */ 590 break; 591 } 592 break; 593 594 default: 595 break; 596 } 597 } 598 599 static void 600 mmem_printerror(const char *head, int rd, int blk, uint32_t code) 601 { 602 603 printf("%s: error %sing blk %d:", head, rd? "read" : "writ", blk); 604 NTOHL(code); 605 if (code & 1) 606 printf(" PT error"); 607 if (code & 2) 608 printf(" Phase error"); 609 if (code & 4) 610 printf(" Block error"); 611 if (code & 010) 612 printf(" Write error"); 613 if (code & 020) 614 printf(" Length error"); 615 if (code & 040) 616 printf(" CRC error"); 617 if (code & ~077) 618 printf(" Unknown error %#x", code & ~077); 619 printf("\n"); 620 } 621 622 int 623 mmemopen(dev_t dev, int flags, int devtype, struct lwp *l) 624 { 625 int diskunit, unit, part, labelpart; 626 struct mmem_softc *sc; 627 struct mmem_pt *pt; 628 629 diskunit = DISKUNIT(dev); 630 unit = MMEM_UNIT(diskunit); 631 part = MMEM_PART(diskunit); 632 labelpart = DISKPART(dev); 633 if ((sc = device_lookup(&mmem_cd, unit)) == NULL 634 || sc->sc_stat == MMEM_INIT 635 || sc->sc_stat == MMEM_INIT2 636 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0) 637 return ENXIO; 638 639 switch (devtype) { 640 case S_IFCHR: 641 pt->pt_dk.dk_copenmask |= (1 << labelpart); 642 break; 643 case S_IFBLK: 644 pt->pt_dk.dk_bopenmask |= (1 << labelpart); 645 break; 646 } 647 648 return 0; 649 } 650 651 int 652 mmemclose(dev_t dev, int flags, int devtype, struct lwp *l) 653 { 654 int diskunit, unit, part, labelpart; 655 struct mmem_softc *sc; 656 struct mmem_pt *pt; 657 658 diskunit = DISKUNIT(dev); 659 unit = MMEM_UNIT(diskunit); 660 part = MMEM_PART(diskunit); 661 sc = mmem_cd.cd_devs[unit]; 662 pt = &sc->sc_pt[part]; 663 labelpart = DISKPART(dev); 664 665 switch (devtype) { 666 case S_IFCHR: 667 pt->pt_dk.dk_copenmask &= ~(1 << labelpart); 668 break; 669 case S_IFBLK: 670 pt->pt_dk.dk_bopenmask &= ~(1 << labelpart); 671 break; 672 } 673 674 return 0; 675 } 676 677 void 678 mmemstrategy(struct buf *bp) 679 { 680 int diskunit, unit, part, labelpart; 681 struct mmem_softc *sc; 682 struct mmem_pt *pt; 683 daddr_t off, nblk, cnt; 684 685 diskunit = DISKUNIT(bp->b_dev); 686 unit = MMEM_UNIT(diskunit); 687 part = MMEM_PART(diskunit); 688 if ((sc = device_lookup(&mmem_cd, unit)) == NULL 689 || sc->sc_stat == MMEM_INIT 690 || sc->sc_stat == MMEM_INIT2 691 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0) 692 goto inval; 693 694 #if 0 695 printf("%s: mmemstrategy: blkno %d, count %ld\n", 696 pt->pt_name, bp->b_blkno, bp->b_bcount); 697 #endif 698 699 if (bp->b_flags & B_READ) { 700 if (sc->sc_racc == 0) 701 goto inval; /* no read */ 702 } else if (sc->sc_wacc == 0) { 703 bp->b_error = EROFS; /* no write */ 704 goto bad; 705 } 706 707 if (bp->b_blkno & ~(~(daddr_t)0 >> (DEV_BSHIFT + 1 /* sign bit */)) 708 || (bp->b_bcount % sc->sc_bsize) != 0) 709 goto inval; 710 711 cnt = howmany(bp->b_bcount, sc->sc_bsize); 712 if (cnt == 0) 713 goto done; /* no work */ 714 715 off = bp->b_blkno * DEV_BSIZE / sc->sc_bsize; 716 717 /* offset to disklabel partition */ 718 labelpart = DISKPART(bp->b_dev); 719 if (labelpart == RAW_PART) { 720 nblk = pt->pt_info.maxblk - pt->pt_info.minblk + 1; 721 } else { 722 off += 723 nblk = pt->pt_dk.dk_label->d_partitions[labelpart].p_offset; 724 nblk += pt->pt_dk.dk_label->d_partitions[labelpart].p_size; 725 } 726 727 /* deal with the EOF condition */ 728 if (off + cnt > nblk) { 729 if (off >= nblk) { 730 if (off == nblk) 731 goto done; 732 goto inval; 733 } 734 cnt = nblk - off; 735 bp->b_resid = bp->b_bcount - (cnt * sc->sc_bsize); 736 } 737 738 bp->b_rawblkno = off; 739 740 /* queue this transfer */ 741 BUFQ_PUT(sc->sc_q, bp); 742 743 if (sc->sc_stat == MMEM_IDLE) 744 mmemstart(sc); 745 746 return; 747 748 inval: bp->b_error = EINVAL; 749 bad: bp->b_flags |= B_ERROR; 750 done: bp->b_resid = bp->b_bcount; 751 biodone(bp); 752 } 753 754 /* 755 * start I/O operations 756 */ 757 static void 758 mmemstart(struct mmem_softc *sc) 759 { 760 struct buf *bp; 761 struct mmem_pt *pt; 762 int s; 763 764 if ((bp = BUFQ_GET(sc->sc_q)) == NULL) { 765 sc->sc_stat = MMEM_IDLE; 766 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, 767 MAPLE_FN_MEMCARD, 1); 768 return; 769 } 770 771 sc->sc_bp = bp; 772 sc->sc_cnt = howmany(bp->b_bcount - bp->b_resid, sc->sc_bsize); 773 KASSERT(sc->sc_cnt); 774 sc->sc_iobuf = bp->b_data; 775 sc->sc_retry = 0; 776 777 pt = &sc->sc_pt[MMEM_PART(DISKUNIT(bp->b_dev))]; 778 s = splbio(); 779 disk_busy(&pt->pt_dk); 780 splx(s); 781 782 /* 783 * I/O access will fail if the removal detection (by maple driver) 784 * occurs before finishing the I/O, so disable it. 785 * We are sending commands, and the removal detection is still alive. 786 */ 787 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 0); 788 789 mmemstart_bp(sc); 790 } 791 792 /* 793 * start/retry a specified I/O operation 794 */ 795 static void 796 mmemstart_bp(struct mmem_softc *sc) 797 { 798 struct buf *bp; 799 int diskunit, part; 800 struct mmem_pt *pt; 801 802 bp = sc->sc_bp; 803 diskunit = DISKUNIT(bp->b_dev); 804 part = MMEM_PART(diskunit); 805 pt = &sc->sc_pt[part]; 806 807 /* handle retry */ 808 if (sc->sc_retry++ > MMEM_MAXRETRY) { 809 /* retry count exceeded */ 810 mmemdone(sc, pt, EIO); 811 return; 812 } 813 814 /* 815 * Start the first phase (phase# = 0). 816 */ 817 /* start read */ 818 sc->sc_stat = (bp->b_flags & B_READ) ? MMEM_READ : MMEM_WRITE1; 819 sc->sc_reqr.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 820 sc->sc_reqr.pt = part; 821 sc->sc_reqr.block = htobe16(bp->b_rawblkno); 822 sc->sc_reqr.phase = 0; /* first phase */ 823 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 824 MAPLE_COMMAND_BREAD, sizeof sc->sc_reqr / 4, &sc->sc_reqr, 0); 825 } 826 827 static void 828 mmemstart_write2(struct mmem_softc *sc) 829 { 830 struct buf *bp; 831 int diskunit, part; 832 struct mmem_pt *pt; 833 834 bp = sc->sc_bp; 835 diskunit = DISKUNIT(bp->b_dev); 836 part = MMEM_PART(diskunit); 837 pt = &sc->sc_pt[part]; 838 839 /* handle retry */ 840 if (sc->sc_retry++ > MMEM_MAXRETRY - 2 /* spare for verify read */) { 841 /* retry count exceeded */ 842 mmemdone(sc, pt, EIO); 843 return; 844 } 845 846 /* 847 * Start the first phase (phase# = 0). 848 */ 849 /* start write */ 850 sc->sc_stat = MMEM_WRITE2; 851 sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_MEMCARD)); 852 sc->sc_reqw.pt = part; 853 sc->sc_reqw.block = htobe16(bp->b_rawblkno); 854 sc->sc_reqw.phase = 0; /* first phase */ 855 memcpy(sc->sc_reqw.data, sc->sc_iobuf /* + sc->sc_waccsz * phase */, 856 sc->sc_waccsz); 857 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_MEMCARD, 858 MAPLE_COMMAND_BWRITE, MMEM_SIZE_REQW(sc) / 4, &sc->sc_reqw, 859 MAPLE_FLAG_CMD_PERIODIC_TIMING); 860 } 861 862 static void 863 mmemdone(struct mmem_softc *sc, struct mmem_pt *pt, int err) 864 { 865 struct buf *bp = sc->sc_bp; 866 int s; 867 int bcnt; 868 869 KASSERT(bp); 870 871 if (err) { 872 bcnt = sc->sc_iobuf - bp->b_data; 873 bp->b_resid = bp->b_bcount - bcnt; 874 875 /* raise error if no block is read */ 876 if (bcnt == 0) { 877 bp->b_error = err; 878 bp->b_flags |= B_ERROR; 879 } 880 goto term_xfer; 881 } 882 883 sc->sc_iobuf += sc->sc_bsize; 884 if (--sc->sc_cnt == 0) { 885 term_xfer: 886 /* terminate current transfer */ 887 sc->sc_bp = NULL; 888 s = splbio(); 889 disk_unbusy(&pt->pt_dk, sc->sc_iobuf - bp->b_data, 890 sc->sc_stat == MMEM_READ); 891 biodone(bp); 892 splx(s); 893 894 /* go next transfer */ 895 mmemstart(sc); 896 } else { 897 /* go next block */ 898 bp->b_rawblkno++; 899 sc->sc_retry = 0; 900 mmemstart_bp(sc); 901 } 902 } 903 904 int 905 mmemread(dev_t dev, struct uio *uio, int flags) 906 { 907 908 return physio(mmemstrategy, NULL, dev, B_READ, minphys, uio); 909 } 910 911 int 912 mmemwrite(dev_t dev, struct uio *uio, int flags) 913 { 914 915 return physio(mmemstrategy, NULL, dev, B_WRITE, minphys, uio); 916 } 917 918 int 919 mmemioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct lwp *l) 920 { 921 int diskunit, unit, part; 922 struct mmem_softc *sc; 923 struct mmem_pt *pt; 924 925 diskunit = DISKUNIT(dev); 926 unit = MMEM_UNIT(diskunit); 927 part = MMEM_PART(diskunit); 928 sc = mmem_cd.cd_devs[unit]; 929 pt = &sc->sc_pt[part]; 930 931 switch (cmd) { 932 case DIOCGDINFO: 933 *(struct disklabel *)data = *pt->pt_dk.dk_label; /* XXX */ 934 break; 935 936 default: 937 /* generic maple ioctl */ 938 return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data, 939 flag, l); 940 } 941 942 return 0; 943 } 944