Example #1
0
/*---------------------------------------------------------------------------*
 *	L3 FSM some states event RELEASE COMPLETE from L2
 *---------------------------------------------------------------------------*/	
static void F_RELCP(call_desc_t *cd)
{
	NDBGL3(L3_F_MSG, "FSM function F_RELCP executing");
	i4b_l3_stop_all_timers(cd);
	i4b_l4_disconnect_ind(cd);
	freecd_by_cd(cd);
}
Example #2
0
/*---------------------------------------------------------------------------*
 *	L3 FSM state U19 event RELEASE COMPLETE from L2
 *---------------------------------------------------------------------------*/	
static void F_19K(call_desc_t *cd)
{
	NDBGL3(L3_F_MSG, "FSM function F_19K executing");
	T308_stop(cd);
	i4b_l4_disconnect_ind(cd);
	freecd_by_cd(cd);
}
Example #3
0
/*---------------------------------------------------------------------------*
 *	L3 FSM state U1 event RELEASE COMPLETE from L2
 *---------------------------------------------------------------------------*/	
static void F_01K(call_desc_t *cd)
{
	NDBGL3(L3_F_MSG, "FSM function F_01K executing");
	T303_stop(cd);
	i4b_l4_disconnect_ind(cd);	/* tell l4 we were rejected */
	freecd_by_cd(cd);	
}
Example #4
0
/*---------------------------------------------------------------------------*
 *	L3 FSM state U12 event RELEASE from L2
 *---------------------------------------------------------------------------*/	
static void F_12J(call_desc_t *cd)
{
	NDBGL3(L3_F_MSG, "FSM function F_12J executing");
	i4b_l3_tx_release_complete(cd, 0);
	i4b_l4_disconnect_ind(cd);	
	freecd_by_cd(cd);
}
Example #5
0
/*---------------------------------------------------------------------------*
 *	L3 FSM state U8 event T313 timeout
 *---------------------------------------------------------------------------*/	
static void F_08Z(call_desc_t *cd)
{
	NDBGL3(L3_F_MSG, "FSM function F_08Z executing");
	cd->cause_out = 102;	/* recovery on timer expiry */
	i4b_l3_tx_disconnect(cd);
	T305_start(cd);
	i4b_l4_disconnect_ind(cd);
}
Example #6
0
/*---------------------------------------------------------------------------*
 *	L3 FSM state U1 event T303 timeout
 *---------------------------------------------------------------------------*/	
static void F_01U(call_desc_t *cd)
{
	NDBGL3(L3_F_MSG, "FSM function F_01U executing");
	if(cd->T303_first_to == 1)
	{
		cd->T303_first_to = 0;
		i4b_l3_tx_setup(cd);
		T303_start(cd);
		cd->Q931state = ST_U1;
	}
	else
	{
		i4b_l4_disconnect_ind(cd);
		freecd_by_cd(cd);
		cd->Q931state = ST_U0;
	}
}
Example #7
0
/*---------------------------------------------------------------------------*
 *	L3 FSM state U19 event T308 timeout
 *---------------------------------------------------------------------------*/	
static void F_19W(call_desc_t *cd)
{
	NDBGL3(L3_F_MSG, "FSM function F_19W executing");
	if(cd->T308_first_to == 0)
	{
		cd->T308_first_to = 1;
		i4b_l3_tx_release(cd, 0);
		T308_start(cd);
		cd->Q931state = ST_U19;
	}
	else
	{
		cd->T308_first_to = 0;
		i4b_l4_disconnect_ind(cd);
		freecd_by_cd(cd);
		cd->Q931state = ST_U0;
	}
}
Example #8
0
/*---------------------------------------------------------------------------*
 *	i4b_mdl_status_ind - status indication from lower layers
 *---------------------------------------------------------------------------*/
