/* Driver for FTP Software's packet driver interface. (PC specific code) * Copyright 1991 Phil Karn, KA9Q */ #include #include #include "global.h" #include "proc.h" #include "mbuf.h" #include "netuser.h" #include "enet.h" #include "arcnet.h" #include "ax25.h" #include "slip.h" #include "kiss.h" #include "iface.h" #include "arp.h" #include "trace.h" #include "pktdrvr.h" #include "config.h" #include "devparam.h" static long access_type __ARGS((int intno,int if_class,int if_type, int if_number, char *type,unsigned typelen, INTERRUPT (*receiver) __ARGS((void)) )); static int driver_info __ARGS((int intno,int handle,int *version, int *class,int *type,int *number,int *basic)); static int release_type __ARGS((int intno,int handle)); static int get_address __ARGS((int intno,int handle,char *buf,int len)); static int pk_raw __ARGS((struct iface *iface,struct mbuf *bp)); static int pk_stop __ARGS((struct iface *iface)); static int send_pkt __ARGS((int intno,char *buffer,unsigned length)); static INTERRUPT (*Pkvec[])() = { pkvec0,pkvec1,pkvec2 }; static struct pktdrvr Pktdrvr[PK_MAX]; static int Derr; static char Pkt_sig[] = "PKT DRVR"; /* Packet driver signature */ /* * Send routine for packet driver */ int pk_send(bp,iface,gateway,tos) struct mbuf *bp; /* Buffer to send */ struct iface *iface; /* Pointer to interface control block */ int32 gateway; /* Ignored */ int tos; { if(iface == NULLIF){ free_p(bp); return -1; } return (*iface->raw)(iface,bp); } /* Send raw packet (caller provides header) */ static int pk_raw(iface,bp) struct iface *iface; /* Pointer to interface control block */ struct mbuf *bp; /* Data field */ { register struct pktdrvr *pp; int16 size; struct mbuf *bp1; pp = &Pktdrvr[iface->dev]; size = len_p(bp); dump(iface,IF_TRACE_OUT,pp->class,bp); iface->rawsndcnt++; iface->lastsent = secclock(); /* Perform class-specific processing, if any */ switch(pp->class){ case CL_ETHERNET: if(size < RUNT){ /* Pad the packet out to the minimum */ if((bp1 = alloc_mbuf(RUNT-size)) == NULLBUF){ free_p(bp); return -1; } memset(bp1->data,0,RUNT-size); bp1->cnt = RUNT-size; append(&bp,bp1); size = RUNT; } break; case CL_KISS: /* This *really* shouldn't be done here, but it was the * easiest way. Put the type field for KISS TNC on front. */ bp = pushdown(bp,1); bp->data[0] = PARAM_DATA; size++; break; } if(bp->next != NULLBUF){ /* Copy to contiguous buffer, since driver can't handle mbufs */ bp1 = copy_p(bp,size); free_p(bp); bp = bp1; if(bp == NULLBUF) return -1; } send_pkt(pp->intno,bp->data,bp->cnt); free_p(bp); return 0; } /* Packet driver receive routine. Called from an assembler hook that pushes * the caller's registers on the stack so we can access and modify them. * This is a rare example of call-by-location in C. */ INTERRUPT pkint(bp, di, si, ds, es, dx, cx, bx, ax, ip, cs, flags, dev) unsigned short bp, di, si, ds, es, dx, cx, bx, ax, ip, cs, flags; int dev; { register struct pktdrvr *pp; if(dev < 0 || dev >= PK_MAX) return; /* Unknown device */ pp = &Pktdrvr[dev]; if(pp->iface == NULLIF) return; /* Unknown device */ switch(ax){ case 0: /* Space allocate call */ if((pp->buffer = alloc_mbuf(cx+sizeof(struct phdr))) != NULLBUF){ pp->buffer->data += sizeof(struct phdr); es = FP_SEG(pp->buffer->data); di = FP_OFF(pp->buffer->data); pp->buffer->cnt = cx; } else { es = di = 0; } break; case 1: /* Packet complete call */ net_route(pp->iface,pp->class,pp->buffer); pp->buffer = NULLBUF; break; default: break; } } /* Shut down the packet interface */ static int pk_stop(iface) struct iface *iface; { struct pktdrvr *pp; pp = &Pktdrvr[iface->dev]; /* Call driver's release_type() entry */ if(release_type(pp->intno,pp->handle1) == -1) tprintf("%s: release_type error code %u\n",iface->name,Derr); if(pp->class == CL_ETHERNET || pp->class == CL_ARCNET){ release_type(pp->intno,pp->handle2); release_type(pp->intno,pp->handle3); } pp->iface = NULLIF; return 0; } /* Attach a packet driver to the system * argv[0]: hardware type, must be "packet" * argv[1]: software interrupt vector, e.g., x7e * argv[2]: interface label, e.g., "trw0" * argv[3]: maximum number of packets allowed on transmit queue, e.g., "5" * argv[4]: maximum transmission unit, bytes, e.g., "1500" */ int pk_attach(argc,argv,p) int argc; char *argv[]; void *p; { register struct iface *if_pk; int class,type; unsigned int intno; static char iptype[] = {IP_TYPE >> 8,IP_TYPE}; static char arptype[] = {ARP_TYPE >> 8,ARP_TYPE}; static char revarptype[] = {REVARP_TYPE >> 8, REVARP_TYPE}; long handle; int i; #ifdef ARCNET static char arcip[] = {ARC_IP}; static char arcarp[] = {ARC_ARP}; #endif long drvvec; char sig[8]; /* Copy of driver signature "PKT DRVR" */ register struct pktdrvr *pp; char tmp[25]; for(i=0;i= PK_MAX){ tprintf("Too many packet drivers\n"); return -1; } if(if_lookup(argv[2]) != NULLIF){ tprintf("Interface %s already exists\n",argv[2]); return -1; } intno = htoi(argv[1]); /* Verify that there's really a packet driver there, so we don't * go off into the ozone (if there's any left) */ drvvec = (long)getvect(intno); movblock(FP_OFF(drvvec)+3, FP_SEG(drvvec), FP_OFF(sig),FP_SEG(sig),strlen(Pkt_sig)); if(strncmp(sig,Pkt_sig,strlen(Pkt_sig)) != 0){ tprintf("No packet driver loaded at int 0x%x\n",intno); return -1; } if_pk = (struct iface *)callocw(1,sizeof(struct iface)); if_pk->name = strdup(argv[2]); if_pk->addr = Ip_addr; pp = &Pktdrvr[i]; if_pk->mtu = atoi(argv[4]); if_pk->dev = i; if_pk->raw = pk_raw; if_pk->stop = pk_stop; pp->intno = intno; pp->iface = if_pk; /* Version 1.08 of the packet driver spec dropped the handle * requirement from the driver_info call. However, if we are using * a version 1.05 packet driver, the following call will fail. */ if(driver_info(intno,-1,NULL,&class,&type,NULL,NULL) < 0){ /* Find out by exhaustive search what class this driver is (ugh) */ for(class=1;class<=NCLASS;class++){ /* Store handle in temp long so we can tell an * error return (-1) from a handle of 0xffff */ handle = access_type(intno,class,ANYTYPE,0,iptype,2, Pkvec[if_pk->dev]); if(handle != -1 || Derr == TYPE_INUSE){ pp->handle1 = handle; break; } } /* Now that we know, release it and do it all over again with the * right type fields */ release_type(intno,pp->handle1); } switch(class){ case CL_ETHERNET: pp->handle1 = access_type(intno,class,ANYTYPE,0,iptype,2, Pkvec[if_pk->dev]); pp->handle2 = access_type(intno,class,ANYTYPE,0,arptype,2, Pkvec[if_pk->dev]); pp->handle3 = access_type(intno,class,ANYTYPE,0,revarptype,2, Pkvec[if_pk->dev]); setencap(if_pk,"Ethernet"); /* Get hardware Ethernet address from driver */ if_pk->hwaddr = mallocw(EADDR_LEN); get_address(intno,pp->handle1,if_pk->hwaddr,EADDR_LEN); if(if_pk->hwaddr[0] & 1){ tprintf("Warning! Interface '%s' has a multicast address:", if_pk->name); tprintf(" (%s)\n", (*if_pk->iftype->format)(tmp,if_pk->hwaddr)); } break; #ifdef ARCNET case CL_ARCNET: pp->handle1 = access_type(intno,class,ANYTYPE,0,arcip,1, Pkvec[if_pk->dev]); pp->handle2 = access_type(intno,class,ANYTYPE,0,arcarp,1, Pkvec[if_pk->dev]); if_pk->output = anet_output; /* Get hardware ARCnet address from driver */ if_pk->hwaddr = mallocw(AADDR_LEN); get_address(intno,pp->handle1,if_pk->hwaddr,AADDR_LEN); break; #endif case CL_SERIAL_LINE: pp->handle1 = access_type(intno,class,ANYTYPE,0,NULLCHAR,0, Pkvec[if_pk->dev]); setencap(if_pk,"SLIP"); break; #ifdef AX25 case CL_KISS: /* Note that the raw routine puts on the command */ case CL_AX25: pp->handle1 = access_type(intno,class,ANYTYPE,0,NULLCHAR,0, Pkvec[if_pk->dev]); setencap(if_pk,"AX25"); if_pk->hwaddr = mallocw(AXALEN); memcpy(if_pk->hwaddr,Mycall,AXALEN); break; #endif case CL_SLFP: pp->handle1 = access_type(intno,class,ANYTYPE,0,NULLCHAR,0, Pkvec[if_pk->dev]); setencap(if_pk,"SLFP"); get_address(intno,pp->handle1,(char *)&if_pk->addr,4); break; default: tprintf("Packet driver has unsupported class %u\n",class); free(if_pk->name); free((char *)if_pk); return -1; } pp->class = class; if_pk->next = Ifaces; Ifaces = if_pk; if_pk->txproc = newproc("pk tx",768,if_tx,if_pk->dev,if_pk,NULL,0); return 0; } static long access_type(intno,if_class,if_type,if_number,type,typelen,receiver) int intno; int if_class; int if_type; int if_number; char *type; unsigned typelen; INTERRUPT (*receiver)(); { union REGS regs; struct SREGS sregs; segread(&sregs); regs.h.dl = if_number; /* Number */ sregs.ds = FP_SEG(type); /* Packet type template */ regs.x.si = FP_OFF(type); regs.x.cx = typelen; /* Length of type */ sregs.es = FP_SEG(receiver); /* Address of receive handler */ regs.x.di = FP_OFF(receiver); regs.x.bx = if_type; /* Type */ regs.h.ah = ACCESS_TYPE; /* Access_type() function */ regs.h.al = if_class; /* Class */ int86x(intno,®s,®s,&sregs); if(regs.x.cflag){ Derr = regs.h.dh; return -1; } else return regs.x.ax; } static int release_type(intno,handle) int intno; int handle; { union REGS regs; regs.x.bx = handle; regs.h.ah = RELEASE_TYPE; int86(intno,®s,®s); if(regs.x.cflag){ Derr = regs.h.dh; return -1; } else return 0; } static int send_pkt(intno,buffer,length) int intno; char *buffer; unsigned length; { union REGS regs; struct SREGS sregs; segread(&sregs); sregs.ds = FP_SEG(buffer); sregs.es = FP_SEG(buffer); /* for buggy univation pkt driver - CDY */ regs.x.si = FP_OFF(buffer); regs.x.cx = length; regs.h.ah = SEND_PKT; int86x(intno,®s,®s,&sregs); if(regs.x.cflag){ Derr = regs.h.dh; return -1; } else return 0; } static int driver_info(intno,handle,version,class,type,number,basic) int intno; int handle; int *version,*class,*type,*number,*basic; { union REGS regs; regs.x.bx = handle; regs.h.ah = DRIVER_INFO; regs.h.al = 0xff; int86(intno,®s,®s); if(regs.x.cflag){ Derr = regs.h.dh; return -1; } if(version != NULL) *version = regs.x.bx; if(class != NULL) *class = regs.h.ch; if(type != NULL) *type = regs.x.dx; if(number != NULL) *number = regs.h.cl; if(basic != NULL) *basic = regs.h.al; return 0; } static int get_address(intno,handle,buf,len) int intno; int handle; char *buf; int len; { union REGS regs; struct SREGS sregs; segread(&sregs); sregs.es = FP_SEG(buf); regs.x.di = FP_OFF(buf); regs.x.cx = len; regs.x.bx = handle; regs.h.ah = GET_ADDRESS; int86x(intno,®s,®s,&sregs); if(regs.x.cflag){ Derr = regs.h.dh; return -1; } return 0; }