static char rcsid[] = "@(#)$Id: nic5000.c,v 1.1 1995/02/14 15:00:37 jkh Exp $"; /******************************************************************************* * II - Version 0.1 $Revision: 1.1 $ $State: Exp $ * * Copyright 1994 Dietmar Friede ******************************************************************************* * Bug reports, patches, comments, suggestions should be sent to: * * jkr@saarlink.de or jkrause@guug.de * ******************************************************************************* * $Log: nic5000.c,v $ * Revision 1.1 1995/02/14 15:00:37 jkh * An ISDN driver that supports the EDSS1 and the 1TR6 ISDN interfaces. * EDSS1 is the "Euro-ISDN", 1TR6 is the soon obsolete german ISDN Interface. * Obtained from: Dietmar Friede and * Juergen Krause * * This is only one part - the rest to follow in a couple of hours. * This part is a benign import, since it doesn't affect anything else. * * ******************************************************************************/ /* * * Copyright (c) 1994 Dietmar Friede (dietmar@friede.de) All rights reserved. * FSF/FSAG GNU Copyright applies * * A low level driver for the NICCY-5000 ISDN/SCSI device * */ #include "snic.h" #if NSNIC > 0 #define SPLSNIC splbio #define ESUCCESS 0 #define SNIC_RETRIES 8 #include "sys/types.h" #include "sys/param.h" #include "sys/ioctl.h" #include "sys/malloc.h" #include "sys/kernel.h" #include "scsi/scsi_all.h" #include "scsi/scsiconf.h" #include "gnu/isdn/isdn_ioctl.h" #include "gnu/i386/isa/niccyreg.h" #include "gnu/scsi/scsi_nic.h" /* #define NETBSD */ #undef SCSI_NOMASK #define OPEN 1 #define LOAD_HEAD 2 #define LOAD_DATA 4 #define LOAD_ENTITY 8 #define IS_DIAL(p) (((p)&0x20)==0) #define IS_LISTEN(p) ((p)&0x20) #define CHAN(pl) (((pl)&7)-1) #define C_CHAN(x) ((x)&1) #define APPL(pl) ((((pl)>>6)&0x7f)-1) #define CARD(pl) (((pl)>>13)&7) #define MK_APPL(pl) (((pl)+1)<<6) #define min(a,b) ((a)<(b)?(a):(b)) #define SNICOUTSTANDING 2 extern int hz; struct snic_data { struct scsi_switch *sc_sw; /* address of scsi low level switch */ int ctrl; /* so they know which one we want */ int targ; /* our scsi target ID */ int lu; /* out scsi lu */ int cmdscount; /* cmds allowed outstanding by board*/ int xfer_block_wait; struct scsi_xfer *free_xfer; struct scsi_xfer scsi_xfer[SNICOUTSTANDING]; /* XXX */ }; struct snic_driver { int size; struct snic_data **snic_data; }*snic_driver; static int next_snic_unit = 0; static unsigned dnlnum = 0; static u_char ack_msg= 0xff; static u_char snic_nxt_b; typedef enum { DISCON, ISDISCON, DIAL, CALLED, CONNECT, IDLE, ACTIVE, WAITING, WAIT_ACK } io_state; typedef struct { char ctrl; u_char msg_nr; short plci; short ncci; short state; Buffer o_buf; } chan_t; struct snic_softc { short sc_stat; u_char sc_flags; u_char sc_unit; u_char sc_ctrl; u_char sc_type; u_short sc_istat; struct scsi_msg sc_icmd; Buffer sc_imsg; Header sc_imsg0; u_short sc_ostat; struct scsi_msg sc_ocmd; Buffer sc_omsg; chan_t sc_chan[2]; u_char sc_state_ind[8]; u_char sc_gotack; } snic_sc[NSNIC]; extern isdn_appl_t isdn_appl[]; extern isdn_ctrl_t isdn_ctrl[]; extern u_short isdn_state; extern int ispy_applnr; extern int Isdn_Appl, Isdn_Ctrl, Isdn_Typ; extern void isdn_start_out(); static old_spy= 0; static void snic_interupt(); static int snic_get_msg(); static void snic_start(); int snic_connect(), snic_listen(), snic_disconnect(), snic_accept(); int snic_output(); #ifdef NETBSD int snicattach(int ctrl, struct scsi_switch *scsi_switch, int physid, int *sunit) { int targ, lu; #else /* FreeBSD */ int snicattach(int ctrl, int targ, int lu, struct scsi_switch *scsi_switch) { #endif int unit,i; struct snic_data *snic, **snicrealloc; struct snic_softc *sc; int cn; isdn_ctrl_t *ctrl0, *ctrl1; #ifdef NETBSD targ = physid >> 3; lu = physid & 7; #endif if(next_snic_unit >= NSNIC) return(0); unit = next_snic_unit; if (next_snic_unit == 0) { snic_driver = malloc(sizeof(struct snic_driver),M_DEVBUF,M_NOWAIT); if(!snic_driver) { printf("snic%d: malloc failed\n",unit); return(0); } bzero(snic_driver,sizeof(snic_driver)); snic_driver->size = 0; } next_snic_unit++; if(unit >= snic_driver->size) { snicrealloc = malloc(sizeof(snic_driver->snic_data) * next_snic_unit, M_DEVBUF,M_NOWAIT); if(!snicrealloc) { printf("snic%d: malloc failed\n",unit); return(0); } /* Make sure we have something to copy before we copy it */ bzero(snicrealloc,sizeof(snic_driver->snic_data) * next_snic_unit); if(snic_driver->size) { bcopy(snic_driver->snic_data,snicrealloc, sizeof(snic_driver->snic_data) * snic_driver->size); free(snic_driver->snic_data,M_DEVBUF); } snic_driver->snic_data = snicrealloc; snic_driver->snic_data[unit] = NULL; snic_driver->size++; } if(snic_driver->snic_data[unit]) { return(0); } snic = snic_driver->snic_data[unit] = malloc(sizeof(struct snic_data),M_DEVBUF,M_NOWAIT); if(!snic) { printf("snic%d: malloc failed\n",unit); return(0); } #ifdef NETBSD *sunit= unit; #endif bzero(snic,sizeof(struct snic_data)); snic->sc_sw = scsi_switch; snic->ctrl = ctrl; snic->targ = targ; snic->lu = lu; snic->cmdscount = SNICOUTSTANDING; /* XXX (ask the board) */ i = snic->cmdscount; while(i-- ) { snic->scsi_xfer[i].next = snic->free_xfer; snic->free_xfer = &snic->scsi_xfer[i]; } sc = &snic_sc[unit]; sc->sc_ctrl = -1; sc->sc_gotack= 1; if ((cn = isdn_ctrl_attach(2)) == -1) { return (0); } sc->sc_ctrl = cn; sc->sc_chan[0].plci = sc->sc_chan[1].plci = -1; ctrl0 = &isdn_ctrl[cn]; ctrl1 = &isdn_ctrl[cn + 1]; sc->sc_chan[0].ctrl = ctrl0->ctrl = cn; sc->sc_chan[1].ctrl = ctrl1->ctrl = cn + 1; ctrl0->o_buf = &sc->sc_chan[0].o_buf.Data[5]; ctrl1->o_buf = &sc->sc_chan[1].o_buf.Data[5]; ctrl0->listen = ctrl1->listen = snic_listen; ctrl0->disconnect = ctrl1->disconnect = snic_disconnect; ctrl0->accept = ctrl1->accept = snic_accept; ctrl0->connect = ctrl1->connect = snic_connect; ctrl0->output = ctrl1->output = snic_output; ctrl0->unit = ctrl1->unit = unit; ctrl0->appl = ctrl1->appl = -1; ctrl0->o_len = ctrl1->o_len = -1; sc->sc_flags= LOAD_ENTITY; return(1); } static struct scsi_xfer *snic_get_xs(int unit) { struct scsi_xfer *xs; struct snic_data *snic; int s; snic = snic_driver->snic_data[unit]; if (xs = snic->free_xfer) { snic->free_xfer = xs->next; xs->flags = 0; } return(xs); } static void snic_free_xs(int unit, struct scsi_xfer *xs) { struct snic_data *snic; snic = snic_driver->snic_data[unit]; xs->next = snic->free_xfer; snic->free_xfer = xs; } static void snic_timout(int unit) { struct snic_softc * sc= &snic_sc[unit&0xff]; if(sc->sc_istat&0x100) { snic_interupt(unit); return; } if(sc->sc_istat & 2) sc->sc_istat= sc->sc_ostat= 0; else if((sc->sc_istat & 0x200) == 0 ) return; if(sc->sc_ostat & 0xff) { sc->sc_istat|= 0x200; timeout(snic_timout,unit,2); return; } if(sc->sc_gotack) snic_start(unit); snic_get_msg(unit); } static int isdn_small_interupt(int unit, struct scsi_xfer *xs) { struct snic_data *snic = snic_driver->snic_data[unit]; struct snic_softc * sc= &snic_sc[unit]; Header *msg = &sc->sc_imsg0; int c; switch (msg->Type) { case 0: if(sc->sc_istat&0x200) break; sc->sc_istat|= 0x200; timeout(snic_timout,unit,2); break; case 0xff: sc->sc_gotack= 1; break; case 0xfe: printf("f"); sc->sc_gotack= 1; for(c= 0; c < 2; c++) { chan_t *chan = &sc->sc_chan[c]; if(chan->state == WAIT_ACK) { chan->state = WAITING; sc->sc_ostat |= c?0x800:0x400; } } break; case 0xfd: printf("fd"); break; default: return(0); } sc->sc_istat&= ~0xff; sc->sc_imsg0.Type= 0; return(1); } static void snic_get_done(int unit, struct scsi_xfer *xs) { struct snic_data *snic = snic_driver->snic_data[unit]; struct snic_softc * sc= &snic_sc[unit]; Header *msg = &sc->sc_imsg0; int len, error; error= xs->error; switch(error) { case XS_NOERROR: if(xs->datalen == 0) sc->sc_imsg.h.Type= 0; if(isdn_small_interupt(unit,xs)) break; if(xs->datalen < (len=(msg->DataLen + 10))) { struct scsi_msg *scsi_cmd= &sc->sc_icmd; /* resubmit it */ sc->sc_imsg.h.Type= 0xba; scsi_cmd->len[1]= (len>>8)&0xff; scsi_cmd->len[2]= len&0xff; xs->retries= SNIC_RETRIES; xs->error = XS_NOERROR; xs->flags &= ~ITSDONE; xs->data = (char *) &sc->sc_imsg; xs->datalen = len; xs->resid = len; if ((*(snic->sc_sw->scsi_cmd))(xs) == SUCCESSFULLY_QUEUED) { return; } error= xs->error | 0x1000; break; } if(xs->datalen <= 10) { sc->sc_istat|= 0x400; sc->sc_imsg.h = sc->sc_imsg0; } sc->sc_imsg0.Type= 0; break; case XS_TIMEOUT: case XS_BUSY: case XS_DRIVER_STUFFUP: break; default: printf("snic%d: unknown error %x\n",unit,xs->error); } if(error) { sc->sc_imsg.h.Type= sc->sc_imsg0.Type= 0; sc->sc_istat&= 0x200; if((sc->sc_istat&0x200) == 0) { sc->sc_istat= 0x200; timeout(snic_timout,unit,2); } } snic_free_xs(unit,xs); if(sc->sc_istat&0x4ff == 0x400 ) sc->sc_istat|= 1; if(sc->sc_istat&0xff) { snic_interupt(unit); return; } if(sc->sc_gotack) snic_start(unit); if(sc->sc_istat & 0x200) return; sc->sc_istat|= 0x200; timeout(snic_timout,unit,2); } static int snic_get_msg(unit) int unit; { struct snic_data *snic = snic_driver->snic_data[unit]; struct snic_softc * sc= &snic_sc[unit]; struct scsi_msg *scsi_cmd= &sc->sc_icmd; struct scsi_xfer *xs; Header *data= &sc->sc_imsg0; int retval; if(sc->sc_istat&0xff) return(-1); sc->sc_istat |= 1; data->Type= 0xbb; sc->sc_istat &= ~0x200; bzero(scsi_cmd, sizeof(struct scsi_msg)); bzero(data,10); scsi_cmd->op_code = GET_MSG_COMMAND; scsi_cmd->len[2]= 10; xs = snic_get_xs(unit); if(!xs) { sc->sc_istat&= ~0xff; data->Type= 0; return(EBUSY); } xs->flags |= (INUSE | SCSI_DATA_IN | SCSI_NOSLEEP); xs->adapter = snic->ctrl; xs->targ = snic->targ; xs->lu = snic->lu; xs->retries = SNIC_RETRIES; xs->timeout = 2000; xs->cmd = (struct scsi_generic *) scsi_cmd; xs->cmdlen = sizeof(struct scsi_msg); xs->data = (char *) data; xs->datalen = 10; xs->resid = 10; xs->when_done = snic_get_done; xs->done_arg = unit; xs->done_arg2 = (int)xs; xs->bp = NULL; xs->error = XS_NOERROR; if(retval = (*(snic->sc_sw->scsi_cmd))(xs)) { sc->sc_istat= ~0xff; data->Type= 0; snic_free_xs(unit,xs); } return (retval); } static void snic_put_done(int unit, struct scsi_xfer *xs) { int retval; struct snic_data *snic = snic_driver->snic_data[unit]; struct snic_softc * sc= &snic_sc[unit]; Header *b= (Header *) xs->data; int c; sc->sc_ostat&= ~0xff; if(xs->error != XS_NOERROR) { snic_free_xs(unit,xs); switch(b->Type) { case 0: return; case 0xff: sc->sc_ostat|= 0x100; return; case BD_DATA_B3_REQ | 0x40: case BD_DATA_B3_REQ: sc->sc_ostat|= 0x400; return; default: sc->sc_ostat|= 0x200; return; } } snic_free_xs(unit,xs); c= 0; switch(b->Type) { case 0xff: break; case BD_DATA_B3_REQ | 0x40: c= 1; case BD_DATA_B3_REQ: sc->sc_chan[c].state = WAIT_ACK; break; default: b->Type= 0; } if(sc->sc_istat&0x100) { snic_interupt(unit); return; } if(sc->sc_ostat&0x100) { sc->sc_ostat&= ~0x100; if(snic_put_msg(unit,&ack_msg,1,0)) sc->sc_ostat|= 0x100; else return; } if(sc->sc_gotack) snic_start(unit); if(sc->sc_istat&0x200) return; sc->sc_istat|= 0x200; timeout(snic_timout,unit,2); } static void snic_start(int unit) { int retval; struct snic_softc * sc= &snic_sc[unit]; Header *b; int c; if(sc->sc_ostat&0x200) { b= &sc->sc_omsg.h; sc->sc_ostat&= ~0x200; if(snic_put_msg(unit,b, b->DataLen+10,2)) sc->sc_ostat|= 0x200; else return; } for(c= 0; c<2; c++) { int cc= (snic_nxt_b++)&1; u_short m= 0x400 << cc; if(sc->sc_ostat&m) { chan_t *chan= &sc->sc_chan[cc]; b= &chan->o_buf.h; sc->sc_ostat&= ~m; if(chan->state == WAITING) { chan->state= ACTIVE; if(snic_put_msg(unit,b, b->DataLen+10,4)) { chan->state= WAITING; sc->sc_ostat|= m; } else return; } } } } int snic_put_msg(int unit, Header *data, unsigned len, int w) { struct snic_softc *sc = &snic_sc[unit]; struct scsi_msg *scsi_cmd = &sc->sc_ocmd; int retval; struct scsi_xfer *xs; struct snic_data *snic = snic_driver->snic_data[unit]; if(data->Type==0) return(0); if(sc->sc_ostat&0xff) return(EBUSY); sc->sc_ostat |= 1; if((data->Type == 0xa8) || (data->Type == 0xe8)) { if(sc->sc_gotack==0) { sc->sc_ostat &= ~0xff; return(EBUSY); } } if(data->Type != 0xff) sc->sc_gotack= 0; bzero(scsi_cmd, sizeof(struct scsi_msg)); scsi_cmd->op_code = PUT_MSG_COMMAND; if(len > 2063) { printf("snic%d: unsupported length %d\n",unit,len); sc->sc_ostat &= ~0xff; return(ENODEV); } scsi_cmd->len[1]= (len >> 8) & 0xff; scsi_cmd->len[2]= len & 0xff; xs = snic_get_xs(unit); if(!xs) { printf("snic pm%d: busy %d\n", unit, w); sc->sc_ostat &= ~0xff; return(EBUSY); } xs->flags |= (INUSE | SCSI_DATA_OUT | SCSI_NOSLEEP); xs->adapter = snic->ctrl; xs->targ = snic->targ; xs->lu = snic->lu; xs->retries = SNIC_RETRIES; xs->timeout = 2000; xs->cmd = (struct scsi_generic *) scsi_cmd; xs->cmdlen = sizeof(struct scsi_msg); xs->data = (char *)data; xs->datalen = len; xs->resid = len; xs->when_done = snic_put_done; xs->done_arg = unit; xs->done_arg2 = (int)xs; xs->bp = NULL; xs->error = XS_NOERROR; if(retval = (*(snic->sc_sw->scsi_cmd))(xs)) { sc->sc_ostat &= ~0xff; snic_free_xs(unit,xs); return(EBUSY); } return(0); } int snicopen(dev_t dev, int flag) { struct snic_softc *sc; u_char unit; int x; unsigned error; u_char b= 0xff; unit = minor(dev); /* minor number out of limits ? */ if (unit >= next_snic_unit) return (ENXIO); sc = &snic_sc[unit]; x= splhigh(); /* Card busy ? */ if (sc->sc_flags & 7) { splx(x); return (EBUSY); } sc->sc_flags |= OPEN; if(sc->sc_flags & LOAD_ENTITY) { snic_get_msg(unit); /* if(snic_put_msg(unit,(Header *) &ack_msg,1,5)) sc->sc_ostat|= 0x100; */ } splx(x); return (0); } int snicclose(dev_t dev, int flag) { struct snic_softc *sc = &snic_sc[minor(dev)]; sc->sc_flags &= ~7; return (0); } int snicioctl(dev_t dev, int cmd, caddr_t data, int flag) { int error; u_char unit= minor(dev); int i, x; struct snic_softc *sc = &snic_sc[minor(dev)]; Buffer *b= &sc->sc_omsg; error = 0; x= splhigh(); while(sc->sc_ostat || (sc->sc_gotack==0)) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "ioctl", 2); if (error != EWOULDBLOCK) { splx(x); return(error); } } switch (cmd) { case NICCY_DEBUG: data[0]= 0x50; bcopy(sc->sc_state_ind,data+1,8); break; case NICCY_LOAD: { struct head *head = (struct head *) data; int len, l, off; bzero(b, 22); b->h.Type = MD_DNL_MOD_REQ; sc->sc_type = head->typ; b->h.SubType = head->typ; b->h.DataLen = 12; bcopy(head->nam, b->Data, 8); bcopy(&head->len, &b->Data[8], 4); sc->sc_flags |= LOAD_HEAD; sc->sc_stat = -1; while((error= snic_put_msg(unit,(Header *) b,22,6)) == EBUSY) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "nic1", 1); if (error != EWOULDBLOCK) break; } if(error == 0) { while (sc->sc_flags & LOAD_HEAD) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "nic2", 1); if (error != EWOULDBLOCK) break; error= 0; } } if (sc->sc_flags & 7) sc->sc_flags = (sc->sc_flags & ~7 ) | OPEN; if(error) { head->status = sc->sc_stat; splx(x); return (error); } len= head->d_len; off= 0; while(len > 0) { while(sc->sc_ostat || (sc->sc_gotack==0)) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "nic7", 2); if (error != EWOULDBLOCK) { splx(x); return(error); } } bzero(b,10); b->h.Type = MD_DNL_MOD_DATA; sc->sc_type = head->typ; b->h.SubType = head->typ; l= min(len,512); len-= l; b->h.DataLen = l + 8; b->h.Number = dnlnum++; b->h.MoreData= len>0; bcopy(head->nam, b->Data, 8); if(error= copyin(head->data+off, b->Data+8, l)) { splx(x); return(error); } off+= l; sc->sc_flags |= LOAD_DATA; sc->sc_stat = -1; while((error= snic_put_msg(unit,(Header *) b,b->h.DataLen+10,7)) == EBUSY) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "nic3", 1); if (error != EWOULDBLOCK) break; } } if(error == 0) { while (sc->sc_flags & LOAD_DATA) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "nic4", 1); if (error != EWOULDBLOCK) break; error= 0; } } if (sc->sc_flags & 7) sc->sc_flags = (sc->sc_flags & ~7 ) | OPEN; head->status = sc->sc_stat; splx(x); return (error); } case NICCY_SET_CLOCK: bzero(b,10); b->h.Type = MD_SET_CLOCK_REQ; b->h.DataLen = 14; bcopy(data, b->Data,14); while((error= snic_put_msg(unit,(Header *) b,24,8)) == EBUSY) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "nic5", 1); if (error != EWOULDBLOCK) break; } splx(x); return (error); case NICCY_SPY: bzero(b,10); b->h.Type = MD_MANUFACT_REQ; b->h.SubType = 18; b->h.DataLen = 1; /* There are ilegal states. So I use them to toggle */ if((data[0] == 0) && (old_spy == 0)) data[0]= 255; else if(data[0] && old_spy ) data[0]= 0; old_spy= b->Data[0]= data[0]; while((error= snic_put_msg(unit,(Header *) b,11,9)) == EBUSY) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "nic6", 1); if (error != EWOULDBLOCK) break; } splx(x); return (error); case NICCY_RESET: bzero(b,10); b->h.Type = MD_RESET_REQ; while((error= snic_put_msg(unit,(Header *) b,10,9)) == EBUSY) { error = tsleep((caddr_t) sc, PZERO | PCATCH, "nic6", 1); if (error != EWOULDBLOCK) break; } sc->sc_flags|= LOAD_ENTITY; splx(x); return (error); default: error = ENODEV; } splx(x); return (error); } #define con_b3_req(unit,mb,pl) en_q(unit,mb|BD_CONN_B3_REQ,0,pl,0,NULL) #define con_act_resp(unit,pl) en_q(unit,DD_CONN_ACT_RSP,0, pl,0,NULL) #define discon_resp(sc,pl) en_q(unit,DD_DISC_RSP,0, pl,0,NULL) #define inf_resp(unit,pl) en_q(unit,DD_INFO_RSP,0, pl,0,NULL) #define listen_b3_req(unit,mb,pl) en_q(unit,mb|BD_LIST_B3_REQ,0,pl,0,NULL) #define con_resp(unit,pl,rea) en_q(unit,DD_CONN_RSP,0, pl, 1,(u_char *) &rea) static int en_q(int unit, int t, int st, int pl, int l, u_char *val) { struct snic_softc * sc= &snic_sc[unit]; Buffer *b= &sc->sc_omsg; int error= 0; if(b->h.Type) { return(EBUSY); } bzero(b,10); if(( t >= 0x80) && CHAN(pl) && ((t & 0x40) == 0)) printf("?%x %x",t,pl); if(t>=0x40) printf("S%x %x",t,pl); b->h.Type = t; b->h.SubType = st; b->h.PLCI = pl; if(l) { b->h.DataLen= l; bcopy(val,b->Data,l); } if((error= snic_put_msg(unit,(Header *) b,10+l,13)) == EBUSY) { sc->sc_ostat|= 0x200; return(0); } return(error); } static int reset_plci(int w, chan_t * chan, short p) { isdn_ctrl_t *ctrl; if (p == -1) return (-1); if(chan == NULL) return(p); ctrl = &isdn_ctrl[chan->ctrl]; if(chan->plci == p) { if (ISBUSY(ctrl->appl)) { isdn_disconn_ind(ctrl->appl); isdn_appl[ctrl->appl].ctrl = -1; isdn_appl[ctrl->appl].state = 0; } ctrl->appl = -1; ctrl->o_len = -1; chan->plci = -1; chan->ncci = -1; chan->state = DISCON; chan->o_buf.h.Type= 0; } return (p); } static int sel_b2_prot_req(int unit, int c, int pl, dlpd_t * dlpd) { return(en_q(unit, (c ? 0x40 : 0)| BD_SEL_PROT_REQ, 2, pl, sizeof(dlpd_t), (u_char *) dlpd)); } static int sel_b3_prot_req(int unit, int mb, u_short pl, ncpd_t * ncpd) { return(en_q(unit, mb | BD_SEL_PROT_REQ, 3, pl, sizeof(ncpd_t), (u_char *) ncpd)); } static int discon_req(int w, int unit , int pl, int rea, int err) { if((pl == 0) || (pl == -1)) return(0); return(en_q(unit, DD_DISC_REQ,0, pl, 1, (u_char *) &rea)); } static int state_ind(int unit, int api, int spv) { u_char buf[3]; buf[0]= unit; buf[1]= api; buf[2]= spv; return(en_q(unit, MD_STATE_IND,0, 0, 3, buf)); } static int con_b3_resp(int unit, int mb, u_short ncci, u_short pl, u_char reject) { u_char buf[32]; int l = 4; bzero(buf, 32); *(u_short *) buf = ncci; buf[2] = reject; buf[3] = 0; /* ncpi ??? */ l += 15; return(en_q(unit, mb | BD_CONN_B3_RSP,0, pl, l, buf)); } int snic_connect(int cn, int ap, int b_channel, int inf_mask, int out_serv ,int out_serv_add, int src_subadr, unsigned ad_len ,char *dest_addr, int spv) { char buf[128]; if (ad_len > 118) return (-1); buf[0] = spv ? 0x53 : 0; buf[1] = b_channel; if (spv) inf_mask |= 0x40000000; *(u_long *) & buf[2] = inf_mask; buf[6] = out_serv; buf[7] = out_serv_add; buf[8] = src_subadr; buf[9] = ad_len; bcopy(dest_addr, &buf[10], ad_len); return (en_q(isdn_ctrl[cn].unit, DD_CONN_REQ, 0, MK_APPL(ap), ad_len + 10, buf)); } int snic_listen(int cn, int ap, int inf_mask, int subadr_mask, int si_mask, int spv) { u_short sbuf[4]; if (spv) inf_mask |= 0x40000000; *(u_long *) sbuf = inf_mask; sbuf[2] = subadr_mask; sbuf[3] = si_mask; return (en_q(isdn_ctrl[cn].unit, DD_LISTEN_REQ, 0, MK_APPL(ap), 8, (u_char *) sbuf)); } int snic_disconnect(int cn, int rea) { isdn_ctrl_t *ctrl = &isdn_ctrl[cn]; chan_t *chan = &snic_sc[ctrl->unit].sc_chan[C_CHAN(cn)]; int p, err; u_char buf[16]; if(chan->ncci != -1) { bzero(buf,16); *(u_short *) buf = chan->ncci; err= en_q(ctrl->unit, (C_CHAN(cn)?0x40:0)|BD_DISC_B3_REQ, 0 , chan->plci, 3+sizeof(ncpi_t), buf); if((err==0) && (ctrl->o_len == 0)) ctrl->o_len= -1; return(err); } p = chan->plci; if ((p == 0) || (p == -1)) return (ENODEV); err= en_q(ctrl->unit, DD_DISC_REQ, 0, p, 1, (u_char *) &rea); if((err==0) && (ctrl->o_len == 0)) ctrl->o_len= -1; return(err); } int snic_accept(int cn, int an, int rea) { isdn_ctrl_t *ctrl = &isdn_ctrl[cn]; struct snic_softc *sc = &snic_sc[ctrl->unit]; chan_t *chan = &sc->sc_chan[C_CHAN(cn)]; isdn_appl_t *appl = &isdn_appl[an]; if(ISFREE(ctrl->appl)) return(ENODEV); if (rea) { ctrl->appl= -1; return(discon_req(1, ctrl->unit, chan->plci, rea, 0)); } ctrl->appl= an; ctrl->lastact = time.tv_sec; appl->ctrl= cn; appl->state= 4; return(sel_b2_prot_req(ctrl->unit, C_CHAN(cn), chan->plci, &appl->dlpd)); } int snic_output(int cn) { isdn_ctrl_t *ctrl = &isdn_ctrl[cn]; struct snic_softc *sc = &snic_sc[ctrl->unit]; chan_t *chan = &sc->sc_chan[C_CHAN(cn)]; int len= ctrl->o_len; Buffer *b= &chan->o_buf; int error= 0; if (sc->sc_state_ind[1] || (chan->ncci == -1)) return (ENODEV); if(chan->state != IDLE) return(EBUSY); chan->state= WAITING; bzero(b,10); b->h.Type = BD_DATA_B3_REQ; if(C_CHAN(cn)) b->h.Type |= 0x40; b->h.PLCI = chan->plci; b->h.DataLen= len+5; *(u_short *) b->Data = chan->ncci; *(u_short *) &b->Data[2] = 0; b->h.Number = b->Data[4] = chan->msg_nr++; chan->state = ACTIVE; ctrl->lastact = time.tv_sec; if((error= snic_put_msg(ctrl->unit,(Header *) b,15+len,14)) == EBUSY) { sc->sc_ostat|= C_CHAN(cn)?0x800:0x400; chan->state= WAITING; return(0); } return(error); } static void badstate(Header *h, int n) { int i; u_char *p= (u_char *)h; printf("Niccy: not implemented %x.%x len %d at %d", h->Type, h->SubType, h->DataLen,n); if(h->DataLen) { p+= 10; for(i=0; i < h->DataLen ; i++) printf(" %x",p[i]); } printf("\n"); } unsigned SavMsgTyp; static void snic_interupt(unsigned unit) { struct snic_softc * sc= &snic_sc[unit&0xff]; Buffer *msg; chan_t *chan; u_short n, mb, c, pl, err = 0; isdn_ctrl_t *ctrl; isdn_appl_t *appl; int error= 0; msg = &sc->sc_imsg; chan= NULL; ctrl= NULL; appl= NULL; SavMsgTyp= msg->h.Type; if(sc->sc_istat & 2) return; if(sc->sc_ostat&0xff) { sc->sc_istat|= 0x101; if(sc->sc_istat&0x200) return; sc->sc_istat|= 0x200; timeout(snic_timout,unit,2); return; } mb= 0; pl = msg->h.PLCI; if(pl && (msg->h.Type >= 0x40) && (msg->h.Type < 0xfd) && (msg->h.Type != 0x47)) { if ((c = CHAN(pl)) < 2) { chan = &sc->sc_chan[c]; ctrl = &isdn_ctrl[chan->ctrl]; } else { c = 0xffff; chan= NULL; ctrl= NULL; } if(ctrl && (ctrl->appl & 0xC0) == 0) appl= &isdn_appl[ctrl->appl]; else if( APPL(pl) < 0x30) appl = &isdn_appl[APPL(pl)]; else if( APPL(pl) < 0x40) appl= NULL; else goto fin; if(msg->h.Type >= 0x80) { mb= msg->h.Type & 0x40; msg->h.Type &= 0xbf; } } SavMsgTyp|= 0x100; if(msg->h.Type>=0x40) printf("I%x %x %x",msg->h.Type,pl,mb); switch (msg->h.Type) { case 0x01: /* INIT IND */ case 0x15: /* POLL IND */ error= en_q(unit,msg->h.Type|0x20,0,0,0,NULL); break; case 0x04: /* DNL MOD CONF */ sc->sc_stat = msg->Data[0]; if (sc->sc_flags & 7) sc->sc_flags = (sc->sc_flags & ~7) | OPEN; break; case 0x06: /* DNL MOD IND */ sc->sc_stat = msg->Data[0]; error= en_q(unit,msg->h.Type|0x20,sc->sc_type,0,1, &msg->Data[1]); if(sc->sc_flags & LOAD_ENTITY) { sc->sc_istat= sc->sc_ostat= 2; timeout(snic_timout,unit,hz); msg->h.Type= 0; return; } if (sc->sc_flags) sc->sc_flags = OPEN; break; case 0x0e: /* SET CLOCK CONF */ error= state_ind(unit,1,0); break; case 0x16: /* STATE IND */ if(sc->sc_flags & LOAD_ENTITY) { if(sc->sc_flags & 7) sc->sc_flags = OPEN; else sc->sc_flags= 0; } bcopy( msg->Data, sc->sc_state_ind, 8); error= en_q(unit,msg->h.Type|0x20,0,0,0,NULL); break; case 0x17: /* STATE RESP */ bcopy( msg->Data, sc->sc_state_ind, 8); break; case 0x1e: /* MANUFACT CONF */ if(msg->h.SubType == 18) break; badstate(&msg->h,1); break; case 0x1f: /* MANUFACT IND */ if(msg->h.SubType == 19) { isdn_input(ispy_applnr, msg->h.DataLen, msg->Data,0); error= en_q(unit,msg->h.Type|0x20,msg->h.SubType,0,0,NULL); break; } badstate(&msg->h,2); break; case 0x40: /* CONNECT CONF */ err = *(u_short *) msg->Data; if (err || (appl == NULL) || (chan == NULL) || (ctrl == NULL)) { if(chan) reset_plci(3, chan, pl); if(appl) appl->state= 0; break; } if (ISBUSY(ctrl->appl)) { error= discon_req(2, unit, pl, 0, 0); break; } chan->plci = pl; chan->msg_nr = 0; chan->ncci = -1; ctrl->lastact = time.tv_sec; ctrl->appl = APPL(pl); appl->ctrl = chan->ctrl; ctrl->islisten= 0; chan->state = DIAL; appl->state= 3; break; case 0x41: /* CONNECT IND */ if (ISBUSY(ctrl->appl)) { error= discon_req(3, unit, pl, 0, 0); break; } chan->plci = pl; chan->msg_nr = 0; chan->ncci = -1; ctrl->lastact = time.tv_sec; ctrl->appl = 0x7f; ctrl->islisten= 1; chan->state = CALLED; msg->Data[msg->Data[3] + 4] = 0; isdn_accept_con_ind(APPL(pl), chan->ctrl, msg->Data[0], msg->Data[1] ,msg->Data[2], msg->Data[3], (char *) &msg->Data[4]); break; case 0x42: /* CONNECT ACTIVE IND */ error= con_act_resp(unit, pl); if (IS_LISTEN(pl)) { isdn_conn_ind(ctrl->appl,chan->ctrl,0); break; } isdn_conn_ind(APPL(pl),chan->ctrl,1); chan->state = CONNECT; ctrl->appl = APPL(pl); appl->ctrl = chan->ctrl; break; case 0x43: /* DISCONNECT CONF */ reset_plci(4, chan, pl); break; case 0x44: /* DISCONNECT IND */ error= discon_resp(unit, reset_plci(5, chan, pl)); break; case 0x47: /* LISTEN CONF */ isdn_state = *(u_short *) msg->Data; break; case 0x4a: /* INFO IND */ isdn_info(APPL(pl),*(u_short *)msg->Data, msg->Data[2], msg->Data+3); error= inf_resp(unit, pl); break; case 0x80: /* SELECT PROT CONF */ err = *(u_short *) msg->Data; if (err) { error= discon_req(4, unit, pl, 0, err); break; } switch (msg->h.SubType) { case 2:/* SELECT B2 PROTOCOL */ if(ISFREE(ctrl->appl)) break; error= sel_b3_prot_req(unit, mb, pl, &isdn_appl[ctrl->appl].ncpd); break; case 3:/* SELECT B3 PROTOCOL */ if (IS_DIAL(pl)) error= con_b3_req(unit, mb, pl); else error= listen_b3_req(unit, mb, pl); break; } break; case 0x81: /* LISTEN B3 CONF */ err = *(u_short *) msg->Data; if (err) { error= discon_req(5, unit, pl, 0, err); break; } error= con_resp(unit, pl, err); break; case 0x82: /* CONNECT B3 CONF */ err = *(u_short *) (msg->Data + 2); n = *(u_short *) msg->Data; if (err) { error= discon_req(6, unit, pl, 0, err); break; } if(ISFREE(ctrl->appl)) break; chan->ncci = n; chan->state = CONNECT; break; case 0x83: /* CONNECT B3 IND */ if(ISFREE(ctrl->appl)) break; n = *(u_short *) msg->Data; chan->ncci = n; chan->state = CONNECT; error= con_b3_resp(unit, mb, n, pl, 0); break; case 0x84: /* CONNECT B3 ACTIVE IND */ if(ISFREE(ctrl->appl)) break; if (chan->state < IDLE) { chan->state = IDLE; ctrl->o_len = 0; timeout(isdn_start_out,chan->ctrl,hz/5); } break; case 0x85: /* DISCONNECT B3 CONF */ if(ISBUSY(ctrl->appl)) chan->state = ISDISCON; err = *(u_short *) (msg->Data + 2); if (err) { error= discon_req(7, unit, pl, 0, err); break; } break; case 0x86: /* DISCONNECT B3 IND */ if(ISBUSY(ctrl->appl)) chan->state = ISDISCON; err = *(u_short *) (msg->Data + 2); error= discon_req(8, unit, pl, 0, err); break; case 0x88: /* DATA B3 CONF */ if(ISFREE(ctrl->appl)) break; err = *(u_short *) (msg->Data + 2); if (err) { printf("e%x\n",err); ctrl->send_err++; isdn_appl[ctrl->appl].send_err++; } chan->state = IDLE; chan->o_buf.h.Type= 0; ctrl->o_len = 0; isdn_start_out(chan->ctrl); break; case 0x89: /* DATA B3 IND */ if(ISFREE(ctrl->appl)) break; if(isdn_input(ctrl->appl, msg->h.DataLen-5, msg->Data+5,ctrl->islisten)) ctrl->lastact = time.tv_sec; break; default: badstate(&msg->h,3); break; } fin: if(error) { printf("x%x %x %x %x %x\n",error,msg->h.Type,sc->sc_istat,sc->sc_ostat,sc->sc_omsg.h.Type); sc->sc_istat|= 0x101; if(sc->sc_istat&0x200) return; sc->sc_istat|= 0x200; timeout(snic_timout,unit,2); return; } msg->h.Type= 0; if(snic_put_msg(unit,(Header *) &ack_msg,1,15)) sc->sc_ostat|= 0x100; sc->sc_istat= 0x200; snic_get_msg(unit); } #endif /* NSNIC > 0 */