int
i4b_mdl_status_ind(struct isdn_l3_driver *d, int status, int parm)
{
	int sendup, update_leds = 0;
	int i;

	NDBGL3(L3_MSG, "isdnif = %d, status = %d, parm = %d",
	    d->isdnif, status, parm);

	switch(status)
	{
		case STI_ATTACH:
			if (parm) {
				NDBGL3(L3_MSG, "STI_ATTACH: attaching isdnif %d", d->isdnif);
			} else {
				NDBGL3(L3_MSG, "STI_ATTACH: dettaching isdnif %d", d->isdnif);
			}
			break;

		case STI_L1STAT:
			i4b_l4_l12stat(d, 1, parm);
			update_leds = 1;
			NDBGL3(L3_MSG, "STI_L1STAT: isdnif %d layer 1 = %s", d->isdnif, status ? "up" : "down");
			break;

		case STI_L2STAT:
			i4b_l4_l12stat(d, 2, parm);
			update_leds = 1;
			NDBGL3(L3_MSG, "STI_L2STAT: isdnif %d layer 2 = %s", d->isdnif, status ? "up" : "down");
			break;

		case STI_TEIASG:
			d->tei = parm;
			i4b_l4_teiasg(d, parm);
			update_leds = 1;
			NDBGL3(L3_MSG, "STI_TEIASG: isdnif %d TEI = %d = 0x%02x", d->isdnif, parm, parm);
			break;

		case STI_PDEACT:	/* L1 T4 timeout */
			NDBGL3(L3_ERR, "STI_PDEACT: isdnif %d TEI = %d = 0x%02x", d->isdnif, parm, parm);

			update_leds = 1;
			sendup = 0;

			for(i=0; i < num_call_desc; i++)
			{
				if(call_desc[i].isdnif == d->isdnif)
                		{
					i4b_l3_stop_all_timers(&(call_desc[i]));
					if(call_desc[i].cdid != CDID_UNUSED) {
						sendup++;
						call_desc[i].cdid = CDID_UNUSED;
					}
				}
			}

			d->dl_est = DL_DOWN;

			for (i = 0; i < d->nbch; i++)
				d->bch_state[i] = BCH_ST_FREE;
			d->tei = -1;

			if(sendup)
			{
				i4b_l4_pdeact(d, sendup);
			}
			break;

		case STI_NOL1ACC:	/* no outgoing access to S0 */
			NDBGL3(L3_ERR, "STI_NOL1ACC: isdnif %d no outgoing access to S0", d->isdnif);
			update_leds = 1;

			for(i=0; i < num_call_desc; i++)
			{
				if(call_desc[i].isdnif == d->isdnif)
                		{
					if(call_desc[i].cdid != CDID_UNUSED)
					{
						SET_CAUSE_TYPE(call_desc[i].cause_in, CAUSET_I4B);
						SET_CAUSE_VAL(call_desc[i].cause_in, CAUSE_I4B_L1ERROR);
						i4b_l4_disconnect_ind(&(call_desc[i]));
					}
				}
			}
			d->dl_est = DL_DOWN;

			for (i = 0; i < d->nbch; i++)
				d->bch_state[i] = BCH_ST_FREE;
			d->tei = -1;

			break;

		default:
			NDBGL3(L3_ERR, "ERROR, isdnif %d, unknown status value %d!", d->isdnif, status);
			break;
	}

	if (update_leds && d != NULL)
		update_controller_leds(d);

	return(0);
}
Example #9
0
/*---------------------------------------------------------------------------*
 *	L3 FSM state U6 event DISCONNECT from L2
 *---------------------------------------------------------------------------*/	
static void F_06Q(call_desc_t *cd)
{
	NDBGL3(L3_F_MSG, "FSM function F_06Q executing");
	i4b_l4_disconnect_ind(cd);
}
Example #10
0
i4bioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
#endif
{
	call_desc_t *cd;
	int error = 0;
	
	if(minor(dev))
		return(ENODEV);

	switch(cmd)
	{
		/* cdid request, reserve cd and return cdid */

		case I4B_CDID_REQ:
		{
			msg_cdid_req_t *mir;
			mir = (msg_cdid_req_t *)data;
			cd = reserve_cd();
			mir->cdid = cd->cdid;
			break;
		}
		
		/* connect request, dial out to remote */
		
		case I4B_CONNECT_REQ:
		{
			msg_connect_req_t *mcr;
			mcr = (msg_connect_req_t *)data;	/* setup ptr */

			if((cd = cd_by_cdid(mcr->cdid)) == NULL)/* get cd */
			{
				DBGL4(L4_ERR, "i4bioctl", ("I4B_CONNECT_REQ ioctl, cdid not found!\n")); 
				error = EINVAL;
				break;
			}

			/* prevent dialling on leased lines */
			if(ctrl_desc[mcr->controller].protocol == PROTOCOL_D64S)
			{
				SET_CAUSE_TYPE(cd->cause_in, CAUSET_I4B);
				SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_LLDIAL);
				i4b_l4_disconnect_ind(cd);
				freecd_by_cd(cd);
				break;
			}

			cd->controller = mcr->controller;	/* fill cd */
			cd->bprot = mcr->bprot;
			cd->driver = mcr->driver;
			cd->driver_unit = mcr->driver_unit;
			cd->cr = get_rand_cr(ctrl_desc[cd->controller].unit);

			cd->shorthold_data.shorthold_algorithm = mcr->shorthold_data.shorthold_algorithm;
			cd->shorthold_data.unitlen_time  = mcr->shorthold_data.unitlen_time;
			cd->shorthold_data.idle_time     = mcr->shorthold_data.idle_time;
			cd->shorthold_data.earlyhup_time = mcr->shorthold_data.earlyhup_time;

			cd->last_aocd_time = 0;
			if(mcr->unitlen_method == ULEN_METHOD_DYNAMIC)
				cd->aocd_flag = 1;
			else
				cd->aocd_flag = 0;
				
			cd->cunits = 0;

			cd->max_idle_time = 0;	/* this is outgoing */

			cd->dir = DIR_OUTGOING;
			
			DBGL4(L4_TIMO, "i4bioctl", ("I4B_CONNECT_REQ times, algorithm=%ld unitlen=%ld idle=%ld earlyhup=%ld\n",
					(long)cd->shorthold_data.shorthold_algorithm, (long)cd->shorthold_data.unitlen_time,
					(long)cd->shorthold_data.idle_time, (long)cd->shorthold_data.earlyhup_time));

			strcpy(cd->dst_telno, mcr->dst_telno);
			strcpy(cd->src_telno, mcr->src_telno);
			cd->display[0] = '\0';

			SET_CAUSE_TYPE(cd->cause_in, CAUSET_I4B);
			SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NORMAL);
			
			switch(mcr->channel)
			{
				case CHAN_B1:
				case CHAN_B2:
					if(ctrl_desc[mcr->controller].bch_state[mcr->channel] != BCH_ST_FREE)
						SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NOCHAN);
					break;

				case CHAN_ANY:
					if((ctrl_desc[mcr->controller].bch_state[CHAN_B1] != BCH_ST_FREE) &&
					   (ctrl_desc[mcr->controller].bch_state[CHAN_B2] != BCH_ST_FREE))
						SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NOCHAN);
					break;

				default:
					SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NOCHAN);
					break;
			}

			cd->channelid = mcr->channel;

			cd->isdntxdelay = mcr->txdelay;
			
			/* check whether we have a pointer. Seems like */
			/* this should be adequate. GJ 19.09.97 */
			if(ctrl_desc[cd->controller].N_CONNECT_REQUEST == NULL)
