/* * Copyright (C) 1987 * Louis A. Mamakos WA3YMH * All rights reserved. * * This code may not be redistributed, sold, included on any collection of * software which is sold. Use of this software is restricted to inclusion * in the KA9Q TCP/IP software package for use on a Commodore-Amiga system. * Commercial use is prohibited. Only educational and Amateur Packet Radio * use is allowed. */ #ifdef AMIGADEVDRV /* * This module is the meat of the Amiga 'internet.device' device driver. There * are assembly language stubs in devstub.asm that call this module when user * program access the device driver. Remember: the tasks running this code are * not our own! */ #include /* Amiga system definitions */ #include #include #include #include #include #include #include #include #include /* get definitions of KA9Q TCP/IP protocol stuff... */ #include "machdep.h" #include "timer.h" #include "mbuf.h" #include "netuser.h" #include "internet.h" #include "icmp.h" #include "ip.h" #include "tcp.h" #include "trace.h" #include "session.h" /* device driver specific definitions */ #define ListEmpty(x) (! ((x)->lh_Head->ln_Succ)) #include "inetdev.h" #ifdef TRACE #define tracedev(x) \ if (trace & TRACE_DEVICE) printf(x) #define tracedev2(x,y) \ if (trace & TRACE_DEVICE) printf(x,y) #define tracedev3(x,y,z) \ if (trace & TRACE_DEVICE) printf(x,y,z) #define tracedev4(x,y,z,zz) \ if (trace & TRACE_DEVICE) printf(x,y,z,zz) #endif char *malloc(); extern void DSClose(), DSBeginIO(), DSAbortIO(); extern struct InternetBase *DSOpen(); extern long DSExpunge(); void indev_tcp_r_upcall(), indev_tcp_t_upcall(), indev_s_upcall(); struct SignalSemaphore INLock; struct Library *MakeLibrary(); /* for open requests */ int nopens; /* from iface */ struct IOINETReq *iob = NULL; int unit_spec; struct InternetBase * dev; int OpenIt = 0, CN1 = 0, IOpenedIt = 0; extern int DeviceSignal; extern struct Process *mytask; printlist(l) struct List *l; { printf("head %x tail %x tailpred %x\n", l->lh_Head, l->lh_Tail, l->lh_TailPred); } /* * Initialize and install the Amiga 'internet.device'. */ void DriverInit() { char *foo[10]; int success; static int WeWereHere; int x; tracedev("DriverInit"); x = WeWereHere; WeWereHere = 1; if (x) { printf("you tried to add the driver twice!!!\n"); return; } foo[0] = (char *) &DSOpen; foo[1] = (char *) &DSClose; foo[2] = (char *) &DSExpunge; foo[3] = (char *) NULL; foo[4] = (char *) &DSBeginIO; foo[5] = (char *) &DSAbortIO; /* add any other custom routines here */ foo[6] = (char *) -1; InternetBase = (struct InternetBase *) MakeLibrary(&foo[0], (char *) NULL, (char *) NULL, (long) sizeof(struct InternetBase), (char *) NULL); if (InternetBase == (struct InternetBase *) 0) { /* display alert? */ return; } InitSemaphore(&(INLock)); ObtainSemaphore(&(INLock)); InitSemaphore(&(InternetBase->ib_lock)); InternetBase->ib_lock.ss_Link.ln_Pri = 0; InternetBase->ib_lock.ss_Link.ln_Name = "internet.device lock"; NewList(&InternetBase->ib_Units); InternetBase->ib_Units.lh_Type = NT_UNKNOWN; InternetBase->lib.lib_Node.ln_Type = NT_DEVICE; InternetBase->lib.lib_Node.ln_Pri = 0; InternetBase->lib.lib_Node.ln_Name = "internet.device"; InternetBase->lib.lib_Flags = LIBF_CHANGED | LIBF_SUMUSED; InternetBase->lib.lib_Version = IN_VERSION; InternetBase->lib.lib_Revision = IN_REVISION; InternetBase->lib.lib_IdString = (APTR) "internet.device 23 May 1987\r\n"; success = AddDevice(InternetBase); Savea4(); OpenIt = 0; IOpenedIt = 0; nopens = 1; CN1 = 5; if (success != 0) myoserr("driver open"); printf("driver added returned %d\n",success); } void DriverShutdown() { long error; extern long *RemoveDevice(); if (!InternetBase) return; if (error = RemDevice(InternetBase)) printf("Can't remove device: error %ld\n", error); } struct InternetBase * NetDevOpen(mdev, munit_spec, miob, mflags) struct InternetBase *mdev; struct IOINETReq *miob; ULONG munit_spec, mflags; { iob = miob; dev = mdev; unit_spec = munit_spec; if (IOpenedIt == 1) { CN1++; return NULL; } OpenIt = 1; Permit(); while (IOpenedIt == 0); Forbid(); IOpenedIt = 0; return dev; } check_driver() { register struct INET_Unit *unit; register struct tcb *tcb; if (OpenIt != 0) { printf("open request!\n"); switch (unit_spec) { case INET_UNIT_TCP: case INET_UNIT_UDP: break; default: iob->io_Error = IOERR_OPENFAIL; iob->io_Device = NULL; iob->io_Unit = NULL; OpenIt = 0; IOpenedIt = 1; goto doneopen; } if ((unit = (struct INET_Unit *) malloc(sizeof(struct INET_Unit))) == NULL) { iob->io_Error = IOERR_OPENFAIL; OpenIt = 0; IOpenedIt = 1; goto doneopen; } tracedev2("malloc ok %x\n", unit); iob->io_Unit = unit; iob->io_Device = (struct Device *)dev; dev->lib.lib_OpenCnt++; unit->iu_Unit.ln_Type = NT_UNKNOWN; unit->iu_Unit.ln_Pri = 0; NewList(&unit->iu_Input); NewList(&unit->iu_Output); printf("newlist ok\n"); unit->iu_Input.lh_Type = NT_UNKNOWN; /* gee, what do we really */ unit->iu_Output.lh_Type = NT_UNKNOWN; /* call these... */ unit->iu_user = iob->io_Offset; /* always returned in Offset */ unit->iu_Act_Input = NULL; unit->iu_Act_Output = NULL; iob->io_lsocket.address = ip_addr; iob->io_lsocket.port = lport++; AddTail(&InternetBase->ib_Units, &unit->iu_Unit); /* perform protocol specific open functions */ printf("addtail\n"); switch (unit_spec) { case INET_UNIT_TCP: /* Forbid(); */ tcb = open_tcp(&(iob->io_lsocket), &(iob->io_fsocket), (USHORT) iob->io_Offset, (USHORT) iob->io_TCP_Window, indev_tcp_r_upcall, indev_tcp_t_upcall, indev_s_upcall, iob->io_INET_TOS, (char *)unit); /* Permit();*/ if (tcb == NULL) goto fail; unit->iu_Unit.ln_Name = "TCP Connection"; unit->iu_type = INET_UNIT_TCP; unit->iu_ccb = tcb; printf("cpopen is %d\n", tcb); break; default: fail: iob->io_Error = IOERR_OPENFAIL; dev->lib.lib_OpenCnt--; Remove(unit); free(unit); OpenIt = 0; IOpenedIt = 1; goto doneopen; } tracedev2("dev is %d\n", dev); OpenIt = 0; IOpenedIt = 1; } doneopen: /* spin until that other guy is all done. We will not get through * this spin until the other guy has done a Forbid() and * then a Permit(), since the IopenedIt gets cleared * AFTER the Forbid(). Sorry i do not use semaphores but * i do not have 1.2 autodocs so am not totally up on their * use. */ while (IOpenedIt); } CheckTcp() { struct Node *head = InternetBase->ib_Units.lh_Head; struct INET_Unit *unit = (struct INET_Unit *) head; struct tcb *tcb; /* let the other guys in */ ReleaseSemaphore(&(INLock)); eihalt(); ObtainSemaphore(&(INLock)); tracedev("start checktcp\n"); tracedev4("heda %x Pred is %x Succ is %x\n", head,head->ln_Pred, head->ln_Succ); for (;unit->iu_Unit.ln_Succ;unit = unit->iu_Unit.ln_Succ) { tracedev3("checktcp: %x Succ %d\n", unit, unit->iu_Unit.ln_Succ); if (unit->iu_type != INET_UNIT_TCP) { tracedev("not a tcp\n"); continue; } tcb = (struct tcb *) unit->iu_ccb; if (tcb == NULL) { tracedev("NULL tcb in unit\n"); continue; } if (tcb->state == ESTABLISHED) { tracedev("unit state is established!\n"); /* continue;*/ } tracedev("do the upcall\n"); do_tupcall(tcb, 512); /* for now- it wil do the right thing */ if (tcb->rcvcnt > 0) do_rupcall(tcb, tcb->rcvcnt); } tracedev("done checktcp\n"); /* ReleaseSemaphore(&(INLock));*/ } void DevClose(dev, iob) struct InternetBase *dev; struct IOINETReq *iob; { register struct INET_Unit *unit; struct tcb *tcb; unit = iob->io_Unit; tcb = unit->iu_ccb; del_tcp(tcb); Remove(unit); free(unit); iob->io_Unit = NULL; iob->io_Device = (struct Device *)dev; dev->lib.lib_OpenCnt--; /* remove iu_Unit from ib_Units list */ /* decrement library use count */ /* free unit structure */ /* delete TCP/UDP connection del_tcp()/del_udp() */ } long DevExpunge(dev) struct InternetBase *dev; { register char *m; register long len; if (InternetBase->lib.lib_OpenCnt) { InternetBase->lib.lib_Flags |= LIBF_DELEXP; return 0; } Remove(InternetBase); /* remove from library list */ len = InternetBase->lib.lib_NegSize + InternetBase->lib.lib_PosSize; m = (char *) ((ULONG)InternetBase - InternetBase->lib.lib_NegSize); FreeMem(m, len); return 0; } #define C_IMMED (1<<0) #define C_READ (1<<1) #define C_WRITE (1<<2) void cmd_Invalid(), cmd_Reset(), cmd_Read(), cmd_Write(), cmd_Update(), cmd_Clear(), cmd_Stop(), cmd_Start(), cmd_Flush(), PerformIO(); struct Commands { void (*cmd_func)(); int cmd_flags; } commands [] = { { cmd_Invalid, C_IMMED }, /* invalid */ { cmd_Reset, C_IMMED }, /* CMD_RESET */ { cmd_Read, C_READ }, /* CMD_READ */ { cmd_Write, C_WRITE }, /* CMD_WRITE */ { cmd_Update, C_WRITE }, /* CMD_UPDATE */ { cmd_Clear, C_IMMED }, /* CMD_CLEAR */ { cmd_Stop, C_IMMED }, /* CMD_STOP */ { cmd_Start, C_IMMED }, /* CMD_START */ { cmd_Flush, C_IMMED }, /* CMD_FLUSH */ }; /* define last valid command */ #define MAX_IO_COMMAND CMD_FLUSH /* BeginIO is called to begin processing of the I/O request */ void DevBeginIO(iob, dev) struct IOINETReq *iob; struct InternetBase *dev; { register struct Commands *cmd; register struct INET_Unit *unit = iob->io_Unit; ObtainSemaphore(&(INLock)); if (iob->io_Command > MAX_IO_COMMAND) { cmd_Invalid(iob, iob->io_Unit); goto done; } tracedev("io. ObtainSme\n"); tracedev("got it\n"); cmd = &commands[iob->io_Command]; tracedev2("cmd is %d\n",iob->io_Command); tracedev2("flags %d\n", iob->io_Flags); if ((cmd->cmd_flags & C_IMMED) == 0) { /* * Code for commands which can queue */ if ((cmd->cmd_flags & C_READ)/* && (unit->iu_Act_Input)*/) { AddTail(&unit->iu_Input, iob); iob->io_Flags &= ~IOF_QUICK; iob->io_Message.mn_Node.ln_Type = NT_MESSAGE; tracedev3("added %d to read queue of unit %d\n", iob, unit); goto done; } if ((cmd->cmd_flags & C_WRITE)/* && (unit->iu_Act_Output)*/) { AddTail(&unit->iu_Output, iob); iob->io_Flags &= ~IOF_QUICK; iob->io_Message.mn_Node.ln_Type = NT_MESSAGE; tracedev3("added %d to write queue of unit %d\n", iob, unit); goto done; } } PerformIO(iob, unit); done: tracedev4("flags QUI %x ~QUI %x %d\n", IOF_QUICK, ~IOF_QUICK, iob->io_Flags); Signal(mytask, DeviceSignal); ReleaseSemaphore(&(INLock)); } void DevAbortIO(iob, dev) struct IOINETReq *iob; struct InternetBase *dev; { printf("DevAbortIo\n"); } void PerformIO(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = 0; iob->io_Actual = 0; (*commands[iob->io_Command].cmd_func)(iob, unit); } void TermIO(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { struct tcb *tcb; tcb = (struct tcb *) unit->iu_ccb; iob->io_OldState = iob->io_State; iob->io_State = tcb->state; if ((iob->io_Flags & IOF_QUICK) == 0) /* not quick I/O */ ReplyMsg(&iob->io_Message); } void cmd_Invalid(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } void cmd_Reset(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } void cmd_Read(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } void cmd_Write(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } void cmd_Update(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } void cmd_Clear(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } void cmd_Stop(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } void cmd_Start(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } void cmd_Flush(iob, unit) struct IOINETReq *iob; struct INET_Unit *unit; { iob->io_Error = IOERR_NOCMD; TermIO(iob, unit); } /* TCP receiver upcall routine. Called with TCB pointer and number of bytes available */ do_rupcall(tcb, cnt) struct tcb *tcb; int16 cnt; { struct mbuf *bp; register struct INET_Unit *unit = (struct INET_Unit *) tcb->user; int amount, recamount; struct IOINETReq *iob; /* ObtainSemaphore(&(INLock));*/ if (ListEmpty(&(unit->iu_Input))) goto done; iob = unit->iu_Act_Input = unit->iu_Input.lh_Head; tracedev4("dev rupcall iob %d unit %d tcb%d\n",iob, unit, tcb); if (iob != NULL) { Remove(iob); amount = min(cnt, iob->io_Length); tracedev3("call recv_tcp %d bytes avail %d\n", amount, cnt); recamount = recv_tcp(tcb, &bp, amount); iob->io_Actual = dqdata(bp, iob->io_Data, recamount); tracedev2("recv_tcp after got %d bytes\n", iob->io_Actual); TermIO(iob, unit); /* ReplyMsg(&(iob->io_Message));*/ } done: /* ReleaseSemaphore(&(INLock)); */ } /* TCP receiver upcall routine. Called with TCB pointer and number of bytes available */ void indev_tcp_r_upcall(tcb, cnt) struct tcb *tcb; int16 cnt; { /* ObtainSemaphore(&(INLock));*/ do_rupcall(tcb, 512); /* ReleaseSemaphore(&(INLock)); */ } /* TCP transmitter upcall routine. Called with TCB pointer and number of bytes free in send window */ do_tupcall(tcb, avail) struct tcb *tcb; int16 avail; { struct mbuf *bp, *qdata(); register struct INET_Unit *unit = (struct INET_Unit *) tcb->user; int amount; struct IOINETReq *iob; if (ListEmpty(&(unit->iu_Output))) goto done; tracedev("non-empty Output\n"); iob = unit->iu_Act_Output = unit->iu_Output.lh_Head; tracedev4("dev tupcall iob %d unit %d tcb%d\n",iob, unit, tcb); if (iob != NULL) { Remove(iob); amount = min(avail, iob->io_Length); tracedev3("t_upcall- send_tcp for addr %x %d bytes\n",iob->io_Data, amount); bp = qdata(iob->io_Data, amount); iob->io_Actual = send_tcp(tcb, bp); tracedev2("send_tcp after got %d bytes\n", iob->io_Actual); TermIO(iob, unit); /* ReplyMsg(&(iob->io_Message));*/ unit->iu_Act_Output = NULL; } done: } void indev_tcp_t_upcall(tcb, avail) struct tcb *tcb; int16 avail; { /* ObtainSemaphore(&(INLock));*/ do_tupcall(tcb, avail); /* ReleaseSemaphore(&(INLock)); */ } void indev_s_upcall(tcb, old, new) struct tcb *tcb; char old, new; { register struct INET_Unit *unit = (struct INET_Unit *) tcb->user; char notify = 0; extern char *tcpstates[]; extern char *reasons[]; extern char *unreach[]; extern char *exceed[]; /* Can't add a check for unknown connection here, it would loop * on a close upcall! We're just careful later on. */ if(unit != NULL) notify = 1; switch(new){ case CLOSE_WAIT: if(notify) printf("%s\r\n",tcpstates[new]); close_tcp(tcb); break; case CLOSED: /* court adjourned */ if(notify){ printf("%s (%s",tcpstates[new],reasons[tcb->reason]); if(tcb->reason == NETWORK){ switch(tcb->type){ case DEST_UNREACH: printf(": %s unreachable",unreach[tcb->code]); break; case TIME_EXCEED: printf(": %s time exceeded",exceed[tcb->code]); break; } } printf(")\r\n"); } del_tcp(tcb); break; default: if(notify) printf("%s\r\n",tcpstates[new]); break; } fflush(stdout); } #endif