gdrom.c revision 1.36 1 /* $NetBSD: gdrom.c,v 1.36 2014/03/14 08:50:08 martin 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.36 2014/03/14 08:50:08 martin 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/bufq.h>
44 #include <sys/ioctl.h>
45 #include <sys/fcntl.h>
46 #include <sys/disklabel.h>
47 #include <sys/disk.h>
48 #include <sys/cdio.h>
49 #include <sys/proc.h>
50 #include <sys/conf.h>
51
52 #include <machine/sysasicvar.h>
53
54 #include "ioconf.h"
55
56 static int gdrommatch(device_t, cfdata_t, void *);
57 static void gdromattach(device_t, device_t, void *);
58
59 dev_type_open(gdromopen);
60 dev_type_close(gdromclose);
61 dev_type_read(gdromread);
62 dev_type_write(gdromwrite);
63 dev_type_ioctl(gdromioctl);
64 dev_type_strategy(gdromstrategy);
65
66 const struct bdevsw gdrom_bdevsw = {
67 gdromopen, gdromclose, gdromstrategy, gdromioctl, nodump,
68 nosize, D_DISK
69 };
70
71 const struct cdevsw gdrom_cdevsw = {
72 gdromopen, gdromclose, gdromread, gdromwrite, gdromioctl,
73 nostop, notty, nopoll, nommap, nokqfilter, D_DISK
74 };
75
76 struct gdrom_softc {
77 device_t sc_dev; /* generic device info */
78 struct disk sc_dk; /* generic disk info */
79 struct bufq_state *sc_bufq; /* device buffer queue */
80 struct buf curbuf; /* state of current I/O operation */
81
82 bool is_open;
83 bool is_busy;
84 bool is_active;
85 int openpart_start; /* start sector of currently open partition */
86
87 int cmd_active;
88 void *cmd_result_buf; /* where to store result data (16 bit aligned) */
89 int cmd_result_size; /* number of bytes allocated for buf */
90 int cmd_actual; /* number of bytes actually read */
91 int cmd_cond; /* resulting condition of command */
92 };
93
94 CFATTACH_DECL_NEW(gdrom, sizeof(struct gdrom_softc),
95 gdrommatch, gdromattach, NULL, NULL);
96
97 struct dkdriver gdromdkdriver = { gdromstrategy };
98
99
100 struct gd_toc {
101 unsigned int entry[99];
102 unsigned int first, last;
103 unsigned int leadout;
104 };
105
106 #ifdef GDROMDEBUG
107 #define DPRINTF(x) printf x
108 #else
109 #define DPRINTF(x) /**/
110 #endif
111
112 #define TOC_LBA(n) ((n) & 0xffffff00)
113 #define TOC_ADR(n) ((n) & 0x0f)
114 #define TOC_CTRL(n) (((n) & 0xf0) >> 4)
115 #define TOC_TRACK(n) (((n) & 0x0000ff00) >> 8)
116
117 #define GDROM(o) (*(volatile uint8_t *)(0xa05f7000 + (o)))
118
119 #define GDSTATSTAT(n) ((n) & 0xf)
120 #define GDSTATDISK(n) (((n) >> 4) & 0xf)
121
122 #define GDROM_BUSY GDROM(0x18)
123 #define GDROM_DATA (*(volatile uint16_t *)(&GDROM(0x80)))
124 #define GDROM_REGX GDROM(0x84)
125 #define GDROM_STAT GDROM(0x8c)
126 #define GDROM_CNTLO GDROM(0x90)
127 #define GDROM_CNTHI GDROM(0x94)
128 #define GDROM_COND GDROM(0x9c)
129
130 #if 0
131 static int gdrom_getstat(void);
132 #endif
133 static int gdrom_do_command(struct gdrom_softc *, void *, void *,
134 unsigned int, int *);
135 static int gdrom_command_sense(struct gdrom_softc *, void *, void *,
136 unsigned int, int *);
137 static int gdrom_read_toc(struct gdrom_softc *, struct gd_toc *);
138 static int gdrom_read_sectors(struct gdrom_softc *, void *, int, int,
139 int *);
140 static int gdrom_mount_disk(struct gdrom_softc *);
141 static int gdrom_intr(void *);
142 static void gdrom_start(struct gdrom_softc *);
143
144 #if 0
145 int
146 gdrom_getstat(void)
147 {
148 uint8_t s1, s2, s3;
149
150 if (GDROM_BUSY & 0x80)
151 return -1;
152 s1 = GDROM_STAT;
153 s2 = GDROM_STAT;
154 s3 = GDROM_STAT;
155 if (GDROM_BUSY & 0x80)
156 return -1;
157 if (s1 == s2)
158 return s1;
159 else if (s2 == s3)
160 return s2;
161 else
162 return -1;
163 }
164 #endif
165
166 int
167 gdrom_intr(void *arg)
168 {
169 struct gdrom_softc *sc = arg;
170 int s;
171 uint8_t cond;
172
173 s = splbio();
174 cond = GDROM_COND;
175 DPRINTF(("GDROM: cond = %x\n", cond));
176 if (!sc->cmd_active) {
177 DPRINTF(("GDROM: inactive IRQ!?\n"));
178 splx(s);
179 return 0;
180 }
181
182 if ((cond & 0x08) != 0) {
183 int cnt = (GDROM_CNTHI << 8) | GDROM_CNTLO;
184 DPRINTF(("GDROM: cnt = %d\n", cnt));
185 sc->cmd_actual += cnt;
186 if (cnt > 0 && sc->cmd_result_size > 0) {
187 int subcnt = (cnt > sc->cmd_result_size ?
188 sc->cmd_result_size : cnt);
189 uint16_t *ptr = sc->cmd_result_buf;
190 sc->cmd_result_buf = ((uint8_t *)sc->cmd_result_buf) +
191 subcnt;
192 sc->cmd_result_size -= subcnt;
193 cnt -= subcnt;
194 while (subcnt > 0) {
195 *ptr++ = GDROM_DATA;
196 subcnt -= 2;
197 }
198 }
199 while (cnt > 0) {
200 (void)GDROM_DATA;
201 cnt -= 2;
202 }
203 }
204 while ((GDROM_BUSY & 0x80) != 0);
205
206 if ((cond & 0x08) == 0) {
207 sc->cmd_cond = cond;
208 sc->cmd_active = 0;
209 wakeup(&sc->cmd_active);
210 }
211
212 splx(s);
213 return 1;
214 }
215
216
217 int
218 gdrom_do_command(struct gdrom_softc *sc, void *req, void *buf,
219 unsigned int nbyt, int *resid)
220 {
221 int i, s;
222 uint16_t *ptr = req;
223
224 while (GDROM_BUSY & 0x88)
225 ;
226 if (buf != NULL) {
227 GDROM_CNTLO = nbyt & 0xff;
228 GDROM_CNTHI = (nbyt >> 8) & 0xff;
229 GDROM_REGX = 0;
230 }
231 sc->cmd_result_buf = buf;
232 sc->cmd_result_size = nbyt;
233
234 if (GDSTATSTAT(GDROM_STAT) == 0x06)
235 return -1;
236
237 GDROM_COND = 0xa0;
238 DELAY(1);
239 while ((GDROM_BUSY & 0x88) != 0x08)
240 ;
241
242 s = splbio();
243
244 sc->cmd_actual = 0;
245 sc->cmd_active = 1;
246
247 for (i = 0; i < 6; i++)
248 GDROM_DATA = ptr[i];
249
250 while (sc->cmd_active)
251 tsleep(&sc->cmd_active, PRIBIO, "gdrom", 0);
252
253 splx(s);
254
255 if (resid != NULL)
256 *resid = sc->cmd_result_size;
257
258 return sc->cmd_cond;
259 }
260
261
262 int gdrom_command_sense(struct gdrom_softc *sc, void *req, void *buf,
263 unsigned int nbyt, int *resid)
264 {
265 /*
266 * 76543210 76543210
267 * 0 0x13 -
268 * 2 - bufsz(hi)
269 * 4 bufsz(lo) -
270 * 6 - -
271 * 8 - -
272 * 10 - -
273 */
274 uint16_t sense_data[5];
275 uint8_t cmd[12];
276 int cond, sense_key, sense_specific;
277
278 cond = gdrom_do_command(sc, req, buf, nbyt, resid);
279
280 if (cond < 0) {
281 DPRINTF(("GDROM: not ready (2:58)\n"));
282 return EIO;
283 }
284
285 if ((cond & 1) == 0) {
286 DPRINTF(("GDROM: no sense. 0:0\n"));
287 return 0;
288 }
289
290 memset(cmd, 0, sizeof(cmd));
291
292 cmd[0] = 0x13;
293 cmd[4] = sizeof(sense_data);
294
295 gdrom_do_command(sc, cmd, sense_data, sizeof(sense_data), NULL);
296
297 sense_key = sense_data[1] & 0xf;
298 sense_specific = sense_data[4];
299 if (sense_key == 11 && sense_specific == 0) {
300 DPRINTF(("GDROM: aborted (ignored). 0:0\n"));
301 return 0;
302 }
303
304 DPRINTF(("GDROM: SENSE %d:", sense_key));
305 DPRINTF(("GDROM: %d\n", sense_specific));
306
307 return sense_key == 0 ? 0 : EIO;
308 }
309
310 int gdrom_read_toc(struct gdrom_softc *sc, struct gd_toc *toc)
311 {
312 /*
313 * 76543210 76543210
314 * 0 0x14 -
315 * 2 - bufsz(hi)
316 * 4 bufsz(lo) -
317 * 6 - -
318 * 8 - -
319 * 10 - -
320 */
321 uint8_t cmd[12];
322
323 memset(cmd, 0, sizeof(cmd));
324
325 cmd[0] = 0x14;
326 cmd[3] = sizeof(struct gd_toc) >> 8;
327 cmd[4] = sizeof(struct gd_toc) & 0xff;
328
329 return gdrom_command_sense(sc, cmd, toc, sizeof(struct gd_toc), NULL);
330 }
331
332 int gdrom_read_sectors(struct gdrom_softc *sc, void *buf, int sector, int cnt,
333 int *resid)
334 {
335 /*
336 * 76543210 76543210
337 * 0 0x30 datafmt
338 * 2 sec(hi) sec(mid)
339 * 4 sec(lo) -
340 * 6 - -
341 * 8 cnt(hi) cnt(mid)
342 * 10 cnt(lo) -
343 */
344 uint8_t cmd[12];
345
346 memset(cmd, 0, sizeof(cmd));
347
348 cmd[0] = 0x30;
349 cmd[1] = 0x20;
350 cmd[2] = sector >> 16;
351 cmd[3] = sector >> 8;
352 cmd[4] = sector;
353 cmd[8] = cnt >> 16;
354 cmd[9] = cnt >> 8;
355 cmd[10] = cnt;
356
357 return gdrom_command_sense(sc, cmd, buf, cnt << 11, resid);
358 }
359
360 int gdrom_mount_disk(struct gdrom_softc *sc)
361 {
362 /*
363 * 76543210 76543210
364 * 0 0x70 -
365 * 2 0x1f -
366 * 4 - -
367 * 6 - -
368 * 8 - -
369 * 10 - -
370 */
371 uint8_t cmd[12];
372
373 memset(cmd, 0, sizeof(cmd));
374
375 cmd[0] = 0x70;
376 cmd[1] = 0x1f;
377
378 return gdrom_command_sense(sc, cmd, NULL, 0, NULL);
379 }
380
381 int
382 gdrommatch(device_t parent, cfdata_t cf, void *aux)
383 {
384 static int gdrom_matched = 0;
385
386 /* Allow only once instance. */
387 if (gdrom_matched)
388 return 0;
389 gdrom_matched = 1;
390
391 return 1;
392 }
393
394 void
395 gdromattach(device_t parent, device_t self, void *aux)
396 {
397 struct gdrom_softc *sc;
398 uint32_t p;
399
400 sc = device_private(self);
401 sc->sc_dev = self;
402
403 bufq_alloc(&sc->sc_bufq, "disksort", BUFQ_SORT_RAWBLOCK);
404
405 /*
406 * Initialize and attach the disk structure.
407 */
408 disk_init(&sc->sc_dk, device_xname(self), &gdromdkdriver);
409 disk_attach(&sc->sc_dk);
410
411 /*
412 * reenable disabled drive
413 */
414 *((volatile uint32_t *)0xa05f74e4) = 0x1fffff;
415 for (p = 0; p < 0x200000 / 4; p++)
416 (void)((volatile uint32_t *)0xa0000000)[p];
417
418 printf(": %s\n", sysasic_intr_string(SYSASIC_IRL9));
419 sysasic_intr_establish(SYSASIC_EVENT_GDROM, IPL_BIO, SYSASIC_IRL9,
420 gdrom_intr, sc);
421 }
422
423 int
424 gdromopen(dev_t dev, int flags, int devtype, struct lwp *l)
425 {
426 struct gdrom_softc *sc;
427 int s, error, unit, cnt;
428 struct gd_toc toc;
429
430 DPRINTF(("GDROM: open\n"));
431
432 unit = DISKUNIT(dev);
433
434 sc = device_lookup_private(&gdrom_cd, unit);
435 if (sc == NULL)
436 return ENXIO;
437
438 if (sc->is_open)
439 return EBUSY;
440
441 s = splbio();
442 while (sc->is_busy)
443 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
444 sc->is_busy = true;
445 splx(s);
446
447 for (cnt = 0; cnt < 5; cnt++)
448 if ((error = gdrom_mount_disk(sc)) == 0)
449 break;
450
451 if (error == 0)
452 error = gdrom_read_toc(sc, &toc);
453
454 sc->is_busy = false;
455 wakeup(&sc->is_busy);
456
457 if (error != 0)
458 return error;
459
460 sc->is_open = true;
461 sc->openpart_start = 150;
462
463 DPRINTF(("GDROM: open OK\n"));
464 return 0;
465 }
466
467 int
468 gdromclose(dev_t dev, int flags, int devtype, struct lwp *l)
469 {
470 struct gdrom_softc *sc;
471 int unit;
472
473 DPRINTF(("GDROM: close\n"));
474
475 unit = DISKUNIT(dev);
476 sc = device_lookup_private(&gdrom_cd, unit);
477
478 sc->is_open = false;
479
480 return 0;
481 }
482
483 void
484 gdromstrategy(struct buf *bp)
485 {
486 struct gdrom_softc *sc;
487 int s, unit;
488
489 DPRINTF(("GDROM: strategy\n"));
490
491 unit = DISKUNIT(bp->b_dev);
492 sc = device_lookup_private(&gdrom_cd, unit);
493
494 if (bp->b_bcount == 0)
495 goto done;
496
497 bp->b_rawblkno = bp->b_blkno / (2048 / DEV_BSIZE) + sc->openpart_start;
498
499 DPRINTF(("GDROM: read_sectors(%p, %lld, %d) [%d bytes]\n",
500 bp->b_data, bp->b_rawblkno,
501 bp->b_bcount >> 11, bp->b_bcount));
502
503 s = splbio();
504 bufq_put(sc->sc_bufq, bp);
505 splx(s);
506 if (!sc->is_active)
507 gdrom_start(sc);
508 return;
509
510 done:
511 bp->b_resid = bp->b_bcount;
512 biodone(bp);
513 }
514
515 void
516 gdrom_start(struct gdrom_softc *sc)
517 {
518 struct buf *bp;
519 int error, resid, s;
520
521 sc->is_active = true;
522
523 for (;;) {
524 s = splbio();
525 bp = bufq_get(sc->sc_bufq);
526 if (bp == NULL) {
527 splx(s);
528 break;
529 }
530
531 while (sc->is_busy)
532 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
533 sc->is_busy = true;
534 disk_busy(&sc->sc_dk);
535 splx(s);
536
537 error = gdrom_read_sectors(sc, bp->b_data, bp->b_rawblkno,
538 bp->b_bcount >> 11, &resid);
539 bp->b_error = error;
540 bp->b_resid = resid;
541 if (error != 0)
542 bp->b_resid = bp->b_bcount;
543
544 sc->is_busy = false;
545 wakeup(&sc->is_busy);
546
547 s = splbio();
548 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
549 (bp->b_flags & B_READ) != 0);
550 splx(s);
551 biodone(bp);
552 }
553
554 sc->is_active = false;
555 }
556
557 int
558 gdromioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
559 {
560 struct gdrom_softc *sc;
561 int unit, error;
562
563 DPRINTF(("GDROM: ioctl %lx\n", cmd));
564
565 unit = DISKUNIT(dev);
566 sc = device_lookup_private(&gdrom_cd, unit);
567
568 switch (cmd) {
569 case CDIOREADMSADDR: {
570 int s, track, sessno = *(int *)addr;
571 struct gd_toc toc;
572
573 if (sessno != 0)
574 return EINVAL;
575
576 s = splbio();
577 while (sc->is_busy)
578 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
579 sc->is_busy = true;
580 splx(s);
581
582 error = gdrom_read_toc(sc, &toc);
583
584 sc->is_busy = false;
585 wakeup(&sc->is_busy);
586
587 if (error != 0)
588 return error;
589 #ifdef GDROMDEBUGTOC
590 { /* Dump the GDROM TOC */
591 unsigned char *ptr = (unsigned char *)&toc;
592 int i;
593
594 printf("gdrom: TOC\n");
595 for(i = 0; i < sizeof(toc); ++i) {
596 printf("%02x", *ptr++);
597 if( i%32 == 31)
598 printf("\n");
599 else if( i%4 == 3)
600 printf(",");
601 }
602 printf("\n");
603 }
604 #endif
605 for (track = TOC_TRACK(toc.last);
606 track >= TOC_TRACK(toc.first);
607 --track) {
608 if (track < 1 || track > 100)
609 return ENXIO;
610 if (TOC_CTRL(toc.entry[track - 1]))
611 break;
612 }
613
614 #ifdef GDROMDEBUGTOC
615 printf("gdrom: Using track %d, LBA %u\n", track,
616 TOC_LBA(toc.entry[track - 1]));
617 #endif
618
619 *(int *)addr = htonl(TOC_LBA(toc.entry[track - 1])) -
620 sc->openpart_start;
621
622 return 0;
623 }
624 default:
625 return ENOTTY;
626 }
627
628 #ifdef DIAGNOSTIC
629 panic("gdromioctl: impossible");
630 #endif
631 }
632
633
634 int
635 gdromread(dev_t dev, struct uio *uio, int flags)
636 {
637
638 DPRINTF(("GDROM: read\n"));
639 return physio(gdromstrategy, NULL, dev, B_READ, minphys, uio);
640 }
641
642 int
643 gdromwrite(dev_t dev, struct uio *uio, int flags)
644 {
645
646 return EROFS;
647 }
648