/*XXX*/				SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NOCHAN);

			if((GET_CAUSE_VAL(cd->cause_in)) != CAUSE_I4B_NORMAL)
			{
				i4b_l4_disconnect_ind(cd);
				freecd_by_cd(cd);
			}
			else
			{
				(*ctrl_desc[cd->controller].N_CONNECT_REQUEST)(mcr->cdid);
			}
			break;
		}
		
		/* connect response, accept/reject/ignore incoming call */
		
		case I4B_CONNECT_RESP:
		{
			msg_connect_resp_t *mcrsp;
			
			mcrsp = (msg_connect_resp_t *)data;

			if((cd = cd_by_cdid(mcrsp->cdid)) == NULL)/* get cd */
			{
				DBGL4(L4_ERR, "i4bioctl", ("I4B_CONNECT_RESP ioctl, cdid not found!\n")); 
				error = EINVAL;
				break;
			}

			T400_stop(cd);

			cd->driver = mcrsp->driver;
			cd->driver_unit = mcrsp->driver_unit;
			cd->max_idle_time = mcrsp->max_idle_time;

			cd->shorthold_data.shorthold_algorithm = SHA_FIXU;
			cd->shorthold_data.unitlen_time = 0;	/* this is incoming */
			cd->shorthold_data.idle_time = 0;
			cd->shorthold_data.earlyhup_time = 0;

			cd->isdntxdelay = mcrsp->txdelay;			
			
			DBGL4(L4_TIMO, "i4bioctl", ("I4B_CONNECT_RESP max_idle_time set to %ld seconds\n", (long)cd->max_idle_time));

			(*ctrl_desc[cd->controller].N_CONNECT_RESPONSE)(mcrsp->cdid, mcrsp->response, mcrsp->cause);
			break;
		}
		
		/* disconnect request, actively terminate connection */
		
		case I4B_DISCONNECT_REQ:
		{
			msg_discon_req_t *mdr;
			
			mdr = (msg_discon_req_t *)data;

			if((cd = cd_by_cdid(mdr->cdid)) == NULL)/* get cd */
			{
				DBGL4(L4_ERR, "i4bioctl", ("I4B_DISCONNECT_REQ ioctl, cdid not found!\n")); 
				error = EINVAL;
				break;
			}

			/* preset causes with our cause */
			cd->cause_in = cd->cause_out = mdr->cause;
			
			(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(mdr->cdid, mdr->cause);
			break;
		}
		
		/* controller info request */

		case I4B_CTRL_INFO_REQ:
		{
			msg_ctrl_info_req_t *mcir;
			
			mcir = (msg_ctrl_info_req_t *)data;
			mcir->ncontroller = nctrl;

			if(mcir->controller > nctrl)
			{
				mcir->ctrl_type = -1;
				mcir->card_type = -1;
			}
			else
			{
				mcir->ctrl_type = 
					ctrl_desc[mcir->controller].ctrl_type;
				mcir->card_type = 
					ctrl_desc[mcir->controller].card_type;

				if(ctrl_desc[mcir->controller].ctrl_type == CTRL_PASSIVE)
					mcir->tei = ctrl_desc[mcir->controller].tei;
				else
					mcir->tei = -1;
			}
			break;
		}
		
		/* dial response */
		
		case I4B_DIALOUT_RESP:
		{
			drvr_link_t *dlt = NULL;
			msg_dialout_resp_t *mdrsp;
			
			mdrsp = (msg_dialout_resp_t *)data;

			switch(mdrsp->driver)
			{
#if NI4BIPR > 0
				case BDRV_IPR:
					dlt = ipr_ret_linktab(mdrsp->driver_unit);
					break;
#endif					

#if NI4BISPPP > 0
				case BDRV_ISPPP:
					dlt = i4bisppp_ret_linktab(mdrsp->driver_unit);
					break;
#endif

#if NI4BTEL > 0
				case BDRV_TEL:
					dlt = tel_ret_linktab(mdrsp->driver_unit);
					break;
#endif

#if NIBC > 0
				case BDRV_IBC:
					dlt = ibc_ret_linktab(mdrsp->driver_unit);
					break;
#endif
			}

			if(dlt != NULL)		
				(*dlt->dial_response)(mdrsp->driver_unit, mdrsp->stat, mdrsp->cause);
			break;
		}
		
		/* update timeout value */
		
		case I4B_TIMEOUT_UPD:
		{
			msg_timeout_upd_t *mtu;
			int x;
			
			mtu = (msg_timeout_upd_t *)data;

			DBGL4(L4_TIMO, "i4bioctl", ("I4B_TIMEOUT_UPD ioctl, alg %d, unit %d, idle %d, early %d!\n",
					mtu->shorthold_data.shorthold_algorithm, mtu->shorthold_data.unitlen_time,
					mtu->shorthold_data.idle_time, mtu->shorthold_data.earlyhup_time )); 

			if((cd = cd_by_cdid(mtu->cdid)) == NULL)/* get cd */
			{
				DBGL4(L4_ERR, "i4bioctl", ("I4B_TIMEOUT_UPD ioctl, cdid not found!\n")); 
				error = EINVAL;
				break;
			}

			switch( mtu->shorthold_data.shorthold_algorithm )
			{
				case SHA_FIXU:
					/*
					 * For this algorithm unitlen_time,
					 * idle_time and earlyhup_time are used.
					 */

					if(!(mtu->shorthold_data.unitlen_time >= 0 &&
					     mtu->shorthold_data.idle_time >= 0    &&
					     mtu->shorthold_data.earlyhup_time >= 0))
					{
						DBGL4(L4_ERR, "i4bioctl", ("I4B_TIMEOUT_UPD ioctl, invalid args for fix unit algorithm!\n")); 
						error = EINVAL;
					}
					break;
	
				case SHA_VARU:
					/*
					 * For this algorithm unitlen_time and
					 * idle_time are used. both must be
					 * positive integers. earlyhup_time is
					 * not used and must be 0.
					 */

					if(!(mtu->shorthold_data.unitlen_time > 0 &&
					     mtu->shorthold_data.idle_time >= 0   &&
					     mtu->shorthold_data.earlyhup_time == 0))
					{
						DBGL4(L4_ERR, "i4bioctl", ("I4B_TIMEOUT_UPD ioctl, invalid args for var unit algorithm!\n")); 
						error = EINVAL;
					}
					break;
	
				default:
					DBGL4(L4_ERR, "i4bioctl", ("I4B_TIMEOUT_UPD ioctl, invalid algorithm!\n")); 
					error = EINVAL;
					break;
			}

			/*
			 * any error set above requires us to break
			 * out of the outer switch
			 */
			if(error != 0)
				break;

			x = SPLI4B();
			cd->shorthold_data.shorthold_algorithm = mtu->shorthold_data.shorthold_algorithm;
			cd->shorthold_data.unitlen_time = mtu->shorthold_data.unitlen_time;
			cd->shorthold_data.idle_time = mtu->shorthold_data.idle_time;
			cd->shorthold_data.earlyhup_time = mtu->shorthold_data.earlyhup_time;
			splx(x);
			break;
		}
			
		/* soft enable/disable interface */
		
		case I4B_UPDOWN_IND:
		{
			msg_updown_ind_t *mui;
			
			mui = (msg_updown_ind_t *)data;

#if NI4BIPR > 0
			if(mui->driver == BDRV_IPR)
			{
				drvr_link_t *dlt;
				dlt = ipr_ret_linktab(mui->driver_unit);
				(*dlt->updown_ind)(mui->driver_unit, mui->updown);
			}
#endif
			break;
		}
		
		/* send ALERT request */
		
		case I4B_ALERT_REQ:
		{
			msg_alert_req_t *mar;
			
			mar = (msg_alert_req_t *)data;

			if((cd = cd_by_cdid(mar->cdid)) == NULL)
			{
				DBGL4(L4_ERR, "i4bioctl", ("I4B_ALERT_REQ ioctl, cdid not found!\n")); 
				error = EINVAL;
				break;
			}

			T400_stop(cd);
			
			(*ctrl_desc[cd->controller].N_ALERT_REQUEST)(mar->cdid);

			break;
		}

		/* version/release number request */
		
		case I4B_VR_REQ:
                {
			msg_vr_req_t *mvr;

			mvr = (msg_vr_req_t *)data;

			mvr->version = VERSION;
			mvr->release = REL;
			mvr->step = STEP;			
			break;
		}

		/* set D-channel protocol for a controller */
		
		case I4B_PROT_IND:
		{
			msg_prot_ind_t *mpi;
			
			mpi = (msg_prot_ind_t *)data;

			ctrl_desc[mpi->controller].protocol = mpi->protocol;
			
			break;
		}
		
		/* Download request */

		case I4B_CTRL_DOWNLOAD:
		{
			struct isdn_dr_prot *prots = NULL, *prots2 = NULL;
			struct isdn_download_request *r =
				(struct isdn_download_request*)data;
			int i;

			if (r->controller < 0 || r->controller >= nctrl)
			{
				error = ENODEV;
				goto download_done;
			}

			if(!ctrl_desc[r->controller].N_DOWNLOAD)
			{
				error = ENODEV;
				goto download_done;
			}

			prots = malloc(r->numprotos * sizeof(struct isdn_dr_prot),
					M_DEVBUF, M_WAITOK);

			prots2 = malloc(r->numprotos * sizeof(struct isdn_dr_prot),
					M_DEVBUF, M_WAITOK);

			if(!prots || !prots2)
			{
				error = ENOMEM;
				goto download_done;
			}

			copyin(r->protocols, prots, r->numprotos * sizeof(struct isdn_dr_prot));

			for(i = 0; i < r->numprotos; i++)
			{
				prots2[i].microcode = malloc(prots[i].bytecount, M_DEVBUF, M_WAITOK);
				copyin(prots[i].microcode, prots2[i].microcode, prots[i].bytecount);
				prots2[i].bytecount = prots[i].bytecount; 
			}

			error = ctrl_desc[r->controller].N_DOWNLOAD(
						ctrl_desc[r->controller].unit,
						r->numprotos, prots2);

download_done:
			if(prots2)
			{
				for(i = 0; i < r->numprotos; i++)
				{
					if(prots2[i].microcode)
					{
						free(prots2[i].microcode, M_DEVBUF);
					}
				}
				free(prots2, M_DEVBUF);
			}

			if(prots)
			{
				free(prots, M_DEVBUF);
			}
			break;
		}

		/* Diagnostic request */

		case I4B_ACTIVE_DIAGNOSTIC:
		{
			struct isdn_diagnostic_request req, *r =
				(struct isdn_diagnostic_request*)data;

			req.in_param = req.out_param = NULL;
			if (r->controller < 0 || r->controller >= nctrl)
			{
				error = ENODEV;
				goto diag_done;
			}

			if(!ctrl_desc[r->controller].N_DIAGNOSTICS)
			{
				error = ENODEV;
				goto diag_done;
			}

			memcpy(&req, r, sizeof(req));

			if(req.in_param_len)
			{
				req.in_param = malloc(r->in_param_len, M_DEVBUF, M_WAITOK);

				if(!req.in_param)
				{
					error = ENOMEM;
					goto diag_done;
				}
				error = copyin(r->in_param, req.in_param, req.in_param_len);
				if (error)
					goto diag_done;
			}

			if(req.out_param_len)
			{
				req.out_param = malloc(r->out_param_len, M_DEVBUF, M_WAITOK);

				if(!req.out_param)
				{
					error = ENOMEM;
					goto diag_done;
				}
			}
			
			error = ctrl_desc[r->controller].N_DIAGNOSTICS(r->controller, &req);

			if(!error && req.out_param_len)
				error = copyout(req.out_param, r->out_param, req.out_param_len);

diag_done:
			if(req.in_param)
				free(req.in_param, M_DEVBUF);
				
			if(req.out_param)
				free(req.out_param, M_DEVBUF);

			break;
		}

		/* default */
		
		default:
			error = ENOTTY;
			break;
	}
	
	return(error);
}
Example #11
0
/*---------------------------------------------------------------------------*
 *	i4bioctl - device driver ioctl routine
 *---------------------------------------------------------------------------*/
