Exemple #1
0
/*---------------------------------------------------------------------------*
 *	variable unit algorithm B channel idle check timeout function
 *---------------------------------------------------------------------------*/
static void
i4b_idle_check_var_unit(call_desc_t *cd)
{
	switch(cd->idletime_state)
	{

	/* see if there has been any activity within the last idle_time seconds */
	case IST_CHECK:
		if( i4b_get_idletime(cd) > (SECOND - cd->shorthold_data.idle_time))
		{	/* activity detected */
			/* check again in one second */
			callout_reset(&cd->idle_timeout, hz,
					(void *)i4b_idle_check, cd);
			cd->timeout_active = 1;
			cd->idletime_state = IST_CHECK;
			NDBGL4(L4_TIMO, "%ld: outgoing-call, var idle timeout - activity at %ld, continuing", (long)SECOND, (long)i4b_get_idletime(cd));
		}
		else
		{	/* no activity, hangup */
			NDBGL4(L4_TIMO, "%ld: outgoing-call, var idle timeout - last activity at %ld", (long)SECOND, (long)i4b_get_idletime(cd));
			(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
			i4b_l4_idle_timeout_ind(cd);
			cd->idletime_state = IST_IDLE;
		}
		break;

	default:
		NDBGL4(L4_ERR, "outgoing-call: var idle timeout invalid idletime_state value!");
		cd->idletime_state = IST_IDLE;
		break;
	}
}
Exemple #2
0
static uint32_t
iavc_tx_capimsg(iavc_softc_t *sc, struct mbuf *m)
{
    uint32_t txlen = 0;
    u_int8_t *dmabuf;

    if (sc->sc_dma) {
	/* Copy message to DMA buffer. */

	if (m->m_next)
	    dmabuf = amcc_put_byte(&sc->sc_sendbuf[0], SEND_DATA_B3_REQ);
	else
	    dmabuf = amcc_put_byte(&sc->sc_sendbuf[0], SEND_MESSAGE);

	dmabuf = amcc_put_word(dmabuf, m->m_len);
	memcpy(dmabuf, m->m_data, m->m_len);
	dmabuf += m->m_len;
	txlen = 5 + m->m_len;

	if (m->m_next) {
	    dmabuf = amcc_put_word(dmabuf, m->m_next->m_len);
	    memcpy(dmabuf, m->m_next->m_data, m->m_next->m_len);
	    txlen += 4 + m->m_next->m_len;
	}

    } else {
	/* Use PIO. */

	if (m->m_next) {
	    iavc_put_byte(sc, SEND_DATA_B3_REQ);
	    NDBGL4(L4_IAVCDBG, "iavc%d: tx SDB3R msg, len = %d",
	      sc->sc_unit, m->m_len);
	} else {
	    iavc_put_byte(sc, SEND_MESSAGE);
	    NDBGL4(L4_IAVCDBG, "iavc%d: tx SM msg, len = %d",
	      sc->sc_unit, m->m_len);
	}
#if 0
    {
	u_int8_t *p = mtod(m, u_int8_t*);
	int len;
	for (len = 0; len < m->m_len; len++) {
	    printf(" %02x", *p++);
	    if (len && (len % 16) == 0)
		printf("\n");
	}
	if (len % 16)
	    printf("\n");
    }
#endif

	iavc_put_slice(sc, m->m_data, m->m_len);

	if (m->m_next)
	    iavc_put_slice(sc, m->m_next->m_data, m->m_next->m_len);
    }

    return txlen;
}
Exemple #3
0
/*---------------------------------------------------------------------------*
 *	i4bputqueue_hipri - put message into front of queue to userland
 *---------------------------------------------------------------------------*/
void
i4bputqueue_hipri(struct mbuf *m)
{
    if(!openflag)
    {
        i4b_Dfreembuf(m);
        return;
    }

    crit_enter();

    if(IF_QFULL(&i4b_rdqueue))
    {
        struct mbuf *m1;
        IF_DEQUEUE(&i4b_rdqueue, m1);
        i4b_Dfreembuf(m1);
        NDBGL4(L4_ERR, "ERROR, queue full, removing entry!");
    }

    IF_PREPEND(&i4b_rdqueue, m);

    crit_exit();

    if(readflag)
    {
        readflag = 0;
        wakeup((caddr_t) &i4b_rdqueue);
    }

    KNOTE(&kq_rd_info.ki_note, 0);
}
Exemple #4
0
/*---------------------------------------------------------------------------*
 *	send MSG_DISCONNECT_IND message to userland
 *---------------------------------------------------------------------------*/
void
i4b_l4_disconnect_ind(call_desc_t *cd)
{
	struct mbuf *m;

	if(cd->timeout_active)
		callout_stop(&cd->idle_timeout);

	if(cd->dlt != NULL)
	{
		(*cd->dlt->line_disconnected)(cd->driver_unit, (void *)cd);
		i4b_unlink_bchandrvr(cd);
	}

	if((cd->channelid >= 0) && (cd->channelid < ctrl_desc[cd->controller].nbch))
	{
		ctrl_desc[cd->controller].bch_state[cd->channelid] = BCH_ST_FREE;
	}
	else
	{
		/* no error, might be hunting call for callback */
		NDBGL4(L4_MSG, "channel free not valid but %d!", cd->channelid);
	}
	
	if((m = i4b_Dgetmbuf(sizeof(msg_disconnect_ind_t))) != NULL)
	{
		msg_disconnect_ind_t *mp = (msg_disconnect_ind_t *)m->m_data;

		mp->header.type = MSG_DISCONNECT_IND;
		mp->header.cdid = cd->cdid;
		mp->cause = cd->cause_in;

		i4bputqueue(m);
	}
}
Exemple #5
0
/*---------------------------------------------------------------------------*
 *	send MSG_CONNECT_ACTIVE_IND message to userland
 *---------------------------------------------------------------------------*/
void
i4b_l4_connect_active_ind(call_desc_t *cd)
{
	struct mbuf *m;

	crit_enter();

	cd->last_active_time = cd->connect_time = SECOND;

	NDBGL4(L4_TIMO, "last_active/connect_time=%ld", (long)cd->connect_time);
	
	i4b_link_bchandrvr(cd);

	(*cd->dlt->line_connected)(cd->driver_unit, (void *)cd);

	i4b_l4_setup_timeout(cd);
	
	crit_exit();
	
	if((m = i4b_Dgetmbuf(sizeof(msg_connect_active_ind_t))) != NULL)
	{
		msg_connect_active_ind_t *mp = (msg_connect_active_ind_t *)m->m_data;

		mp->header.type = MSG_CONNECT_ACTIVE_IND;
		mp->header.cdid = cd->cdid;
		mp->controller = cd->controller;
		mp->channel = cd->channelid;
		if(cd->datetime[0] != '\0')
			strcpy(mp->datetime, cd->datetime);
		else
			mp->datetime[0] = '\0';
		i4bputqueue(m);
	}
}
/*---------------------------------------------------------------------------*
 *	this routine is called from L4 handler at connect time
 *---------------------------------------------------------------------------*/
static void
rbch_connect(void *softc, void *cdp)
{
	call_desc_t *cd = (call_desc_t *)cdp;
	struct rbch_softc *sc = softc;

	sc->sc_bprot = cd->bprot;

#if I4BRBCHACCT
	if(sc->sc_bprot == BPROT_RHDLC)
	{
		sc->sc_iinb = 0;
		sc->sc_ioutb = 0;
		sc->sc_linb = 0;
		sc->sc_loutb = 0;

		START_TIMER(sc->sc_callout, rbch_timeout, sc, I4BRBCHACCTINTVL*hz);
	}
#endif
	if(!(sc->sc_devstate & ST_CONNECTED))
	{
		NDBGL4(L4_RBCHDBG, "B channel %d at ISDN %d, wakeup",
			cd->channelid, cd->isdnif);
		sc->sc_devstate |= ST_CONNECTED;
		sc->sc_cd = cdp;
		wakeup((void *)sc);
		selnotify(&sc->selp, 0, 0);
	}
}
/*---------------------------------------------------------------------------*
 *	this routine is called from the HSCX interrupt handler
 *	when the last frame has been sent out and there is no
 *	further frame (mbuf) in the tx queue.
 *---------------------------------------------------------------------------*/
static void
rbch_tx_queue_empty(void *softc)
{
	struct rbch_softc *sc = softc;

	if(sc->sc_devstate & ST_WRWAITEMPTY)
	{
		NDBGL4(L4_RBCHDBG, "(minor=%d): wakeup", sc->sc_unit);
		sc->sc_devstate &= ~ST_WRWAITEMPTY;
		wakeup((void *) &sc->sc_ilt->tx_queue);
	}
	else
	{
		NDBGL4(L4_RBCHDBG, "(minor=%d) NO wakeup", sc->sc_unit);
	}
	selnotify(&sc->selp, 0, 0);
}
Exemple #8
0
/*---------------------------------------------------------------------------*
 *	fixed unit algorithm B channel idle check timeout setup
 *---------------------------------------------------------------------------*/
static void
i4b_l4_setup_timeout_fix_unit(call_desc_t *cd)
{
	/* outgoing call */
	
	if((cd->shorthold_data.idle_time > 0) && (cd->shorthold_data.unitlen_time == 0))
	{
		/* outgoing call: simple max idletime check */
		
		callout_reset(&cd->idle_timeout, hz / 2,
				(void *)i4b_idle_check, cd);
		cd->timeout_active = 1;
		NDBGL4(L4_TIMO, "%ld: outgoing-call, setup idle_time to %ld",
			(long)SECOND, (long)cd->shorthold_data.idle_time);
	}
	else if((cd->shorthold_data.unitlen_time > 0) && (cd->shorthold_data.unitlen_time > (cd->shorthold_data.idle_time + cd->shorthold_data.earlyhup_time)))
	{
		/* outgoing call: full shorthold mode check */
		
		callout_reset(&cd->idle_timeout, 
			hz * (cd->shorthold_data.unitlen_time - 
			  (cd->shorthold_data.idle_time + 
			  cd->shorthold_data.earlyhup_time)),
			(void *)i4b_idle_check, cd);
		cd->timeout_active = 1;
		cd->idletime_state = IST_NONCHK;
		NDBGL4(L4_TIMO, "%ld: outgoing-call, start %ld sec nocheck window", 
			(long)SECOND, (long)(cd->shorthold_data.unitlen_time - (cd->shorthold_data.idle_time + cd->shorthold_data.earlyhup_time)));

		if(cd->aocd_flag == 0)
		{
			cd->units_type = CHARGE_CALC;
			cd->cunits++;
			i4b_l4_charging_ind(cd);
		}
	}
	else
	{
		/* parms somehow got wrong .. */
		
		NDBGL4(L4_ERR, "%ld: ERROR: idletime[%ld]+earlyhup[%ld] > unitlength[%ld]!",
			(long)SECOND, (long)cd->shorthold_data.idle_time, (long)cd->shorthold_data.earlyhup_time, (long)cd->shorthold_data.unitlen_time);
	}
}
Exemple #9
0
static void iavc_handle_rx(iavc_softc_t *sc)
{
    u_int8_t *dmabuf = 0, cmd;

    if (sc->sc_dma) {
	dmabuf = amcc_get_byte(&sc->sc_recvbuf[0], &cmd);
    } else {
	cmd = iavc_get_byte(sc);
    }

    NDBGL4(L4_IAVCDBG, "iavc%d: command = 0x%02x", sc->sc_unit, cmd);

    switch (cmd) {
    case RECEIVE_DATA_B3_IND:
	iavc_receive(sc, dmabuf, 1);
	break;

    case RECEIVE_MESSAGE:
	iavc_receive(sc, dmabuf, 0);
	break;

    case RECEIVE_NEW_NCCI:
	iavc_receive_new_ncci(sc, dmabuf);
	break;

    case RECEIVE_FREE_NCCI:
	iavc_receive_free_ncci(sc, dmabuf);
	break;

    case RECEIVE_START:
	iavc_receive_start(sc);
	break;

    case RECEIVE_STOP:
	iavc_receive_stop(sc);
	break;

    case RECEIVE_INIT:
	iavc_receive_init(sc, dmabuf);
	break;

    case RECEIVE_TASK_READY:
	iavc_receive_task_ready(sc, dmabuf);
	break;

    case RECEIVE_DEBUGMSG:
	iavc_receive_debugmsg(sc, dmabuf);
	break;

    default:
	aprint_error_dev(&sc->sc_dev, "unknown msg %02x\n", cmd);
    }
}
/*---------------------------------------------------------------------------*
 *	this routine is called from L4 handler at disconnect time
 *---------------------------------------------------------------------------*/
static void
rbch_disconnect(void *softc, void *cdp)
{
	call_desc_t *cd = cdp;
	struct rbch_softc *sc = softc;

	int s;

        if(cd != sc->sc_cd)
	{
		NDBGL4(L4_RBCHDBG, "B channel %d at ISDN %d not active",
		    cd->channelid, cd->isdnif);
		return;
	}

	s = splnet();

	NDBGL4(L4_RBCHDBG, "B channel %d at ISDN %d disconnect",
	    cd->channelid, cd->isdnif);

	sc->sc_devstate &= ~ST_CONNECTED;

#if I4BRBCHACCT
	if (sc->sc_cd)
		i4b_l4_accounting(sc->sc_cd->cdid, ACCT_FINAL,
		    sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_ioutb, sc->sc_iinb);

	STOP_TIMER(sc->sc_callout, rbch_timeout, sc);
#endif

	sc->sc_cd = NULL;
	if (sc->sc_devstate & ST_RDWAITDATA)
		wakeup(&sc->sc_ilt->rx_queue);
	if (sc->sc_devstate & ST_WRWAITEMPTY)
		wakeup(&sc->sc_ilt->tx_queue);

	splx(s);

	selnotify(&sc->selp, 0, 0);
}
/*---------------------------------------------------------------------------*
 *	device driver select
 *---------------------------------------------------------------------------*/
PDEVSTATIC int
isdnbchanselect(dev_t dev, int rw, struct lwp *l)
{
	int unit = minor(dev);
	struct rbch_softc *sc = &rbch_softc[unit];
        int s;

	s = splhigh();

	if(!(sc->sc_devstate & ST_ISOPEN))
	{
		splx(s);
		NDBGL4(L4_RBCHDBG, "(minor=%d) not open anymore", unit);
		return(1);
	}

	if(sc->sc_devstate & ST_CONNECTED)
	{
		struct ifqueue *iqp;

		switch(rw)
		{
			case FREAD:
				if(sc->sc_bprot == BPROT_RHDLC)
					iqp = &sc->sc_hdlcq;
				else
					iqp = isdn_linktab[unit]->rx_queue;

				if(!IF_QEMPTY(iqp))
				{
					splx(s);
					return(1);
				}
				break;

			case FWRITE:
				if(!IF_QFULL(isdn_linktab[unit]->rx_queue))
				{
					splx(s);
					return(1);
				}
				break;

			default:
				splx(s);
				return 0;
		}
	}
	selrecord(l, &sc->selp);
	splx(s);
	return(0);
}
/*---------------------------------------------------------------------------*
 *	this routine is called from the HSCX interrupt handler
 *	when a new frame (mbuf) has been received and is to be put on
 *	the rx queue.
 *---------------------------------------------------------------------------*/
static void
rbch_rx_data_rdy(void *softc)
{
	struct rbch_softc *sc = softc;

	if(sc->sc_bprot == BPROT_RHDLC)
	{
		register struct mbuf *m;

		if((m = *sc->sc_ilt->rx_mbuf) == NULL)
			return;

		m->m_pkthdr.len = m->m_len;

		if(IF_QFULL(&sc->sc_hdlcq))
		{
			NDBGL4(L4_RBCHDBG, "(minor=%d) hdlc rx queue full!", sc->sc_unit);
			m_freem(m);
		}
		else
		{
			IF_ENQUEUE(&sc->sc_hdlcq, m);
		}
	}

	if(sc->sc_devstate & ST_RDWAITDATA)
	{
		NDBGL4(L4_RBCHDBG, "(minor=%d) wakeup", sc->sc_unit);
		sc->sc_devstate &= ~ST_RDWAITDATA;
		wakeup((void *) &sc->sc_ilt->rx_queue);
	}
	else
	{
		NDBGL4(L4_RBCHDBG, "(minor=%d) NO wakeup", sc->sc_unit);
	}
	selnotify(&sc->selp, 0, 0);
}
Exemple #13
0
/*---------------------------------------------------------------------------*
 *	B channel idle check timeout setup
 *---------------------------------------------------------------------------*/ 
static void
i4b_l4_setup_timeout(call_desc_t *cd)
{
	NDBGL4(L4_TIMO, "%ld: direction %d, shorthold algorithm %d",
		(long)SECOND, cd->dir, cd->shorthold_data.shorthold_algorithm);
	
	cd->timeout_active = 0;
	cd->idletime_state = IST_IDLE;
	
	if((cd->dir == DIR_INCOMING) && (cd->max_idle_time > 0))
	{
		/* incoming call: simple max idletime check */
	
		callout_reset(&cd->idle_timeout, hz / 2,
				(void *)i4b_idle_check, cd);
		cd->timeout_active = 1;
		NDBGL4(L4_TIMO, "%ld: incoming-call, setup max_idle_time to %ld", (long)SECOND, (long)cd->max_idle_time);
	}
	else if((cd->dir == DIR_OUTGOING) && (cd->shorthold_data.idle_time > 0))
	{
		switch( cd->shorthold_data.shorthold_algorithm )
		{
			default:	/* fall into the old fix algorithm */
			case SHA_FIXU:
				i4b_l4_setup_timeout_fix_unit( cd );
				break;
				
			case SHA_VARU:
				i4b_l4_setup_timeout_var_unit( cd );
				break;
		}
	}
	else
	{
		NDBGL4(L4_TIMO, "no idle_timeout configured");
	}
}
/*---------------------------------------------------------------------------*
 *	close rbch device
 *---------------------------------------------------------------------------*/
PDEVSTATIC int
isdnbchanclose(dev_t dev, int flag, int fmt,
	struct lwp *l)
{
	int unit = minor(dev);
	struct rbch_softc *sc = &rbch_softc[unit];

	if(sc->sc_devstate & ST_CONNECTED)
		i4b_l4_drvrdisc(sc->sc_cd->cdid);

	sc->sc_devstate &= ~ST_ISOPEN;

	rbch_clrq(sc);

	NDBGL4(L4_RBCHDBG, "channel %d, closed", unit);

	return(0);
}
Exemple #15
0
/*---------------------------------------------------------------------------*
 *	variable unit algorithm B channel idle check timeout setup
 *---------------------------------------------------------------------------*/
static void
i4b_l4_setup_timeout_var_unit(call_desc_t *cd)
{
	/* outgoing call: variable unit idletime check */
		
	/*
	 * start checking for an idle connect one second before the end of the unit.
	 * The one second takes into account of rounding due to the driver only
	 * using the seconds and not the uSeconds of the current time
	 */
	cd->idletime_state = IST_CHECK;	/* move directly to the checking state */

	callout_reset(&cd->idle_timeout, 
			hz * (cd->shorthold_data.unitlen_time - 1),
			(void *)i4b_idle_check, cd);
	cd->timeout_active = 1;
	NDBGL4(L4_TIMO, "%ld: outgoing-call, var idle time - setup to %ld",
		(long)SECOND, (long)cd->shorthold_data.unitlen_time);
}
/*---------------------------------------------------------------------------*
 *	open rbch device
 *---------------------------------------------------------------------------*/
PDEVSTATIC int
isdnbchanopen(dev_t dev, int flag, int fmt,
	struct lwp *l)
{
	int unit = minor(dev);

	if(unit >= NISDNBCHAN)
		return(ENXIO);

	if(rbch_softc[unit].sc_devstate & ST_ISOPEN)
		return(EBUSY);

#if 0
	rbch_clrq(unit);
#endif

	rbch_softc[unit].sc_devstate |= ST_ISOPEN;

	NDBGL4(L4_RBCHDBG, "unit %d, open", unit);

	return(0);
}
Exemple #17
0
static int iavc_receive_start(iavc_softc_t *sc, u_int8_t *dmabuf)
{
    struct mbuf *m = i4b_Dgetmbuf(3);
    u_int8_t *p;

    if (sc->sc_blocked && sc->sc_state == IAVC_UP)
	printf("iavc%d: receive_start\n", sc->sc_unit);

    if (!m) {
	printf("iavc%d: can't get memory\n", sc->sc_unit);
	return (ENOMEM);
    }

    /*
     * byte  0x73 = SEND_POLLACK
     */

    p = amcc_put_byte(mtod(m, u_int8_t*), 0);
    p = amcc_put_byte(p, 0);
    p = amcc_put_byte(p, SEND_POLLACK);
    
    _IF_PREPEND(&sc->sc_txq, m);

    NDBGL4(L4_IAVCDBG, "iavc%d: blocked = %d, state = %d",
		sc->sc_unit, sc->sc_blocked, sc->sc_state);

    sc->sc_blocked = FALSE;
    iavc_start_tx(sc);
    
    /* If this was our first START, register our readiness */

    if (sc->sc_state != IAVC_UP) {
	sc->sc_state = IAVC_UP;
	capi_ll_control(&sc->sc_capi, CAPI_CTRL_READY, TRUE);
    }

    return 0;
}
Exemple #18
0
/*---------------------------------------------------------------------------*
 *	i4bputqueue_hipri - put message into front of queue to userland
 *---------------------------------------------------------------------------*/
void
i4bputqueue_hipri(struct mbuf *m)
{
	int x;
	
	if(!openflag)
	{
		i4b_Dfreembuf(m);
		return;
	}

	x = splnet();
	
	if(IF_QFULL(&i4b_rdqueue))
	{
		struct mbuf *m1;
		IF_DEQUEUE(&i4b_rdqueue, m1);
		i4b_Dfreembuf(m1);
		NDBGL4(L4_ERR, "ERROR, queue full, removing entry!");
	}

	IF_PREPEND(&i4b_rdqueue, m);

	splx(x);	

	if(readflag)
	{
		readflag = 0;
		wakeup((caddr_t) &i4b_rdqueue);
	}

	if(selflag)
	{
		selflag = 0;
		selwakeup(&select_rd_info);
	}
}
Exemple #19
0
/*---------------------------------------------------------------------------*
 *	i4bputqueue - put message into queue to userland
 *---------------------------------------------------------------------------*/
void
i4bputqueue(struct mbuf *m)
{
	int x;

	if(!openflag)
	{
		i4b_Dfreembuf(m);
		return;
	}

	x = splnet();

	if(IF_QFULL(&i4b_rdqueue))
	{
		struct mbuf *m1;
		IF_DEQUEUE(&i4b_rdqueue, m1);
		i4b_Dfreembuf(m1);
		NDBGL4(L4_ERR, "ERROR, queue full, removing entry!");
	}

	IF_ENQUEUE(&i4b_rdqueue, m);

	splx(x);

	if(readflag)
	{
		readflag = 0;
		wakeup((void *) &i4b_rdqueue);
	}

	if(selflag)
	{
		selflag = 0;
		selnotify(&select_rd_info, 0, 0);
	}
}
Exemple #20
0
static int iavc_receive_start(iavc_softc_t *sc)
{
    struct mbuf *m = i4b_Dgetmbuf(3);
    u_int8_t *p;

    if (sc->sc_blocked && sc->sc_state == IAVC_UP)
	printf("%s: receive_start\n", device_xname(&sc->sc_dev));

    if (!m) {
	aprint_error_dev(&sc->sc_dev, "can't get memory\n");
	return (ENOMEM);
    }

    /*
     * byte  0x73 = SEND_POLLACK
     */

    p = amcc_put_byte(mtod(m, u_int8_t*), 0);
    p = amcc_put_byte(p, 0);
    p = amcc_put_byte(p, SEND_POLLACK);

    IF_PREPEND(&sc->sc_txq, m);

    NDBGL4(L4_IAVCDBG, "%s: blocked = %d, state = %d",
      device_xname(&sc->sc_dev), sc->sc_blocked, sc->sc_state);

    sc->sc_blocked = 0;
    iavc_start_tx(sc);

    /* If this was our first START, register our readiness */
    if (sc->sc_state != IAVC_UP) {
	sc->sc_state = IAVC_UP;
	capi_ll_control(&sc->sc_capi, CAPI_CTRL_READY, 1);
    }

    return 0;
}
Exemple #21
0
/*---------------------------------------------------------------------------*
 *	fixed unit algorithm B channel idle check timeout function
 *---------------------------------------------------------------------------*/
static void
i4b_idle_check_fix_unit(call_desc_t *cd)
{

	/* simple idletime calculation */

	if((cd->shorthold_data.idle_time > 0) && (cd->shorthold_data.unitlen_time == 0))
	{
		if((i4b_get_idletime(cd) + cd->shorthold_data.idle_time) <= SECOND)
		{
			NDBGL4(L4_TIMO, "%ld: outgoing-call-st, idle timeout, disconnecting!", (long)SECOND);
			(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
			i4b_l4_idle_timeout_ind(cd);
		}
		else
		{
			NDBGL4(L4_TIMO, "%ld: outgoing-call-st, activity, last_active=%ld, max_idle=%ld",
					(long)SECOND, (long)i4b_get_idletime(cd), (long)cd->shorthold_data.idle_time);
			callout_reset(&cd->idle_timeout, hz / 2,
					(void *)i4b_idle_check, cd);
			cd->timeout_active = 1;
		}
	}

	/* full shorthold mode calculation */

	else if((cd->shorthold_data.unitlen_time > 0)
	         && (cd->shorthold_data.unitlen_time > (cd->shorthold_data.idle_time + cd->shorthold_data.earlyhup_time)))
	{
		switch(cd->idletime_state)
		{

		case IST_NONCHK:	/* end of non-check time */

			callout_reset(&cd->idle_timeout,
					hz*(cd->shorthold_data.idle_time),
					(void *)i4b_idle_check, cd);
			cd->idletimechk_start = SECOND;
			cd->idletime_state = IST_CHECK;
			cd->timeout_active = 1;
			NDBGL4(L4_TIMO, "%ld: outgoing-call, idletime check window reached!", (long)SECOND);
			break;

		case IST_CHECK:		/* end of idletime chk */
			if((i4b_get_idletime(cd) > cd->idletimechk_start) &&
			   (i4b_get_idletime(cd) <= SECOND))
			{	/* activity detected */
				callout_reset(&cd->idle_timeout,
					hz*(cd->shorthold_data.earlyhup_time),
					(void *)i4b_idle_check, cd);
				cd->timeout_active = 1;
				cd->idletime_state = IST_SAFE;
				NDBGL4(L4_TIMO, "%ld: outgoing-call, activity at %ld, wait earlyhup-end", (long)SECOND, (long)i4b_get_idletime(cd));
			}
			else
			{	/* no activity, hangup */
				NDBGL4(L4_TIMO, "%ld: outgoing-call, idle timeout, last activity at %ld", (long)SECOND, (long)i4b_get_idletime(cd));
				(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
				i4b_l4_idle_timeout_ind(cd);
				cd->idletime_state = IST_IDLE;
			}
			break;

		case IST_SAFE:	/* end of earlyhup time */

			callout_reset(&cd->idle_timeout,
				hz*(cd->shorthold_data.unitlen_time - 
				  (cd->shorthold_data.idle_time +
				  cd->shorthold_data.earlyhup_time)),
				(void *)i4b_idle_check, cd); 
			cd->timeout_active = 1;
			cd->idletime_state = IST_NONCHK;

			if(cd->aocd_flag == 0)
			{
				cd->units_type = CHARGE_CALC;
				cd->cunits++;
				i4b_l4_charging_ind(cd);
			}
			
			NDBGL4(L4_TIMO, "%ld: outgoing-call, earlyhup end, wait for idletime start", (long)SECOND);
			break;

		default:
			NDBGL4(L4_ERR, "outgoing-call: invalid idletime_state value!");
			cd->idletime_state = IST_IDLE;
			break;
		}
	}
}
Exemple #22
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);
}
Exemple #23
0
static void iavc_start_tx(iavc_softc_t *sc)
{
    struct mbuf *m;
    u_int8_t *dmabuf;
    u_int32_t txlen = 0;
    
    /* If device has put us on hold, punt. */

    if (sc->sc_blocked) {
	return;
    }

    /* If using DMA and transmitter busy, punt. */
    
    if (sc->sc_dma && (sc->sc_csr & EN_TX_TC_INT)) {
	return;
    }

    /* Else, see if we have messages to send. */

    _IF_DEQUEUE(&sc->sc_txq, m);
    if (!m) {
	return;
    }

    /* Have message, will send. */

    if (CAPIMSG_LEN(m->m_data)) {
	/* A proper CAPI message, possibly with B3 data */

	if (sc->sc_dma) {
	    /* Copy message to DMA buffer. */

	    if (m->m_next) {
		dmabuf = amcc_put_byte(&sc->sc_sendbuf[0], SEND_DATA_B3_REQ);
	    } else {
		dmabuf = amcc_put_byte(&sc->sc_sendbuf[0], SEND_MESSAGE);
	    }

	    dmabuf = amcc_put_word(dmabuf, m->m_len);
	    bcopy(m->m_data, dmabuf, m->m_len);
	    dmabuf += m->m_len;
	    txlen = 5 + m->m_len;

	    if (m->m_next) {
		dmabuf = amcc_put_word(dmabuf, m->m_next->m_len);
		bcopy(m->m_next->m_data, dmabuf, m->m_next->m_len);
		txlen += 4 + m->m_next->m_len;
	    }

	} else {
	    /* Use PIO. */

	    if (m->m_next) {
		iavc_put_byte(sc, SEND_DATA_B3_REQ);
		NDBGL4(L4_IAVCDBG, "iavc%d: tx SDB3R msg, len = %d", sc->sc_unit, m->m_len);
	    } else {
		iavc_put_byte(sc, SEND_MESSAGE);
		NDBGL4(L4_IAVCDBG, "iavc%d: tx SM msg, len = %d", sc->sc_unit, m->m_len);
	    }
#if 0
    {
	u_int8_t *p = mtod(m, u_int8_t*);
	int len;
	for (len = 0; len < m->m_len; len++) {
	    printf(" %02x", *p++);
	    if (len && (len % 16) == 0) printf("\n");
	}
	if (len % 16) printf("\n");
    }
#endif

	    iavc_put_slice(sc, m->m_data, m->m_len);

	    if (m->m_next) {
		iavc_put_slice(sc, m->m_next->m_data, m->m_next->m_len);
	    }
	}

    } else {
	/* A board control message to be sent as is */

	if (sc->sc_dma) {
Exemple #24
0
/*---------------------------------------------------------------------------*
 *	B channel idle check timeout function
 *---------------------------------------------------------------------------*/ 
void
i4b_idle_check(call_desc_t *cd)
{
	if(cd->cdid == CDID_UNUSED)
		return;
	
	crit_enter();

	/* failsafe */

	if(cd->timeout_active == 0)
	{
		NDBGL4(L4_ERR, "ERROR: timeout_active == 0 !!!");
	}
	else
	{	
		cd->timeout_active = 0;
	}
	
	/* incoming connections, simple idletime check */

	if(cd->dir == DIR_INCOMING)
	{
		if((i4b_get_idletime(cd) + cd->max_idle_time) <= SECOND)
		{
			NDBGL4(L4_TIMO, "%ld: incoming-call, line idle timeout, disconnecting!", (long)SECOND);
			(*ctrl_desc[cd->controller].N_DISCONNECT_REQUEST)(cd->cdid,
					(CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
			i4b_l4_idle_timeout_ind(cd);
		}
		else
		{
			NDBGL4(L4_TIMO, "%ld: incoming-call, activity, last_active=%ld, max_idle=%ld", (long)SECOND, (long)i4b_get_idletime(cd), (long)cd->max_idle_time);

			callout_reset(&cd->idle_timeout, hz / 2,
					(void *)i4b_idle_check, cd);
			cd->timeout_active = 1;
		}
	}

	/* outgoing connections */

	else if(cd->dir == DIR_OUTGOING)
	{
		switch( cd->shorthold_data.shorthold_algorithm )
		{
			case SHA_FIXU:
				i4b_idle_check_fix_unit( cd );
				break;
			case SHA_VARU:
				i4b_idle_check_var_unit( cd );
				break;
			default:
				NDBGL4(L4_TIMO, "%ld: bad value for shorthold_algorithm of %d",
					(long)SECOND, cd->shorthold_data.shorthold_algorithm);
				i4b_idle_check_fix_unit( cd );
				break;
		}
	}
	crit_exit();
}
/*---------------------------------------------------------------------------*
 *	rbch device ioctl handlibg
 *---------------------------------------------------------------------------*/
PDEVSTATIC int
isdnbchanioctl(dev_t dev, IOCTL_CMD_T cmd, void *data, int flag,
	struct lwp *l)
{
	int error = 0;
	int unit = minor(dev);
	struct rbch_softc *sc = &rbch_softc[unit];

	switch(cmd)
	{
		case FIOASYNC:	/* Set async mode */
			if (*(int *)data)
			{
				NDBGL4(L4_RBCHDBG, "unit %d, setting async mode", unit);
			}
			else
			{
				NDBGL4(L4_RBCHDBG, "unit %d, clearing async mode", unit);
			}
			break;

		case FIONBIO:
			if (*(int *)data)
			{
				NDBGL4(L4_RBCHDBG, "unit %d, setting non-blocking mode", unit);
				sc->sc_devstate |= ST_NOBLOCK;
			}
			else
			{
				NDBGL4(L4_RBCHDBG, "unit %d, clearing non-blocking mode", unit);
				sc->sc_devstate &= ~ST_NOBLOCK;
			}
			break;

		case TIOCCDTR:	/* Clear DTR */
			if(sc->sc_devstate & ST_CONNECTED)
			{
				NDBGL4(L4_RBCHDBG, "unit %d, disconnecting for DTR down", unit);
				i4b_l4_drvrdisc(sc->sc_cd->cdid);
			}
			break;

		case I4B_RBCH_DIALOUT:
                {
			size_t x;

			for (x = 0; x < TELNO_MAX && ((char *)data)[x]; x++)
				;
			if (x)
			{
				NDBGL4(L4_RBCHDBG, "%d, attempting dialout to %s", unit, (char *)data);
				i4b_l4_dialoutnumber(rbch_driver_id, unit, x, (char *)data);
				break;
			}
			/* fall through to SDTR */
		}

		case TIOCSDTR:	/* Set DTR */
			NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout (DTR)", unit);
			i4b_l4_dialout(rbch_driver_id, unit);
			break;

		case TIOCSETA:	/* Set termios struct */
			break;

		case TIOCGETA:	/* Get termios struct */
			*(struct termios *)data = sc->it_in;
			break;

		case TIOCMGET:
			*(int *)data = TIOCM_LE|TIOCM_DTR|TIOCM_RTS|TIOCM_CTS|TIOCM_DSR;
			if (sc->sc_devstate & ST_CONNECTED)
				*(int *)data |= TIOCM_CD;
			break;

		case I4B_RBCH_VR_REQ:
                {
			msg_vr_req_t *mvr;

			mvr = (msg_vr_req_t *)data;

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

		default:	/* Unknown stuff */
			NDBGL4(L4_RBCHDBG, "(minor=%d) ioctl, unknown cmd %lx", unit, (u_long)cmd);
			error = EINVAL;
			break;
	}
	return(error);
}
/*---------------------------------------------------------------------------*
 *	write to rbch device
 *---------------------------------------------------------------------------*/
PDEVSTATIC int
isdnbchanwrite(dev_t dev, struct uio * uio, int ioflag)
{
	struct mbuf *m;
	int error = 0;
	int unit = minor(dev);
	struct rbch_softc *sc = &rbch_softc[unit];

	int s;

	NDBGL4(L4_RBCHDBG, "unit %d, write", unit);

	s = splnet();
	if(!(sc->sc_devstate & ST_ISOPEN))
	{
		NDBGL4(L4_RBCHDBG, "unit %d, write while not open", unit);
		splx(s);
		return(EIO);
	}

	if((sc->sc_devstate & ST_NOBLOCK))
	{
		if(!(sc->sc_devstate & ST_CONNECTED)) {
			splx(s);
			return(EWOULDBLOCK);
		}
		if(IF_QFULL(sc->sc_ilt->tx_queue) && (sc->sc_devstate & ST_ISOPEN)) {
			splx(s);
			return(EWOULDBLOCK);
	}
	}
	else
	{
		while(!(sc->sc_devstate & ST_CONNECTED))
		{
			NDBGL4(L4_RBCHDBG, "unit %d, write wait init", unit);

			error = tsleep((void *) &rbch_softc[unit],
						   TTIPRI | PCATCH,
						   "wrrbch", 0 );
			if(error == ERESTART) {
				splx(s);
				return (ERESTART);
			}
			else if(error == EINTR)
			{
				splx(s);
				NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait init", unit);
				return(EINTR);
			}
			else if(error)
			{
				splx(s);
				NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep init", unit, error);
				return(error);
			}
			tsleep((void *) &rbch_softc[unit], TTIPRI | PCATCH, "xrbch", (hz*1));
		}

		while(IF_QFULL(sc->sc_ilt->tx_queue) && (sc->sc_devstate & ST_ISOPEN))
		{
			sc->sc_devstate |= ST_WRWAITEMPTY;

			NDBGL4(L4_RBCHDBG, "unit %d, write queue full", unit);

			if ((error = tsleep((void *) &sc->sc_ilt->tx_queue,
					    TTIPRI | PCATCH,
					    "wrbch", 0)) != 0) {
				sc->sc_devstate &= ~ST_WRWAITEMPTY;
				if(error == ERESTART)
				{
					splx(s);
					return(ERESTART);
				}
				else if(error == EINTR)
				{
					splx(s);
					NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait write", unit);
					return(error);
				}
				else if(error)
				{
					splx(s);
					NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep write", unit, error);
					return(error);
				}
				else if (!(sc->sc_devstate & ST_CONNECTED)) {
					splx(s);
					return 0;
				}
			}
		}
	}

	if(!(sc->sc_devstate & ST_ISOPEN))
	{
		NDBGL4(L4_RBCHDBG, "unit %d, not open anymore", unit);
		splx(s);
		return(EIO);
	}

	if((m = i4b_Bgetmbuf(BCH_MAX_DATALEN)) != NULL)
	{
		m->m_len = min(BCH_MAX_DATALEN, uio->uio_resid);

		NDBGL4(L4_RBCHDBG, "unit %d, write %d bytes", unit, m->m_len);

		error = uiomove(m->m_data, m->m_len, uio);

		if(IF_QFULL(sc->sc_ilt->tx_queue))
		{
			m_freem(m);
		}
		else
		{
			IF_ENQUEUE(sc->sc_ilt->tx_queue, m);
		}

		(*sc->sc_ilt->bchannel_driver->bch_tx_start)(sc->sc_ilt->l1token, sc->sc_ilt->channel);
	}

	splx(s);

	return(error);
}
/*---------------------------------------------------------------------------*
 *	read from rbch device
 *---------------------------------------------------------------------------*/
PDEVSTATIC int
isdnbchanread(dev_t dev, struct uio *uio, int ioflag)
{
	struct mbuf *m;
	int error = 0;
	int unit = minor(dev);
	struct ifqueue *iqp;
	struct rbch_softc *sc = &rbch_softc[unit];

	int s;

	NDBGL4(L4_RBCHDBG, "unit %d, enter read", unit);

	s = splnet();
	if(!(sc->sc_devstate & ST_ISOPEN))
	{
		splx(s);
		NDBGL4(L4_RBCHDBG, "unit %d, read while not open", unit);
		return(EIO);
	}

	if((sc->sc_devstate & ST_NOBLOCK))
	{
		if(!(sc->sc_devstate & ST_CONNECTED)) {
			splx(s);
			return(EWOULDBLOCK);
		}

		if(sc->sc_bprot == BPROT_RHDLC)
			iqp = &sc->sc_hdlcq;
		else
			iqp = sc->sc_ilt->rx_queue;

		if(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN)) {
			splx(s);
			return(EWOULDBLOCK);
	}
	}
	else
	{
		while(!(sc->sc_devstate & ST_CONNECTED))
		{
			NDBGL4(L4_RBCHDBG, "unit %d, wait read init", unit);

			if((error = tsleep((void *) &rbch_softc[unit],
					   TTIPRI | PCATCH,
					   "rrrbch", 0 )) != 0)
			{
				splx(s);
				NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep", unit, error);
				return(error);
			}
		}

		if(sc->sc_bprot == BPROT_RHDLC)
			iqp = &sc->sc_hdlcq;
		else
			iqp = sc->sc_ilt->rx_queue;

		while(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN))
		{
			sc->sc_devstate |= ST_RDWAITDATA;

			NDBGL4(L4_RBCHDBG, "unit %d, wait read data", unit);

			if((error = tsleep((void *) &sc->sc_ilt->rx_queue,
					   TTIPRI | PCATCH,
					   "rrbch", 0 )) != 0)
			{
				splx(s);
				NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep read", unit, error);
				sc->sc_devstate &= ~ST_RDWAITDATA;
				return(error);
			} else if (!(sc->sc_devstate & ST_CONNECTED)) {
				splx(s);
				return 0;
			}
		}
	}

	IF_DEQUEUE(iqp, m);

	NDBGL4(L4_RBCHDBG, "unit %d, read %d bytes", unit, m->m_len);

	if(m && m->m_len)
	{
		error = uiomove(m->m_data, m->m_len, uio);
	}
	else
	{
		NDBGL4(L4_RBCHDBG, "unit %d, error %d uiomove", unit, error);
		error = EIO;
	}

	if(m)
		i4b_Bfreembuf(m);

	splx(s);

	return(error);
}
Exemple #28
0
/*---------------------------------------------------------------------------*
 *	i4bioctl - device driver ioctl routine
 *---------------------------------------------------------------------------*/
PDEVSTATIC int
i4bioctl(struct dev_ioctl_args *ap)
{
    cdev_t dev = ap->a_head.a_dev;
    caddr_t data = ap->a_data;
    call_desc_t *cd;
    int error = 0;

    if(minor(dev))
        return(ENODEV);

    switch(ap->a_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;
        }

        /* 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;

        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);

        if(mcr->keypad[0] != '\0')
            strcpy(cd->keypad, mcr->keypad);
        else
            cd->keypad[0] = '\0';

        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:
        {
            int i;
            for (i = 0;
                    i < ctrl_desc[mcr->controller].nbch &&
                    ctrl_desc[mcr->controller].bch_state[i] != BCH_ST_FREE;
                    i++);
            if (i == ctrl_desc[mcr->controller].nbch)
                SET_CAUSE_VAL(cd->cause_in, CAUSE_I4B_NOCHAN);
            /* else mcr->channel = i; XXX */
        }
        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 */
        {
            NDBGL4(L4_ERR, "I4B_CONNECT_RESP ioctl, cdid not found!");
            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;

        NDBGL4(L4_TIMO, "I4B_CONNECT_RESP max_idle_time set to %ld seconds", (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 */
        {
            NDBGL4(L4_ERR, "I4B_DISCONNECT_REQ ioctl, cdid not found!");
            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;
            mcir->nbch =
                ctrl_desc[mcir->controller].nbch;

            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 NI4BING > 0
        case BDRV_ING:
            dlt = ing_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;

        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;

        crit_enter();
        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;
        crit_exit();
        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)
        {
            NDBGL4(L4_ERR, "I4B_ALERT_REQ ioctl, cdid not found!");
            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 = kmalloc(r->numprotos * sizeof(struct isdn_dr_prot),
                        M_DEVBUF, M_WAITOK);

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

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

        for(i = 0; i < r->numprotos; i++)
        {
            prots2[i].microcode = kmalloc(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)
                {
                    kfree(prots2[i].microcode, M_DEVBUF);
                }
            }
            kfree(prots2, M_DEVBUF);
        }

        if(prots)
        {
            kfree(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)
        {
            /* XXX arbitrary limit */
            if (req.in_param_len >
                    I4B_ACTIVE_DIAGNOSTIC_MAXPARAMLEN) {
                error = EINVAL;
                goto diag_done;
            }

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

            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 = kmalloc(r->out_param_len, M_DEVBUF, M_WAITOK);

        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)
            kfree(req.in_param, M_DEVBUF);

        if(req.out_param)
            kfree(req.out_param, M_DEVBUF);

        break;
    }

    /* default */

    default:
        error = ENOTTY;
        break;
    }

    return(error);
}