1 /* $NetBSD: gdrom.c,v 1.26 2008/08/01 20:19:49 marcus Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 Marcus Comstedt 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Marcus Comstedt. 18 * 4. Neither the name of The NetBSD Foundation nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 36 __KERNEL_RCSID(0, "$NetBSD: gdrom.c,v 1.26 2008/08/01 20:19:49 marcus Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 42 #include <sys/buf.h> 43 #include <sys/ioctl.h> 44 #include <sys/fcntl.h> 45 #include <sys/disklabel.h> 46 #include <sys/disk.h> 47 #include <sys/cdio.h> 48 #include <sys/proc.h> 49 #include <sys/conf.h> 50 51 #include <machine/sysasicvar.h> 52 53 int gdrommatch(struct device *, struct cfdata *, void *); 54 void gdromattach(struct device *, struct device *, void *); 55 56 dev_type_open(gdromopen); 57 dev_type_close(gdromclose); 58 dev_type_read(gdromread); 59 dev_type_write(gdromwrite); 60 dev_type_ioctl(gdromioctl); 61 dev_type_strategy(gdromstrategy); 62 63 const struct bdevsw gdrom_bdevsw = { 64 gdromopen, gdromclose, gdromstrategy, gdromioctl, nodump, 65 nosize, D_DISK 66 }; 67 68 const struct cdevsw gdrom_cdevsw = { 69 gdromopen, gdromclose, gdromread, gdromwrite, gdromioctl, 70 nostop, notty, nopoll, nommap, nokqfilter, D_DISK 71 }; 72 73 struct gdrom_softc { 74 struct device sc_dv; /* generic device info; must come first */ 75 struct disk dkdev; /* generic disk info */ 76 struct buf curbuf; /* state of current I/O operation */ 77 78 int is_open, is_busy; 79 int openpart_start; /* start sector of currently open partition */ 80 81 int cmd_active; 82 void *cmd_result_buf; /* where to store result data (16 bit aligned) */ 83 int cmd_result_size; /* number of bytes allocated for buf */ 84 int cmd_actual; /* number of bytes actually read */ 85 int cmd_cond; /* resulting condition of command */ 86 }; 87 88 CFATTACH_DECL(gdrom, sizeof(struct gdrom_softc), 89 gdrommatch, gdromattach, NULL, NULL); 90 91 struct dkdriver gdromdkdriver = { gdromstrategy }; 92 93 extern struct cfdriver gdrom_cd; 94 95 96 struct gd_toc { 97 unsigned int entry[99]; 98 unsigned int first, last; 99 unsigned int leadout; 100 }; 101 102 #define TOC_LBA(n) ((n) & 0xffffff00) 103 #define TOC_ADR(n) ((n) & 0x0f) 104 #define TOC_CTRL(n) (((n) & 0xf0) >> 4) 105 #define TOC_TRACK(n) (((n) & 0x0000ff00) >> 8) 106 107 #define GDROM(o) (*(volatile unsigned char *)(0xa05f7000 + (o))) 108 109 #define GDSTATSTAT(n) ((n) & 0xf) 110 #define GDSTATDISK(n) (((n) >> 4) & 0xf) 111 112 #define GDROM_BUSY GDROM(0x18) 113 #define GDROM_DATA (*(volatile short *) & GDROM(0x80)) 114 #define GDROM_REGX GDROM(0x84) 115 #define GDROM_STAT GDROM(0x8c) 116 #define GDROM_CNTLO GDROM(0x90) 117 #define GDROM_CNTHI GDROM(0x94) 118 #define GDROM_COND GDROM(0x9c) 119 120 int gdrom_getstat(void); 121 int gdrom_do_command(struct gdrom_softc *, void *, void *, unsigned int); 122 int gdrom_command_sense(struct gdrom_softc *, void *, void *, unsigned int); 123 int gdrom_read_toc(struct gdrom_softc *, struct gd_toc *); 124 int gdrom_read_sectors(struct gdrom_softc *, void *, int, int); 125 int gdrom_mount_disk(struct gdrom_softc *); 126 int gdrom_intr(void *); 127 128 int gdrom_getstat(void) 129 { 130 int s1, s2, s3; 131 132 if (GDROM_BUSY & 0x80) 133 return -1; 134 s1 = GDROM_STAT; 135 s2 = GDROM_STAT; 136 s3 = GDROM_STAT; 137 if (GDROM_BUSY & 0x80) 138 return -1; 139 if (s1 == s2) 140 return s1; 141 else if (s2 == s3) 142 return s2; 143 else 144 return -1; 145 } 146 147 int 148 gdrom_intr(void *arg) 149 { 150 struct gdrom_softc *sc = arg; 151 int s, cond; 152 153 s = splbio(); 154 cond = GDROM_COND; 155 #ifdef GDROMDEBUG 156 printf("GDROM: cond = %x\n", cond); 157 #endif 158 if (!sc->cmd_active) { 159 #ifdef GDROMDEBUG 160 printf("GDROM: inactive IRQ!?\n"); 161 #endif 162 splx(s); 163 return 0; 164 } 165 166 if ((cond & 8)) { 167 int cnt = (GDROM_CNTHI << 8) | GDROM_CNTLO; 168 #ifdef GDROMDEBUG 169 printf("GDROM: cnt = %d\n", cnt); 170 #endif 171 sc->cmd_actual += cnt; 172 if (cnt > 0 && sc->cmd_result_size > 0) { 173 int subcnt = (cnt > sc->cmd_result_size ? 174 sc->cmd_result_size : cnt); 175 int16_t *ptr = sc->cmd_result_buf; 176 sc->cmd_result_buf = ((char *)sc->cmd_result_buf) + 177 subcnt; 178 sc->cmd_result_size -= subcnt; 179 cnt -= subcnt; 180 while (subcnt > 0) { 181 *ptr++ = GDROM_DATA; 182 subcnt -= 2; 183 } 184 } 185 while (cnt > 0) { 186 volatile int16_t tmp; 187 tmp = GDROM_DATA; 188 cnt -= 2; 189 } 190 } 191 while (GDROM_BUSY & 0x80); 192 193 if ((cond & 8) == 0) { 194 sc->cmd_cond = cond; 195 sc->cmd_active = 0; 196 wakeup(&sc->cmd_active); 197 } 198 199 splx(s); 200 return 1; 201 } 202 203 204 int gdrom_do_command(struct gdrom_softc *sc, void *req, void *buf, 205 unsigned int nbyt) 206 { 207 int i, s; 208 short *ptr = req; 209 210 while (GDROM_BUSY & 0x88) 211 ; 212 if (buf != NULL) { 213 GDROM_CNTLO = nbyt & 0xff; 214 GDROM_CNTHI = (nbyt >> 8) & 0xff; 215 GDROM_REGX = 0; 216 } 217 sc->cmd_result_buf = buf; 218 sc->cmd_result_size = nbyt; 219 220 if (GDSTATSTAT(GDROM_STAT) == 6) 221 return -1; 222 223 GDROM_COND = 0xa0; 224 for (i = 0; i < 64; i++) 225 ; 226 while ((GDROM_BUSY & 0x88) != 8) 227 ; 228 229 s = splbio(); 230 231 sc->cmd_actual = 0; 232 sc->cmd_active = 1; 233 234 for (i = 0; i< 6; i++) 235 GDROM_DATA = ptr[i]; 236 237 while (sc->cmd_active) 238 tsleep(&sc->cmd_active, PRIBIO, "gdrom", 0); 239 240 splx(s); 241 242 return sc->cmd_cond; 243 } 244 245 246 int gdrom_command_sense(struct gdrom_softc *sc, void *req, void *buf, 247 unsigned int nbyt) 248 { 249 /* 76543210 76543210 250 0 0x13 - 251 2 - bufsz(hi) 252 4 bufsz(lo) - 253 6 - - 254 8 - - 255 10 - - */ 256 unsigned short sense_data[5]; 257 unsigned char cmd[12]; 258 int sense_key, sense_specific; 259 260 int cond = gdrom_do_command(sc, req, buf, nbyt); 261 262 if (cond < 0) { 263 #ifdef GDROMDEBUG 264 printf("GDROM: not ready (2:58)\n"); 265 #endif 266 return EIO; 267 } 268 269 if ((cond & 1) == 0) { 270 #ifdef GDROMDEBUG 271 printf("GDROM: no sense. 0:0\n"); 272 #endif 273 return 0; 274 } 275 276 memset(cmd, 0, sizeof(cmd)); 277 278 cmd[0] = 0x13; 279 cmd[4] = sizeof(sense_data); 280 281 gdrom_do_command(sc, cmd, sense_data, sizeof(sense_data)); 282 283 sense_key = sense_data[1] & 0xf; 284 sense_specific = sense_data[4]; 285 if (sense_key == 11 && sense_specific == 0) { 286 #ifdef GDROMDEBUG 287 printf("GDROM: aborted (ignored). 0:0\n"); 288 #endif 289 return 0; 290 } 291 292 #ifdef GDROMDEBUG 293 printf("GDROM: SENSE %d:", sense_key); 294 printf("GDROM: %d\n", sense_specific); 295 #endif 296 297 return sense_key == 0 ? 0 : EIO; 298 } 299 300 int gdrom_read_toc(struct gdrom_softc *sc, struct gd_toc *toc) 301 { 302 /* 76543210 76543210 303 0 0x14 - 304 2 - bufsz(hi) 305 4 bufsz(lo) - 306 6 - - 307 8 - - 308 10 - - */ 309 unsigned char cmd[12]; 310 311 memset(cmd, 0, sizeof(cmd)); 312 313 cmd[0] = 0x14; 314 cmd[3] = sizeof(struct gd_toc) >> 8; 315 cmd[4] = sizeof(struct gd_toc) & 0xff; 316 317 return gdrom_command_sense(sc, cmd, toc, sizeof(struct gd_toc)); 318 } 319 320 int gdrom_read_sectors(struct gdrom_softc *sc, void *buf, int sector, int cnt) 321 { 322 /* 76543210 76543210 323 0 0x30 datafmt 324 2 sec(hi) sec(mid) 325 4 sec(lo) - 326 6 - - 327 8 cnt(hi) cnt(mid) 328 10 cnt(lo) - */ 329 unsigned char cmd[12]; 330 331 memset(cmd, 0, sizeof(cmd)); 332 333 cmd[0] = 0x30; 334 cmd[1] = 0x20; 335 cmd[2] = sector>>16; 336 cmd[3] = sector>>8; 337 cmd[4] = sector; 338 cmd[8] = cnt>>16; 339 cmd[9] = cnt>>8; 340 cmd[10] = cnt; 341 342 return gdrom_command_sense(sc, cmd, buf, cnt << 11); 343 } 344 345 int gdrom_mount_disk(struct gdrom_softc *sc) 346 { 347 /* 76543210 76543210 348 0 0x70 - 349 2 0x1f - 350 4 - - 351 6 - - 352 8 - - 353 10 - - */ 354 unsigned char cmd[12]; 355 356 memset(cmd, 0, sizeof(cmd)); 357 358 cmd[0] = 0x70; 359 cmd[1] = 0x1f; 360 361 return gdrom_command_sense(sc, cmd, NULL, 0); 362 } 363 364 int 365 gdrommatch(struct device *parent, struct cfdata *cf, void *aux) 366 { 367 static int gdrom_matched = 0; 368 369 /* Allow only once instance. */ 370 if (gdrom_matched) 371 return 0; 372 gdrom_matched = 1; 373 374 return 1; 375 } 376 377 void 378 gdromattach(struct device *parent, struct device *self, void *aux) 379 { 380 struct gdrom_softc *sc; 381 uint32_t p, x; 382 383 sc = (struct gdrom_softc *)self; 384 385 /* 386 * Initialize and attach the disk structure. 387 */ 388 disk_init(&sc->dkdev, sc->sc_dv.dv_xname, &gdromdkdriver); 389 disk_attach(&sc->dkdev); 390 391 /* 392 * reenable disabled drive 393 */ 394 *((volatile uint32_t *)0xa05f74e4) = 0x1fffff; 395 for (p = 0; p < 0x200000 / 4; p++) 396 x = ((volatile uint32_t *)0xa0000000)[p]; 397 398 printf(": %s\n", sysasic_intr_string(SYSASIC_IRL9)); 399 sysasic_intr_establish(SYSASIC_EVENT_GDROM, IPL_BIO, SYSASIC_IRL9, 400 gdrom_intr, sc); 401 } 402 403 int 404 gdromopen(dev_t dev, int flags, int devtype, struct lwp *l) 405 { 406 struct gdrom_softc *sc; 407 int s, error, unit, cnt; 408 struct gd_toc toc; 409 410 #ifdef GDROMDEBUG 411 printf("GDROM: open\n"); 412 #endif 413 414 unit = DISKUNIT(dev); 415 416 sc = device_lookup_private(&gdrom_cd, unit); 417 if (sc == NULL) 418 return ENXIO; 419 420 if (sc->is_open) 421 return EBUSY; 422 423 s = splbio(); 424 while (sc->is_busy) 425 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 426 sc->is_busy = 1; 427 splx(s); 428 429 for (cnt = 0; cnt < 5; cnt++) 430 if ((error = gdrom_mount_disk(sc)) == 0) 431 break; 432 433 if (!error) 434 error = gdrom_read_toc(sc, &toc); 435 436 sc->is_busy = 0; 437 wakeup(&sc->is_busy); 438 439 if (error) 440 return error; 441 442 sc->is_open = 1; 443 sc->openpart_start = 150; 444 445 #ifdef GDROMDEBUG 446 printf("GDROM: open OK\n"); 447 #endif 448 return 0; 449 } 450 451 int 452 gdromclose(dev_t dev, int flags, int devtype, struct lwp *l) 453 { 454 struct gdrom_softc *sc; 455 int unit; 456 #ifdef GDROMDEBUG 457 printf("GDROM: close\n"); 458 #endif 459 unit = DISKUNIT(dev); 460 sc = device_lookup_private(&gdrom_cd, unit); 461 462 sc->is_open = 0; 463 464 return 0; 465 } 466 467 void 468 gdromstrategy(struct buf *bp) 469 { 470 struct gdrom_softc *sc; 471 int s, unit, error; 472 #ifdef GDROMDEBUG 473 printf("GDROM: strategy\n"); 474 #endif 475 476 unit = DISKUNIT(bp->b_dev); 477 sc = device_lookup_private(&gdrom_cd, unit); 478 479 if (bp->b_bcount == 0) 480 goto done; 481 482 bp->b_rawblkno = bp->b_blkno / (2048 / DEV_BSIZE) + sc->openpart_start; 483 484 #ifdef GDROMDEBUG 485 printf("GDROM: read_sectors(%p, %d, %ld) [%ld bytes]\n", 486 bp->b_data, bp->b_rawblkno, 487 bp->b_bcount>>11, bp->b_bcount); 488 #endif 489 s = splbio(); 490 while (sc->is_busy) 491 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 492 sc->is_busy = 1; 493 splx(s); 494 495 if ((error = gdrom_read_sectors(sc, bp->b_data, bp->b_rawblkno, 496 bp->b_bcount >> 11))) 497 bp->b_error = error; 498 499 sc->is_busy = 0; 500 wakeup(&sc->is_busy); 501 502 done: 503 bp->b_resid = bp->b_bcount; 504 biodone(bp); 505 } 506 507 int 508 gdromioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) 509 { 510 struct gdrom_softc *sc; 511 int unit, error; 512 #ifdef GDROMDEBUG 513 printf("GDROM: ioctl %lx\n", cmd); 514 #endif 515 516 unit = DISKUNIT(dev); 517 sc = device_lookup_private(&gdrom_cd, unit); 518 519 switch (cmd) { 520 case CDIOREADMSADDR: { 521 int s, track, sessno = *(int *)addr; 522 struct gd_toc toc; 523 524 if (sessno != 0) 525 return EINVAL; 526 527 s = splbio(); 528 while (sc->is_busy) 529 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 530 sc->is_busy = 1; 531 splx(s); 532 533 error = gdrom_read_toc(sc, &toc); 534 535 sc->is_busy = 0; 536 wakeup(&sc->is_busy); 537 538 if (error) 539 return error; 540 541 for (track = TOC_TRACK(toc.last); 542 track >= TOC_TRACK(toc.first); 543 --track) 544 if (TOC_CTRL(toc.entry[track-1])) 545 break; 546 547 if (track < TOC_TRACK(toc.first) || track > 100) 548 return ENXIO; 549 550 *(int *)addr = htonl(TOC_LBA(toc.entry[track-1])) - 551 sc->openpart_start; 552 553 return 0; 554 } 555 default: 556 return EINVAL; 557 } 558 559 #ifdef DIAGNOSTIC 560 panic("gdromioctl: impossible"); 561 #endif 562 } 563 564 565 int 566 gdromread(dev_t dev, struct uio *uio, int flags) 567 { 568 #ifdef GDROMDEBUG 569 printf("GDROM: read\n"); 570 #endif 571 return physio(gdromstrategy, NULL, dev, B_READ, minphys, uio); 572 } 573 574 int 575 gdromwrite(dev_t dev, struct uio *uio, int flags) 576 { 577 578 return EROFS; 579 } 580