/* Call me Parse5A(). * I accept a pointer to a string containing a command in the * Mark5A command set as defined in the memo "Preliminary Mark5A * command set," by Alan R. Whitney, 29 September, 2001. The * command in this string is implemented using calls to StreamStor * functions as described in "Boulder Instruments StreamStor * Real-Time Storage Controller, Installation and User's Guide" * and "Conduant StreamStor Software Enhancements Plan * (preliminary) 10/08/2001. There are also calls to send and * receive status information to and from the input and output * boards on the PCI bus. * Note that I modify the input string (!); keep a copy if need be. * I return a pointer to another string that is the response * to be returned to the program that sent the command. For * certain specific errors, I return a NULL pointer, but for most * errors I return a string for the calling program to interpret as * an error. I may also send error and warning messages to stderr * depending, in part, on msglev. * Revised: 2005 June 01, JAB */ #define _LARGEFILE_SOURCE /* Large file support (LFS) */ #define _LARGEFILE64_SOURCE #include "Parse5A.h" /* The following are for Mark5B, are used herein, but are also * available globally */ // unsigned long inbuf[MAXWD + 6]; /* Input buffer to hold one frame + */ unsigned long * inbuf; /* To point to input buffer */ // /* (Must be longer than MAXWD) */ // unsigned long * pdata = inbuf; /* Input data */ unsigned long * pdata; /* Input data */ unsigned long long foff = 0; /* Offset into disk file, longs (NOT bytes) */ struct Header header, header0, header64; /* For Mark5B */ /* The following are used by several of the functions below */ static char bk[] = { 'A', 'B', '-', '?' }; /* Label banks */ static char optn[8] = "n"; /* For mode in fopen() */ static char * ptr[16]; /* Up to 16 tokens in instring */ static char * pt; /* '?' at end of query? */ static char outstring[1024]; /* To be returned (sometimes) */ static double srot = 0.0; /* Start ROT, if any */ static char formt[8]; /* Same as outb.formt[]? */ static int trke = 0; /* Track-number error? */ /* trke = 0, normal OK; 1, duplicated track OK; 2, error */ static int trkt; /* Track number read from frame header */ static int trkd; /* trkt converted to 2--33, 102--133 notation */ static long pati, patj, patlen; /* From PatScan() */ static UINT banko; /* Old (previous) version of bank */ static int prot = 0; /* Tag protection sequence */ static int armOnly = FALSE; /* Arm only in Dplay()? */ /* Note that the following two strings might apply to either bank */ static char vsn[XLR_LABEL_LENGTH] = ""; /* Copy of VSN */ static char dms[32] = "Unknown"; /* Disk-module status */ static int getLabel(int tbank) { /* Read Label for bank tbank, parse into VSN and DMS */ char * pchar; /* In order to cover both cases, bank==tbank or not, we use * XLRGetBankStatus() instead of XLRGetLabel() */ if (XLRGetBankStatus(dev, tbank, &bkstat) != XLR_SUCCESS) { /* OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s getLabel() ERROR: XLRGetBankStatus() returned error \n", me); return(-1); /* Error */ } (void) errors(); /* But bkstat.Label is OK only if bkstat.State is STATE_READY */ if (bkstat.State != STATE_READY) { /* OK? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s getLabel() ERROR: Bank %c is not ready \n", me, bk[tbank]); (void) errors(); return(-2); /* Error */ } /* Else OK. Separate Label into its parts: VSN preceding and DMS * following the record separator, RS = \036, if any */ if ((pchar = strchr(bkstat.Label, '\036')) == NULL) { /* Got RS? */ (void) strncpy(vsn, bkstat.Label, sizeof(vsn)); /* Nope */ (void) strcpy(dms, "Unknown"); } else { /* Found RS */ *pchar = '\0'; /* Terminate VSN string in bkstat.Label */ (void) strncpy(vsn, bkstat.Label, sizeof(vsn)); /* Copy VSN */ (void) strncpy(dms, pchar+1, sizeof(dms)); /* Copy DMS */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s getLabel() DEBUG: VSN %s, DMS %s \n", me, vsn, dms); return(0); /* Normal end */ } /* End of getLabel() */ static int setLabel() { /* Set Label from VSN and DMS (active bank only) */ char label[XLR_LABEL_LENGTH]; /* Temporary copy */ (void) strncpy(label, vsn, sizeof(label)); /* Copy VSN */ (void) strncat(label, "\036", sizeof(label) - strlen(label)); /* Add RS */ (void) strncat(label, dms, sizeof(label) - strlen(label)); /* Copy DMS */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s setLabel() DEBUG: VSN %s, DMS %s \n" "%s setLabel() DEBUG: label %s \n", me, vsn, dms, me, label); /* If bank is write protected, then we need to temporarily disable * protection in order to XLRSetLabel(). Get bank status */ if (XLRGetBankStatus(dev, bank, &bkstat) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s setLabel() ERROR: XLRGetBankStatus() returned error \n", me); (void) errors(); return(-1); /* Error */ } if (bkstat.WriteProtected && /* Protected? */ XLRClearWriteProtect(dev) != XLR_SUCCESS) { /* Clear protect, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s setLabel() ERROR: XLRClearWriteProtect() returned error \n", me); (void) errors(); return(-2); /* Error */ } /* Else OK, write new label */ if (XLRSetLabel(dev, label, strlen(label)) != XLR_SUCCESS) { /* Write OK? */ (void) errors(); /* Nope */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s setLabel() ERROR: XLRSetLabel() returned error \n", me); return(-3); /* Error */ } /* Restore protection if need be */ if (bkstat.WriteProtected && /* Was protected? */ XLRSetWriteProtect(dev) != XLR_SUCCESS) { /* Yes, set protect, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Protect() ERROR: XLRSetWriteProtect() returned error \n", me); (void) errors(); return(-4); /* Error */ } return(0); /* Else normal end */ } /* End of setLabel() */ static int FFH() { /* Find and decode Mark-5B frame headers */ int i, ii, jj; unsigned long k, kk, kkk; unsigned char idata[8]; /* For crcc() */ unsigned char * iref; /* * Search through data array * */ for (i = 0; i < MAXWD; i++) { /* Each long word */ if (pdata[i] != SYNC) { /* Found Sync? */ // (void) Tvg(i); /* No, check for TVG pattern */ continue; /* Then to next word */ } /* * Here we seem to have found Sync, but check CRCC to be sure * * * Calculate VLBA CRC-16 over 48 bits of time code (only) */ iref = (unsigned char *) (pdata + i + 2); /* Start of time code */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, "%s FFH() DEBUG iref[] ", me); /* Yes */ for (ii = 0; ii < 8; ii++) /* Each iref[] byte */ (void) fprintf(stderr, "%02x", iref[ii]); (void) fprintf(stderr, " \n"); } /* End of if debuggery */ for (ii = 0; ii < 8; ii++) /* Each byte of output */ idata[ii] = 0; /* Initialize output */ /* Note that we invert the byte order but not the bit order */ for (ii = 0; ii < 8; ii++) { /* Each byte of words 2 and 3 */ /* (Now ii is the input index, 8 bytes, but 2 are skipped, to give * the 6 bytes or 48 bits of time code to put into idata[]) */ if (ii < 4) /* Word 2 (input bytes 0--3)? */ idata[3 - ii] = iref[ii]; /* Yes */ else if (ii > 5) /* Word 3 (input bytes 6--7)? */ idata[11 - ii] = iref[ii]; /* Yes */ /* Else skip input bytes 4--5 */ } /* End of for ii each byte of words 2 and 3 */ k = crcc(idata, 48, 040003, 16); /* Calculated CRCC from idata[] */ kk = pdata[i + 3] & 0xffff; /* Raw CRCC from header */ /* Calculate the alternative bit-reversed CRCC * (cf., Parse5A.c, lines 5890--5900) */ kkk = 0; /* Initialize */ for (jj = 0; jj < 8; jj++) { /* Each bit in a byte */ if (iref[4] & 1 << jj) /* Set? */ kkk |= 1 << 15 - jj; /* Yes, set corresponding bit in kkk */ if (iref[5] & 1 << jj) /* Set? */ kkk |= 1 << 7 - jj; /* Yes, set corresponding bit in kkk */ } /* End of for jj each bit in a byte */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFH() DEBUG: Words 0--3: %#lx %#lx %#lx %#lx \n", me, pdata[i], pdata[i + 1], pdata[i + 2], pdata[i + 3]); (void) fprintf(stderr, "%s FFH() DEBUG: iref[5] %#x, iref[4] %#x \n", me, iref[5], iref[4]); (void) fprintf(stderr, "%s FFH() DEBUG: k %#lx, kk %#lx, kkk %#lx, foff %Lu, i %d \n", me, k, kk, kkk, foff, i); } /* End of if debuggery */ /* Compare calculated CRCC with alternative CRCC from data */ if (k != kkk) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFH() WARNING: NOT header: %#lx %#lx %#lx %#lx \n", me, pdata[i], pdata[i + 1], pdata[i + 2], pdata[i + 3]); continue; /* Try next sync */ } /* Else OK * * * Here we have found a frame header; return answers * * * (See page 1 of memo cited and notes above) */ header.userspec = (pdata[i + 1] & 0xffff0000) >> 16; header.t = (pdata[i + 1] & 0x8000) >> 15; header.frameno = pdata[i + 1] & 0x7fff; (void) snprintf(header.tcword1, 9, "%8.8lx", pdata[i + 2]); (void) snprintf(header.tcword2, 5, "%4.4lx", (pdata[i + 3] & 0xffff0000) >> 16); header.crcc = kk; foff += i; /* Bump foff (longs) to where we found sync */ if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, "%s FFH() DEBUG: " /* Yes */ " UserSpec %#x, T %d, frame# %u, TCW %s.%s, CRCC %#x \n", me, header.userspec, header.t, header.frameno, header.tcword1, header.tcword2, header.crcc); return(0); /* Normal end */ } /* End of for i through data array */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, "%s FFH() DEBUG: No sync found \n", me); /* Yes */ return(-1); /* Error, no sync found */ } /* End of FFH() */ /* *** Dismount() and Mount() are called from Reset(), Dplay() from Play(), *** and changeBank() from Bank_select() (all below) *** */ static void * Dismount(void * null) { /* From reset = dismount (below) */ ldir.delayed = TRUE; /* Delayed completion */ ldir.mounted = FALSE; /* Disks are being dismounted */ /* An XLRReset() should allow swapping disks */ if (XLRReset(dev) != XLR_SUCCESS) { /* Reset, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dismount() ERROR: \007 XLRReset() returned error \n", me); (void) errors(); ldir.delayed = FALSE; /* Not done, but this allows a retry */ return(NULL); /* Error (and not done) */ } XLRClose(dev); /* Try an even stronger reset */ if (XLRCardReset(indx) != XLR_SUCCESS) { /* Card reset, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, "%s Dismount() ERROR: \007 XLRCardReset() returned error \n", me); (void) errors(); ldir.delayed = FALSE; /* Not done, but this allows a retry */ return(NULL); /* Error (and not done) */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dismount() DEBUG: Reset and CardReset OK; dismount done \n", me); ldir.delayed = FALSE; /* Done */ return(NULL); /* Normal end */ } /* End of Dismount() */ static void * Mount(void * null) { /* From reset = mount (below) */ int k; ldir.delayed = TRUE; /* Delayed completion */ /* Flush any pending errors (Where did they come from?) */ (void) XLRGetLastError(); error = 0; /* Do another reset. Is this needed?? */ if (XLRCardReset(indx) != XLR_SUCCESS) { /* Card reset, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, "%s Mount() ERROR: \007 XLRCardReset() returned error \n", me); (void) errors(); ldir.delayed = FALSE; /* Not done, but this allows a retry */ return(NULL); /* Error (and not done) */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Mount() DEBUG: XLRCardReset() OK \n", me); sleep(2); /* Wait a couple seconds */ /* Reopen and reconfigure */ if (XLROpen(indx, &dev) != XLR_SUCCESS) { /* Yes, reopen OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Mount() ERROR: \007 XLROpen() returned error \n", me); (void) errors(); ldir.delayed = FALSE; /* Not done, but this allows a retry */ return(NULL); /* Error (and not done) */ } (void) errors(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Mount() DEBUG: XLROpen() OK \n", me); if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Mount() ERROR: \007 XLRGetDeviceStatus() returned error \n", me); (void) errors(); ldir.delayed = FALSE; /* Not done, but this allows a retry */ return(NULL); /* Error (and not done) */ } (void) errors(); if ((k = checkDisks()) < 0) { /* Check disks, OK? */ /* (This also sets dinfo[]) */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Mount() ERROR: \007 checkDisks() returned error %d \n", me, k); (void) errors(); ldir.delayed = FALSE; /* Not done, but this allows a retry */ return(NULL); /* Error (and not done) */ } if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* Default mode, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Mount() ERROR: \007 XLRSetMode() returned error \n", me); (void) errors(); ldir.delayed = FALSE; /* Not done, but this allows a retry */ return(NULL); /* Error (and not done) */ } if (XLRSetFPDPMode(dev, SS_FPDP_RECVMASTER, SS_OPT_FPDPNRASSERT) != XLR_SUCCESS) { /* Default FPDP mode (or SS_OPT_FPDPALL) OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Mount() ERROR: \007 XLRSetFPDPMode() returned error \n", me); (void) errors(); ldir.delayed = FALSE; /* Not done, but this allows a retry */ return(NULL); /* Error (and not done) */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s Mount() DEBUG: Set modes OK \n", me); /* Yes */ (void) readdir(); /* Try to get a directory from the new disk(s) */ drive = 0; /* Reset drive number for get_stats */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s Mount() DEBUG: Mount done \n", me); /* Yes */ ldir.mounted = TRUE; /* Disks are now mounted */ ldir.delayed = FALSE; /* Done */ return(NULL); /* Normal end */ } /* End of Mount() */ static void * Dplay(void * null) { /* Delayed play */ struct timeval tv; /* For gettimeofday() */ double nrot; /* ROT now */ double dtt; /* Wait time, seconds */ struct timespec wait; /* Wait time */ int k; char osm[32]; char * pmode, * posm; ldir.delayed = TRUE; /* Delayed completion */ /* Setting the SS mode for playing, setting the FPDP mode for * playing, setting the arm-to-fill-buffer-to-trigger play option, * and starting filling the play buffer were all moved here * from Play() because these delay a couple seconds. And Dplay() * is now also called from play=arm with armOnly TRUE */ /* Set SS mode for playing */ if (XLRSetMode(dev, SS_MODE_EXT) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() ERROR: \007 XLRSetMode() returned error \n", me); (void) errors(); /* Error, and we're not done, but there's no way to signal an error */ ldir.delayed = FALSE; return(NULL); /* Error */ } (void) errors(); if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() DEBUG: SS mode set for playing \n", me); /* Set FPDP mode for playing * * SS_FPDP_XMIT = transmit (does not drive clock), * SS_FPDP_XMITMASTER = normal transmit (with clock) */ if (XLRSetFPDPMode(dev, found == 2 ? SS_FPDP_XMITMASTER : SS_FPDP_XMIT, 0) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() ERROR: \007 XLRSetFPDPMode() returned error \n", me); (void) errors(); /* Error, and we're not done, but there's no way to signal an error */ ldir.delayed = FALSE; return(NULL); /* Error */ } (void) errors(); if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() DEBUG: FPDP mode set for playing \n", me); /* Set the arm-to-fill-buffer-to-trigger play option */ if (XLRSetOption(dev, SS_OPT_PLAYARM) != XLR_SUCCESS) { /* Arm, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() ERROR: \007 XLRSetOption() returned error \n", me); (void) errors(); /* Error, and we're not done, but there's no way to signal an error */ ldir.delayed = FALSE; return(NULL); /* Error */ } (void) errors(); if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() DEBUG: Option play arm set \n", me); /* Start filling play buffer (readdesc.AddrHi = plapnt[1] and * readdesc.AddrLo = plapnt[0] were set in Play()) */ if (XLRPlayback(dev, readdesc.AddrHi, readdesc.AddrLo) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() ERROR: \007 XLRPlayback() returned error \n", me); (void) errors(); /* Error, and we're not done, but there's no way to signal an error */ ldir.delayed = FALSE; return(NULL); /* Error */ } else if (msglev < 1) { /* Debuggery? */ (void) errors(); /* Yes */ (void) fprintf(stderr, "%s Dplay() DEBUG: Play buffer filling \n", me); } /* Check: In case called from play=arm (arm only), then end here */ if (armOnly) { /* Arm only? */ ldir.delayed = FALSE; /* Yes */ return(NULL); /* Normal end for this case */ } /* Else now what time is it? */ if ((k = gettimeofday(&tv, NULL)) < 0) { /* Errors? */ if (msglev < 2) /* Yes, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() ERROR: \007 gettimeofday() %d ", me, k); perror("error"); ldir.delayed = FALSE; /* But we're not done */ return(NULL); /* Error */ } nrot = rot + rot_rate*(tv.tv_sec - tvROT.tv_sec) + /* Rot now */ rot_rate*(tv.tv_usec - tvROT.tv_usec)/1.0e6; /* (rot is the ROT at time tvROT, * rot_rate is 32.0e6 * speed_up from getrot()) */ dtt = (srot - nrot)/rot_rate; /* How long to wait, seconds */ if (dtt >= 0.1) { /* Check: Must wait at least 0.1 second */ wait.tv_sec = (time_t) dtt; /* OK, integer seconds */ wait.tv_nsec = (long) ((dtt - wait.tv_sec)*1.0e9); /* Plus nanoseconds */ (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); /* Allow cancellation (interrupt) during following nanosleep */ pthread_testcancel(); (void) nanosleep(&wait, NULL); /* Wait here until srot */ pthread_testcancel(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() DEBUG: Return from nanosleep() \n", me); } /* End of if wait for at least 0.1 second */ else /* Error: No wait possible */ if (msglev < 2) /* Debuggery */ (void) fprintf(stderr, /* Yes */ "%s Dplay() ERROR: \007 No wait possible, dtt %.3f \n", me, dtt); if (XLRPlayTrigger(dev) != XLR_SUCCESS) { /* Trigger playing, OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Dplay() ERROR: \007 XLRPlayTrigger() returned error \n", me); (void) errors(); /* We try to go on */ } (void) errors(); /* The following sequence waits a few milliseconds for the scan * to begin playing, then checks to see whether the output section * of the I/O board has synced, and, if not, then re-sends the * CQ sync sequence */ wait.tv_sec = 0; /* Yes, wait some fraction of a second */ wait.tv_nsec = 10000000; /* (That's 10 milliseconds) */ pthread_testcancel(); (void) nanosleep(&wait, NULL); /* Wait here */ pthread_testcancel(); (void) outBoard(GET); /* Then get the output-board parameters */ /* Now outb.mode is vlbi, st, or tvr, * and outb.tmode is 8, 16, 32, 64, or 0 if unknown, * outb.formt is mark4, vlba, or null if unknown, * all as read from the output board */ if (msglev < 1) { /* Debuggery? */ if (strcasecmp(outb.mode, "vlbi") == 0) { /* Yes. VLBI? */ (void) sprintf(osm, "%d", outb.tmode); /* Yes, show tmode */ posm = osm; pmode = outb.formt; /* And this mode = format */ } else if (strcasecmp(outb.mode, "st") == 0) { /* ST? */ posm = outb.formt; /* Yes, show formt */ pmode = outb.mode; /* And this mode */ } else { /* TVR or unknown */ posm = ""; pmode = outb.mode; /* And this mode */ } (void) fprintf(stderr, /* Then debug print output-board status */ "%s Dplay() DEBUG: I/O status %s %s %c %d \n", me, pmode, posm, outb.synced ? 'S' : '-', outb.numrs); } /* End of if debuggery */ /* Now here is the ugly piece of code: If not synced at this time, * then re-send the same I/O board parameters, which now gives * another CQ sequence */ if (! outb.synced) /* Synced? */ (void) outBoard(SET); /* Not yet, try again */ ldir.doOff = TRUE; /* Now OK to offPlay() if halted */ ldir.delayed = FALSE; /* Done */ return(NULL); /* Normal end */ } /* End of Dplay() */ static void * changeBank(void * null) { /* Change to other bank */ int k; ldir.delayed = TRUE; /* Delayed completion */ ldir.mounted = FALSE; /* Not mounted during transition */ /* Select other bank */ if (XLRSelectBank(dev, bank) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s changeBank() ERROR: \007 XLRSelectBank() returned error \n", me); (void) errors(); bank = banko; /* Error, so we (probably) didn't change banks */ (void) strcpy(messg, "Bank change failed"); error = 1006; /* Failed attempt to change banks */ ldir.mounted = TRUE; /* ?? */ ldir.delayed = FALSE; /* (There is no way to return an error code) */ return(NULL); /* Error */ } (void) errors(); /* Else OK (so far) */ if (msglev < 1) { /* Debug check status of this bank? */ if (XLRGetBankStatus(dev, bank, &bkstat) != XLR_SUCCESS) { /* OK? */ (void) fprintf(stderr, /* Nope */ "%s changeBank() ERROR: XLRGetBankStatus() returned error \n", me); (void) errors(); /* Error, but we try to go on */ } else { /* Debug and OK status info */ (void) errors(); (void) fprintf(stderr, "%s changeBank() DEBUG: Bank %c, Label %s, \n", me, bank == 0 ? 'A' : 'B', bkstat.Label); (void) fprintf(stderr, " Length %Ld,", bkstat.Length); if (bkstat.State == STATE_NOT_READY) /* State? */ (void) fprintf(stderr, " STATE_NOT_READY,"); else if (bkstat.State == STATE_TRANSITION) (void) fprintf(stderr, " STATE_TRANSITION,"); else if (bkstat.State == STATE_READY) (void) fprintf(stderr, " STATE_READY,"); if (! bkstat.Selected) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " Selected, \n "); if (! bkstat.PowerRequested) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " PowerRequested,"); if (! bkstat.PowerEnabled) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " PowerEnabled, \n "); if (bkstat.MediaStatus == MEDIASTATUS_EMPTY) /* MediaStatus */ (void) fprintf(stderr, " MEDIASTATUS_EMPTY,"); else if (bkstat.MediaStatus == MEDIASTATUS_NOT_EMPTY) (void) fprintf(stderr, " MEDIASTATUS_NOT_EMPTY,"); else if (bkstat.MediaStatus == MEDIASTATUS_FULL) (void) fprintf(stderr, " MEDIASTATUS_FULL,"); else if (bkstat.MediaStatus == MEDIASTATUS_FAULTED) (void) fprintf(stderr, " MEDIASTATUS_FAULTED,"); if (! bkstat.WriteProtected) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " WriteProtected,"); if (! bkstat.ErrorCode) /* Error? */ (void) fprintf(stderr, " NO error \n"); else /* Error */ (void) fprintf(stderr, " Error %d %d \n", bkstat.ErrorCode, bkstat.ErrorData); (void) fprintf(stderr, " TotalCapacity %Ld \n", bkstat.TotalCapacity * 4096LL); } /* End of else OK status info */ } /* End of if debuggery */ /* Change everything over to the new bank */ bank += 100; /* To trigger CheckBanks() */ if ((k = CheckBanks()) < 0 && /* Change over to the new bank, OK? */ msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s changeBank() ERROR: CheckBanks() returned error %d \n", me, k); /* (There is no way to return an error code) */ (void) errors(); sdir->n = 0; /* Reset pointer */ sdir->recpnt = XLRGetLength(dev); /* Update record offset, bytes */ (void) errors(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s changeBank() DEBUG: Normal end \n", me); ldir.mounted = TRUE; /* Presume new bank is OK */ ldir.delayed = FALSE; /* Done */ return(NULL); /* Normal end */ } /* End of changeBank() */ /* ** Most of the following functions are called only by Parse5A() below ** * With a few exceptions, each function has the same name as the * corresponding keyword except that the function names begin with * capital letters */ static char * Reset() { /* reset */ static pthread_t thread; /* One extra thread at a time */ static long long zero = 0LL; unsigned long plapnt[2]; /* Convert to/from long long */ long long * pplapnt = (long long *) plapnt; int k; /* * There is no reset query * */ if (pt != NULL) /* Was it a query? */ return("!reset? 2 : No such query ;"); /* Error */ /* * If mounted, then get updated device status to check * */ if (ldir.mounted && /* Mounted? */ XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status, OK? */ (void) errors(); /* Nope, error in reading status */ return("!reset = 4 : Can't get device status ;"); /* Error */ } (void) errors(); /* * One required argument: abort, dismount, mount, erase, * or erase_last_scan * */ if (ptr[1] == NULL) /* No argument is an error */ return("!reset = 8 : Must have argument ;"); /* Parameter eror */ /* * First check for abort * */ if (strcasecmp(ptr[1], "abort") == 0) { /* Abort (something)? */ if (datah > 0 && datah != 5 && nowbyte < endbyte) { /* Yes, end other transfer? */ nowbyte = endbyte; /* Yes */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() DEBUG: Ending transfer, datah %d \n", me, datah); return("!reset = 0 ;"); /* Abort OK */ } else /* Nothing to abort */ return("!reset = 6 : Functions to abort are not active ;"); } /* * Else not abort. Check: Data transfer in progress? * */ if (datah > 0) /* OK? */ return("!reset = 6 : Can't reset while data transfer in progress ;"); /* * Check: Can't reset while recording or playing * */ if (devstatus.Recording || /* Recording? */ devstatus.Playing) /* Or playing? */ /* (Formerly had "playing" also) */ return("!reset = 6 : Can't reset while recording or playing ;"); /* * Check: Previous command required to be protect=off * */ if (prot != 1) /* OK? */ return("!reset = 6 : Previous protect=off required ;"); /* Nope, error */ /* * Else OK * */ ldir.recording = ldir.scanplaying = ldir.playing = FALSE; /* * Dismount? * */ if (strcasecmp(ptr[1], "dismount") == 0) { if (ldir.bmode) /* Yes, bank mode? */ return("!reset = 6 : Can't dismount in bank mode ;"); /* Error */ if (datah > 0) /* Data transfer in progress? */ return("!reset = 6 : Can't reset while data transfer in progress ;"); ldir.mounted = FALSE; /* Else OK */ ldir.delayed = TRUE; (void) pthread_create(&thread, NULL, Dismount, NULL); (void) pthread_detach(thread); return("!reset = 1 ;"); /* Delayed completion, normal for dismount */ } /* * Mount? * */ if (strcasecmp(ptr[1], "mount") == 0) { if (ldir.bmode) /* Yes, bank mode? */ return("!reset = 6 : Can't mount in bank mode ;"); /* Error */ if (datah > 0) /* Data transfer in progress? */ return("!reset = 6 : Can't reset while data transfer in progress ;"); (void) pthread_create(&thread, NULL, Mount, NULL); /* Else OK */ (void) pthread_detach(thread); return("!reset = 1 ;"); /* Delayed completion, normal for mount */ } /* * Erase? * */ if (strcasecmp(ptr[1], "erase") == 0) { /* * Yes. Instead of XLRRecord() and XLRStop(), we now do XLRErase() * */ if (XLRErase(dev, SS_OVERWRITE_NONE) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() ERROR: XLRErase() returned error \n", me); (void) errors(); return("!reset = 4 : Can't SS erase ;"); /* Error */ } /* Else presumably OK */ (void) errors(); /* * Set DMS to "Erased" * */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Erased", then skip setLabel() */ if (strcasecmp(dms, "Erased") != 0 && /* Not already Erased? */ ldir.dsmask & 0x1) { /* And allowed change to Erased? */ (void) strcpy(dms, "Erased"); /* OK, set DMS to Erased */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } /* * Now also clear the user directory * */ /* (Surely this is not necessary, what with writedir() below) ?? */ if (XLRSetUserDir(dev, (void *) &zero, 8) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() ERROR: XLRSetUserDir() returned error \n", me); (void) errors(); return("!reset = 4 : Can't set user directory ;"); /* Error */ } else /* OK */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() DEBUG: XLRSetUserDir() OK \n", me); (void) errors(); /* * Default mode * */ if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() ERROR: XLRSetMode() returned error \n", me); (void) errors(); return("!reset = 4 : Can't set SS mode ;"); /* Error */ } /* * Else OK, reset scans * */ (void) errors(); /* Clear sdir (part of udir) */ (void) memset(sdir, 0, sizeof(struct SDir)); readdesc.AddrHi = 0; readdesc.AddrLo = 0; readdesc.XferLength = 0; (void) writedir(); /* Save a copy of sdir in file DIR and on SS disks */ return("!reset = 0 ;"); /* Normal end for erase */ } /* * Kill last scan? * */ if (strcasecmp(ptr[1], "erase_last_scan") == 0) { if (sdir->nscans <= 0) /* Yes. Scans? */ return("!reset = 6 : No scan to erase ;"); /* Nope, error */ /* * Else OK * */ *pplapnt = sdir->start[sdir->nscans - 1]; /* Start of last scan, bytes */ if (XLRTruncate(dev, plapnt[1], plapnt[0]) != XLR_SUCCESS) { /* OK? */ if (msglev < 3) /* No, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() ERROR: XLRTruncate() returned error \n", me); (void) errors(); return("!reset = 4 : Can't truncate scan ;"); /* Error */ } /* * Reset directory entry, and save revised directory * */ sdir->nscans -= 1; /* Now one less scan */ sdir->start[sdir->nscans] = 0L; /* Clear this entry */ sdir->length[sdir->nscans] = 0L; sdir->scanName[sdir->nscans][0] = '\0'; sdir->n = MAX(0, sdir->nscans - 1); /* Set the scan pointer to the last-recorded scan */ startbyte = sdir->plapnt = sdir->start[sdir->n]; /* Start of scan */ endbyte = sdir->start[sdir->n] + sdir->length[sdir->n]; /* End of scan */ sdir->recpnt = XLRGetLength(dev); /* Record offset */ /* (endbyte and sdir->recpnt and pplapnt should all be the same) */ (void) errors(); /* * Save a copy of sdir in file DIR and on SS disks * */ (void) writedir(); /* (This might give two copies of the same directory, but that's OK) */ /* * Set DMS to "Unknown" * */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Unknown", then skip setLabel() */ if (strcasecmp(dms, "Unknown") != 0 && /* Not already Unknown? */ ldir.dsmask & 0x1) { /* And allowed change to Unknown (= Erased)? */ (void) strcpy(dms, "Unknown"); /* OK, set DMS to Unknown */ if ((k = setLabel()) < 0) { /* And write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Reset() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } return("!reset = 0 ;"); /* Normal end for erase last scan */ } /* * Else none of the above is an error * */ return("!reset = 8 : Must be erase, erase_last_scan, or abort ;"); /* Error */ } /* End of Reset() */ static char * Recover() { /* recover */ /* Use Conduant's XLRRecoverData() to recover * recorded data lost but not overwritten. */ UINT Rmode = 0; /* Data Recovery modes: * SS_RECOVER_POWERFAIL 0 * SS_RECOVER_OVERWRITE 1 * SS_RECOVER_UNERASE 2 */ int i, j; char line[MAXLENGTH + 2]; /* Recover can not be a query, check that first */ if (pt != NULL) { /* Was it a query? */ (void) sprintf(outstring, "!%s? 2 : No such query ;", ptr[0]); /* Yes */ return(outstring); /* Error */ } /* * Else not a query. Check that nothing else is going on * */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ (void) sprintf(outstring, /* Nope */ "!%s = 6 : Not while other activity ;", ptr[0]); return(outstring); /* Error, must wait for other activity to finish */ } if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Recover() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't get device status ;", ptr[0]); return(outstring); /* XLR error */ } if (devstatus.Recording || devstatus.Playing) { /* Recording or Playing? */ (void) sprintf(outstring, /* Yes */ "!%s = 6 : Not while recording or playing ;", ptr[0]); return(outstring); /* Error, must wait for other activity to finish */ } /* * Else OK. One optional argument to be 0, 1, or 2 * */ if (ptr[1] != NULL && ptr[1][0] != '\0' && /* Got? */ (Rmode = atol(ptr[1])) > 2) { /* And, OK? */ (void) sprintf(outstring, /* Nope, error */ "!%s = 8 : Illegal mode %u ;", ptr[0], Rmode); return(outstring); /* Error */ } /* (Else keep default above) */ /* * Try to recover data * */ if (XLRRecoverData(dev, Rmode) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Recover() ERROR: \007 XLRRecoverData() returned error \n", me); (void) errors(); (void) sprintf(outstring, /* Error */ "!%s = 4 : Can't XLRRecoverData(%d) ;", ptr[0], Rmode); return(outstring); /* Error */ } (void) errors(); /* Else OK */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Recover() DEBUG: XLRRecoverData() OK \n", me); if (Rmode == 0) { /* Try to fix directory? */ sdir->recpnt = XLRGetLength(dev); /* Yes, record offset, bytes */ if ((error = XLRGetLastError()) != 3) { /* Errors? */ (void) errors(); /* Yes */ (void) sprintf(outstring, /* Error */ "!%s = 4 : Can't XLRGetLength() ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); /* Else OK. Set end of last scan to recpnt? * (We presume that most of sdir is correct for the last scan) */ if (sdir->start[sdir->n] + sdir->length[sdir->n] < sdir->recpnt) /* OK? */ sdir->length[sdir->n] = sdir->recpnt - sdir->start[sdir->n]; /* Yes */ /* Should we delete '+' from scanName[] of last scan? * We presume that the first scan on a pack might have a real '+' */ if (sdir->n > 0) { /* First scan? */ j = 0; /* Not first scan */ for (i = 0; i < MAXLENGTH; i++) { /* Each char in scanName[] */ if (sdir->scanName[sdir->n][i] == '\0') /* End? */ break; /* Yes, out of for i */ if (sdir->scanName[sdir->n][i] == '+') /* Plus? */ continue; /* Yes, to next i (omit '+') */ /* Else keep this char in line[] */ line[j] = sdir->scanName[sdir->n][i]; j++; /* For next loop */ } /* End of for i each char in scanName[] */ line[j] = '\0'; /* End of string */ (void) strncpy(sdir->scanName[sdir->n], line, MAXLENGTH); /* Copy line[] (without '+') back into scanName[] */ } /* End of if not first scan */ (void) writedir(); /* Save a copy of sdir in file DIR and on SS disks */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Recover() DEBUG: n %d, recpnt %Ld, length %Ld \n", me, sdir->n, sdir->recpnt, sdir->length[sdir->n]); } /* End of if Rmode, try to fix directory */ /* Presumably OK */ (void) sprintf(outstring, "!%s = 0 ;", ptr[0]); return(outstring); /* Normal end */ } /* End of Recover() */ static char * Bank_mode() { /* bank_mode or bank_info */ /* (Bank_info is active; bank_mode is no longer in use) */ S_BANKSTATUS lbkstat[2]; /* Local version of bkstat */ UINT obank; /* Other (not-selected) bank */ int i, k; /* Bank_info can be a query, check that first */ if (pt != NULL) { /* Was it a query? */ if (! ldir.bmode) { /* Yes, are we in bank mode? */ (void) sprintf(outstring, "!%s? 6 : Not in bank mode ;", ptr[0]); return(outstring); /* Error */ } for (i = 0; i < 2; i++) { /* Else OK, each bank */ if (XLRGetBankStatus(dev, i, lbkstat+i) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, "%s Bank_mode() ERROR: " /* Yes */ " XLRGetBankStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s? 4 : Can't get bank status ;", ptr[0]); /* XLR error */ return(outstring); /* Error */ } (void) errors(); /* Else OK, got bank-status info */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Bank_mode() DEBUG: Bank %c, Label %s, \n", me, bk[i], lbkstat[i].Label); (void) fprintf(stderr, " Length %Ld,", lbkstat[i].Length); if (lbkstat[i].State == STATE_NOT_READY) /* State? */ (void) fprintf(stderr, " STATE_NOT_READY,"); else if (lbkstat[i].State == STATE_TRANSITION) (void) fprintf(stderr, " STATE_TRANSITION,"); else if (lbkstat[i].State == STATE_READY) (void) fprintf(stderr, " STATE_READY,"); if (! lbkstat[i].Selected) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " Selected, \n "); if (! lbkstat[i].PowerRequested) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " PowerRequested,"); if (! lbkstat[i].PowerEnabled) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " PowerEnabled, \n "); if (lbkstat[i].MediaStatus == MEDIASTATUS_EMPTY) /* MediaStatus */ (void) fprintf(stderr, " MEDIASTATUS_EMPTY,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_NOT_EMPTY) (void) fprintf(stderr, " MEDIASTATUS_NOT_EMPTY,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_FULL) (void) fprintf(stderr, " MEDIASTATUS_FULL,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_FAULTED) (void) fprintf(stderr, " MEDIASTATUS_FAULTED,"); if (! lbkstat[i].WriteProtected) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " WriteProtected,"); if (! lbkstat[i].ErrorCode) /* Error? */ (void) fprintf(stderr, " NO error \n"); else /* Error */ (void) fprintf(stderr, " Error %d %d \n", lbkstat[i].ErrorCode, lbkstat[i].ErrorData); (void) fprintf(stderr, " TotalCapacity %Ld \n", lbkstat[i].TotalCapacity * 4096LL); } /* End of if debuggery */ } /* End of for i each bank */ obank = (bank + 1) % 2; /* Other (not selected) bank */ (void) sprintf(outstring, "!%s? 0 : %c : %Ld : %c : %Ld ;", ptr[0], lbkstat[bank].Selected ? bk[bank] : bk[3], /* Selected? */ /* (bank not selected is an error) */ lbkstat[bank].State == STATE_READY && /* Ready? */ lbkstat[bank].MediaStatus != MEDIASTATUS_FAULTED && /* Not faulted? */ lbkstat[bank].MediaStatus != MEDIASTATUS_FULL && /* Not full? */ ! lbkstat[bank].WriteProtected && /* Not protected? */ ! lbkstat[bank].ErrorCode /* No error? */ ? lbkstat[bank].TotalCapacity * 4096LL - lbkstat[bank].Length /* Then space left to be recorded, bytes */ : 0LL, /* Else 0 */ lbkstat[obank].Selected ? bk[3] : /* Selected? */ /* (obank selected is an error) */ (lbkstat[obank].State == STATE_READY ? bk[obank] : bk[2]), /* Ready? */ lbkstat[obank].State == STATE_READY && /* Ready? */ lbkstat[obank].MediaStatus != MEDIASTATUS_FAULTED && /* Not faulted? */ lbkstat[obank].MediaStatus != MEDIASTATUS_FULL && /* Not full? */ ! lbkstat[obank].WriteProtected && /* Not protected? */ ! lbkstat[obank].ErrorCode /* No error? */ ? lbkstat[obank].TotalCapacity * 4096LL - lbkstat[obank].Length /* Then space left to be recorded, bytes */ : 0LL); /* Else 0 */ return(outstring); /* Normal end for query */ } /* Else not a query, one optional argument */ if (strcasecmp(ptr[1], "off") == 0) { /* Off? */ /* Yes, try to disable bank mode */ /* ?? This doesn't really seem to work ?? */ if (XLRSetBankMode(dev, SS_BANKMODE_DISABLED) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Bank_mode() ERROR: XLRSetBankMode() returned error \n", me); (void) errors(); ldir.bmode = FALSE; /* ?? */ (void) sprintf(outstring, "!%s = 4 : Can't disable bank mode ;", ptr[0]); /* XLR error */ return(outstring); /* Error */ } ldir.bmode = FALSE; /* Else OK */ } else if (ptr[1] == NULL || ptr[1][0] == '\0' || /* (Default to on) */ strcasecmp(ptr[1], "on") == 0) { /* On? */ /* Yes, try to enable bank mode */ if (XLRSetBankMode(dev, SS_BANKMODE_NORMAL) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Bank_mode() ERROR: XLRSetBankMode() returned error \n", me); (void) errors(); ldir.bmode = FALSE; /* ?? */ (void) sprintf(outstring, "!%s = 4 : Can't set bank mode ;", ptr[0]); /* XLR error */ return(outstring); /* Error */ } ldir.bmode = TRUE; /* Else OK */ /* Change everything over to the bank */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Bank_mode() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't get device status ;", ptr[0]); /* XLR error */ return(outstring); /* Error */ } (void) errors(); if ((k = checkDisks()) < 0) { /* Check disks, OK? */ /* (This also sets dinfo[]) */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Bank_mode() ERROR: checkDisks() returned error %d \n", me, k); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't checkDisks() ;", ptr[0]); /* Error */ return(outstring); /* Error */ } (void) readdir(); /* Try to get a directory from the new disk(s) */ drive = 0; /* Reset drive number for get_stats */ } /* End of else if on */ else { /* None of these above is an error */ (void) sprintf(outstring, "!%s = 8 : Must be off or on ;", ptr[0]); /* Error */ return(outstring); /* Error */ } (void) sprintf(outstring, "!%s = 0 ;", ptr[0]); /* Else OK */ return(outstring); /* Normal end */ } /* End of Bank_mode() */ static char * Bank_switch() { /* bank_switch */ S_BANKSTATUS lbkstat; /* Local version of bkstat */ UINT obank; /* Other (not-selected) bank */ /* Bank_switch can be a query, check that first */ if (pt != NULL) { /* Was it a query? */ /* (There seems to be no XLRGetBankMode()) */ (void) sprintf(outstring, "!bank_switch? 0 : %s ;", /* Yes */ ldir.abmode ? "on" : "off"); /* (We don't know about ) */ return(outstring); } /* Else not a query, one optional argument */ if (ptr[1] == NULL || ptr[1][0] == '\0' || /* (Default to off) */ strcasecmp(ptr[1], "off") == 0) { /* Off? */ /* Yes, disable automatic bank switching */ if (XLRSetBankMode(dev, SS_BANKMODE_NORMAL) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Bank_switch() ERROR: XLRSetBankMode() returned error \n", me); (void) errors(); (void) strcpy(messg, "Can't change auto-bank-switch mode"); error = 1019; ldir.abmode = FALSE; /* ?? */ return("!bank_switch = 4 : Can't set auto-bank-switch mode ;"); /* Error */ } ldir.abmode = FALSE; /* Else OK */ ldir.bmode = TRUE; /* End of if Off (etc.) */ } else if (strcasecmp(ptr[1], "on") == 0) { /* On? */ /* Yes, but check: Other bank must be ready and erased */ obank = (bank + 1) % 2; /* Other (not selected) bank */ /* Get bank status of other bank */ if (XLRGetBankStatus(dev, obank, &lbkstat) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Bank_switch() ERROR: " /* Yes */ " XLRGetBankStatus() error bank %c \n", me, bk[obank]); (void) errors(); (void) sprintf(outstring, "!%s? 4 : Bank %c : Can't get bank status ;", ptr[0], bk[obank]); return(outstring); /* Bank error */ } (void) errors(); /* Else OK, check other bank status */ if (lbkstat.State != STATE_READY || /* (Should be) Ready? */ lbkstat.Selected || /* But not selected? */ // lbkstat.MediaStatus != MEDIASTATUS_EMPTY || /* Empty? */ // lbkstat.WriteProtected || /* But not write protected? */ lbkstat.ErrorCode) { /* And no errors? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Bank_switch() ERROR: " /* Yes */ "bank %c must be erased, ready, and not write protected \n", me, bk[obank]); ldir.abmode = FALSE; /* (Presumably) */ return("!bank_switch = 4 : Other bank must be erased and ready ;"); /* Error */ } /* Else OK. Enable automatic bank switching, OK? */ if (XLRSetBankMode(dev, SS_BANKMODE_AUTO_ON_FULL) != XLR_SUCCESS) { if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Bank_switch() ERROR: XLRSetBankMode() returned error \n", me); (void) errors(); (void) strcpy(messg, "Can't change auto-bank-switch mode"); error = 1019; ldir.abmode = FALSE; /* ?? (Presumably) */ return("!bank_switch = 4 : Can't set auto-bank-switch mode ;"); /* Error */ } ldir.abmode = TRUE; /* Else OK */ } /* End of else if on */ else /* None of these above is an error */ return("!bank_switch = 8 : Must be off or on ;"); /* Error */ return("!bank_switch = 0 ;"); /* Normal end */ } /* End of Bank_switch() */ static char * Bank_select() { /* bank_select and bank_set */ static pthread_t thread = 0xffffffff; /* For changeBank() */ S_BANKSTATUS lbkstat[2]; /* Local version of bkstat */ int i; char * pchar; /* Bank_select or bank_set can be a query, check that first */ if (pt != NULL) { /* Was it a query? */ if (ldir.bmode) { /* Yes, are we in bank mode? */ for (i = 0; i < 2; i++) { /* Yes, each bank */ if (XLRGetBankStatus(dev, i, lbkstat+i) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Bank_select() ERROR: " /* Yes */ " XLRGetBankStatus() error bank %c \n", me, bk[i]); (void) errors(); (void) sprintf(outstring, "!%s? 4 : Bank %c : Can't get bank status ;", ptr[0], bk[i]); return(outstring); /* Bank error */ } (void) errors(); /* Else OK, got lbkstat[] */ if (msglev < 1) { /* Debuggery status of banks? */ (void) fprintf(stderr, /* Yes */ "%s Bank_select() DEBUG: Bank %c, Label %s, \n", me, bk[i], lbkstat[i].Label); (void) fprintf(stderr, " Length %Ld,", lbkstat[i].Length); if (lbkstat[i].State == STATE_NOT_READY) /* State? */ (void) fprintf(stderr, " STATE_NOT_READY,"); else if (lbkstat[i].State == STATE_TRANSITION) (void) fprintf(stderr, " STATE_TRANSITION,"); else if (lbkstat[i].State == STATE_READY) (void) fprintf(stderr, " STATE_READY,"); if (! lbkstat[i].Selected) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " Selected, \n "); if (! lbkstat[i].PowerRequested) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " PowerRequested,"); if (! lbkstat[i].PowerEnabled) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " PowerEnabled, \n "); if (lbkstat[i].MediaStatus == MEDIASTATUS_EMPTY) /* MediaStatus */ (void) fprintf(stderr, " MEDIASTATUS_EMPTY,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_NOT_EMPTY) (void) fprintf(stderr, " MEDIASTATUS_NOT_EMPTY,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_FULL) (void) fprintf(stderr, " MEDIASTATUS_FULL,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_FAULTED) (void) fprintf(stderr, " MEDIASTATUS_FAULTED,"); if (! lbkstat[i].WriteProtected) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " WriteProtected,"); if (! lbkstat[i].ErrorCode) /* Error? */ (void) fprintf(stderr, " NO error \n"); else /* Bank error */ (void) fprintf(stderr, " Error %d %d \n", lbkstat[i].ErrorCode, lbkstat[i].ErrorData); (void) fprintf(stderr, " TotalCapacity %Ld \n", lbkstat[i].TotalCapacity * 4096LL); } /* End of if debuggery */ /* * Null out RS in Label, if any * */ if ((pchar = strchr(lbkstat[i].Label, '\036')) != NULL) /* RS? */ *pchar = '\0'; /* Yes, end string here */ } /* End of for i each bank */ if (! lbkstat[bank].Selected) /* OK? */ (void) sprintf(outstring, "!%s? 6 : Bank %c not selected ;", /* Nope */ ptr[0], bk[bank]); else { /* OK */ i = (bank + 1) % 2; /* Other (not selected) bank */ (void) sprintf(outstring, "!%s? 0 : %c : %s : %c : %s ;", ptr[0], bk[bank], lbkstat[bank].State == STATE_READY ? lbkstat[bank].Label : "", lbkstat[i].State == STATE_READY ? bk[i] : bk[2], lbkstat[i].State == STATE_READY ? lbkstat[i].Label : ""); } } /* End of if bank mode */ else /* Error, not in bank mode */ (void) sprintf(outstring, "!%s? 6 : Not in bank mode ;", ptr[0]); return(outstring); /* Normal end for query */ } /* Else not a query. But we need device status * to check for other data transfer in progress */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status, OK? */ (void) errors(); /* Nope, error in reading status */ (void) sprintf(outstring, "!%s = 4 : Can't get device status ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); /* Check: Other data transfer in progress? */ if (datah > 0 || devstatus.Recording || devstatus.Playing) { if (msglev < 2) /* Yes, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s BankSelect() ERROR: Data transfer in progress \n", me); (void) sprintf(outstring, "!%s = 6 : Data transfer in progress ;", ptr[0]); return(outstring); /* Error */ } /* Else OK */ banko = bank; /* Remember old bank */ /* One optional argument */ if (ptr[1] == NULL || strcasecmp(ptr[1], "A") == 0) /* Argument? */ bank = 0; /* (Default bank A) */ else if (strcasecmp(ptr[1], "B") == 0) bank = 1; /* Bank B */ else if (strcasecmp(ptr[1], "inc") == 0) /* Increment? */ bank = (bank + 1) % 2; /* (Or bank = 1 - bank) */ else { /* None of these above is an error */ (void) sprintf(outstring, "!%s = 8 : Must be A, B, or inc ;", ptr[0]); /* Unknown argument */ return(outstring); /* Error */ } if (banko != bank) { /* Bank changed? */ ldir.mounted = FALSE; /* Yes, changeBank() will cause dismount */ ldir.delayed = TRUE; /* (These should be set here, rather than in the new thread, * because there may be a delay before the changeBank() thread * gets started.) */ if (pthread_create(&thread, NULL, changeBank, NULL) != 0 && /* OK? */ msglev < 2) { /* And debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Bank_select() ERROR: pthread_create() returned ", me); perror("error"); /* Error, but we try to go on */ } (void) pthread_detach(thread); /* Thread not to hold memory */ (void) sprintf(outstring, "!%s = 1 ;", ptr[0]); /* Delayed completion */ } else /* Don't change bank */ (void) sprintf(outstring, "!%s = 0 : No change in bank ;", ptr[0]); return(outstring); /* Normal end */ } /* End of Bank_select() */ static char * Input_mode() { /* input_mode */ /* Input_mode can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ (void) inBoard(GET); /* Yes, read input board parameters */ if (strcasecmp(inb.mode, "vlbi") == 0 || /* VLBI mode? */ strcasecmp(inb.mode, "mark4") == 0 || strcasecmp(inb.mode, "vlba") == 0) (void) sprintf(outstring, "!input_mode? 0 : %s : %d ;", /* Yes */ inb.mode, inb.ntracks); else /* ST, test, TVG, or other mode */ (void) sprintf(outstring, "!input_mode? 0 : %s ;", inb.mode); return(outstring); } /* Input_mode has two optional arguments, first: vlbi, st, or test */ if (ptr[1] == NULL) { /* Argument? */ if (inb.errb) /* Nope, take defaults, formatter number OK? */ (void) strcpy(inb.mode, "st"); /* Nope, only ST in this case */ else /* OK to vlbi modes */ (void) strcpy(inb.mode, "mark4"); /* Copy to struct */ /* Note that the default depends on errb, that is the formatter serial */ inb.ntracks = 32; /* Default, might be changed below */ } else if (strcasecmp(ptr[1], "st") == 0 || strcasecmp(ptr[1], "test") == 0 || strcasecmp(ptr[1], "tvg") == 0 || strcasecmp(ptr[1], "vlbi") == 0 || strcasecmp(ptr[1], "mark4") == 0 || strcasecmp(ptr[1], "vlba") == 0) { /* OK? */ if (inb.errb && /* Yes, but for mark4 mode, errb must be FALSE */ strcasecmp(ptr[1], "mark4") == 0) /* OK? */ return("!input_mode = 8 : Check formatter serial number even ;"); /* Nope, error */ (void) strcpy(inb.mode, ptr[1]); /* Else OK, copy to struct */ } else if (ptr[1][0] != '\0') /* None of these is an error */ return("!input_mode = 8 : Unknown parameter ;"); /* Error */ /* Interpret the second argument depending on test or not */ if (ptr[1] != NULL && ptr[2] != NULL) { /* Second argument? */ if (strcasecmp(inb.mode, "vlbi") == 0 || strcasecmp(inb.mode, "mark4") == 0 || strcasecmp(inb.mode, "vlba") == 0) { /* VLBI modes? */ inb.ntracks = atoi(ptr[2]); /* Yes. Number of tracks */ if (inb.ntracks % 8 != 0 || inb.ntracks > 64 || inb.ntracks < 8) /* OK? */ return("!input_mode = 8 : Must be 8, 16, 32, or 64 ;"); /* Error */ } else /* Not VLBI */ inb.ntracks = 32; /* Ignore second argument, take default */ } (void) inBoard(SET); /* OK, send to hardware */ return("!input_mode = 0 ;"); /* Normal */ } /* End of Input_mode() */ static char * Mode() { /* Record and play mode */ char * pmode, * pdsm, * posm; char dsm[32], osm[32]; /* The current parameters are needed for either command or query */ (void) inBoard(GET); /* Get input-board parameters */ /* Now inb.mode has mark4, vlba, st, or tvg, * and inb.ntracks has 8, 16, 32, or 64 */ (void) outBoard(GET); /* And output-board parameters */ /* Now outb.mode is vlbi, st, or tvr, * and outb.tmode is 8, 16, 32, 64, or 0 if unknown, * outb.formt is mark4, vlba, or null if unknown, * all as read from the output board */ /* Mode can be a query, check that first */ if (pt != NULL) { /* Was it a query? */ if (strcasecmp(inb.mode, "mark4") == 0 || strcasecmp(inb.mode, "vlba") == 0) { /* VLBI? */ (void) sprintf(dsm, "%d", inb.ntracks); /* Yes, show ntracks */ pdsm = dsm; } else if (strcasecmp(inb.mode, "st") == 0) /* ST? */ pdsm = outb.formt; /* Yes, show output format */ else /* TVG or unknown */ pdsm = ""; if (strcasecmp(outb.mode, "vlbi") == 0) { /* VLBI? */ (void) sprintf(osm, "%d", outb.tmode); /* Yes, show tmode */ posm = osm; pmode = outb.formt; /* And this mode = format */ } else if (strcasecmp(outb.mode, "st") == 0) { /* ST? */ posm = outb.formt; /* Yes, show formt */ pmode = outb.mode; /* And this mode */ } else { /* TVR or unknown */ posm = ""; pmode = outb.mode; /* And this mode */ } (void) sprintf(outstring, "!mode? 0 : %s : %s : %s : %s : %c : %d ;", inb.mode, pdsm, pmode, posm, outb.synced ? 'S' : '-', outb.numrs); return(outstring); /* Normal end for query */ } /* Else not a query. Mode command has up to four optional arguments: * first mark4, vlba, st, or tvg, then number of tracks or ... */ if (ptr[1] == NULL || ptr[1][0] == '\0') { /* Argument? */ (void) strcpy(inb.mode, "st"); /* Nope, take input defaults */ (void) strcpy(outb.mode, "st"); /* Take output defaults */ /* (Other defaults set below) */ outb.formt[0] = '\0'; /* Default: Not VLBI */ /* (Some of these might be overwritten below) */ } else if (strcasecmp(ptr[1], "mark4") == 0 || strcasecmp(ptr[1], "vlba") == 0) { /* Mark4 or VLBA? */ if (inb.errb && strcasecmp(ptr[1], "mark4") == 0) /* Yes, errors? */ /* (For mark4 mode not st, errb must be FALSE) */ return("!mode = 6 : Check formatter serial number even ;"); /* Error */ (void) strcpy(inb.mode, ptr[1]); /* Else OK */ (void) strcpy(outb.mode, "vlbi"); (void) strcpy(outb.formt, ptr[1]); /* (Some of these might be overwritten below) */ } else if (strcasecmp(ptr[1], "st") == 0 || /* ST? */ strcasecmp(ptr[1], "test") == 0 || /* Test? */ strcasecmp(ptr[1], "tvg") == 0) { /* Or TVG? */ (void) strcpy(inb.mode, ptr[1]); (void) strcpy(outb.mode, ptr[1]); outb.formt[0] = '\0'; /* Default: Not VLBI */ } /* (Some of these might be overwritten below) */ else /* None of the above is an error */ return("!mode = 8 : Must be mark4, vlba, st, or tvg ;"); /* Error */ /* Else end of if (first) argument */ outb.tmode = 32; /* Defaults in case no second argument */ inb.ntracks = 32; if (ptr[1] != NULL && ptr[2] != NULL && ptr[2][0] != '\0') { /* Second argument? */ if (strspn(ptr[2], "0123456789.") == strlen(ptr[2])) { /* Yes, numeric? */ outb.tmode = atoi(ptr[2]); /* Yes, number of tracks */ if (outb.tmode % 8 != 0 || outb.tmode > 64 || outb.tmode < 8) /* OK? */ return("!mode = 8 : Must be 8, 16, 32, or 64 ;"); /* Nope, error */ inb.ntracks = outb.tmode; /* Else OK */ } else if (strcasecmp(inb.mode, "st") == 0) { /* Not numeric and ST? */ if (strcasecmp(ptr[2], "mark4") == 0 || strcasecmp(ptr[2], "vlba") == 0) /* OK? */ (void) strcpy(outb.formt, ptr[2]); /* Yes */ else /* Not OK */ return("!mode = 8 : Must be mark4 or vlba ;"); /* Error */ } else /* Not numeric and not ST, not OK */ return("!mode = 8 : Don't understand 2nd argument ;"); /* Error */ } /* End of if second argument */ (void) inBoard(SET); /* Send to input hardware */ /* Check for two extra arguments to override settings for output only */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[3][0] != '\0') { /* Third argument? */ if (strcasecmp(ptr[3], "mark4") == 0 || /* Yes */ strcasecmp(ptr[3], "vlba") == 0) { /* Mark4 or VLBA? */ (void) strcpy(outb.mode, "vlbi"); /* Yes */ (void) strcpy(outb.formt, ptr[3]); } else if (strcasecmp(ptr[3], "st") == 0) { /* ST? */ (void) strcpy(outb.mode, ptr[3]); /* Yes */ outb.formt[0] = '\0'; /* Default: Not VLBI */ } /* (But may be changed below) */ else /* None of the above is an error */ return("!mode = 8 : Must be mark4, vlba, or st ;"); /* Error */ } /* End of if third argument */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[4] != NULL && ptr[4][0] != '\0') { /* Fourth argument? */ if (strspn(ptr[4], "0123456789.") == strlen(ptr[4])) { /* Yes, numeric? */ outb.tmode = atoi(ptr[4]); /* Yes, number of tracks */ if (outb.tmode % 8 != 0 || outb.tmode > 64 || outb.tmode < 8) /* OK? */ return("!mode = 8 : Must be 8, 16, 32, or 64 ;"); /* Error */ } else if (strcasecmp(outb.mode, "st") == 0 && /* Not numeric and ST? */ (strcasecmp(ptr[4], "mark4") == 0 || strcasecmp(ptr[4], "vlba") == 0)) /* OK? */ (void) strcpy(outb.formt, ptr[4]); /* Yes */ else /* Not OK */ return("!mode = 8 Don't understand 4th argument ;"); /* Error */ } /* End of if fourth argument */ if (outb.tmode == 64 && outb.freq > 20.0) /* Check play_rate OK? */ return("!mode = 6 : Play_rate too high for this mode ;"); (void) outBoard(SET); /* Else OK, send to output hardware */ return("!mode = 0 ;"); /* Normal end for this case */ } /* End of Mode() */ static char * Record() { /* record */ static char scanName[MAXLENGTH] = ""; /* Local copy of sdir->scanName[][] */ unsigned int i; int j, k; char * poff = "off"; size_t mlen; char line[MAXLENGTH + 8] = ""; /* * Check: Can't record in disk-FIFO mode * */ if (ldir.dfifo) { /* OK? */ (void) sprintf(outstring, "!record%s 6 : Can't work in disk-FIFO mode ;", pt != NULL ? "?" : " ="); /* Nope */ return(outstring); /* Error */ } /* * We need device status for either query or command * */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status, OK? */ (void) errors(); /* Nope, error in reading status */ (void) sprintf(outstring, "!record%s 4 : Can't get device status ;", pt != NULL ? "?" : " ="); return(outstring); /* Error */ } (void) errors(); /* * Record can be a query; check that first * */ if (pt != NULL) { /* Was it a query? */ /* Yes, recording on or off? */ if (devstatus.Recording) { /* Recording? */ poff = "on"; /* Yes */ j = sdir->nscans; /* Point to scan being recorded */ /* * Copy scanName[] to line[] and replace spaces with " : " * */ /* (This is for legacy only; there shouldn't be any spaces) */ for (i = 0; i < strlen(scanName); i++) if (scanName[i] == ' ') /* Space? */ (void) strcat(line, " : "); /* Yes, replace with colon */ else /* Not space, so just copy */ (void) strncat(line, scanName + i, 1); (void) outBoard(GET); /* Get output-board parameters to check */ if (outb.throt) /* Throttled recording? */ poff = "throttled"; /* Yes, error, can't keep up, some data loss */ /* (Polling should generate the corresponding error message) */ else if (devstatus.Overflow[0]) /* Overflow (presumably on record)? */ poff = "overflow"; /* Yes */ (void) sprintf(outstring, "!record? 0 : %s : %d : %s ;", poff, j + 1, line); } /* End of if recording */ else { /* Not recording, but should we be? */ if (XLRGetDirectory(dev, &dir) != XLR_SUCCESS) { /* XLR directory, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() WARNING: XLRGetDirectory() returned error \n", me); dir.Full = 0; /* Assume not full, and try to go on */ } if (ldir.recording && dir.Full) /* Stopped at end of medium? */ poff = "halted"; /* Yes */ else if (ldir.recording) /* This case should never happen */ poff = "waiting"; /* Or error? */ if ((j = ldir.reclstn) >= 0) { /* Last scan done by Record(), known? */ /* Yes. Copy scanName[] to line[] and replace spaces with " : " * (This is for legacy only; there shouldn't be any spaces) */ for (i = 0; i < strlen(sdir->scanName[j]); i++) if (sdir->scanName[j][i] == ' ') /* Space? */ (void) strcat(line, " : "); /* Yes, replace with colon */ else /* Not space, so just copy */ (void) strncat(line, sdir->scanName[j] + i, 1); } (void) sprintf(outstring, "!record? 0 : %s : %d : %s ;", poff, j + 1, line); } /* End of else not recording */ return(outstring); /* Normal end for query case */ } /* * Else not a query; we need bank status * */ if (ldir.bmode) { /* Bank mode? */ if (XLRGetBankStatus(dev, bank, &bkstat) != XLR_SUCCESS) { /* Yes, get bank status, OK? */ (void) fprintf(stderr, /* Nope */ "%s Record() ERROR: XLRGetBankStatus() returned error \n", me); (void) errors(); return("!record = 4 : Can't get bank status ;"); } /* Else OK status info */ (void) errors(); if (devinfo.NumDrives < 1) { /* Any disks available? */ error = 1002; /* Message number for this case */ (void) strcpy(messg, "No disks available"); return("!record = 6 : No disks available ;"); /* Error */ } /* * We can't record if write protected * */ if (bkstat.WriteProtected) /* Protected? */ return("!record = 6 : Bank is write protected ;"); /* Yes */ /* Else not protected */ } /* End of if bank mode */ /* (Some of the other stuff below won't work if not in bank mode) */ /* * Record has one required argument, on or off, and one or more * optional arguments: scan name or scan label * */ if (ptr[1] == NULL) /* Argument? */ return("!record = 8 : Need argument ;"); /* Nope, error */ /* * Check: We can't record (either on or off) if other data * transfers are active * */ if (datah > 0 || /* Other data transfers? */ devstatus.Playing || ldir.playing || ldir.scanplaying) /* OK? */ return("!record = 6 : Can't record while other data transfers ;"); /* Nope */ /* * Else OK. Off? * */ if (strcasecmp(ptr[1], "off") == 0) { /* Yes, end this scan (if any) */ /* (We ignore any other parameters) */ /* (We do some of this even if we were not recording) */ if (XLRStop(dev) != XLR_SUCCESS) { /* Stop, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() ERROR: XLRStop() returned error \n", me); (void) errors(); return("!record = 4 : Can't XLRStop() ;"); /* Error */ } (void) errors(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() DEBUG: XLRStop() OK \n", me); if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* Default mode, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() WARNING: XLRSetMode() returned error \n", me); (void) errors(); return("!record = 4 : Can't set SS mode ;"); /* Error */ } (void) errors(); /* * Were we recording? * */ if (! devstatus.Recording && ! ldir.recording) return("!record = 6 : Already off ;"); /* Nope, quit here */ /* (This avoids junk scans with only a copy of the directory) */ /* * Yes, we were recording, but now we're not * */ ldir.recording = FALSE; /* * We need device status again because it's changed * */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status, OK? */ (void) errors(); /* Nope, error in reading status */ return("!record = 4 : Can't get device status ;"); } (void) errors(); /* * Check for automatic bank switch * * * (This is necessary before getLabel() below, else we might get * the label from the wrong bank) */ if ((k = CheckBanks()) < 0) { /* Check banks, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() ERROR: \007 CheckBanks() returned error %d \n", me, k); (void) errors(); return("!record = 4 : Can't CheckBanks() ;"); /* Error */ } /* (CheckBanks() updates the user (sdir) directory * (in case we're in a different bank)) */ /* * Set up to write a directory * */ /* We need the SS directory */ if (XLRGetDirectory(dev, &dir) != XLR_SUCCESS) /* OK? */ { (void) errors(); /* Nope */ return("!record = 4 : Can't get directory ;"); /* Error */ } (void) errors(); /* Else probably OK */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() DEBUG: XLRGetDirectory() OK \n", me); /* Some checks for warnings: */ if (dir.AppendLength <= 0L) /* Length of this scan OK? */ (void) fprintf(stderr, /* Nope */ "%s Record() WARNING: AppendLength is %Ld \n", me, dir.AppendLength); if (dir.Length <= 0L) /* Recorded length OK? */ (void) fprintf(stderr, /* Maybe, maybe not */ "%s Record() WARNING: Recorded Length is %Ld \n", me, dir.Length); } /* End of if debuggery */ /* * Set DMS to "Recorded" * */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Recorded", then skip setLabel() */ if (strcasecmp(dms, "Recorded") != 0 && /* Not already Recorded? */ ldir.dsmask & 0x4) { /* And allowed change to Recorded? */ (void) strcpy(dms, "Recorded"); /* OK, set DMS to Recorded */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } /* * Check: An empty directory here implies that this scan * is the continuation of the last scan that ran off the end of the * other bank (i.e., bank_switch=on). (sdir->scanName[sdir->nscans][0] * is set to '+', and sdir->length[sdir->nscans] is set non-0 at * record=on) * */ if (sdir->nscans == 0 && sdir->scanName[0][0] == '\0' && /* Empty? */ sdir->length[0] == 0L) { /* And clear? */ /* Yes, so this seems to be a continuation scan */ (void) strcpy(sdir->scanName[0], "+"); /* Prefix '+' */ /* With remembered scanName (which has no '+') */ (void) strncat(sdir->scanName[0], scanName, MAXLENGTH - 1); sdir->start[0] = 0LL; /* (Should already be 0) */ sdir->n = 0; /* (Should already be 0) */ /* (nscans is bumped below) */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() DEBUG: Got empty directory; presume continuation \n" " scanName[0] set to \"%s\" \n", me, sdir->scanName[0]); } /* End of if directory was empty and clear */ else { /* Normal, copy scanName to delete '+' */ if (msglev < 1 && scanName[0] == '\0') /* Debuggery and empty name? */ (void) fprintf(stderr, /* Yes */ "%s Record() WARNING: Empty scanName[] \n", me); (void) strncpy(sdir->scanName[sdir->nscans], scanName, MAXLENGTH); } /* Set parameters for this and next scan */ sdir->length[sdir->nscans] = dir.AppendLength; sdir->recpnt = dir.Length; /* * Bump to next scan after the one just recorded * */ if (++sdir->nscans >= MAXSCANS) { /* OK? */ (void) fprintf(stderr, /* Nope */ "%s Record() WARNING: Directory overflow \n", me); sdir->nscans = 0; /* Not OK. Reset (!) */ sdir->n = 0; } /* * Now sdir->nscans points to the next (not-yet-started) scan entry * */ sdir->start[sdir->nscans] = 0L; /* Clear this next entry */ sdir->length[sdir->nscans] = 0L; sdir->scanName[sdir->nscans][0] = '\0'; sdir->n = MAX(0, sdir->nscans - 1); /* Point to the just-recorded scan */ ldir.reclstn = sdir->n; /* Last scan done by Record() */ startbyte = sdir->plapnt = sdir->start[sdir->n]; /* Set the play pointer to the start of the just-recorded scan */ endbyte = startbyte + dir.AppendLength; /* End of just-recorded scan */ (void) writedir(); /* Save a copy of sdir in file DIR and on SS disks */ scanName[0] = '\0'; /* We're done with local scanName[] */ ldir.recording = FALSE; return("!record = 0 ;"); /* Normal end for "off" */ } /* End of if "off" */ /* * On? * */ if (strcasecmp(ptr[1], "on") == 0) { /* * Yes, but check: Was record or something else already on? * */ if (datah > 0 || /* Other data transfer in progress? */ devstatus.Playing || ldir.playing || ldir.scanplaying) /* Playing? */ return("!record = 6 : Other data transfer in progress ;"); /* Error */ if (devstatus.Recording || /* Recording? */ ldir.recording) /* Previous recording not ended? */ return("!record = 6 : Previous recording still active ;"); /* Error */ /* * Else OK to start a recording * */ scanName[0] = '\0'; /* Initialize */ sdir->scanName[sdir->nscans][0] = '\0'; /* If we find multiple fields, then we interchange the order to make * up the scan label: field2_field3_field1_field4, with underlines (_) * as shown. Note the field1 is ptr[2], field2 is ptr[3], etc. * The canonical order is: Scan : Experiment : Station : Source ; * which we make into: Experiment_Station_Scan_Source */ if (ptr[2] != NULL) { /* Scan name or scan label? */ if (msglev < 1) /* Yes. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() DEBUG: Scan name or label \"%s\" \n", me, ptr[2]); mlen = MAXLENGTH - 1; /* mlen is number of bytes available */ /* * Here we look for an optional experiment name, station code, and * source name, and, if so, we append it (them) onto the scan label * with underline(s) ('_') as separator(s). Note that we allow * ptr[3] and the others to be null strings, which generate double * underlines(__) with nothing between * */ if (ptr[3] != NULL) { /* Experiment name also? */ (void) strncat(sdir->scanName[sdir->nscans], ptr[3], mlen); /* Yes */ (void) strncat(scanName, ptr[3], mlen); } if (ptr[3] != NULL && ptr[4] != NULL) { /* Station code also? */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Yes, space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); mlen = MAX(0, mlen - 1); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[4], mlen); (void) strncat(scanName, ptr[4], mlen); } if (ptr[3] != NULL) { /* Previous token? */ /* Yes. We already know that ptr[2] != NULL, so we have a scan name, * but we append the underline only if we have also a previous token, * either experiment name or station code or both */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); } /* We already know that we have a scan name (or scan label), * that is prt[2] != NULL, so add scan name */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[2], mlen); /* Save ptr[2] as a scanName[] */ (void) strncat(scanName, ptr[2], mlen); /* And a local copy */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Space left */ /* * For the possibility that this scan might turn out to be the * last scan on this disk pack, we append '+' onto sdir->scanName[]. * This '+' is omitted on the local copy, scanName[], which is used * to delete the '+' on record=off, if any * */ (void) strncat(sdir->scanName[sdir->nscans], "+", mlen); if (ptr[3] != NULL && ptr[4] != NULL && ptr[5] != NULL) { /* Source name also? */ mlen = MAX(0, MAXLENGTH - 1 - strlen(sdir->scanName[sdir->nscans])); /* Yes, space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); mlen = MAX(0, mlen - 1); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[5], mlen); (void) strncat(scanName, ptr[5], mlen); } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s Record() DEBUG: " /* Yes */ " scanName \"%s\", sdir->scanName[] \"%s\" \n", me, scanName, sdir->scanName[sdir->nscans]); } /* End of if scan name or scan label */ else { /* No scan name, but set '+' (note above) */ (void) strcpy(sdir->scanName[sdir->nscans], "+"); scanName[0] = '\0'; } sdir->start[sdir->nscans] = XLRGetLength(dev); /* Scan start, bytes */ /* (This is presumably the same as dir.Length (at this time)) */ (void) errors(); /* * In case this ends up as the last scan in bank_switch=on mode, * set sdir->length[] to correspond to all the rest of the disk pack * (This gets corrected on record=off, if any) * */ sdir->length[sdir->nscans] = bkstat.TotalCapacity * 4096LL - sdir->start[sdir->nscans]; /* bytes */ /* (Note that sdir->n still points to the previous scan because the * scan to which all this applies is just starting. We can't * bump sdir->n until later) */ ldir.reclstn = sdir->nscans; /* To be last scan done by Record() */ /* * Set up to write a directory * */ if (XLRGetDirectory(dev, &dir) != XLR_SUCCESS) { /* OK? */ (void) errors(); /* Nope */ return("!record = 4 : Can't get directory ;"); /* Error */ } (void) errors(); /* * Else OK * */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() DEBUG: XLRGetDirectory() OK \n", me); (void) writedir(); /* Save a copy of sdir in file DIR and on SS disks */ /* * Set DMS to "Recorded" * */ /* (We need to do the setLabel() before SS_MODE changes) */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Recorded", then skip setLabel() */ if (strcasecmp(dms, "Recorded") != 0 && /* Not already Recorded? */ ldir.dsmask & 0x4) { /* And allowed change to Recorded? */ (void) strcpy(dms, "Recorded"); /* OK, set DMS to Recorded */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } if (XLRSetMode(dev, SS_MODE_EXT) != XLR_SUCCESS) { /* Set mode, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Record() ERROR: XLRSetMode() returned error \n", me); (void) errors(); return("!record = 4 : Can't set SS mode ;"); /* Error */ } if (XLRAppend(dev) != XLR_SUCCESS) { /* Recording on, OK? */ (void) errors(); /* Nope */ return("!record = 4 : Can't append recording ;"); /* Error */ } (void) errors(); (void) outBoard(GET); /* Clear throttled flag if any */ ldir.recording = TRUE; /* Now we're recording */ orpntr = sdir->start[sdir->nscans]; /* Reset old record pointer */ if (forml) /* Formal parsing mode? */ return("!record = 1 ;"); /* Yes. Delayed completion, normal for "on" */ else /* Informal parsing mode */ return("!record = 0 ;"); /* Fix this later */ } /* End of if "on" */ /* * Neither "on" nor "off" is an error * */ return("!record = 8 : Must be on or off ;"); /* Error */ } /* End of Record() */ static char * Play() { /* play */ static pthread_t thread = 0xffffffff; /* For delayed play (Dplay()) */ unsigned int pbstat; /* From XLRGetPlayBufferStatus() */ BOOLEAN opton; /* From XLRGetOption() */ unsigned long plapnt[2]; /* Convert to|from long long */ unsigned long long * pplapnt = (unsigned long long *) plapnt; int doDMS = FALSE; int k; /* * Can't play in disk-FIFO mode * */ if (ldir.dfifo) { /* OK? */ (void) sprintf(outstring, "!play%s 6 : Can't work in disk-FIFO mode ;", pt != NULL ? "?" : " ="); /* Nope */ return(outstring); /* Error */ } /* * We need devstatus for either query or off * */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ (void) errors(); /* Nope */ (void) sprintf(outstring, "!play%s 4 : Can't get device status ;", pt != NULL ? "?" : " ="); return(outstring); /* Error in reading status */ } (void) errors(); /* (We assume that devinfo is current) */ if (! ldir.delayed) /* Dplay() active? */ thread = 0xffffffff; /* Nope, can't be */ /* * Play can be a query; check that first * */ if (pt != NULL) { /* Was it a query? */ /* Yes. Check first for play-arm option */ if (XLRGetOption(dev, SS_OPT_PLAYARM, &opton) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 XLRGetOption() returned error \n", me); (void) errors(); return("!play? 4 : Can't get playarm option ;"); /* Error */ } (void) errors(); /* (The order of the following checks is important) */ if (opton && ! ldir.playing) { /* Arming or armed? */ /* Yes, which one? */ if (XLRGetPlayBufferStatus(dev, &pbstat) != XLR_SUCCESS) { /* OK? */ (void) errors(); /* Nope */ return("!play? 4 : Can't get play-buffer status ;"); /* Error */ } (void) errors(); if (pbstat == SS_PBS_FILLING) /* Arming? */ return("!play? 0 : arming ;"); /* Yes */ else if (pbstat == SS_PBS_FULL) /* Armed? */ return("!play? 0 : armed ;"); /* Yes */ else /* No-fair playback-buffer status */ return("!play? 4 : Incorrect play-buffer status ;"); /* Error */ } else if (devstatus.Playing) /* Are we playing? */ return("!play? 0 : on ;"); /* Yes */ else if (ldir.delayed && thread != 0xffffffff) /* Waiting? */ return("!play? 0 : waiting ;"); /* Yes */ else if (ldir.playing || ldir.scanplaying) /* Should we be playing? */ return("!play? 0 : halted ;"); /* Yes, probably end of recording */ else /* No */ return("!play? 0 : off ;"); /* Not playing */ } /* End of if query */ /* * We can't do any playing if other data transfers are on * */ if (datah > 0 || devstatus.Recording) { /* Other data transfer in progress or recording? */ if (msglev < 2) /* Yes, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: Can't play while other data transfer \n", me); return("!play = 6 : Can't play while other data transfer ;"); /* Error */ } if (devinfo.NumDrives < 1) { /* Any disks available? */ error = 1002; /* Message number for this case */ (void) strcpy(messg, "No disks available"); return("!play = 6 : No disks available ;"); /* Error */ } /* * Play has three possible arguments: first arm, on, or off, then * the byte number in the recorded data stream and * a ROT at which to start playing. The second (optional) * argument, if any, is needed first * */ if (ptr[1] != NULL && ptr[2] != NULL) /* Playback "pointer"? */ sdir->plapnt = atoll(ptr[2]); /* Yes */ if (sdir->plapnt < 0) /* Negative play pointers are no fair */ sdir->plapnt = 0; *pplapnt = sdir->plapnt; plapnt[0] &= 0xfffffff8; /* Now 8-byte aligned */ readdesc.AddrHi = plapnt[1]; readdesc.AddrLo = plapnt[0]; /* (These are used also in Dplay()) */ readdesc.XferLength = 0; readdesc.BufferAddr = NULL; /* * Off or default? * */ if (ptr[1] == NULL || ptr[1][0] == '\0' || strcasecmp(ptr[1], "off") == 0) { if (devstatus.Playing || /* Yes */ ldir.playing || ldir.scanplaying) { /* But are we now playing? */ if (XLRStop(dev) != XLR_SUCCESS) { /* Yes, stop, OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: XLRStop() returned error \n", me); (void) errors(); return("!play = 4 : Can't XLRStop() ;"); /* Error */ } (void) errors(); if (ldir.dsmask & 0x2) /* Allowed DMS change to Played? */ doDMS = TRUE; /* Yes. We were playing, so set DMS below */ /* (Else doDMS stays FALSE) */ ldir.playing = FALSE; /* We were playing, but now we're not */ ldir.scanplaying = FALSE; /* * We need device status again because it's changed * */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* OK? */ (void) errors(); /* Nope, error in reading status */ return("!play = 4 : Can't get device status ;"); } (void) errors(); /* * Check for automatic bank switch * * * This is necessary before getLabel() below, else we might get * the label from the wrong bank. CheckBanks() updates the * user (sdir) directory in case we're in a different bank. */ if ((k = CheckBanks()) < 0) { /* Check banks, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 CheckBanks() returned error %d \n", me, k); (void) errors(); return("!play = 4 : Can't CheckBanks() ;"); /* Error */ } } /* End of if (formerly) playing */ if (ldir.doOff) { /* Should we (have been) playing? */ if (ptr[2] == NULL) { /* Yes. Playback index on command line? */ /* No, update playback index to the end of this playing */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() DEBUG: Calling XLRGetPlayLength() \n", me); sdir->plapnt += XLRGetPlayLength(dev); /* Update playback index to the end of this play */ *pplapnt = sdir->plapnt; readdesc.AddrHi = plapnt[1]; readdesc.AddrLo = plapnt[0]; } if ((error = XLRGetLastError()) != 3) /* Errors? */ return("!play = 4 : Can't get play length ;"); /* Yes, error */ } /* * Formerly playing or not ... * */ ldir.playing = FALSE; /* Done playing */ ldir.scanplaying = FALSE; ldir.doOff = FALSE; /* Don't offPlay() */ if (ldir.delayed && thread != 0xffffffff) { /* Thread waiting to start? */ (void) pthread_cancel(thread); /* Yes, cancel it */ ldir.delayed = FALSE; if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: Aborting a Dplay() in waiting \n", me); } thread = 0xffffffff; /* Dplay() thread not waiting to start */ /* * Either way, we set option and modes back to defaults * */ if (XLRClearOption(dev, SS_OPT_PLAYARM) != XLR_SUCCESS) { /* Clear to normal-play option, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 XLRClearOption() returned error \n", me); (void) errors(); return("!play = 4 : Can't clear playarm option ;"); /* Error */ } (void) errors(); if (XLRSetFPDPMode(dev, SS_FPDP_RECVMASTER, SS_OPT_FPDPNRASSERT) != XLR_SUCCESS) { /* Back to default FPDP mode, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: XLRSetFPDPMode() returned error \n", me); (void) errors(); return("!play = 4 : Can't set FPDP mode ;"); /* Error */ } (void) errors(); if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* Default mode, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 XLRSetMode() returned error \n", me); (void) errors(); return("!play = 4 : Can't set SS mode ;"); /* Error */ } (void) errors(); if (XLRSetPlaybackLength(dev, 0, 0) != XLR_SUCCESS) { /* Zero length, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Play() ERROR: \007 XLRSetPlaybackLength() returned error \n", me); /* Yes */ (void) errors(); return("!play = 4 : Can't set playback length ;"); /* Error */ } (void) errors(); /* * If we were playing (now stopped), then set DMS to "Played" * */ if (doDMS) { /* (We need to do the setLabel() after SS_MODE back to normal) */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Played", then skip setLabel() */ if (strcasecmp(dms, "Played") != 0) { /* Already Played? */ (void) strcpy(dms, "Played"); /* No, set DMS to Played */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } } /* End of if we were playing */ /* Else not (formerly) playing, so don't change DMS */ return("!play = 0 ;"); /* Normal end for "off" */ } /* * Arm? * */ if (strcasecmp(ptr[1], "arm") == 0) { if (devstatus.Playing || ldir.playing || ldir.scanplaying || ldir.delayed) /* Yes, but we can't arm while already playing or in Dplay(), OK? */ return("!play = 6 : Already playing or delayed ;"); /* Nope, error */ sdir->recpnt = XLRGetLength(dev); /* Update record offset, bytes */ (void) errors(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() DEBUG: Record pointer updated \n", me); /* * Set DMS to "Played" * */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Played", then skip setLabel() */ if (strcasecmp(dms, "Played") != 0 && /* Not already Played? */ ldir.dsmask & 0x2) { /* And allowed change to Played? */ (void) strcpy(dms, "Played"); /* OK, set DMS to Played */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } /* * Arming is now done in Dplay() in arm-only mode * */ armOnly = TRUE; /* Arm only */ ldir.delayed = TRUE; if (pthread_create(&thread, NULL, Dplay, NULL) != 0 && /* OK? */ msglev < 2) { /* And debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 pthread_create() returned ", me); perror("error"); /* Error, but we try to go on */ } (void) pthread_detach(thread); ldir.playing = FALSE; /* Set local playing status */ ldir.scanplaying = FALSE; ldir.doOff = FALSE; /* Do not offPlay() (yet) */ return("!play = 1 ;"); /* Delayed completion, normal for "arm" */ } /* * On? * */ if (strcasecmp(ptr[1], "on") == 0) { /* * Yes. Set DMS to "Played" * */ /* (We need to do the setLabel() before SS_MODE) */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Played", then skip setLabel() */ if (strcasecmp(dms, "Played") != 0 && /* Not already Played? */ ldir.dsmask & 0x2) { /* And allowed change to Played? */ (void) strcpy(dms, "Played"); /* OK, set DMS to Played */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } /* * If there is a third argument (start ROT), then we set the * arm-to-trigger play option and spawn a pthread Dplay() for * delayed play * */ if (ptr[2] != NULL && ptr[3] != NULL) { /* Start ROT? */ srot = atof(ptr[3]); /* Yes, decode it (for Dplay()) */ if (ldir.delayed) /* Delayed completion already is an error */ return("!play = 6 : Already active delayed ;"); /* Yes, error */ /* * And check: We can't turn play on while already playing * */ if (devstatus.Playing || ldir.playing || ldir.scanplaying) /* OK? */ return("!play = 6 : Already playing ;"); /* Nope, error */ /* * Check for certain other errors * */ if (srot <= 0.0 || rot <= 0.0 || tvROT.tv_sec == 0 || task_id == 0) /* (This checks for a prior task_ID = ...) OK? */ return("!play = 6 : No ROT or task ID available ;"); /* Error */ /* * Else OK. Setting the SS mode for playing, setting the FPDP mode * for playing, setting the arm-to-fill-buffer-to-trigger play option, * and starting filling the play buffer were all moved to Dplay(), * which will be in another thread, and which has the delayed-play * trigger after nanosleep() * */ armOnly = FALSE; /* Normal Dplay() mode */ ldir.delayed = TRUE; if (pthread_create(&thread, NULL, Dplay, NULL) != 0 && /* OK? */ msglev < 2) { /* And debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 pthread_create() returned ", me); perror("error"); /* Error, but we try to go on */ } (void) pthread_detach(thread); ldir.playing = TRUE; /* Now set local playing status */ ldir.doOff = FALSE; /* Do not offPlay() (yet) */ return("!play = 1 ;"); /* OK, delayed completion */ /* End of if start ROT */ } /* * Else if we are arming or armed, then trigger playing * */ if (XLRGetOption(dev, SS_OPT_PLAYARM, &opton) != XLR_SUCCESS) { /* Get play-arm option, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 XLRGetOption() returned error \n", me); (void) errors(); return("!play = 4 : Can't get playarm option ;"); /* Error */ } (void) errors(); /* * Arming or armed? * */ if (opton) { /* Yes, but a second argument is an error in this case */ if (ptr[2] != NULL) { /* Playback "pointer"? */ /* Yes, error for this case */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: Playback pointer not allowed in this case \n", me); (void) errors(); return("!play = 8 : Playback pointer not allowed ;"); /* Error */ } /* * And check: Already playing or waiting to start is an error * */ if (ldir.playing || ldir.scanplaying) /* OK? */ return("!play = 6 : Already playing ;"); /* Nope, error */ /* * Else OK, trigger playing * */ if (XLRPlayTrigger(dev) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: XLRPlayTrigger() returned error \n", me); (void) errors(); return("!play = 4 : Can't trigger playing ;"); /* Error */ } ldir.playing = TRUE; /* Set local playing status */ ldir.scanplaying = FALSE; ldir.doOff = TRUE; /* Do offPlay() if halted */ return("!play = 1 ;"); /* Delayed completion, normal for "on" */ } /* * Else not arming or armed, so start normal play. * But first check: Already playing? * */ if (devstatus.Playing || ldir.playing || ldir.scanplaying) /* OK? */ return("!play = 6 : Already playing ;"); /* Nope, error */ sdir->recpnt = XLRGetLength(dev); /* Update record offset, bytes */ /* (We can't do that while playing) */ (void) errors(); /* * Set SS mode for playing * */ if (XLRSetMode(dev, SS_MODE_EXT) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 XLRSetMode() returned error \n", me); (void) errors(); return("!play = 4 : Can't set SS mode ;"); /* Error */ } (void) errors(); /* * Set FPDP mode for playing * * * SS_FPDP_XMIT = transmit (does not drive clock), * SS_FPDP_XMITMASTER = normal transmit (with clock) */ if (XLRSetFPDPMode(dev, found == 2 ? SS_FPDP_XMITMASTER : SS_FPDP_XMIT, 0) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 XLRSetFPDPMode() returned error \n", me); (void) errors(); return("!play = 4 : Can't set FPDP mode ;"); /* Error */ } (void) errors(); /* * Start normal playing * */ if (XLRPlayback(dev, plapnt[1], plapnt[0]) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() WARNING: XLRPlayback() returned error \n", me); (void) errors(); return("!play = 4 : Can't start playing ;"); /* Error */ } (void) errors(); /* Else OK */ ldir.playing = TRUE; /* Set local playing status */ ldir.scanplaying = FALSE; ldir.doOff = TRUE; /* Do offPlay() if halted */ return("!play = 1 ;"); /* Delayed completion, normal for "on" */ } /* * Neither off nor arm, nor on is an error * */ return("!play = 8 : Must be arm, on, or off ;"); /* Error */ } /* End of Play() */ int offPlay() { /* Housekeeping after play is halted */ /* Call offPlay() when halted during play or scan_play */ /* (We do not change playing or scanplaying) */ unsigned long plapnt[2]; /* Convert to/from long long */ unsigned long long * pplapnt = (unsigned long long *) plapnt; if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s offPlay() WARNING: Play halted \n", me); /* * Stop playing * */ if (XLRStop(dev) != XLR_SUCCESS) { /* Yes, stop, OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s offPlay() WARNING: XLRStop() returned error \n", me); (void) errors(); return(-1); /* Error */ } (void) errors(); if (msglev < 1) /* Yes. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s offPlay() DEBUG: Calling XLRGetPlayLength() \n", me); /* * Update playback index to the end of this playing * */ sdir->plapnt += XLRGetPlayLength(dev); (void) errors(); *pplapnt = sdir->plapnt; readdesc.AddrHi = plapnt[1]; readdesc.AddrLo = plapnt[0]; ldir.doOff = FALSE; /* Not again */ /* * Reset to default option and modes * */ if (XLRClearOption(dev, SS_OPT_PLAYARM) != XLR_SUCCESS) { /* Clear to normal-play option, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s offPlay() WARNING: XLRClearOption() returned error \n", me); (void) errors(); return(-2); /* Error */ } (void) errors(); if (XLRSetFPDPMode(dev, SS_FPDP_RECVMASTER, SS_OPT_FPDPNRASSERT) != XLR_SUCCESS) { /* Back to default FPDP mode, OK? */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s offPlay() WARNING: XLRSetFPDPMode() returned error \n", me); (void) errors(); return(-3); /* Error */ } (void) errors(); if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* Default mode, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s offPlay() ERROR: \007 XLRSetMode() returned error \n", me); (void) errors(); return(-4); /* Error */ } (void) errors(); if (XLRSetPlaybackLength(dev, 0, 0) != XLR_SUCCESS) { /* Zero length, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s offPlay() ERROR: \007 XLRSetPlaybackLength() returned error \n", me); /* Yes */ (void) errors(); return(-5); /* Error */ } (void) errors(); return(0); /* Else OK, normal end */ } /* End of offPlay() */ static char * Scan_play() { /* scan_play */ static pthread_t thread = 0xffffffff; /* For delayed play (Dplay()) */ unsigned int pbstat; /* From XLRGetPlayBufferStatus() */ BOOLEAN opton; /* Return from XLRGetOption() */ unsigned long plapnt[2]; /* Convert to/from long long */ unsigned long long * pplapnt = (unsigned long long *) plapnt; int k; /* * We need device status for either query or off * */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ (void) errors(); /* Nope */ (void) sprintf(outstring, "!scan_play%s 4 : Can't get device status ;", pt != NULL ? "?" : " ="); return(outstring); /* Error in reading status */ } (void) errors(); /* (We assume that devinfo is current) */ /* * Scan_play can be a query; check that first * */ if (pt != NULL) { /* Was it a query? */ /* * Yes. Check first for play-arm option * */ if (XLRGetOption(dev, SS_OPT_PLAYARM, &opton) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() ERROR: \007 XLRGetOption() returned error \n", me); (void) errors(); return("!scan_play? 4 : Can't get playarm option ;"); /* Error */ } (void) errors(); /* (The order of the following checks is important) */ if (opton && ! ldir.scanplaying) { /* Arming or armed? */ /* Yes, which one? */ if (XLRGetPlayBufferStatus(dev, &pbstat) != XLR_SUCCESS) { /* OK? */ (void) errors(); /* Nope */ return("!scan_play? 4 : Can't get play-buffer status ;"); /* Error */ } (void) errors(); if (pbstat == SS_PBS_FILLING) /* Arming? */ return("!scan_play? 0 : arming ;"); /* Yes */ else if (pbstat == SS_PBS_FULL) /* Armed? */ return("!scan_play? 0 : armed ;"); /* Yes */ else /* No-fair playback-buffer status */ return("!scan_play? 4 : Incorrect play-buffer status ;"); /* Error */ } else if (devstatus.Playing && ldir.scanplaying) /* Are we playing? */ return("!scan_play? 0 : active ;"); /* Yes */ else if (ldir.scanplaying) /* Should we be playing? */ return("!scan_play? 0 : halted ;"); /* Yes, probably end of scan */ else /* No */ return("!scan_play? 0 : inactive ;"); /* Not scan_playing */ } /* End of if query */ /* * Not a query. We can't do any playing if recording is on * */ if (datah > 0 || devstatus.Recording) { /* Other data transfer in progress or recording? */ if (msglev < 2) /* Yes, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() ERROR: Can't play while other data transfer \n", me); return("!scan_play = 6 : Can't play while other data transfer ;"); } if (devinfo.NumDrives < 1) { /* Any disks available? */ error = 1002; /* Message number for this case */ (void) strcpy(messg, "No disks available"); return("!scan_play = 6 : No disks available ;"); /* Error */ } /* * No scan_play if no scans * */ if (sdir->nscans <= 0) /* Do we have any scans? */ return("!scan_play = 6 : No scan to play ;"); /* Nope, error */ /* * Else OK. On or default? * */ if (ptr[1] == NULL || ptr[1][0] == '\0' || strcasecmp(ptr[1], "on") == 0) { if (devstatus.Playing || ldir.playing || ldir.scanplaying) /* Yes, but we can't turn play on while already playing, OK? */ return("!scan_play = 6 : Already playing ;"); /* Nope, error */ /* * If we are arming or armed, then trigger playing * */ if (XLRGetOption(dev, SS_OPT_PLAYARM, &opton) != XLR_SUCCESS) { /* Get play-arm option, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() ERROR: \007 XLRGetOption() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't get playarm option ;"); /* Error */ } (void) errors(); if (opton) { /* Arming or armed? */ if (XLRPlayTrigger(dev) != XLR_SUCCESS) { /* Yes, trigger playing, OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() WARNING: XLRPlayTrigger() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't trigger playing ;"); /* Error */ } ldir.scanplaying = TRUE; /* Set local playing status */ ldir.playing = FALSE; ldir.doOff = TRUE; /* Do offPlay() if halted */ return("!scan_play = 1 ;"); /* Delayed completion, normal for "on" */ } /* * Else not arming or armed, so start normal play * */ sdir->recpnt = XLRGetLength(dev); /* Update record offset, bytes */ /* (We can't do that while playing) */ (void) errors(); /* * Set DMS to "Played" * */ /* (We need to do the setLabel() before SS_MODE) */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Played", then skip setLabel() */ if (strcasecmp(dms, "Played") != 0 && /* Not already Played? */ ldir.dsmask & 0x2) { /* And allowed change to Played? */ (void) strcpy(dms, "Played"); /* OK, set DMS to Played */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } /* * Set mode for playing * */ if (XLRSetMode(dev, SS_MODE_EXT) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s scan_play() ERROR: \007 " /* Yes */ "XLRSetMode() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't set SS mode ;"); /* Error */ } (void) errors(); /* * Set FPDP mode for playing * * * SS_FPDP_XMIT = transmit (does not drive clock), * SS_FPDP_XMITMASTER = normal transmit (with clock) */ if (XLRSetFPDPMode(dev, found == 2 ? SS_FPDP_XMITMASTER : SS_FPDP_XMIT, 0) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() ERROR: \007 XLRSetFPDPMode() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't set FPDP mode ;"); /* Error */ } (void) errors(); /* * Set the play length * */ *pplapnt = endbyte - startbyte; /* Play length */ plapnt[0] &= 0xfffffff8; /* Now 8-byte aligned */ if (XLRSetPlaybackLength(dev, plapnt[1], plapnt[0]) != XLR_SUCCESS) { /* Set length, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Scan_play() ERROR: \007 " /* Yes */ "XLRSetPlaybackLength() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't set play length ;"); /* Error */ } /* * Set the play pointer * */ *pplapnt = startbyte; plapnt[0] &= 0xfffffff8; /* Now 8-byte aligned */ /* * Start playing * */ if (XLRPlayback(dev, plapnt[1], plapnt[0]) != XLR_SUCCESS) { /* Play OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() WARNING: XLRPlayback() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't start playback ;"); /* Error */ } (void) errors(); ldir.scanplaying = TRUE; /* Set local playing status */ ldir.playing = FALSE; ldir.doOff = TRUE; /* Do offPlay() if halted */ return("!scan_play = 1 ;"); /* Delayed completion, normal for "on" */ } /* * Off? * */ if (strcasecmp(ptr[1], "off") == 0) { if (devstatus.Playing) { /* Yes, now playing? */ if (XLRStop(dev) != XLR_SUCCESS) { /* Yes, stop, OK? */ if (msglev < 2) /* Nope, error. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() WARNING: XLRStop() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't XLRStop() ;"); /* Error */ } (void) errors(); } /* End of if (formerly) playing */ if (ldir.doOff) { /* Should we (have been) playing? */ sdir->plapnt += XLRGetPlayLength(dev); /* Yes */ /* Update playback index to the end of this play */ *pplapnt = sdir->plapnt; readdesc.AddrHi = plapnt[1]; readdesc.AddrLo = plapnt[0]; } (void) errors(); /* * Formerly playing or not ... * */ ldir.playing = FALSE; /* Done playing */ ldir.scanplaying = FALSE; ldir.doOff = FALSE; /* Don't offPlay() */ /* * Set option and modes back to defaults * */ if (XLRClearOption(dev, SS_OPT_PLAYARM) != XLR_SUCCESS) { /* Clear to normal-play option, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() ERROR: \007 XLRClearOption() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't clear playarm option ;"); /* Error */ } (void) errors(); if (XLRSetFPDPMode(dev, SS_FPDP_RECVMASTER, SS_OPT_FPDPNRASSERT) != XLR_SUCCESS) { /* Back to default FPDP mode, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() WARNING: XLRSetFPDPMode() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't set FPDP mode ;"); /* Error */ } (void) errors(); if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* Default mode, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() ERROR: \007 XLRSetMode() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't set SS mode ;"); /* Error */ } (void) errors(); if (XLRSetPlaybackLength(dev, 0, 0) != XLR_SUCCESS) { /* Zero length, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Scan_play() ERROR: \007 " /* Yes */ "XLRSetPlaybackLength() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't set play length ;"); /* Error */ } (void) errors(); return("!scan_play = 0 ;"); /* Else normal end for "off" */ } /* * Arm? * */ if (strcasecmp(ptr[1], "arm") == 0) { sdir->recpnt = XLRGetLength(dev); /* Yes, update record offset, bytes */ /* (We can't do that while playing) */ /* * Set DMS to "Played" * */ /* (We need to do the setLabel() before SS_MODE) */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Played", then skip setLabel() */ if (strcasecmp(dms, "Played") != 0 && /* Not already Played? */ ldir.dsmask & 0x2) { /* And allowed change to Played? */ (void) strcpy(dms, "Played"); /* OK, set DMS to Played */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Scan_play() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } /* * Set the play length * */ *pplapnt = endbyte - startbyte; /* Play length */ plapnt[0] &= 0xfffffff8; /* Now 8-byte aligned */ if (XLRSetPlaybackLength(dev, plapnt[1], plapnt[0]) != XLR_SUCCESS) { /* Set length, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Scan_play() ERROR: \007 " /* Yes */ "XLRSetPlaybackLength() returned error \n", me); (void) errors(); return("!scan_play = 4 : Can't set play length ;"); /* Error */ } /* * Set the play pointer * */ *pplapnt = startbyte; plapnt[0] &= 0xfffffff8; /* Now 8-byte aligned */ /* * Arming is now done in Dplay() in arm-only mode * */ armOnly = TRUE; /* Arm only */ ldir.delayed = TRUE; if (pthread_create(&thread, NULL, Dplay, NULL) != 0 && /* OK? */ msglev < 2) { /* And debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Play() ERROR: \007 pthread_create() returned ", me); perror("error"); /* Error, but we try to go on */ } (void) pthread_detach(thread); ldir.scanplaying = FALSE; /* Set local playing status */ ldir.doOff = FALSE; /* Do not offPlay() (yet) */ return("!scan_play = 1 ;"); /* Delayed completion, normal for "arm" */ } /* * Neither on, nor off, nor arm is an error * */ return("!scan_play = 8 : Must be arm, on, or off ;"); /* Error */ } /* End of Scan_play() */ static char * Play_rate() { /* play_rate */ double freq = 8.0, freq2, bfreq; /* Modified outb.freq */ /* This freq default is equivalent to 9 MHz for clock */ (void) outBoard(GET); /* Get output-board parameters */ /* (But outb.freq can't be read back) */ /* Play_rate can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ /* Yes, but we can't read it back from hardware */ freq = outb.freq; /* Initialize, MHz */ /* (This should be the same as sdir->playRate) */ if (strcasecmp(outb.formt, "vlba") == 0 || /* VLBA format? */ strcasecmp(outb.formt, "mark4") == 0) /* Or Mark 4? */ freq *= 1.125; /* Yes, account for parity */ if (strcasecmp(outb.formt, "vlba") == 0) /* VLBA format? */ freq *= 1.008; /* Yes, account for non-data-replacement */ if (outb.tmode == 64) /* 64-tracks? */ freq2 = 2.0*freq; /* Yes, double to get clock rate */ else /* Not 64-track */ freq2 = freq; (void) sprintf(outstring, "!play_rate? 0 : %.5f : %.5f : %.5f ;", outb.freq, freq, freq2); /* MHz */ return(outstring); /* Normal end if query */ } /* Not a query. The new play_rate has two optional arguments, * but we also allow the old format: one argument, numeric only */ if (ptr[1] == NULL) { /* Argument? */ sdir->playRate = outb.freq = 8.0; /* No, take default */ /* (Which usually corresponds to clock = 9 MHz) */ (void) outBoard(SET); /* Transfer to hardware */ return("!play_rate = 0 ;"); /* Yes, normal end for this case */ } if (ptr[2] != NULL) /* Got two arguments? */ freq = atof(ptr[2]); /* Yes, get freq */ if (ptr[2] == NULL && strspn(ptr[1], "0123456789.") == strlen(ptr[1])) /* Old format (numeric only)? */ bfreq = atof(ptr[1]); /* Yes */ /* Else the first argument tells how to interpret this freq */ else if (strcasecmp(ptr[1], "data") == 0) /* Data frequency? */ bfreq = freq; /* Yes */ else if (strncasecmp(ptr[1], "clock", 5) == 0 || /* Clock or clockgen? */ ptr[1][0] == '\0') { /* Or null for clock default */ bfreq = freq; /* Yes, initialize */ if (strcasecmp(outb.formt, "vlba") == 0 || /* VLBA format? */ strcasecmp(outb.formt, "mark4") == 0) /* Or Mark 4? */ bfreq /= 1.125; /* Yes, account for parity */ if (strcasecmp(outb.formt, "vlba") == 0) /* VLBA format? */ bfreq /= 1.008; /* Yes, account for non-data-replacement */ } else if (strcasecmp(ptr[1], "ext") == 0) /* External clock? */ bfreq = 0.0; /* Yes */ else /* None of the above is an error */ return("!play_rate = 8 : Must be data, clock, clockgen, or ext ;"); /* Parameter error */ if (strcasecmp(ptr[1], "clockgen") == 0 && /* Clock generator? */ outb.tmode == 64) /* And 64 tracks? */ bfreq /= 2.0; /* Yes, clock runs at double speed */ if (bfreq < 0.0 || bfreq > 40.0 || /* (Zero means external clock) */ (outb.tmode == 64 && bfreq > 20.0)) /* Sanity check, OK? */ return("!play_rate = 8 : Frequency out of range ;"); /* No, error */ sdir->playRate = outb.freq = bfreq; /* OK, now the same */ (void) outBoard(SET); /* Transfer to hardware */ return("!play_rate = 0 ;"); /* Normal */ } /* End of Play_rate() */ static char * Skip() { /* skip */ static unsigned int nskip = 0; /* Last skip */ static int sneg = FALSE; /* Sign of nskip negative */ long long len; long long skip; /* Number of bytes to skip */ unsigned int mskip; /* Skip can now be a query; check that first */ if (pt != NULL) { /* Query? */ (void) sprintf(outstring, "!skip? 0 : %d ;", /* Yes */ sneg ? -(int)nskip : nskip); return(outstring); /* Normal for this case */ } /* Else a command; one required argument: number of bytes to skip */ if (ptr[1] == NULL || ptr[1][0] == '\0') /* Argument? */ return("!skip = 8 : Argument required ;"); /* Nope, error */ /* Don't implement a skip if we're waiting to start */ if (ldir.delayed) /* Another thread pending? */ return("!skip = 6 : Delayed pending ;"); /* Yes, error */ /* (No skip allowed in this case) */ /* OK, got argument and OK to skip */ skip = atoll(ptr[1]); /* Number of bytes to skip */ /* The argument of XLRSkip() "must be 8 byte aligned" so we * arbitrarily truncate down and to an int */ mskip = (unsigned int) ABS(skip) & 0xfffffff8; /* Check: Are we playing or net2out? */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status? */ (void) errors(); return("!skip = 4 : Can't get device status ;"); /* Error */ } (void) errors(); if (devstatus.Playing) { /* Playing? */ nskip = XLRSkip(dev, mskip, skip >= 0); /* Yes, do skip */ /* (nskip is now the magnitude of the skip actually done) */ (void) errors(); } else if (datah == 2 && ldir.n2odoing > 0) { /* Or net2out? */ nskip = XLRSkip(dev, mskip, skip >= 0); /* Yes, do skip */ /* (nskip is now the magnitude of the skip actually done) */ (void) errors(); if (msglev < 1) { /* Debuggery? */ len = XLRGetFIFOLength(dev); /* Yes, FIFO length now */ (void) fprintf(stderr, /* Yes */ "%s Skip() DEBUG: len %Ld, mskip %u, nskip %u \n", me, len, mskip, nskip); } } /* End of else if net2out */ else /* Neither playing nor net2out, so don't actually do skip */ nskip = mskip; /* Update the playback index for this skip */ if (skip >= 0) { /* Positive? */ sdir->plapnt += nskip; /* Yes */ sneg = FALSE; /* Not negative */ } else { /* Negative */ sdir->plapnt -= nskip; sneg = TRUE; /* Negative */ } if (nskip == mskip) /* Did we do the full skip? */ return("!skip = 0 ;"); /* Yes. Normal end */ else /* Nope */ return("!skip = 0 : Requested skip was not achieved ;"); } /* End of Skip() */ static char * Track_select() { /* track_select and track_set */ /* Track_select can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ (void) outBoard(GET); /* Yes, get trka and trkb from hardware */ (void) sprintf(outstring, "!%s? 0 : %d : %d ;", ptr[0], outb.trka, outb.trkb); return(outstring); /* Normal for query case */ } /* Command: Track_select has two optional arguments: * tracks A and B for Mark-4 decoder and track A for track_check */ if (ptr[1] == NULL || ptr[1][0] == '\0') { /* Argument? */ outb.trka = 15; /* No, take defaults */ outb.trkb = 16; } else { /* Not-null first argument */ if (strcasecmp(ptr[1], "inc") == 0) { /* Yes, increment? */ outb.trka++; /* Yes */ if (outb.trka > 33 && outb.trka < 102) /* Up to upper 32? */ outb.trka = 102; /* Yes */ else if (outb.trka > 133) /* Wrap? */ outb.trka = 2; /* Yes */ } else if (strcasecmp(ptr[1], "dec") == 0) { /* Decrement? */ outb.trka--; /* Yes */ if (outb.trka > 33 && outb.trka < 102) /* Down to lower 32? */ outb.trka = 33; /* Yes */ else if (outb.trka < 2) /* Unwrap? */ outb.trka = 133; /* Yes */ } else /* Not increment or decrement, assume number */ outb.trka = atoi(ptr[1]); /* Set track A */ } if (outb.trka < 2 || (outb.trka > 33 && outb.trka < 102) || outb.trka > 133) { /* Track A in range? */ (void) sprintf(outstring, "!%s = 8 : Track out of range ;", ptr[0]); return(outstring); /* Error */ } if (ptr[1] != NULL && ptr[2] != NULL && ptr[2][0] != '\0') { /* Another not-null argument? */ if (strcasecmp(ptr[2], "inc") == 0) { /* Yes, increment? */ outb.trkb++; /* Yes */ if (outb.trkb > 33 && outb.trkb < 102) /* Up to upper 32? */ outb.trkb = 102; /* Yes */ else if (outb.trkb > 133) /* Wrap? */ outb.trkb = 2; /* Yes */ } else if (strcasecmp(ptr[2], "dec") == 0) { /* Decrement? */ outb.trkb--; /* Yes */ if (outb.trkb > 33 && outb.trkb < 102) /* Down to lower 32? */ outb.trkb = 33; /* Yes */ else if (outb.trkb < 2) /* Unwrap? */ outb.trkb = 133; /* Yes */ } else /* Not increment or decrement, assume number */ outb.trkb = atoi(ptr[2]); /* Set track B */ } if (outb.trkb < 2 || (outb.trkb > 33 && outb.trkb < 102) || outb.trkb > 133) { /* Track B in range? */ (void) sprintf(outstring, "!%s = 8 : Track out of range ;", ptr[0]); return(outstring); /* Error */ } /* Else OK */ (void) outBoard(SET); /* Set trka and trkb to hardware */ (void) sprintf(outstring, "!%s = 0 ;", ptr[0]); return(outstring); /* Normal end */ } /* End of Track_select() */ static char * Status() { /* status */ unsigned long status = 0; char * msg = ""; char line[64]; int i; /* Should have '?' but no arguments, but we don't check */ if (ldir.delayed) /* Special case: Delayed completion pending? */ status |= 0x008; /* Yes */ if (error == 0 || error == 3) /* Pending error? */ error = XLRGetLastError(); /* No, get update */ else if (error >= 1000) { /* Error from elsewhere? */ status |= 0x002; /* Yes */ msg = messg; } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Status() DEBUG: Initial error code %lu \n", me, error); if (error != 3 && error < 1000) { /* XLR error? */ status |= 0x002; /* Yes */ if (XLRGetErrorMessage(messg, error) != XLR_SUCCESS) /* Get error message, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, "%s Status() ERROR: XLR error %lu, " "but XLRGetErrorMessage() failed \n", me, error); msg = messg; /* Remember error message */ } /* (XLRGetDeviceInfo() doesn't work if playing or recording. * We assume that it's unchanged from a previous call) */ if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Status() calling XLRGetDeviceStatus(), error %lu \n", me, error); if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Status() ERROR: XLRGetDeviceStatus() returned error \n", me); status |= 0x002; /* Error */ if ((error = XLRGetLastError()) != 3) { /* Errors? */ if (XLRGetErrorMessage(messg, error) != XLR_SUCCESS) /* Yes, get error message, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, "%s Status() ERROR: XLR error %lu, " "but XLRGetErrorMessage() failed \n", me, error); /* (But we try to go on) */ msg = messg; /* Remember error message */ } } else { /* DeviceStatus OK */ if (devstatus.SystemReady) status |= 0x001; /* Ready */ if (! ldir.dfifo && (devstatus.DriveFail || devstatus.SysError || /* Errors? * * (DriveFail seems to be set in case disk FIFO) */ devstatus.CtlrError || devstatus.FifoFull || devstatus.Overflow[0])) { status |= 0x002; /* Yes */ error = 0; /* No message number for this case */ if (devstatus.DriveFail) msg = "DriveFail"; if (devstatus.SysError) { (void) sprintf(line, "SysError %#x ", devstatus.SysErrorCode); msg = line; } if (devstatus.CtlrError) msg = "CtlrError"; if (devstatus.FifoFull) { msg = "FifoFull"; devstatus.FifoFull = FALSE; } /* For next time */ if (devstatus.Overflow[0]) { status |= 0x080; /* Overflow (presumably on record) */ msg = "Overflow"; devstatus.Overflow[0] = FALSE; /* For next time */ } } /* End of if devstatus errors */ if (ldir.dfifo) /* Disk-FIFO-only special mode? */ status |= 0x020; /* Yes */ if (devstatus.Recording) { status |= 0x048; /* Recording and delayed completion */ (void) outBoard(GET); /* Get output-board parameters */ if (outb.throt) /* Throttled recording? */ status |= 0x402; /* Yes, error, can't keep up, some data loss */ /* (Polling should generate the corresponding error message) */ } /* End of if recording */ else if (devinfo.NumDrives > 0 && ! devstatus.Playing && ! ldir.dfifo) { /* Not playing, not recording, but why? */ if (XLRGetDirectory(dev, &dir) != XLR_SUCCESS) { /* Directory OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Status() ERROR: XLRGetDirectory() returned error \n", me); status |= 0x002; /* Error */ } (void) errors(); if (dir.Full) status |= 0x080; /* Full media on record */ } /* End if else if not playing and not recording */ if (devstatus.Playing) status |= 0x108; /* Playing and delayed completion */ else if (ldir.playing || ldir.scanplaying) /* Not playing, but should we be? */ status |= 0x200; /* Yes, presumably halted by end of recording */ } /* End of else DeviceStatus OK */ if (datah > 0 && datah != 3) { /* Some other data transfer in progress? */ /* (datah = 3 is OK because no data transfer is in progress) */ status |= 0x008; /* Yes, but which one? */ if (datah == 6) /* disk2file? (bit 12) */ status |= 0x01000; /* Yes */ if (datah == 7) /* file2disk? (bit 13) */ status |= 0x02000; /* Yes */ if (datah == 4) /* disk2net? (bit 14) */ status |= 0x04000; /* Yes */ if ((datah == 1 || datah == 2) && ldir.n2ddoing > 0) /* net2disk? (bit 15) */ status |= 0x08000; /* Yes */ if (datah == 5) /* in2net? (bit 16) */ status |= 0x10000; /* Yes */ if ((datah == 1 || datah == 2) && ldir.n2odoing > 0) /* net2out? (bit 17) */ status |= 0x20000; /* Yes */ } /* End of if some other data transfer */ if (ldir.bmode) { /* Augmented output for bank mode? */ for (i = 0; i < 2; i++) { /* Yes, each bank */ if (XLRGetBankStatus(dev, i, &bkstat) != XLR_SUCCESS) { /* OK? */ status |= 0x002; /* Nope, error */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, "%s Status() ERROR: " /* Yes */ " XLRGetBankStatus() returned error \n", me); if ((error = XLRGetLastError()) != 3) { /* Errors? */ if (XLRGetErrorMessage(messg, error) != XLR_SUCCESS) /* Yes, get error message, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, "%s Status() ERROR: XLR error %lu, " "but XLRGetErrorMessage() failed \n", me, error); /* (We try to go on) */ msg = messg; /* Remember error message */ } } else { /* Bank status, bank i, info OK */ if (bkstat.Selected) /* Bank i selected? */ status |= 1 << (20 + 4 * i); /* Yes */ if (bkstat.State == STATE_READY) /* Bank i ready? */ status |= 1 << (21 + 4 * i); /* Yes */ if (bkstat.MediaStatus == MEDIASTATUS_FULL || /* Bank i full? */ bkstat.MediaStatus == MEDIASTATUS_FAULTED) /* Or faulty? */ status |= 1 << (22 + 4 * i); /* Yes */ if (bkstat.WriteProtected) /* Bank i write protected? */ status |= 1 << (23 + 4 * i); /* Yes */ } /* End of else OK status info */ } /* End of for i each bank */ } /* End of if bank mode */ if (error == 3) /* Error with message? */ (void) sprintf(outstring, "!status? 0 : %#010lx ;", status); /* Nope */ else /* Yes, error message found above */ (void) sprintf(outstring, "!status? 0 : %#010lx : %lu : %s ;", status, error, msg); error = 0; /* Reset for next time */ return(outstring); } /* End of Status() */ static char * Error() { /* error */ /* Should have '?' but no arguments, but we don't check */ if (error == 0 || error == 3) /* Pending error? */ error = XLRGetLastError(); /* No, get update */ /* Get error message? */ if (error < 1000 && /* Not error from elsewhere? */ XLRGetErrorMessage(messg, error) != XLR_SUCCESS) /* OK? */ return("!error? 4 : Error getting error message ;"); /* Error */ (void) sprintf(outstring, "!error? 0 : %lu : %s ;", error, messg); /* Error number and string */ error = 0; /* Reset for next time */ return(outstring); /* Normal end */ } /* End of Error() */ static char * Position() { /* position */ long long plapnt = sdir->plapnt; /* May be updated below */ /* Should have '?' but no arguments, but we don't check */ /* Check: No go in disk-FIFO mode */ if (ldir.dfifo) /* OK? */ return("!position? 6 : Can't work in disk-FIFO mode ;"); /* Nope, error */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status? */ (void) errors(); return("!position? 4 : Can't get device status ;"); /* Error */ } (void) errors(); /* (This should not be needed) */ /* Is playback on or off? * (If playing, then we assume that sdir->recpnt is unchanged; * if not playing, then we assume that plapnt is unchanged) */ if (devstatus.Playing) { /* Playing? */ plapnt += XLRGetPlayLength(dev); /* Yes, update play position */ if ((error = XLRGetLastError()) != 3) { /* Errors? */ (void) errors(); /* Yes */ return("!position? 4 : Can't get play length ;"); /* Error */ } } else { /* Not playing */ sdir->recpnt = XLRGetLength(dev); /* Record offset, bytes */ if ((error = XLRGetLastError()) != 3) { /* Errors? */ (void) errors(); /* Yes */ return("!position? 4 : Can't get record length ;"); /* Error */ } } /* Else OK */ if (forml) /* Formal parsing mode? */ (void) sprintf(outstring, "!position? 0 : %Ld : %Ld ;", /* Yes */ sdir->recpnt, plapnt); else /* Informal parsing mode; add "="; broken return for old FS */ (void) sprintf(outstring, "!position? = 0 : %Ld : %Ld ;", /* Yes */ sdir->recpnt, plapnt); return(outstring); /* Normal end */ } /* End of Position() */ static char * Disc_size() { /* disc_size or disk_size */ char line[512]; /* Scratch */ unsigned int i; /* Should have '?' but no arguments, but we don't check */ /* We need to be in bank mode, else dinfo[] is nonsense */ if (! ldir.bmode) { /* Bank mode? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc_size() WARNING: Not in bank mode \n", me); (void) sprintf(outstring, "!%s? 6 : Not in bank mode ;", ptr[0]); return(outstring); /* Error */ } (void) sprintf(outstring, "!%s? 0 ", ptr[0]); /* Start of line */ for (i = 0; i < 8; i++) { /* Each potential disk */ if (dinfo[i].Serial[0] == '\0') { /* OK? */ (void) strcat(outstring, ": "); /* No, assume no such drive */ continue; /* To next i */ } /* Else OK */ (void) sprintf(line, ": %Ld ", dinfo[i].Capacity * 512LL); /* Yes */ (void) strcat(outstring, line); } /* End of for i each disk */ (void) errors(); (void) strcat(outstring, ";"); /* After last disk */ return(outstring); } /* End of Disc_size() */ static char * Disc_serial() { /* disc_serial or disk_serial */ char line[512]; /* Scratch */ int j; unsigned int i; /* Should have '?' but no arguments, but we don't check */ /* We need to be in bank mode, else dinfo[] is nonsense */ if (! ldir.bmode) { /* Bank mode? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc_serial() WARNING: Not in bank mode \n", me); (void) sprintf(outstring, "!%s? 6 : Not in bank mode ;", ptr[0]); return(outstring); /* Error */ } if (forml) /* Formal parsing mode? */ (void) sprintf(outstring, "!%s? 0 ", ptr[0]); /* Yes, start of line */ else /* Informal parsing mode; add "="; broken return for old FS */ (void) sprintf(outstring, "!%s? = 0 ", ptr[0]); for (i = 0; i < 8; i++) { /* Each potential disk */ if (dinfo[i].Serial[0] == '\0') { /* OK? */ (void) strcat(outstring, ": "); /* No, assume no such drive */ continue; /* To next i */ } /* Else OK */ j = strspn(dinfo[i].Serial, " "); /* Starts with lots of spaces */ (void) sprintf(line, ": %s ", j+dinfo[i].Serial); (void) strcat(outstring, line); } /* End of for i each disk */ (void) errors(); (void) strcat(outstring, ";"); /* After last disk */ return(outstring); } /* End of Disc_serial() */ static char * Disc_error() { /* disc_error or disk_error */ /* Should have '?' but no arguments, but we don't check */ // ?? This section is not what ARW wants ?? /* One drive in error is reported in devstatus */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ (void) errors(); /* Nope, error in reading status */ (void) sprintf(outstring, "!%s? 4 : Can't get device status ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); if (devstatus.DriveFail) { /* Failure? */ (void) sprintf(outstring, "!%s? 0 : %d ;", /* Yes */ ptr[0], devstatus.DriveFailNumber); return(outstring); /* Some drive has failed */ } /* Else OK */ (void) sprintf(outstring, "!%s? 0 ;", ptr[0]); return(outstring); /* No drive is known to have failed */ } /* End of Disc_error() */ static char * Disc_state_mask() { /* disc_state_mask or disk_state_mask */ /* Notes: * ldir.dsmask & 0x1 = allow DMS to change to "Erased" * ldir.dsmask & 0x2 = allow DMS to change to "Played" * ldir.dsmask & 0x4 = allow DMS to change to "Recorded" */ /* * Disk_state_mask can be a query; check that first * */ if (pt != NULL) { /* Was it a query? */ (void) sprintf(outstring, "!%s? 0 : %d : %d : %d ;", ptr[0], /* Yes */ ldir.dsmask & 0x1, /* Erase_mask_enable */ (ldir.dsmask & 0x2) >> 1, /* Play_mask_enable */ (ldir.dsmask & 0x4) >> 2); /* Record_mask_enable */ return(outstring); } /* End of if query */ /* * Else not a query; command has three optional arguments * */ /* (We accept zero for FALSE or anything not zero for TRUE) */ if (ptr[1] != NULL && ptr[1][0] != '\0') { /* First argument? */ if (atoi(ptr[1])) /* Yes. TRUE? */ ldir.dsmask |= 0x1; /* Yes */ else /* FALSE */ ldir.dsmask &= 0x6; } if (ptr[1] != NULL && ptr[2] != NULL && ptr[2][0] != '\0') { /* Second argument? */ if (atoi(ptr[2])) /* Yes. TRUE? */ ldir.dsmask |= 0x2; /* Yes */ else /* FALSE */ ldir.dsmask &= 0x5; } if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[3][0] != '\0') { /* Third argument? */ if (atoi(ptr[3])) /* Yes. TRUE? */ ldir.dsmask |= 0x4; /* Yes */ else /* FALSE */ ldir.dsmask &= 0x3; } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc_state_mask() DEBUG: dsmask %#x \n", me, ldir.dsmask); (void) sprintf(outstring, "%s = 0 ;", ptr[0]); return(outstring); /* Normal end */ } /* End of Disc_state_mask() */ static char * Disc_state() { /* disc_state or disk_state (DMS) */ static char * states[] = /* Possible DMSs */ { "Unknown", "Recorded", "Played", "Erased", "Error" }; S_BANKSTATUS lbkstat[2]; /* Local version of bkstat */ char ldms[2][32]; /* Local copy of dms[] for each bank */ UINT obank; /* Other (not-selected) bank */ int i, k; /* * We must be in bank mode for disk_state * */ if (! ldir.bmode) { /* Bank mode? */ (void) sprintf(outstring, "!%s%s 6 : Not in bank mode ;", ptr[0], pt == NULL ? " =" : "?"); return(outstring); /* Error */ } /* * disk_state can be a query; check that first * */ if (pt != NULL) { /* Was it a query? */ /* * Yes, we need to find and show the DMS for both banks * */ for (i = 0; i < 2; i++) { /* Else OK, each bank */ if (XLRGetBankStatus(dev, i, lbkstat+i) != XLR_SUCCESS) { /* OK? */ (void) fprintf(stderr, "%s Disc_state() ERROR: " /* Nope */ " XLRGetBankStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s? 4 : Can't get bank status ;", ptr[0]); /* XLR error */ return(outstring); /* Error */ } (void) errors(); /* Else OK, got bank-status info */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc_state() DEBUG: Bank %c, Label %s, \n", me, bk[i], lbkstat[i].Label); (void) fprintf(stderr, " Length %Ld,", lbkstat[i].Length); if (lbkstat[i].State == STATE_NOT_READY) /* State? */ (void) fprintf(stderr, " STATE_NOT_READY,"); else if (lbkstat[i].State == STATE_TRANSITION) (void) fprintf(stderr, " STATE_TRANSITION,"); else if (lbkstat[i].State == STATE_READY) (void) fprintf(stderr, " STATE_READY,"); if (! lbkstat[i].Selected) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " Selected, \n "); if (! lbkstat[i].PowerRequested) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " PowerRequested,"); if (! lbkstat[i].PowerEnabled) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " PowerEnabled, \n "); if (lbkstat[i].MediaStatus == MEDIASTATUS_EMPTY) /* MediaStatus */ (void) fprintf(stderr, " MEDIASTATUS_EMPTY,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_NOT_EMPTY) (void) fprintf(stderr, " MEDIASTATUS_NOT_EMPTY,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_FULL) (void) fprintf(stderr, " MEDIASTATUS_FULL,"); else if (lbkstat[i].MediaStatus == MEDIASTATUS_FAULTED) (void) fprintf(stderr, " MEDIASTATUS_FAULTED,"); if (! lbkstat[i].WriteProtected) (void) fprintf(stderr, " NOT"); (void) fprintf(stderr, " WriteProtected,"); if (! lbkstat[i].ErrorCode) /* Error? */ (void) fprintf(stderr, " NO error \n"); else /* Error */ (void) fprintf(stderr, " Error %d %d \n", lbkstat[i].ErrorCode, lbkstat[i].ErrorData); (void) fprintf(stderr, " TotalCapacity %Ld \n", lbkstat[i].TotalCapacity * 4096LL); } /* End of if debuggery */ } /* End of for i each bank */ /* * Get DMS for selected bank * */ if ((k = getLabel(bank)) < 0) { /* OK? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc_state() ERROR: getLabel() bank %c returned %d \n", me, bk[bank], k); (void) sprintf(outstring, "!%s? 4 : Can't get bank state ;", ptr[0]); return(outstring); /* Error */ } /* * Else OK, make local copy * */ (void) strncpy(ldms[bank], dms, 32); /* * Get DMS for other bank * */ obank = (bank + 1) % 2; /* Other (not selected) bank */ if (getLabel(obank) == 0) /* OK? */ /* Yes, make local copy */ (void) strncpy(ldms[obank], dms, 32); /* (The error case is handled below) */ /* * Format for output * */ (void) sprintf(outstring, "!%s? 0 : %c : %s : %c : %s ;", ptr[0], lbkstat[bank].Selected ? bk[bank] : bk[3], /* Selected? */ /* (bank not selected is an error) */ lbkstat[bank].Selected ? ldms[bank] : states[4], /* State? */ lbkstat[obank].State == STATE_READY ? bk[obank] : bk[2], /* Ready? */ /* (obank not ready is OK, but then DMS is unknown) */ lbkstat[obank].State == STATE_READY ? ldms[obank] : states[0]); return(outstring); } /* End of if a query */ /* * Else not a query; command requires one argument * */ if (ptr[1] == NULL) { /* Argument? */ (void) sprintf(outstring, "%s = 8 : Must have argument ;", ptr[0]); return(outstring); /* Parameter error */ } /* * Check: Must have preceding 'protect=off' * */ if (prot != 1) { /* OK? */ (void) sprintf(outstring, "%s = 6 : Previous protect=off required ;", ptr[0]); /* Nope */ return(outstring); /* Error */ } /* * Check: Proper argument? * */ /* (We allow argument to be any proper DMS; should we?) */ for (i = 0; i < 5; i++) /* Each proper DMS in states[] */ if (strcasecmp(ptr[1], states[i]) == 0) /* This one? */ break; /* Yes, out of for i */ if (i >= 5) { /* Found OK? */ (void) sprintf(outstring, "%s = 8 : Illegal parameter ;", ptr[0]); return(outstring); /* Parameter error */ } /* * Else OK * */ if (getLabel(bank) < 0) { /* Get VSN (and old DMS), OK? */ (void) sprintf(outstring, "%s = 4 : Can't get Label ;", ptr[0]); return(outstring); /* Presumably XLR error */ } /* Else OK */ (void) strcpy(dms, states[i]); /* Copy new DMS */ if (setLabel() < 0) { /* Set new DMS, OK? */ (void) sprintf(outstring, "%s = 4 : Can't set Label ;", ptr[0]); return(outstring); /* Presumably XLR error */ } /* Else OK */ (void) sprintf(outstring, "%s = 0 ;", ptr[0]); return(outstring); /* Normal end */ } /* End of Disc_state() */ static char * Play_mode() { /* play_mode */ (void) outBoard(GET); /* Get output-board parameters */ /* Play_mode can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ /* Yes, outb.mode is vlbi or st, outb.tmode is 8, 16, 32, 64, * or 0 if unknown, outb.formt is mark4, vlba, or null if * unknown, all as read from the output board */ (void) sprintf(outstring, "!play_mode? 0 : %s : %s : %d : %c : %d ;", outb.mode, outb.formt, outb.tmode, outb.synced ? 'S' : '-', outb.numrs); return(outstring); /* Normal for this case */ } /* Play_mode has three optional arguments */ if (ptr[1] == NULL) { /* Arguments? */ (void) strcpy(outb.mode, "vlbi"); /* No, take all defaults */ (void) strcpy(outb.formt, "mark4"); outb.tmode = 32; } else if (strcasecmp(ptr[1], "vlbi") == 0 || strcasecmp(ptr[1], "st") == 0) /* First argument OK? */ (void) strcpy(outb.mode, ptr[1]); /* Yes */ else if (ptr[1][0] != '\0') /* Skip? */ return("!play_mode = 8 ;"); /* Nope, none of these is an error */ if (ptr[1] != NULL && ptr[2] != NULL) { /* Second argument? */ if (strcasecmp(ptr[2], "mark4") == 0 || /* Yes, OK? */ strcasecmp(ptr[2], "vlba") == 0) (void) strcpy(outb.formt, ptr[2]); /* Yes */ else if (ptr[2][0] != '\0') /* Take second-argument defaults? */ return("!play_mode = 8 ;"); /* Nope, none of these is an error */ } if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL) /* Third argument? */ outb.tmode = atoi(ptr[3]); /* Yes */ if (outb.tmode % 8 != 0 || outb.tmode > 64 || outb.tmode < 8) /* OK? */ return("!play_mode = 8 ;"); /* Nope, error */ (void) outBoard(SET); /* Else send to hardware */ return("!play_mode = 0 ;"); /* OK */ } /* End of Play_mode() */ static char * Data_check() { /* data_check, track_check, or scan_check */ static long long startb = 0; /* Remembered frame start byte */ static double startt = 0.0; /* Remembered frame start time, seconds */ static double timep = 0.0; /* Remembered frame period, seconds */ static unsigned int period = 0; /* Remembered frame period, bytes */ static char * strk[] = { "", " D", " ?" }; /* To print status of track */ struct DTim satime; /* Local copy of dattime */ static double ln2 = log(2.0); struct date timet; struct date * dt = &timet; time_t timex; /* From time() */ struct tm * tmpo; /* From gmtime() */ int mo, mda, nwd, juld; /* For daze() */ int jd, tmjd, mjd; /* Julian day numbers */ int nyr, nyrd; /* For dazl() */ long long skipped = 0; /* Bytes skipped since last time */ int skippedok = FALSE; /* Skipped OK? */ unsigned long plapnt[2]; /* Convert to/from long long */ long long * pplapnt = (long long *) plapnt; char line[512]; /* Scratch */ int st, ntracks, err, pr, trk, ff; int i, n, j, jmax = 1; int tmode; /* Same as outb.tmode? */ double timesec, dskip; double t0s, t0ss, t64s, t64ss, dtt; /* Decoded frame times */ double freq, freq2; /* Here are some notes on all these trk* variables. Who's confused? * Some of these are in this function, some in FFHeader(), some global. * outb.trka is the track number, 2--33 and 102--133 notation, used in * track check and from which some of the others derive * trk is the bit number, 0--63, corresponding to outb.trka * or is -1 to signify not doing track_check * trkm is the modified trk where a duplicated track should come from, * that is where to find the track to duplicate * trkt (global) is the track number decoded from a frame header * trkd (global) is trkt converted to 2--33, 102--133 notation * trkv is the calculated trkt, that is what trkt should be in the * simplest case, that is no duplication * trkc is the track that should have been duplicated to this track * in header notation, that is trkc = trkt if correctly duplicated * trke (global) is 0 if we found the correct track, no error * or is 1 if we found a correctly duplicated track * or is 2 if we found the wrong track; an error */ /* Should have '?', but we don't check */ /* * Check: OK to try a read? * */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ (void) sprintf(outstring, /* Nope */ "!%s? 6 : Can't read while data transfer in progress ;", ptr[0]); return(outstring); /* Error, must wait for other activity to finish */ } if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s? 4 : Can't get device status ;", ptr[0]); return(outstring); /* XLR error */ } if (devstatus.Recording || devstatus.Playing) { /* Recording or Playing? */ (void) sprintf(outstring, /* Yes */ "!%s? 6 : Not while recording or playing ;", ptr[0]); return(outstring); /* Error, must wait for other activity to finish */ } /* (Else OK to read) */ /* * If scan_check, then at least one scan must be in directory * */ if (strcasecmp(ptr[0], "scan_check") == 0 && /* Scan_check? */ sdir->nscans <= 0) { /* Do we have any scans? */ (void) sprintf(outstring, "!%s? 6 : No scans ;", ptr[0]); /* Nope */ return(outstring); /* Error */ } /* * SS mode to PCI for reading * */ if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* Set mode, OK? */ (void) fprintf(stderr, /* Nope */ "%s Data_check() ERROR: \007 XLRSetMode() returned error \n", me); if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() WARNING: XLRSetMode() returned error \n", me); (void) errors(); /* Rule: No effect on playback index */ if (XLRStop(dev) != XLR_SUCCESS) /* Stop, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() WARNING: XLRStop() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s? 4 : Can't XLRStop() ;", ptr[0]); return(outstring); /* Error */ } /* Revised: data_check, track_check, and scan_check all come here; * we need to determine which is wanted. Track_check uses a single * track specified by outb.trka from Track_select(). Scan_check * uses two passes of the central part of data_check. The following * parameters are used in FFHeader() below. */ if (strcasecmp(ptr[0], "track_check") == 0) /* Track_check? */ trk = outb.trka - (outb.trka > 100 ? 70 : 2); /* Yes, set bit number */ /* Check this bit only (-100 + 32 - 2 = -70) */ /* But this is not the correct bit number for all cases * because for ntracks < 64, some bits fan out to more * than one track. See trkm in FFHeader() below. */ else /* data_check or scan_check; take default */ trk = -1; readdesc.BufferAddr = rbuff; /* Read buffer */ readdesc.XferLength = 4 * BUFFL; /* Bytes */ /* rbuff[] is now larger than this, but we can't go much farther * (i.e., than 1 MByte) without falling off the end of the scan */ /* Scan_check uses two reads, one at the beginning of the scan, * one near the end, and compares the results */ if (strcasecmp(ptr[0], "scan_check") == 0) /* Scan_check? */ jmax = 2; /* Yes, two reads */ /* (Else jmax = 1) */ /* * One or two reads * */ for (j = 0; j < jmax; j++) { /* Each loop */ if (jmax > 1 && j == 0) /* First of two loops? */ *pplapnt = startbyte; /* Yes, start of this scan */ /* Formerly: *pplapnt = sdir->start[sdir->n]; */ else if (jmax > 1 && j == 1) /* Second of two loops? */ *pplapnt = endbyte - 1000000; /* Yes, a megabyte back from the end of this scan */ /* Formerly: *pplapnt = sdir->start[sdir->n] + * sdir->length[sdir->n] - 1000000; */ else /* jmax = 1, one loop */ *pplapnt = sdir->plapnt; /* Just use plapnt (not startbyte) */ if (*pplapnt < 0) /* Negative plapnt is no fair */ *pplapnt = 0; /* (?? This really should be an error ??) */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() DEBUG: plapnt %Ld \n", me, *pplapnt); plapnt[0] &= 0xfffffff8; /* Now 8-byte aligned */ readdesc.AddrHi = plapnt[1]; readdesc.AddrLo = plapnt[0]; if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() DEBUG: AddrHi %lu, AddrLo %lu, XferLength %lu \n", me, readdesc.AddrHi, readdesc.AddrLo, readdesc.XferLength); /* Now read */ if (XLRRead(dev, &readdesc) != XLR_SUCCESS) { /* Read into rbuff, OK? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() WARNING: XLRRead() returned error \n", me); (void) errors(); /* Rule: No effect on playback index */ if (XLRStop(dev) != XLR_SUCCESS) /* Stop, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() WARNING: XLRStop() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s? 4 : Can't XLRStop() ;", ptr[0]); return(outstring); /* Error */ } /* * Else OK; try to find VLBI data in this data stream * */ /* First check for Mark-5B data */ foff = 0; /* Initialize */ for (i = 0; i < 65; i++) { /* Get 65 headers */ /* (That's 64 frames from first to last header) */ pdata = rbuff + foff; /* Start of next scan, 32-bit words */ /* Find next Mark-5B header in buffer */ if (FFH() != 0) { /* OK? */ if (msglev < 1) /* Nope, presume end or error. Debuggery? */ (void) fprintf(stderr, "%s Data_check() DEBUG: " " Mark5B FFH() returned error \n", me); /* (FFH() has presumably already printed an error message) */ break; /* Out of for i, get 65 headers */ } else if (i == 0) /* OK, first header? */ (void) memcpy((void *) &header0, (void *) &header, /* Yes */ sizeof(struct Header)); /* Save it */ else if (i == 64) /* 64th header? */ (void) memcpy((void *) &header64, (void *) &header, /* Yes */ sizeof(struct Header)); /* Save it */ /* (These saved headers are used below) */ foff += 4; /* Bump foff over frame header to start next read */ /* (FFH() has already bumped foff over scanned data) */ } /* End of for i get 65 headers */ /* * If we found 65 headers, then assume Mark5B; but check: * * * * Calculate parameters from differences between 64th and * * * * 0th frame headers, that is 64 between-header intervals * */ while (i > 64 && /* Found Mark5B and */ (j = (int) header64.frameno - header0.frameno) == 64) { /* Nums OK? */ /* Yes, calculate time duration of the 64 headers */ (void) sscanf(header0.tcword1, "%3d", &mjd); (void) sscanf(header0.tcword1 + 3, "%5lf", &t0s); (void) sscanf(header0.tcword2, "%4lf", &t0ss); (void) sscanf(header64.tcword1 + 3, "%5lf", &t64s); (void) sscanf(header64.tcword2, "%4lf", &t64ss); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: t0s %.1f, t0ss, %.5f, t64s %.1f, t64ss %.5f \n", me, t0s, t0ss * 1.0e-4, t64s, t64ss * 1.0e-4); dtt = t64s + t64ss * 1.0e-4 - t0s - t0ss * 1.0e-4; /* Time offset, sec */ if (dtt <= 0.0) { /* OK? */ if (msglev < 1) /* Not OK, debuggery? */ (void) fprintf(stderr, "%s ERROR: dtt %e \n", me, dtt); /* Yes */ break; /* Error, out of while found Mark5B */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: Time per frame %.6e seconds \n", me, dtt / 64.0); /* Calculate the corresponding data rate in words/second. * Note that this is NOT necessarily the track data rate */ freq = 0.0025 / dtt; /* Times 64 MHz */ /* (0.0025 seconds is the time for 64 frames at 64 MHz. * Legal values for freq are 1.0, 0.5, 0.25, 0.125, and 0.0625, * but we don't check) */ if (freq > 0.5) /* Over 32 MHz? */ (void) fprintf(stderr, /* Yes */ "%s WARNING: freq %.9f MHz is over 32 MHz \n", me, 64.0 * freq); n = (int) (log(freq) / ln2 - 0.5); /* (The -0.5 gives roundoff to a negative integer. * Legal values for n are 0, -1, -2, -3, and -4, * but we don't check) */ freq2 = exp(n * ln2); /* Times 64 MHz */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: Data rate %.6f or %.1f Mwords/sec \n", me, 64.0 * freq, 64.0 * freq2); if (fabs(freq - freq2) > 1.0e-8) /* OK? */ (void) fprintf(stderr, /* Nope */ "%s WARNING: freq %.9f MHz is a non-standard value \n", me, 64.0 * freq); dt->second = t0s + t0ss * 1.0e-4; /* To seconds, others are 0 */ /* (Note dt = &timet) */ /* We have only the mjd, which here means the last 3 digits of the * Julian day number. We assume that the most recent previous * date when the Julian day number had these last 3 digits gives * the correct date--year and day of the year. The following * rigmarole is to find this date, which will be wrong if the * data are more than about 1000 days = 2.74 years old. */ timex = time((time_t *) NULL); /* What time is it? */ tmpo = gmtime(&timex); /* Convert to date and time */ tmpo->tm_year += 1900; /* To conventional year */ tmpo->tm_yday++; /* To day of the year */ daze_(&tmpo->tm_year, &tmpo->tm_yday, &mo, &mda, &nwd, &juld); /* Get juld, today's Julian day number */ tmjd = juld % 1000; /* Today's equivalent mjd (3 digits) */ jd = juld - tmjd + mjd - (tmjd < mjd ? 1000 : 0); /* Now jd is our best guess for the Julian day number for the data */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() DEBUG: juld %d, mjd %d, jd %d \n", me, juld, mjd, jd); dazl_(&jd, &nyr, &mo, &mda, &nyrd, &nwd); /* Get year and day of the year from this Julian day number */ dt->year = nyr; /* (To shorts) */ dt->day = nyrd; /* dt->second has all the rest */ dt->hour = (int)(dt->second/3600.0); dt->second -= dt->hour*3600.0; dt->minute = (int)(dt->second/60.0); dt->second -= dt->minute*60.0; if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() DEBUG: nyr %d, nyrd %d, %2.2d:%2.2d:%05.2f \n", me, nyr, nyrd, dt->hour, dt->minute, dt->second); (void) sprintf(outstring, /* Print some info therefrom */ "!%s? 0 : %d : %s : %s : : %s : %.3f ;", ptr[0], sdir->n + 1, sdir->scanName[sdir->n], "mark5B", date2string(timet), 64.0 * freq); /* (dt = &timet) */ return(outstring); /* Normal end for this case */ } /* End of while found Mark5B and numbers OK */ /* We get here in case not Mark5B */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() DEBUG: Not Mark5B \n", me); /* So we assume Mark5A, that parity could be either stripped * (st = FALSE) or not (st = TRUE), and that the number of tracks, * ntracks, might be 8, 16, 32, or 64 tracks. We need to try 5 of * these 8 cases, preferably in order of likelyhood. (Don't be * confused: st does not mean stripped, instead st stands for * straight through, which means that parity was not stripped.) */ for (st = 1; st >= 0; st--) { /* Straight through? */ for (ntracks = 64; ntracks >= 8; ntracks /= 2) { /* Number of tracks */ /* Certain cases are impossible; check for these first */ if (st && ntracks != 32) /* ST and not 32? */ continue; /* Yes, to next ntracks */ if ((ff = FFHeader(&readdesc, &dattime, st, ntracks, trk)) >= 0) /* Find VLBI header, OK? */ break; /* Yes, out of for ntracks */ } /* End of for ntracks */ if (ntracks >= 8) /* Found? */ break; /* Yes, out of for st */ } /* End of for st */ if (st < 0) /* Did we find VLBI data? */ break; /* Nope, out of for j */ /* Else here we have VLBI data with one or several frame headers * found, and we know st (parity or not) and ntracks and some other * stuff. Note that ff > 0 means that frame periods are unknown */ /* Calculate the frame header time in seconds */ timesec = dattime.timet.day*86400.0 + dattime.timet.hour*3600.0 + dattime.timet.minute*60.0 + dattime.timet.second; /* Check: Same byte period and time period as last time? */ if (ff == 0 && period == dattime.period && fabs(timep - dattime.timep) < 0.0000001) { /* Yes, calculate the skipped parameter */ dskip = (timesec - startt) / timep; /* (This should be an integer number of frame headers * because the times are each read from a frame header) */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() DEBUG: dskip %.5f \n", me, dskip); skipped = startb + period * (long long) (dskip + (dskip < 0.0 ? -0.5 : 0.5)) - /* (That (long long) business avoids some round-off errors) */ (long long) (*pplapnt + dattime.offset); /* (That's ought-to-be minus is) */ skippedok = TRUE; } else { /* No previous frame header or an incorrect one */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() DEBUG: Previous frame header differs \n" " period %d, dattime.period %Ld, " "timep %.9f, dattime.timep %.9f \n", me, period, dattime.period, timep, dattime.timep); skippedok = FALSE; /* (This is not an error) */ } /* * Save parameters for next time * */ if (jmax > 1 && j == 0) /* First of two loops? */ (void) memcpy(&satime, &dattime, sizeof(struct DTim)); /* Yes, save a copy of dattime in satime */ startb = *pplapnt + dattime.offset; startt = timesec; period = dattime.period; timep = dattime.timep; } /* End of for j each loop */ /* * We're done reading, so ... * */ if (XLRStop(dev) != XLR_SUCCESS) /* Stop, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() WARNING: XLRStop() returned error \n", me); /* We try to go on */ (void) errors(); if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) /* Back to default mode, OK? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() WARNING: XLRSetMode() returned error \n", me); /* We try to go on */ (void) errors(); if (st < 0) { /* Did we find VLBI data? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Data_check() WARNING: VLBI data not found \n", me); /* Not VLBI, reset saved parameters */ startb = 0; startt = 0.0; period = 0; timep = 0.0; err = PatScan(&readdesc); /* Not VLBI, try for SS or TVG pattern */ /* Note: err = -2 means found SS pattern * err = -3 means not a known pattern * err = -4 means found TVG pattern * Also note that PatScan() now also returns pati, patj, and patlen */ if (jmax < 2 && err == -2) /* Normal and SS pattern? */ (void) sprintf(outstring, "!%s? 0 : ss : %ld : %ld : %ld ;", ptr[0], pati, patj, patlen); /* Yes */ else if (jmax > 1 && err == -2) /* Scan_check and SS pattern? */ (void) sprintf(outstring, /* Yes */ "!%s? 0 : %d : %s : ss : %ld : %ld : %ld ;", ptr[0], sdir->n + 1, sdir->scanName[sdir->n], pati, patj, patlen); else if (jmax < 2 && err == -4) /* Normal and TVG pattern? */ (void) sprintf(outstring, "!%s? 0 : tvg : %ld : %ld : %ld ;", /* Yes */ ptr[0], pati, patj, patlen); else if (jmax > 1 && err == -4) /* Scan_check and TVG pattern? */ (void) sprintf(outstring, /* Yes */ "!%s? 0 : %d : %s : tvg : %ld : %ld : %ld ;", ptr[0], sdir->n + 1, sdir->scanName[sdir->n], pati, patj, patlen); else if (jmax < 2 && err == -3) /* Normal and unknown? */ (void) sprintf(outstring, "!%s? 0 : ? ;", ptr[0]); /* Yes */ else /* Scan_check and unknown */ (void) sprintf(outstring, "!%s? 0 : %d : %s : ? ;", /* Yes */ ptr[0], sdir->n + 1, sdir->scanName[sdir->n]); return(outstring); /* Not VLBI */ } if (st) /* Straight through found? */ (void) strcpy(line, "st : "); /* Yes */ else /* Not straight through */ line[0] = '\0'; tmode = ntracks; /* Number of tracks found */ /* (formt should have been set by FFHeader() above) */ pr = 0; /* Initialize */ if (strcasecmp(formt, "mark4") == 0) { /* Output format Mark 4? */ pr = 5; /* Yes, precision for print (formerly 3) */ (void) strcat(line, formt); } else if (strcasecmp(formt, "vlba") == 0) { /* VLBA? */ pr = 4; /* Yes, precision for print */ (void) strcat(line, formt); } else /* Don't know */ (void) strcat(line, "?"); if (! st) /* Not ST? */ (void) sprintf(line+strlen(line), " : %d", tmode); /* Add ntracks */ /* Format output */ if (trk >= 0) { /* Case track_check? */ if (skippedok) /* Known skipped? */ (void) sprintf(outstring, /* Yes */ "!%s? 0 : %s : %s : %Ld : %.*fs : %.3f : %d%s : %Ld ;", ptr[0], line, date2string(dattime.timet), dattime.offset, pr, dattime.timep, 0.02 / dattime.timep, trkd, strk[trke], skipped); else if (ff == 0) /* Unknown skipped but OK periods? */ (void) sprintf(outstring, /* Yes */ "!%s? 0 : %s : %s : %Ld : %.*fs : %.3f : %d%s : ;", ptr[0], line, date2string(dattime.timet), dattime.offset, pr, dattime.timep, 0.02 / dattime.timep, trkd, strk[trke]); else /* Unknown skipped and periods */ (void) sprintf(outstring, "!%s? 0 : %s : %s : %Ld : ? : ? : : ;", ptr[0], line, date2string(dattime.timet), dattime.offset); /* ?? We also don't know the track number--fix this later ?? */ } /* End of if track_check */ else if (jmax < 2 && skippedok) /* Normal and known skipped? */ (void) sprintf(outstring, /* Yes */ "!%s? %s0 : %s : %s : %Ld : %.*fs : %Ld : %Ld ;", ptr[0], forml ? "" : "= ", line, date2string(dattime.timet), dattime.offset, pr, dattime.timep, dattime.period, skipped); else if (jmax > 1 && skippedok) /* Scan_check and known skipped? */ (void) sprintf(outstring, /* Yes */ "!%s? 0 : %d : %s : %s : %s : %.1fs : %.3f : %Ld ;", ptr[0], sdir->n + 1, sdir->scanName[sdir->n], line, date2string(satime.timet), dattime.timep * (endbyte - startbyte) / dattime.period, /* Formerly: dattime.timep*sdir->length[sdir->n]/dattime.period, */ 0.02 / dattime.timep, skipped); /* (0.02 = 20000 / 10^6 to megabaud) */ else if (jmax < 2 && ff == 0) /* Normal but skipped unknown? */ /* (Use a blank to mean unknown but probably not an error, * use '?' to indicate unknown and a probable error) */ (void) sprintf(outstring, /* Yes */ "!%s? %s0 : %s : %s : %Ld : %.*fs : %Ld : ;", ptr[0], forml ? "" : "= ", line, date2string(dattime.timet), dattime.offset, pr, dattime.timep, dattime.period); else if (jmax < 2 && ff > 0) /* Normal but skipped and periods unknown? */ (void) sprintf(outstring, /* Yes */ "!%s? %s0 : %s : %s : %Ld : ? : ? : ;", ptr[0], forml ? "" : "= ", line, date2string(dattime.timet), dattime.offset); else /* Scan_check and skipped unknown--should be an error */ (void) sprintf(outstring, "!%s? 0 : %d : %s : %s : %s : ? : ? : ? ;", ptr[0], sdir->n + 1, sdir->scanName[sdir->n], line, date2string(satime.timet)); return(outstring); /* Normal end */ } /* End of Data_check() */ static char * Dir_info() { /* dir_info */ /* Should have '?' but no arguments, but we don't check */ /* Check: No go in disk-FIFO mode */ if (ldir.dfifo) /* OK? */ return("!dir_info? 6 : Can't work in disk-FIFO mode ;"); /* Nope, error */ /* First we need to update various data. We should update devinfo, * but that doesn't work during recording, so we assume that it was * already set correctly */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status? */ (void) errors(); return("!dir_info? 4 : Can't get device status ;"); /* Error */ } (void) errors(); /* Is playback on or off? */ if (! devstatus.Playing) /* Not playing? */ sdir->recpnt = XLRGetLength(dev); /* Yes, record offset, bytes */ /* (If playing, we assume that sdir->recpnt is already set) */ (void) errors(); (void) sprintf(outstring, "!dir_info? 0 : %d : %Ld : %Ld ;", sdir->nscans, sdir->recpnt, devinfo.TotalCapacity * 4096LL); return(outstring); /* Normal end */ } /* End of Dir_info() */ static char * Rtime() { /* rtime */ double freq, dtime, drate, pleft; long long bleft; /* Bytes left to be recorded */ long long len, length, capacity; char line[16]; char * mode = NULL; char * submode = NULL; int tmode = 0; /* Copy of outb.tmode */ unsigned int i, iend, bus, ms; /* Should have '?' but no arguments, but we don't check */ /* We can't work in disk-FIFO mode */ if (ldir.dfifo) /* OK? */ return("!rtime? 6 : Can't work in disk-FIFO mode ;"); /* Nope, error */ /* We should update devinfo, but that doesn't work during recording, * so we assume that it was already set correctly */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status? */ (void) errors(); return("!rtime? 4 : Can't get device status ;"); /* Error */ } (void) errors(); /* Is playback on or off? */ if (! devstatus.Playing) /* Not playing? */ sdir->recpnt = XLRGetLength(dev); /* Yes, record offset, bytes */ /* (If playing, we assume that sdir->recpnt is already set) */ (void) errors(); (void) inBoard(GET); /* Read input board parameters */ (void) outBoard(GET); /* And output-board parameters */ /* Calculate remaining recording time (if possible) */ if (strcasecmp(inb.mode, "st") == 0) { /* ST mode? */ tmode = 32; /* Yes, ST mode has 32 tracks */ freq = 1.125; /* 9/8 for parity */ mode = inb.mode; submode = outb.formt; if (strcasecmp(outb.formt, "vlba") == 0) /* VLBA? */ freq *= 1.008; /* Yes, 126/125 for VLBA */ } else if (strcasecmp(inb.mode, "vlbi") == 0 || /* VLBI modes? */ strcasecmp(inb.mode, "vlba") == 0 || strcasecmp(inb.mode, "mark4") == 0) { tmode = inb.ntracks; /* Yes */ mode = inb.mode; if (strcasecmp(inb.mode, "vlba") == 0) /* VLBA? */ freq = 1.008; /* Yes, 126/125 for no data replacement */ else /* Not VLBA */ freq = 1.0; /* No parity */ /* (Leave submode NULL; use tmode) */ } else { /* Not VLBI */ freq = 1.0; /* No parity */ mode = inb.mode; tmode = 32; /* (Leave submode NULL; use tmode) */ } freq *= outb.freq; /* Now MHz (but might be 0) */ /* Check: Can we do the calculations below? */ if (devinfo.NumDrives == 0 || devinfo.TotalCapacity == 0) /* OK? */ return("!rtime? 6 : Can't do calculations ;"); /* Nope, error */ /* Find a revised capacity instead of devinfo.TotalCapacity and rate */ length = sdir->recpnt / devinfo.NumDrives; /* Initialize */ if (ldir.bmode) /* Bank mode? */ iend = 8; /* Yes */ else /* Not bank mode */ iend = 16; for (i = 0; i < iend; i++) { /* Each potential disk */ if (dinfo[i].Serial[0] == '\0') /* OK? */ continue; /* No, assume no such drive, to next i */ bus = i / 2; /* Bus number */ ms = i % 2; /* Assume one master and one slave per bus */ /* (XLR_MASTER_DRIVE = 0, XLR_SLAVE_DRIVE = 1) */ len = XLRGetDiskLength(dev, bus, ms); /* Recorded length on this disk */ if (len == SS_UNDEFINED_LENGTH && /* Not OK? */ ((error = XLRGetLastError()) == i+160 || /* "drive missing/failing"? */ /* (This occurs for slave disks in all-master sets) */ error == i+168 || /* ?? SS looks at this disk too, SS error? ?? */ error == 10)) /* "Invalid request for system mode"? */ /* (This occurs when recording is in progress) */ error = 0; /* Presumably no slave disk; forget it */ else /* Some other error? */ (void) errors(); if (len != SS_UNDEFINED_LENGTH && msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Rtime() DEBUG: Disk %d, len %Ld \n", me, i, len); if (len > length && length > 131072LL) { /* "Long" disk? */ if (msglev < 1 && len > length + 131072LL) /* Yes, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Rtime() WARNING: Disk %d, len %Ld > %Ld \n", me, i, len, sdir->recpnt / devinfo.NumDrives); length = len; /* "Long" disk (so far) */ } } /* Now length is the number of bytes recorded on the "longest" disk, and * we assume that the game will stop when the "longest" disk is filled. * This does not take into account the increased rate that this disk * is (probably) being filled, so we also multiply drate by * length * devinfo.NumDrives / sdir->recpnt on the assumption that * this gives the increased rate for this "longest" disk. */ capacity = 4096LL * devinfo.TotalCapacity; /* To bytes, no correction */ bleft = MAX(0LL, capacity - length * devinfo.NumDrives); /* Corrected bytes left but not < 0 */ pleft = 100.0 * bleft / capacity; /* % left */ if (submode == NULL) { /* Encode submode? */ (void) sprintf(line, "%d", tmode); /* Yes */ submode = line; } if (freq <= 0.0) { /* Unknown freq? */ (void) sprintf(outstring, /* Yes, error, can't compute time */ "!rtime? 4 : ? : %.9f : %.1f : %s : %s : ? : ? : " "Don't know frequency ;", bleft * 1.0e-9, pleft, mode, submode); } else { /* Known freq */ drate = freq * tmode; /* Mbaud (all tracks) */ if (sdir->recpnt > 0) drate *= (double) length * devinfo.NumDrives / sdir->recpnt; /* To compensate for slow disks---notes above */ dtime = bleft * 8.0e-6 / drate; /* Seconds left */ /* (That's / 8 bits/byte) */ (void) sprintf(outstring, "!rtime? 0 : %.1f : %.9f : %.1f : %s : %s : %.3f : %.1f ;", dtime, bleft * 1.0e-9, pleft, mode, submode, outb.freq, freq * tmode); } return(outstring); /* Normal end */ } /* End of Rtime() */ static char * Set_scan() { /* set_scan or scan_set */ static char spar[MAXLENGTH] = ""; /* To save previous ptr[1] */ static char unl = (UNL)[0]; /* '_' Works! */ char ptr1[MAXLENGTH]; /* Local temporary copy of ptr[1] */ char scanName[MAXLENGTH] = ""; /* Local temporary copy of scan label */ char * ptf[4]; /* Subfields in scan label, if any */ int i, j, jj, ff = 13; double add; unsigned long long addbyte; char * ptc, * ptcc; /* Scratch */ /* * Set_scan or scan_set can be a query; check that first * */ if (pt != NULL) { /* Was it a query? */ if (sdir->nscans > 0) /* Yes, do we have any scans? */ (void) sprintf(outstring, "!%s? 0 : %d : %s : %Ld : %Ld ;", /* Yes */ ptr[0], MIN(sdir->nscans, sdir->n + 1), sdir->scanName[sdir->n], startbyte, endbyte); else /* No scans */ (void) sprintf(outstring, "!%s? 6 : No scans ;", ptr[0]); /* Error */ return(outstring); /* End of if query */ } /* * Else not a query. Check: Can we do it? * */ if (sdir->nscans <= 0) { /* OK? */ (void) sprintf(outstring, "!%s = 6 : No scans ;", ptr[0]); /* Nope */ return(outstring); /* Error */ } /* We need to check devstatus */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't get device status ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); /* We can't do scan_set if any data transfers are active and in progress * (because scan_set might change their parameters) */ if (datah > 3 || datah == 0 && (devstatus.Recording || devstatus.Playing)) { /* Other data transfer in progress? (Note this allows scan_set=... * when disk2net is connected but not sending) */ if (msglev < 2) /* Yes, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() ERROR: Data transfer in progress \n", me); (void) sprintf(outstring, "!%s = 6 : Data transfer in progress ;", ptr[0]); return(outstring); /* Error */ } /* Clear to normal-play option */ if (XLRClearOption(dev, SS_OPT_PLAYARM) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() ERROR: \007 XLRClearOption() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't clear playarm option ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); /* * Zero, one, two, or three arguments * */ if (ptr[1] == NULL || ptr[1][0] == '\0') { /* First argument? */ sdir->n = sdir->nscans - 1; /* No, default to last scan */ if (msglev <= 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() DEBUG: Setting sdir->n to %d \n", me, sdir->n); } else { /* Got first argument */ /* This argument can be a scan label, perhaps abbreviated and with * underlines, a scan number, "inc", "dec", or "next". We need to check * for "next" first because that causes a replacement of the first * parameter. (This does not preclude a scan whose name is "next"; * use an underline: 'scan_set=next.') (And ldir.scann is global * so it can be reset on bank change.) */ if (strcasecmp(ptr[1], "next") == 0) /* "Next"? */ (void) strncpy(ptr1, spar, sizeof(ptr1)); /* Yes */ /* So use spar = previous ptr[1], and don't change scann or spar */ else { /* Normal case, not "next" */ (void) strncpy(ptr1, ptr[1], sizeof(ptr1)); /* No, save working copy */ (void) strncpy(spar, ptr[1], sizeof(spar)); /* And for 'next' time */ ldir.scann = 0; /* Search from the first scan */ } /* Now search for a matching label. (Never mind why we do this first.) * Case: no underlines in first argument; match any part of scan label * Case: at least one underline; match only corresponding part(s) */ if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, "%s Set_scan() DEBUG: nscans %d \n", /* Yes */ me, sdir->nscans); ptc = ptr1; /* Initialize to start of copy of first argument */ for (j = 0; j < 4; j++) { /* Find up to 4 subfields in first argument */ ptf[j] = ptc; /* This subfield starts here */ if ((ptcc = strchr(ptc, unl)) != NULL) { /* Another underline? */ /* Yes, found (another) underline in first argument */ ptcc[0] = '\0'; /* Mark end of this subfield */ /* (Else subfield extends to end of first argument) */ /* (ptc and ptcc might enclose a null subfield---that's OK) */ ptc = ptcc + 1; /* For start of next loop */ } else /* No more underlines, no more subfields */ break; /* Out of for j */ /* Else loop to next j */ } /* End of for j subfields in first argument */ /* Now j+1 has number of subfields found in first argument */ if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, "%s Set_scan() DEBUG: j %d \n", me, j); /* Yes */ if (j == 0) { /* No underlines, only one subfield in first argument? */ for (i = ldir.scann; i < sdir->nscans; i++) /* Yes, each scan from scann */ if (strcasestr(sdir->scanName[i], ptr1) != NULL) /* Match? */ /* (Note that we ignore case, and this is a "containing" match, * that is, we search the whole scan label. This matches * the legacy behavior. */ break; /* Yes, match. Out of for i */ } else { /* j > 0; found j+1 subfield(s) to be matched in scan label */ for (i = ldir.scann; i < sdir->nscans; i++) { /* Each scan from scann */ (void) strncpy(scanName, sdir->scanName[i], sizeof(scanName)); /* Local temporary copy of scan label to trash */ ptc = scanName; /* Initialize to start of scan label */ for (jj = 0; jj <= j; jj++) { /* Each subfield in first argument */ if ((ptcc = strchr(ptc, unl)) != NULL) /* Underline in scanName[]? */ /* Yes, found (another) underline in scanName[] */ ptcc[0] = '\0'; /* Mark end of this subfield */ /* (Else last subfield extends to end of scanName[]) */ /* So now ptc points to the start, and, if need be, ptcc marked * the end of this subfield (which might be empty) in scanName[] */ if (strcasestr(ptc, ptf[jj]) == ptc) { /* Check, match? */ /* Use == ptc for "starting-with" match or * use != NULL for "containing" match. * And see man pages for case ptf[] ("needle") is empty string */ if (ptcc != NULL) /* Yes, match. Underline in scanName[]? */ ptc = ptcc + 1; /* Yes, this ptc for next jj loop */ else /* No more underlines in scanName[]; end of check subfields */ ptc = ""; /* An empty ptc[] matches only an empty ptf[] */ continue; /* To next jj */ } else /* No match, so not this scan */ break; /* Out of for jj (to next i) */ } /* End of for jj each subfield */ if (jj > j) /* Matching this scan? */ break; /* Yes, out of for i */ /* Else try next i */ } /* End of for i each scan */ /* (i may be used below) */ } /* End of else j > 0, found subfields */ /* Here i < sdir->nscans means that we found a match with scan i. * (i may be used below) */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() DEBUG: i %d, nscans %d, j %d, jj %d \n", me, i, sdir->nscans, j, jj); /* But check first for "inc" or "dec" * (in case a scan name contains "inc" or "dec") */ if (strcasecmp(ptr1, "inc") == 0) /* Increment? */ sdir->n = (sdir->n + 1) % sdir->nscans; /* Yes */ else if (strcasecmp(ptr1, "dec") == 0) /* Decrement? */ sdir->n = (sdir->n + sdir->nscans - 1) % sdir->nscans; /* Yes */ /* (sdir->nscans is known to be > 0) */ /* Else check for a scan number (in case ptr[1] is all numeric) */ else if (strspn(ptr1, "0123456789") == strlen(ptr[1]) && /* Numeric? */ (j = atoi(ptr[1])) > 0 && j <= sdir->nscans) /* And a scan number? */ sdir->n = j - 1; /* Yes */ /* Otherwise did we find the matching name, i, above? */ else if (i < sdir->nscans) /* Found? */ sdir->n = i; /* Yes */ else { /* None of the above is an error */ (void) sprintf(outstring, "!%s = 6 : No such scan ;", ptr[0]); return(outstring); /* Error */ } } /* End of else got first argument */ /* * Now we know scan number, sdir->n, so set defaults * */ ldir.scann = (sdir->n + 1) % sdir->nscans; /* Remember next scan number in case 'next' next time */ if (ptr[1] != NULL && ptr[1][0] != '\0' && /* First argument? */ strcasecmp(ptr[1], "next") != 0) /* And "Next" this time? */ (void) strncpy(spar, ptr[1], sizeof(spar)); /* No */ /* So remember this ptr[1] in case 'next' next time */ startbyte = sdir->plapnt = sdir->start[sdir->n]; /* Start of scan */ endbyte = sdir->start[sdir->n] + sdir->length[sdir->n]; /* End of scan */ /* We also set the (default) filename to the scan label */ (void) strncpy(ldir.filenam, sdir->scanName[sdir->n], MAXNAME); /* But we delete experiment name, source name, and station code * as separated by spaces (' '), if any. This is for legacy only */ if ((ptc = strchr(ldir.filenam, ' ')) != NULL) /* Find ' ', if any */ ptc[0] = '\0'; /* Yes, end filenam here */ /* We append ".mk5" unless there is already a filename suffix */ if (strchr(ldir.filenam, '.') == NULL) /* Already got '.'? */ (void) strncat(ldir.filenam, ".mk5", MAXNAME - 1 - strlen(ldir.filenam)); /* No, add suffix */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() DEBUG: filenam \"%s\" \n", me, ldir.filenam); /* Optional second argument says where to set startbyte and play pointer */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[2][0] != '\0') { /* Second argument? */ if (strcasecmp(ptr[2], "s") == 0 || /* Yes, set to start? */ strcasecmp(ptr[2], "b") == 0) /* Or beginning of scan? */ startbyte = sdir->plapnt = sdir->start[sdir->n]; /* Yes */ else if (strcasecmp(ptr[2], "s+") == 0) /* Set to just past start? */ startbyte = sdir->plapnt = sdir->start[sdir->n] + 65536; /* Yes */ else if (strcasecmp(ptr[2], "c") == 0 || /* Set to center? */ strcasecmp(ptr[2], "m") == 0) /* Or middle of scan? */ /* (Otherwise "m" is taken to be "0m" below) */ startbyte = sdir->plapnt = sdir->start[sdir->n] + /* Yes */ sdir->length[sdir->n] / 2; else if (strcasecmp(ptr[2], "e") == 0) /* Set to (near) end? */ startbyte = sdir->plapnt = sdir->start[sdir->n] + /* Yes */ sdir->length[sdir->n] - 1000000; /* (No check) */ /* Else check for all-numeric plus + or -, that is offset bytes */ else if ((ptr[2][0] == '+' || ptr[2][0] == '-') && strspn(ptr[2] + 1, "0123456789") == strlen(ptr[2] + 1)) { /* Got? */ addbyte = atoll(ptr[2] + 1); /* Yes (always positive) */ if (addbyte > sdir->length[sdir->n]) { /* Check, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() ERROR: Addbyte exceeds length of scan \n", me); (void) strcpy(messg, "Scan_set offset falls outside scan"); /* Error */ error = 1021; /* Scan_set offset too large */ /* We retain the default and otherwise ignore this error */ } else if (ptr[2][0] == '+') /* Offset from start of scan? */ startbyte = sdir->plapnt = sdir->start[sdir->n] + addbyte; /* Yes */ else /* ptr[2][0] = '-', so offset back from end of scan */ startbyte = sdir->plapnt = sdir->start[sdir->n] + sdir->length[sdir->n] - addbyte; /* Subtract offset from end */ } /* Else this argument can be a time, +time, or -time, and converting * this time to bytes requires dattime.timet and dattime.period. * So we need ScanInfo() to read the scan information first, then * AddTime() to decode the time format. */ else if ((ff = ScanInfo()) == 0 && /* Scan information OK? */ (add = AddTime(ptr[2])) >= 0.0) { /* And add time, OK? */ /* (Note add will be positive even if ptr[2][0] = '-') */ addbyte = (long long) (add * dattime.period / dattime.timep + 0.5); /* Yes, convert add to bytes */ if (addbyte + dattime.offset > sdir->length[sdir->n]) { /* Check, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() ERROR: Add bytes exceeds length of scan \n", me); (void) strcpy(messg, "Scan_set offset falls outside scan"); /* Error */ error = 1021; /* Scan_set offset too large */ /* We retain the default and otherwise ignore this error */ } /* Else OK, apply offset */ else if (ptr[2][0] == '-') /* Minus? */ startbyte = sdir->plapnt = sdir->start[sdir->n] + /* Yes */ sdir->length[sdir->n] - addbyte; /* End of scan minus add bytes */ else /* Time or +time; start of scan plus add bytes */ startbyte = sdir->plapnt = sdir->start[sdir->n] + dattime.offset + addbyte; } /* None of the above is an error */ else if (ff != 0) { /* Faulty scan? */ (void) sprintf(outstring, "!%s = 4 : Probably flawed scan ;", ptr[0]); return(outstring); /* Error */ } else { /* Parameter error */ (void) sprintf(outstring, "!%s = 8 : Can't decode start byte ;", ptr[0]); return(outstring); /* Error */ } } /* End of if second argument */ /* (Else retain default above, equivalent to S) */ /* Optional third argument says where to put the end pointer */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[3][0] != '\0') { /* Got a third argument? */ /* Yes. Check for all-numeric plus + or -, that is offset bytes */ if ((ptr[3][0] == '+' || ptr[3][0] == '-') && strspn(ptr[3] + 1, "0123456789") == strlen(ptr[3] + 1)) { /* Got? */ addbyte = atoll(ptr[3] + 1); /* Yes (always positive) */ if (startbyte + addbyte > /* Check: Inside scan? */ sdir->start[sdir->n] + sdir->length[sdir->n]) { /* OK? */ /* (This checks both + and - cases) */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Set_scan() ERROR: " /* Yes */ " Addbyte falls off start or end of scan \n", me); (void) strcpy(messg, "Scan_set offset falls outside scan"); /* Error */ error = 1021; /* Scan_set offset too large */ /* We retain the default and otherwise ignore this error */ } else if (ptr[3][0] == '+') /* Offset from startbyte? */ endbyte = startbyte + addbyte; /* Yes, add offset */ else /* ptr[3][0] = '-', so offset back from end of scan */ endbyte = sdir->start[sdir->n] + sdir->length[sdir->n] - addbyte; } /* Else the third argument can be a time, +time, or -time, so we need * to have ScanInfo(), and we need to redo ScanInfo() even if it was * done above because startbyte might have changed. */ else if ((ff = ScanInfo()) == 0 && /* Scan info? */ (add = AddTime(ptr[3])) > 0.0) { /* And add time, OK? */ /* (Note add will be positive even if ptr[3][0] = '-') */ addbyte = (long long) (add * dattime.period / dattime.timep + 0.5); /* Yes, convert add to bytes */ if (startbyte + addbyte > /* Check case +|-time or numeric time */ sdir->start[sdir->n] + sdir->length[sdir->n]) { /* OK? */ /* (This checks both + and - cases) */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Set_scan() ERROR: " /* Yes */ " Addbyte falls off start or end of scan \n", me); (void) strcpy(messg, "Scan_set offset falls outside scan"); /* Error */ error = 1021; /* Scan_set offset too large */ /* We retain the default and otherwise ignore this error */ } else if (ptr[3][0] == '-') /* Else OK. Case -time? */ endbyte = sdir->start[sdir->n] + sdir->length[sdir->n] - addbyte; /* Yes, subtract from end-of-scan */ else /* Time or +time; startbyte plus add bytes */ endbyte = startbyte + addbyte; /* Add to startbyte */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Set_scan() DEBUG: endbyte %Ld \n", me, endbyte); } /* None of the above is an error */ else if (ff != 0) { /* Faulty scan? */ (void) sprintf(outstring, "!%s = 4 : Probably flawed scan ;", ptr[0]); return(outstring); /* Error */ } else { /* Parameter error */ (void) sprintf(outstring, "!%s = 8 : Can't decode end byte ;", ptr[0]); return(outstring); /* Error */ } } /* End of if got third argument */ /* (Else retain default from above) */ (void) sprintf(outstring, "!%s = 0 ;", ptr[0]); /* Else OK */ return(outstring); /* Normal end */ } /* End of Set_scan() */ static char * Next_scan() { /* next_scan and scan_dir */ /* Should have '?' but no arguments, but we don't check */ if (sdir->nscans <= 0) /* Do we have any scans? */ (void) sprintf(outstring, "!%s? 6 ;", ptr[0]); /* Nope, error */ else { /* Query the local scan directory */ (void) sprintf(outstring, "!%s? 0 : %d : %s : %Ld : %Ld ;", ptr[0], sdir->n + 1, sdir->scanName[sdir->n], sdir->start[sdir->n], sdir->start[sdir->n] + sdir->length[sdir->n]); sdir->n = (sdir->n + 1) % sdir->nscans; /* For next time */ } return(outstring); /* Normal end */ } /* End of Next_scan() */ static char * Disc_model() { /* disc_model or disk_model */ char line[512]; /* Scratch */ char * prc; /* Scratch */ unsigned int i; /* Should have '?' but no arguments, but we don't check */ /* We need to be in bank mode, else dinfo[] is nonsense */ if (! ldir.bmode) { /* Bank mode? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc_model() WARNING: Not in bank mode \n", me); (void) sprintf(outstring, "!%s? 6 : Not in bank mode ;", ptr[0]); return(outstring); /* Error */ } (void) sprintf(outstring, "!%s? 0 ", ptr[0]); /* Start of line */ for (i = 0; i < 8; i++) { /* Each potential disk */ if (dinfo[i].Serial[0] == '\0') { /* OK? */ (void) strcat(outstring, ": "); /* No, assume no such drive */ (void) XLRGetLastError(); /* Clear error */ continue; /* To next i */ } /* Else OK */ prc = strstr(dinfo[i].Model, " "); /* Ends with lots of spaces */ if (prc != NULL) /* Found? */ prc[0] = '\0'; /* Yes */ (void) sprintf(line, ": %s ", dinfo[i].Model); (void) strcat(outstring, line); } /* End of for i each disk */ (void) errors(); (void) strcat(outstring, ";"); /* After last disk */ return(outstring); } /* End of Disc_model() */ static char * DTS_id() { /* dts_id */ static char * revsn = "2.7x"; /* Command-set revision */ char name[128] = ""; /* For "serial number" */ int fport; /* For hardware_id file */ struct stat buf; /* For stat() */ time_t times[4], mtime; /* Times of last modification from stat() */ struct tm * pltim; /* From localtime() */ int i; char * ptc; /* Should have '?' but no arguments, but we don't check */ /* "Serial number" */ if ((fport = open("/etc/hardware_id", O_RDONLY)) < 0 || /* Open OK? */ read(fport, name, sizeof(name)) < 8) /* Read hardware_id, OK? */ /* Nope, presumably no such file */ (void) gethostname(name, sizeof(name)); /* So use machine name */ if (fport >= 0) /* Was open() OK? */ (void) close(fport); /* Yes, close it */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s DTS_id() DEBUG: name %s \n", /* Yes */ me, name); if ((ptc = strchr(name, '.')) != NULL) /* Find first '.' */ ptc[0] = '\0'; /* Use only up to first '.' */ if ((ptc = strchr(name, '\n')) != NULL) /* Find first '\n' */ ptc[0] = '\0'; /* Use only up to first '\n' */ /* Get modification times of relevant files */ (void) stat("/home/jball/C/Parse5A.h", &buf); times[0] = buf.st_mtime; (void) stat("/home/jball/C/Parse5A.c", &buf); times[1] = buf.st_mtime; (void) stat("/home/jball/C/Mark5A.c", &buf); times[2] = buf.st_mtime; (void) stat("/home/jball/C/IOBoard.c", &buf); times[3] = buf.st_mtime; if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s DTS_id() DEBUG: times[] %ld %ld %ld %ld \n", me, times[0], times[1], times[2], times[3]); mtime = times[0]; /* Initialize */ for (i = 1; i < 4; i++) /* Each times[] */ if (times[i] > mtime) /* Later? */ mtime = times[i]; /* Yes */ pltim = localtime(&mtime); /* Get local-time struct */ (void) sprintf(outstring, "!DTS_id? 0 : %s : %04dy%03dd%02dh : 1 : %s : 1 : 1 : %s : %#x : %#x ;", ldir.iob ? "Mark5A" : "Mark5P", 1900 + pltim->tm_year, 1 + pltim->tm_yday, pltim->tm_hour, name, revsn, *idr, *odr); return(outstring); } /* End of DTS_id() */ static char * Net_protocol() { /* net_protocol */ static char protcl[32]; /* Remember protocol, "tcp" or "udp", etc. */ int change = FALSE; unsigned int k; void * ptv; /* Net_protocol can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ if (strcasecmp(smod[1], "udp") == 0 && socbuf > 65528) /* Yes, for UDP and too big? */ k = 54752; /* Yes (should this be calculated ??) */ else /* Normal */ k = socbuf; if (rcvbufs != sndbufs) /* Error? */ (void) sprintf(outstring, /* Yes */ "!net_protocol? 0 : %s : rcvbufs %u ?, sndbufs %u ? : %u ;", smod[1], rcvbufs, sndbufs, k); /* Error */ else /* OK */ (void) sprintf(outstring, "!net_protocol? 0 : %s : %u : %u : %u ;", smod[1], rcvbufs, k, nbuf); /* (In bytes) */ return(outstring); /* End if if query */ } /* Else not a query; check for errors */ if (datah != 0 || ports[0] >= 0) /* Already connected? */ return("!net_protocol = 6 : Can't change during data transfers ;"); /* Else OK, three optional arguments */ if (ptr[1] != NULL && ptr[1][0] != '\0') /* First argument, protocol? */ (void) strncpy(protcl, ptr[1], sizeof(protcl)); /* Yes */ else /* Default first argument */ (void) strcpy(protcl, "tcp"); smod[1] = smod[2] = protcl; /* Set (either case) */ /* (We never change smod[0]) */ /* Second argument = socbuf_size */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[2][0] != '\0' && /* OK? */ (k = atoi(ptr[2])) >= 0) /* (0 is OK to reset defaults) */ sndbufs = rcvbufs = k; /* Yes, bytes */ /* (These won't get checked until later) */ /* Third argument = workbuf_size = socbuf */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL) { /* Got? */ socbuf = atoi(ptr[3]) & 0xfffffff8; /* Yes, bytes; must be integer * 8 */ change = TRUE; } /* Fourth argument = nbuf = number of working buffers in a cycle */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[4] != NULL) { /* Got? */ nbuf = atoi(ptr[4]); /* Yes */ change = TRUE; } /* Check: nbuf * socbuf should be <= 128 MByte */ if (nbuf * socbuf > 134217728) { /* OK? */ if (msglev < 2) /* Nope Debuggery? */ (void) fprintf(stderr, "%s Net_protocol() ERROR: nbuf * workbuf size %u > 128 MByte \n", me, nbuf * socbuf); /* Yes */ (void) strcpy(messg, "Nbuf * workbuf size too large"); /* Error */ error = 1007; /* socbuf too large */ return("!net_protocol = 8 : nbuf * workbuf size too large "); /* Error */ } else if (change) { /* Did we change socbuf or nbuf? */ free(rbuf); /* Yes, release previous version of rbuf */ if ((ptv = calloc(nbuf, socbuf)) == NULL) { /* For new rbuf, OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ERROR: \007 calloc(%u, %u) returned ", me, nbuf, socbuf); perror("error"); } (void) strcpy(messg, "Nbuf * workbuf size too large"); /* Error */ error = 1007; /* workbuf size too large (or something) */ (void) sprintf(outstring, "!net_protocol = 8 : calloc(%u, %u) returned error ;", nbuf, socbuf); return(outstring); /* Error */ } /* Else OK */ rbuf = (unsigned long long *) ptv; } /* End of if change */ return("!net_protocol = 0 ;"); /* Normal end */ } /* End of Net_protocol() */ static char * In2net() { /* in2net */ /* Transfer data from input board to network data socket */ static char name[128] = "localhost"; /* For target machine's name */ static long long nbyte = 0L; /* Local remembered copy of nowbyte */ long long len, lenl; unsigned int k; int val, val2; /* Status of in2net in ldir.i2ndoing */ /* i2ndoing = 0 Inactive * i2ndoing = 1 Socket open and connected (for in2net use) * i2ndoing = 2 Socket open, connected, and active (inputing data) */ /* Debuggery and threads active? */ if (msglev < 1 && (threadtoX != 0xffffffff || threadXto != 0xffffffff)) { (void) sem_getvalue(&semXto, &val); /* Yes, Read-SS semaphore */ (void) sem_getvalue(&semtoX, &val2); /* Write-socket semaphore */ (void) fprintf(stderr, "%s In2net() DEBUG: val %d, val2 %d \n", me, val, val2); } /* In2net can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ if (ports[0] < 0 || datah < 3 || datah == 4) /* Yes, socket OK? */ ldir.i2ndoing = 0; /* Nope, presume we're idle */ if (ldir.i2ndoing > 0) { /* Now are we doing something? */ len = XLRGetFIFOLength(dev); /* Yes, so this function should be OK */ (void) errors(); if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() DEBUG: len %Ld error %lu \n", me, len, error); } /* End of if doing something */ else /* XLRGetFIFOLength() generates errors in this case, so ... */ len = 0; lenl = XLRGetLength(dev); (void) errors(); if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() DEBUG: lenl %Ld error %lu \n", me, lenl, error); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() DEBUG: len %Ld nowbyte %Ld lenl %Ld \n", me, len, nowbyte, lenl); if (ldir.i2ndoing <= 0) /* Now what are we doing? */ (void) sprintf(outstring, "!in2net? 0 : inactive : %s : %Ld ;", name, nbyte); /* (nbyte should be the value of nowbyte at the end of the * last previous in2net transfer, if any) */ else if (ldir.i2ndoing == 1) /* Socket connected? */ (void) sprintf(outstring, "!in2net? 0 : connected : %s : %Ld : %Ld ;", name, lenl + nowbyte, lenl); /* Yes (but not active) */ else { /* Active; presumably i2ndoing = 2 and datah = 5 */ if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() ERROR: XLRGetDeviceStatus() returned error \n", me); /* What to do? We try to go on */ } (void) errors(); if (devstatus.FifoFull) { /* Overflow? */ (void) sprintf(outstring, /* Yes */ "!in2net? 4 : sending : %s : %Ld : %Ld : Overflow ;", name, lenl + nowbyte, lenl); devstatus.FifoFull = FALSE; /* Reset for next time */ } else /* OK */ (void) sprintf(outstring, "!in2net? 0 : sending : %s : %Ld : %Ld ;", name, lenl + nowbyte, lenl); } /* End of else active */ return(outstring); /* End of if query */ } /* Else not a query. Check: One required argument, connect, on, off, * or disconnect, and one optional argument, the target machine's name */ if (ptr[1] == NULL) /* Argument? */ return("!in2net = 8 : Must have argument ;"); /* Nope, parameter error */ /* Second (optional) argument, if any, is needed first */ if (ptr[2] != NULL && ptr[2][0] != '\0') /* Optional target hostname? */ (void) strncpy(name, ptr[2], sizeof(name)); /* Yes, save it */ if (strcasecmp(ptr[1], "connect") == 0 || /* Connect socket? */ strcasecmp(ptr[1], "open") == 0) { /* Yes, but first check for errors */ if (datah != 0 || ports[0] >= 0 || strlen(name) < 3) /* Already connected or no name? */ return("!in2net = 6 : Already connected or no name ;"); /* Error */ /* Pick up send-buffer size as another optional argument? */ if (ptr[2] != NULL && ptr[3] != NULL && ptr[3][0] != '\0' && (k = atoi(ptr[3])) > 0) /* OK? */ sndbufs = k; /* Yes */ /* Pick up size of buffer to send to socket * as yet another optional argument? */ if (ptr[2] != NULL && ptr[3] != NULL && ptr[4] != NULL) { /* Got? */ if ((k = atoi(ptr[4])) > 0 && k <= sizeof(rbuff)) /* Yes, OK? */ socbuf = k; /* Yes */ else { /* Not OK, keep default */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, "%s In2net() ERROR: socbuf %d > %d \n", me, k, sizeof(rbuff)); /* Yes */ (void) strcpy(messg, "Workbuf size too large"); /* Error */ error = 1007; /* socbuf too large */ } } /* Open and connect */ if ((ports[0] = mksock("m5data", name, 2)) < 0) /* OK? */ return("!in2net = 4 : Can't make or can't connect socket ;"); /* Error */ /* Else open and connect OK */ nowbyte = 0L; /* Reset byte counter */ nbyte = 0L; orpntr = 0; datah = 3; /* Send-data socket now open and connected */ ldir.i2ndoing = 1; /* Socket connected (for in2net use) */ /* Turn off (sic) input clock to SS (i.e., initially off) */ inb.notclock = TRUE; (void) inBoard(SET); /* Send to hardware */ /* Configure FIFO or disk FIFO */ if (ldir.dfifo) { /* Disk FIFO? */ if (XLRSetMode(dev, SS_MODE_EXT_TO_PCI_FIFO) != XLR_SUCCESS) { /* Yes, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() ERROR: XLRSetMode() returned error \n", me); (void) errors(); return("!in2net = 4 : Can't set SS disk-FIFO mode ;"); /* Error */ } } /* End of if dfifo disk FIFO */ else { /* RAM FIFO only */ if (XLRSetMode(dev, SS_MODE_READ_EXT) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() ERROR: XLRSetMode() returned error \n", me); (void) errors(); return("!in2net = 4 : Can't set SS mode ;"); /* XLR error */ } } // if (XLRSetFifoMode(dev, XLR_MODE_FIFO) != XLR_SUCCESS) { /* OK? */ // if (msglev < 2) /* Nope, debuggery? */ // (void) fprintf(stderr, /* Yes */ // "%s In2net() ERROR: XLRSetFifoMode() returned error \n", me); // (void) errors(); // return("!in2net = 4 : Can't set FIFO mode ;"); /* XLR error */ } /* (FPDP mode should already be SS_FPDP_RECVMASTER, SS_OPT_FPDPNRASSERT) */ /* Start FIFO */ if (XLRRecord(dev, XLR_WRAP_ENABLE, 1) != XLR_SUCCESS) { /* Record, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() ERROR: XLRRecord() returned error \n", me); (void) errors(); return("!in2net = 4 : Can't record ;"); /* XLR error */ } /* Else OK; start threads toX() and Xto() */ // ldir.delayed = TRUE; /* Delayed completion */ if (pthread_create(&threadtoX, NULL, toX, NULL) != 0) { /* OK? */ if (msglev < 2) { /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() ERROR: \007 pthread_create() on toX() returned ", me); perror("error"); /* Error */ } threadtoX = 0xffffffff; // ldir.delayed = FALSE; return("!in2net = 4 : Can't pthread_create() ;"); /* Pthread error */ } (void) pthread_detach(threadtoX); if (pthread_create(&threadXto, NULL, Xto, NULL) != 0) { /* OK? */ if (msglev < 2) { /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() ERROR: \007 pthread_create() on Xto() returned ", me); perror("error"); /* Error */ } threadXto = 0xffffffff; // ldir.delayed = FALSE; return("!in2net = 4 : Can't pthread_create() ;"); /* Pthread error */ } /* Else OK */ (void) pthread_detach(threadXto); datah = 5; /* This is needed for the threads */ return("!in2net = 0 ;"); /* Normal return for case "connect" */ } if (strcasecmp(ptr[1], "on") == 0) { /* Turn on data flow? */ /* Yes, but first check for errors */ if (ports[0] < 0 || datah < 3) /* Not open or wrong open? */ return("!in2net = 6 : No socket or wrong open ;"); /* Error */ /* Turn on input clock to SS */ inb.notclock = FALSE; /* (That's not notclock = clock) */ (void) inBoard(SET); /* Send to hardware */ datah = 5; /* Turn on sending data, in2net case */ /* (datah should already be 5) */ ldir.i2ndoing = 2; /* Socket connected and active (sending data) */ return("!in2net = 1 ;"); /* Delayed completion, normal for "on" */ } if (strcasecmp(ptr[1], "off") == 0) { /* Turn off data flow? */ /* Yes, first check for errors */ if ((datah != 3 && datah != 5) || ports[0] < 0) /* Not open or wrong open? */ return("!in2net = 6 : No socket or wrong open ;"); /* Yes, error */ /* Else turn off input clock to SS */ inb.notclock = TRUE; /* Turn clock off */ (void) inBoard(SET); /* Send to hardware */ ldir.i2ndoing = 1; /* Inputing data inactive */ /* (We leave datah at 5 to flush the FIFO buffer and everything else on) * (No point setting nbyte here because its probably still changing) */ return("!in2net = 0 ;"); /* Normal return for "off" */ } if (strcasecmp(ptr[1], "disconnect") == 0 || /* Disconnect socket? */ strcasecmp(ptr[1], "close") == 0) { /* Yes, first check for errors */ if ((datah != 3 && datah != 5) || ports[0] < 0) /* OK? */ return("!in2net = 6 : No socket or wrong open ;"); /* Nope, error */ /* Stop data input first (it should already be stopped) */ inb.notclock = TRUE; /* Turn clock off */ (void) inBoard(SET); /* Send to hardware */ if (msglev < 1 && (lenl = XLRGetLength(dev)) > 0) /* Debuggery and data left? */ (void) fprintf(stderr, /* Yes, maybe an error */ "%s In2net() WARNING: Closing active socket, lenl %Ld \n", me, lenl); /* We try to go on */ (void) errors(); /* Stop data output (and flush the buffer (?)) */ if (XLRStop(dev) != XLR_SUCCESS) { /* OK? */ (void) errors(); return("!in2net = 4 : Can't XLRStop() ;"); /* Nope, error */ } (void) close(ports[0]); /* Close socket */ ports[0] = -1; /* Socket no longer in use */ datah = 0; /* No data socket open */ /* (This might also cancel threads toX() and Xto()) */ // ldir.delayed = FALSE; ldir.i2ndoing = 0; /* In2net inactive */ /* Cancel both threads */ (void) pthread_cancel(threadtoX); (void) pthread_cancel(threadXto); /* Now turn input clock to SS back on (back to normal) */ inb.notclock = FALSE; /* (That's not notclock = clock) */ (void) inBoard(SET); /* Send to hardware */ if (! ldir.dfifo) { /* Disk-FIFO mode? */ /* No. Configure SS back to default modes */ // if (XLRSetFifoMode(dev, XLR_MODE_DEFAULT) != XLR_SUCCESS) { /* OK? */ // if (msglev < 2) /* Nope, degubbery? */ // (void) fprintf(stderr, /* Yes */ // "%s In2net() ERROR: XLRSetFifoMode() returned error \n", me); // (void) errors(); // return("!in2net = 4 : Can't set FIFO mode ;"); /* XLR error */ } if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* To default, OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s In2net() ERROR: XLRSetMode() returned error \n", me); (void) errors(); return("!in2net = 4 : Can't set SS mode ;"); /* Error */ } (void) errors(); } /* End of if not disk-FIFO mode */ /* (FPDP mode should still be SS_FPDP_RECVMASTER, SS_OPT_FPDPNRASSERT) */ nbyte = nowbyte; /* Remember ending nowbyte for query above */ return("!in2net = 0 ;"); /* Normal return for case "disconnect" */ } /* None of the above is a parameter error */ return("!in2net = 8 : Must be connect, on, off, or disconnect ;"); } /* End of In2net() */ static char * Disc2net() { /* disc2net or disk2net */ /* Transfer data from disks to network data socket */ static char name[128] = "localhost"; /* For target machine's name */ unsigned int k; int val, val2; /* Status of disc2net in ldir.d2ndoing */ /* d2ndoing = 0 Inactive * d2ndoing = 1 Socket open and connected (for disc2net use) * (datah = 3) * d2ndoing = 2 And data flowing * (datah = 4) */ /* Debuggery and threads active? */ if (msglev < 1 && (threadtoX != 0xffffffff || threadXto != 0xffffffff)) { (void) sem_getvalue(&semXto, &val); /* Yes, Read-disk semaphore */ (void) sem_getvalue(&semtoX, &val2); /* Write-socket semaphore */ (void) fprintf(stderr, /* Yes */ "%s Disc2net() DEBUG: val %d, val2 %d \n", me, val, val2); } /* Disc2net can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ if (ports[0] < 0 || datah < 3) /* Yes, anything doing? */ ldir.d2ndoing = 0; /* Nope */ if (ldir.d2ndoing <= 0) /* Otherwise what are we doing? */ (void) sprintf(outstring, "!%s? 0 : inactive : %s ;", /* Nothing */ ptr[0], name); else if (datah < 4 || nowbyte + 8 > endbyte) /* Socket connected? */ (void) sprintf(outstring, "!%s? 0 : connected : %s : %Ld : %Ld : %Ld ;", ptr[0], name, startbyte, nowbyte, endbyte); /* Yes */ else /* d2ndoing >= 1 and socket OK; presume datah = 4 */ (void) sprintf(outstring, "!%s? 0 : active : %s : %Ld : %Ld : %Ld ;", ptr[0], name, startbyte, nowbyte, endbyte); return(outstring); /* End of if query */ } /* Else not a query */ if (devinfo.NumDrives < 1) { /* Any disks available? */ error = 1002; /* Message number for this case */ (void) strcpy(messg, "No disks available"); (void) sprintf(outstring, "!%s = 6 : No disks available ;", ptr[0]); return(outstring); /* Error */ } /* Check: One required argument, connect, on, or * disconnect, and one or two optional arguments, the target machine's * name or the starting and ending byte numbers to send */ if (ptr[1] == NULL) { /* Argument? */ (void) sprintf(outstring, "!%s = 8 : Must have argument ;", ptr[0]); return(outstring); /* Error */ } if (strcasecmp(ptr[1], "connect") == 0 || /* Connect socket? */ strcasecmp(ptr[1], "open") == 0) { /* Yes, first check for errors */ if (datah != 0 || ports[0] >= 0) { /* Already open and connected? */ (void) sprintf(outstring, "!%s = 6 : Already connected or wrong open ;", ptr[0]); /* Yes, error */ return(outstring); /* Error */ } /* Check here for second (optional) argument */ if (ptr[2] != NULL && ptr[2][0] != '\0') /* Optional target hostname? */ (void) strncpy(name, ptr[2], sizeof(name)); /* Yes, save it */ /* Now check name */ if (strlen(name) < 3) { /* OK? */ (void) sprintf(outstring, "!%s = 8 : Probably wrong name ;", ptr[0]); return(outstring); /* Error */ } /* Pick up send-buffer size as another optional argument? */ if (ptr[2] != NULL && ptr[3] != NULL && ptr[3][0] != '\0' && (k = atoi(ptr[3])) > 0) /* OK? */ sndbufs = k; /* Yes */ /* Pick up size of buffer to send to socket * as yet another optional argument? */ if (ptr[2] != NULL && ptr[3] != NULL && ptr[4] != NULL) { /* Got? */ if ((k = atoi(ptr[4])) > 0 && k <= sizeof(rbuff)) /* Yes, OK? */ socbuf = k; /* Yes */ else { /* Not OK, keep default */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, "%s Disc2net() ERROR: socbuf %d > %d \n", me, k, sizeof(rbuff)); /* Yes */ (void) strcpy(messg, "Workbuf size too large"); /* Error */ error = 1007; /* socbuf too large */ } } /* Open and connect */ if ((ports[0] = mksock("m5data", name, 2)) < 0) { /* OK? */ (void) sprintf(outstring, "!%s = 4 : Can't make socket ;", ptr[0]); return(outstring); /* Error */ } /* Else OK */ datah = 3; /* Send-data socket now open and connected */ ldir.d2ndoing = 1; /* Socket connected (for disc2net use) */ nowbyte = startbyte; /* Start at the beginning */ orpntr = 0; /* (We do not start threads toX() and Xto() until "on" below) */ (void) sprintf(outstring, "!%s = 0 ;", ptr[0]); return(outstring); /* Normal return for case "connect" */ } if (strcasecmp(ptr[1], "on") == 0) { /* Turn on data flow? */ /* Yes, but first check for errors */ if (datah < 3 || ports[0] < 0) { /* Not connected or wrong connection? */ (void) sprintf(outstring, "!%s = 6 : Not connected or wrong open ;", ptr[0]); /* Yes, error */ return(outstring); /* Error */ } if (datah != 3 || ldir.d2ndoing != 1) { /* Already on or other transfer? */ (void) sprintf(outstring, /* Yes */ "!%s = 6 : Already on or wrong transfer active ;", ptr[0]); return(outstring); /* Error */ } /* Else OK. (Defaults have already been set by, for example, scan_set) */ /* Check for two (optional) arguments */ if (ptr[2] != NULL && ptr[2][0] != '\0') /* Start byte number? */ startbyte = atoll(ptr[2]); /* Yes */ if (ptr[2] != NULL && ptr[3] != NULL && ptr[3][0] != '\0') { /* End byte number? */ endbyte = atoll(ptr[3]); /* Yes */ if (ptr[3][0] == '+') /* Length? */ endbyte += startbyte; /* Yes */ } nowbyte = startbyte; /* Start at the beginning */ orpntr = 0; /* Configure SS for disks to PCI */ if (ldir.d2ndoing < 2 && XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* If not already, play to PCI, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc2net() WARNING: XLRSetMode() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't set SS mode ;", ptr[0]); return(outstring); /* Error */ } /* Start threads toX() and Xto() */ if (pthread_create(&threadtoX, NULL, toX, NULL) != 0) { /* OK? */ if (msglev < 2) { /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc2net() ERROR: pthread_create() on toX() returned ", me); perror("error"); /* Error */ } threadtoX = 0xffffffff; (void) sprintf(outstring, /* Pthread error */ "!%s = 4 : Can't pthread_create() ;", ptr[0]); return(outstring); /* Error */ } /* Else OK */ (void) pthread_detach(threadtoX); if (pthread_create(&threadXto, NULL, Xto, NULL) != 0) { /* OK? */ if (msglev < 2) { /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disk2net() ERROR: pthread_create() on Xto() returned ", me); perror("error"); /* Error */ } threadXto = 0xffffffff; (void) sprintf(outstring, /* Pthread error */ "!%s = 4 : Can't pthread_create() ;", ptr[0]); return(outstring); /* Error */ } /* Else OK */ (void) pthread_detach(threadXto); ldir.d2ndoing = 2; /* Socket open, connected, and active (sending data) */ datah = 4; /* This is needed to start the threads */ (void) sprintf(outstring, "!%s = 1 ;", ptr[0]); /* Delayed completion */ return(outstring); /* Normal return for case "on" */ } if (strcasecmp(ptr[1], "disconnect") == 0 || /* Close socket? */ strcasecmp(ptr[1], "close") == 0) { /* Yes, first check for errors */ if (ports[0] < 0 || datah > 4) { /* OK? */ (void) sprintf(outstring, "!%s = 6 : Not connected or wrong open ;", ptr[0]); /* Nope, error */ return(outstring); /* Error */ } /* (Data flow should already be off) */ (void) close(ports[0]); /* Disconnect and close socket */ ports[0] = -1; /* Socket no longer in use */ datah = 0; /* No data socket open */ ldir.d2ndoing = 0; /* Disk2net inactive */ /* Cancel both threads */ (void) pthread_cancel(threadtoX); (void) pthread_cancel(threadXto); /* Configure SS back to default mode */ if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, degubbery? */ (void) fprintf(stderr, /* Yes */ "%s Disc2net() WARNING: XLRSetMode() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't set SS mode ;", ptr[0]); return(outstring); /* Error */ } (void) sprintf(outstring, "!%s = 0 ;", ptr[0]); /* Else OK */ return(outstring); /* Normal return for case "disconnect" */ } /* None of the above is a parameter error */ (void) sprintf(outstring, "!%s = 8 : Must be connect, on, or disconnect ;", ptr[0]); return(outstring); /* Error */ } /* End of Disc2net() */ static char * Net2disc() { /* net2disc or net2disk */ /* Transfer data from network data socket to disks */ static char scanName[MAXLENGTH] = "net2disk"; /* Local copy of sdir->scanName[][] */ int j, k, mlen; int val, val2; /* Status of net2disc is in ldir.n2ddoing * n2ddoing = 0 Inactive * n2ddoing = 1 Socket open (for net2disc use) */ /* Debuggery and threads active? */ if (msglev < 1 && (threadtoX != 0xffffffff || threadXto != 0xffffffff)) { (void) sem_getvalue(&semXto, &val); /* Yes, Read-socket semaphore */ (void) sem_getvalue(&semtoX, &val2); /* Write-disk semaphore */ (void) fprintf(stderr, "%s Net2disc() DEBUG: val %d, val2 %d \n", me, val, val2); } /* * Net2disc can be a query; check that first * */ if (pt != NULL) { /* Was it a query? */ if (ldir.n2ddoing <= 0) { /* Yes. What are we doing? */ if (ldir.n2dlstn < 0) /* Nothing, but have we done something? */ (void) sprintf(outstring, "!%s? 0 : inactive ;", ptr[0]); /* Nope */ else { /* Yes, show the last scan done by Net2disc() */ j = ldir.n2dlstn; (void) sprintf(outstring, "!%s? 0 : inactive : %d : %s ;", ptr[0], j + 1, sdir->scanName[j]); } } else { /* Doing something */ j = sdir->nscans; if (datah < 2 || ports[0] < 0) /* Connected? */ (void) sprintf(outstring, "!%s? 0 : waiting : %d : %s ;", /* No */ ptr[0], j + 1, sdir->scanName[j]); else /* Presumably connected */ (void) sprintf(outstring, "!%s? 0 : active : %d : %s ;", ptr[0], j + 1, sdir->scanName[j]); } /* End of else doing something */ return(outstring); /* End of if query */ } /* * Else not a query. We need bank status * */ if (ldir.bmode) { /* Bank mode? */ if (XLRGetBankStatus(dev, bank, &bkstat) != XLR_SUCCESS) { /* Yes, get bank status, OK? */ (void) fprintf(stderr, /* Nope */ "%s Net2Disc() ERROR: XLRGetBankStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't get bank status ;", ptr[0]); return(outstring); /* Error */ } /* * Else OK status info * */ (void) errors(); if (devinfo.NumDrives < 1) { /* Any disks available? */ error = 1002; /* Message number for this case */ (void) strcpy(messg, "No disks available"); (void) sprintf(outstring, "!%s = 6 : No disks available ;", ptr[0]); return(outstring); /* Error */ } /* * We can't write to disk if write protected * */ if (bkstat.WriteProtected) { /* Protected? */ (void) sprintf(outstring, "!%s = 6 : Bank is write protected ;", ptr[0]); /* Yes */ return(outstring); /* Error */ } /* * Else not protected * */ } /* End of if bank mode */ /* * Check: One required argument, open or close * */ if (ptr[1] == NULL) { /* Argument? */ (void) sprintf(outstring, "!%s = 8 : Must have argument ;", ptr[0]); return(outstring); /* Parsing error */ } /* * Else OK to start * */ /* If we find multiple fields, then we interchange the order to make * up the scan label: field2_field3_field1_field4, with underlines (_) * as shown. Note the field1 is ptr[2], field2 is ptr[3], etc. * The canonical order is: Scan : Experiment : Station : Source ; * which we make into: Experiment_Station_Scan_Source */ if (ptr[2] != NULL) { /* Scan name or scan label? */ scanName[0] = '\0'; /* Initialize */ sdir->scanName[sdir->nscans][0] = '\0'; mlen = MAXLENGTH - 1; /* Yes, mlen is number of bytes available */ /* * Here we look for an optional experiment name, station code, and * source name, and, if so, we append it (them) onto the scan label * with underline(s) ('_') as separator(s). Note that we allow * ptr[3] and the others to be null strings, which generate double * underlines(__) with nothing between * */ if (ptr[3] != NULL) { /* Experiment name also? */ (void) strncat(sdir->scanName[sdir->nscans], ptr[3], mlen); /* Yes */ (void) strncat(scanName, ptr[3], mlen); } if (ptr[3] != NULL && ptr[4] != NULL) { /* Station code also? */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Yes, space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); mlen = MAX(0, mlen - 1); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[4], mlen); (void) strncat(scanName, ptr[4], mlen); } if (ptr[3] != NULL) { /* Previous token? */ /* Yes. We already know that ptr[2] != NULL, so we have a scan name, * but we append the underline only if we have also a previous token, * either experiment name or station code or both */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); } /* We already know that we have a scan name or scan label, * that is prt[2] != NULL */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[2], mlen); /* Save ptr[2] as a scanName[] */ (void) strncat(scanName, ptr[2], mlen); /* And a local copy */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Space left */ /* * For the possibility that this scan might turn out to be the * last scan on this disk pack, we append '+' onto sdir->scanName[]. * This '+' is omitted on the local copy, scanName[], which is used * to delete the '+' on record=off, if any * */ (void) strncat(sdir->scanName[sdir->nscans], "+", mlen); if (ptr[3] != NULL && ptr[4] != NULL && ptr[5] != NULL) { /* Source name also? */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Yes, space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); mlen = MAX(0, mlen - 1); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[5], mlen); (void) strncat(scanName, ptr[5], mlen); } } /* End of if scan name */ /* Else retain default above */ /* * Open socket? * */ if (strcasecmp(ptr[1], "open") == 0) { /* * Yes, first check for errors * */ if (datah != 0 || ports[0] >= 0) { /* Already open? */ (void) sprintf(outstring, "!%s = 6 : Already open or wrong open ;", ptr[0]); /* Yes, error */ return(outstring); /* Error */ } /* * Open receive-data socket * */ if ((ports[0] = mksock("m5data", NULL, 1)) < 0) { /* OK? */ (void) sprintf(outstring, "!%s = 4 : Can't make socket ;", ptr[0]); return(outstring); /* Error */ } /* * Set DMS to "Recorded" * */ /* (We need to do the setLabel() before SS_MODE) */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Recorded", then skip setLabel() */ if (strcasecmp(dms, "Recorded") != 0 && /* Not already Recorded? */ ldir.dsmask & 0x4) { /* And allowed change to Recorded? */ (void) strcpy(dms, "Recorded"); /* OK, set DMS to Recorded */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } /* * Configure SS for PCI to disks * */ if (ldir.n2ddoing < 1 && XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* If not already, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() WARNING: XLRSetMode() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't set SS mode ;", ptr[0]); return(outstring); /* Error */ } /* * Set scan directory * */ if (scanName[0] != '\0') /* Do we have a scan name? */ (void) strcpy(sdir->scanName[sdir->nscans], scanName); /* Yes, use it */ else /* Nope, use command name */ (void) strcpy(sdir->scanName[sdir->nscans], ptr[0]); sdir->start[sdir->nscans] = XLRGetLength(dev); /* Scan start, bytes */ /* * Recording on * */ if (XLRAppend(dev) != XLR_SUCCESS) /* Record, OK? */ { if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() ERROR: XLRAppend() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't append recording ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); datah = 1; /* Receive-data socket now open */ ldir.n2dlstn = sdir->nscans; /* Remember scan number we're writing */ if (strcmp(smod[1], "udp") == 0) /* Receiving udp? */ datah = 2; /* Yes, no connection needed */ ldir.n2ddoing = 1; /* Data socket open (for net2disc use) */ (void) sprintf(outstring, "!%s = 1 ;", ptr[0]); /* Delayed completion */ return(outstring); /* Normal return for case "open" */ } /* * Close socket, etc.? * */ if (strcasecmp(ptr[1], "close") == 0) { /* * Yes, but first check for errors * */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() DEBUG: ports[0] %d, datah %d, n2ddoing %d \n", me, ports[0], datah, ldir.n2ddoing); if (datah > 2 && msglev < 2) /* Warning? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() WARNING: Closing data socket %d \n", me, ports[0]); /* * Recording off * */ /* (This is required before data can be used) */ if (XLRStop(dev) != XLR_SUCCESS) { /* Stop, OK? */ (void) errors(); /* Nope */ (void) sprintf(outstring, "!%s = 4 : Can't XLRStop() ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() DEBUG: XLRStop() OK \n", me); /* * More error checks * */ if (ports[0] < 0 && datah != 1 && datah != 2 && ldir.n2ddoing <= 0) { /* Already closed? */ /* Yes; this eliminates spurious directory entries */ (void) sprintf(outstring, "!%s = 6 : Already closed ;", ptr[0]); return(outstring); /* Error */ } /* * Else OK * */ if (ports[0] >= 0) /* Socket open? */ (void) close(ports[0]); /* Yes, close it */ ports[0] = -1; /* Socket no longer in use */ datah = 0; /* No data socket open */ ldir.n2ddoing = 0; /* Net2disc inactive */ if (XLRGetDirectory(dev, &dir) != XLR_SUCCESS) /* OK? */ { (void) errors(); /* Nope */ (void) sprintf(outstring, "!%s = 4 : Can't get directory ;", ptr[0]); return(outstring); /* Error */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() DEBUG: XLRGetDirectory() OK \n", me); (void) errors(); sdir->length[sdir->nscans] = dir.AppendLength; sdir->recpnt = dir.Length; if (++sdir->nscans >= MAXSCANS) { /* Bump for next scan, OK? */ (void) fprintf(stderr, /* Nope */ "%s Net2disc() ERROR: \007 Directory overflow \n", me); sdir->nscans = 0; /* Not OK. Reset (!) */ sdir->n = 0; } sdir->start[sdir->nscans] = 0L; /* Clear this next entry */ sdir->length[sdir->nscans] = 0L; sdir->scanName[sdir->nscans][0] = '\0'; sdir->n = MAX(0, sdir->nscans - 1); /* Set the scan pointer to the just-recorded scan */ startbyte = sdir->plapnt = sdir->start[sdir->n]; /* Set the play pointer to the start of the just-recorded scan */ endbyte = startbyte + dir.AppendLength; (void) writedir(); /* Save copy of current sdir on SS disks */ /* * Configure SS back to default mode * */ if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { if (msglev < 2) /* Nope, degubbery? */ (void) fprintf(stderr, /* Yes */ "%s Net2disc() WARNING: XLRSetMode() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't set SS mode ;", ptr[0]); return(outstring); /* Error */ } (void) sprintf(outstring, "!%s = 0 ;", ptr[0]); /* Else OK */ return(outstring); /* Normal return for case "close" */ } /* * None of the above is a parameter error * */ (void) sprintf(outstring, "!%s = 8 : Must be open or close ;", ptr[0]); return(outstring); /* Error */ } /* End of Net2disc() */ static char * Net2out() { /* net2out */ /* Transfer data from network data socket to output board */ long long len = -1L, lenl = -1L; struct pollfd polls; int k, ok = FALSE; int val, val2; /* Status of net2out is in ldir.n2odoing * n2odoing = 0 Inactive * n2odoing = 1 Socket open (for net2out use) */ /* Debuggery and threads active? */ if (msglev < 1 && (threadtoX != 0xffffffff || threadXto != 0xffffffff)) { (void) sem_getvalue(&semXto, &val); /* Yes, Read-socket semaphore */ (void) sem_getvalue(&semtoX, &val2); /* Write-SS semaphore */ (void) fprintf(stderr, "%s Net2out() DEBUG: val %d, val2 %d \n", me, val, val2); } /* Net2out can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ if (ldir.n2odoing) { /* Yes, are we doing something? */ len = XLRGetFIFOLength(dev); /* Yes */ (void) errors(); lenl = XLRGetLength(dev); (void) errors(); if (datah == 2 && ports[0] >= 0) { /* OK to poll()? */ polls.fd = ports[0]; /* Yes. File descriptor */ polls.events = POLLIN; /* Requested event */ if (poll(&polls, 1, 100) > 0 && polls.revents == POLLIN) /* Wait up to 100 ms, OK? */ ok = TRUE; /* Yes */ /* Else poll() timeout or error, ok = FALSE */ /* End of if OK to poll() */ } /* Else not OK to poll, ok = FALSE */ /* End of if doing something */ } /* Else doing nothing, lenl = len = -1, and ok = FALSE */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() DEBUG: len %Ld, lenl %Ld, nowbyte %Ld, data %s \n", me, len, lenl, nowbyte, ok ? "Yes" : "No"); if (ldir.n2odoing <= 0) { /* So are we doing something (again)? */ (void) sprintf(outstring, "!%s? 0 : inactive : %Ld ;", ptr[0], nowbyte); return(outstring); /* Doing nothing */ } else if (datah < 2 || ports[0] < 0) { /* Connected? */ (void) sprintf(outstring, "!%s? 0 : waiting : %Ld ;", ptr[0], nowbyte); return(outstring); /* Presumably waiting for a connection */ } else if (! ok) { /* Data flow? */ (void) sprintf(outstring, "!%s? 0 : paused : %Ld ;", ptr[0], nowbyte); return(outstring); /* No, presumably waiting for data */ } else { /* Presumably connected and active */ (void) sprintf(outstring, "!%s? 0 : active : %Ld ;", ptr[0], nowbyte); return(outstring); } } /* Else not a query. Check: One required argument, open or close */ if (ptr[1] == NULL) /* Argument? */ return("!net2out = 8 : Must have argument ;"); /* Nope, parameter error */ if (strcasecmp(ptr[1], "open") == 0) { /* Open socket? */ /* Yes, but first check for errors */ if (datah != 0 || ports[0] >= 0 || ldir.n2odoing > 0) /* Already open? */ return("!net2out = 6 : Already open or wrong open ;"); /* Yes, error */ /* Pick up receive-buffer size as an optional argument? */ if (ptr[2] != NULL && (k = atoi(ptr[2])) > 0) /* OK? */ rcvbufs = k; /* Yes */ /* Open receive-data socket */ if ((ports[0] = mksock("m5data", NULL, 1)) < 0) /* OK? */ return("!net2out = 4 : Can't open socket ;"); /* Nope, error */ /* * Set FPDP mode for playing * * * SS_FPDP_XMIT = transmit (does not drive clock), * SS_FPDP_XMITMASTER = normal transmit (with clock) */ if (XLRSetFPDPMode(dev, found == 2 ? SS_FPDP_XMITMASTER : SS_FPDP_XMIT, 0) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() WARNING: XLRSetFPDPMode() returned error \n", me); (void) errors(); return("!net2out = 4 : Can't set FPDP mode ;"); /* Error */ } (void) errors(); /* Configure SS for PCI to output board (no disks) */ if (XLRSetMode(dev, SS_MODE_WRITE_EXT) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() WARNING: XLRSetMode() returned error \n", me); (void) errors(); return("!net2out = 4 : Can't set SS mode ;"); /* XLR error */ } (void) errors(); /* Recording on */ if (XLRRecord(dev, FALSE, 1) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* No. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() WARNING: XLRRecord() returned error \n", me); (void) errors(); return("!net2out = 4 : Can't record ;"); /* XLR error */ } (void) errors(); datah = 1; /* Receive-data socket now open */ if (strcmp(smod[1], "udp") == 0) /* Receiving udp? */ datah = 2; /* Yes, no connection needed */ ldir.n2odoing = 1; /* Data socket open (for net2out use) */ nowbyte = 0L; /* Initialize */ return("!net2out = 1 ;"); /* Normal return for case "open" */ } if (strcasecmp(ptr[1], "close") == 0) { /* Close socket? */ /* Yes, but first check for errors */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() DEBUG: ports[0] %d, datah %d \n", me, ports[0], datah); if ((ports[0] < 0 || datah == 0) && msglev < 1) /* Another warning? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() WARNING: net2out socket not in use \n", me); if (datah > 2 && msglev < 1) /* Warning? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() WARNING: Closing send-data socket %d \n", me, ports[0]); if (ports[0] >= 0) /* Socket open? */ (void) close(ports[0]); /* Yes, close it */ else if (msglev < 1) /* Socket not open, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() WARNING: Socket not open \n", me); ports[0] = -1; /* Socket no longer in use */ datah = 0; /* No data socket open */ ldir.n2odoing = 0; /* Net2out inactive */ /* Recording off */ if (XLRStop(dev) != XLR_SUCCESS) { /* Stop, OK? */ (void) errors(); return("!net2out = 4 : Can't XLRStop() ;"); /* Nope, error */ } (void) errors(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() DEBUG: XLRStop() OK \n", me); /* Configure FPDP back to default */ if (XLRSetFPDPMode(dev, SS_FPDP_RECVMASTER, SS_OPT_FPDPNRASSERT) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() WARNING: XLRSetFPDPMode() returned error \n", me); (void) errors(); return("!net2out = 4 : Can't set FPDP mode ;"); /* Error */ } (void) errors(); /* Configure SS back to default */ if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { if (msglev < 2) /* Nope, degubbery? */ (void) fprintf(stderr, /* Yes */ "%s Net2out() WARNING: XLRSetMode() returned error \n", me); (void) errors(); return("!net2out = 4 : Can't set SS mode ;"); /* Error */ } (void) errors(); return("!net2out = 0 ;"); /* Normal return for case "close" */ } /* None of the above is a parameter error */ return("!net2out = 8 : Must be open or close ;"); /* Error */ } /* End of Net2out() */ static char * Disc2file() { /* disc2file or disk2file */ /* Transfer data from SS disks to a file */ /* Status of Disc2file() is in ldir.d2fdoing * d2fdoing = 0 Inactive * d2fdoing = 1 Active */ int fport; /* Output file */ FILE * file; /* Output file also as a stream */ char * mood; /* For fdopen() */ int k; /* Disc2file can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ if (datah != 6) /* Yes, done? */ ldir.d2fdoing = 0; /* Yes */ if (ldir.d2fdoing <= 0) { /* Otherwise what are we doing? */ if (strlen(ldir.d2ffile) > 0) /* Nothing, but have we done something? */ (void) sprintf(outstring, "!%s? 0 : inactive : %s ;", /* Yes */ ptr[0], ldir.d2ffile); else /* Done nothing */ (void) sprintf(outstring, "!%s? 0 : inactive ;", ptr[0]); } else /* d2fdoing = 1 */ (void) sprintf(outstring, "!%s? 0 : active : %s : %Ld : %Ld : %Ld : %s ;", ptr[0], ldir.d2ffile, startbyte, nowbyte, endbyte, optn); return(outstring); /* End of if query */ } /* Else not a query. Check: OK to start this transfer? */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ (void) sprintf(outstring, /* Nope */ "!%s = 6 : Not while other activity ;", ptr[0]); return(outstring); /* Error, must wait for other activity to finish */ } if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc2file() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't get device status ;", ptr[0]); return(outstring); /* XLR error */ } if (devstatus.Recording || devstatus.Playing) { /* Recording or Playing? */ (void) sprintf(outstring, /* Yes */ "!%s = 6 : Not while recording or playing ;", ptr[0]); return(outstring); /* Error, must wait for other activity to finish */ } /* OK. All arguments have defaults and are optional. * Defaults have already been set by, for example, scan_set */ if (ptr[1] != NULL && ptr[1][0] != '\0') /* File name? */ (void) strncpy(ldir.d2ffile, ptr[1], MAXNAME); /* Yes */ else /* Use filenam[] from Set_scan() */ (void) strncpy(ldir.d2ffile, ldir.filenam, MAXNAME); if (ptr[1] != NULL && ptr[2] != NULL && ptr[2][0] != '\0') /* Start byte number? */ startbyte = atoll(ptr[2]); /* Yes */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[3][0] != '\0') { /* End byte number? */ endbyte = atoll(ptr[3]); /* Yes */ if (ptr[3][0] == '+') /* Length? */ endbyte += startbyte; /* Yes */ } (void) strcpy(optn, "n"); /* Default: Do not use any existing file */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[4] != NULL) /* Option (erase, append, or neither)? */ (void) strncpy(optn, ptr[4], sizeof(optn)); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc2file() DEBUG: %s %Ld %Ld %s \n", me, ldir.d2ffile, startbyte, endbyte, optn); /* Open or create output file */ if (optn[0] == 'w' || optn[0] == 'W') { /* Erase (truncate) file, if any? */ k = O_WRONLY | O_CREAT | O_TRUNC; /* Yes */ mood = "w"; } else if (optn[0] == 'a' || optn[0] == 'A') { /* Append, don't erase? */ k = O_WRONLY | O_CREAT; /* Yes */ mood = "a"; } else { /* Check: Don't use existing file, if any */ k = O_WRONLY | O_CREAT | O_EXCL; /* (If file exists, open() will fail) */ mood = "w"; } if ((fport = open64(ldir.d2ffile, k)) < 0) { /* OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc2file() ERROR: \007 Can't open64() file ", me); perror(ldir.d2ffile); } /* Which error? */ if (errno == EEXIST) /* File exists and optn[] is "n"? */ (void) sprintf(outstring, "!%s = 6 : Won't use existing file ;", ptr[0]); else /* Other error */ (void) sprintf(outstring, "!%s = 4 : Can't open file ;", ptr[0]); ldir.d2fdoing = 0; return(outstring); /* Error */ } (void) fchmod(fport, 0664); /* Ignore errors (e.g., owned by someone else) */ if ((file = fdopen(fport, mood)) == NULL) { /* Open also as a stream, OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Disc2file() ERROR: \007 Can't fdopen() file ", me); perror(ldir.d2ffile); } (void) sprintf(outstring, "!%s = 4 : Can't fdopen() file ;", ptr[0]); ldir.d2fdoing = 0; return(outstring); /* Error */ } nowbyte = startbyte; /* Initialize */ orpntr = 0; if (endbyte == 0) /* Default? */ endbyte = 1000000; /* Yes */ /* Transfer to the global array */ ports[0] = fport; fports[0] = file; ldir.d2fdoing = 1; /* Now we're doing something */ datah = 6; /* And main() handles the actual transfer */ (void) sprintf(outstring, "!%s = 1 ;", ptr[0]); /* OK, delayed completion */ return(outstring); /* Normal return */ } /* End of Disc2file() */ static char * File2disc() { /* file2disc and file2disk */ /* * Transfer data from a file to SS disks * */ static char scanName[MAXLENGTH] = ""; /* Local copy of scanName[][] */ /* Status of File2disc() is in ldir.f2ddoing * f2ddoing = 0 Inactive * f2ddoing = 1 Active */ int fport; /* Input file */ int j, k, mlen; FILE * file; /* Input file aslo as a stream */ struct stat64 stats; /* For fstat64() */ char * ptc; /* * File2disc can be a query; check that first * */ if (pt != NULL) { /* Was it a query? */ if (datah != 7) /* Yes, done? */ ldir.f2ddoing = 0; /* Yes */ if (ldir.f2ddoing <= 0) { /* Otherwise what are we doing? */ if (ldir.f2dlstn < 0) /* Nothing, but have we done something? */ (void) sprintf(outstring, "!%s? 0 : inactive ;", ptr[0]); /* Nope */ else { /* Yes, show the last scan done by File2disc() */ j = ldir.f2dlstn; (void) sprintf(outstring, "!%s? 0 : inactive : %d : %s ;", ptr[0], j + 1, sdir->scanName[j]); } } else { /* Doing something */ j = sdir->nscans; if (datah < 2 || ports[0] < 0) /* Connected? */ (void) sprintf(outstring, "!%s? 0 : waiting : %d : %s ;", /* No */ ptr[0], j + 1, sdir->scanName[j]); /* (I think this "waiting" state should never occur) */ else /* Presumably active */ (void) sprintf(outstring, "!%s? 0 : active : %s : %Ld : %Ld : %Ld : %d : %s ;", ptr[0], ldir.f2dfile, startbyte, nowbyte, endbyte, j + 1, sdir->scanName[j]); } /* End of else doing something */ return(outstring); /* End of if query */ } /* * Else not a query. We need bank status * */ if (ldir.bmode) { /* Bank mode? */ if (XLRGetBankStatus(dev, bank, &bkstat) != XLR_SUCCESS) { /* Yes, get bank status, OK? */ (void) fprintf(stderr, /* Nope */ "%s File2disc() ERROR: XLRGetBankStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't get bank status ;", ptr[0]); return(outstring); /* Error */ } /* * Else OK status info * */ (void) errors(); if (devinfo.NumDrives < 1) { /* Any disks available? */ error = 1002; /* Message number for this case */ (void) strcpy(messg, "No disks available"); (void) sprintf(outstring, "!%s = 6 : No disks available ;", ptr[0]); return(outstring); /* Error */ } /* * We can't write to disk if write protected * */ if (bkstat.WriteProtected) { /* Protected? */ (void) sprintf(outstring, "!%s = 6 : Bank is write protected ;", ptr[0]); /* Yes */ return(outstring); /* Error */ } /* * Else not protected * */ } /* End of if bank mode */ /* * Check: OK to start this transfer? * */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ (void) sprintf(outstring, /* Nope */ "!%s = 6 : Not while other activity ;", ptr[0]); return(outstring); /* Error, must wait for other activity to finish */ } if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s File2disc() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't get device status ;", ptr[0]); return(outstring); /* XLR error */ } if (devstatus.Recording || devstatus.Playing) { /* Recording or Playing? */ (void) sprintf(outstring, /* Yes */ "!%s = 6 : Not while recording or playing ;", ptr[0]); return(outstring); /* Error, must wait for other activity to finish */ } /* * OK. All arguments have defaults and are optional. Set defaults: * */ if (ldir.f2dlstn < 0) /* Been here, done this? */ (void) strcpy(ldir.f2dfile, "save.data"); /* Nope, dumb default */ /* Else keep previous f2dfile */ startbyte = 0; endbyte = 0; /* All of file (changed below) */ if (ptr[1] != NULL && ptr[1][0] != '\0') /* File name? */ (void) strncpy(ldir.f2dfile, ptr[1], MAXNAME); /* Yes */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[2][0] != '\0') /* Start byte number? */ startbyte = atoll(ptr[2]); /* Yes */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[3][0] != '\0') /* End byte number? */ endbyte = atoll(ptr[3]); /* Yes */ /* * Optional arguments: scan name or scan label? * */ if (ptr[1] != NULL && ptr[2] != NULL && ptr[3] != NULL && ptr[4] != NULL) { /* Scan name? */ /* If we find multiple fields, then we interchange the order to make * up the scan label: field2_field3_field1_field4, with underlines (_) * as shown. Note the field1 is ptr[4], field2 is ptr[5], etc. * The canonical order is: Scan : Experiment : Station : Source ; * which we make into: Experiment_Station_Scan_Source */ scanName[0] = '\0'; /* Initialize */ sdir->scanName[sdir->nscans][0] = '\0'; mlen = MAXLENGTH - 1; /* mlen is number of bytes available */ /* * Here we look for an optional experiment name, station code, and * source name, and, if so, we append it (them) onto the scan label * with underline(s) ('_') as separator(s). Note that we allow * ptr[5] and the others to be null strings, which generate double * underlines(__) with nothing between * */ if (ptr[5] != NULL) { /* Experiment name also? */ (void) strncat(sdir->scanName[sdir->nscans], ptr[5], mlen); /* Yes */ (void) strncat(scanName, ptr[5], mlen); } if (ptr[5] != NULL && ptr[6] != NULL) { /* Station code also? */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Yes, space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); mlen = MAX(0, mlen - 1); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[6], mlen); (void) strncat(scanName, ptr[6], mlen); } if (ptr[5] != NULL) { /* Previous token? */ /* Yes. We already know that ptr[4] != NULL, so we have a scan name, * but we append the underline only if we have also a previous token, * either experiment name or station code or both */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); } /* We already know that we have a scan name or scan label, * that is prt[2] != NULL */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[4], mlen); /* Save ptr[4] as a scanName[] */ (void) strncat(scanName, ptr[4], mlen); /* And a local copy */ mlen = MAX(0, MAXLENGTH - 1 - strlen(scanName)); /* Space left */ /* * For the possibility that this scan might turn out to be the * last scan on this disk pack, we append '+' onto sdir->scanName[]. * This '+' is omitted on the local copy, scanName[], which is used * to delete the '+' on record=off, if any * */ (void) strncat(sdir->scanName[sdir->nscans], "+", mlen); if (ptr[5] != NULL && ptr[6] != NULL && ptr[7] != NULL) { /* Source name also? */ mlen = MAX(0, MAXLENGTH - 1 - strlen(sdir->scanName[sdir->nscans])); /* Yes, space left */ (void) strncat(scanName, UNL, mlen); /* Underline, local copy */ (void) strncat(sdir->scanName[sdir->nscans], UNL, mlen); mlen = MAX(0, mlen - 1); /* Update space left */ (void) strncat(sdir->scanName[sdir->nscans], ptr[7], mlen); (void) strncat(scanName, ptr[7], mlen); } } /* End of if scan name or scan label */ else { /* No scan name, use file name less path if any */ if ((ptc = strrchr(ldir.f2dfile, '/')) != NULL) /* Found '/'? */ (void) strncpy(sdir->scanName[sdir->nscans], ptc + 1, MAXLENGTH - 1); else /* No '/', no path */ (void) strncpy(sdir->scanName[sdir->nscans], ldir.f2dfile, MAXLENGTH - 1); /* And delete suffix (e.g., .mk5), if any */ if ((ptc = strrchr(sdir->scanName[sdir->nscans], '.')) != NULL) /* Found '.'? */ ptc[0] = '\0'; /* Yes, delete '.' and all after */ } /* End of else no scan name */ sdir->start[sdir->nscans] = XLRGetLength(dev); /* Scan start, bytes */ /* Is this the same as dir.Length (at this time)? */ /* * Open input file * */ if ((fport = open64(ldir.f2dfile, O_RDONLY)) < 0) { /* OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s File2disc() ERROR: Can't open64() file ", me); perror(ldir.f2dfile); } (void) sprintf(outstring, "!%s = 4 : Can't open file ;", ptr[0]); ldir.f2ddoing = 0; return(outstring); /* Error */ } /* * Position file to start byte * */ if (lseek64(fport, startbyte, SEEK_SET) < 0) { /* To startbyte, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s File2disc() ERROR: \007 Can't lseek64() on file ", me); perror(ldir.f2dfile); (void) sprintf(outstring, "!%s = 4 : Can't seek on file ;", ptr[0]); ldir.f2ddoing = 0; return(outstring); /* Error */ } if ((file = fdopen(fport, "r")) == NULL) { /* Open also as a stream, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s File2disc() ERROR: Can't fdopen() file ", me); perror(ldir.f2dfile); (void) sprintf(outstring, "!%s = 4 : Can't fdopen() file ;", ptr[0]); ldir.f2ddoing = 0; return(outstring); /* Error */ } nowbyte = startbyte; /* Initialize */ orpntr = 0; if (endbyte <= startbyte) { /* Default to end of file? */ (void) fstat64(fport, &stats); /* Yes, get stats */ endbyte = stats.st_size; /* File size in bytes */ } /* * Transfer to the global array * */ ports[0] = fport; fports[0] = file; /* * Set DMS to "Recorded" * */ /* (We need to do the setLabel() before SS_MODE) */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s File2disc() WARNING: getLabel() returned error %d \n", me, k); /* (We try to go on */ } /* If DMS is already "Recorded", then skip setLabel() */ if (strcasecmp(dms, "Recorded") != 0 && /* Not already Recorded? */ ldir.dsmask & 0x4) { /* And allowed change to Recorded? */ (void) strcpy(dms, "Recorded"); /* OK, set DMS to Recorded */ if ((k = setLabel()) < 0) { /* Write VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s File2disc() WARNING: setLabel() returned error %d \n", me, k); /* (We try to go on */ } } /* * Configure SS for PCI to disks * */ if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s File2disc() WARNING: XLRSetMode() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't set SS mode ;", ptr[0]); return(outstring); /* Error */ } /* * Recording on * */ if (XLRAppend(dev) != XLR_SUCCESS) /* Record, OK? */ { (void) errors(); (void) sprintf(outstring, "!%s = 4 : Can't append recording ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); ldir.f2ddoing = 1; /* Now we're doing something */ datah = 7; /* Now main() handles the actual transfer */ ldir.f2dlstn = sdir->nscans; /* Remember this scan number */ (void) sprintf(outstring, "!%s = 1 ;", ptr[0]); /* OK, delayed completion */ return(outstring); /* Normal return */ } /* End of File2disc() */ static char * Task_ID() { /* task_ID */ int k; /* task_ID can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ (void) sprintf(outstring, "!task_ID? 0 : %ld ;", task_id); /* ?? More here ?? */ return(outstring); /* Normal end for query */ } /* Else one required argument */ if (ptr[1] == NULL) /* Argument? */ return("!task_ID = 8 : Must have argument ;"); /* Nope, parameter error */ task_id = atoll(ptr[1]); /* Decode temporary task_id */ k = setrot(); /* Set rotman port to receive ROT broadcasts */ if (k < 0) /* OK? */ return("!task_ID = 4 : Can't setrot() ;"); /* Nope, error */ return("!task_ID = 0 ;"); /* Else OK */ } /* End of Task_ID() */ static char * Start_stats() { /* start_stats */ int i; char line[128]; /* Check: Must not be recording or playing (even for a query) */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ (void) sprintf(outstring, "!%s%s 6 : Not while other activity ;", ptr[0], pt == NULL ? " =" : "?"); return(outstring); /* Nope, error */ } if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Start_stats() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s%s 4 : Can't get device status ;", ptr[0], pt == NULL ? " =" : "?"); return(outstring); /* Error */ } (void) errors(); if (devstatus.Recording || devstatus.Playing) { /* Recording or Playing? */ (void) sprintf(outstring, "!%s%s 6 : Not while recording or playing ;", ptr[0], pt == NULL ? " =" : "?"); return(outstring); /* Yes, error */ } /* Else OK. start_stats can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ /* Yes. Get present (or default) drive-stats ranges */ if (XLRGetDriveStats(dev, 0, 0, dstats) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Start_stats() ERROR: XLRGetDriveStats() returned error \n", me); (void) errors(); return ("!start_stats? 4 : Can't get drive stats ;"); /* Error */ } (void) sprintf(outstring, "!%s? 0 ", ptr[0]); /* OK. Start of line */ for (i = 0; i < XLR_MAXBINS - 1; i++) { /* Each argument */ (void) sprintf(line, ": %.6fs ", 15.0e-9*dstats[i].range); /* (That's counts to seconds for a clock period of 15 ns) */ (void) strcat(outstring, line); } (void) strcat(outstring, ";"); /* After last argument */ return(outstring); } /* End of if a query */ /* Else not a query. Enable collection of drive statistics */ if (XLRSetOption(dev, SS_OPT_DRVSTATS) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Start_stats() ERROR: XLRSetOption() returned error \n", me); (void) errors(); return ("!start_stats = 4 : Can't set SS option ;"); /* Error */ } (void) errors(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Start_stats() DEBUG: XLRSetOption() OK \n", me); /* Get present (or default) drive-stats ranges */ if (XLRGetDriveStats(dev, 0, 0, dstats) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Start_stats() ERROR: XLRGetDriveStats() returned error \n", me); (void) errors(); return ("!start_stats = 4 : Can't get drive stats ;"); /* Error */ } /* Up to 7 arguments interpreted as times for the bin boundaries */ for (i = 0; i < XLR_MAXBINS; i++) { /* Each possible argument */ if (ptr[i+1] == NULL) /* Another argument? */ break; /* Nope, out of for i */ if (ptr[i+1][0] != '\0') /* Skip parameter? */ dstats[i].range = (unsigned long) (atof(ptr[i+1])/15.0e-9); /* Range */ /* (That's seconds to counts for a clock period of 15 ns) */ } for (i = 0; i < XLR_MAXBINS; i++) /* Each possible argument */ dstats[i].count = 0; /* Clear counts */ /* Now set these revised values */ if (XLRSetDriveStats(dev, dstats) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Start_stats() ERROR: XLRSetDriveStats() returned error \n", me); (void) errors(); return ("!start_stats = 4 : Can't get drive stats ;"); /* Error */ } drive = 0; /* Reset drive number for get_stats */ return("!start_stats = 1 ;"); /* Else OK, delayed completion */ } /* End of Start_stats() */ static char * Get_stats() { /* get_stats */ static char * nok[] = { "Fault", "OK", "NotSMART" }; char line[128]; int i; unsigned int bus, ms; /* Should have '?' but no arguments, but we don't check */ /* Check: Must not be recording or playing */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ (void) sprintf(outstring, "!%s? 6 : Not while other activity ;", ptr[0]); return(outstring); /* Nope, error */ } if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Get_stats() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s%s 4 : Can't get device status ;", ptr[0], pt == NULL ? " =" : "?"); return(outstring); /* Error */ } (void) errors(); if (devstatus.Recording || devstatus.Playing) { /* Recording or Playing? */ (void) sprintf(outstring, "!%s? 6 : Not while recording or playing ;", ptr[0]); return(outstring); /* Yes, error */ } if (XLRGetDeviceInfo(dev, &devinfo) != XLR_SUCCESS) { /* Device info, OK? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, "%s Get_stats() ERROR: \007 " /* Yes */ "XLRGetDeviceInfo() returned error \n", me); (void) errors(); /* We try to go on */ } /* Does this drive exist? If not, find one that does */ while (drive < (ldir.bmode ? 8 : 16)) { if (dinfo[drive].Serial[0] != '\0') /* OK? */ break; /* Yes, presume this drive exists */ drive++; /* Else not this one, try another */ } if (drive > (ldir.bmode ? 7 : 15)) /* Over? */ drive = 0; /* Yes, loop */ /* Else this drive is probably OK */ bus = drive / 2; ms = drive % 2; /* Assume one master and one slave per bus */ /* (XLR_MASTER_DRIVE = 0, XLR_SLAVE_DRIVE = 1) */ if (XLRGetDriveStats(dev, bus, ms, dstats) != XLR_SUCCESS) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Get_stats() ERROR: XLRGetDriveStats() returned error \n", me); (void) errors(); (void) XLRGetLastError(); /* Flush this error */ error = 0; (void) sprintf(outstring, "!%s? 4 : %d : Probably no such disk ;", ptr[0], drive); drive++; /* For next time */ return(outstring); /* Error (sort of) */ } /* OK. Start of line */ (void) sprintf(outstring, "!%s? 0 : %d ", ptr[0], drive); for (i = 0; i < XLR_MAXBINS; i++) { /* Each bin count */ (void) sprintf(line, ": %ld ", dstats[i].count); (void) strcat(outstring, line); /* Append */ } /* Add block count and SMART state after last bin count */ i = dinfo[drive].SMARTCapable ? dinfo[drive].SMARTState : 2; (void) sprintf(line, ": %ld : %s ;", XLRDiskRepBlkCount(dev, bus, ms), nok[i]); (void) errors(); (void) strcat(outstring, line); drive++; /* For next time */ return(outstring); /* Normal end */ } /* End of Get_Stats() */ static char * Replaced_blks() { /* replaced_blks */ unsigned int i, bus, ms; char line[32]; /* Should have '?' but no arguments, but we don't check */ /* Check: Must not be recording or playing */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ (void) sprintf(outstring, "!%s? 6 : Not while other activity ;", ptr[0]); return(outstring); /* Nope, error */ } if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Replaced_blks() ERROR: \007 " /* Yes */ "XLRGetDeviceStatus() returned error \n", me); (void) errors(); (void) sprintf(outstring, "!%s? 4 : Can't get device status ;", ptr[0]); return(outstring); /* Error */ } (void) errors(); if (devstatus.Recording || devstatus.Playing) { /* Recording or Playing? */ (void) sprintf(outstring, "!%s? 6 : Not during recording or playing ;", ptr[0]); return(outstring); /* Yes, error */ } if (XLRGetDeviceInfo(dev, &devinfo) != XLR_SUCCESS) { /* Device info, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, "%s Replaced_blks() ERROR: \007 " /* Yes */ "XLRGetDeviceInfo() returned error \n", me); (void) errors(); /* We try to go on */ } (void) sprintf(outstring, "!%s? 0 ", ptr[0]); /* Start of line */ for (i = 0; i < 8; i++) { /* Each potential disk */ if (dinfo[i].Serial[0] == '\0') { /* OK? */ (void) strcat(outstring, ": "); /* No, assume no such drive */ continue; /* To next i */ } /* Else OK */ bus = i / 2; ms = i % 2; /* Assume one master and one slave per bus */ /* (XLR_MASTER_DRIVE = 0, XLR_SLAVE_DRIVE = 1) */ (void) sprintf(line, ": %ld ", XLRDiskRepBlkCount(dev, bus, ms)); (void) strcat(outstring, line); } /* End of for i each disk */ (void) errors(); (void) sprintf(line, ": %ld ;", XLRTotalRepBlkCount(dev)); /* After disks */ (void) errors(); (void) strcat(outstring, line); return(outstring); /* Normal end */ } /* End of Replaced_blks() */ static char * SS_rev1() { /* SS_rev1 */ /* Should have '?' but no arguments, but we don't check */ /* Check: Must not be recording or playing */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ (void) sprintf(outstring, /* Nope */ "!%s? 6 : Not while other activity ;", ptr[0]); return(outstring); /* Nope, error */ } if (XLRGetDeviceInfo(dev, &devinfo) != XLR_SUCCESS) { /* Device info, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ss_rev1() ERROR: \007 XLRGetDeviceInfo() returned error \n", me); (void) errors(); return("!SS_rev1? 4 : Can't get device info ;"); /* Error */ } (void) errors(); if (XLRGetVersion(dev, &swrev) != XLR_SUCCESS) { /* SW Version, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s SS_rev1() ERROR: \007 XLRGetVersion() returned error \n", me); (void) errors(); return("!SS_rev1? 4 Can't get SS version ;"); /* Error */ } (void) errors(); (void) sprintf(outstring, "!SS_rev1? 0 : \"BoardType %s, SerialNum %d, " "ApiVersion %s, ApiDateCode %s\" ;", devinfo.BoardType, devinfo.SerialNum, swrev.ApiVersion, swrev.ApiDateCode); return(outstring); /* Normal end */ } /* End of SS_rev1() */ static char * SS_rev2() { /* SS_rev2 */ /* Should have '?' but no arguments, but we don't check */ if (XLRGetVersion(dev, &swrev) != XLR_SUCCESS) { /* SW Version, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s SS_rev2() ERROR: \007 XLRGetVersion() returned error \n", me); (void) errors(); return("!SS_rev2? 4 : Can't get SS version ;"); /* Error */ } (void) errors(); (void) sprintf(outstring, "!SS_rev2? 0 : \"FirmwareVersion %s, " "FirmDateCode %s, MonitorVersion %s, XbarVersion %s, " "AtaVersion %s, UAtaVersion %s, DriverVersion %s\" ;", swrev.FirmwareVersion, swrev.FirmDateCode, swrev.MonitorVersion, swrev.XbarVersion, swrev.AtaVersion, swrev.UAtaVersion, swrev.DriverVersion); return(outstring); /* Normal end */ } /* End of SS_rev2() */ static char * OS_rev1() { /* OS_rev1 */ FILE * pfile; char line[256]; /* Should have '?' but no arguments, but we don't check */ /* Open and read the version string in /proc/version */ if ((pfile = fopen("/proc/version", "r")) == NULL) { /* OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s OS_rev1() ERROR: \007 fopen() returned error on ", me); perror("/proc/version"); } (void) fclose(pfile); return("!OS_rev1? 4 : Can't fopen /proc/version ;"); /* Error */ } if (fgets(line, sizeof(line), pfile) == NULL) { /* Read, OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s OS_rev1() ERROR: \007 fgets() eturned error on ", me); perror("/proc/version"); } (void) fclose(pfile); return("!OS_rev1? 4 : Can't read /proc/version ;"); /* Error */ } (void) fclose(pfile); line[61] = '\0'; /* Delete last part of line */ (void) sprintf(outstring, "!OS_rev1? 0 : \"%s\" ;", line); return(outstring); /* Normal end */ } /* End of OS_rev1() */ static char * OS_rev2() { /* OS_rev2 */ FILE * pfile; char line[256]; /* Should have '?' but no arguments, but we don't check */ /* Open and read the version string in /proc/version */ if ((pfile = fopen("/proc/version", "r")) == NULL) { /* OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s OS_rev2() ERROR: \007 fopen() returned error on ", me); perror("/proc/version"); } (void) fclose(pfile); return("!OS_rev2? 4 : Can't open /proc/version ;"); /* Error */ } if (fgets(line, sizeof(line), pfile) == NULL) { /* Read, OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s OS_rev2() ERROR: \007 fgets() eturned error on ", me); perror("/proc/version"); } (void) fclose(pfile); return("!OS_rev2? 4 : Can't read /proc/version ;"); /* Error */ } (void) fclose(pfile); line[strlen(line)-1] = '\0'; /* Delete \n */ (void) sprintf(outstring, "!OS_rev2? 0 : \"%s\" ;", line+62); /* (Print only last part of line) */ return(outstring); /* Normal end */ } /* End of OS_rev2() */ static char * VSN() { /* VSN */ int i, k, ick = 8, icnt = 0, ok = FALSE; long long isz = 0LL; /* Size of disks */ char line[32] = ""; /* Scratch */ /* VSN works only in bank mode */ if (! ldir.bmode) { /* Bank mode? */ (void) sprintf(outstring, "!%s%s 6 : Not in bank mode ;", /* Nope */ ptr[0], pt == NULL ? " =" : "?"); return(outstring); /* Error */ } /* VSN can be a query; check that first */ if (pt != NULL) { /* Query? */ /* Yes. Check disk serial numbers first. * (We need devinfo, but we assume that it's already there) */ for (i = 0; i < 8; i++) /* Each disk (in bank mode) */ if (strncmp(pvsn->dinfo[i].Serial, dinfo[i].Serial, XLR_MAX_DRIVESERIAL) != 0) /* OK? */ break; /* Nope */ /* (We usually use i below) */ if ((k = getLabel(bank)) < 0) { /* Get VSN and DMS, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s VSN() ERROR: getLabel() returned error %d \n", me, k); if (k == -1) /* Can't get bank status? */ (void) sprintf(outstring, "!%s? 4 : Can't get bank status ;", ptr[0]); else if (k == -2) /* Bank not ready? */ (void) sprintf(outstring, "!%s? 4 : Bank not ready ;", ptr[0]); else /* Other error */ (void) sprintf(outstring, "!%s? 4 ;", ptr[0]); } /* End of if get VSN and DMS OK */ /* Else OK so far; cross check VSN, if possible */ else if (pvsn->vsn[0] != '\0' && strncmp(vsn, pvsn->vsn, strlen(vsn)) != 0) { /* OK? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s VSN() ERROR: VSN mismatch \"%s\" \"%s\" \n", me, vsn, pvsn->vsn); (void) sprintf(outstring, "!%s? 4 : %s : %s : VSN mismatch ;", ptr[0], vsn, pvsn->vsn); /* Error */ } /* Else VSN OK or blank, can we cross check serial numbers? */ else if (pvsn->dinfo[0].Serial[0] == '\0') { /* Got serial numbers? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s VSN() WARNING: No remembered serial numbers \n", me); /* (This might be from SSErase and is not an error) */ (void) sprintf(outstring, "!%s? 0 : %s : unknown ;", ptr[0], vsn); } /* Else OK to cross check serial numbers */ else if (i < 8) { /* All match? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s VSN() WARNING: Serial-number mismatch %d \"%s\" \"%s\" \n", me, i, pvsn->dinfo[i].Serial, dinfo[i].Serial); (void) sprintf(outstring, /* Error */ "!%s? 0 : %s : fail : %d : %s : %s : Disk serial-number mismatch ;", ptr[0], vsn, i, pvsn->dinfo[i].Serial, dinfo[i].Serial); /* (Note that we catch and log only the first mismatch) */ } else /* OK, all match */ (void) sprintf(outstring, "!%s? 0 : %s : OK ;", ptr[0], vsn); /* Note that in the return, the OK is to distinguish this case, * all match, from the case above, no remembered serial numbers */ return(outstring); /* Normal end for query */ } /* Else not a query; check for previous protect=off */ if (prot != 1) { /* Required previous command to be protect=off, OK? */ (void) sprintf(outstring, /* Nope */ "!%s = 6 : Previous protect=off required ;", ptr[0]); return(outstring); /* Error */ } /* One required argument, which must be 8 chars */ if (ptr[1] == NULL || strlen(ptr[1]) != 8) { /* Check, OK? */ (void) sprintf(outstring, /* Nope */ "!%s = 8 : VSN must be 8 chars ;", ptr[0]); /* Error */ return(outstring); /* Error */ } /* Other checks on argument */ for (i = 0; i < 8; i++) /* Each of the chars in ptr[1] */ if (isalpha(ptr[1][i])) /* Alphabetic? */ vsn[i] = toupper(ptr[1][i]); /* Yes, convert to upper case */ else if (ptr[1][i] == '-') { /* Hyphen separator? */ vsn[i] = '-'; /* Yes, copy it */ ick = i; /* And remember where we found it */ } else if (isdigit(ptr[1][i]) && i > ick) /* Numeric and after '-'? */ vsn[i] = ptr[1][i]; /* Yes, OK */ else /* None of the above is not OK */ ick = 9; /* Some error */ vsn[i] = '\0'; /* Temporary end of string */ if (strlen(vsn) != 8 || ick < 2 || ick > 6) /* OK? */ (void) sprintf(outstring, /* Nope */ "!%s = 8 : VSN must be OwnerID-SerialNum ;", ptr[0]); /* Error */ else { /* OK so far. Calculate the numbers to append. * First find the smallest disk capacity */ for (i = 0; i < 8; i++) { /* Each (potential) disk */ if (dinfo[i].Serial[0] == '\0') /* OK? */ continue; /* No, assume no such drive, to next i */ icnt++; /* Else count disks */ if (isz == 0 || dinfo[i].Capacity * 512LL < isz) /* Smallest or first? */ isz = dinfo[i].Capacity * 512LL; /* Yes */ } /* End of for i each disk */ (void) sprintf(line, "/%Ld/%d", (icnt*isz/10000000000LL)*10LL, 128*icnt); /* (That should be /capacity,GB/maxdatarate,Mbaud, and * capacity is now truncated to 10 GB) */ (void) strncat(vsn, line, sizeof(vsn)); /* Append to VSN */ ok = TRUE; } /* End of else OK */ /* If it passed all those checks, then we write the augmented VSN */ if (ok) { /* Augmented OK? */ if ((k = setLabel()) < 0) { /* Yes, write VSN, OK? */ (void) errors(); /* Nope */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s VSN() ERROR: setLabel() returned error %d \n", me, k); (void) sprintf(outstring, "!%s = 4 : Can't set label ;", ptr[0]); } /* Else OK, so update serial numbers in user directory */ else if (writedir() < 0) /* Save copy of current udir on SS disks, OK? */ (void) sprintf(outstring, "!%s = 4 : Can't write serial numbers ;", ptr[0]); /* Nope, error */ else /* OK */ (void) sprintf(outstring, "!%s = 0 ;", ptr[0]); /* Normal */ } /* End of if augmented */ (void) errors(); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s VSN() DEBUG: Augmented VSN %s \n", me, vsn); return(outstring); /* Normal or abnormal end */ } /* End of VSN() */ static char * Protect() { /* protect */ /* Protect can be a query; check that first */ if (pt != NULL) { /* Was it a query? */ /* Yes, but this can only work in bank mode */ if (! ldir.bmode) /* Bank mode? */ return("!protect? 6 : Can't determine protect in non-bank mode ;"); if (XLRGetBankStatus(dev, bank, &bkstat) != XLR_SUCCESS) { /* OK? */ (void) fprintf(stderr, /* Nope */ "%s Protect() ERROR: XLRGetBankStatus() returned error \n", me); (void) errors(); return("!protect? 4 : Can't get bank status ;"); } /* Else OK status info */ (void) errors(); if (bkstat.WriteProtected) /* Protected? */ return("!protect? 0 : on ;"); /* Yes */ return("!protect? 0 : off ;"); /* Else not protected */ } /* Else not a query */ if (ptr[1] == NULL || ptr[1][0] == '\0') /* Argument? */ return("!protect = 8 : Must have argument ;"); /* Nope, error */ if (strcasecmp(ptr[1], "on") == 0) { /* On? */ if (XLRSetWriteProtect(dev) != XLR_SUCCESS) { /* Yes, OK? */ if (msglev < 3) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Protect() ERROR: XLRSetWriteProtect() returned error \n", me); (void) errors(); return("!protect = 4 : Can't set write protect ;"); /* Error */ } return("!protect = 0 ;"); /* Else OK, normal end for case 'on' */ } else if (strcasecmp(ptr[1], "off") == 0) { /* Off? */ prot = 2; /* Yes. We trigger prot (for following command) * even if this protect=off fails (below) */ if (XLRClearWriteProtect(dev) != XLR_SUCCESS) { /* Clear protect, OK? */ if (msglev < 3) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Protect() ERROR: XLRClearWriteProtect() returned error \n", me); (void) errors(); return("!protect = 4 : Can't clear write protect ;"); /* Error */ } return("!protect = 0 ;"); /* Normal end for case 'off' */ } else /* Neither off nor on is an error */ return("!protect = 8 : Must be on or off ;"); } /* End of Protect() */ /* *** Preceding functions are intended to be called only by Parse5A() *** */ char * Parse5A (char * instring) { static int first = TRUE; /* First time? */ /* We arrange the following dictionary so that (1) first come functions * that can work even if delayed and not mounted, then (2) functions that * can work if delayed but can't work if not mounted, then (3) functions * that can work if not mounted but can't work if delayed, and lastly * (4) functions that can't work if either delayed or not mounted. * Beware: The starting and ending indices of these groups are coded below. * Order within groups is arbitrary. */ /* ** The dictionary: { "keyword", function } ** */ static struct Dict dict[] = { /* * Group 1, start 0 * */ { "input_mode", Input_mode }, { "mode", Mode }, { "play_rate", Play_rate }, { "track_select", Track_select }, { "track_set", Track_select }, { "play_mode", Play_mode }, { "dts_id", DTS_id }, { "task_ID", Task_ID }, { "OS_rev1", OS_rev1 }, { "OS_rev2", OS_rev2 }, { "net_protocol", Net_protocol }, { "disc_state_mask", Disc_state_mask }, { "disk_state_mask", Disc_state_mask }, /* * Group 2, start 13 * */ { "play", Play }, /* * Group 3, start 14 * */ { "reset", Reset }, { "status", Status }, /* * Group 4, start 16 * */ { "record", Record }, { "skip", Skip }, { "dir_info", Dir_info }, { "error", Error }, { "position", Position }, { "rtime", Rtime }, { "disc_size", Disc_size }, { "disk_size", Disc_size }, { "disc_serial", Disc_serial }, { "disk_serial", Disc_serial }, { "disc_error", Disc_error }, { "disk_error", Disc_error }, { "disc_state", Disc_state }, { "disk_state", Disc_state }, { "data_check", Data_check }, { "track_check", Data_check }, { "scan_check", Data_check }, { "scan_play", Scan_play }, { "next_scan", Next_scan }, { "scan_dir", Next_scan }, { "disc_model", Disc_model }, { "disk_model", Disc_model }, { "disc2net", Disc2net }, { "disk2net", Disc2net }, { "net2disc", Net2disc }, { "net2disk", Net2disc }, { "disc2file", Disc2file }, { "disk2file", Disc2file }, { "file2disc", File2disc }, { "file2disk", File2disc }, { "net2out", Net2out }, { "in2net", In2net }, { "SS_rev1", SS_rev1 }, { "SS_rev2", SS_rev2 }, { "start_stats", Start_stats }, { "get_stats", Get_stats }, { "replaced_blks", Replaced_blks }, { "set_scan", Set_scan }, { "scan_set", Set_scan }, { "bank_mode", Bank_mode }, { "bank_info", Bank_mode }, { "bank_switch", Bank_switch }, { "bank_select", Bank_select }, { "bank_set", Bank_select }, { "VSN", VSN }, { "protect", Protect }, { "recover", Recover } /* End of struct Dict dict[] dictionary */ }; static int ncmnd = sizeof(dict)/sizeof(struct Dict); char * qnull = NULL; /* Next char in instring or NULL */ char * pte = NULL; /* Point to '=', if any, at end of command */ /* (pt (in global) points to '?', if any, at end of query) */ int i, n, len; /* ** First time set defaults ** */ if (first) { /* Do first time only */ devstatus.Recording = FALSE; /* Debug */ outb.freq = sdir->playRate = 8.0; /* MHz */ devstatus.Playing = FALSE; /* Debug */ readdesc.AddrHi = 0; readdesc.AddrLo = 0; readdesc.XferLength = 0; sdir->plapnt = 0; task_id = 0; first = FALSE; /* Never again */ } /* ** Preliminaries ** */ len = strlen(instring); /* Length of input string */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() DEBUG: instring = %s", me, instring); /* instring normally ends with \n, but ... */ if (instring[len-1] != '\n') /* \n? */ (void) fprintf(stderr, " \n"); /* Nope, add one */ } prot = MAX(0, prot - 1); /* Decrement prot count */ /* (prot is used in reset and VSN commands) */ outstring[0] = '\0'; /* Initialize */ /* ** Token may end with a '?' (query) or '=' (command) ** */ pt = strchr(instring, '?'); /* Find '?', if any */ /* (If there was no '?', then pt = NULL) */ if ((pte = strchr(instring, '=')) != NULL) /* Found '='? */ pte[0] = '\0'; /* Yes, null it out (and pte != NULL remembers it) */ if (pt != NULL) /* Found '?'? */ pt[0] = '\0'; /* Yes, null it out (and pt != NULL remembers it) */ /* (In formal-parsing mode, there must be either '?' (query) or * '=' (command). Neither '?' nor '=' is an error, and both * '?' and '=' is also an error, but we can't check and return here * because we don't yet know the keyword) */ /* ** Get command or query ** */ if (forml) { /* * Formal parsing? * */ if ((ptr[0] = strtok(instring, ";\n\r")) == NULL) { /* Yes, keyword OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() ERROR: \007 No keyword in instring \n", me); return(NULL); /* Error */ } if ((ptr[0] = strtok(ptr[0], " \t")) == NULL) { /* Eliminate spaces, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() ERROR: \007 Blank keyword in instring \n", me); return(NULL); /* Error */ } } /* (NULL is the best we can do in case no keyword) */ else { /* * Informal parsing * */ if ((ptr[0] = strtok(instring, " =:;\t\n\r")) == NULL) { /* Get command, OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() ERROR: \007 No command in instring \n", me); return(NULL); /* Error */ } if (pte == NULL) /* Found '='? */ pte = ptr[0] + strlen(ptr[0]); /* Nope (or something) */ } /* End of else informal parsing */ /* * Find keyword in dict[] dictionary * */ for (n = 0; n < ncmnd; n++) /* Each of dict[] */ if (strcasecmp(ptr[0], dict[n].key) == 0) /* This one? */ break; /* Yes, found */ if (n >= ncmnd) { /* Did we find keyword in dict[]? */ /* Nope, error, probably mispeld */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() ERROR: \007 Unknown keyword %s \n", me, ptr[0]); (void) sprintf(outstring, "!%s%s 7 : Unknown keyword ;", ptr[0], pt == NULL ? " =" : "?"); /* (That 7 means "no such keyword" error) */ return(outstring); /* Error */ } /* Else OK; n is the keyword index number: dict[n] */ /* ** Get arguments of command, if any ** */ /* (In principle, queries can have arguments also, but none do) */ if (forml) { /* * Formal parsing? * */ /* * Now we can check for wrong syntax * */ if (pt == NULL && pte == NULL) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Parse5A() WARNING: " /* Yes */ " Keyword must have '=' or '?' following \n", me); (void) sprintf(outstring, "!%s 3 : Must have '=' or '?' ;", ptr[0]); /* (That 3 means "syntax error") */ return(outstring); /* Error */ } if (pt != NULL && pte != NULL) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Parse5A() WARNING: " /* Yes */ " Keyword must have either '=' or '?', not both \n", me); (void) sprintf(outstring, "!%s 3 : Must not have both '=' and '?' ;", ptr[0]); /* (That 3 means "syntax error") */ return(outstring); /* Error */ } /* Else OK so far. Get arguments, if any */ for (i = 1; i < 16; i++) { /* Up to 15 arguments */ if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() DEBUG: Start i %d \n", me, i); if (i == 1) { /* First argument? */ if (pt != NULL) /* Yes, query? */ qnull = pt+1; /* Yes, where to start looking */ else /* Not query, must be command */ qnull = pte+1; /* Where to start looking */ /* (pt and pte both NULL or both not NULL * were previously detected errors) */ if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() DEBUG: i 1, qnull \"%s\" \n", me, qnull); } /* End of if first argument */ if (qnull == NULL) { /* End? */ ptr[i] = NULL; /* Yes, set NULL pointer */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() DEBUG: End, ptr[%d] set NULL \n", me, i); break; /* Out of for i; the end */ } /* Else find this next argument */ ptr[i] = strsep(&qnull, ":;\n\r"); /* (But not whitespace) */ /* (See man pages for strsep() and strtok()) */ if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, "%s Parse5A() DEBUG: " /* Yes */ " From strsep(), ptr[%d] \"%s\", qnull \"%s\" \n", me, i, ptr[i] == NULL ? "NULL" : ptr[i], qnull == NULL ? "NULL" : qnull); /* Got argument, but it might be null */ if (ptr[i][0] != '\0') { /* Null string? */ ptr[i] = strtok(ptr[i], " \t"); /* Nope */ /* Not-null argument, so squeeze out whitespace, if any */ if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, "%s Parse5A() DEBUG: " /* Yes */ " From strtok(), ptr[%d] \"%s\" \n", me, i, ptr[i] == NULL ? "NULL" : ptr[i]); if (ptr[i] == NULL) /* NULL pointer (from strtok())? */ ptr[i] = ""; /* Yes, change to a null string */ } /* End of if not (initially) a null string */ if (ptr[i][0] == '\0') { /* Now is it a null string? */ if (msglev < 1) /* Yes. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() DEBUG: ptr[%d] is now a null string \n", me, i); if (qnull == NULL) { /* Any more arguments after this one? */ ptr[i] = NULL; /* Nope, set NULL pointer */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s Parse5A() DEBUG: " /* Yes */ " End of arguments; ptr[%d] set to NULL \n", me, i); break; /* Out of for i; the end */ } /* Else leave ptr[i] as a null argument */ continue; /* To next i */ } /* Else normal (not null) argument */ if (msglev < 1) /* Yes. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() DEBUG: ptr[%d] is \"%s\" \n", me, i, ptr[i]); } /* End of for i arguments */ } /* End of if formal */ else { /* * Informal parsing * */ for (i = 1; i < 16; i++) { /* Up to 15 arguments */ if (i == 1) { /* First argument? */ if (pt != NULL) /* Yes, query? */ qnull = pt+1; /* Yes, where to start looking */ else /* Not query, must be command */ qnull = pte+1; /* Where to start looking */ /* (pt and pte both NULL or both not NULL are errors) */ } /* End of if first argument */ if ((ptr[i] = strtok(qnull, " =:;\t\n\r")) == NULL) /* Get argument, OK? */ break; /* Nope, no more arguments */ if (strcmp(ptr[i], "-") == 0) /* Treat "-" as null? */ ptr[i] = ""; /* Yes, point to null string */ } /* End of for i arguments */ } /* End of else informal parsing */ if (i >= 16 && msglev < 2) /* Too many arguments and debuggery? */ (void) fprintf(stderr, /* Yes */ "%s Parse5A() WARNING: Too many arguments \n", me); /* (We try to go on) */ /* * Check: Group-2 functions can't work if not mounted * */ if (! ldir.mounted && n > 12 && n < 14) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Parse5A() WARNING: " /* Yes */ " Group-2 functions can't work if dismounted \n", me); (void) sprintf(outstring, "!%s%s 6 : Not mounted ;", /* Warning */ ptr[0], pt == NULL ? " =" : "?"); /* (That 6 means "inconsistent or conflicting request") */ return(outstring); /* Error, mount disks and try again */ } /* * Check: Group-3 functions can't work if delayed * */ if (ldir.delayed && n > 13 && n < 16) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Parse5A() WARNING: " /* Yes */ " Group-3 functions can't work if delayed \n", me); (void) sprintf(outstring, "!%s%s 5 : Wait for delayed ;", /* Warning */ ptr[0], pt == NULL ? " =" : "?"); /* (That 5 means "busy ... try again later") */ return(outstring); /* Error, try again later */ } /* * Check: Group-4 functions can't work if delayed or not mounted * */ if ((ldir.delayed || ! ldir.mounted) && n > 15) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, "%s Parse5A() WARNING: " /* Yes */ " Group-4 functions can't work if delayed or dismounted \n", me); (void) sprintf(outstring, "!%s%s 6 : Delayed or not mounted ;", ptr[0], pt == NULL ? " =" : "?"); /* Warning */ /* (That 6 means "inconsistent or conflicting request") */ return(outstring); /* Error, fix it and try again */ } /* ** Each command gets a specific function's response ** */ return(dict[n].fun()); /* Do it */ } /* End of Parse5A() */ int FFHeader (PS_READDESC prdesc, struct DTim * pdtime, int st, int ntracks, int trk) { /* Decode some information from the contents of the track buffer. * * Except in straight-through (st = TRUE) mode, the parity bit is stripped * out before recording. And, in the simplest (32-track) case, all * the bits in a given word will be the same within the frame header. * So, finding sync and decoding the time can be done on a word * or byte basis. No bit manipulation is needed. * Sync is characterized by 32 words of all 1s for the 32-track case, * or 64 words of 1s for the 64-track case. But we need to check also * the CRC to distinguish between Mark 4 and VLBA. * * Note that for the simplest case, 32 tracks, the sync pattern * will show 32 consecutive 32-bit words of all Fs. Finding this * is proof of sync, but we need also to compute the CRC to * distinguish between Mark-4 and VLBA formats. If neither CRC * works, then error and return negative. Actually we look at * each of the 4 bytes in each word on the assumption that we * might have a bad track--no data. If there is a bad track in * each of all 4 bytes, then we give up and return negative. * After finding sync and determining Mark-4 or VLBA, we decode * the date and time and other things to return in *pdtime. */ extern char * me; /* My name */ int kk = -1; int h, i, ii, iib, iic, j, jm, jj, k, l, myr, trkm, trkv, trkc; int hh, hhb; int iend, isrt, istep, ioff, jstrt, jend, jstep, mjd, hend, sstep; unsigned char * bufc; /* = BufferAddr */ unsigned char buft[64]; /* Temporary scratch */ char line[64]; /* Scratch */ unsigned long rbuf[BUFFL]; /* Local modified copy of rbuff[] */ struct date * dt; /* Scratch */ struct date tim; /* For time of second header */ double time1; /* TOT time of first header to seconds */ double time2; /* TOT time of second header to seconds */ double playRate = sdir->playRate; /* Local copy to modify */ time_t timex; /* From time() */ struct tm * tmpo; /* From gmtime() */ int mo, mda, nwd, juld; /* For daze() */ int jd, tmjd; /* Julian day numbers */ int nyr, nyrd; /* For dazl() */ int vlba = FALSE; int bitt; /* Mask bits (in byte) to search for sync */ int bitm; /* Mask one track (in byte) for CRC and decode */ int bits, bitc; /* Scratch */ /* * * Initialize * * */ iend = prdesc->XferLength; /* Bytes */ if (st) { /* Straight through (ST) mode? */ sstep = 9; /* Yes, parity */ hend = 36; jstep = ntracks / 8; /* Undo NRZ encoding for this case */ rbuf[0] = 0xffffffff; /* ?? */ for (i = 1; i < BUFFL; i++) /* Each rbuf() */ rbuf[i] = prdesc->BufferAddr[i-1] ^ prdesc->BufferAddr[i]; bufc = (unsigned char *) rbuf; /* Modified data as bytes */ } else { /* Not ST */ sstep = 8; /* No parity */ if (ntracks == 64) { /* 64 tracks is a special case */ hend = 64; /* To find sync */ jstep = 4; /* Actually step size for h */ } else { /* Not 64 tracks */ hend = 32; jstep = ntracks / 8; } bufc = (unsigned char *) prdesc->BufferAddr; /* Data as bytes */ } istep = ntracks / 8; /* (Misnamed) */ /* 8 bytes for 64 tracks, 4 for 32, 2 for 16, 1 byte for 8 tracks */ if (trk < 0) { /* Normal case from data_check or scan_check? */ bitm = 0x08; /* Yes (arbitrary choice of track to check CRC) */ if (ntracks < 32) /* 8 or 16 track case? */ bitt = 0xfe; /* Yes */ /* (The point is to allow 8- or 16-track Mark-III recordings, * which really have only 7 or 14 tracks recorded) */ else /* 32 or 64 tracks, take default */ bitt = 0xff; jstrt = 0; jend = ntracks / 8; } else { /* Case from track_check, trk >= 0 */ /* Certain cases require a modified bit number, * See "Mark 5A Track Mapping," Mark-5 Memo 011.1. We check * the modified bit number, trkm, to which the prescribed * track should be mapped. Note 0 <= trk < 64 */ trkm = (ntracks < 32 ? trk / 2 : trk) % ntracks; bitt = bitm = 1 << trkm % 8; jstrt = trkm / 8; jend = jstrt + 1; } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: iend %d, istep %d, st %d, trk %d \n", me, iend, istep, st, trk); /* * * Find five sequential frame headers (either VLBA or Mark 4) * * */ for (l = 0; l < 5; l++) { /* Each frame header */ if (l == 0) /* First frame header? */ isrt = 8*sstep*istep; /* Yes, start near the beginning of the buffer */ /* (We might back up over this to get aux data) */ else /* Subsequent frame header */ isrt = ioff+2496*sstep*istep; /* Start offset near next header */ /* (Should be integer multiple of 4) */ /* First try to find sync */ for (j = jstrt; j < jend; j++) { /* Each byte in a word (if necessary) */ if (trk >= 32 && ntracks == 64) /* Special case? */ jm = j - 4; /* Yes */ else /* Normal case */ jm = j; for (i = isrt; i < iend; i += 4) { /* Through input buffer (bytes) */ /* Except in ST mode, the bit preceding sync should be 0 */ if (!st && (bufc[j+i-istep] & bitt) != 0) /* OK? */ continue; /* Nope, to next i */ for (h = 0; h < hend; h++) { /* Check up to next hend bits */ if (h % sstep == 8) { /* Parity bit? */ if ((bufc[j+i+h*jstep] & bitt) != 0) /* Yes, OK? */ /* (bitt masks bits to use within each byte) */ break; /* Nope, out of for h */ else /* Parity OK */ continue; /* To next h */ } if ((bufc[jm+i+h*jstep] & bitt) != bitt) /* Not parity, OK? */ /* (Note all bits set in bitt must be set in bufc[]) */ break; /* Nope, out of for h */ } /* End of for h */ if (h >= hend) { /* Got? */ /* Yes, but now check for two zeros in the next byte */ hhb = 0; /* Initialize count zeros */ for (hh = 0; hh < 8; hh++) /* Each bit in the next byte */ if ((bufc[jm+i+(hend+hh)*jstep] & bitt) == 0 && ++hhb > 1) /* Got two zeros? */ break; /* Yes, out of for hh */ if (hhb > 1) /* Got two zeros? */ break; /* Yes, out of for i */ } /* Else try another i */ } /* End of for i */ if (i < iend) /* Found sync? */ break; /* Yes, out of for j */ /* Else try another j */ } /* End of for j */ if (j >= jend) { /* OK? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: Sync not found, st %d, ntracks %d \n", me, st, ntracks); if (l == 0) /* First header? */ return(-1); /* Yes, warning, sync not found */ /* Else sync not found for this header, but l > 0, so we seem * to have found one header but failed to find all five. * In this case, we try to make do without frame-period information */ return(1); /* No frame-period information */ } /* Else we found sync for this l (end of try to find sync), * and j+i is the byte index to the start of this sync (in bufc[]) */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: Got sync: i %d, j %d, st %d, ntracks %d \n", me, i, j, st, ntracks); /* * * Is it VLBA? Calculate VLBA CRC and check * * */ for (iib = 0; iib < 20; iib++) /* Each buft[] byte */ buft[iib] = 0; /* Initialize to clear */ /* Read 160 bits (of 180 if parity) to include the whole header. * Note that we interpret this header in the Mark-4 style, namely * that the aux-data block precedes the sync word. In VLBA speak, * this is the aux-data block belonging to the preceding frame */ for (ii = 0; ii < 20*sstep; ii++) { /* Each bufc[] bit offset */ if (ii % sstep == 8) /* Yes. Skip parity bit? */ continue; /* Yes, to next ii */ /* Start at j+i minus 8 bytes before sync (each istep bytes). * All bits in all bytes should be the same across all active tracks * except bytes 0, 1, 4, 5, and 6. This is empirical and does not * seem to correspond to Alan's Figure 2, page 28, alas. * I think that NRAO has changed the definitions of some of these * bytes since Alan's memo was written. */ iib = ii / sstep; /* Byte number in buft[] */ if (iib < 2 || iib == 4 || iib == 5 || iib == 6) /* Check multiple tracks? */ bitc = bitm; /* No, check just this one track */ else /* Check all tracks in bitt */ bitc = bitt; if ((bits = bufc[j+i+(ii-8*sstep)*istep] & bitc) == bitc) { /* Bit(s) Set? */ if (iib < 4 || iib > 11) /* Yes. Some bytes are bit reversed */ iic = 7 - ii % sstep; /* Bit number in buft[iib] */ else /* And some are not */ iic = ii % sstep; /* Bit number in buft[iib] */ buft[iib] |= 1 << iic; /* Set this bit in buft[] */ } /* End of if bit(s) set */ else if (bits != 0) /* Error? */ break; /* Yes error, all tracks are not the same, out of for ii */ } /* End of for ii */ if (ii < 20*sstep) { /* Error? */ if (msglev < 1) /* Yes, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: Not VLBA l %d, ii %d, bitc %#x, bits %#x \n", me, l, ii, bitc, bits); kk = -1; /* Error */ } else /* OK (so far) */ kk = 0; /* Initialize */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, "%s FFHeader() DEBUG: VLBA buft[] = ", me); for (iib = 0; iib < 20; iib++) /* Each buft[] byte */ (void) fprintf(stderr, "%02x", buft[iib]); (void) fprintf(stderr, " \n"); } /* End of if debuggery */ /* Calculate VLBA CRC-16 over 48 bits of time code (only) */ k = crcc(buft + 12, 48, 040003, 16); /* Get (assumed VLBA) CRC from data to compare */ for (jj = 0; jj < 8; jj++) { /* Each bit in buft[18] and buft[19] */ if (buft[19] & 1 << jj) /* Set? */ kk |= 1 << 15 - jj; /* Yes, set corresponding bit in kk */ if (buft[18] & 1 << jj) /* Set? */ kk |= 1 << 7 - jj; /* Yes, set corresponding bit in kk */ } /* End of for jj */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: vlba k %#x kk %#x \n", me, k, kk); if (k == kk) { /* Did we find it as VLBA? */ vlba = TRUE; /* Yes */ (void) strcpy(formt, "vlba"); /* Label it */ /* (Note this is not put into outb.formt[]) */ /* Decode VLBA "MJD" and time */ (void) sprintf(line, "%02x%02x%02x%02x%02x%02x", /* First to hex */ buft[12], buft[13], buft[14], buft[15], buft[16], buft[17]); if (l > 0) /* Subsequent frame header? */ dt = &tim; /* Yes, local struct */ else /* First frame header */ dt = &pdtime->timet; /* Point to data time */ (void) sscanf(line, "%3d%9lf", &mjd, &dt->second); /* To day and time */ mjd = (mjd + 1) % 1000; /* MJD convention, see AENA B4 */ /* Here we need to add half a decimillisecond (50 microseconds) * in case the decimillisecond digit is 2 or 7 (the only other * legal values are 0 and 5) */ if ((buft[17] & 0x0f) % 5) /* Not 0? */ dt->second += 0.5; /* Yes (that's actually 50 microseconds) */ dt->second *= 1.0e-4; /* To seconds */ if (l == 4) { /* Last frame header? */ if (trk >= 0) { /* Yes, case track_check? */ (void) sprintf(line, "%02x", buft[4]); /* Yes, get track number */ trkt = -1; /* Initialize in case sscanf() fails */ (void) sscanf(line, "%2o", &trkt); /* Track number from header as octal */ /* (See table 18, page 24 of "Mark IIIA/IV/VLBA Tape Formats, * Recording Modes and Compatibility," memo 230.3) And note on * page 11 that D = DAR non-zero means upper 32 tracks (of 64). * This is really just empirical. */ if (buft[6] & 0xf0) /* D or headstack 1 means upper 32 tracks? */ trkt += 16; /* Yes */ /* Calculate what trkt should be (VLBA case) */ trkv = trk / 2 + (trk + 3) % 2 * 16; if (ntracks < 32) trkv += (trk / 32) * 16; if (trkt != trkv) { /* Track number OK? */ /* Not OK, but is it an error or a duplicated track? */ trkc = ntracks < 32 ? trkm * 2 : trkm; trkc = trkc / 2 + (trkc + 3) % 2 * 16; /* Should duplicate this one */ if (trkt == trkc) /* Correctly duplicated track? */ trke = 1; /* Yes */ else /* Not */ trke = 2; /* Error, wrong track found */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s FFHeader() WARNING: " /* Yes */ " Track number %d (0%s) should be %d \n", me, trkt, line, trkv); } /* End of if track number not OK */ else /* Track number OK */ trke = 0; /* Not error */ /* Calculate decoded equivalent track number (VLBA case) */ trkd = 3 + trkt % 16 * 2 - trkt / 16; if (buft[6] & 0xf0) /* D means upper 32 tracks? */ trkd += 101; /* Yes */ /* (See table 18, page 24 of "Mark IIIA/IV/VLBA Tape Formats, * Recording Modes and Compatibility," memo 230.3) */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s FFHeader() DEBUG: " /* Yes */ " vlba trk %d, trkt %d, trkv %d, trkd %d \n", me, trk, trkt, trkv, trkd); } /* End of if trk case track_check */ time2 = dt->second; /* Time on last frame header */ pdtime->timep = (int)((time2-time1)*200.0+0.5)*0.00125; /* Track frame period, seconds. That's /4 because we * read 5 frame headers and /0.00125 to convert to steps * of 1.25 milliseconds because timep must be an integer * times 1.25 milliseconds */ pdtime->period = (i-pdtime->offset)/4; /* That's last minus first over 4 steps to give * track frame period, SS bytes */ playRate = pdtime->period / pdtime->timep / istep * 8.0e-6 / sstep; /* That e-6 converts to MHz; 8/sstep accounts for parity */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s FFHeader() DEBUG: period %Ld, " "timep %.4f, istep %d, sstep %d, playRate %.3f \n", me, pdtime->period, pdtime->timep, istep, sstep, playRate); } /* End of if last frame header */ else if (l == 0) { /* First frame header? */ time1 = dt->second; /* Yes. (Others are 0) */ /* We have only the mjd, which here means the last 3 digits of the * Julian day number. We assume that the most recent previous * date when the Julian day number had these last 3 digits gives * the correct date--year and day of the year. The following * rigmarole is to find this date, which will be wrong if the * data are more than about 1000 days = 2.74 years old. */ timex = time((time_t *) NULL); /* What time is it? */ tmpo = gmtime(&timex); /* Convert to date and time */ tmpo->tm_year += 1900; /* To conventional year */ tmpo->tm_yday++; /* To day of the year */ daze_(&tmpo->tm_year, &tmpo->tm_yday, &mo, &mda, &nwd, &juld); /* Get juld, today's Julian day number */ tmjd = juld % 1000; /* Today's equivalent mjd (3 digits) */ jd = juld - tmjd + mjd - (tmjd < mjd ? 1000 : 0); /* Now jd is our best guess for the Julian day number for the data */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: juld %d, mjd %d, jd %d \n", me, juld, mjd, jd); dazl_(&jd, &nyr, &mo, &mda, &nyrd, &nwd); /* Get year and day of the year from this Julian day number */ dt->year = nyr; /* (To shorts) */ dt->day = nyrd; /* dt->second has all the rest */ dt->hour = (int)(dt->second/3600.0); dt->second -= dt->hour*3600.0; dt->minute = (int)(dt->second/60.0); dt->second -= dt->minute*60.0; if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: nyr %d, nyrd %d, %2.2d:%2.2d:%05.2f \n", me, nyr, nyrd, dt->hour, dt->minute, dt->second); ioff = i; /* Offset in buffer to first header */ /* (Or -8*istep if the aux data is viewed as the start of a frame) */ pdtime->offset = ioff; /* Return offset to first header in SS bytes */ } /* End of else first frame header */ else /* Other frame header (neither first nor last) */ ioff = i; /* Offset in buffer to this header */ continue; /* To next l (normal for case VLBA) */ } /* End of if k as VLBA */ /* * Else not VLBA; is it Mark 4? Calculate Mark-4 CRC and check * */ for (iib = 0; iib < 20; iib++) /* Each buft[] byte */ buft[iib] = 0; /* Initialize */ /* Read 160 bits (of 180 if parity) to include the whole header */ for (ii = 0; ii < 20*sstep; ii++) { /* Each bufc[] bit offset */ if (ii % sstep == 8) /* Yes. Skip parity bit? */ continue; /* Yes, to next ii */ /* Start at j+i minus 8 bytes of aux data (each actually istep bytes) */ iib = ii / sstep; /* Byte number in buft[] */ iic = 7 - ii % sstep; /* Bit number in buft[iib] */ /* (Because the bits are in this order) */ if (iib > 11 && iib < 18) { /* Bytes 12 thru 17 must be the same in all active tracks */ if ((bits = bufc[j+i+(ii-8*sstep)*istep] & bitt) == bitt) buft[iib] |= 1 << iic; /* Set this bit in buft[] */ else if (bits != 0) /* Error? */ break; /* Yes, out of for ii */ } /* Else outside 12 thru 17 so check just the one track in bitm */ else if (bufc[j+i+(ii-8*sstep)*istep] & bitm) /* Bit set? */ /* (bitm is to be a 1-bit mask for a typical track) */ buft[iib] |= 1 << iic; /* Yes, set this bit in buft[] */ } /* End of for ii */ if (ii < 20*sstep) { /* Error? */ if (msglev < 1) /* Yes. Debuggery? */ (void) fprintf(stderr, "%s FFHeader() DEBUG: " /* Yes */ " Not mark4 l %d, ii %d, bitt %#x, bits %#x \n", me, l, ii, bitt, bits); kk = -1; /* Error */ } else /* OK (so far) */ kk = 0; /* Initialize */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, "%s FFHeader() DEBUG: mark4 buft[] = ", me); for (iib = 0; iib < 20; iib++) /* Each buft[] byte */ (void) fprintf(stderr, "%02x", buft[iib]); (void) fprintf(stderr, " \n"); } /* End of if debuggery */ /* Calculate CRC-12 over 148 bits of aux data, sync, and time code */ k = crcc(buft, 148, 007003, 12); /* Get CRC from data to compare. Now would you believe (!) that * the three nibbles are in the wrong order (i.e., in increasing * significance with time rather than decreasing significance with * time as is the time), AND the bits within the nibbles are also * in wrong order (compared to all the other nibbles in the header). * So ... */ for (jj = 0; jj < 8; jj++) /* Each bit in buft[19] */ if (buft[19] & 1 << jj) /* Set? */ kk |= 1 << 11 - jj; /* Yes, set corresponding bit in kk */ for (jj = 0; jj < 4; jj++) /* Each bit in the right nibble of buft[18] */ if (buft[18] & 1 << jj) /* Set? */ kk |= 1 << 3 - jj; /* Yes, set corresponding bit in kk */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: mark4 k %#x kk %#x \n", me, k, kk); if (kk == k) { /* Did we find it as Mark 4? */ (void) strcpy(formt, "mark4"); /* Yes, label it */ /* (Note this is not put into outb.formt[]) */ /* Decode Mark-4 time */ (void) sprintf(line, "%02x%02x%02x%02x%02x%02x%02x", /* To hex */ buft[12], buft[13], buft[14], buft[15], buft[16], buft[17], buft[18]); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s FFHeader() DEBUG: line = %s \n", me, line); if (l > 0) /* Subsequent frame header? */ dt = &tim; /* Yes, local struct */ else /* First frame header */ dt = &pdtime->timet; /* Point to data time */ (void) sscanf(line, "%1hd%3hd%2hd%2hd%5lf", /* To struct date */ &dt->year, &dt->day, &dt->hour, &dt->minute, &dt->second); dt->second += (buft[18] >> 4) % 5 * 0.25; /* Add the infamous quarter milliseconds if necessary */ dt->second /= 1000.0; /* Milliseconds to seconds */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s FFHeader() DEBUG: " /* Yes */ " l %d year %d day %03d hour %02d min %02d second %08.5f \n", me, l, dt->year, dt->day, dt->hour, dt->minute, dt->second); if (l == 4) { /* Last frame header? */ if (trk >= 0) { /* Yes, case track_check? */ (void) sprintf(line, "%02x", buft[4]); /* Yes, decode track number */ trkt = -1; /* Initialize in case sscanf() fails */ (void) sscanf(line, "%2d", &trkt); /* Track number from header */ trkv = trk > 31 ? trk + 10 : trk + 2; /* trkv is what trkt should be in the simplest case */ if (trkt != trkv) { /* Track number OK? */ /* Not OK, but is it an error or a duplicated track? */ trkc = (ntracks < 32 ? trkm * 2 : trkm) + (trkm > 31 ? 70 : 2); /* (ntracks=64 only) */ /* trkc should be a duplicate of trkt */ if (trkt == trkc) /* Correctly duplicated track? */ trke = 1; /* Yes, show 'D' */ else if (trkt < 0) { /* Special case Mark-III has no track numbers? */ trke = 0; /* Yes, but this is not really an error */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() WARNING: Track number %d should be %d \n", me, trkt, trkc); } else { /* Not */ trke = 2; /* Error, wrong track found, show '?' */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() WARNING: Track number %d should be %d \n", me, trkt, trkc); } } /* End of if track number not OK */ else /* Track number OK */ trke = 0; /* Not error */ /* Calculate decoded equivalent track number (Mark-4 case) */ trkd = trkt > 40 ? trkt + 60 : trkt; } /* End of if trk case track_check */ time2 = dt->day*86400.0 + dt->hour*3600.0 + dt->minute*60.0 + dt->second; /* Time on last frame header to seconds */ pdtime->timep = (int)((time2-time1)*200.0+0.5)*0.00125; /* Track frame period, seconds. That's /4 because we * read 5 frame headers and /0.00125 to convert to steps * of 1.25 milliseconds because timep must be an integer * times 1.25 milliseconds */ pdtime->period = (i-8*sstep*istep-pdtime->offset)/4; /* That's last minus first over 4 steps to give * track frame period, SS bytes */ playRate = pdtime->period / pdtime->timep / istep * 8.0e-6 / sstep; /* That e-6 converts to MHz; 8/sstep accounts for parity */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s FFHeader() DEBUG: period %Ld, " "timep %.4f, istep %d, sstep %d, playRate %.3f \n", me, pdtime->period, pdtime->timep, istep, sstep, playRate); } /* End of if last frame header */ else if (l == 0) { /* First frame header? */ time1 = dt->day*86400.0 + dt->hour*3600.0 + dt->minute*60.0 + dt->second; /* Time on first frame header to seconds */ ioff = i-8*sstep*istep; /* Offset in buffer to this header */ /* Here we make an educated guess at the correct year given that * dt->year now contains only the last digit of the year */ timex = time((time_t *) NULL); /* What time is it? */ tmpo = gmtime(&timex); /* Convert to date and time */ /* Now we pick the most recent past or present year that * ends in the digit now in dt->year */ tmpo->tm_yday++; /* To day of the year */ tmpo->tm_year += 1900; /* To conventional year */ myr = tmpo->tm_year % 10; /* Last digit of current year */ if (myr == dt->year && dt->day <= tmpo->tm_yday) /* This year? */ dt->year = tmpo->tm_year; /* Yes */ else /* The most recent possible previous year */ dt->year += tmpo->tm_year - myr - (dt->year >= myr ? 10 : 0); pdtime->offset = ioff; /* Return offset to first header in SS bytes */ } /* End of else if first frame header */ else /* Other frame header (neither first nor last) */ ioff = i-8*sstep*istep; /* Offset in buffer to this header */ continue; /* To next l (normal for case Mark 4) */ } /* End of if k as Mark 4 */ else /* k != kk */ break; /* Out of for l */ } /* End of for l five frame headers */ if (l >= 4) { /* OK? */ if (vlba) /* Yes. VLBA? */ pdtime->offset -= 8*sstep*istep; /* Yes, back over aux data */ return(0); /* Normal end */ } /* Else error: Sync found, but VLBI header CRC is neither Mark 4 nor VLBA */ if (msglev < 1) { /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s FFHeader() DEBUG: Sync found, but can't identify header \n", me); (void) fprintf(stderr, " st %d, ntracks %d \n", st, ntracks); } return(-2); /* Found sync but not VLBI header */ } /* End of FFHeader() */ int PatScan (PS_READDESC prdesc) { /* Look for other patterns */ unsigned long patrn; /* Search pattern */ unsigned long patsv; /* Save value of pattern */ unsigned long patpr; /* Previous value of pattern */ long j, jend, k; /* * Here we have a problem--VLBI data not found--so we look, instead, * first for SS pattern (SSPAT) * */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s PatScan() DEBUG: Looking for SS test pattern \n", me); /* (pati, patj, and patlen are used by Data_scan()) */ patlen = prdesc->XferLength/4; /* Length in 32-bit words */ for (pati = 0; pati < patlen; pati++) /* Search through the whole buffer */ if (prdesc->BufferAddr[pati] == SSPAT) /* SS pattern? */ break; /* Yes */ if (pati < patlen) { /* Found start of SS pattern? */ for (patj = pati+1; patj < patlen; patj++) { /* Yes, scan from here */ if (prdesc->BufferAddr[pati] != SSPAT) /* In pattern? */ break; /* Nope, out of for patj */ } if (msglev < 2) { /* Yes. Debugery? */ (void) fprintf(stderr, /* Yes */ "%s PatScan() WARNING: Found SS pattern, not VLBI data \n", me); (void) fprintf(stderr, " start word %ld, end %ld, end buffer %ld \n", pati, patj, patlen); } return(-2); /* Error: Found SS pattern instead of VLBI data */ } /* * Else we have neither VLBI data nor SS pattern, so try TVG pattern * */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s PatScan() DEBUG: Looking for TVG pattern \n", me); /* Note that the TVG pattern restarts at each 1-second tic, and note * that the buffer we have contains much less than a second of data, * therefore a 1-second tic inside the buffer is statistically * unlikely. This means that we should search through the TVG * pattern to find a match to the start of the buffer rather than * searching through the buffer to find a match to the start of * the TVG pattern. But the t0 word of the TVG pattern is * undefined, so we need to search more than the first word of * the buffer in case the pattern starts (t0) on the first word of * the buffer. We also need to allow for possibly high error * rates--this is, after all, a test. */ for (pati = 0; pati < 9; pati++) { /* Check the first nine words of the buffer */ for (k = 0; k < 32768; k++) /* Search a full cycle of TVG pattern */ if (prdesc->BufferAddr[pati] == tvg(k == 0)) /* Found? */ break; /* Yes, out of for k */ if (k < 32768) /* Found? */ break; /* Yes, out of for pati */ } if (pati >= 9) { /* Found start of TVG pattern? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s PatScan() WARNING: No known pattern found \n", me); jend = msglev < 0 ? patlen/2 : 16; /* LOTS of print or just 4 lines? */ if (msglev < 1) /* More debuggery? */ for (j = 0; j < jend; j += 4) /* Yes */ (void) fprintf(stderr, "%s PatScan() DEBUG: " " j %05ld %#10.8lx %#10.8lx %#10.8lx %#10.8lx \n", me, j, prdesc->BufferAddr[j], prdesc->BufferAddr[j+1], prdesc->BufferAddr[j+2], prdesc->BufferAddr[j+3]); return(-3); /* Presumably an error: Not a known pattern */ } patpr = prdesc->BufferAddr[pati]; /* Previous value of pattern */ /* Here we seem to have TVG pattern; follow it as far as possible. * k shows where in the cycle we start, and i shows where in * the buffer we start */ for (patj = pati+1; patj < patlen; patj++) { /* Scan from here */ patsv = tvg(FALSE); /* Next pattern (save for debug print below) */ if (prdesc->BufferAddr[patj] != patsv) { /* In pattern? */ /* No, but this might be a 1-second reset: Check this */ patrn = tvg(TRUE); /* Back to the first word of TVG pattern */ if (prdesc->BufferAddr[patj] == patrn || /* Restart pattern? */ /* (This case occurs if t0 has the last word of the previous cycle, * so patj is already on t1) */ prdesc->BufferAddr[++patj] == patrn) /* Or skiped t0? */ /* (This case occurs if t0 has any other number, and patj needs to * be incremented to be on t1) */ { if (msglev < 2) /* Yes. Debuggery? */ (void) fprintf(stderr, "%s PatScan() WARNING: " /* Yes */ " TVG cycle restarted at %ld (%ld in cycle) \n", me, patj, (k-pati+patj) % 32768); patpr = patrn; /* Save as previous value of pattern */ continue; /* Cycle restarts, to next j */ } else /* Presumably end of pattern or error */ break; /* Out of for patj */ } /* Else yes, in pattern */ patpr = patsv; /* Previous value of pattern (for debug print below) */ } /* End of for patj scan from here */ /* End of TVG pattern */ if (msglev < 2) { /* Debugery? */ (void) fprintf(stderr, /* Yes */ "%s PatScan() WARNING: Found TVG pattern, not VLBI data \n", me); (void) fprintf(stderr, " start word %ld, end %ld, end buffer %ld \n", pati, patj, patlen); if (patj < patlen) { /* End of buffer? */ (void) fprintf(stderr, " previous pattern %#lx, buffer[end] %#lx, " " pattern %#lx, buffer[end+1] %#lx \n", patpr, prdesc->BufferAddr[patj-1], patsv, prdesc->BufferAddr[patj]); /* (patj was incremented above; this compensates) */ } } return(-4); /* Found TVG pattern, not VLBI data */ } /* End of PatScan() */ char * date2string (struct date ind) { /* Convert a date structure to a string to be printed in VLBI standard * format, namely nnnnynnndnnhnnmnns (19 chars including \0). * There are checks for no-fair values, which gives "Error" if so. * The string returned is NOT saved between calls. Make copies * if need be. */ static char line[64]; /* * Error checks * */ if (ind.year < 0 || ind.year > 2500 || /* OK? */ ind.day < 1 || ind.day > 366 || ind.hour < 0 || ind.hour > 23 || ind.minute < 0 || ind.minute > 59 || ind.second < 0.0 || ind.second > 60.0 || ((ind.day*24+ind.hour)*60+ind.minute)*60.0+ind.second > 31708801.0) /* (31708801 = 367*24*60*60+1, that 1 for a leap second) */ return(" Error "); /* Nope, error */ (void) sprintf(line, "%04dy%03dd%02dh%02dm%06.3fs", /* VLBI format */ ind.year, ind.day, ind.hour, ind.minute, ind.second); return(line); /* Normal end */ } /* End of date2string() */ double AddTime(char * instring) { /* Parse the "time" string in instring. Year, if * present, is ignored. Offset these from * dattime.timet..... to get the answer in seconds. * Some error checking is done, and errors cause * a negative return. */ char * ptt; char * ptn; int plus = FALSE; int dd = -1, hh = -1, mm = -1; int exd = 0, exh = 0, exm = 0, exs = 0; double add = 0.0, ss = -1.0; ptn = instring; /* Initialize */ if (ptn[0] == '+' || ptn[0] == '-') { /* Simple add? */ plus = TRUE; /* Yes */ ptn++; /* Skip over + or - */ } if ((ptt = strpbrk(ptn, "yY")) != NULL) /* Year? */ ptn = ptt + 1; /* Yes (but ignore the number) */ if ((ptt = strpbrk(ptn, "dD")) != NULL) { /* Day? */ dd = atoi(ptn); /* Yes */ if (dd < 1 || dd > 366) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s AddTime() WARNING: Parsing date/time in instring %s \n", me, instring); return(-1.0); /* Error */ } hh = mm = 0; /* All smaller units must be 0 */ ss = 0.0; ptn = ptt + 1; /* To start of next field */ } if ((ptt = strpbrk(ptn, "hH")) != NULL) { /* Hour? */ hh = atoi(ptn); /* Yes */ if (hh < 0 || hh > 23) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s AddTime() WARNING: Parsing date/time in instring %s \n", me, instring); return(-2.0); /* Error */ } mm = 0; /* All smaller units must be 0 */ ss = 0.0; ptn = ptt + 1; /* To start of next field */ } if ((ptt = strpbrk(ptn, "mM")) != NULL) { /* Minute? */ mm = atoi(ptn); /* Yes */ if (mm < 0 || mm > 59) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s AddTime() WARNING: Parsing date/time in instring %s \n", me, instring); return(-3.0); /* Error */ } ss = 0.0; /* All smaller units must be 0 */ ptn = ptt + 1; /* To start of next field */ } if (strpbrk(ptn, "sS") != NULL) { /* Second? */ ss = atof(ptn); /* Yes */ if (ss < 0.0 || ss >= 60.0) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s AddTime() WARNING: Parsing date/time in instring %s \n", me, instring); return(-4.0); /* Error */ } } /* Now process these semi-times */ if (ss >= 0.0) { /* Got second? */ if (plus) /* Yes, just add? */ add += ss; /* Yes */ else { /* Offset */ exs = ss < dattime.timet.second; add += ss - dattime.timet.second + 60.0 * exs; } } if (mm >= 0) { /* Got minute? */ if (plus) /* Yes, just add? */ add += 60.0 * mm; /* Yes */ else { /* Offset */ exm = mm < dattime.timet.minute + exs; add += 60.0 * (mm - dattime.timet.minute - exs + 60 * exm); } } if (hh >= 0) { /* Got hour? */ if (plus) /* Yes, just add? */ add += 3600.0 * hh; /* Yes */ else { /* Offset */ exh = hh < dattime.timet.hour + exm; add += 3600.0 * (hh - dattime.timet.hour - exm + 24 * exh); } } if (dd > 0) { /* Got day? */ if (plus) /* Yes, just add? */ add += 86400.0 * dd; /* Yes */ else { /* Offset */ exd = dd < dattime.timet.day + exh; add += 86400.0 * (dd - dattime.timet.day - exh + 365 * exd); } } /* (That 365 should not be used, and never in leap years) */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s AddTime() DEBUG: Add %.5f seconds \n", me, add); return(add); /* In seconds */ } /* End of AddTime() */ int ScanInfo() { /* Read a buffer full from the start of the current scan and * extract the struct SDir parameters from it (if possible). * Most of this was copied from Data_check(). Return 0 if * OK, negative on error, or positive on partial error * (from FFHeader()). */ unsigned long plapnt[2]; /* Convert to/from long long */ long long * pplapnt = (long long *) plapnt; int st, ntracks, ff; /* First check: OK to do this read? */ if (datah > 0 || ldir.delayed || ports[0] >= 0) { /* OK? */ if (msglev < 1) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ScanInfo() ERROR: Can't read while data transfer in progress \n", me); return(-1); /* Error */ } if (XLRGetDeviceStatus(dev, &devstatus) != XLR_SUCCESS) { /* Status OK? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ScanInfo() ERROR: XLRGetDeviceStatus() returned error \n", me); (void) errors(); return(-2); /* Error */ } (void) errors(); if (devstatus.Recording || devstatus.Playing) { /* Recording or Playing? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ScanInfo() ERROR: Can't read while recording or playing \n", me); return(-3); /* Error */ } if (sdir->nscans <= 0) { /* Do we have any scans? */ if (msglev < 2) /* Nope, debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ScanInfo() ERROR: No scans available \n", me); return(-4); /* Error */ } /* (Else OK to read) */ /* Check startbyte somewhere inside the current scan */ if (startbyte < sdir->start[sdir->n] || startbyte > sdir->start[sdir->n] + sdir->length[sdir->n] - 4 * BUFFL) { /* OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ScanInfo() ERROR: startbyte is outside scan \n", me); return(-5); /* Error */ } /* * SS mode to PCI for reading * */ if (XLRSetMode(dev, SS_MODE_PCI) != XLR_SUCCESS) { /* Set mode, OK? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ScanInfo() ERROR: XLRSetMode() returned error \n", me); (void) errors(); return(-6); /* Error */ } readdesc.BufferAddr = rbuff; /* Read buffer */ readdesc.XferLength = 4 * BUFFL; /* Bytes */ *pplapnt = startbyte; /* Start of this read */ plapnt[0] &= 0xfffffff8; /* Now 8-byte aligned */ readdesc.AddrHi = plapnt[1]; readdesc.AddrLo = plapnt[0]; /* Now read */ if (XLRRead(dev, &readdesc) != XLR_SUCCESS) { /* Read into rbuff, OK? */ if (msglev < 1) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ScanInfo() ERROR: XLRRead() returned error \n", me); (void) errors(); return(-7); /* Error */ } /* * Else OK; now try to find VLBI data in this data stream * */ /* We assume that parity could be either stripped (st = FALSE) or not * (st = TRUE) and that the number of tracks, ntracks, might be * 8, 16, 32, or 64 tracks. We need to try 5 of these 8 cases, * preferably in order of likelyhood. (Don't be confused: * st does not mean stripped, instead st stands for straight through, * which means parity was not stripped.) */ for (st = 1; st >= 0; st--) { /* Straight through? */ for (ntracks = 64; ntracks >= 8; ntracks /= 2) { /* Number of tracks */ /* Certain cases are impossible; check for these first */ if (st && ntracks != 32) /* ST and not 32? */ continue; /* Yes, to next ntracks */ if ((ff = FFHeader(&readdesc, &dattime, st, ntracks, -1)) >= 0) /* Find VLBI header, OK? */ break; /* Yes, out of for ntracks */ } /* End of for ntracks */ if (ntracks >= 8) /* Found? */ break; /* Yes, out of for st */ } /* End of for st */ if (st < 0) { /* Did we find VLBI data? */ if (msglev < 2) /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s ScanInfo() ERROR: Can't find VLBI data \n", me); (void) errors(); return(-8); /* Error */ } /* Else here we have VLBI data with one or several frame headers * found, and we know st (parity or not) and ntracks and some other * stuff. Note that ff > 0 means that frame periods are unknown */ ///* Calculate the frame header time in seconds */ //timesec = dattime.timet.day*86400.0 + dattime.timet.hour*3600.0 + // dattime.timet.minute*60.0 + dattime.timet.second; return(ff); /* Normal end */ } /* End of ScanInfo() */ int crcc (unsigned char * idata, int len, int mask, int cycl) { /* Calculate the CRC (cyclic redundancy check) of the bit stream * in idata of len bits (sic) using mask and cycl. Examples: * mask = 040003 (octal) cycl = 16 for label, len = 64 bits, * for VLBA frame header, len = 48 bits. Or mask = 007003 (octal) * cycl = 12 for Mark-4 frame header, len = 148 bits. Return the * answer. Mostly copied from ARW's FORTRAN subroutine CRCC(). * Revised: 2005 March 10, JAB */ unsigned int istate = 0; int idbit; /* Count bits from 1 */ int q, ich, icb; for (idbit = 1; idbit <= len; idbit++) { /* Each of len bits */ q = istate & 1; /* Output bit */ /* In ARW's original, bits are numbered 1 to 16 left to right (!) * in a 16-bit word. This curious numbering is perhaps because the * bytes are numbered left to right in a word in hppa. We need to * pick out the same bit given 8-bit chars. We number bits right * to left in a char as usual but process them in inverse order. */ ich = (idbit-1)/8; /* Char number, 0 to (len-1)/8 */ icb = 7-(idbit-1)%8; /* Bit number within a char, 0 to 7 */ if ((((idata[ich] >> icb) & 1) ^ q) == 0) /* Feedback value */ istate &= -2; /* Clear LSB */ else { istate ^= mask; /* Invert bits with 1s in mask */ istate |= 1; /* Set LSB */ } istate = istate >> 1 | (istate & 1) << cycl - 1; /* Right rotate one bit */ } /* End of for idbit */ return((int) istate); } /* End of crcc() */ unsigned long tvg(int init) { /* Calculate the test-vector-generator (TVG) pattern. * Reference: VSI-H Specification, Rev 1.0, 7 August 2000, available * at http://web.haystack.mit.edu/vsi/. See especially Figure 6, * page 29 and Table 13, page 20. Referring to Figure 6, q (below) * represents the Q outputs of the flip-flops, r the upper inputs of * the exors, and u the lower 16 bits of the answer (output). * Revised: 2001 December 18, JAB */ static unsigned short q; unsigned short r, u; if (init) /* Initialize? */ q = 0xffff; /* Yes */ /* Set r to a copy of q right shifted by 1 bit */ r = q >> 1; /* And set r bit 15 to a copy of q bit 2 */ if (q & 4) /* Bit 2 set? */ r |= 0x8000; /* Yes */ /* We need to overwrite r bit 14 with a copy of u bit 0, * so we need to do q exor r temporarily to get this bit */ if ((q ^ r) & 1) /* Bit 0 set? */ r |= 0x4000; /* Yes */ else /* Not set */ r &= 0xbfff; /* Now set u to q exor r */ u = q ^ r; /* Lower 16 bits of the answer */ /* For next time, set q to a copy of u right shifted by 1 bit */ q = u >> 1; /* And set q bit 15 (invisible) to a copy of q bit 0 */ if (q & 1) /* Bit 0 set? */ q |= 0x8000; /* Yes */ /* u has the lower 16 bits of the answer; the upper 16 bits * are the lower 16 bits inverted and shifted up there */ return(((unsigned long) ~u << 16) + u); } /* End of tvg() */