PDEVSTATIC int
isdnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct isdn_l3_driver *d;
	call_desc_t *cd;
	int error = 0;
	
	if(minor(dev))
		return(ENODEV);

	switch(cmd)
	{
		/* cdid request, reserve cd and return cdid */

		case I4B_CDID_REQ:
		{
			msg_cdid_req_t *mir;
			mir = (msg_cdid_req_t *)data;
			cd = reserve_cd();
			mir->cdid = cd->cdid;
			break;
		}
		
		/* connect request, dial out to remote */
		
		case I4B_CONNECT_REQ:
		{
			msg_connect_req_t *mcr;
			mcr = (msg_connect_req_t *)data;	/* setup ptr */

			if((cd = cd_by_cdid(mcr->cdid)) == NULL)/* get cd */
			{
				NDBGL4(L4_ERR, "I4B_CONNECT_REQ ioctl, cdid not found!"); 
				error = EINVAL;
				break;
			}
			cd->bri = -1;
			cd->l3drv = NULL;

			d = isdn_find_l3_by_bri(mcr->controller);
			if (d == NULL) {
				error = EINVAL;
				break;
			}

			/* prevent dialling on leased lines */
			if(d->protocol == PROTOCOL_D64S)
			{
				SET_CAUSE_TYPE(cd->cause_in, CAUSET_I4B);
				SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_LLDIAL);
				i4b_l4_disconnect_ind(cd);
				freecd_by_cd(cd);
				break;
			}

			cd->bri = mcr->controller;	/* fill cd */
			cd->l3drv = d;
			cd->bprot = mcr->bprot;
			cd->bchan_driver_index = mcr->driver;
			cd->bchan_driver_unit = mcr->driver_unit;
			cd->cr = get_rand_cr(cd->bri);

			cd->shorthold_data.shorthold_algorithm = mcr->shorthold_data.shorthold_algorithm;
			cd->shorthold_data.unitlen_time  = mcr->shorthold_data.unitlen_time;
			cd->shorthold_data.idle_time     = mcr->shorthold_data.idle_time;
			cd->shorthold_data.earlyhup_time = mcr->shorthold_data.earlyhup_time;

			cd->last_aocd_time = 0;
			if(mcr->unitlen_method == ULEN_METHOD_DYNAMIC)
				cd->aocd_flag = 1;
			else
				cd->aocd_flag = 0;
				
			cd->cunits = 0;

			cd->max_idle_time = 0;	/* this is outgoing */

			cd->dir = DIR_OUTGOING;
			
			NDBGL4(L4_TIMO, "I4B_CONNECT_REQ times, algorithm=%ld unitlen=%ld idle=%ld earlyhup=%ld",
					(long)cd->shorthold_data.shorthold_algorithm, (long)cd->shorthold_data.unitlen_time,
					(long)cd->shorthold_data.idle_time, (long)cd->shorthold_data.earlyhup_time);

			strcpy(cd->dst_telno, mcr->dst_telno);
			strcpy(cd->src_telno, mcr->src_telno);
			cd->display[0] = '\0';

			SET_CAUSE_TYPE(cd->cause_in, CAUSET_I4B);
			SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NORMAL);
			
			switch(mcr->channel)
			{
				case CHAN_B1:
				case CHAN_B2:
					if(d->bch_state[mcr->channel] != BCH_ST_FREE)
						SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NOCHAN);
					break;

				case CHAN_ANY:
					if((d->bch_state[CHAN_B1] != BCH_ST_FREE) &&
					   (d->bch_state[CHAN_B2] != BCH_ST_FREE))
						SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NOCHAN);
					break;

				default:
					SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NOCHAN);
					break;
			}

			cd->channelid = mcr->channel;

			cd->isdntxdelay = mcr->txdelay;
			
			if((GET_CAUSE_VAL(cd->cause_in)) != CAUSE_I4B_NORMAL)
			{
				i4b_l4_disconnect_ind(cd);
				freecd_by_cd(cd);
			}
			else
			{
				d->l3driver->N_CONNECT_REQUEST(cd);
			}
			break;
		}
		
		/* connect response, accept/reject/ignore incoming call */
		
		case I4B_CONNECT_RESP:
		{
			msg_connect_resp_t *mcrsp;
			
			mcrsp = (msg_connect_resp_t *)data;

			if((cd = cd_by_cdid(mcrsp->cdid)) == NULL)/* get cd */
			{
				NDBGL4(L4_ERR, "I4B_CONNECT_RESP ioctl, cdid not found!"); 
				error = EINVAL;
				break;
			}

			T400_stop(cd);

			cd->bchan_driver_index = mcrsp->driver;
			cd->bchan_driver_unit = mcrsp->driver_unit;
			cd->max_idle_time = mcrsp->max_idle_time;

			cd->shorthold_data.shorthold_algorithm = SHA_FIXU;
			cd->shorthold_data.unitlen_time = 0;	/* this is incoming */
			cd->shorthold_data.idle_time = 0;
			cd->shorthold_data.earlyhup_time = 0;

			cd->isdntxdelay = mcrsp->txdelay;			
			
			NDBGL4(L4_TIMO, "I4B_CONNECT_RESP max_idle_time set to %ld seconds", (long)cd->max_idle_time);

			d = isdn_find_l3_by_bri(cd->bri);
			if (d == NULL) {
				error = EINVAL;
				break;
			}
			d->l3driver->N_CONNECT_RESPONSE(cd, mcrsp->response, mcrsp->cause);
			break;
		}
		
		/* disconnect request, actively terminate connection */
		
		case I4B_DISCONNECT_REQ:
		{
			msg_discon_req_t *mdr;
			
			mdr = (msg_discon_req_t *)data;

			if((cd = cd_by_cdid(mdr->cdid)) == NULL)/* get cd */
			{
				NDBGL4(L4_ERR, "I4B_DISCONNECT_REQ ioctl, cdid %d not found!", mdr->cdid); 
				error = EINVAL;
				break;
			}

			/* preset causes with our cause */
			cd->cause_in = cd->cause_out = mdr->cause;

			d = isdn_find_l3_by_bri(cd->bri);
			if (d == NULL) {
				error = EINVAL;
				break;
			}

			d->l3driver->N_DISCONNECT_REQUEST(cd, mdr->cause);
			break;
		}
		
		/* controller info request */

		case I4B_CTRL_INFO_REQ:
		{
			msg_ctrl_info_req_t *mcir;
			struct isdn_l3_driver *d;
			int bri;
			
			mcir = (msg_ctrl_info_req_t *)data;
			bri = mcir->controller;
			memset(mcir, 0, sizeof(msg_ctrl_info_req_t));
			mcir->controller = bri;
			mcir->ncontroller = isdn_count_bri(&mcir->maxbri);
			d = isdn_find_l3_by_bri(bri);
			if (d != NULL) {
				mcir->tei = d->tei;
				strncpy(mcir->devname, d->devname, sizeof(mcir->devname)-1);
				strncpy(mcir->cardname, d->card_name, sizeof(mcir->cardname)-1);
			} else {
				error = ENODEV;
			}
			break;
		}
		
		/* dial response */
		
		case I4B_DIALOUT_RESP:
		{
			const struct isdn_l4_driver_functions *drv;
			msg_dialout_resp_t *mdrsp;
			void * l4_softc;
			
			mdrsp = (msg_dialout_resp_t *)data;
			drv = isdn_l4_get_driver(mdrsp->driver, mdrsp->driver_unit);

			if(drv != NULL)	{
				l4_softc = (*drv->get_softc)(mdrsp->driver_unit);
				(*drv->dial_response)(l4_softc, mdrsp->stat, mdrsp->cause);
			}
			break;
		}
		
		/* update timeout value */
		
		case I4B_TIMEOUT_UPD:
		{
			msg_timeout_upd_t *mtu;
			int x;
			
			mtu = (msg_timeout_upd_t *)data;

			NDBGL4(L4_TIMO, "I4B_TIMEOUT_UPD ioctl, alg %d, unit %d, idle %d, early %d!",
					mtu->shorthold_data.shorthold_algorithm, mtu->shorthold_data.unitlen_time,
					mtu->shorthold_data.idle_time, mtu->shorthold_data.earlyhup_time); 

			if((cd = cd_by_cdid(mtu->cdid)) == NULL)/* get cd */
			{
				NDBGL4(L4_ERR, "I4B_TIMEOUT_UPD ioctl, cdid not found!"); 
				error = EINVAL;
				break;
			}

			switch( mtu->shorthold_data.shorthold_algorithm )
			{
				case SHA_FIXU:
					/*
					 * For this algorithm unitlen_time,
					 * idle_time and earlyhup_time are used.
					 */

					if(!(mtu->shorthold_data.unitlen_time >= 0 &&
					     mtu->shorthold_data.idle_time >= 0    &&
					     mtu->shorthold_data.earlyhup_time >= 0))
					{
						NDBGL4(L4_ERR, "I4B_TIMEOUT_UPD ioctl, invalid args for fix unit algorithm!"); 
						error = EINVAL;
					}
					break;
	
				case SHA_VARU:
					/*
					 * For this algorithm unitlen_time and
					 * idle_time are used. both must be
					 * positive integers. earlyhup_time is
					 * not used and must be 0.
					 */

					if(!(mtu->shorthold_data.unitlen_time > 0 &&
					     mtu->shorthold_data.idle_time >= 0   &&
					     mtu->shorthold_data.earlyhup_time == 0))
					{
						NDBGL4(L4_ERR, "I4B_TIMEOUT_UPD ioctl, invalid args for var unit algorithm!"); 
						error = EINVAL;
					}
					break;
	
				default:
					NDBGL4(L4_ERR, "I4B_TIMEOUT_UPD ioctl, invalid algorithm!"); 
					error = EINVAL;
					break;
			}

			/*
			 * any error set above requires us to break
			 * out of the outer switch
			 */
			if(error != 0)
				break;

			x = splnet();
			cd->shorthold_data.shorthold_algorithm = mtu->shorthold_data.shorthold_algorithm;
			cd->shorthold_data.unitlen_time = mtu->shorthold_data.unitlen_time;
			cd->shorthold_data.idle_time = mtu->shorthold_data.idle_time;
			cd->shorthold_data.earlyhup_time = mtu->shorthold_data.earlyhup_time;
			splx(x);
			break;
		}
			
		/* soft enable/disable interface */
		
		case I4B_UPDOWN_IND:
		{
			msg_updown_ind_t *mui;
			const struct isdn_l4_driver_functions *drv;
			void * l4_softc;

			mui = (msg_updown_ind_t *)data;
			drv = isdn_l4_get_driver(mui->driver, mui->driver_unit);

			if (drv)
			{
				l4_softc = drv->get_softc(mui->driver_unit);
				(*drv->updown_ind)(l4_softc, mui->updown);
			}
			break;
		}
		
		/* send ALERT request */
		
		case I4B_ALERT_REQ:
		{
			msg_alert_req_t *mar;
			
			mar = (msg_alert_req_t *)data;

			if((cd = cd_by_cdid(mar->cdid)) == NULL)
			{
				NDBGL4(L4_ERR, "I4B_ALERT_REQ ioctl, cdid not found!"); 
				error = EINVAL;
				break;
			}

			T400_stop(cd);
			
			d = cd->l3drv;
			if (d == NULL) {
				error = EINVAL;
				break;
			}
			d->l3driver->N_ALERT_REQUEST(cd);

			break;
		}

		/* version/release number request */
		
		case I4B_VR_REQ:
                {
			msg_vr_req_t *mvr;

			mvr = (msg_vr_req_t *)data;

			mvr->version = VERSION;
			mvr->release = REL;
			mvr->step = STEP;			
			break;
		}

		/* set D-channel protocol for a controller */
		
		case I4B_PROT_IND:
		{
			msg_prot_ind_t *mpi;
			
			mpi = (msg_prot_ind_t *)data;

			d = isdn_find_l3_by_bri(mpi->controller);
			if (d == NULL) {
				error = EINVAL;
				break;
			}
			d->protocol = mpi->protocol;
			
			break;
		}

		case I4B_L4DRIVER_LOOKUP:
		{
			msg_l4driver_lookup_t *lookup = (msg_l4driver_lookup_t*)data;
			lookup->name[L4DRIVER_NAME_SIZ-1] = 0;
			lookup->driver_id = isdn_l4_find_driverid(lookup->name);
			if (lookup->driver_id < 0)
				error = ENXIO;
			break;
		}
		
		/* Download request */
		case I4B_CTRL_DOWNLOAD:
		{
			struct isdn_dr_prot *prots = NULL, *prots2 = NULL;
			struct isdn_download_request *r =
				(struct isdn_download_request*)data;
			int i;

			d = isdn_find_l3_by_bri(r->controller);
			if (d == NULL)
			{
				error = ENODEV;
				goto download_done;
			}

			if(d->l3driver->N_DOWNLOAD == NULL)
			{
				error = ENODEV;
				goto download_done;
			}

			prots = malloc(r->numprotos * sizeof(struct isdn_dr_prot),
					M_DEVBUF, M_WAITOK);

			prots2 = malloc(r->numprotos * sizeof(struct isdn_dr_prot),
					M_DEVBUF, M_WAITOK);

			if(!prots || !prots2)
			{
				error = ENOMEM;
				goto download_done;
			}

			copyin(r->protocols, prots, r->numprotos * sizeof(struct isdn_dr_prot));

			for(i = 0; i < r->numprotos; i++)
			{
				prots2[i].microcode = malloc(prots[i].bytecount, M_DEVBUF, M_WAITOK);
				copyin(prots[i].microcode, prots2[i].microcode, prots[i].bytecount);
				prots2[i].bytecount = prots[i].bytecount; 
			}

			error = d->l3driver->N_DOWNLOAD(
						d->l1_token,
						r->numprotos, prots2);

download_done:
			if(prots2)
			{
				for(i = 0; i < r->numprotos; i++)
				{
					if(prots2[i].microcode)
					{
						free(prots2[i].microcode, M_DEVBUF);
					}
				}
				free(prots2, M_DEVBUF);
			}

			if(prots)
			{
				free(prots, M_DEVBUF);
			}
			break;
		}

		/* Diagnostic request */

		case I4B_ACTIVE_DIAGNOSTIC:
		{
			struct isdn_diagnostic_request req, *r =
				(struct isdn_diagnostic_request*)data;

			req.in_param = req.out_param = NULL;
			d = isdn_find_l3_by_bri(r->controller);
			if (d == NULL)
			{
				error = ENODEV;
				goto diag_done;
			}

			if (d->l3driver->N_DIAGNOSTICS == NULL)
			{
				error = ENODEV;
				goto diag_done;
			}

			memcpy(&req, r, sizeof(req));

			if(req.in_param_len)
			{
				/* XXX arbitrary limit */
				if (req.in_param_len > I4B_ACTIVE_DIAGNOSTIC_MAXPARAMLEN) {
					error = EINVAL;
					goto diag_done;
				}

				req.in_param = malloc(r->in_param_len, M_DEVBUF, M_WAITOK);

				if(!req.in_param)
				{
					error = ENOMEM;
					goto diag_done;
				}
				error = copyin(r->in_param, req.in_param, req.in_param_len);
				if (error)
					goto diag_done;
			}

			if(req.out_param_len)
			{
				req.out_param = malloc(r->out_param_len, M_DEVBUF, M_WAITOK);

				if(!req.out_param)
				{
					error = ENOMEM;
					goto diag_done;
				}
			}
			
			error = d->l3driver->N_DIAGNOSTICS(d->l1_token, &req);

			if(!error && req.out_param_len)
				error = copyout(req.out_param, r->out_param, req.out_param_len);

diag_done:
			if(req.in_param)
				free(req.in_param, M_DEVBUF);
				
			if(req.out_param)
				free(req.out_param, M_DEVBUF);

			break;
		}

		/* default */
		
		default:
			error = ENOTTY;
			break;
	}
	
	return(error);
}