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