diff --git a/afpcdev.c b/afpcdev.c index c646857..1adc84c 100644 --- a/afpcdev.c +++ b/afpcdev.c @@ -50,6 +50,7 @@ #define afpOverTCPOptionsItem 301 #define useLargeReadsItem 302 #define forceAFP22Item 303 +#define fakeSleepItem 304 #define fstMissingError 3000 #define noEasyMountError 3001 @@ -492,6 +493,18 @@ void DoHit(long ctlID, CtlRecHndl ctlHandle) } else if (menuItem == forceAFP22Item) { flags ^= fForceAFP22; CheckMItem((flags & fForceAFP22) ? TRUE : FALSE, forceAFP22Item); + + /* Fake sleep is allowed only with AFP 2.2. */ + if (flags & fForceAFP22) { + EnableMItem(fakeSleepItem); + } else { + DisableMItem(fakeSleepItem); + CheckMItem(FALSE, fakeSleepItem); + flags &= ~fFakeSleep; + } + } else if (menuItem == fakeSleepItem) { + flags ^= fFakeSleep; + CheckMItem((flags & fFakeSleep) ? TRUE : FALSE, fakeSleepItem); } SetCtlValue(afpOverTCPOptionsItem, ctlHandle); diff --git a/afpcdev.rez b/afpcdev.rez index 057746c..8b8418f 100644 --- a/afpcdev.rez +++ b/afpcdev.rez @@ -97,6 +97,7 @@ resource rIcon (1) { #define afpOverTCPOptionsItem 301 #define useLargeReadsItem 302 #define forceAFP22Item 303 +#define fakeSleepItem 304 /* * Controls in the control panel window (for 640 mode or 320 mode) @@ -244,7 +245,7 @@ resource rMenu (optionsMenu) { optionsMenu, /* menu ID */ refIsResource*menuTitleRefShift + refIsResource*itemRefShift, optionsMenu, /* menu title ref (not drawn) */ - {afpOverTCPOptionsItem, useLargeReadsItem, forceAFP22Item}; + {afpOverTCPOptionsItem, useLargeReadsItem, forceAFP22Item, fakeSleepItem}; }; resource rPString(optionsMenu,noCrossBank) { "" }; @@ -275,6 +276,15 @@ resource rMenuItem (forceAFP22Item) { }; resource rPString(forceAFP22Item,noCrossBank) { "Force AFP Version 2.2" }; +resource rMenuItem (fakeSleepItem) { + fakeSleepItem, /* menu item ID */ + "","", + 0, + fDisabled+refIsResource*itemTitleRefShift, + fakeSleepItem /* menu item title ref */ +}; +resource rPString(fakeSleepItem,noCrossBank) { "Fake Sleep to Keep Alive" }; + /* * Controls in the help window */ diff --git a/afpoptions.c b/afpoptions.c index 1246442..fe809db 100644 --- a/afpoptions.c +++ b/afpoptions.c @@ -9,5 +9,7 @@ AFPOptions afpOptions[] = {"AFP over TCP (LargeReads)", fLargeReads}, {"AFP over TCP (AFP2.2)", fForceAFP22}, {"AFP over TCP (LargeReads,AFP2.2)", fLargeReads | fForceAFP22}, + {"AFP over TCP (AFP2.2,FakeSleep)", fForceAFP22 | fFakeSleep}, + {"AFP over TCP (LR,AFP2.2,FS)", fLargeReads | fForceAFP22 | fFakeSleep}, {0, 0} }; diff --git a/afpoptions.h b/afpoptions.h index 00de274..a289374 100644 --- a/afpoptions.h +++ b/afpoptions.h @@ -3,6 +3,7 @@ #define fLargeReads 0x0001 #define fForceAFP22 0x0002 +#define fFakeSleep 0x0004 typedef struct AFPOptions { char *zoneString; diff --git a/aspinterface.c b/aspinterface.c index ae00954..547714e 100644 --- a/aspinterface.c +++ b/aspinterface.c @@ -28,6 +28,7 @@ typedef struct FPReadRec { #define kFPRead 27 #define kFPLogin 18 +#define kFPZzzzz 122 /* For forced AFP 2.2 login */ static Byte loginBuf[100]; @@ -41,6 +42,7 @@ static void DoSPCommand(Session *sess, ASPCommandRec *commandRec); static void DoSPWrite(Session *sess, ASPWriteRec *commandRec); static void CompleteASPCommand(SPCommandRec *commandRec, Word result); +static void InitSleepRec(Session *sess, Byte sessRefNum); Session sessionTbl[MAX_SESSIONS]; @@ -54,6 +56,7 @@ LongWord DispatchASPCommand(SPCommandRec *commandRec) { stateReg = ForceRomIn(); +top: if (commandRec->command == aspGetStatusCommand || commandRec->command==aspOpenSessionCommand) { @@ -84,6 +87,7 @@ LongWord DispatchASPCommand(SPCommandRec *commandRec) { sess = &sessionTbl[i]; sess->spCommandRec = commandRec; sess->commandPending = TRUE; + InitSleepRec(sess, i + SESSION_NUM_START); if ((i = StartTCPConnection(sess)) != 0) { FlagFatalError(sess, i); @@ -186,7 +190,30 @@ LongWord DispatchASPCommand(SPCommandRec *commandRec) { } } -ret: +ret: + /* + * If requested by user, we tell the server we are "going to sleep" + * after every command we send. This avoids having the server + * disconnect us because we can't send tickles for a while. + * + * This implementation is designed to work with Netatalk: + * -Apple's docs say FPZzzzz was introduced with AFP 2.3, but Netatalk + * supports it with AFP 2.2 and doesn't support AFP 2.3 at all. + * -Apple's docs also say there is no reply to FPZzzzz, but Netatalk + * send a reply with four bytes of data. + * Netatalk includes comments indicating Apple's implementation in OS X + * may really work more like Netatalk, but I haven't checked this. + */ + if ((sess->atipMapping.flags & fFakeSleep) + && sess->loggedIn && commandRec->result == 0 + && (commandRec->command == aspCommandCommand + || commandRec->command == aspWriteCommand) + && commandRec != (SPCommandRec*)&sess->sleepCommandRec) + { + commandRec = (SPCommandRec*)&sess->sleepCommandRec; + goto top; + } + RestoreStateReg(stateReg); return 0; @@ -454,6 +481,19 @@ void CallAttentionRoutine(Session *sess, Byte attenType, Word atten) { CallCompletionRoutine((void *)(sess->attention + 1)); } +static void InitSleepRec(Session *sess, Byte sessRefNum) { + sess->sleepCommandRec.async = 0; + sess->sleepCommandRec.command = aspCommandCommand; + sess->sleepCommandRec.completionPtr = 0; + sess->sleepCommandRec.refNum = sessRefNum; + sess->sleepCommandRec.cmdBlkLength = sizeof(sess->fpZzzzzRec); + sess->sleepCommandRec.cmdBlkAddr = (LongWord)&sess->fpZzzzzRec; + sess->sleepCommandRec.replyBufferLen = sizeof(sess->fpZzzzzResponseRec); + sess->sleepCommandRec.replyBufferAddr = (LongWord)&sess->fpZzzzzResponseRec; + sess->fpZzzzzRec.CommandCode = kFPZzzzz; + sess->fpZzzzzRec.Flag = 0; +} + void PollAllSessions(void) { unsigned int i; diff --git a/cmdproc.asm b/cmdproc.asm index 817e846..5ad3d93 100644 --- a/cmdproc.asm +++ b/cmdproc.asm @@ -88,6 +88,19 @@ jslOldPFIListSessions2 entry rtl end +pfiLoginContCmdProc start + lda cmdRecPtr+2 + pha + lda cmdRecPtr + pha +jslOldPFILoginCont entry + jsl jslOldPFILoginCont ; to be changed + rep #$30 ; ensure full native mode + jsl PostLoginCont + rtl + end + + CallCompletionRoutine start phb jsl ForceLCBank2 ;use LC bank 2 diff --git a/cmdproc.h b/cmdproc.h index 996f917..e49a316 100644 --- a/cmdproc.h +++ b/cmdproc.h @@ -7,6 +7,8 @@ void pfiLoginCmdProc(void); extern LongWord jslOldPFILogin; void pfiLogin2CmdProc(void); extern LongWord jslOldPFILogin2; +void pfiLoginContCmdProc(void); +extern LongWord jslOldPFILoginCont; void pfiListSessions2CmdProc(void); extern LongWord jslOldPFIListSessions2; void CallCompletionRoutine(void *); diff --git a/installcmds.c b/installcmds.c index 0437140..14b60fb 100644 --- a/installcmds.c +++ b/installcmds.c @@ -21,6 +21,7 @@ NewCmd newCmds[] = { {nbpLookupNameCommand, nbpCmdProc, NULL}, {pfiLoginCommand, pfiLoginCmdProc, &jslOldPFILogin}, {pfiLogin2Command, pfiLogin2CmdProc, &jslOldPFILogin2}, + {pfiLoginContCommand, pfiLoginContCmdProc, &jslOldPFILoginCont}, {pfiListSessions2Command, pfiListSessions2CmdProc, &jslOldPFIListSessions2}, {0, 0, 0} }; diff --git a/savenames.c b/savenames.c index a63707a..5bdb018 100644 --- a/savenames.c +++ b/savenames.c @@ -87,6 +87,10 @@ void SaveNames(PFILogin2Rec *commandRec) { goto ret; } + if (commandRec->result == 0 && commandRec->sessRefID >= SESSION_NUM_START) { + sessionTbl[commandRec->sessRefID - SESSION_NUM_START].loggedIn = TRUE; + } + if (commandRec->sessRefID <= MAX_ASP_SESSION_NUM) { nameRec = &aspSessionNames[commandRec->sessRefID]; aspActiveFlags[commandRec->sessRefID] = 1; @@ -154,3 +158,16 @@ ret: } #pragma databank 0 +#pragma databank 1 +void PostLoginCont(PFILoginContRec *commandRec) { + Word stateReg; + + stateReg = ForceRomIn(); + + if (commandRec->result == 0 && commandRec->sessRefID >= SESSION_NUM_START) { + sessionTbl[commandRec->sessRefID - SESSION_NUM_START].loggedIn = TRUE; + } + + RestoreStateReg(stateReg); +} +#pragma databank 0 diff --git a/session.h b/session.h index 95b240b..cb0b51b 100644 --- a/session.h +++ b/session.h @@ -2,6 +2,7 @@ #define SESSION_H #include +#include #include "dsiproto.h" #include "atipmapping.h" @@ -23,6 +24,11 @@ typedef enum DSISessionStatus { needsReset /* set after control-reset in P8 mode */ } DSISessionStatus; +typedef struct FPZzzzzRec { + Word CommandCode; /* includes pad byte */ + LongWord Flag; +} FPZzzzzRec; + typedef struct Session { /* Marinetti TCP connection status */ Word ipid; @@ -62,6 +68,14 @@ typedef struct Session { /* Attention routine header (followed by the routine) */ ASPAttentionHeaderRec *attention; + + /* Is this session successfully logged in? */ + Boolean loggedIn; + + /* Records for sending (fake) sleep messages to server */ + ASPCommandRec sleepCommandRec; + FPZzzzzRec fpZzzzzRec; + Byte fpZzzzzResponseRec[4]; } Session; #endif