#include "ext.h" #include "syscd.h" #include "ext_strings.h" #include "ext_common.h" #define REQ_COUNT 8 // number of simultaneous async search requests(!) #define FromBCD(x) (((((x)>>4)&0x0F)*10)+((x)&0x0F)) #define ToBCD(x) ((((x)/10)<<4)+((x)%10)) #define ReadTOC 100 #define ReadQ 101 #define ReadHead 102 #define ATrkSearch 103 #define APlay 104 #define APause 105 #define AStop 106 #define AStatus 107 #define AScan 108 #define AVolume 109 #define AEject 7 #define WhoIsThere 97 #define DiskStatus 8 #define DriverGestalt 43 #define AccRun 65 #define SearchVerb 0 #define PlayVerb 1 #define StopVerb 2 #define PlayModeStereo 9 #define DriverName ".AppleCD" //#define DRIVERNAME_DEBUG #define MAX_CD_ARG 16 enum { // relevant cd codes CHANGEBLOCKSIZE = 79, READTOC = 100, GETSPINDLESPEED = 113, SETSPINDLESPEED = 114, READAUDIO = 115, AUDIOSTATUS = 107, AUDIOSTOP = 106, MAXSPEED = 255 }; long syscd_devcount = 0; t_syscd syscd_devices[CD_MAX_DEVICES]; Boolean syscd_allocated[129]; CDCntrlParam syscd_ejbuf; short syscd_findnext(void) { short drvrRefNum; DCtlHandle aDCtlEntry; unsigned char *aDriverName; char *appleDriverName = ".AppleCD"; char cdrivername[256]; for (drvrRefNum = 33; drvrRefNum <= 128; drvrRefNum++) { if ((drvrRefNum >= 33 && drvrRefNum <= 39) || (drvrRefNum >= 49 && drvrRefNum <= 128)) { aDCtlEntry = GetDCtlEntry(-drvrRefNum); if(!syscd_allocated[drvrRefNum]) { if (aDCtlEntry) { if (((**aDCtlEntry).dCtlFlags >> dRAMBased) & 1) aDriverName = (unsigned char *)((*(DCtlPtr)aDCtlEntry).dCtlDriver) + 18; else aDriverName = (unsigned char *)((**aDCtlEntry).dCtlDriver) + 18; ptoccpy(cdrivername,(char *)aDriverName); #ifdef DRIVERNAME_DEBUG if (cdrivername[0]) post("cd: driver %s at %ld",cdrivername,-drvrRefNum); #endif if (!strncmp(cdrivername, appleDriverName, 8)) { syscd_allocated[drvrRefNum] = true; return -drvrRefNum; } } } } } return -1; } void syscd_find(t_symbol *objname) { short refNum; do { refNum = syscd_findnext(); //debug_printf("syscd_findnext %ld",(long)refNum); if (refNum != -1) syscd_add(objname,refNum); } while (refNum != -1); } // add a CD drive/device void syscd_add(t_symbol *objname, short refNum) { t_syscd *c = syscd_devices + syscd_devcount; DCtlHandle dh; QHdrPtr qh; DrvQEl *dq; long *utbp; CDGenericParam *p; long intf; DriverGestaltBootResponse *res; Boolean foundInDriveQueue = false; short dCount,err,saveDrive; CDCntrlParam cpb; qh = GetDrvQHdr(); dCount = 0; for (dq = (DrvQEl *)qh->qHead; dq ; dq = (DrvQEl *)dq->qLink) { #ifdef DRIVERNAME_DEBUG post("cd: drive %ld has refnum %ld",dq->dQDrive,dq->dQRefNum); #endif if (dq->dQRefNum==refNum) { c->d_driveNums[dCount++] = dq->dQDrive; foundInDriveQueue = true; dCount++; continue; } if (dq==(DrvQEl *)qh->qTail) break; } //debug_printf("after dq loop"); if (!foundInDriveQueue) { //error("cd: driver %d missing in drive queue",refNum); return; } c->d_ownername = objname; c->d_numDrives = dCount; c->d_refNum = refNum; saveDrive = c->d_driveNums[0]; c->d_driveNums[0] = 1; syscd_setup(c,(CDCntrlParam *)&cpb,1,DriverGestalt,false); p = (CDGenericParam *)&cpb; *((long *)&p->csParam[0]) = 'intf'; err = PBStatus((ParmBlkPtr)&cpb,FALSE); BlockMove(&p->csParam[2],&intf,4); if (1) { post("CD interface type %c%c%c%c",(short)((intf>>24)&0xFF), (short)((intf>>16)&0xFF),(short)((intf>>8)&0xFF),(short)(intf&0xFF)); } else { intf = 0; error("%s: driver gestalt error %d",c->d_ownername->s_name,err); } c->d_kind = intf; *((long *)&p->csParam[0]) = 'boot'; err = PBStatus((ParmBlkPtr)&cpb,FALSE); if (1) { res = (DriverGestaltBootResponse *)&p->csParam[2]; if (intf == 'scsi') { post("%s: SCSI ID %d, LUN %d",c->d_ownername->s_name,res->extDev>>3,res->extDev&7); c->d_id = res->extDev>>3; c->d_lun = res->extDev&7; c->d_bus = 0; // ? } else if (intf == 'atpi') { c->d_id = c->d_bus = res->extDev; // refine later post("%s: ATAPI bus %d",c->d_ownername->s_name,res->extDev); } } else error("%s: driver gestalt error %d",c->d_ownername->s_name,err); syscd_devcount++; c->d_driveNums[0] = saveDrive; } void syscd_open(t_symbol *objname) { // the CD-ROM driver should already be open syscd_find(objname); // find all the devices if (!syscd_devcount) { //error("%s: no drives",objname); setmem(syscd_allocated,129,0); // so you can look again return; } } short syscd_control(t_syscd *x, CDCntrlParam *cs, short drive, short whichCall, short async) { if (!async) cs->ioCompletion = 0; cs->ioVRefNum = x->d_driveNums[drive-1]; cs->ioRefNum = x->d_refNum; cs->csCode = whichCall; return PBControl((ParmBlkPtr)cs,async); } short syscd_status(t_syscd *x, CDCntrlParam *cs, short drive, short whichCall, short async) { if (!async) cs->ioCompletion = 0; cs->ioVRefNum = x->d_driveNums[drive-1]; cs->ioRefNum = x->d_refNum; cs->csCode = whichCall; return PBStatus((ParmBlkPtr)cs,async); } void syscd_setup(t_syscd *x, CDCntrlParam *cs, short drive, short whichCall, short async) { if (!async) cs->ioCompletion = 0; cs->ioVRefNum = x->d_driveNums[drive-1]; cs->ioRefNum = x->d_refNum; cs->csCode = whichCall; } short syscd_getspeed(t_syscd *x, short drive) { ParamBlockRec pb; pb.cntrlParam.ioVRefNum = x->d_driveNums[drive-1]; pb.cntrlParam.ioCRefNum = x->d_refNum; pb.cntrlParam.csCode = GETSPINDLESPEED; if (!PBControlSync(&pb)) return pb.cntrlParam.csParam[0]; else return -1; } void syscd_stopplay(t_syscd *x, short drive) { ParamBlockRec pb; short err,state; pb.cntrlParam.ioVRefNum = x->d_driveNums[drive-1]; pb.cntrlParam.ioCRefNum = x->d_refNum; pb.cntrlParam.csCode = AUDIOSTATUS; err = PBControlSync(&pb); if (err) return; state = pb.cntrlParam.csParam[0] >> 8; if (state == 0 && state <= 2) { // play, paused, or muted pb.cntrlParam.csCode = AUDIOSTOP; pb.cntrlParam.csParam[0] = pb.cntrlParam.csParam[1] = pb.cntrlParam.csParam[2] = 0; err = PBControlSync(&pb); } } void syscd_setspeed(t_syscd *x, short drive, short speed) { ParamBlockRec pb; pb.cntrlParam.ioVRefNum = x->d_driveNums[drive-1]; pb.cntrlParam.ioCRefNum = x->d_refNum; pb.cntrlParam.csCode = SETSPINDLESPEED; pb.cntrlParam.csParam[0] = speed; PBControlSync(&pb); } t_syscd *syscd_select(t_symbol *objname,short argc, t_atom *argv) { t_syscd *cdev = 0; short busArg = 0, ordinalArg = 0; long i; t_syscd *dc; if (argc >= 1) { if (argv->a_type==A_LONG) busArg = argv->a_w.w_long; else if (argv->a_type==A_SYM) { if (syscd_ispound(argv->a_w.w_sym)) busArg = 0; else ordinalArg = syscd_ordcheck(argv->a_w.w_sym); } } if (ordinalArg) { if (ordinalArg > syscd_devcount) { post("%s: no %s drive, disabling object",objname->s_name,argv->a_w.w_sym->s_name); return 0; } else { cdev = syscd_devices + ordinalArg-1; post("%s: %s drive is ID %d",objname->s_name,argv->a_w.w_sym->s_name,cdev->d_id); return cdev; } } else { for (i = 0, dc = syscd_devices; i < syscd_devcount; i++, dc++) { if (dc->d_kind == 'scsi' && dc->d_id == busArg) { cdev = dc; return cdev; } else if (dc->d_kind == 'atpi' && !busArg) { cdev = dc; return cdev; } } } if (syscd_devcount) { cdev = syscd_devices; post("%s: using ID %d",cdev->d_ownername->s_name,cdev->d_id); } else { post("%s: no drive, or drive has no audio CD",objname->s_name); } return cdev; } short syscd_ordcheck(t_symbol *s) { short i; static t_symbol *ps_ord[6]; static char ord_inited = false; if (!ord_inited) { ps_ord[0] = gensym("first"); ps_ord[1] = gensym("second"); ps_ord[2] = gensym("third"); ps_ord[3] = gensym("fourth"); ps_ord[4] = gensym("fifth"); ps_ord[5] = gensym("sixth"); } for (i = 0; i < 6; i++) if (s==ps_ord[i]) return i + 1; return 0; } short syscd_ispound(t_symbol *s) { short i; if (s->s_name[0]=='#') if (InRange(s->s_name[1],'1','9')) if (!s->s_name[2]) return true; return false; } short syscd_gettoc(t_syscd *x, short drive, t_cdtoc **toc, short dopost) { ParamBlockRec pb; short err; short i; INDEX *p; Byte firstTrack,lastTrack; long ntrk; pb.cntrlParam.ioVRefNum = x->d_driveNums[drive-1]; pb.cntrlParam.ioCRefNum = x->d_refNum; pb.cntrlParam.csCode = READTOC; pb.cntrlParam.csParam[0] = 1; if (err = PBControlSync(&pb)) return err; firstTrack = pb.cntrlParam.csParam[0] >> 8 & 0xff; lastTrack = pb.cntrlParam.csParam[0] >> 0 & 0xff; ntrk = bcd2x(lastTrack) - bcd2x(firstTrack) + 1; *toc = (t_cdtoc *)NewPtr(sizeof(t_cdtoc) + (ntrk * sizeof(INDEX))); (*toc)->c_first = bcd2x(firstTrack); (*toc)->c_last = bcd2x(lastTrack); (*toc)->c_ntracks = ntrk; pb.cntrlParam.csCode = READTOC; pb.cntrlParam.csParam[0] = 2; if (err = PBControlSync(&pb)) return err; p = (*toc)->c_toc + ntrk; p->min = pb.cntrlParam.csParam[0] >> 8 & 0xff; p->sec = pb.cntrlParam.csParam[0] >> 0 & 0xff; p->blk = pb.cntrlParam.csParam[1] >> 8 & 0xff; pb.cntrlParam.csCode = READTOC; pb.cntrlParam.csParam[0] = 3; *(long *) &pb.cntrlParam.csParam[1] = (long)(*toc)->c_toc; pb.cntrlParam.csParam[3] = ntrk * sizeof(INDEX); pb.cntrlParam.csParam[4] = 1 << 8; if (err = PBControlSync(&pb)) { DisposePtr((char *)*toc); return err; } if (dopost) { post("tracks: %ld", ntrk); for (i = 0, p = (*toc)->c_toc; i <= ntrk; i++, p++) { if (i == ntrk) post("end: %2d %2d %2d", bcd2x( p->min ), bcd2x( p->sec ), bcd2x( p->blk )); else post("track %2d: %2d %2d %2d", i + 1,bcd2x( p->min ), bcd2x( p->sec ), bcd2x( p->blk )); } } return 0; } // async eject void syscd_eject(t_syscd *x, short drive) { short err; static IOCompletionUPP syscd_ejupp; if (!syscd_ejupp) syscd_ejupp = NewIOCompletionProc(syscd_ejectcompletion); syscd_ejbuf.ioCompletion = syscd_ejupp; BlockMove(x,&syscd_ejbuf.unused,sizeof(t_syscd *)); syscd_control(x,&syscd_ejbuf,x->d_driveNums[drive-1],AEject,true); } void syscd_unmount(t_syscd *x, t_symbol *s, short argc, t_atom *argv) { short vol = argv->a_w.w_long; short err; //post("syscd_unmount"); err = Eject(0L,vol); if (err) error("%s: eject error %d",x->d_ownername->s_name,err); err = UnmountVol(0L,vol); if (err) error("%s: unmount error %d",x->d_ownername->s_name,err); } pascal void syscd_ejectcompletion(ParmBlkPtr *pb) { CDCntrlParam *cpb = (CDCntrlParam *)pb; t_syscd *x; t_atom a; BlockMove(cpb->unused,&x,sizeof(t_syscd *)); if (!cpb->ioCompletion) { a.a_type = A_LONG; a.a_w.w_long = cpb->ioVRefNum; defer_low(x,(method)syscd_unmount,0L,1,&a); } }