diff options
| author | Jordan K. Hubbard <jkh@FreeBSD.org> | 1995-01-29 22:51:41 +0000 |
|---|---|---|
| committer | Jordan K. Hubbard <jkh@FreeBSD.org> | 1995-01-29 22:51:41 +0000 |
| commit | e9dd2c6798b27da7bb6b46632b283835186ff9a6 (patch) | |
| tree | 7501d952be5894818874957a02152cbd25bab616 /sys/gnu | |
| parent | 55e658a8a18b6c6b4d4fb946d6b1f98f18d2e490 (diff) | |
Notes
Diffstat (limited to 'sys/gnu')
| -rw-r--r-- | sys/gnu/i386/isa/scd.c | 1321 | ||||
| -rw-r--r-- | sys/gnu/i386/isa/scdreg.h | 313 | ||||
| -rw-r--r-- | sys/gnu/i386/scd.c | 1321 | ||||
| -rw-r--r-- | sys/gnu/i386/scdreg.h | 313 |
4 files changed, 1508 insertions, 1760 deletions
diff --git a/sys/gnu/i386/isa/scd.c b/sys/gnu/i386/isa/scd.c index 82e1e0494e69b..2c1b2c7f4f744 100644 --- a/sys/gnu/i386/isa/scd.c +++ b/sys/gnu/i386/isa/scd.c @@ -32,9 +32,12 @@ * */ -/* First released version. Please send any comments to micke@dynas.se */ -#define DEBUG 0 /* 0(no debug), 1(debug), 2(debug on each read) */ +/* $Id: scd.c,v 1.4 1995/01/28 23:53:16 micke Exp micke $ */ + +/* Please send any comments to micke@dynas.se */ + +#define SCD_DEBUG 0 #include "scd.h" #if NSCD > 0 @@ -52,10 +55,11 @@ #include <sys/dkbad.h> #include <sys/disklabel.h> #include <sys/devconf.h> +#include <machine/stdarg.h> #include <i386/isa/isa.h> #include <i386/isa/isa_device.h> -#include <gnu/i386/scdreg.h> +#include <i386/isa/scdreg.h> #define scd_part(dev) ((minor(dev)) & 7) #define scd_unit(dev) (((minor(dev)) & 0x38) >> 3) @@ -84,6 +88,13 @@ #define SCDBLKSIZE 2048 +#ifdef SCD_DEBUG + int scd_debuglevel = SCD_DEBUG; +# define XDEBUG(level, data) {if (scd_debuglevel >= level) printf data;} +#else +# define XDEBUG(level, data) +#endif + struct scd_mbx { short unit; short port; @@ -104,13 +115,13 @@ struct scd_data { int blksize; u_long disksize; struct disklabel dlabel; - int openflags; + int openflag; struct { unsigned char start_msf[3]; } toc[MAX_TRACKS]; short first_track; short last_track; -/* struct s_sony_subcode subcode; */ + struct ioc_play_msf last_play; short audio_status; struct buf head; /* head of buf queue */ @@ -129,29 +140,29 @@ static bcd_t bin2bcd(int b); static void hsg2msf(int hsg, bcd_t *msf); static int msf2hsg(bcd_t *msf); -static void get_drive_configuration(unsigned short unit, - unsigned char res_reg[], - unsigned int *res_size); -static int handle_attention(unsigned unit); +static void process_attention(unsigned unit); static inline void write_control(unsigned port, unsigned data); -static int write_params(unsigned port, unsigned char *params, int num_params); -static int do_cmd(unsigned int unit, - unsigned char cmd, - unsigned char *params, - unsigned int num_params, - unsigned char *result_buffer, - unsigned int *result_size); -static void set_drive_params(unsigned unit); +static int waitfor_status_bits(int unit, int bits_set, int bits_clear); +static int waitfor_attention(int unit); +static int send_cmd(u_int unit, u_char cmd, u_int nargs, ...); +static void init_drive(unsigned unit); +static int spin_up(unsigned unit); +static int read_toc(dev_t dev); +static int get_result(u_int unit, int result_len, u_char *result); +static void print_error(int unit, int errcode); static void scd_start(int unit); static void scd_doread(int state, struct scd_mbx *mbxin); static int scd_eject(int unit); static int scd_stop(int unit); +static int scd_pause(int unit); +static int scd_resume(int unit); static int scd_playtracks(int unit, struct ioc_play_track *pt); static int scd_playmsf(int unit, struct ioc_play_msf *msf); +static int scd_play(int unit, struct ioc_play_msf *msf); static int scd_subchan(int unit, struct ioc_read_subchannel *sc); -static int read_subcode(int unit, struct s_sony_subcode *sc); +static int read_subcode(int unit, struct sony_subchannel_position_data *sc); extern int hz; @@ -191,7 +202,7 @@ int scd_attach(struct isa_device *dev) kdc_scd[dev->id_unit].kdc_description = scd_data[dev->id_unit].name; printf("scd%d: <%s>\n", dev->id_unit, scd_data[dev->id_unit].name); - set_drive_params(dev->id_unit); + init_drive(dev->id_unit); cd->flags = SCDINIT; cd->audio_status = CD_AS_AUDIO_INVALID; @@ -199,465 +210,6 @@ int scd_attach(struct isa_device *dev) return 1; } -static int -bcd2bin(bcd_t b) -{ - return (b >> 4) * 10 + (b & 15); -} - -static bcd_t -bin2bcd(int b) -{ - return ((b / 10) << 4) | (b % 10); -} - -static void -hsg2msf(int hsg, bcd_t *msf) -{ - hsg += 150; - M_msf(msf) = bin2bcd(hsg / 4500); - hsg %= 4500; - S_msf(msf) = bin2bcd(hsg / 75); - F_msf(msf) = bin2bcd(hsg % 75); -} - -static int -msf2hsg(bcd_t *msf) -{ - return (bcd2bin(M_msf(msf)) * 60 + - bcd2bin(S_msf(msf))) * 75 + - bcd2bin(F_msf(msf)) - 150; -} - -static int -handle_attention(unsigned unit) -{ - unsigned port = scd_data[unit].iobase; - unsigned char atten_code; - static int loop_count = 0; - int i; - - if (!IS_ATTENTION(port)) { - loop_count = 0; - return 0; - } - - if (loop_count > 20) { - /* printf("scd%d: Too many attentions: %d\n", unit, loop_count); */ - loop_count = 0; - return 0; - } - - write_control(port, SCD_ATTN_CLR_BIT); - atten_code = inb(port+SCD_RESULT_REG); - -#if DEBUG - printf("scd%d: DEBUG: ********* ATTENTION = 0x%x\n", unit, atten_code); -#endif - - switch (atten_code) { - /* Someone changed the CD. Mark it as changed */ - case SONY_MECH_LOADED_ATTN: - scd_data[unit].flags &= ~(SCDTOC|SCDSPINNING|SCDVALID); - break; - - case SONY_SPIN_DOWN_COMPLETE_ATTN: - scd_data[unit].flags &= ~SCDSPINNING; - break; - - case SONY_SPIN_UP_COMPLETE_ATTN: - scd_data[unit].flags |= SCDSPINNING; - break; - - case SONY_AUDIO_PLAY_DONE_ATTN: - scd_data[unit].audio_status = CD_AS_PLAY_COMPLETED; -/* read_subcode(); */ - break; - - case SONY_EJECT_PUSHED_ATTN: - scd_data[unit].flags &= ~SCDVALID; - break; - - case SONY_LEAD_IN_ERR_ATTN: - case SONY_LEAD_OUT_ERR_ATTN: - case SONY_DATA_TRACK_ERR_ATTN: - case SONY_AUDIO_PLAYBACK_ERR_ATTN: - break; - default: -#if DEBUG - printf("scd%d: unknown ATTENTION = 0x%x\n", unit, atten_code); -#endif - break; - } - loop_count++; - return 1; -} - -/* Returns 0 OR sony error code */ -static int -spin_up(unsigned unit) -{ - unsigned char res_reg[12]; - unsigned int res_size; - int res; - int loop_count = 0; - -again: - res = do_cmd(unit, SCD_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - if (res != 0) { -#if DEBUG > 1 - printf("scd%d: CMD_SPIN_UP error 0x%x\n", unit, res); -#endif - return res; - } - - if (!(scd_data[unit].flags & SCDTOC)) { /* XXX probably no good ... */ - res = do_cmd(unit, SCD_READ_TOC_CMD, NULL, 0, res_reg, &res_size); - if (res == SONY_NOT_SPIN_ERR) { - if (loop_count++ < 3) - goto again; - return res; - } - } - - if (res != 0) - return res; - - scd_data[unit].flags |= SCDSPINNING; - - return 0; -} - -static struct s_sony_tracklist * -get_tl(struct s_sony_session_toc *toc, int size) -{ - struct s_sony_tracklist *tl = (struct s_sony_tracklist *)&toc->pointb0; - - (char *)tl -= 1; /* Cannot take address of bitfield (above) */ - - if (toc->pointb0 != 0xb0) { -#if DEBUG - if (toc->pointb0 != tl->track) - printf("scd: WARNING: pointb0 != track0\n"); -#endif - return tl; - } - (char *)tl += 9; - if (toc->pointb1 != 0xb1) - return tl; - (char *)tl += 9; - if (toc->pointb2 != 0xb2) - return tl; - (char *)tl += 9; - if (toc->pointb3 != 0xb3) - return tl; - (char *)tl += 9; - if (toc->pointb4 != 0xb4) - return tl; - (char *)tl += 9; - if (toc->pointc0 != 0xc0) - return tl; - (char *)tl += 9; - return tl; -} - -/* Return value same as do_cmd() */ -static int -read_toc(dev_t dev) -{ - unsigned unit; - struct scd_data *cd; - unsigned part = 0; /* For now ... */ - unsigned char params[1]; - unsigned char msf[3]; - unsigned int res_size; - struct s_sony_session_toc toc; - struct s_sony_tracklist *tl; - int i, j; - u_long first, last; - - unit = scd_unit(dev); - cd = scd_data + unit; - - params[0] = part+1; - i = do_cmd(unit, SCD_REQ_TOC_DATA_SPEC_CMD, - params, - 1, - (unsigned char *)toc.exec_status, - &res_size); - if (i != 0) - return i; - - tl = get_tl(&toc, res_size); - first = msf2hsg(tl->track_start_msf); - last = msf2hsg(toc.lead_out_start_msf); - cd->blksize = SCDBLKSIZE; - cd->disksize = last*cd->blksize/DEV_BSIZE; - -#if DEBUG - printf("scd%d: firstsector = %d, lastsector = %d", unit, - first, last); -#endif - - cd->first_track = bcd2bin(toc.first_track_num); - cd->last_track = bcd2bin(toc.last_track_num); - if (cd->last_track > (MAX_TRACKS-2)) - cd->last_track = MAX_TRACKS-2; - for (j = 0, i = cd->first_track; i <= cd->last_track; i++, j++) { - bcopy(tl[j].track_start_msf, cd->toc[i].start_msf, 3); -#if DEBUG - if ((j % 3) == 0) - printf("\nscd%d: tracks ", unit); - printf("[%03d: %2d %2d %2d] ", i, - bcd2bin(cd->toc[i].start_msf[0]), - bcd2bin(cd->toc[i].start_msf[1]), - bcd2bin(cd->toc[i].start_msf[2])); -#endif - } - hsg2msf(msf2hsg(toc.lead_out_start_msf)-1, msf); - bcopy(msf, cd->toc[cd->last_track+1].start_msf, 3); -#if DEBUG - i = cd->last_track+1; - printf("[END: %2d %2d %2d]\n", - bcd2bin(cd->toc[i].start_msf[0]), - bcd2bin(cd->toc[i].start_msf[1]), - bcd2bin(cd->toc[i].start_msf[2])); -#endif - - bzero(&cd->dlabel,sizeof(struct disklabel)); - /* filled with spaces first */ - strncpy(cd->dlabel.d_typename," ", - sizeof(cd->dlabel.d_typename)); - strncpy(cd->dlabel.d_typename, cd->name, - min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1)); - strncpy(cd->dlabel.d_packname,"unknown ", - sizeof(cd->dlabel.d_packname)); - cd->dlabel.d_secsize = cd->blksize; - cd->dlabel.d_nsectors = 100; - cd->dlabel.d_ntracks = 1; - cd->dlabel.d_ncylinders = (cd->disksize/100)+1; - cd->dlabel.d_secpercyl = 100; - cd->dlabel.d_secperunit = cd->disksize; - cd->dlabel.d_rpm = 300; - cd->dlabel.d_interleave = 1; - cd->dlabel.d_flags = D_REMOVABLE; - cd->dlabel.d_npartitions= 1; - cd->dlabel.d_partitions[0].p_offset = 0; - cd->dlabel.d_partitions[0].p_size = cd->disksize; - cd->dlabel.d_partitions[0].p_fstype = 9; - cd->dlabel.d_magic = DISKMAGIC; - cd->dlabel.d_magic2 = DISKMAGIC; - cd->dlabel.d_checksum = dkcksum(&cd->dlabel); - - cd->flags |= SCDTOC; - - return 0; -} - -static inline void -write_control(unsigned port, unsigned data) -{ - outb(port + SCD_CONTROL_REG, data); -} - -static int -write_params(unsigned port, unsigned char *params, int num_params) -{ - int retry_count = 20000; - - while ((retry_count-- > 0) && (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT))) - ; - if (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT)) - return EIO; - - while (num_params-- > 0) - outb(port+SCD_PARAM_REG, *params++); - - return 0; -} - -static void -set_drive_params(unsigned unit) -{ - unsigned char res_reg[12]; - unsigned int res_size; - unsigned char params[3]; - int res; - - params[0] = SCD_SD_MECH_CONTROL; - params[1] = 0x03; /* Set auto spin up and auto eject */ - if (scd_data[unit].double_speed) - params[1] |= 0x04; /* Set the drive to double speed if possible */ - - res = do_cmd(unit, SCD_SET_DRIVE_PARAM_CMD, - params, - 2, - res_reg, - &res_size); - - if (res != 0) - printf("scd%d: Unable to set mechanical parameters. Errcode = 0x%x\n", res); -} - -static int -get_result(unsigned unit, unsigned char *result) -{ - unsigned int port = scd_data[unit].iobase; - unsigned int flags = scd_data[unit].flags; - unsigned int res_reg = port + SCD_RESULT_REG; - unsigned char c; - int len = 0; - int datalen = 0; - int loop_index = 0; - unsigned int retry_count; - - while (handle_attention(unit)) - ; - - if (flags & SCDPROBING) { - retry_count = 50; /* While probing tsleep doesn't sleep */ - while ((retry_count-- > 0) - && (IS_BUSY(port) || !STATUS_BIT(port, SCD_RES_RDY_BIT))) - { - DELAY(100000); - while (handle_attention(unit)) - ; - } - } else { - retry_count = 100; /* Wait max 10 sec. */ - while ((retry_count-- > 0) - && (IS_BUSY(port) || !STATUS_BIT(port, SCD_RES_RDY_BIT))) - { - tsleep(get_result, PZERO - 1, "get_result", hz/10); - while (handle_attention(unit)) - ; - } - } - while (handle_attention(unit)) - ; - - if (IS_BUSY(port) || !STATUS_BIT(port, SCD_RES_RDY_BIT)) { - printf("scd%d: timeout @%d\n", unit, __LINE__); - return -EIO; - } - - write_control(port, SCD_RES_RDY_CLR_BIT); - switch ((*result++ = inb(res_reg)) & 0xf0) { - case 0x50: - return 1; - case 0x20: - *result = inb(res_reg); - return 2; - case 0x00: - default: - datalen = inb(res_reg); - len = 2; - loop_index = len; - *result++ = datalen; - } - -#if DEBUG - printf("scd%d: DEBUG: get_result bytes = %d\n", unit, datalen); -#endif - - while (datalen-- > 0) { - if (loop_index++ >= 10) { - loop_index = 1; - retry_count = 200; - if (flags & SCDPROBING) - retry_count += 10000; - while ((retry_count > 0) && !STATUS_BIT(port, SCD_RES_RDY_BIT)) { - retry_count--; - if (retry_count < 100) - tsleep(get_result, PZERO - 1, "get_result", hz/50); - else - DELAY(10); - } - if (!STATUS_BIT(port, SCD_RES_RDY_BIT)) { - printf("scd%d: timeout @%d\n", unit, __LINE__); - return -EIO; - } - write_control(port, SCD_RES_RDY_CLR_BIT); - } - *result++ = inb(res_reg); - len++; - } - return len; -} - -/* Returns 0 or (sony error code) or -(errno code) */ -static int -do_cmd(unsigned int unit, - unsigned char cmd, - unsigned char *params, - unsigned int num_params, - unsigned char *result_buffer, - unsigned int *result_size) -{ - unsigned port = scd_data[unit].iobase; - unsigned int retry_count; - int res; - -#if DEBUG - printf("SCD: do_cmd: 0x%x\n", cmd); -#endif - - while (handle_attention(unit)) - ; - retry_count = 40; - while (retry_count-- > 0 && IS_BUSY(port)) { - while (handle_attention(unit)) - ; - tsleep(do_cmd, PZERO - 1, "do_cmd", hz/4); - } - if (IS_BUSY(port)) { -#if DEBUG - printf("SCD: timeout out @%d\n", __LINE__); -#endif - return -EIO; - } - write_control(port, SCD_RES_RDY_CLR_BIT); - write_control(port, SCD_PARAM_CLR_BIT); - - retry_count = 40; - while ((retry_count-- > 0) && (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT))) - ; - if (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT)) - return -EIO; - - while (num_params-- > 0) - outb(port+SCD_PARAM_REG, *params++); - outb(port+SCD_CMD_REG, cmd); - - if ((res = get_result(unit, result_buffer)) < 0) - return res; - if (res < 2) - return -EIO; - *result_size = res; - if (result_buffer[0] == 0x20) - return result_buffer[1]; - return 0; -} - -static void -print_error(int unit, int errcode) -{ - switch (errcode) { - case SONY_NOT_LOAD_ERR: - printf("scd%d: door is open\n", unit); - break; - case SONY_NO_DISK_ERR: - printf("scd%d: no cd inside\n", unit); - break; - default: - if (errcode < 0) - printf("scd%d: device timeout\n", unit); - else - printf("scd%d: unexpected error 0x%x\n", unit, errcode); - break; - } -} - int scdopen(dev_t dev) { @@ -678,12 +230,10 @@ scdopen(dev_t dev) return ENXIO; /* invalidated in the meantime? mark all open part's invalid */ - if (cd->openflags) + if (cd->openflag) return ENXIO; -#if DEBUG - printf("scd%d: DEBUG: status = 0x%x\n", unit, inb(cd->iobase+SCD_STATUS_REG)); -#endif + XDEBUG(1,("scd%d: DEBUG: status = 0x%x\n", unit, inb(cd->iobase+IREG_STATUS))); if ((rc = spin_up(unit)) != 0) { print_error(unit, rc); @@ -693,7 +243,7 @@ scdopen(dev_t dev) int loop_count = 3; while (loop_count-- > 0 && (rc = read_toc(dev)) != 0) { - if (rc == SONY_NOT_SPIN_ERR) { + if (rc == ERR_NOT_SPINNING) { rc = spin_up(unit); if (rc) { print_error(unit, rc);\ @@ -706,7 +256,7 @@ scdopen(dev_t dev) } } - cd->openflags = 1; + cd->openflag = 1; cd->flags |= SCDVALID; kdc_scd[unit].kdc_state = DC_BUSY; @@ -729,16 +279,18 @@ scdclose(dev_t dev) part = scd_part(dev); phys = scd_phys(dev); - if (!(cd->flags & SCDINIT) || !cd->openflags) + if (!(cd->flags & SCDINIT) || !cd->openflag) return ENXIO; - (void)do_cmd(unit, SCD_SPIN_DOWN_CMD, (char *)0, 0, rdata, &rlen); - cd->flags &= ~SCDSPINNING; + if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) { + (void)send_cmd(unit, CMD_SPIN_DOWN, 0); + cd->flags &= ~SCDSPINNING; + } kdc_scd[unit].kdc_state = DC_IDLE; /* close channel */ - cd->openflags = 0; + cd->openflag = 0; return 0; } @@ -753,10 +305,8 @@ scdstrategy(struct buf *bp) cd = scd_data + unit; - /* test validity */ -#if DEBUG > 1 - printf("scd%d: DEBUG: strategy: block=%ld, bcount=%ld\n", unit, bp->b_blkno, bp->b_bcount); -#endif + XDEBUG(2, ("scd%d: DEBUG: strategy: block=%ld, bcount=%ld\n", unit, bp->b_blkno, bp->b_bcount)); + if (unit >= NSCD || bp->b_blkno < 0 || (bp->b_bcount % SCDBLKSIZE)) { printf("scd%d: strategy failure: blkno = %d, bcount = %d\n", unit, bp->b_blkno, bp->b_bcount); @@ -827,7 +377,7 @@ scd_start(int unit) if ((bp = qp->b_actf) != 0) { /* block found to process, dequeue */ - qp->b_actf = bp->b_actf; /* changed from: bp->av_forw <se> */ + qp->b_actf = bp->b_actf; cd->flags |= SCDMBXBSY; splx(s); } else { @@ -859,9 +409,7 @@ scdioctl(dev_t dev, int cmd, caddr_t addr, int flags) part = scd_part(dev); cd = scd_data + unit; -#if DEBUG - printf("scd%d: ioctl: cmd=0x%lx\n", unit, cmd); -#endif + XDEBUG(1, ("scd%d: ioctl: cmd=0x%lx\n", unit, cmd)); if (!(cd->flags & SCDVALID)) return EIO; @@ -897,9 +445,9 @@ scdioctl(dev_t dev, int cmd, caddr_t addr, int flags) case CDIOCSETRIGHT: return EINVAL; case CDIOCRESUME: -/* return scd_resume(unit); */ + return scd_resume(unit); case CDIOCPAUSE: -/* return scd_pause(unit); */ + return scd_pause(unit); case CDIOCSTART: return EINVAL; case CDIOCSTOP: @@ -908,12 +456,35 @@ scdioctl(dev_t dev, int cmd, caddr_t addr, int flags) return scd_eject(unit); case CDIOCALLOW: return 0; + case CDIOCSETDEBUG: +#ifdef SCD_DEBUG + scd_debuglevel++; +#endif + return 0; + case CDIOCCLRDEBUG: +#ifdef SCD_DEBUG + scd_debuglevel = 0; + +#endif + return 0; default: printf("scd%d: unsupported ioctl (cmd=0x%lx)\n", unit, cmd); return ENOTTY; } } +int +scdsize(dev_t dev) +{ + return -1; +} + +void +scdintr() +{ + return; +} + /*************************************************************** * lower level of driver starts here **************************************************************/ @@ -928,14 +499,19 @@ scd_playtracks(int unit, struct ioc_play_track *pt) int rc, i; if (!(cd->flags & SCDTOC) && (rc = read_toc(unit)) != 0) { - print_error(unit, rc); - return EIO; + if (rc == -ERR_NOT_SPINNING) { + if (spin_up(unit) != 0) + return EIO; + rc = read_toc(unit); + } + if (rc != 0) { + print_error(unit, rc); + return EIO; + } } -#if DEBUG - printf("scd%d: playtracks from %d:%d to %d:%d\n", unit, - a, pt->start_index, z, pt->end_index); -#endif + XDEBUG(1, ("scd%d: playtracks from %d:%d to %d:%d\n", unit, + a, pt->start_index, z, pt->end_index)); if ( a < cd->first_track || a > cd->last_track @@ -943,40 +519,57 @@ scd_playtracks(int unit, struct ioc_play_track *pt) || z > cd->last_track) return EINVAL; - for (i = 0; i < 3; i++) { - ((char *)&msf.start_m)[i] = bcd2bin(cd->toc[a].start_msf[i]); - ((char *)&msf.end_m)[i] = bcd2bin(cd->toc[z+1].start_msf[i]); - } - return scd_playmsf(unit, &msf); + bcopy(cd->toc[a].start_msf, &msf.start_m, 3); + hsg2msf(msf2hsg(cd->toc[z+1].start_msf)-1, &msf.end_m); + + return scd_play(unit, &msf); } +/* The start/end msf is expected to be in bin format */ static int -scd_playmsf(int unit, struct ioc_play_msf *msf) +scd_playmsf(int unit, struct ioc_play_msf *msfin) { - struct scd_data *cd = scd_data + unit; - unsigned char sdata[10]; - unsigned char rdata[10]; - int rlen, rc; + struct ioc_play_msf msf; - sdata[0] = 0x03; - sdata[1] = bin2bcd(msf->start_m); - sdata[2] = bin2bcd(msf->start_s); - sdata[3] = bin2bcd(msf->start_f); - sdata[4] = bin2bcd(msf->end_m); - sdata[5] = bin2bcd(msf->end_s); - sdata[6] = bin2bcd(msf->end_f); + msf.start_m = bin2bcd(msfin->start_m); + msf.start_s = bin2bcd(msfin->start_s); + msf.start_f = bin2bcd(msfin->start_f); + msf.end_m = bin2bcd(msfin->end_m); + msf.end_s = bin2bcd(msfin->end_s); + msf.end_f = bin2bcd(msfin->end_f); -#if DEBUG - printf("scd%d: playmsf: %02d:%02d:%02d -> %02d:%02d:%02d\n", unit, + return scd_play(unit, &msf); +} + +/* The start/end msf is expected to be in bcd format */ +static int +scd_play(int unit, struct ioc_play_msf *msf) +{ + struct scd_data *cd = scd_data + unit; + int i, rc; + + XDEBUG(1, ("scd%d: playing: %02x:%02x:%02x -> %02x:%02x:%02x\n", unit, msf->start_m, msf->start_s, msf->start_f, - msf->end_m, msf->end_s, msf->end_f); -#endif + msf->end_m, msf->end_s, msf->end_f)); - if ((rc = do_cmd(unit, SCD_AUDIO_PLAYBACK_CMD, sdata, 7, rdata, &rlen)) != 0) { - print_error(unit, rc); - return EIO; + for (i = 0; i < 2; i++) { + rc = send_cmd(unit, CMD_PLAY_AUDIO, 7, + 0x03, + msf->start_m, msf->start_s, msf->start_f, + msf->end_m, msf->end_s, msf->end_f); + if (rc == -ERR_NOT_SPINNING) { + cd->flags &= ~SCDSPINNING; + if (spin_up(unit) != 0) + return EIO; + } else if (rc < 0) { + print_error(unit, rc); + return EIO; + } else { + break; + } } cd->audio_status = CD_AS_PLAY_IN_PROGRESS; + bcopy((char *)msf, (char *)&cd->last_play, sizeof(struct ioc_play_msf)); return 0; } @@ -984,27 +577,63 @@ static int scd_stop(int unit) { struct scd_data *cd = scd_data + unit; - char rdata[10]; - int rlen; - (void)do_cmd(unit, SCD_AUDIO_STOP_CMD, (char *)0, 0, rdata, &rlen); + (void)send_cmd(unit, CMD_STOP_AUDIO, 0); cd->audio_status = CD_AS_PLAY_COMPLETED; return 0; } static int +scd_pause(int unit) +{ + struct scd_data *cd = scd_data + unit; + struct sony_subchannel_position_data subpos; + + if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) + return EINVAL; + + if (read_subcode(unit, &subpos) != 0) + return EIO; + + if (send_cmd(unit, CMD_STOP_AUDIO, 0) != 0) + return EIO; + + cd->last_play.start_m = subpos.abs_msf[0]; + cd->last_play.start_s = subpos.abs_msf[1]; + cd->last_play.start_f = subpos.abs_msf[2]; + cd->audio_status = CD_AS_PLAY_PAUSED; + + XDEBUG(1, ("scd%d: pause @ %02x:%02x:%02x\n", unit, + cd->last_play.start_m, + cd->last_play.start_s, + cd->last_play.start_f)); + + return 0; +} + +static int +scd_resume(int unit) +{ + if (scd_data[unit].audio_status != CD_AS_PLAY_PAUSED) + return EINVAL; + return scd_play(unit, &scd_data[unit].last_play); +} + +static int scd_eject(int unit) { struct scd_data *cd = scd_data + unit; int port = cd->iobase; - char rdata[10]; - int rlen; - (void)do_cmd(unit, SCD_AUDIO_STOP_CMD, (char *)0, 0, rdata, &rlen); - (void)do_cmd(unit, SCD_SPIN_DOWN_CMD, (char *)0, 0, rdata, &rlen); + cd->audio_status = CD_AS_AUDIO_INVALID; cd->flags &= ~(SCDSPINNING|SCDTOC); - if (do_cmd(unit, SCD_EJECT_CMD, (char *)0, 0, rdata, &rlen) != 0) + + if (send_cmd(unit, CMD_STOP_AUDIO, 0) != 0 || + send_cmd(unit, CMD_SPIN_DOWN, 0) != 0 || + send_cmd(unit, CMD_EJECT, 0) != 0) + { return EIO; + } return 0; } @@ -1012,14 +641,12 @@ static int scd_subchan(int unit, struct ioc_read_subchannel *sc) { struct scd_data *cd = scd_data + unit; - struct s_sony_subcode q; + struct sony_subchannel_position_data q; struct cd_sub_channel_info data; -#if DEBUG - printf("scd%d: subchan af=%d, df=%d\n", unit, + XDEBUG(1, ("scd%d: subchan af=%d, df=%d\n", unit, sc->address_format, - sc->data_format); -#endif + sc->data_format)); if (sc->address_format != CD_MSF_FORMAT) return EINVAL; @@ -1032,7 +659,7 @@ scd_subchan(int unit, struct ioc_read_subchannel *sc) data.header.audio_status = cd->audio_status; data.what.position.data_format = CD_MSF_FORMAT; - data.what.position.track_number = bcd2bin(q.track_num); + data.what.position.track_number = bcd2bin(q.track_number); data.what.position.reladdr.msf.unused = 0; data.what.position.reladdr.msf.minute = bcd2bin(q.rel_msf[0]); data.what.position.reladdr.msf.second = bcd2bin(q.rel_msf[1]); @@ -1047,74 +674,63 @@ scd_subchan(int unit, struct ioc_read_subchannel *sc) return 0; } -/* check to see if a Sony CD-ROM is attached to the ISA bus */ - -static void -get_drive_configuration(unsigned short unit, - unsigned char res_reg[], - unsigned int *res_size) -{ - unsigned port = scd_data[unit].iobase; - int retry_count = 10000; - - outb(port + SCD_CONTROL_REG, SCD_DRIVE_RESET_BIT); /* reset drive */ - - while ((retry_count-- > 0) && (!IS_ATTENTION(port))) - DELAY(10); - - /* If I use a shorter delay, I get a timeout in get_result() */ - /* I guess there is a better way, but I don't have the manuals */ - DELAY(500000); - - (void)do_cmd(unit, SCD_REQ_DRIVE_CONFIG_CMD, - NULL, - 0, - (unsigned char *) res_reg, - res_size); - return; -} - int scd_probe(struct isa_device *dev) { - struct s_sony_drive_config drive_config; + struct sony_drive_configuration drive_config; int unit = dev->id_unit; - unsigned int res_size; - int i; - static char buf[8+16+8+3]; - char *s = buf; + int rc; + static char namebuf[8+16+8+3]; + char *s = namebuf; + int loop_count = 0; scd_data[unit].flags = SCDPROBING; scd_data[unit].iobase = dev->id_iobase; bzero(&drive_config, sizeof(drive_config)); - get_drive_configuration(unit, drive_config.exec_status, &res_size); - if (res_size < sizeof(drive_config) - || (drive_config.exec_status[0] & 0xf0) != 0x00) - { +again: + /* Reset drive */ + write_control(dev->id_iobase, CBIT_RESET_DRIVE); + + /* Calm down */ + DELAY(300000); + + /* Only the ATTENTION bit may be set */ + if ((inb(dev->id_iobase+IREG_STATUS) & ~1) != 0) { + XDEBUG(1, ("scd: too many bits set. probe failed.\n")); return 0; } + rc = send_cmd(unit, CMD_GET_DRIVE_CONFIG, 0); + if (rc != sizeof(drive_config)) { + /* Sometimes if the drive is playing audio I get */ + /* the bad result 82. Fix by repeating the reset */ + if (rc > 0 && loop_count++ == 0) + goto again; + return 0; + } + if (get_result(unit, rc, (u_char *)&drive_config) != 0) + return 0; - bcopy(drive_config.vendor_id, buf, 8); - s = buf+8; - while (*(s-1) == ' ') + bcopy(drive_config.vendor, namebuf, 8); + s = namebuf+8; + while (*(s-1) == ' ') /* Strip trailing spaces */ s--; *s++ = ' '; - bcopy(drive_config.product_id, s, 16); + bcopy(drive_config.product, s, 16); s += 16; while (*(s-1) == ' ') s--; *s++ = ' '; - bcopy(drive_config.product_rev_level, s, 8); + bcopy(drive_config.revision, s, 8); s += 8; while (*(s-1) == ' ') s--; *s = 0; - scd_data[unit].name = buf; + scd_data[unit].name = namebuf; - if (SONY_HWC_DOUBLE_SPEED(drive_config)) + if (drive_config.config & 0x10) scd_data[unit].double_speed = 1; else scd_data[unit].double_speed = 0; @@ -1123,17 +739,24 @@ scd_probe(struct isa_device *dev) } static int -read_subcode(int unit, struct s_sony_subcode *sc) +read_subcode(int unit, struct sony_subchannel_position_data *sc) { - int rlen; + int rc; - if (do_cmd(unit, SCD_REQ_SUBCODE_ADDRESS_CMD, - (char *)0, 0, sc->exec_status, &rlen) != 0) + rc = send_cmd(unit, CMD_GET_SUBCHANNEL_DATA, 0); + if (rc < 0 || rc < sizeof(*sc)) + return EIO; + if (get_result(unit, rc, (u_char *)sc) != 0) return EIO; return 0; } -/* State machine copied from mcd.c /Micke */ +/* State machine copied from mcd.c */ + +/* This (and the code in mcd.c) will not work with more than one drive */ +/* because there is only one mbxsave below. Should fix that some day. */ +/* (mbxsave & state should probably be included in the scd_data struct and */ +/* the unit number used as first argument to scd_doread().) /Micke */ /* state machine to process read requests * initialize with SCD_S_BEGIN: reset state machine @@ -1154,7 +777,6 @@ scd_doread(int state, struct scd_mbx *mbxin) int port = mbx->port; struct buf *bp = mbx->bp; struct scd_data *cd = scd_data + unit; - int reg,i,k,c; int blknum; caddr_t addr; @@ -1170,8 +792,7 @@ loop: /* get status */ mbx->count = RDELAY_WAIT; - while (handle_attention(unit)) - ; + process_attention(unit); goto trystat; case SCD_S_WAITSTAT: @@ -1188,15 +809,14 @@ trystat: return; } - while (handle_attention(unit)) - ; -#if 0 + process_attention(unit); + /* reject, if audio active */ - if (cd->status & SCDAUDIOBSY) { + if (cd->audio_status & CD_AS_PLAY_IN_PROGRESS) { printf("scd%d: audio is active\n",unit); - goto readerr; + goto harderr; } -#endif + mbx->sz = cd->blksize; firstblock: @@ -1211,17 +831,16 @@ nextblock: blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE)) + mbx->p_offset + mbx->skip/mbx->sz; -#if DEBUG > 1 - printf("scd%d: scd_doread: read blknum=%d\n", unit, blknum); -#endif + XDEBUG(2, ("scd%d: scd_doread: read blknum=%d\n", unit, blknum)); + /* build parameter block */ hsg2msf(blknum, sdata); - write_control(port, SCD_RES_RDY_CLR_BIT); - write_control(port, SCD_PARAM_CLR_BIT); - write_control(port, SCD_DATA_RDY_CLR_BIT); + write_control(port, CBIT_RESULT_READY_CLEAR); + write_control(port, CBIT_RPARAM_CLEAR); + write_control(port, CBIT_DATA_READY_CLEAR); - if (FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT)) + if (FSTATUS_BIT(port, FBIT_WPARAM_READY)) goto writeparam; mbx->count = 100; @@ -1235,25 +854,24 @@ nextblock: printf("scd%d: timeout waiting for drive to spin up.\n", unit); goto harderr; } - if (!STATUS_BIT(port, SCD_RES_RDY_BIT)) { + if (!STATUS_BIT(port, SBIT_RESULT_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITSPIN,hz/100); /* XXX */ return; } - write_control(port, SCD_RES_RDY_CLR_BIT); - switch ((i = inb(port+SCD_RESULT_REG)) & 0xf0) { + write_control(port, CBIT_RESULT_READY_CLEAR); + switch ((i = inb(port+IREG_RESULT)) & 0xf0) { case 0x20: - i = inb(port+SCD_RESULT_REG); + i = inb(port+IREG_RESULT); print_error(unit, i); goto harderr; case 0x00: - (void)inb(port+SCD_RESULT_REG); + (void)inb(port+IREG_RESULT); cd->flags |= SCDSPINNING; break; } -#if DEBUG - printf("scd%d: DEBUG: spin up complete\n", unit); -#endif + XDEBUG(1, ("scd%d: DEBUG: spin up complete\n", unit)); + state = SCD_S_BEGIN1; goto loop; @@ -1263,31 +881,26 @@ nextblock: printf("scd%d: timeout. write param not ready.\n",unit); goto harderr; } - if (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT)) { + if (!FSTATUS_BIT(port, FBIT_WPARAM_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITFIFO,hz/100); /* XXX */ return; } -#if DEBUG - printf("scd%d: mbx->count (writeparamwait) = %d(%d)\n", unit, mbx->count, 100); -#endif + XDEBUG(1, ("scd%d: mbx->count (writeparamwait) = %d(%d)\n", unit, mbx->count, 100)); writeparam: /* The reason this test isn't done 'till now is to make sure */ - /* that it is ok to send the SPIN_UP command below */ + /* that it is ok to send the SPIN_UP cmd below. */ if (!(cd->flags & SCDSPINNING)) { - -#if DEBUG - printf("scd%d: spinning up drive ...\n", unit); -#endif - outb(port+SCD_CMD_REG, SCD_SPIN_UP_CMD); + XDEBUG(1, ("scd%d: spinning up drive ...\n", unit)); + outb(port+OREG_COMMAND, CMD_SPIN_UP); mbx->count = 300; timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITSPIN,hz/100); /* XXX */ return; } - reg = port + SCD_PARAM_REG; + reg = port + OREG_WPARAMS; /* send the read command */ disable_intr(); outb(reg, sdata[0]); @@ -1296,12 +909,12 @@ writeparam: outb(reg, 0); outb(reg, 0); outb(reg, 1); - outb(port+SCD_CMD_REG, SCD_READ_BLKERR_STAT_CMD); + outb(port+OREG_COMMAND, CMD_READ); enable_intr(); mbx->count = RDELAY_WAITREAD; for (i = 0; i < 50; i++) { - if (STATUS_BIT(port, SCD_DATA_RDY_BIT)) + if (STATUS_BIT(port, SBIT_DATA_READY)) goto got_data; DELAY(100); } @@ -1313,34 +926,30 @@ writeparam: case SCD_S_WAITREAD: untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITREAD); if (mbx->count-- <= 0) { - if (STATUS_BIT(port, SCD_RES_RDY_BIT)) + if (STATUS_BIT(port, SBIT_RESULT_READY)) goto got_param; printf("scd%d: timeout while reading data\n",unit); goto readerr; } - if (!STATUS_BIT(port, SCD_DATA_RDY_BIT)) { - while (handle_attention(unit)) - ; + if (!STATUS_BIT(port, SBIT_DATA_READY)) { + process_attention(unit); if (!(cd->flags & SCDVALID)) goto changed; timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITREAD,hz/100); /* XXX */ return; } - -#if DEBUG > 1 - printf("scd%d: mbx->count (after RDY_BIT) = %d(%d)\n", unit, mbx->count, RDELAY_WAITREAD); -#endif + XDEBUG(2, ("scd%d: mbx->count (after RDY_BIT) = %d(%d)\n", unit, mbx->count, RDELAY_WAITREAD)); got_data: /* data is ready */ addr = bp->b_un.b_addr + mbx->skip; - write_control(port, SCD_DATA_RDY_CLR_BIT); - insb(port+SCD_READ_REG, addr, mbx->sz); + write_control(port, CBIT_DATA_READY_CLEAR); + insb(port+IREG_DATA, addr, mbx->sz); mbx->count = 100; for (i = 0; i < 20; i++) { - if (STATUS_BIT(port, SCD_RES_RDY_BIT)) + if (STATUS_BIT(port, SBIT_RESULT_READY)) goto waitfor_param; DELAY(100); } @@ -1354,34 +963,32 @@ got_data: } waitfor_param: - if (!STATUS_BIT(port, SCD_RES_RDY_BIT)) { + if (!STATUS_BIT(port, SBIT_RESULT_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITPARAM,hz/100); /* XXX */ return; } -#if DEBUG - if (mbx->count < 100) +#if SCD_DEBUG + if (mbx->count < 100 && scd_debuglevel > 0) printf("scd%d: mbx->count (paramwait) = %d(%d)\n", unit, mbx->count, 100); #endif got_param: - write_control(port, SCD_RES_RDY_CLR_BIT); - switch ((i = inb(port+SCD_RESULT_REG)) & 0xf0) { + write_control(port, CBIT_RESULT_READY_CLEAR); + switch ((i = inb(port+IREG_RESULT)) & 0xf0) { case 0x50: switch (i) { - case SONY_UNREC_CIRC_ERR: - case SONY_UNREC_LECC_ERR: + case ERR_FATAL_READ_ERROR1: + case ERR_FATAL_READ_ERROR2: printf("scd%d: unrecoverable read error 0x%x\n", unit, i); goto harderr; } break; case 0x20: - i = inb(port+SCD_RESULT_REG); + i = inb(port+IREG_RESULT); switch (i) { - case SONY_NOT_SPIN_ERR: -#if DEBUG - printf("scd%d: read error: drive not spinning\n", unit); -#endif + case ERR_NOT_SPINNING: + XDEBUG(1, ("scd%d: read error: drive not spinning\n", unit)); if (mbx->retry-- > 0) { state = SCD_S_BEGIN1; cd->flags &= ~SCDSPINNING; @@ -1393,7 +1000,7 @@ got_param: goto readerr; } case 0x00: - i = inb(port+SCD_RESULT_REG); + i = inb(port+IREG_RESULT); break; } @@ -1433,9 +1040,415 @@ changed: goto harderr; } -int -scdsize(dev_t dev) +static int +bcd2bin(bcd_t b) { - return -1; + return (b >> 4) * 10 + (b & 15); +} + +static bcd_t +bin2bcd(int b) +{ + return ((b / 10) << 4) | (b % 10); +} + +static void +hsg2msf(int hsg, bcd_t *msf) +{ + hsg += 150; + M_msf(msf) = bin2bcd(hsg / 4500); + hsg %= 4500; + S_msf(msf) = bin2bcd(hsg / 75); + F_msf(msf) = bin2bcd(hsg % 75); +} + +static int +msf2hsg(bcd_t *msf) +{ + return (bcd2bin(M_msf(msf)) * 60 + + bcd2bin(S_msf(msf))) * 75 + + bcd2bin(F_msf(msf)) - 150; +} + +static void +process_attention(unsigned unit) +{ + unsigned port = scd_data[unit].iobase; + unsigned char code; + int count = 0; + int i; + + while (IS_ATTENTION(port) && count++ < 30) { + write_control(port, CBIT_ATTENTION_CLEAR); + code = inb(port+IREG_RESULT); + +#if SCD_DEBUG + if (scd_debuglevel > 0) { + if (count == 1) + printf("scd%d: DEBUG: ATTENTIONS = 0x%x", unit, code); + else + printf(",0x%x", code); + } +#endif + + switch (code) { + case ATTEN_SPIN_DOWN: + scd_data[unit].flags &= ~SCDSPINNING; + break; + + case ATTEN_SPIN_UP_DONE: + scd_data[unit].flags |= SCDSPINNING; + break; + + case ATTEN_AUDIO_DONE: + scd_data[unit].audio_status = CD_AS_PLAY_COMPLETED; + break; + + case ATTEN_DRIVE_LOADED: + scd_data[unit].flags &= ~(SCDTOC|SCDSPINNING|SCDVALID); + scd_data[unit].audio_status = CD_AS_AUDIO_INVALID; + break; + + case ATTEN_EJECT_PUSHED: + scd_data[unit].flags &= ~SCDVALID; + break; + } + DELAY(100); + } +#if SCD_DEBUG + if (scd_debuglevel > 0 && count > 0) + printf("\n"); +#endif +} + +/* Returns 0 OR sony error code */ +static int +spin_up(unsigned unit) +{ + unsigned char res_reg[12]; + unsigned int res_size; + int rc; + int loop_count = 0; + +again: + rc = send_cmd(unit, CMD_SPIN_UP, NULL, 0, res_reg, &res_size); + if (rc != 0) { + XDEBUG(2, ("scd%d: CMD_SPIN_UP error 0x%x\n", unit, rc)); + return rc; + } + + if (!(scd_data[unit].flags & SCDTOC)) { + rc = send_cmd(unit, CMD_READ_TOC, 0); + if (rc == ERR_NOT_SPINNING) { + if (loop_count++ < 3) + goto again; + return rc; + } + if (rc != 0) + return rc; + } + + scd_data[unit].flags |= SCDSPINNING; + + return 0; +} + +static struct sony_tracklist * +get_tl(struct sony_toc *toc, int size) +{ + struct sony_tracklist *tl = &toc->tracks[0]; + + if (tl->track != 0xb0) + return tl; + (char *)tl += 9; + if (tl->track != 0xb1) + return tl; + (char *)tl += 9; + if (tl->track != 0xb2) + return tl; + (char *)tl += 9; + if (tl->track != 0xb3) + return tl; + (char *)tl += 9; + if (tl->track != 0xb4) + return tl; + (char *)tl += 9; + if (tl->track != 0xc0) + return tl; + (char *)tl += 9; + return tl; +} + +static int +read_toc(dev_t dev) +{ + unsigned unit; + struct scd_data *cd; + unsigned part = 0; /* For now ... */ + struct sony_toc toc; + struct sony_tracklist *tl; + int rc, i, j; + u_long first, last; + + unit = scd_unit(dev); + cd = scd_data + unit; + + rc = send_cmd(unit, CMD_GET_TOC, 1, part+1); + if (rc < 0) + return rc; + if (rc > sizeof(toc)) { + printf("scd%d: program error: toc too large (%d)\n", unit, rc); + return EIO; + } + if (get_result(unit, rc, (u_char *)&toc) != 0) + return EIO; + + XDEBUG(1, ("scd%d: toc read. len = %d, sizeof(toc) = %d\n", unit, rc, sizeof(toc))); + + tl = get_tl(&toc, rc); + first = msf2hsg(tl->start_msf); + last = msf2hsg(toc.lead_out_start_msf); + cd->blksize = SCDBLKSIZE; + cd->disksize = last*cd->blksize/DEV_BSIZE; + + XDEBUG(1, ("scd%d: firstsector = %d, lastsector = %d", unit, + first, last)); + + cd->first_track = bcd2bin(toc.first_track); + cd->last_track = bcd2bin(toc.last_track); + if (cd->last_track > (MAX_TRACKS-2)) + cd->last_track = MAX_TRACKS-2; + for (j = 0, i = cd->first_track; i <= cd->last_track; i++, j++) { + bcopy(tl[j].start_msf, cd->toc[i].start_msf, 3); +#ifdef SCD_DEBUG + if (scd_debuglevel > 0) { + if ((j % 3) == 0) + printf("\nscd%d: tracks ", unit); + printf("[%03d: %2d %2d %2d] ", i, + bcd2bin(cd->toc[i].start_msf[0]), + bcd2bin(cd->toc[i].start_msf[1]), + bcd2bin(cd->toc[i].start_msf[2])); + } +#endif + } + bcopy(toc.lead_out_start_msf, cd->toc[cd->last_track+1].start_msf, 3); +#ifdef SCD_DEBUG + if (scd_debuglevel > 0) { + i = cd->last_track+1; + printf("[END: %2d %2d %2d]\n", + bcd2bin(cd->toc[i].start_msf[0]), + bcd2bin(cd->toc[i].start_msf[1]), + bcd2bin(cd->toc[i].start_msf[2])); + } +#endif + + bzero(&cd->dlabel,sizeof(struct disklabel)); + /* filled with spaces first */ + strncpy(cd->dlabel.d_typename," ", + sizeof(cd->dlabel.d_typename)); + strncpy(cd->dlabel.d_typename, cd->name, + min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1)); + strncpy(cd->dlabel.d_packname,"unknown ", + sizeof(cd->dlabel.d_packname)); + cd->dlabel.d_secsize = cd->blksize; + cd->dlabel.d_nsectors = 100; + cd->dlabel.d_ntracks = 1; + cd->dlabel.d_ncylinders = (cd->disksize/100)+1; + cd->dlabel.d_secpercyl = 100; + cd->dlabel.d_secperunit = cd->disksize; + cd->dlabel.d_rpm = 300; + cd->dlabel.d_interleave = 1; + cd->dlabel.d_flags = D_REMOVABLE; + cd->dlabel.d_npartitions= 1; + cd->dlabel.d_partitions[0].p_offset = 0; + cd->dlabel.d_partitions[0].p_size = cd->disksize; + cd->dlabel.d_partitions[0].p_fstype = 9; + cd->dlabel.d_magic = DISKMAGIC; + cd->dlabel.d_magic2 = DISKMAGIC; + cd->dlabel.d_checksum = dkcksum(&cd->dlabel); + + cd->flags |= SCDTOC; + + return 0; +} + +static inline void +write_control(unsigned port, unsigned data) +{ + outb(port + OREG_CONTROL, data); +} + +static void +init_drive(unsigned unit) +{ + int rc; + + rc = send_cmd(unit, CMD_SET_DRIVE_PARAM, 2, + 0x05, 0x03 | ((scd_data[unit].double_speed) ? 0x04: 0)); + if (rc != 0) + printf("scd%d: Unable to set parameters. Errcode = 0x%x\n", unit, rc); +} + +/* Returns 0 or errno */ +static int +get_result(u_int unit, int result_len, u_char *result) +{ + unsigned int port = scd_data[unit].iobase; + unsigned int res_reg = port + IREG_RESULT; + unsigned char c; + int loop_index = 2; /* send_cmd() reads two bytes ... */ + + XDEBUG(1, ("scd%d: DEBUG: get_result: bytes=%d\n", unit, result_len)); + + while (result_len-- > 0) { + if (loop_index++ >= 10) { + loop_index = 1; + if (waitfor_status_bits(unit, SBIT_RESULT_READY, 0)) + return EIO; + write_control(port, CBIT_RESULT_READY_CLEAR); + } + if (result) + *result++ = inb(res_reg); + else + (void)inb(res_reg); + } + return 0; +} + +/* Returns -0x100 for timeout, -(drive error code) OR number of result bytes */ +static int +send_cmd(u_int unit, u_char cmd, u_int nargs, ...) +{ + va_list ap; + u_int port = scd_data[unit].iobase; + u_int reg; + u_char c; + int rc; + int i; + + if (waitfor_status_bits(unit, 0, SBIT_BUSY)) { + printf("scd%d: drive busy\n", unit); + return -0x100; + } + + XDEBUG(1,("scd%d: DEBUG: send_cmd: cmd=0x%x nargs=%d", unit, cmd, nargs)); + + write_control(port, CBIT_RESULT_READY_CLEAR); + write_control(port, CBIT_RPARAM_CLEAR); + + for (i = 0; i < 100; i++) + if (FSTATUS_BIT(port, FBIT_WPARAM_READY)) + break; + if (!FSTATUS_BIT(port, FBIT_WPARAM_READY)) { + XDEBUG(1, ("\nscd%d: wparam timeout\n", unit)); + return -EIO; + } + + va_start(ap, nargs); + reg = port + OREG_WPARAMS; + for (i = 0; i < nargs; i++) { + c = (u_char)va_arg(ap, int); + outb(reg, c); + XDEBUG(1, (",{0x%x}", c)); + } + va_end(ap); + XDEBUG(1, ("\n")); + + outb(port+OREG_COMMAND, cmd); + + if (rc = waitfor_status_bits(unit, SBIT_RESULT_READY, SBIT_BUSY)) + return -0x100; + + reg = port + IREG_RESULT; + write_control(port, CBIT_RESULT_READY_CLEAR); + switch ((rc = inb(reg)) & 0xf0) { + case 0x20: + rc = inb(reg); + /* FALL TROUGH */ + case 0x50: + XDEBUG(1, ("scd%d: DEBUG: send_cmd: drive_error=0x%x\n", unit, rc)); + return -rc; + case 0x00: + default: + rc = inb(reg); + XDEBUG(1, ("scd%d: DEBUG: send_cmd: result_len=%d\n", unit, rc)); + return rc; + } +} + +static void +print_error(int unit, int errcode) +{ + switch (errcode) { + case -ERR_CD_NOT_LOADED: + printf("scd%d: door is open\n", unit); + break; + case -ERR_NO_CD_INSIDE: + printf("scd%d: no cd inside\n", unit); + break; + default: + if (errcode == -0x100 || errcode > 0) + printf("scd%d: device timeout\n", unit); + else + printf("scd%d: unexpected error 0x%x\n", unit, -errcode); + break; + } +} + +/* Returns 0 or errno value */ +static int +waitfor_status_bits(int unit, int bits_set, int bits_clear) +{ + u_int port = scd_data[unit].iobase; + u_int flags = scd_data[unit].flags; + u_int reg = port + IREG_STATUS; + u_int max_loop; + u_char c = 0; + + if (flags & SCDPROBING) { + max_loop = 0; + while (max_loop++ < 1000) { + c = inb(reg); + if (c == 0xff) + return EIO; + if (c & SBIT_ATTENTION) { + process_attention(unit); + continue; + } + if ((c & bits_set) == bits_set && + (c & bits_clear) == 0) + { + break; + } + DELAY(10000); + } + } else { + max_loop = 100; + while (max_loop-- > 0) { + c = inb(reg); + if (c & SBIT_ATTENTION) { + process_attention(unit); + continue; + } + if ((c & bits_set) == bits_set && + (c & bits_clear) == 0) + { + break; + } + tsleep(waitfor_status_bits, PZERO - 1, "waitfor", hz/10); + } + } + if ((c & bits_set) == bits_set && + (c & bits_clear) == 0) + { + return 0; + } +#ifdef SCD_DEBUG + if (scd_debuglevel > 0) + printf("scd%d: DEBUG: waitfor: TIMEOUT (0x%x,(0x%x,0x%x))\n", unit, c, bits_set, bits_clear); + else +#endif + printf("scd%d: timeout.\n", unit); + return EIO; } #endif /* NSCD > 0 */ diff --git a/sys/gnu/i386/isa/scdreg.h b/sys/gnu/i386/isa/scdreg.h index 76a25c5d44bb2..223013e778b57 100644 --- a/sys/gnu/i386/isa/scdreg.h +++ b/sys/gnu/i386/isa/scdreg.h @@ -2,7 +2,7 @@ * Copyright (c) 1995 Mikael Hybsch * * The Linux driver cdu31a has been used as a reference when writing this - * code, therefor bringing it under the GNU Public License. The following + * code, therefore bringing it under the GNU Public License. The following * conditions of redistribution therefore apply: * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * $Id$ + * */ #ifndef SCD_H @@ -35,249 +37,108 @@ typedef unsigned char bcd_t; #define S_msf(msf) msf[1] #define F_msf(msf) msf[2] -#define IS_ATTENTION(port) ((inb(port+SCD_STATUS_REG) & SCD_ATTN_BIT) != 0) -#define IS_BUSY(port) ((inb(port+SCD_STATUS_REG) & SCD_BUSY_BIT) != 0) -#define IS_DATA_RDY(port) ((inb(port+SCD_STATUS_REG) & SCD_DATA_RDY_BIT) != 0) -#define STATUS_BIT(port, bit) ((inb(port+SCD_STATUS_REG) & (bit)) != 0) -#define FIFOST_BIT(port, bit) ((inb(port+SCD_FIFOST_REG) & (bit)) != 0) - -#define SCD_CMD_REG 0 -#define SCD_PARAM_REG 1 -#define SCD_WRITE_REG 2 -#define SCD_CONTROL_REG 3 -# define SCD_ATTN_CLR_BIT 0x01 -# define SCD_RES_RDY_CLR_BIT 0x02 -# define SCD_DATA_RDY_CLR_BIT 0x04 -# define SCD_ATTN_INT_EN_BIT 0x08 -# define SCD_RES_RDY_INT_EN_BIT 0x10 -# define SCD_DATA_RDY_INT_EN_BIT 0x20 -# define SCD_PARAM_CLR_BIT 0x40 -# define SCD_DRIVE_RESET_BIT 0x80 - -#define SCD_STATUS_REG 0 -# define SCD_ATTN_BIT 0x01 -# define SCD_RES_RDY_BIT 0x02 -# define SCD_DATA_RDY_BIT 0x04 -# define SCD_ATTN_INT_ST_BIT 0x08 -# define SCD_RES_RDY_INT_ST_BIT 0x10 -# define SCD_DATA_RDY_INT_ST_BIT 0x20 -# define SCD_DATA_REQUEST_BIT 0x40 -# define SCD_BUSY_BIT 0x80 -#define SCD_RESULT_REG 1 -#define SCD_READ_REG 2 -#define SCD_FIFOST_REG 3 -# define SCD_PARAM_WRITE_RDY_BIT 0x01 -# define SCD_PARAM_REG_EMPTY_BIT 0x02 -# define SCD_RES_REG_NOT_EMP_BIT 0x04 -# define SCD_RES_REG_FULL_BIT 0x08 - -#define SCD_REQ_DRIVE_CONFIG_CMD 0x00 -#define SCD_REQ_DRIVE_MODE_CMD 0x01 -#define SCD_REQ_DRIVE_PARAM_CMD 0x02 -#define SCD_REQ_MECH_STATUS_CMD 0x03 -#define SCD_REQ_AUDIO_STATUS_CMD 0x04 -#define SCD_SET_DRIVE_PARAM_CMD 0x10 -#define SCD_REQ_TOC_DATA_CMD 0x20 -#define SCD_REQ_SUBCODE_ADDRESS_CMD 0x21 -#define SCD_REQ_UPC_EAN_CMD 0x22 -#define SCD_REQ_ISRC_CMD 0x23 -#define SCD_REQ_TOC_DATA_SPEC_CMD 0x24 - -#define SCD_READ_TOC_CMD 0x30 -#define SCD_SEEK_CMD 0x31 -#define SCD_READ_CMD 0x32 -#define SCD_READ_BLKERR_STAT_CMD 0x34 -#define SCD_ABORT_CMD 0x35 -#define SCD_READ_TOC_SPEC_CMD 0x36 +#define IS_ATTENTION(port) ((inb(port+IREG_STATUS) & SBIT_ATTENTION) != 0) +#define IS_BUSY(port) ((inb(port+IREG_STATUS) & SBIT_BUSY) != 0) +#define IS_DATA_RDY(port) ((inb(port+IREG_STATUS) & SBIT_DATA_READY) != 0) +#define STATUS_BIT(port, bit) ((inb(port+IREG_STATUS) & (bit)) != 0) +#define FSTATUS_BIT(port, bit) ((inb(port+IREG_FSTATUS) & (bit)) != 0) -#define SCD_AUDIO_PLAYBACK_CMD 0x40 -#define SCD_AUDIO_STOP_CMD 0x41 -#define SCD_AUDIO_SCAN_CMD 0x42 +#define OREG_COMMAND 0 +#define OREG_WPARAMS 1 +#define OREG_CONTROL 3 +#define CBIT_ATTENTION_CLEAR 0x01 +#define CBIT_RESULT_READY_CLEAR 0x02 +#define CBIT_DATA_READY_CLEAR 0x04 +#define CBIT_RPARAM_CLEAR 0x40 +#define CBIT_RESET_DRIVE 0x80 -#define SCD_EJECT_CMD 0x50 -#define SCD_SPIN_UP_CMD 0x51 -#define SCD_SPIN_DOWN_CMD 0x52 +#define IREG_STATUS 0 +#define SBIT_ATTENTION 0x01 +#define SBIT_RESULT_READY 0x02 +#define SBIT_DATA_READY 0x04 +#define SBIT_BUSY 0x80 -#define SCD_SD_DECODE_PARAM 0x00 -#define SCD_SD_INTERFACE_PARAM 0x01 -#define SCD_SD_BUFFERING_PARAM 0x02 -#define SCD_SD_AUDIO_PARAM 0x03 -#define SCD_SD_AUDIO_VOLUME 0x04 -#define SCD_SD_MECH_CONTROL 0x05 -#define SCD_SD_AUTO_SPIN_DOWN_TIME 0x06 - -#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03) -#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04) -#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08) -#define SONY_HWC_DOUBLE_SPEED(c) (c.hw_config[0] & 0x10) -#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6) -#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01) -#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02) -#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04) - -#define SONY_HWC_CADDY_LOAD_MECH 0x00 -#define SONY_HWC_TRAY_LOAD_MECH 0x01 -#define SONY_HWC_POPUP_LOAD_MECH 0x02 -#define SONY_HWC_UNKWN_LOAD_MECH 0x03 - -#define SONY_HWC_8KB_BUFFER 0x00 -#define SONY_HWC_32KB_BUFFER 0x01 -#define SONY_HWC_64KB_BUFFER 0x02 -#define SONY_HWC_UNKWN_BUFFER 0x03 - -/* - * The following are errors returned from the drive. - */ +#define IREG_RESULT 1 +#define IREG_DATA 2 +#define IREG_FSTATUS 3 +#define FBIT_WPARAM_READY 0x01 -#define SONY_ILL_CMD_ERR 0x10 -#define SONY_ILL_PARAM_ERR 0x11 +#define CMD_GET_DRIVE_CONFIG 0x00 +#define CMD_SET_DRIVE_PARAM 0x10 +#define CMD_GET_SUBCHANNEL_DATA 0x21 +#define CMD_GET_TOC 0x24 +#define CMD_READ_TOC 0x30 +#define CMD_READ 0x34 +#define CMD_PLAY_AUDIO 0x40 +#define CMD_STOP_AUDIO 0x41 +#define CMD_EJECT 0x50 +#define CMD_SPIN_UP 0x51 +#define CMD_SPIN_DOWN 0x52 -#define SONY_NOT_LOAD_ERR 0x20 -#define SONY_NO_DISK_ERR 0x21 -#define SONY_NOT_SPIN_ERR 0x22 -#define SONY_SPIN_ERR 0x23 -#define SONY_SPINDLE_SERVO_ERR 0x25 -#define SONY_FOCUS_SERVO_ERR 0x26 -#define SONY_EJECT_MECH_ERR 0x29 -#define SONY_AUDIO_PLAYING_ERR 0x2a -#define SONY_EMERGENCY_EJECT_ERR 0x2c +#define ERR_CD_NOT_LOADED 0x20 +#define ERR_NO_CD_INSIDE 0x21 +#define ERR_NOT_SPINNING 0x22 +#define ERR_FATAL_READ_ERROR1 0x53 +#define ERR_FATAL_READ_ERROR2 0x57 -#define SONY_FOCUS_ERR 0x30 -#define SONY_FRAME_SYNC_ERR 0x31 -#define SONY_SUBCODE_ADDR_ERR 0x32 -#define SONY_BLOCK_SYNC_ERR 0x33 -#define SONY_HEADER_ADDR_ERR 0x34 +#define ATTEN_DRIVE_LOADED 0x80 +#define ATTEN_EJECT_PUSHED 0x81 +#define ATTEN_AUDIO_DONE 0x90 +#define ATTEN_SPIN_UP_DONE 0x24 +#define ATTEN_SPIN_DOWN 0x27 +#define ATTEN_EJECT_DONE 0x28 -#define SONY_ILL_TRACK_R_ERR 0x40 -#define SONY_MODE_0_R_ERR 0x41 -#define SONY_ILL_MODE_R_ERR 0x42 -#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43 -#define SONY_MODE_R_ERR 0x44 -#define SONY_FORM_R_ERR 0x45 -#define SONY_LEAD_OUT_R_ERR 0x46 -#define SONY_BUFFER_OVERRUN_R_ERR 0x47 -#define SONY_UNREC_CIRC_ERR 0x53 -#define SONY_UNREC_LECC_ERR 0x57 - -#define SONY_NO_TOC_ERR 0x60 -#define SONY_SUBCODE_DATA_NVAL_ERR 0x61 -#define SONY_FOCUS_ON_TOC_READ_ERR 0x63 -#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64 -#define SONY_TOC_DATA_ERR 0x65 - -#define SONY_HW_FAILURE_ERR 0x70 -#define SONY_LEAD_IN_A_ERR 0x91 -#define SONY_LEAD_OUT_A_ERR 0x92 -#define SONY_DATA_TRACK_A_ERR 0x93 +struct sony_drive_configuration { + char vendor[8]; + char product[16]; + char revision[8]; + u_short config; +}; -#define SONY_NO_CIRC_ERR_BLK_STAT 0x50 -#define SONY_NO_LECC_ERR_BLK_STAT 0x54 -#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55 -#define SONY_NO_ERR_DETECTION_STAT 0x59 +/* Almost same as cd_sub_channel_position_data */ +struct sony_subchannel_position_data { + u_char control:4; + u_char addr_type:4; + u_char track_number; + u_char index_number; + u_char rel_msf[3]; + u_char dummy; + u_char abs_msf[3]; +}; -#define SONY_EMER_EJECT_ATTN 0x2c -#define SONY_HW_FAILURE_ATTN 0x70 -#define SONY_MECH_LOADED_ATTN 0x80 -#define SONY_EJECT_PUSHED_ATTN 0x81 +struct sony_tracklist { + u_char :8; + u_char track; + u_char start_msf[3]; +}; -#define SONY_AUDIO_PLAY_DONE_ATTN 0x90 -#define SONY_LEAD_IN_ERR_ATTN 0x91 -#define SONY_LEAD_OUT_ERR_ATTN 0x92 -#define SONY_DATA_TRACK_ERR_ATTN 0x93 -#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94 +#define MAX_TRACKS 100 -#define SONY_SPIN_UP_COMPLETE_ATTN 0x24 -#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25 -#define SONY_FOCUS_SERVO_ERR_ATTN 0x26 -#define SONY_TOC_READ_DONE_ATTN 0x62 -#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63 -#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65 +struct sony_toc { + u_char session_number; -#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27 -#define SONY_EJECT_COMPLETE_ATTN 0x28 -#define SONY_EJECT_MECH_ERR_ATTN 0x29 + u_char :8; + u_char :8; + u_char first_track; + u_char :8; + u_char :8; + u_char :8; + u_char :8; + u_char last_track; + u_char :8; + u_char :8; -struct s_sony_drive_config -{ - unsigned char exec_status[2]; - char vendor_id[8]; - char product_id[16]; - char product_rev_level[8]; - unsigned char hw_config[2]; -}; + u_char :8; + u_char :8; + u_char lead_out_start_msf[3]; -struct s_sony_subcode -{ - unsigned char exec_status[2]; - unsigned char address :4; - unsigned char control :4; - unsigned char track_num; - unsigned char index_num; - unsigned char rel_msf[3]; - unsigned char reserved1; - unsigned char abs_msf[3]; -}; + struct sony_tracklist tracks[MAX_TRACKS]; -#define MAX_TRACKS 100 + /* The rest is just to take space in case all data is returned */ -struct s_sony_session_toc -{ - unsigned char exec_status[2]; - unsigned char session_number; - unsigned char address0 :4; - unsigned char control0 :4; - unsigned char point0; - unsigned char first_track_num; - unsigned char disk_type; - unsigned char dummy0; - unsigned char address1 :4; - unsigned char control1 :4; - unsigned char point1; - unsigned char last_track_num; - unsigned char dummy1; - unsigned char dummy2; - unsigned char address2 :4; - unsigned char control2 :4; - unsigned char point2; - unsigned char lead_out_start_msf[3]; - unsigned char addressb0 :4; - unsigned char controlb0 :4; - unsigned char pointb0; - unsigned char next_poss_prog_area_msf[3]; - unsigned char num_mode_5_pointers; - unsigned char max_start_outer_leadout_msf[3]; - unsigned char addressb1 :4; - unsigned char controlb1 :4; - unsigned char pointb1; - unsigned char dummyb0_1[4]; - unsigned char num_skip_interval_pointers; - unsigned char num_skip_track_assignments; - unsigned char dummyb0_2; - unsigned char addressb2 :4; - unsigned char controlb2 :4; - unsigned char pointb2; - unsigned char tracksb2[7]; - unsigned char addressb3 :4; - unsigned char controlb3 :4; - unsigned char pointb3; - unsigned char tracksb3[7]; - unsigned char addressb4 :4; - unsigned char controlb4 :4; - unsigned char pointb4; - unsigned char tracksb4[7]; - unsigned char addressc0 :4; - unsigned char controlc0 :4; - unsigned char pointc0; - unsigned char dummyc0[7]; - struct s_sony_tracklist - { - unsigned char address :4; - unsigned char control :4; - unsigned char track; - unsigned char track_start_msf[3]; - } tracks[MAX_TRACKS]; + u_char dummy[6*9]; }; #endif /* SCD_H */ diff --git a/sys/gnu/i386/scd.c b/sys/gnu/i386/scd.c index 82e1e0494e69b..2c1b2c7f4f744 100644 --- a/sys/gnu/i386/scd.c +++ b/sys/gnu/i386/scd.c @@ -32,9 +32,12 @@ * */ -/* First released version. Please send any comments to micke@dynas.se */ -#define DEBUG 0 /* 0(no debug), 1(debug), 2(debug on each read) */ +/* $Id: scd.c,v 1.4 1995/01/28 23:53:16 micke Exp micke $ */ + +/* Please send any comments to micke@dynas.se */ + +#define SCD_DEBUG 0 #include "scd.h" #if NSCD > 0 @@ -52,10 +55,11 @@ #include <sys/dkbad.h> #include <sys/disklabel.h> #include <sys/devconf.h> +#include <machine/stdarg.h> #include <i386/isa/isa.h> #include <i386/isa/isa_device.h> -#include <gnu/i386/scdreg.h> +#include <i386/isa/scdreg.h> #define scd_part(dev) ((minor(dev)) & 7) #define scd_unit(dev) (((minor(dev)) & 0x38) >> 3) @@ -84,6 +88,13 @@ #define SCDBLKSIZE 2048 +#ifdef SCD_DEBUG + int scd_debuglevel = SCD_DEBUG; +# define XDEBUG(level, data) {if (scd_debuglevel >= level) printf data;} +#else +# define XDEBUG(level, data) +#endif + struct scd_mbx { short unit; short port; @@ -104,13 +115,13 @@ struct scd_data { int blksize; u_long disksize; struct disklabel dlabel; - int openflags; + int openflag; struct { unsigned char start_msf[3]; } toc[MAX_TRACKS]; short first_track; short last_track; -/* struct s_sony_subcode subcode; */ + struct ioc_play_msf last_play; short audio_status; struct buf head; /* head of buf queue */ @@ -129,29 +140,29 @@ static bcd_t bin2bcd(int b); static void hsg2msf(int hsg, bcd_t *msf); static int msf2hsg(bcd_t *msf); -static void get_drive_configuration(unsigned short unit, - unsigned char res_reg[], - unsigned int *res_size); -static int handle_attention(unsigned unit); +static void process_attention(unsigned unit); static inline void write_control(unsigned port, unsigned data); -static int write_params(unsigned port, unsigned char *params, int num_params); -static int do_cmd(unsigned int unit, - unsigned char cmd, - unsigned char *params, - unsigned int num_params, - unsigned char *result_buffer, - unsigned int *result_size); -static void set_drive_params(unsigned unit); +static int waitfor_status_bits(int unit, int bits_set, int bits_clear); +static int waitfor_attention(int unit); +static int send_cmd(u_int unit, u_char cmd, u_int nargs, ...); +static void init_drive(unsigned unit); +static int spin_up(unsigned unit); +static int read_toc(dev_t dev); +static int get_result(u_int unit, int result_len, u_char *result); +static void print_error(int unit, int errcode); static void scd_start(int unit); static void scd_doread(int state, struct scd_mbx *mbxin); static int scd_eject(int unit); static int scd_stop(int unit); +static int scd_pause(int unit); +static int scd_resume(int unit); static int scd_playtracks(int unit, struct ioc_play_track *pt); static int scd_playmsf(int unit, struct ioc_play_msf *msf); +static int scd_play(int unit, struct ioc_play_msf *msf); static int scd_subchan(int unit, struct ioc_read_subchannel *sc); -static int read_subcode(int unit, struct s_sony_subcode *sc); +static int read_subcode(int unit, struct sony_subchannel_position_data *sc); extern int hz; @@ -191,7 +202,7 @@ int scd_attach(struct isa_device *dev) kdc_scd[dev->id_unit].kdc_description = scd_data[dev->id_unit].name; printf("scd%d: <%s>\n", dev->id_unit, scd_data[dev->id_unit].name); - set_drive_params(dev->id_unit); + init_drive(dev->id_unit); cd->flags = SCDINIT; cd->audio_status = CD_AS_AUDIO_INVALID; @@ -199,465 +210,6 @@ int scd_attach(struct isa_device *dev) return 1; } -static int -bcd2bin(bcd_t b) -{ - return (b >> 4) * 10 + (b & 15); -} - -static bcd_t -bin2bcd(int b) -{ - return ((b / 10) << 4) | (b % 10); -} - -static void -hsg2msf(int hsg, bcd_t *msf) -{ - hsg += 150; - M_msf(msf) = bin2bcd(hsg / 4500); - hsg %= 4500; - S_msf(msf) = bin2bcd(hsg / 75); - F_msf(msf) = bin2bcd(hsg % 75); -} - -static int -msf2hsg(bcd_t *msf) -{ - return (bcd2bin(M_msf(msf)) * 60 + - bcd2bin(S_msf(msf))) * 75 + - bcd2bin(F_msf(msf)) - 150; -} - -static int -handle_attention(unsigned unit) -{ - unsigned port = scd_data[unit].iobase; - unsigned char atten_code; - static int loop_count = 0; - int i; - - if (!IS_ATTENTION(port)) { - loop_count = 0; - return 0; - } - - if (loop_count > 20) { - /* printf("scd%d: Too many attentions: %d\n", unit, loop_count); */ - loop_count = 0; - return 0; - } - - write_control(port, SCD_ATTN_CLR_BIT); - atten_code = inb(port+SCD_RESULT_REG); - -#if DEBUG - printf("scd%d: DEBUG: ********* ATTENTION = 0x%x\n", unit, atten_code); -#endif - - switch (atten_code) { - /* Someone changed the CD. Mark it as changed */ - case SONY_MECH_LOADED_ATTN: - scd_data[unit].flags &= ~(SCDTOC|SCDSPINNING|SCDVALID); - break; - - case SONY_SPIN_DOWN_COMPLETE_ATTN: - scd_data[unit].flags &= ~SCDSPINNING; - break; - - case SONY_SPIN_UP_COMPLETE_ATTN: - scd_data[unit].flags |= SCDSPINNING; - break; - - case SONY_AUDIO_PLAY_DONE_ATTN: - scd_data[unit].audio_status = CD_AS_PLAY_COMPLETED; -/* read_subcode(); */ - break; - - case SONY_EJECT_PUSHED_ATTN: - scd_data[unit].flags &= ~SCDVALID; - break; - - case SONY_LEAD_IN_ERR_ATTN: - case SONY_LEAD_OUT_ERR_ATTN: - case SONY_DATA_TRACK_ERR_ATTN: - case SONY_AUDIO_PLAYBACK_ERR_ATTN: - break; - default: -#if DEBUG - printf("scd%d: unknown ATTENTION = 0x%x\n", unit, atten_code); -#endif - break; - } - loop_count++; - return 1; -} - -/* Returns 0 OR sony error code */ -static int -spin_up(unsigned unit) -{ - unsigned char res_reg[12]; - unsigned int res_size; - int res; - int loop_count = 0; - -again: - res = do_cmd(unit, SCD_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - if (res != 0) { -#if DEBUG > 1 - printf("scd%d: CMD_SPIN_UP error 0x%x\n", unit, res); -#endif - return res; - } - - if (!(scd_data[unit].flags & SCDTOC)) { /* XXX probably no good ... */ - res = do_cmd(unit, SCD_READ_TOC_CMD, NULL, 0, res_reg, &res_size); - if (res == SONY_NOT_SPIN_ERR) { - if (loop_count++ < 3) - goto again; - return res; - } - } - - if (res != 0) - return res; - - scd_data[unit].flags |= SCDSPINNING; - - return 0; -} - -static struct s_sony_tracklist * -get_tl(struct s_sony_session_toc *toc, int size) -{ - struct s_sony_tracklist *tl = (struct s_sony_tracklist *)&toc->pointb0; - - (char *)tl -= 1; /* Cannot take address of bitfield (above) */ - - if (toc->pointb0 != 0xb0) { -#if DEBUG - if (toc->pointb0 != tl->track) - printf("scd: WARNING: pointb0 != track0\n"); -#endif - return tl; - } - (char *)tl += 9; - if (toc->pointb1 != 0xb1) - return tl; - (char *)tl += 9; - if (toc->pointb2 != 0xb2) - return tl; - (char *)tl += 9; - if (toc->pointb3 != 0xb3) - return tl; - (char *)tl += 9; - if (toc->pointb4 != 0xb4) - return tl; - (char *)tl += 9; - if (toc->pointc0 != 0xc0) - return tl; - (char *)tl += 9; - return tl; -} - -/* Return value same as do_cmd() */ -static int -read_toc(dev_t dev) -{ - unsigned unit; - struct scd_data *cd; - unsigned part = 0; /* For now ... */ - unsigned char params[1]; - unsigned char msf[3]; - unsigned int res_size; - struct s_sony_session_toc toc; - struct s_sony_tracklist *tl; - int i, j; - u_long first, last; - - unit = scd_unit(dev); - cd = scd_data + unit; - - params[0] = part+1; - i = do_cmd(unit, SCD_REQ_TOC_DATA_SPEC_CMD, - params, - 1, - (unsigned char *)toc.exec_status, - &res_size); - if (i != 0) - return i; - - tl = get_tl(&toc, res_size); - first = msf2hsg(tl->track_start_msf); - last = msf2hsg(toc.lead_out_start_msf); - cd->blksize = SCDBLKSIZE; - cd->disksize = last*cd->blksize/DEV_BSIZE; - -#if DEBUG - printf("scd%d: firstsector = %d, lastsector = %d", unit, - first, last); -#endif - - cd->first_track = bcd2bin(toc.first_track_num); - cd->last_track = bcd2bin(toc.last_track_num); - if (cd->last_track > (MAX_TRACKS-2)) - cd->last_track = MAX_TRACKS-2; - for (j = 0, i = cd->first_track; i <= cd->last_track; i++, j++) { - bcopy(tl[j].track_start_msf, cd->toc[i].start_msf, 3); -#if DEBUG - if ((j % 3) == 0) - printf("\nscd%d: tracks ", unit); - printf("[%03d: %2d %2d %2d] ", i, - bcd2bin(cd->toc[i].start_msf[0]), - bcd2bin(cd->toc[i].start_msf[1]), - bcd2bin(cd->toc[i].start_msf[2])); -#endif - } - hsg2msf(msf2hsg(toc.lead_out_start_msf)-1, msf); - bcopy(msf, cd->toc[cd->last_track+1].start_msf, 3); -#if DEBUG - i = cd->last_track+1; - printf("[END: %2d %2d %2d]\n", - bcd2bin(cd->toc[i].start_msf[0]), - bcd2bin(cd->toc[i].start_msf[1]), - bcd2bin(cd->toc[i].start_msf[2])); -#endif - - bzero(&cd->dlabel,sizeof(struct disklabel)); - /* filled with spaces first */ - strncpy(cd->dlabel.d_typename," ", - sizeof(cd->dlabel.d_typename)); - strncpy(cd->dlabel.d_typename, cd->name, - min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1)); - strncpy(cd->dlabel.d_packname,"unknown ", - sizeof(cd->dlabel.d_packname)); - cd->dlabel.d_secsize = cd->blksize; - cd->dlabel.d_nsectors = 100; - cd->dlabel.d_ntracks = 1; - cd->dlabel.d_ncylinders = (cd->disksize/100)+1; - cd->dlabel.d_secpercyl = 100; - cd->dlabel.d_secperunit = cd->disksize; - cd->dlabel.d_rpm = 300; - cd->dlabel.d_interleave = 1; - cd->dlabel.d_flags = D_REMOVABLE; - cd->dlabel.d_npartitions= 1; - cd->dlabel.d_partitions[0].p_offset = 0; - cd->dlabel.d_partitions[0].p_size = cd->disksize; - cd->dlabel.d_partitions[0].p_fstype = 9; - cd->dlabel.d_magic = DISKMAGIC; - cd->dlabel.d_magic2 = DISKMAGIC; - cd->dlabel.d_checksum = dkcksum(&cd->dlabel); - - cd->flags |= SCDTOC; - - return 0; -} - -static inline void -write_control(unsigned port, unsigned data) -{ - outb(port + SCD_CONTROL_REG, data); -} - -static int -write_params(unsigned port, unsigned char *params, int num_params) -{ - int retry_count = 20000; - - while ((retry_count-- > 0) && (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT))) - ; - if (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT)) - return EIO; - - while (num_params-- > 0) - outb(port+SCD_PARAM_REG, *params++); - - return 0; -} - -static void -set_drive_params(unsigned unit) -{ - unsigned char res_reg[12]; - unsigned int res_size; - unsigned char params[3]; - int res; - - params[0] = SCD_SD_MECH_CONTROL; - params[1] = 0x03; /* Set auto spin up and auto eject */ - if (scd_data[unit].double_speed) - params[1] |= 0x04; /* Set the drive to double speed if possible */ - - res = do_cmd(unit, SCD_SET_DRIVE_PARAM_CMD, - params, - 2, - res_reg, - &res_size); - - if (res != 0) - printf("scd%d: Unable to set mechanical parameters. Errcode = 0x%x\n", res); -} - -static int -get_result(unsigned unit, unsigned char *result) -{ - unsigned int port = scd_data[unit].iobase; - unsigned int flags = scd_data[unit].flags; - unsigned int res_reg = port + SCD_RESULT_REG; - unsigned char c; - int len = 0; - int datalen = 0; - int loop_index = 0; - unsigned int retry_count; - - while (handle_attention(unit)) - ; - - if (flags & SCDPROBING) { - retry_count = 50; /* While probing tsleep doesn't sleep */ - while ((retry_count-- > 0) - && (IS_BUSY(port) || !STATUS_BIT(port, SCD_RES_RDY_BIT))) - { - DELAY(100000); - while (handle_attention(unit)) - ; - } - } else { - retry_count = 100; /* Wait max 10 sec. */ - while ((retry_count-- > 0) - && (IS_BUSY(port) || !STATUS_BIT(port, SCD_RES_RDY_BIT))) - { - tsleep(get_result, PZERO - 1, "get_result", hz/10); - while (handle_attention(unit)) - ; - } - } - while (handle_attention(unit)) - ; - - if (IS_BUSY(port) || !STATUS_BIT(port, SCD_RES_RDY_BIT)) { - printf("scd%d: timeout @%d\n", unit, __LINE__); - return -EIO; - } - - write_control(port, SCD_RES_RDY_CLR_BIT); - switch ((*result++ = inb(res_reg)) & 0xf0) { - case 0x50: - return 1; - case 0x20: - *result = inb(res_reg); - return 2; - case 0x00: - default: - datalen = inb(res_reg); - len = 2; - loop_index = len; - *result++ = datalen; - } - -#if DEBUG - printf("scd%d: DEBUG: get_result bytes = %d\n", unit, datalen); -#endif - - while (datalen-- > 0) { - if (loop_index++ >= 10) { - loop_index = 1; - retry_count = 200; - if (flags & SCDPROBING) - retry_count += 10000; - while ((retry_count > 0) && !STATUS_BIT(port, SCD_RES_RDY_BIT)) { - retry_count--; - if (retry_count < 100) - tsleep(get_result, PZERO - 1, "get_result", hz/50); - else - DELAY(10); - } - if (!STATUS_BIT(port, SCD_RES_RDY_BIT)) { - printf("scd%d: timeout @%d\n", unit, __LINE__); - return -EIO; - } - write_control(port, SCD_RES_RDY_CLR_BIT); - } - *result++ = inb(res_reg); - len++; - } - return len; -} - -/* Returns 0 or (sony error code) or -(errno code) */ -static int -do_cmd(unsigned int unit, - unsigned char cmd, - unsigned char *params, - unsigned int num_params, - unsigned char *result_buffer, - unsigned int *result_size) -{ - unsigned port = scd_data[unit].iobase; - unsigned int retry_count; - int res; - -#if DEBUG - printf("SCD: do_cmd: 0x%x\n", cmd); -#endif - - while (handle_attention(unit)) - ; - retry_count = 40; - while (retry_count-- > 0 && IS_BUSY(port)) { - while (handle_attention(unit)) - ; - tsleep(do_cmd, PZERO - 1, "do_cmd", hz/4); - } - if (IS_BUSY(port)) { -#if DEBUG - printf("SCD: timeout out @%d\n", __LINE__); -#endif - return -EIO; - } - write_control(port, SCD_RES_RDY_CLR_BIT); - write_control(port, SCD_PARAM_CLR_BIT); - - retry_count = 40; - while ((retry_count-- > 0) && (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT))) - ; - if (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT)) - return -EIO; - - while (num_params-- > 0) - outb(port+SCD_PARAM_REG, *params++); - outb(port+SCD_CMD_REG, cmd); - - if ((res = get_result(unit, result_buffer)) < 0) - return res; - if (res < 2) - return -EIO; - *result_size = res; - if (result_buffer[0] == 0x20) - return result_buffer[1]; - return 0; -} - -static void -print_error(int unit, int errcode) -{ - switch (errcode) { - case SONY_NOT_LOAD_ERR: - printf("scd%d: door is open\n", unit); - break; - case SONY_NO_DISK_ERR: - printf("scd%d: no cd inside\n", unit); - break; - default: - if (errcode < 0) - printf("scd%d: device timeout\n", unit); - else - printf("scd%d: unexpected error 0x%x\n", unit, errcode); - break; - } -} - int scdopen(dev_t dev) { @@ -678,12 +230,10 @@ scdopen(dev_t dev) return ENXIO; /* invalidated in the meantime? mark all open part's invalid */ - if (cd->openflags) + if (cd->openflag) return ENXIO; -#if DEBUG - printf("scd%d: DEBUG: status = 0x%x\n", unit, inb(cd->iobase+SCD_STATUS_REG)); -#endif + XDEBUG(1,("scd%d: DEBUG: status = 0x%x\n", unit, inb(cd->iobase+IREG_STATUS))); if ((rc = spin_up(unit)) != 0) { print_error(unit, rc); @@ -693,7 +243,7 @@ scdopen(dev_t dev) int loop_count = 3; while (loop_count-- > 0 && (rc = read_toc(dev)) != 0) { - if (rc == SONY_NOT_SPIN_ERR) { + if (rc == ERR_NOT_SPINNING) { rc = spin_up(unit); if (rc) { print_error(unit, rc);\ @@ -706,7 +256,7 @@ scdopen(dev_t dev) } } - cd->openflags = 1; + cd->openflag = 1; cd->flags |= SCDVALID; kdc_scd[unit].kdc_state = DC_BUSY; @@ -729,16 +279,18 @@ scdclose(dev_t dev) part = scd_part(dev); phys = scd_phys(dev); - if (!(cd->flags & SCDINIT) || !cd->openflags) + if (!(cd->flags & SCDINIT) || !cd->openflag) return ENXIO; - (void)do_cmd(unit, SCD_SPIN_DOWN_CMD, (char *)0, 0, rdata, &rlen); - cd->flags &= ~SCDSPINNING; + if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) { + (void)send_cmd(unit, CMD_SPIN_DOWN, 0); + cd->flags &= ~SCDSPINNING; + } kdc_scd[unit].kdc_state = DC_IDLE; /* close channel */ - cd->openflags = 0; + cd->openflag = 0; return 0; } @@ -753,10 +305,8 @@ scdstrategy(struct buf *bp) cd = scd_data + unit; - /* test validity */ -#if DEBUG > 1 - printf("scd%d: DEBUG: strategy: block=%ld, bcount=%ld\n", unit, bp->b_blkno, bp->b_bcount); -#endif + XDEBUG(2, ("scd%d: DEBUG: strategy: block=%ld, bcount=%ld\n", unit, bp->b_blkno, bp->b_bcount)); + if (unit >= NSCD || bp->b_blkno < 0 || (bp->b_bcount % SCDBLKSIZE)) { printf("scd%d: strategy failure: blkno = %d, bcount = %d\n", unit, bp->b_blkno, bp->b_bcount); @@ -827,7 +377,7 @@ scd_start(int unit) if ((bp = qp->b_actf) != 0) { /* block found to process, dequeue */ - qp->b_actf = bp->b_actf; /* changed from: bp->av_forw <se> */ + qp->b_actf = bp->b_actf; cd->flags |= SCDMBXBSY; splx(s); } else { @@ -859,9 +409,7 @@ scdioctl(dev_t dev, int cmd, caddr_t addr, int flags) part = scd_part(dev); cd = scd_data + unit; -#if DEBUG - printf("scd%d: ioctl: cmd=0x%lx\n", unit, cmd); -#endif + XDEBUG(1, ("scd%d: ioctl: cmd=0x%lx\n", unit, cmd)); if (!(cd->flags & SCDVALID)) return EIO; @@ -897,9 +445,9 @@ scdioctl(dev_t dev, int cmd, caddr_t addr, int flags) case CDIOCSETRIGHT: return EINVAL; case CDIOCRESUME: -/* return scd_resume(unit); */ + return scd_resume(unit); case CDIOCPAUSE: -/* return scd_pause(unit); */ + return scd_pause(unit); case CDIOCSTART: return EINVAL; case CDIOCSTOP: @@ -908,12 +456,35 @@ scdioctl(dev_t dev, int cmd, caddr_t addr, int flags) return scd_eject(unit); case CDIOCALLOW: return 0; + case CDIOCSETDEBUG: +#ifdef SCD_DEBUG + scd_debuglevel++; +#endif + return 0; + case CDIOCCLRDEBUG: +#ifdef SCD_DEBUG + scd_debuglevel = 0; + +#endif + return 0; default: printf("scd%d: unsupported ioctl (cmd=0x%lx)\n", unit, cmd); return ENOTTY; } } +int +scdsize(dev_t dev) +{ + return -1; +} + +void +scdintr() +{ + return; +} + /*************************************************************** * lower level of driver starts here **************************************************************/ @@ -928,14 +499,19 @@ scd_playtracks(int unit, struct ioc_play_track *pt) int rc, i; if (!(cd->flags & SCDTOC) && (rc = read_toc(unit)) != 0) { - print_error(unit, rc); - return EIO; + if (rc == -ERR_NOT_SPINNING) { + if (spin_up(unit) != 0) + return EIO; + rc = read_toc(unit); + } + if (rc != 0) { + print_error(unit, rc); + return EIO; + } } -#if DEBUG - printf("scd%d: playtracks from %d:%d to %d:%d\n", unit, - a, pt->start_index, z, pt->end_index); -#endif + XDEBUG(1, ("scd%d: playtracks from %d:%d to %d:%d\n", unit, + a, pt->start_index, z, pt->end_index)); if ( a < cd->first_track || a > cd->last_track @@ -943,40 +519,57 @@ scd_playtracks(int unit, struct ioc_play_track *pt) || z > cd->last_track) return EINVAL; - for (i = 0; i < 3; i++) { - ((char *)&msf.start_m)[i] = bcd2bin(cd->toc[a].start_msf[i]); - ((char *)&msf.end_m)[i] = bcd2bin(cd->toc[z+1].start_msf[i]); - } - return scd_playmsf(unit, &msf); + bcopy(cd->toc[a].start_msf, &msf.start_m, 3); + hsg2msf(msf2hsg(cd->toc[z+1].start_msf)-1, &msf.end_m); + + return scd_play(unit, &msf); } +/* The start/end msf is expected to be in bin format */ static int -scd_playmsf(int unit, struct ioc_play_msf *msf) +scd_playmsf(int unit, struct ioc_play_msf *msfin) { - struct scd_data *cd = scd_data + unit; - unsigned char sdata[10]; - unsigned char rdata[10]; - int rlen, rc; + struct ioc_play_msf msf; - sdata[0] = 0x03; - sdata[1] = bin2bcd(msf->start_m); - sdata[2] = bin2bcd(msf->start_s); - sdata[3] = bin2bcd(msf->start_f); - sdata[4] = bin2bcd(msf->end_m); - sdata[5] = bin2bcd(msf->end_s); - sdata[6] = bin2bcd(msf->end_f); + msf.start_m = bin2bcd(msfin->start_m); + msf.start_s = bin2bcd(msfin->start_s); + msf.start_f = bin2bcd(msfin->start_f); + msf.end_m = bin2bcd(msfin->end_m); + msf.end_s = bin2bcd(msfin->end_s); + msf.end_f = bin2bcd(msfin->end_f); -#if DEBUG - printf("scd%d: playmsf: %02d:%02d:%02d -> %02d:%02d:%02d\n", unit, + return scd_play(unit, &msf); +} + +/* The start/end msf is expected to be in bcd format */ +static int +scd_play(int unit, struct ioc_play_msf *msf) +{ + struct scd_data *cd = scd_data + unit; + int i, rc; + + XDEBUG(1, ("scd%d: playing: %02x:%02x:%02x -> %02x:%02x:%02x\n", unit, msf->start_m, msf->start_s, msf->start_f, - msf->end_m, msf->end_s, msf->end_f); -#endif + msf->end_m, msf->end_s, msf->end_f)); - if ((rc = do_cmd(unit, SCD_AUDIO_PLAYBACK_CMD, sdata, 7, rdata, &rlen)) != 0) { - print_error(unit, rc); - return EIO; + for (i = 0; i < 2; i++) { + rc = send_cmd(unit, CMD_PLAY_AUDIO, 7, + 0x03, + msf->start_m, msf->start_s, msf->start_f, + msf->end_m, msf->end_s, msf->end_f); + if (rc == -ERR_NOT_SPINNING) { + cd->flags &= ~SCDSPINNING; + if (spin_up(unit) != 0) + return EIO; + } else if (rc < 0) { + print_error(unit, rc); + return EIO; + } else { + break; + } } cd->audio_status = CD_AS_PLAY_IN_PROGRESS; + bcopy((char *)msf, (char *)&cd->last_play, sizeof(struct ioc_play_msf)); return 0; } @@ -984,27 +577,63 @@ static int scd_stop(int unit) { struct scd_data *cd = scd_data + unit; - char rdata[10]; - int rlen; - (void)do_cmd(unit, SCD_AUDIO_STOP_CMD, (char *)0, 0, rdata, &rlen); + (void)send_cmd(unit, CMD_STOP_AUDIO, 0); cd->audio_status = CD_AS_PLAY_COMPLETED; return 0; } static int +scd_pause(int unit) +{ + struct scd_data *cd = scd_data + unit; + struct sony_subchannel_position_data subpos; + + if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) + return EINVAL; + + if (read_subcode(unit, &subpos) != 0) + return EIO; + + if (send_cmd(unit, CMD_STOP_AUDIO, 0) != 0) + return EIO; + + cd->last_play.start_m = subpos.abs_msf[0]; + cd->last_play.start_s = subpos.abs_msf[1]; + cd->last_play.start_f = subpos.abs_msf[2]; + cd->audio_status = CD_AS_PLAY_PAUSED; + + XDEBUG(1, ("scd%d: pause @ %02x:%02x:%02x\n", unit, + cd->last_play.start_m, + cd->last_play.start_s, + cd->last_play.start_f)); + + return 0; +} + +static int +scd_resume(int unit) +{ + if (scd_data[unit].audio_status != CD_AS_PLAY_PAUSED) + return EINVAL; + return scd_play(unit, &scd_data[unit].last_play); +} + +static int scd_eject(int unit) { struct scd_data *cd = scd_data + unit; int port = cd->iobase; - char rdata[10]; - int rlen; - (void)do_cmd(unit, SCD_AUDIO_STOP_CMD, (char *)0, 0, rdata, &rlen); - (void)do_cmd(unit, SCD_SPIN_DOWN_CMD, (char *)0, 0, rdata, &rlen); + cd->audio_status = CD_AS_AUDIO_INVALID; cd->flags &= ~(SCDSPINNING|SCDTOC); - if (do_cmd(unit, SCD_EJECT_CMD, (char *)0, 0, rdata, &rlen) != 0) + + if (send_cmd(unit, CMD_STOP_AUDIO, 0) != 0 || + send_cmd(unit, CMD_SPIN_DOWN, 0) != 0 || + send_cmd(unit, CMD_EJECT, 0) != 0) + { return EIO; + } return 0; } @@ -1012,14 +641,12 @@ static int scd_subchan(int unit, struct ioc_read_subchannel *sc) { struct scd_data *cd = scd_data + unit; - struct s_sony_subcode q; + struct sony_subchannel_position_data q; struct cd_sub_channel_info data; -#if DEBUG - printf("scd%d: subchan af=%d, df=%d\n", unit, + XDEBUG(1, ("scd%d: subchan af=%d, df=%d\n", unit, sc->address_format, - sc->data_format); -#endif + sc->data_format)); if (sc->address_format != CD_MSF_FORMAT) return EINVAL; @@ -1032,7 +659,7 @@ scd_subchan(int unit, struct ioc_read_subchannel *sc) data.header.audio_status = cd->audio_status; data.what.position.data_format = CD_MSF_FORMAT; - data.what.position.track_number = bcd2bin(q.track_num); + data.what.position.track_number = bcd2bin(q.track_number); data.what.position.reladdr.msf.unused = 0; data.what.position.reladdr.msf.minute = bcd2bin(q.rel_msf[0]); data.what.position.reladdr.msf.second = bcd2bin(q.rel_msf[1]); @@ -1047,74 +674,63 @@ scd_subchan(int unit, struct ioc_read_subchannel *sc) return 0; } -/* check to see if a Sony CD-ROM is attached to the ISA bus */ - -static void -get_drive_configuration(unsigned short unit, - unsigned char res_reg[], - unsigned int *res_size) -{ - unsigned port = scd_data[unit].iobase; - int retry_count = 10000; - - outb(port + SCD_CONTROL_REG, SCD_DRIVE_RESET_BIT); /* reset drive */ - - while ((retry_count-- > 0) && (!IS_ATTENTION(port))) - DELAY(10); - - /* If I use a shorter delay, I get a timeout in get_result() */ - /* I guess there is a better way, but I don't have the manuals */ - DELAY(500000); - - (void)do_cmd(unit, SCD_REQ_DRIVE_CONFIG_CMD, - NULL, - 0, - (unsigned char *) res_reg, - res_size); - return; -} - int scd_probe(struct isa_device *dev) { - struct s_sony_drive_config drive_config; + struct sony_drive_configuration drive_config; int unit = dev->id_unit; - unsigned int res_size; - int i; - static char buf[8+16+8+3]; - char *s = buf; + int rc; + static char namebuf[8+16+8+3]; + char *s = namebuf; + int loop_count = 0; scd_data[unit].flags = SCDPROBING; scd_data[unit].iobase = dev->id_iobase; bzero(&drive_config, sizeof(drive_config)); - get_drive_configuration(unit, drive_config.exec_status, &res_size); - if (res_size < sizeof(drive_config) - || (drive_config.exec_status[0] & 0xf0) != 0x00) - { +again: + /* Reset drive */ + write_control(dev->id_iobase, CBIT_RESET_DRIVE); + + /* Calm down */ + DELAY(300000); + + /* Only the ATTENTION bit may be set */ + if ((inb(dev->id_iobase+IREG_STATUS) & ~1) != 0) { + XDEBUG(1, ("scd: too many bits set. probe failed.\n")); return 0; } + rc = send_cmd(unit, CMD_GET_DRIVE_CONFIG, 0); + if (rc != sizeof(drive_config)) { + /* Sometimes if the drive is playing audio I get */ + /* the bad result 82. Fix by repeating the reset */ + if (rc > 0 && loop_count++ == 0) + goto again; + return 0; + } + if (get_result(unit, rc, (u_char *)&drive_config) != 0) + return 0; - bcopy(drive_config.vendor_id, buf, 8); - s = buf+8; - while (*(s-1) == ' ') + bcopy(drive_config.vendor, namebuf, 8); + s = namebuf+8; + while (*(s-1) == ' ') /* Strip trailing spaces */ s--; *s++ = ' '; - bcopy(drive_config.product_id, s, 16); + bcopy(drive_config.product, s, 16); s += 16; while (*(s-1) == ' ') s--; *s++ = ' '; - bcopy(drive_config.product_rev_level, s, 8); + bcopy(drive_config.revision, s, 8); s += 8; while (*(s-1) == ' ') s--; *s = 0; - scd_data[unit].name = buf; + scd_data[unit].name = namebuf; - if (SONY_HWC_DOUBLE_SPEED(drive_config)) + if (drive_config.config & 0x10) scd_data[unit].double_speed = 1; else scd_data[unit].double_speed = 0; @@ -1123,17 +739,24 @@ scd_probe(struct isa_device *dev) } static int -read_subcode(int unit, struct s_sony_subcode *sc) +read_subcode(int unit, struct sony_subchannel_position_data *sc) { - int rlen; + int rc; - if (do_cmd(unit, SCD_REQ_SUBCODE_ADDRESS_CMD, - (char *)0, 0, sc->exec_status, &rlen) != 0) + rc = send_cmd(unit, CMD_GET_SUBCHANNEL_DATA, 0); + if (rc < 0 || rc < sizeof(*sc)) + return EIO; + if (get_result(unit, rc, (u_char *)sc) != 0) return EIO; return 0; } -/* State machine copied from mcd.c /Micke */ +/* State machine copied from mcd.c */ + +/* This (and the code in mcd.c) will not work with more than one drive */ +/* because there is only one mbxsave below. Should fix that some day. */ +/* (mbxsave & state should probably be included in the scd_data struct and */ +/* the unit number used as first argument to scd_doread().) /Micke */ /* state machine to process read requests * initialize with SCD_S_BEGIN: reset state machine @@ -1154,7 +777,6 @@ scd_doread(int state, struct scd_mbx *mbxin) int port = mbx->port; struct buf *bp = mbx->bp; struct scd_data *cd = scd_data + unit; - int reg,i,k,c; int blknum; caddr_t addr; @@ -1170,8 +792,7 @@ loop: /* get status */ mbx->count = RDELAY_WAIT; - while (handle_attention(unit)) - ; + process_attention(unit); goto trystat; case SCD_S_WAITSTAT: @@ -1188,15 +809,14 @@ trystat: return; } - while (handle_attention(unit)) - ; -#if 0 + process_attention(unit); + /* reject, if audio active */ - if (cd->status & SCDAUDIOBSY) { + if (cd->audio_status & CD_AS_PLAY_IN_PROGRESS) { printf("scd%d: audio is active\n",unit); - goto readerr; + goto harderr; } -#endif + mbx->sz = cd->blksize; firstblock: @@ -1211,17 +831,16 @@ nextblock: blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE)) + mbx->p_offset + mbx->skip/mbx->sz; -#if DEBUG > 1 - printf("scd%d: scd_doread: read blknum=%d\n", unit, blknum); -#endif + XDEBUG(2, ("scd%d: scd_doread: read blknum=%d\n", unit, blknum)); + /* build parameter block */ hsg2msf(blknum, sdata); - write_control(port, SCD_RES_RDY_CLR_BIT); - write_control(port, SCD_PARAM_CLR_BIT); - write_control(port, SCD_DATA_RDY_CLR_BIT); + write_control(port, CBIT_RESULT_READY_CLEAR); + write_control(port, CBIT_RPARAM_CLEAR); + write_control(port, CBIT_DATA_READY_CLEAR); - if (FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT)) + if (FSTATUS_BIT(port, FBIT_WPARAM_READY)) goto writeparam; mbx->count = 100; @@ -1235,25 +854,24 @@ nextblock: printf("scd%d: timeout waiting for drive to spin up.\n", unit); goto harderr; } - if (!STATUS_BIT(port, SCD_RES_RDY_BIT)) { + if (!STATUS_BIT(port, SBIT_RESULT_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITSPIN,hz/100); /* XXX */ return; } - write_control(port, SCD_RES_RDY_CLR_BIT); - switch ((i = inb(port+SCD_RESULT_REG)) & 0xf0) { + write_control(port, CBIT_RESULT_READY_CLEAR); + switch ((i = inb(port+IREG_RESULT)) & 0xf0) { case 0x20: - i = inb(port+SCD_RESULT_REG); + i = inb(port+IREG_RESULT); print_error(unit, i); goto harderr; case 0x00: - (void)inb(port+SCD_RESULT_REG); + (void)inb(port+IREG_RESULT); cd->flags |= SCDSPINNING; break; } -#if DEBUG - printf("scd%d: DEBUG: spin up complete\n", unit); -#endif + XDEBUG(1, ("scd%d: DEBUG: spin up complete\n", unit)); + state = SCD_S_BEGIN1; goto loop; @@ -1263,31 +881,26 @@ nextblock: printf("scd%d: timeout. write param not ready.\n",unit); goto harderr; } - if (!FIFOST_BIT(port, SCD_PARAM_WRITE_RDY_BIT)) { + if (!FSTATUS_BIT(port, FBIT_WPARAM_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITFIFO,hz/100); /* XXX */ return; } -#if DEBUG - printf("scd%d: mbx->count (writeparamwait) = %d(%d)\n", unit, mbx->count, 100); -#endif + XDEBUG(1, ("scd%d: mbx->count (writeparamwait) = %d(%d)\n", unit, mbx->count, 100)); writeparam: /* The reason this test isn't done 'till now is to make sure */ - /* that it is ok to send the SPIN_UP command below */ + /* that it is ok to send the SPIN_UP cmd below. */ if (!(cd->flags & SCDSPINNING)) { - -#if DEBUG - printf("scd%d: spinning up drive ...\n", unit); -#endif - outb(port+SCD_CMD_REG, SCD_SPIN_UP_CMD); + XDEBUG(1, ("scd%d: spinning up drive ...\n", unit)); + outb(port+OREG_COMMAND, CMD_SPIN_UP); mbx->count = 300; timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITSPIN,hz/100); /* XXX */ return; } - reg = port + SCD_PARAM_REG; + reg = port + OREG_WPARAMS; /* send the read command */ disable_intr(); outb(reg, sdata[0]); @@ -1296,12 +909,12 @@ writeparam: outb(reg, 0); outb(reg, 0); outb(reg, 1); - outb(port+SCD_CMD_REG, SCD_READ_BLKERR_STAT_CMD); + outb(port+OREG_COMMAND, CMD_READ); enable_intr(); mbx->count = RDELAY_WAITREAD; for (i = 0; i < 50; i++) { - if (STATUS_BIT(port, SCD_DATA_RDY_BIT)) + if (STATUS_BIT(port, SBIT_DATA_READY)) goto got_data; DELAY(100); } @@ -1313,34 +926,30 @@ writeparam: case SCD_S_WAITREAD: untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITREAD); if (mbx->count-- <= 0) { - if (STATUS_BIT(port, SCD_RES_RDY_BIT)) + if (STATUS_BIT(port, SBIT_RESULT_READY)) goto got_param; printf("scd%d: timeout while reading data\n",unit); goto readerr; } - if (!STATUS_BIT(port, SCD_DATA_RDY_BIT)) { - while (handle_attention(unit)) - ; + if (!STATUS_BIT(port, SBIT_DATA_READY)) { + process_attention(unit); if (!(cd->flags & SCDVALID)) goto changed; timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITREAD,hz/100); /* XXX */ return; } - -#if DEBUG > 1 - printf("scd%d: mbx->count (after RDY_BIT) = %d(%d)\n", unit, mbx->count, RDELAY_WAITREAD); -#endif + XDEBUG(2, ("scd%d: mbx->count (after RDY_BIT) = %d(%d)\n", unit, mbx->count, RDELAY_WAITREAD)); got_data: /* data is ready */ addr = bp->b_un.b_addr + mbx->skip; - write_control(port, SCD_DATA_RDY_CLR_BIT); - insb(port+SCD_READ_REG, addr, mbx->sz); + write_control(port, CBIT_DATA_READY_CLEAR); + insb(port+IREG_DATA, addr, mbx->sz); mbx->count = 100; for (i = 0; i < 20; i++) { - if (STATUS_BIT(port, SCD_RES_RDY_BIT)) + if (STATUS_BIT(port, SBIT_RESULT_READY)) goto waitfor_param; DELAY(100); } @@ -1354,34 +963,32 @@ got_data: } waitfor_param: - if (!STATUS_BIT(port, SCD_RES_RDY_BIT)) { + if (!STATUS_BIT(port, SBIT_RESULT_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITPARAM,hz/100); /* XXX */ return; } -#if DEBUG - if (mbx->count < 100) +#if SCD_DEBUG + if (mbx->count < 100 && scd_debuglevel > 0) printf("scd%d: mbx->count (paramwait) = %d(%d)\n", unit, mbx->count, 100); #endif got_param: - write_control(port, SCD_RES_RDY_CLR_BIT); - switch ((i = inb(port+SCD_RESULT_REG)) & 0xf0) { + write_control(port, CBIT_RESULT_READY_CLEAR); + switch ((i = inb(port+IREG_RESULT)) & 0xf0) { case 0x50: switch (i) { - case SONY_UNREC_CIRC_ERR: - case SONY_UNREC_LECC_ERR: + case ERR_FATAL_READ_ERROR1: + case ERR_FATAL_READ_ERROR2: printf("scd%d: unrecoverable read error 0x%x\n", unit, i); goto harderr; } break; case 0x20: - i = inb(port+SCD_RESULT_REG); + i = inb(port+IREG_RESULT); switch (i) { - case SONY_NOT_SPIN_ERR: -#if DEBUG - printf("scd%d: read error: drive not spinning\n", unit); -#endif + case ERR_NOT_SPINNING: + XDEBUG(1, ("scd%d: read error: drive not spinning\n", unit)); if (mbx->retry-- > 0) { state = SCD_S_BEGIN1; cd->flags &= ~SCDSPINNING; @@ -1393,7 +1000,7 @@ got_param: goto readerr; } case 0x00: - i = inb(port+SCD_RESULT_REG); + i = inb(port+IREG_RESULT); break; } @@ -1433,9 +1040,415 @@ changed: goto harderr; } -int -scdsize(dev_t dev) +static int +bcd2bin(bcd_t b) { - return -1; + return (b >> 4) * 10 + (b & 15); +} + +static bcd_t +bin2bcd(int b) +{ + return ((b / 10) << 4) | (b % 10); +} + +static void +hsg2msf(int hsg, bcd_t *msf) +{ + hsg += 150; + M_msf(msf) = bin2bcd(hsg / 4500); + hsg %= 4500; + S_msf(msf) = bin2bcd(hsg / 75); + F_msf(msf) = bin2bcd(hsg % 75); +} + +static int +msf2hsg(bcd_t *msf) +{ + return (bcd2bin(M_msf(msf)) * 60 + + bcd2bin(S_msf(msf))) * 75 + + bcd2bin(F_msf(msf)) - 150; +} + +static void +process_attention(unsigned unit) +{ + unsigned port = scd_data[unit].iobase; + unsigned char code; + int count = 0; + int i; + + while (IS_ATTENTION(port) && count++ < 30) { + write_control(port, CBIT_ATTENTION_CLEAR); + code = inb(port+IREG_RESULT); + +#if SCD_DEBUG + if (scd_debuglevel > 0) { + if (count == 1) + printf("scd%d: DEBUG: ATTENTIONS = 0x%x", unit, code); + else + printf(",0x%x", code); + } +#endif + + switch (code) { + case ATTEN_SPIN_DOWN: + scd_data[unit].flags &= ~SCDSPINNING; + break; + + case ATTEN_SPIN_UP_DONE: + scd_data[unit].flags |= SCDSPINNING; + break; + + case ATTEN_AUDIO_DONE: + scd_data[unit].audio_status = CD_AS_PLAY_COMPLETED; + break; + + case ATTEN_DRIVE_LOADED: + scd_data[unit].flags &= ~(SCDTOC|SCDSPINNING|SCDVALID); + scd_data[unit].audio_status = CD_AS_AUDIO_INVALID; + break; + + case ATTEN_EJECT_PUSHED: + scd_data[unit].flags &= ~SCDVALID; + break; + } + DELAY(100); + } +#if SCD_DEBUG + if (scd_debuglevel > 0 && count > 0) + printf("\n"); +#endif +} + +/* Returns 0 OR sony error code */ +static int +spin_up(unsigned unit) +{ + unsigned char res_reg[12]; + unsigned int res_size; + int rc; + int loop_count = 0; + +again: + rc = send_cmd(unit, CMD_SPIN_UP, NULL, 0, res_reg, &res_size); + if (rc != 0) { + XDEBUG(2, ("scd%d: CMD_SPIN_UP error 0x%x\n", unit, rc)); + return rc; + } + + if (!(scd_data[unit].flags & SCDTOC)) { + rc = send_cmd(unit, CMD_READ_TOC, 0); + if (rc == ERR_NOT_SPINNING) { + if (loop_count++ < 3) + goto again; + return rc; + } + if (rc != 0) + return rc; + } + + scd_data[unit].flags |= SCDSPINNING; + + return 0; +} + +static struct sony_tracklist * +get_tl(struct sony_toc *toc, int size) +{ + struct sony_tracklist *tl = &toc->tracks[0]; + + if (tl->track != 0xb0) + return tl; + (char *)tl += 9; + if (tl->track != 0xb1) + return tl; + (char *)tl += 9; + if (tl->track != 0xb2) + return tl; + (char *)tl += 9; + if (tl->track != 0xb3) + return tl; + (char *)tl += 9; + if (tl->track != 0xb4) + return tl; + (char *)tl += 9; + if (tl->track != 0xc0) + return tl; + (char *)tl += 9; + return tl; +} + +static int +read_toc(dev_t dev) +{ + unsigned unit; + struct scd_data *cd; + unsigned part = 0; /* For now ... */ + struct sony_toc toc; + struct sony_tracklist *tl; + int rc, i, j; + u_long first, last; + + unit = scd_unit(dev); + cd = scd_data + unit; + + rc = send_cmd(unit, CMD_GET_TOC, 1, part+1); + if (rc < 0) + return rc; + if (rc > sizeof(toc)) { + printf("scd%d: program error: toc too large (%d)\n", unit, rc); + return EIO; + } + if (get_result(unit, rc, (u_char *)&toc) != 0) + return EIO; + + XDEBUG(1, ("scd%d: toc read. len = %d, sizeof(toc) = %d\n", unit, rc, sizeof(toc))); + + tl = get_tl(&toc, rc); + first = msf2hsg(tl->start_msf); + last = msf2hsg(toc.lead_out_start_msf); + cd->blksize = SCDBLKSIZE; + cd->disksize = last*cd->blksize/DEV_BSIZE; + + XDEBUG(1, ("scd%d: firstsector = %d, lastsector = %d", unit, + first, last)); + + cd->first_track = bcd2bin(toc.first_track); + cd->last_track = bcd2bin(toc.last_track); + if (cd->last_track > (MAX_TRACKS-2)) + cd->last_track = MAX_TRACKS-2; + for (j = 0, i = cd->first_track; i <= cd->last_track; i++, j++) { + bcopy(tl[j].start_msf, cd->toc[i].start_msf, 3); +#ifdef SCD_DEBUG + if (scd_debuglevel > 0) { + if ((j % 3) == 0) + printf("\nscd%d: tracks ", unit); + printf("[%03d: %2d %2d %2d] ", i, + bcd2bin(cd->toc[i].start_msf[0]), + bcd2bin(cd->toc[i].start_msf[1]), + bcd2bin(cd->toc[i].start_msf[2])); + } +#endif + } + bcopy(toc.lead_out_start_msf, cd->toc[cd->last_track+1].start_msf, 3); +#ifdef SCD_DEBUG + if (scd_debuglevel > 0) { + i = cd->last_track+1; + printf("[END: %2d %2d %2d]\n", + bcd2bin(cd->toc[i].start_msf[0]), + bcd2bin(cd->toc[i].start_msf[1]), + bcd2bin(cd->toc[i].start_msf[2])); + } +#endif + + bzero(&cd->dlabel,sizeof(struct disklabel)); + /* filled with spaces first */ + strncpy(cd->dlabel.d_typename," ", + sizeof(cd->dlabel.d_typename)); + strncpy(cd->dlabel.d_typename, cd->name, + min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1)); + strncpy(cd->dlabel.d_packname,"unknown ", + sizeof(cd->dlabel.d_packname)); + cd->dlabel.d_secsize = cd->blksize; + cd->dlabel.d_nsectors = 100; + cd->dlabel.d_ntracks = 1; + cd->dlabel.d_ncylinders = (cd->disksize/100)+1; + cd->dlabel.d_secpercyl = 100; + cd->dlabel.d_secperunit = cd->disksize; + cd->dlabel.d_rpm = 300; + cd->dlabel.d_interleave = 1; + cd->dlabel.d_flags = D_REMOVABLE; + cd->dlabel.d_npartitions= 1; + cd->dlabel.d_partitions[0].p_offset = 0; + cd->dlabel.d_partitions[0].p_size = cd->disksize; + cd->dlabel.d_partitions[0].p_fstype = 9; + cd->dlabel.d_magic = DISKMAGIC; + cd->dlabel.d_magic2 = DISKMAGIC; + cd->dlabel.d_checksum = dkcksum(&cd->dlabel); + + cd->flags |= SCDTOC; + + return 0; +} + +static inline void +write_control(unsigned port, unsigned data) +{ + outb(port + OREG_CONTROL, data); +} + +static void +init_drive(unsigned unit) +{ + int rc; + + rc = send_cmd(unit, CMD_SET_DRIVE_PARAM, 2, + 0x05, 0x03 | ((scd_data[unit].double_speed) ? 0x04: 0)); + if (rc != 0) + printf("scd%d: Unable to set parameters. Errcode = 0x%x\n", unit, rc); +} + +/* Returns 0 or errno */ +static int +get_result(u_int unit, int result_len, u_char *result) +{ + unsigned int port = scd_data[unit].iobase; + unsigned int res_reg = port + IREG_RESULT; + unsigned char c; + int loop_index = 2; /* send_cmd() reads two bytes ... */ + + XDEBUG(1, ("scd%d: DEBUG: get_result: bytes=%d\n", unit, result_len)); + + while (result_len-- > 0) { + if (loop_index++ >= 10) { + loop_index = 1; + if (waitfor_status_bits(unit, SBIT_RESULT_READY, 0)) + return EIO; + write_control(port, CBIT_RESULT_READY_CLEAR); + } + if (result) + *result++ = inb(res_reg); + else + (void)inb(res_reg); + } + return 0; +} + +/* Returns -0x100 for timeout, -(drive error code) OR number of result bytes */ +static int +send_cmd(u_int unit, u_char cmd, u_int nargs, ...) +{ + va_list ap; + u_int port = scd_data[unit].iobase; + u_int reg; + u_char c; + int rc; + int i; + + if (waitfor_status_bits(unit, 0, SBIT_BUSY)) { + printf("scd%d: drive busy\n", unit); + return -0x100; + } + + XDEBUG(1,("scd%d: DEBUG: send_cmd: cmd=0x%x nargs=%d", unit, cmd, nargs)); + + write_control(port, CBIT_RESULT_READY_CLEAR); + write_control(port, CBIT_RPARAM_CLEAR); + + for (i = 0; i < 100; i++) + if (FSTATUS_BIT(port, FBIT_WPARAM_READY)) + break; + if (!FSTATUS_BIT(port, FBIT_WPARAM_READY)) { + XDEBUG(1, ("\nscd%d: wparam timeout\n", unit)); + return -EIO; + } + + va_start(ap, nargs); + reg = port + OREG_WPARAMS; + for (i = 0; i < nargs; i++) { + c = (u_char)va_arg(ap, int); + outb(reg, c); + XDEBUG(1, (",{0x%x}", c)); + } + va_end(ap); + XDEBUG(1, ("\n")); + + outb(port+OREG_COMMAND, cmd); + + if (rc = waitfor_status_bits(unit, SBIT_RESULT_READY, SBIT_BUSY)) + return -0x100; + + reg = port + IREG_RESULT; + write_control(port, CBIT_RESULT_READY_CLEAR); + switch ((rc = inb(reg)) & 0xf0) { + case 0x20: + rc = inb(reg); + /* FALL TROUGH */ + case 0x50: + XDEBUG(1, ("scd%d: DEBUG: send_cmd: drive_error=0x%x\n", unit, rc)); + return -rc; + case 0x00: + default: + rc = inb(reg); + XDEBUG(1, ("scd%d: DEBUG: send_cmd: result_len=%d\n", unit, rc)); + return rc; + } +} + +static void +print_error(int unit, int errcode) +{ + switch (errcode) { + case -ERR_CD_NOT_LOADED: + printf("scd%d: door is open\n", unit); + break; + case -ERR_NO_CD_INSIDE: + printf("scd%d: no cd inside\n", unit); + break; + default: + if (errcode == -0x100 || errcode > 0) + printf("scd%d: device timeout\n", unit); + else + printf("scd%d: unexpected error 0x%x\n", unit, -errcode); + break; + } +} + +/* Returns 0 or errno value */ +static int +waitfor_status_bits(int unit, int bits_set, int bits_clear) +{ + u_int port = scd_data[unit].iobase; + u_int flags = scd_data[unit].flags; + u_int reg = port + IREG_STATUS; + u_int max_loop; + u_char c = 0; + + if (flags & SCDPROBING) { + max_loop = 0; + while (max_loop++ < 1000) { + c = inb(reg); + if (c == 0xff) + return EIO; + if (c & SBIT_ATTENTION) { + process_attention(unit); + continue; + } + if ((c & bits_set) == bits_set && + (c & bits_clear) == 0) + { + break; + } + DELAY(10000); + } + } else { + max_loop = 100; + while (max_loop-- > 0) { + c = inb(reg); + if (c & SBIT_ATTENTION) { + process_attention(unit); + continue; + } + if ((c & bits_set) == bits_set && + (c & bits_clear) == 0) + { + break; + } + tsleep(waitfor_status_bits, PZERO - 1, "waitfor", hz/10); + } + } + if ((c & bits_set) == bits_set && + (c & bits_clear) == 0) + { + return 0; + } +#ifdef SCD_DEBUG + if (scd_debuglevel > 0) + printf("scd%d: DEBUG: waitfor: TIMEOUT (0x%x,(0x%x,0x%x))\n", unit, c, bits_set, bits_clear); + else +#endif + printf("scd%d: timeout.\n", unit); + return EIO; } #endif /* NSCD > 0 */ diff --git a/sys/gnu/i386/scdreg.h b/sys/gnu/i386/scdreg.h index 76a25c5d44bb2..223013e778b57 100644 --- a/sys/gnu/i386/scdreg.h +++ b/sys/gnu/i386/scdreg.h @@ -2,7 +2,7 @@ * Copyright (c) 1995 Mikael Hybsch * * The Linux driver cdu31a has been used as a reference when writing this - * code, therefor bringing it under the GNU Public License. The following + * code, therefore bringing it under the GNU Public License. The following * conditions of redistribution therefore apply: * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * $Id$ + * */ #ifndef SCD_H @@ -35,249 +37,108 @@ typedef unsigned char bcd_t; #define S_msf(msf) msf[1] #define F_msf(msf) msf[2] -#define IS_ATTENTION(port) ((inb(port+SCD_STATUS_REG) & SCD_ATTN_BIT) != 0) -#define IS_BUSY(port) ((inb(port+SCD_STATUS_REG) & SCD_BUSY_BIT) != 0) -#define IS_DATA_RDY(port) ((inb(port+SCD_STATUS_REG) & SCD_DATA_RDY_BIT) != 0) -#define STATUS_BIT(port, bit) ((inb(port+SCD_STATUS_REG) & (bit)) != 0) -#define FIFOST_BIT(port, bit) ((inb(port+SCD_FIFOST_REG) & (bit)) != 0) - -#define SCD_CMD_REG 0 -#define SCD_PARAM_REG 1 -#define SCD_WRITE_REG 2 -#define SCD_CONTROL_REG 3 -# define SCD_ATTN_CLR_BIT 0x01 -# define SCD_RES_RDY_CLR_BIT 0x02 -# define SCD_DATA_RDY_CLR_BIT 0x04 -# define SCD_ATTN_INT_EN_BIT 0x08 -# define SCD_RES_RDY_INT_EN_BIT 0x10 -# define SCD_DATA_RDY_INT_EN_BIT 0x20 -# define SCD_PARAM_CLR_BIT 0x40 -# define SCD_DRIVE_RESET_BIT 0x80 - -#define SCD_STATUS_REG 0 -# define SCD_ATTN_BIT 0x01 -# define SCD_RES_RDY_BIT 0x02 -# define SCD_DATA_RDY_BIT 0x04 -# define SCD_ATTN_INT_ST_BIT 0x08 -# define SCD_RES_RDY_INT_ST_BIT 0x10 -# define SCD_DATA_RDY_INT_ST_BIT 0x20 -# define SCD_DATA_REQUEST_BIT 0x40 -# define SCD_BUSY_BIT 0x80 -#define SCD_RESULT_REG 1 -#define SCD_READ_REG 2 -#define SCD_FIFOST_REG 3 -# define SCD_PARAM_WRITE_RDY_BIT 0x01 -# define SCD_PARAM_REG_EMPTY_BIT 0x02 -# define SCD_RES_REG_NOT_EMP_BIT 0x04 -# define SCD_RES_REG_FULL_BIT 0x08 - -#define SCD_REQ_DRIVE_CONFIG_CMD 0x00 -#define SCD_REQ_DRIVE_MODE_CMD 0x01 -#define SCD_REQ_DRIVE_PARAM_CMD 0x02 -#define SCD_REQ_MECH_STATUS_CMD 0x03 -#define SCD_REQ_AUDIO_STATUS_CMD 0x04 -#define SCD_SET_DRIVE_PARAM_CMD 0x10 -#define SCD_REQ_TOC_DATA_CMD 0x20 -#define SCD_REQ_SUBCODE_ADDRESS_CMD 0x21 -#define SCD_REQ_UPC_EAN_CMD 0x22 -#define SCD_REQ_ISRC_CMD 0x23 -#define SCD_REQ_TOC_DATA_SPEC_CMD 0x24 - -#define SCD_READ_TOC_CMD 0x30 -#define SCD_SEEK_CMD 0x31 -#define SCD_READ_CMD 0x32 -#define SCD_READ_BLKERR_STAT_CMD 0x34 -#define SCD_ABORT_CMD 0x35 -#define SCD_READ_TOC_SPEC_CMD 0x36 +#define IS_ATTENTION(port) ((inb(port+IREG_STATUS) & SBIT_ATTENTION) != 0) +#define IS_BUSY(port) ((inb(port+IREG_STATUS) & SBIT_BUSY) != 0) +#define IS_DATA_RDY(port) ((inb(port+IREG_STATUS) & SBIT_DATA_READY) != 0) +#define STATUS_BIT(port, bit) ((inb(port+IREG_STATUS) & (bit)) != 0) +#define FSTATUS_BIT(port, bit) ((inb(port+IREG_FSTATUS) & (bit)) != 0) -#define SCD_AUDIO_PLAYBACK_CMD 0x40 -#define SCD_AUDIO_STOP_CMD 0x41 -#define SCD_AUDIO_SCAN_CMD 0x42 +#define OREG_COMMAND 0 +#define OREG_WPARAMS 1 +#define OREG_CONTROL 3 +#define CBIT_ATTENTION_CLEAR 0x01 +#define CBIT_RESULT_READY_CLEAR 0x02 +#define CBIT_DATA_READY_CLEAR 0x04 +#define CBIT_RPARAM_CLEAR 0x40 +#define CBIT_RESET_DRIVE 0x80 -#define SCD_EJECT_CMD 0x50 -#define SCD_SPIN_UP_CMD 0x51 -#define SCD_SPIN_DOWN_CMD 0x52 +#define IREG_STATUS 0 +#define SBIT_ATTENTION 0x01 +#define SBIT_RESULT_READY 0x02 +#define SBIT_DATA_READY 0x04 +#define SBIT_BUSY 0x80 -#define SCD_SD_DECODE_PARAM 0x00 -#define SCD_SD_INTERFACE_PARAM 0x01 -#define SCD_SD_BUFFERING_PARAM 0x02 -#define SCD_SD_AUDIO_PARAM 0x03 -#define SCD_SD_AUDIO_VOLUME 0x04 -#define SCD_SD_MECH_CONTROL 0x05 -#define SCD_SD_AUTO_SPIN_DOWN_TIME 0x06 - -#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03) -#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04) -#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08) -#define SONY_HWC_DOUBLE_SPEED(c) (c.hw_config[0] & 0x10) -#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6) -#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01) -#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02) -#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04) - -#define SONY_HWC_CADDY_LOAD_MECH 0x00 -#define SONY_HWC_TRAY_LOAD_MECH 0x01 -#define SONY_HWC_POPUP_LOAD_MECH 0x02 -#define SONY_HWC_UNKWN_LOAD_MECH 0x03 - -#define SONY_HWC_8KB_BUFFER 0x00 -#define SONY_HWC_32KB_BUFFER 0x01 -#define SONY_HWC_64KB_BUFFER 0x02 -#define SONY_HWC_UNKWN_BUFFER 0x03 - -/* - * The following are errors returned from the drive. - */ +#define IREG_RESULT 1 +#define IREG_DATA 2 +#define IREG_FSTATUS 3 +#define FBIT_WPARAM_READY 0x01 -#define SONY_ILL_CMD_ERR 0x10 -#define SONY_ILL_PARAM_ERR 0x11 +#define CMD_GET_DRIVE_CONFIG 0x00 +#define CMD_SET_DRIVE_PARAM 0x10 +#define CMD_GET_SUBCHANNEL_DATA 0x21 +#define CMD_GET_TOC 0x24 +#define CMD_READ_TOC 0x30 +#define CMD_READ 0x34 +#define CMD_PLAY_AUDIO 0x40 +#define CMD_STOP_AUDIO 0x41 +#define CMD_EJECT 0x50 +#define CMD_SPIN_UP 0x51 +#define CMD_SPIN_DOWN 0x52 -#define SONY_NOT_LOAD_ERR 0x20 -#define SONY_NO_DISK_ERR 0x21 -#define SONY_NOT_SPIN_ERR 0x22 -#define SONY_SPIN_ERR 0x23 -#define SONY_SPINDLE_SERVO_ERR 0x25 -#define SONY_FOCUS_SERVO_ERR 0x26 -#define SONY_EJECT_MECH_ERR 0x29 -#define SONY_AUDIO_PLAYING_ERR 0x2a -#define SONY_EMERGENCY_EJECT_ERR 0x2c +#define ERR_CD_NOT_LOADED 0x20 +#define ERR_NO_CD_INSIDE 0x21 +#define ERR_NOT_SPINNING 0x22 +#define ERR_FATAL_READ_ERROR1 0x53 +#define ERR_FATAL_READ_ERROR2 0x57 -#define SONY_FOCUS_ERR 0x30 -#define SONY_FRAME_SYNC_ERR 0x31 -#define SONY_SUBCODE_ADDR_ERR 0x32 -#define SONY_BLOCK_SYNC_ERR 0x33 -#define SONY_HEADER_ADDR_ERR 0x34 +#define ATTEN_DRIVE_LOADED 0x80 +#define ATTEN_EJECT_PUSHED 0x81 +#define ATTEN_AUDIO_DONE 0x90 +#define ATTEN_SPIN_UP_DONE 0x24 +#define ATTEN_SPIN_DOWN 0x27 +#define ATTEN_EJECT_DONE 0x28 -#define SONY_ILL_TRACK_R_ERR 0x40 -#define SONY_MODE_0_R_ERR 0x41 -#define SONY_ILL_MODE_R_ERR 0x42 -#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43 -#define SONY_MODE_R_ERR 0x44 -#define SONY_FORM_R_ERR 0x45 -#define SONY_LEAD_OUT_R_ERR 0x46 -#define SONY_BUFFER_OVERRUN_R_ERR 0x47 -#define SONY_UNREC_CIRC_ERR 0x53 -#define SONY_UNREC_LECC_ERR 0x57 - -#define SONY_NO_TOC_ERR 0x60 -#define SONY_SUBCODE_DATA_NVAL_ERR 0x61 -#define SONY_FOCUS_ON_TOC_READ_ERR 0x63 -#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64 -#define SONY_TOC_DATA_ERR 0x65 - -#define SONY_HW_FAILURE_ERR 0x70 -#define SONY_LEAD_IN_A_ERR 0x91 -#define SONY_LEAD_OUT_A_ERR 0x92 -#define SONY_DATA_TRACK_A_ERR 0x93 +struct sony_drive_configuration { + char vendor[8]; + char product[16]; + char revision[8]; + u_short config; +}; -#define SONY_NO_CIRC_ERR_BLK_STAT 0x50 -#define SONY_NO_LECC_ERR_BLK_STAT 0x54 -#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55 -#define SONY_NO_ERR_DETECTION_STAT 0x59 +/* Almost same as cd_sub_channel_position_data */ +struct sony_subchannel_position_data { + u_char control:4; + u_char addr_type:4; + u_char track_number; + u_char index_number; + u_char rel_msf[3]; + u_char dummy; + u_char abs_msf[3]; +}; -#define SONY_EMER_EJECT_ATTN 0x2c -#define SONY_HW_FAILURE_ATTN 0x70 -#define SONY_MECH_LOADED_ATTN 0x80 -#define SONY_EJECT_PUSHED_ATTN 0x81 +struct sony_tracklist { + u_char :8; + u_char track; + u_char start_msf[3]; +}; -#define SONY_AUDIO_PLAY_DONE_ATTN 0x90 -#define SONY_LEAD_IN_ERR_ATTN 0x91 -#define SONY_LEAD_OUT_ERR_ATTN 0x92 -#define SONY_DATA_TRACK_ERR_ATTN 0x93 -#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94 +#define MAX_TRACKS 100 -#define SONY_SPIN_UP_COMPLETE_ATTN 0x24 -#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25 -#define SONY_FOCUS_SERVO_ERR_ATTN 0x26 -#define SONY_TOC_READ_DONE_ATTN 0x62 -#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63 -#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65 +struct sony_toc { + u_char session_number; -#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27 -#define SONY_EJECT_COMPLETE_ATTN 0x28 -#define SONY_EJECT_MECH_ERR_ATTN 0x29 + u_char :8; + u_char :8; + u_char first_track; + u_char :8; + u_char :8; + u_char :8; + u_char :8; + u_char last_track; + u_char :8; + u_char :8; -struct s_sony_drive_config -{ - unsigned char exec_status[2]; - char vendor_id[8]; - char product_id[16]; - char product_rev_level[8]; - unsigned char hw_config[2]; -}; + u_char :8; + u_char :8; + u_char lead_out_start_msf[3]; -struct s_sony_subcode -{ - unsigned char exec_status[2]; - unsigned char address :4; - unsigned char control :4; - unsigned char track_num; - unsigned char index_num; - unsigned char rel_msf[3]; - unsigned char reserved1; - unsigned char abs_msf[3]; -}; + struct sony_tracklist tracks[MAX_TRACKS]; -#define MAX_TRACKS 100 + /* The rest is just to take space in case all data is returned */ -struct s_sony_session_toc -{ - unsigned char exec_status[2]; - unsigned char session_number; - unsigned char address0 :4; - unsigned char control0 :4; - unsigned char point0; - unsigned char first_track_num; - unsigned char disk_type; - unsigned char dummy0; - unsigned char address1 :4; - unsigned char control1 :4; - unsigned char point1; - unsigned char last_track_num; - unsigned char dummy1; - unsigned char dummy2; - unsigned char address2 :4; - unsigned char control2 :4; - unsigned char point2; - unsigned char lead_out_start_msf[3]; - unsigned char addressb0 :4; - unsigned char controlb0 :4; - unsigned char pointb0; - unsigned char next_poss_prog_area_msf[3]; - unsigned char num_mode_5_pointers; - unsigned char max_start_outer_leadout_msf[3]; - unsigned char addressb1 :4; - unsigned char controlb1 :4; - unsigned char pointb1; - unsigned char dummyb0_1[4]; - unsigned char num_skip_interval_pointers; - unsigned char num_skip_track_assignments; - unsigned char dummyb0_2; - unsigned char addressb2 :4; - unsigned char controlb2 :4; - unsigned char pointb2; - unsigned char tracksb2[7]; - unsigned char addressb3 :4; - unsigned char controlb3 :4; - unsigned char pointb3; - unsigned char tracksb3[7]; - unsigned char addressb4 :4; - unsigned char controlb4 :4; - unsigned char pointb4; - unsigned char tracksb4[7]; - unsigned char addressc0 :4; - unsigned char controlc0 :4; - unsigned char pointc0; - unsigned char dummyc0[7]; - struct s_sony_tracklist - { - unsigned char address :4; - unsigned char control :4; - unsigned char track; - unsigned char track_start_msf[3]; - } tracks[MAX_TRACKS]; + u_char dummy[6*9]; }; #endif /* SCD_H */ |
