OpenSS7 SS7 for the Common Man |
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
||||||||||||||||||||||||||
Home | Overview | Status | News | Documentation | Resources | About | |||||||||||||||||||||
File /code/strss7/drivers/acb56/sdt_acb56.c#ident "@(#) $RCSfile: sdt_acb56.c,v $ $Name: $($Revision: 0.8.2.5 $) $Date: 2003/04/03 19:49:24 $" static char const ident[] = "$RCSfile: sdt_acb56.c,v $ $Name: $($Revision: 0.8.2.5 $) $Date: 2003/04/03 19:49:24 $"; /* * This is an implementation of the Signalling Data Terminal for the SeaLevel * ACB56[tm] V.35 ISA card. It provides the driver routines necessary for * interface to the SDT driver kernel module. */ #include <linux/config.h> #include <linux/version.h> #ifdef MODVERSIONS #include <linux/modversions.h> #endif #include <linux/module.h> #include <sys/stream.h> #include <sys/stropts.h> #include <sys/cmn_err.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/ioport.h> #include <asm/io.h> #include <asm/dma.h> #include <ss7/lmi.h> #include <ss7/lmi_ioctl.h> #include <ss7/sdli.h> #include <ss7/sdli_ioctl.h> #include <ss7/sdti.h> #include <ss7/sdti_ioctl.h> #include "../debug.h" /* generic debugging macros */ #include "../bufq.h" /* generic buffer queues */ #include "../priv.h" /* generic data structures */ #include "../lock.h" /* generic queue locking functions */ #include "../queue.h" /* generic put and srv routines */ #include "../alloc.h" /* generic buffer allocation routines */ #define ACB56_DESCRIP "ACB56: SS7/SDT (Signalling Data Terminal) STREAMS DRIVER." #define ACB56_COPYRIGHT "Copyright (c) 1997-2002 OpenSS7 Corpoation. All Rights Reserved." #define ACB56_DEVICES "Supports the SeaLevel ACB56(tm) V.35 boards." #define ACB56_CONTACT "Brian Bidulock <bidulock@openss7.org>" #define ACB56_LICENSE "GPL" #define ACB56_BANNER ACB56_DESCRIP "\n" \ ACB56_COPYRIGHT "\n" \ ACB56_DEVICES "\n" \ ACB56_CONTACT "\n" MODULE_AUTHOR(ACB56_CONTACT); MODULE_DESCRIPTION(ACB56_DESCRIP); MODULE_SUPPORTED_DEVICE(ACB56_DEVICES); #ifdef MODULE_LICENSE MODULE_LICENSE(ACB56_LICENSE); #endif #ifdef USE_VOID_PUT_PROC #define INT void #else #define INT int #endif typedef void (*bufcall_fnc_t) (long); /* * ========================================================================= * * STREAMS Definitions * * ========================================================================= */ #define SDT_MOD_NAME "sdt-acb56" #define SDT_MOD_ID SDT_IOC_MAGIC STATIC struct module_info sdt_rinfo = { mi_idnum:SDT_MOD_ID, /* Module ID number */ mi_idname:SDT_MOD_NAME "-rd", /* Module name */ mi_minpsz:0, /* Min packet size accepted */ mi_maxpsz:INFPSZ, /* Max packet size accepted */ mi_hiwat:9 * 279, /* Hi water mark */ mi_lowat:3 * 279, /* Lo water mark */ }; STATIC struct module_info sdt_winfo = { mi_idnum:SDT_MOD_ID, /* Module ID number */ mi_idname:SDT_MOD_NAME "-wr", /* Module name */ mi_minpsz:0, /* Min packet size accepted */ mi_maxpsz:INFPSZ, /* Max packet size accepted */ mi_hiwat:1, /* Hi water mark */ mi_lowat:0, /* Lo water mark */ }; STATIC int sdt_open(queue_t *, dev_t *, int, int, cred_t *); STATIC int sdt_close(queue_t *, int, cred_t *); STATIC INT sdt_rput(queue_t *, mblk_t *); STATIC INT sdt_rsrv(queue_t *); STATIC INT sdt_wput(queue_t *, mblk_t *); STATIC INT sdt_wsrv(queue_t *); STATIC struct qinit sdt_rinit = { qi_putp:sdt_rput, /* Read put (msg from below) */ qi_srvp:sdt_rsrv, /* Read queue service */ qi_open:sdt_open, /* Each open */ qi_qclose:sdt_close, /* Last close */ qi_minfo:&sdt_rinfo, /* Information */ }; STATIC struct qinit sdt_winit = { qi_putp:sdt_wput, /* Write put (msg from above) */ qi_srvp:sdt_wsrv, /* Write queue service */ qi_minfo:&sdt_winfo, /* Information */ }; STATIC struct streamtab sdt_info = { st_rdinit:&sdt_rinit, /* Upper read queue */ st_wrinit:&sdt_winit, /* Upper write queue */ }; /* * ========================================================================= * * ACB56-SDT Private Structure * * ========================================================================= */ typedef struct sdt { STR_DECLARATION (struct sdt); /* stream structure declaration */ mblk_t *rbuf; /* normal data reassembly */ mblk_t *xbuf; /* expedited data reassembly */ lmi_option_t option; /* LMI options */ struct { bufq_t tb; /* transmission buffer */ mblk_t *tx_cmp; /* tx compression buffer */ mblk_t *rx_cmp; /* rx compression buffer */ uint tx_repeat; /* tx compression count */ uint rx_repeat; /* rx compression count */ struct { ulong t8; /* SDT T8 timer */ } timer; sdt_config_t config; /* SDT configuration */ sdt_statem_t statem; /* SDT state machine */ sdt_notify_t notify; /* SDT notification options */ sdt_stats_t stats; /* SDT statistics */ sdt_stats_t stamp; /* SDT statistics timestamps */ lmi_sta_t statsp; /* SDT statistics periods */ } sdt; struct { long tickbytes; /* traffic bytes per tick */ long bytecount; /* traffic bytes this tick */ long timestamp; /* traffic last tick */ struct { ulong t9; } timer; sdl_config_t config; /* SDL configuration */ sdl_statem_t statem; /* SDL state machine variables */ sdl_notify_t notify; /* SDL notification options */ sdl_stats_t stats; /* SDL statistics */ sdl_stats_t stamp; /* SDL statistics timestamps */ lmi_sta_t statsp; /* SDL statistics periods */ } sdl; struct { int ifindex; /* interface index */ int irq; /* interrupt */ int iobase; /* io base */ int dma_rx; /* rx dma channel */ int dma_tx; /* tx dma channel */ int rx_octet_mode; /* Is octet counting on? */ int rx_octet_count; /* bits (not octets) counted */ mblk_t *tx_msg; /* transmit buffer */ mblk_t *rx_msg; /* receive buffer */ mblk_t *cp_msg; /* compressed su buffer */ unsigned char *tx_buf; /* current tx buffer pointer */ unsigned char *tx_max; /* end of tx buffer pointer */ int tx_error; /* transmission error */ unsigned char *rx_buf; /* current rx buffer pointer */ unsigned char *rx_max; /* end oft rx buffer pointer */ int rx_error; /* reception error */ unsigned char regs[16]; /* register images */ unsigned char rr0; /* register image reg 0 */ bufq_t tinputq; } dev; } sdt_t; #define SDT_PRIV(__q) ((sdt_t *)(__q)->q_ptr) /* * ======================================================================== * * PRIMITIVES * * ======================================================================== */ /* * ------------------------------------------------------------------------ * * Primitive sent upstream * * ------------------------------------------------------------------------ */ /* * M_ERROR * ----------------------------------- */ STATIC int m_error(queue_t * q, int err) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; if ((mp = ss7_allocb(q, 2, BPRI_MED))) { mp->b_datap->db_type = M_ERROR; *(mp->b_wptr)++ = err < 0 ? -err : err; *(mp->b_wptr)++ = err < 0 ? -err : err; sdt->t.state = TS_NOSTATES; sdt->state = LMI_UNUSABLE; printd(("%s: %p: <- M_ERROR\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * M_HANGUP * ----------------------------------- */ STATIC int m_hangup(queue_t * q, int err) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; if ((mp = ss7_allocb(q, 2, BPRI_MED))) { mp->b_datap->db_type = M_HANGUP; *(mp->b_wptr)++ = err < 0 ? -err : err; *(mp->b_wptr)++ = err < 0 ? -err : err; sdt->t.state = TS_NOSTATES; sdt->state = LMI_UNUSABLE; printd(("%s: %p: <- M_HANGUP\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_INFO_ACK * ----------------------------------- */ STATIC INLINE int lmi_info_ack(queue_t * q, caddr_t ppa_ptr, size_t ppa_len) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; lmi_info_ack_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + ppa_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_INFO_ACK; p->lmi_version = 1; p->lmi_state = sdt->state; p->lmi_max_sdu = sdt->sdt.config.m + 1 + ((sdt->option.popt & SS7_POPT_XSN) ? 6 : 3); p->lmi_min_sdu = ((sdt->option.popt & SS7_POPT_XSN) ? 6 : 3); p->lmi_header_len = 0; p->lmi_ppa_style = LMI_STYLE2; bcopy(ppa_ptr, mp->b_wptr, ppa_len); mp->b_wptr += ppa_len; printd(("%s: %p: <- LMI_INFO_ACK\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_OK_ACK * ----------------------------------- */ STATIC INLINE int lmi_ok_ack(queue_t * q, long prim) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; lmi_ok_ack_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_OK_ACK; p->lmi_correct_primitive = prim; switch (sdt->state) { case LMI_ATTACH_PENDING: sdt->state = LMI_DISABLED; break; case LMI_DETACH_PENDING: sdt->state = LMI_UNATTACHED; break; default: break; } p->lmi_state = sdt->state; printd(("%s: %p: <- LMI_OK_ACK\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_ERROR_ACK * ----------------------------------- */ STATIC INLINE int lmi_error_ack(queue_t * q, long prim, ulong reason, ulong errno) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; lmi_error_ack_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_ERROR_ACK; p->lmi_errno = errno; p->lmi_reason = reason; p->lmi_error_primitive = prim; switch (sdt->state) { case LMI_ATTACH_PENDING: sdt->state = LMI_UNATTACHED; break; case LMI_DETACH_PENDING: sdt->state = LMI_DISABLED; break; case LMI_ENABLE_PENDING: sdt->state = LMI_DISABLED; break; case LMI_DISABLE_PENDING: sdt->state = LMI_ENABLED; break; default: break; } p->lmi_state = sdt->state; printd(("%s: %p: <- LMI_ERROR_ACK\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_ENABLE_CON * ----------------------------------- */ STATIC INLINE int lmi_enable_con(queue_t * q) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; lmi_enable_con_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_ENABLE_CON; assure(sdt->state == LMI_ENABLE_PENDING); sdt->state = LMI_ENABLED; p->lmi_state = sdt->state; printd(("%s: %p: <- LMI_ENABLE_CON\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_DISABLE_CON * ----------------------------------- */ STATIC INLINE int lmi_disable_con(queue_t * q) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; lmi_disable_con_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_DISABLE_CON; assure(sdt->state == LMI_DISABLE_PENDING); sdt->state = LMI_DISABLED; p->lmi_state = sdt->state; printd(("%s: %p: <- LMI_DISABLE_CON\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_OPTMGMT_ACK * ----------------------------------- */ STATIC INLINE int lmi_optmgmt_ack(queue_t * q, ulong flags, caddr_t opt_ptr, size_t opt_len) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; lmi_optmgmt_ack_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_OPTMGMT_ACK; p->lmi_opt_length = opt_len; p->lmi_opt_offset = opt_len ? sizeof(*p) : 0; p->lmi_mgmt_flags = flags; printd(("%s: %p: <- LMI_OPTMGMT_ACK\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_ERROR_IND * ----------------------------------- */ STATIC INLINE int lmi_error_ind(queue_t * q, ulong errno, ulong reason) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp; lmi_error_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_ERROR_IND; p->lmi_errno = errno; p->lmi_reason = reason; p->lmi_state = sdt->state; printd(("%s: %p: <- LMI_ERROR_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_STATS_IND * ----------------------------------- */ STATIC INLINE int lmi_stats_ind(queue_t * q, ulong interval) { sdt_t *sdt = SDT_PRIV(q); if (canputnext(sdt->oq)) { mblk_t *mp; lmi_stats_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_STATS_IND; p->lmi_interval = interval; p->lmi_timestamp = jiffies; printd(("%s: %p: <- LMI_STATS_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } rare(); return (-EBUSY); } /* * LMI_EVENT_IND * ----------------------------------- */ STATIC INLINE int lmi_event_ind(queue_t * q, ulong oid, ulong level) { sdt_t *sdt = SDT_PRIV(q); if (canputnext(sdt->oq)) { mblk_t *mp; lmi_event_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_EVENT_IND; p->lmi_objectid = oid; p->lmi_timestamp = jiffies; p->lmi_severity = level; printd(("%s: %p: <- LMI_EVENT_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } rare(); return (-EBUSY); } /* * SDT_RC_SIGNAL_UNIT_IND * ----------------------------------- */ STATIC INLINE int sdt_rc_signal_unit_ind(queue_t * q, sdt_t * sdt, mblk_t * dp, ulong count) { if (count) { if (canputnext(sdt->oq)) { mblk_t *mp; sdt_rc_signal_unit_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->sdt_primitive = SDT_RC_SIGNAL_UNIT_IND; p->sdt_count = count; mp->b_cont = dp; // printd(("%s: %p: <- SDT_RC_SIGNAL_UNIT_IND\n", SDT_MOD_NAME, // sdt)); putnext(sdt->oq, mp); return (QR_ABSORBED); } rare(); return (-ENOBUFS); } rare(); return (-EBUSY); } swerr(); return (-EFAULT); } /* * SDT_RC_CONGESTION_ACCEPT_IND * ----------------------------------- */ STATIC INLINE int sdt_rc_congestion_accept_ind(queue_t * q, sdt_t * sdt) { mblk_t *mp; sdt_rc_congestion_accept_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->sdt_primitive = SDT_RC_CONGESTION_ACCEPT_IND; printd(("%s: %p: <- SDT_RC_CONGESTION_ACCEPT_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * SDT_RC_CONGESTION_DISCARD_IND * ----------------------------------- */ STATIC INLINE int sdt_rc_congestion_discard_ind(queue_t * q, sdt_t * sdt) { mblk_t *mp; sdt_rc_congestion_discard_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->sdt_primitive = SDT_RC_CONGESTION_DISCARD_IND; printd(("%s: %p: <- SDT_RC_CONGESTION_DISCARD_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * SDT_RC_NO_CONGESTION_IND * ----------------------------------- */ STATIC INLINE int sdt_rc_no_congestion_ind(queue_t * q, sdt_t * sdt) { mblk_t *mp; sdt_rc_no_congestion_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->sdt_primitive = SDT_RC_NO_CONGESTION_IND; printd(("%s: %p: <- SDT_RC_NO_CONGESTION_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * SDT_IAC_CORRECT_SU_IND * ----------------------------------- */ STATIC INLINE int sdt_iac_correct_su_ind(queue_t * q, sdt_t * sdt) { if (canputnext(sdt->oq)) { mblk_t *mp; sdt_iac_correct_su_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = ((typeof(p)) mp->b_wptr)++; p->sdt_primitive = SDT_IAC_CORRECT_SU_IND; printd(("%s: %p: <- SDT_IAC_CORRECT_SU_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } rare(); return (-EBUSY); } /* * SDT_IAC_ABORT_PROVING_IND * ----------------------------------- */ STATIC INLINE int sdt_iac_abort_proving_ind(queue_t * q, sdt_t * sdt) { mblk_t *mp; sdt_iac_abort_proving_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->sdt_primitive = SDT_IAC_ABORT_PROVING_IND; printd(("%s: %p: <- SDT_IAC_ABORT_PROVING_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * SDT_LSC_LINK_FAILURE_IND * ----------------------------------- */ STATIC INLINE int sdt_lsc_link_failure_ind(queue_t * q, sdt_t * sdt) { mblk_t *mp; sdt_lsc_link_failure_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->sdt_primitive = SDT_LSC_LINK_FAILURE_IND; printd(("%s: %p: <- SDT_LSC_LINK_FAILURE_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * SDT_TXC_TRANSMISSION_REQUEST_IND * ----------------------------------- */ STATIC INLINE int sdt_txc_transmission_request_ind(queue_t * q, sdt_t * sdt) { mblk_t *mp; sdt_txc_transmission_request_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->sdt_primitive = SDT_TXC_TRANSMISSION_REQUEST_IND; printd(("%s: %p: <- SDT_TXC_TRANSMISSION_REQUEST_IND\n", SDT_MOD_NAME, sdt)); putnext(sdt->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * ========================================================================= * * PROTOCOL STATE MACHINE FUNCTIONS * * ========================================================================= */ /* * ------------------------------------------------------------------------ * * Timers * * ------------------------------------------------------------------------ */ enum { tall, t8, t9 }; #define SDT_DECLARE_TIMER(__o,__t) \ STATIC int __o ## _ ## __t ## _timeout(__o ## _t *); \ STATIC void __o ## _ ## __t ## _expiry(caddr_t data) \ { \ __o ## _do_timeout(data, # __t, &((_o ## _t *)data)->timers. __t, &__o ## _ ## __t ## _timeout, & __o ## _t ## _expiry); \ } \ STATIC void __o ## _stop_timer_ ## __t (__o ## _t *__o) \ { \ __o ## _stop_timer(__o, # __t, &__o->timers. __t); \ } \ STATIC void __o ## _start_timer_ ## __t (__o ## _t *__o) \ { \ __o ## _start_timer(__o, # __t, &__o->timers. __t, &__o ## _ ## __t ## _expiry, __o->config. __t); \ } \ STATIC INLINE void sdt_do_timeout(caddr_t data, const char *timer, ulong * timeo, int (*to_fnc) (ct_t *), void (*exp_fnc) (caddr_t)) { sdt_t *sdt = (sdt_t *) data; if (xchg(timeo, 0)) { if (lis_spin_trylock(&sdt->lock)) { printd(("%s: %p: %s timeout at %lu\n", SDT_MOD_NAME, sdt, timer, jiffies)); switch (to_fnc(sdt)) { default: case QR_DONE: sdt_put(sdt); lis_spin_unlock(&sdt->lock); return; case -ENOMEM: case -ENOBUFS: case -EBUSY: case -EAGAIN: break; } lis_spin_unlock(&sdt->lock); } else printd(("%s: %p: %s timeout collision oat %lu\n", SDT_MOD_NAME, sdt, timer, jiffies)); /* back off timer one tick */ *timeo = timeout(exp_fnc, data, 100); } } STATIC INLINE void sdt_stop_timer(sdt_t * sdt, const char *timer, ulong * timeo) { ulong to; if ((to = xchg(timeo, 0))) { untimeout(to); printd(("%s: %p: stopping %s at %lu\n", SDT_MOD_NAME, sdt, timer, jiffies)); sdt_put(sdt); } return; } STATIC INLINE void sdt_start_timer(sdt_t * sdt, const char *timer, ulong * timeo, void (*exp_func) (caddr_t), ulong val) { printd(("%s: %p: starting %s %lu ms at %lu\n", SDT_MOD_NAME, sdt, timer, val * 1000 / HZ, jiffies)); *timeo = timeout(exp_func, (caddr_t) sdt, val); } SDT_DECLARE_TIMER(sdt, t8); SDT_DECLARE_TIMER(sdt, t9); STATIC INLINE void __sdt_timer_stop(sdt_t * sdt, const uint t) { int single = 1; switch (t) { case tall: single = 0; /* fall through */ case t8: sdt_stop_timer_t8(sdt); if (single) break; /* fall through */ case t9: sdt_stop_timer_t9(sdt); if (single) break; /* fall through */ default: swerr(); break; } } STATIC INLINE void sdt_timer_stop(sdt_t * sdt, const uint t) { int flags; lis_spin_lock_irqsave(&sdt->lock, &flags); { __sdt_timer_stop(sdt, t); } lis_spin_unlock_irqrestore(&sdt->lock, &flags); } STATIC INLINE void sdt_timer_start(sdt_t * sdt, const uint t) { int flags; lis_spin_lock_irqsave(&sdt->lock, flags); { __sdt_timer_stop(sdt, t); switch (t) { case t8: sdt_start_timer_t8(sdt); break; case t9: sdt_start_timer_t9(sdt); break; default: swerr(); break; } } lis_spin_unlock_irqrestore(&sdt->lock, flags); } /* * ------------------------------------------------------------------------- * * SDT State Machines * * ------------------------------------------------------------------------- */ STATIC INLINE void sdt_aerm_stop(queue_t * q, sdt_t * sdt) { sdt->sdt.statem.aerm_state = SDT_STATE_IDLE; sdt->sdt.statem.Ti = sdt->sdt.config.Tin; } STATIC INLINE void sdt_eim_stop(queue_t * q, sdt_t * sdt) { sdt->sdt.statem.eim_state = SDT_STATE_IDLE; sdt_timer_stop(q, t8); } STATIC INLINE void sdt_suerm_stop(queue_t * q, sdt_t * sdt) { sdt_eim_stop(q, sdt); sdt->sdt.statem.suerm_state = SDT_STATE_IDLE; } STATIC INLINE int sdt_lsc_link_failure(queue_t * q, sdt_t * sdt) { return sdt_lsc_link_failure_ind(q, sdt); } STATIC INLINE void sdt_daedr_start(queue_t * q, sdt_t * sdt) { sdt->sdt.statem.daedr_state = SDT_STATE_IN_SERVICE; sdt->sdl.statem.rx_state = SDL_STATE_IN_SERVICE; sdt->sdl.config.ifflags |= (SDL_IF_UP | SDL_IF_RX_RUNNING); } STATIC INLINE void sdt_eim_start(queue_t * q, sdt_t * sdt) { sdt->sdt.statem.Ce = 0; sdt->sdt.statem.interval_error = 0; sdt->sdt.statem.su_received = 0; sdt_timer_start(q, t8); sdt->sdt.statem.eim_state = SDT_STATE_MONITORING; } STATIC INLINE void sdt_suerm_start(queue_t * q, sdt_t * sdt) { if (sdt->option.popt & SS7_POPT_HSL) sdt_eim_start(q, sdt); else { sdt->sdt.statem.Cs = 0; sdt->sdt.statem.Ns = 0; sdt->sdt.statem.suerm_state = SDT_STATE_IN_SERVICE; } } STATIC INLINE void sdt_aerm_set_ti_to_tie(queue_t * q, sdt_t * sdt) { if (sdt->sdt.statem.aerm_state == SDT_STATE_IDLE) sdt->sdt.statem.Ti = sdt->sdt.config.Tie; } STATIC INLINE void sdt_aerm_start(queue_t * q, sdt_t * sdt) { sdt->sdt.statem.Ca = 0; sdt->sdt.statem.aborted_proving = 0; sdt->sdt.statem.aerm_state = SDT_STATE_MONITORING; } STATIC INLINE void sdt_iac_correct_su(queue_t * q, sdt_t * sdt) { sdt_iac_correct_su_ind(q, sdt); } STATIC INLINE void sdt_iac_abort_proving(queue_t * q, sdt_t * sdt) { sdt_iac_abort_proving_ind(q, sdt); } STATIC INLINE void sdt_daedt_start(queue_t * q, sdt_t * sdt) { sdt_t *sdt = SDT_PRIV(q); sdt->sdt.statem.daedt_state = SDT_STATE_IN_SERVICE; sdt->sdl.statem.tx_state = SDL_STATE_IN_SERVICE; sdt->sdl.config.ifflags |= (SDL_IF_UP | SDL_IF_TX_RUNNING); } STATIC INLINE void sdt_aerm_set_ti_to_tin(queue_t * q, sdt_t * sdt) { if (sdt->sdt.statem.aerm_state == SDT_STATE_IDLE) sdt->sdt.statem.Ti = sdt->sdt.config.Tin; } STATIC INLINE void sdt_aerm_su_in_error(queue_t * q, sdt_t * sdt) { if (sdt->sdt.statem.aerm_state == SDT_STATE_MONITORING) { sdt->sdt.statem.Ca++; if (sdt->sdt.statem.Ca == sdt->sdt.statem.Ti) { sdt->sdt.statem.aborted_proving = 1; sdt_iac_abort_proving(q, sdt); sdt->sdt.statem.Ca--; sdt->sdt.statem.aerm_state = SDT_STATE_IDLE; } } } STATIC INLINE void sdt_aerm_correct_su(queue_t * q, sdt_t * sdt) { if (sdt->sdt.statem.aerm_state == SDT_STATE_IDLE) { if (sdt->sdt.statem.aborted_proving) { sdt_iac_correct_su(q, sdt); sdt->sdt.statem.aborted_proving = 0; } } } STATIC INLINE void sdt_suerm_su_in_error(queue_t * q, sdt_t * sdt) { if (sdt->sdt.statem.suerm_state == SDT_STATE_IN_SERVICE) { sdt->sdt.statem.Cs++; if (sdt->sdt.statem.Cs >= sdt->sdt.config.T) { sdt_lsc_link_failure(q, sdt); sdt->sdt.statem.Ca--; sdt->sdt.statem.suerm_state = SDT_STATE_IDLE; return; } sdt->sdt.statem.Ns++; if (sdt->sdt.statem.Ns >= sdt->sdt.config.D) { sdt->sdt.statem.Ns = 0; if (sdt->sdt.statem.Cs) sdt->sdt.statem.Cs--; } } } STATIC INLINE void sdt_eim_su_in_error(queue_t * q, sdt_t * sdt) { if (sdt->sdt.statem.eim_state == SDT_STATE_MONITORING) sdt->sdt.statem.interval_error = 1; } STATIC INLINE void sdt_suerm_correct_su(queue_t * q, sdt_t * sdt) { if (sdt->sdt.statem.suerm_state == SDT_STATE_IN_SERVICE) { sdt->sdt.statem.Ns++; if (sdt->sdt.statem.Ns >= sdt->sdt.config.D) { sdt->sdt.statem.Ns = 0; if (sdt->sdt.statem.Cs) sdt->sdt.statem.Cs--; } } } STATIC INLINE void sdt_eim_correct_su(queue_t * q, sdt_t * sdt) { if (sdt->sdt.statem.eim_state == SDT_STATE_MONITORING) sdt->sdt.statem.su_received = 1; } STATIC INLINE void sdt_daedr_correct_su(queue_t * q, sdt_t * sdt) { sdt_eim_correct_su(q, sdt); sdt_suerm_correct_su(q, sdt); sdt_aerm_correct_su(q, sdt); } /* * Hooks to underlying driver * ----------------------------------- */ STATIC INLINE void sdt_daedr_su_in_error(queue_t * q, sdt_t * sdt) { sdt_t *sdt = SDT_PRIV(q); if (sdt->sdt.statem.daedr_state != SDT_STATE_IDLE) { /* cancel compression */ if (sdt->sdt.rx_cmp) { printd(("SU in error\n")); freemsg(xchg(&sdt->sdt.rx_cmp, NULL)); } sdt_eim_su_in_error(q, sdt); sdt_suerm_su_in_error(q, sdt); sdt_aerm_su_in_error(q, sdt); sdt->sdt.stats.rx_sus_in_error++; } } STATIC INLINE void sdt_daedr_received_bits(sdt_t * sdt, mblk_t * mp) { if (sdt->sdt.statem.daedr_state != SDT_STATE_IDLE) { int i, len, mlen = (sdt->option.popt & SS7_POPT_XSN) ? 8 : 5, min_len = mlen - 2, max_len = min_len + sdt->sdt.config.m + 1; printd(("%s: %p: SDT_DAEDR_RECEIVED_BITS [%d]<-\n", SDT_MOD_NAME, sdt, msgdsize(mp))); if (mp) { int lmax, li, sif; len = msgdsize(mp); if (len > max_len || min_len > len) { if (len > max_len) sdt->sdt.stats.rx_frame_too_long++; if (len < min_len) sdt->sdt.stats.rx_frame_too_short++; goto error_su; } if (sdt->option.popt & SS7_POPT_XSN) { lmax = 0x1ff; li = (mp->b_rptr[4] | (mp->b_rptr[5] << 8)); } else { lmax = 0x3f; li = mp->b_rptr[2]; } sif = len - min_len; if ((li &= lmax) == sif || (li == lmax && sif > lmax)) goto good_su; sdt->sdt.stats.rx_length_error++; error_su: sdt_daedr_su_in_error(NULL, sdt); freemsg(mp); if (sdt->sdt.rx_cmp) freemsg(xchg(&sdt->sdt.rx_cmp, NULL)); return; good_su: if (sdt->sdt.rx_cmp) { if (len > mlen || len < min_len || len != msgdsize(sdt->sdt.rx_cmp)) goto no_match; for (i = 0; i < len; i++) if (mp->b_rptr[i] != sdt->sdt.rx_cmp->b_rptr[i]) goto no_match; sdt->sdt.rx_repeat++; sdt->sdt.stats.rx_sus_compressed++; freemsg(mp); return; no_match: if (sdt->sdt.rx_repeat) { #if 0 mblk_t *cp; if ((cp = dupb(sdt->sdt.rx_cmp))) { if (sdt_rc_signal_unit_ind(NULL, sdt, cp, sdt->sdt.rx_repeat) != QR_ABSORBED) { sdt->sdt.stats.rx_buffer_overflows++; sdt->sdl.stats.rx_buffer_overflows++; freemsg(cp); } } #endif sdt_daedr_correct_su(NULL, sdt); sdt->sdt.rx_repeat = 0; } } if (len <= mlen) { if (sdt->sdt.rx_cmp || (sdt->sdt.rx_cmp = allocb(mlen, BPRI_HI))) { bcopy(mp->b_rptr, sdt->sdt.rx_cmp->b_rptr, len); sdt->sdt.rx_cmp->b_wptr = sdt->sdt.rx_cmp->b_rptr + len; sdt->sdt.rx_repeat = 0; } } if (sdt_rc_signal_unit_ind(NULL, sdt, mp, 1) != QR_ABSORBED) { sdt->sdt.stats.rx_buffer_overflows++; sdt->sdl.stats.rx_buffer_overflows++; freemsg(mp); } sdt_daedr_correct_su(NULL, sdt); } else swerr(); return; } } STATIC INLINE mblk_t *sdt_daedt_transmission_request(queue_t * q) { sdt_t *sdt = SDT_PRIV(q); mblk_t *mp = NULL; if (sdt->sdt.statem.daedt_state != SDT_STATE_IDLE) { if ((mp = bufq_dequeue(&sdt->sdt.tb))) { int len = msgdsize(mp); int hlen = (sdt->option.popt & SS7_POPT_XSN) ? 6 : 3; int mlen = hlen + 2; if (!sdt->sdt.tb.q_count) qenable(sdt->iq); /* back-enable */ if (mlen < hlen) goto dont_repeat; if (mlen == hlen + 1 || mlen == hlen + 2) { int li, sio; if (sdt->option.popt & SS7_POPT_XSN) { li = ((mp->b_rptr[5] << 8) | mp->b_rptr[4]) & 0x1ff; sio = mp->b_rptr[6]; } else { li = mp->b_rptr[2] & 0x3f; sio = mp->b_rptr[3]; } if (li != mlen - hlen) goto dont_repeat; switch (sio) { case 0x0: /* SIO */ case 0x1: /* SIN */ case 0x2: /* SIE */ case 0x3: /* SIOS */ case 0x4: /* SIPO */ break; case 0x5: /* SIB */ default: goto dont_repeat; } } if (len <= mlen && (sdt->sdt.tx_cmp || (sdt->sdt.tx_cmp = allocb(mlen, BPRI_HI)))) { mblk_t *cp = sdt->sdt.tx_cmp; cp->b_rptr = cp->b_datap->db_base; bcopy(mp->b_rptr, cp->b_rptr, len); cp->b_wptr = cp->b_rptr + len; /* always correct length indicator */ if (sdt->option.popt && SS7_POPT_XSN) { cp->b_rptr[4] &= 0x00; cp->b_rptr[5] &= 0xfe; cp->b_rptr[4] += (len - hlen); } else { cp->b_rptr[2] &= 0xc0; cp->b_rptr[2] += (len - hlen); } sdt->sdt.tx_repeat = 0; goto done; } dont_repeat: if (sdt->sdt.tx_cmp) { freemsg(xchg(&sdt->sdt.tx_cmp, NULL)); sdt->sdt.tx_repeat = 0; } } else if ((sdt->sdl.config.ifclock != SDL_CLOCK_NONE) && sdt->sdt.tx_cmp && (mp = dupmsg(sdt->sdt.tx_cmp))) { sdt->sdt.stats.tx_sus_repeated++; sdt->sdt.tx_repeat++; } } done: if (!mp) { sdt->sdt.stats.tx_buffer_overflows++; sdt->sdl.stats.tx_buffer_overflows++; } return (mp); } /* * ======================================================================== * * EVENTS * * ======================================================================== */ /* * RX WAKEUP * ----------------------------------- */ STATIC int sdt_rx_wakeup(queue_t * q) { (void) q; return (0); } /* * TX WAKEUP * ----------------------------------- */ STATIC int sdt_tx_wakeup(queue_t * q) { sdt_t *sdt = SDT_PRIV(q); if (sdt->sdt.statem.daedt_state != SDT_STATE_IDLE) { mblk_t *mp, *dp; long tdiff; int size; while (canputnext(sdt->iq)) { switch (sdt->sdl.config.ifclock) { case SDL_CLOCK_TICK: case SDL_CLOCK_SHAPER: if ((tdiff = jiffies - sdt->sdl.timestamp) < 0) { /* throttle back for a t9 */ printd(("%s: %p: %s throttling back a tick\n", SDT_MOD_NAME, sdt, __FUNCTION__)); if (!sdt->sdl.timer.t9) sdt_timer_start(q, t9); return (0); } if (tdiff > 0) { sdt->sdl.bytecount = 0; sdt->sdl.timestamp = jiffies; } } if (sdt->t.serv_type == T_CLTS) { if (!(mp = t_unitdata_req(q))) goto overflow; } else { if (!(mp = t_data_req(q))) goto overflow; } if (!(dp = sdt_daedt_transmission_request(q))) goto done; size = msgdsize(dp); linkb(mp, dp); printd(("%s: %p: T_UNITDATA_REQ [%d] ->\n", SDT_MOD_NAME, sdt, size)); putnext(sdt->iq, mp); sdt->sdt.stats.tx_bytes += size; sdt->sdt.stats.tx_sus++; switch (sdt->sdl.config.ifclock) { case SDL_CLOCK_TICK: case SDL_CLOCK_SHAPER: sdt->sdl.bytecount += size; ensure(sdt->sdl.tickbytes > 0, sdt->sdl.tickbytes = 1); while (sdt->sdl.bytecount >= sdt->sdl.tickbytes) { sdt->sdl.bytecount -= sdt->sdl.tickbytes; sdt->sdl.timestamp++; } } switch (sdt->sdl.config.ifclock) { case SDL_CLOCK_TICK: if (!(sdt->option.popt & SS7_POPT_PCR) && sdt->sdt.tx_cmp && !sdt->sdt.tb.q_count) { if (!sdt->sdl.timer.t9) sdt_timer_start(q, t9); printd(("%s: %p: %s sleeping for a tick\n", SDT_MOD_NAME, sdt, __FUNCTION__)); return (0); } case SDL_CLOCK_SHAPER: continue; } return (0); } return (0); done: freemsg(mp); return (0); overflow: printd(("%s: %p: %s buffer overflow\n", SDT_MOD_NAME, sdt, __FUNCTION__)); sdt->sdt.stats.tx_buffer_overflows++; sdt->sdl.stats.tx_buffer_overflows++; return (0); } return (0); } /* * ------------------------------------------------------------------------- * * Timer Events * * ------------------------------------------------------------------------- */ /* * T8 EXPIRY * ----------------------------------- */ STATIC int sdt_t8_timeout(sdt_t * sdt) { sdt_t *sdt = SDT_PRIV(q); int err; if (sdt->sdt.statem.eim_state == SDT_STATE_MONITORING) { sdt_timer_start(sdt, t8); if (sdt->sdt.statem.su_received) { sdt->sdt.statem.su_received = 0; if (!sdt->sdt.statem.interval_error) { if ((sdt->sdt.statem.Ce -= sdt->sdt.config.De) < 0) sdt->sdt.statem.Ce = 0; return (QR_DONE); } } sdt->sdt.statem.Ce += sdt->sdt.config.Ue; if (sdt->sdt.statem.Ce > sdt->sdt.config.Te) { if ((err = sdt_lsc_link_failure(NULL, sdt))) { sdt->sdt.statem.Ce -= sdt->sdt.config.Ue; return (err); } sdt->sdt.statem.eim_state = SDT_STATE_IDLE; } sdt->sdt.statem.interval_error = 0; } return (QR_DONE); } /* * T9 EXPIRY * ----------------------------------- */ STATIC int sdt_t9_timeout(queue_t * q) { return sdt_tx_wakeup(SDT_PRIV(q)->iq); } /* * ------------------------------------------------------------------------- * * LM User -> LM Provider (SS7IP) Primitives * * ------------------------------------------------------------------------- */ /* * LMI_INFO_REQ * ----------------------------------- */ STATIC int lmi_info_req(queue_t * q, mblk_t * mp) { lmi_info_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr < mp->b_rptr + sizeof(*p)) goto emsgsize; return lmi_info_ack(q, NULL, 0); emsgsize: return lmi_error_ack(q, LMI_INFO_REQ, LMI_PROTOSHORT, EMSGSIZE); } /* * LMI_ATTACH_REQ * ----------------------------------- * Attachment requires a local address as a PPA (Physical Point of Attachment). The local * address is necessary for T_BIND on both connection oriented and connectionless transports. * For style 2 transports, we copy the PPA and bind the transport with T_BIND. Style 1 * transports are already bound and this primitive is invalid for style 1 transports. */ STATIC int lmi_attach_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); lmi_attach_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr < mp->b_rptr + sizeof(*p)) goto emsgsize; if (sdt->state == LMI_UNUSABLE) goto eagain; if (sdt->style != LMI_STYLE2) goto eopnotsupp; if (sdt->state != LMI_UNATTACHED) goto outstate; sdt->state = LMI_ATTACH_PENDING; if (mp->b_wptr < mp->b_rptr + sizeof(*p) + sdt->t.add_size) goto badppa; bcopy(p->lmi_ppa, &sdt->t.loc, sdt->t.add_size); /* start bind in motion */ return t_bind_req(q); badppa: ptrace(("%s: PROTO: bad ppa (too short)\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_ATTACH_REQ, LMI_BADPPA, EMSGSIZE); outstate: ptrace(("%s: PROTO: out of state\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_ATTACH_REQ, LMI_OUTSTATE, EPROTO); eopnotsupp: ptrace(("%s: PROTO: primitive not support for style\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_ATTACH_REQ, LMI_NOTSUPP, EOPNOTSUPP); eagain: /* wait for stream to become usable */ return (-EAGAIN); emsgsize: ptrace(("%s: PROTO: M_PROTO block too short\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_ATTACH_REQ, LMI_PROTOSHORT, EMSGSIZE); } /* * LMI_DETACH_REQ * ----------------------------------- * Detaching requires that the stream be detached from the local address. This results in a * T_UNBIND connection oriented and connectionless transports. For style 2 transports, we * perform the unbinding operation. For style 1 transports, we were already bound and this * primitive is invalid for style 1 transports. */ STATIC int lmi_detach_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); lmi_detach_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr < mp->b_rptr + sizeof(*p)) goto emsgsize; if (sdt->state == LMI_UNUSABLE) goto eagain; if (sdt->style != LMI_STYLE2) goto eopnotsupp; if (sdt->state != LMI_DISABLED) goto outstate; sdt->state = LMI_DETACH_PENDING; bzero(&sdt->t.loc, sdt->t.add_size); /* start unbind in motion */ return t_unbind_req(q); outstate: ptrace(("%s: PROTO: out of state\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_DETACH_REQ, LMI_OUTSTATE, EPROTO); eopnotsupp: ptrace(("%s: PROTO: primitive not support for style\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_ATTACH_REQ, LMI_NOTSUPP, EOPNOTSUPP); eagain: /* wait for stream to become usable */ return (-EAGAIN); emsgsize: ptrace(("%s: PROTO: M_PROTO block too short\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_DETACH_REQ, LMI_PROTOSHORT, EMSGSIZE); } /* * LMI_ENABLE_REQ * ----------------------------------- * For style 1 connection oriented transports, we are completely connected and can simply * acknowledge the enable. For style 1 connectionless transports we need to know the remote * address of the peer and require a remote address as part of the enable. For style 2 * connection oriented transports we need to know the remote address for connection or if no * remote address is provided, simply stay listening. (Note: style 2 connection oriented * transports are quite insecure for this reason. Style 1 is preferrable for connection * oriented transports for a number of reasons). */ STATIC int lmi_enable_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); lmi_enable_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr < mp->b_rptr + sizeof(*p)) goto emsgsize; if (sdt->state == LMI_UNUSABLE) goto eagain; if (sdt->state != LMI_DISABLED) goto outstate; sdt->state = LMI_ENABLE_PENDING; switch (sdt->t.serv_type) { case T_CLTS: assure(sdt->t.state == TS_IDLE); if (mp->b_wptr < mp->b_rptr + sizeof(*p) + sdt->t.add_size) goto badppa; bcopy(p->lmi_rem, &sdt->t.rem, sdt->t.add_size); return lmi_enable_con(q); case T_COTS: case T_COTS_ORD: switch (sdt->style) { case LMI_STYLE1: assure(sdt->t.state == TS_DATA_XFER); return lmi_enable_con(q); case LMI_STYLE2: assure(sdt->t.state == TS_IDLE); if (mp->b_wptr == mp->b_rptr + sizeof(*p)) /* wait for T_CONN_IND */ return (QR_DONE); if (mp->b_wptr < mp->b_rptr + sizeof(*p) + sdt->t.add_size) goto badppa; bcopy(p->lmi_rem, &sdt->t.rem, sdt->t.add_size); /* start connection in motion */ return t_conn_req(q); } } badppa: ptrace(("%s: PROTO: bad ppa (too short)\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_ATTACH_REQ, LMI_BADPPA, EMSGSIZE); outstate: ptrace(("%s: PROTO: out of state\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_ENABLE_REQ, LMI_OUTSTATE, EPROTO); eagain: /* wait for stream to become usable */ return (-EAGAIN); emsgsize: ptrace(("%s: PROTO: M_PROTO block too short\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_ENABLE_REQ, LMI_PROTOSHORT, EMSGSIZE); } /* * LMI_DISABLE_REQ * ----------------------------------- * For style 1 connection oriented transports, we stay completely connected and can simply * acknowledge the disable. For style 1 connectionless transports we may also simply * acknowledge the disable. Fort style 2 connection oriented transports we need to * disconnect the connection. */ STATIC int lmi_disable_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); lmi_disable_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr < mp->b_rptr + sizeof(*p)) goto emsgsize; if (sdt->state == LMI_UNUSABLE) goto eagain; if (sdt->state != LMI_ENABLED) goto outstate; sdt->state = LMI_DISABLE_PENDING; if (sdt->style == LMI_STYLE1 || sdt->t.serv_type == T_CLTS) return lmi_disable_con(q); /* start disconnect in motion */ if (sdt->t.serv_type == T_COTS_ORD) return t_ordrel_req(q); return t_discon_req(q, 0); outstate: ptrace(("%s: PROTO: out of state\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_DISABLE_REQ, LMI_OUTSTATE, EPROTO); eagain: /* wait for stream to become available */ return (-EAGAIN); emsgsize: ptrace(("%s: PROTO: M_PROTO block too short\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_DISABLE_REQ, LMI_PROTOSHORT, EMSGSIZE); } /* * LMI_OPTMGMT_REQ * ----------------------------------- */ STATIC int lmi_optmgmt_req(queue_t * q, mblk_t * mp) { lmi_optmgmt_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr < mp->b_rptr + sizeof(*p)) goto emsgsize; goto eopnotsupp; eopnotsupp: ptrace(("%s: PROTO: Primitive is not supported\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_OPTMGMT_REQ, LMI_NOTSUPP, EOPNOTSUPP); emsgsize: ptrace(("%s: PROTO: M_PROTO block too short\n", SDT_MOD_NAME)); return lmi_error_ack(q, LMI_OPTMGMT_REQ, LMI_PROTOSHORT, EMSGSIZE); } /* * ------------------------------------------------------------------------- * * SDT User -> SDT Provider (SS7IP) Primitives * * ------------------------------------------------------------------------- */ /* * M_DATA * ----------------------------------- */ STATIC int sdt_send_data(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); // printd(("%s: %p: Queuing SDT data\n", SDT_MOD_NAME, sdt)); bufq_queue(&sdt->sdt.tb, mp); if (sdt->sdt.tx_cmp) { freemsg(xchg(&sdt->sdt.tx_cmp, NULL)); sdt->sdt.tx_repeat = 0; } return (QR_ABSORBED); } /* * SDT_DAEDT_TRANSMISSION_REQ: * ----------------------------------- */ STATIC int sdt_daedt_transmission_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); // printd(("%s: %p: Queuing SDT data\n", SDT_MOD_NAME, sdt)); bufq_queue(&sdt->sdt.tb, mp->b_cont); return (QR_TRIMMED); } /* * SDT_DAEDT_START_REQ: * ----------------------------------- */ STATIC int sdt_daedt_start_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); sdt_daedt_start(q, sdt); return (QR_DONE); } /* * SDT_DAEDR_START_REQ: * ----------------------------------- */ STATIC int sdt_daedr_start_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); sdt_daedr_start(q, sdt); return (QR_DONE); } /* * SDT_AERM_START_REQ: * ----------------------------------- */ STATIC int sdt_aerm_start_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); sdt_aerm_start(q, sdt); return (QR_DONE); } /* * SDT_AERM_STOP_REQ: * ----------------------------------- */ STATIC int sdt_aerm_stop_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); sdt_aerm_stop(q, sdt); return (QR_DONE); } /* * SDT_AERM_SET_TI_TO_TIN_REQ: * ----------------------------------- */ STATIC int sdt_aerm_set_ti_to_tin_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); sdt_aerm_set_ti_to_tin(q, sdt); return (QR_DONE); } /* * SDT_AERM_SET_TI_TO_TIE_REQ: * ----------------------------------- */ STATIC int sdt_aerm_set_ti_to_tie_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); sdt_aerm_set_ti_to_tie(q, sdt); return (QR_DONE); } /* * SDT_SUERM_START_REQ: * ----------------------------------- */ STATIC int sdt_suerm_start_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); sdt_suerm_start(q, sdt); return (QR_DONE); } /* * SDT_SUERM_STOP_REQ: * ----------------------------------- */ STATIC int sdt_suerm_stop_req(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); if (sdt->state != LMI_ENABLED) return m_error(q, EPROTO); sdt_suerm_stop(q, sdt); return (QR_DONE); } // #define ACB56_MOD_ID 0x1111 // #define ACB56_MOD_NAME "acb56" // #define ACB56_CMAJOR 0 /* FIXME: pick something */ // #define ACB56_NMINOR 256 /* as many as possible */ #ifndef ACB56_CMAJOR #define ACB56_CMAJOR 248 /* FIXME: pick something */ #endif #define ACB56_NMINOR 5 /* as many as possible */ #ifndef LSSU_SIB #define LSSU_SIB 0x5 #endif static int debug = -1; static int io[] = { -1, -1, -1, -1, -1 }; static int irq[] = { -1, -1, -1, -1, -1 }; static int dma_rx[] = { -1, -1, -1, -1, -1 }; static int dma_tx[] = { -1, -1, -1, -1, -1 }; static int mode[] = { -1, -1, -1, -1, -1 }; static int clock[] = { -1, -1, -1, -1, -1 }; MODULE_PARM(debug, "i"); /* debug flag */ MODULE_PARM(io, "1-5i"); /* i/o port for the i'th card */ MODULE_PARM(irq, "1-5i"); /* irq for the i'th card */ MODULE_PARM(dma_rx, "1-5i"); /* dma for the i'th card */ MODULE_PARM(dma_tx, "1-5i"); /* dma for the i'th card */ MODULE_PARM(mode, "i-5i"); /* interface mode */ MODULE_PARM(clock, "i-5i"); /* clock source */ enum { ACB56_MODE_DTE, ACB56_MODE_DCE, ACB56_MODE_LOOP, ACB56_MODE_JACK, ACB56_MODE_ECHO, ACB56_MODE_BOTH }; static const int mode_map[] = { -1, /* SDL_MODE_NONE */ -1, /* SDL_MODE_DSU */ -1, /* SDL_MODE_CSU */ ACB56_MODE_DTE, /* SDL_MODE_DTE */ ACB56_MODE_DCE, /* SDL_MODE_DCE */ -1, /* SDL_MODE_CLIENT */ -1, /* SDL_MODE_SERVER */ -1, /* SDL_MODE_PEER */ ACB56_MODE_ECHO, /* SDL_MODE_ECHO */ -1, /* SDL_MODE_REM_LB */ ACB56_MODE_LOOP, /* SDL_MODE_LOC_LB */ ACB56_MODE_BOTH, /* SDL_MODE_LB_ECHO */ ACB56_MODE_JACK /* SDL_MODE_TEST */ }; enum { ACB56_CLOCK_EXT, ACB56_CLOCK_INT, ACB56_CLOCK_DPLL }; static const int clock_map[] = { -1, /* SDL_CLOCK_NONE */ ACB56_CLOCK_INT, /* SDL_CLOCK_INT */ ACB56_CLOCK_EXT, /* SDL_CLOCK_EXT */ -1, /* SDL_CLOCK_LOOP */ -1, /* SDL_CLOCK_MASTER */ -1, /* SDL_CLOCK_SLAVE */ ACB56_CLOCK_DPLL, /* SDL_CLOCK_DPLL */ -1, /* SDL_CLOCK_ABR */ -1 /* SDL_CLOCK_SHAPER */ }; static int acb56_cmajor = ACB56_CMAJOR; typedef struct acb56_stats { dev_ulong repeated_sus; dev_ulong compressed_sus; dev_ulong recv_fisus; dev_ulong recv_lssus; dev_ulong recv_msus; dev_ulong parity_errors; dev_ulong cts_transitions; dev_ulong dcd_transitions; dev_ulong cha_ext_status; dev_ulong cha_rx_char_avail; dev_ulong cha_rx_sp_cond; dev_ulong cha_tx_buf_empty; dev_ulong chb_ext_status; dev_ulong chb_rx_char_avail; dev_ulong chb_rx_sp_cond; dev_ulong chb_tx_buf_empty; dev_ulong interrupts; } acb56_stats_t; /* * ========================================================================= * * INTERRUPT SERVICE ROUTINES * * ========================================================================= */ static inline void acb56_tx_setup_next_frame(sdt_t * sdt) { sdt->sdt.stats.tx_sus++; sdt->sdt.stats.tx_bytes += 4; if (bufq_length(&sdt->dev.tinputq)) { freemsg(xchg(&sdt->dev.tx_msg, NULL)); sdt->dev.tx_msg = bufq_dequeue(&sdt->dev.tinputq); } else { int len = msgdsize(sdt->dev.tx_msg); if (len > 5 || (len > 3 && sdt->dev.tx_msg->b_rptr[3] == LSSU_SIB)) { /* its an MSU or SIB, make FISU out of it */ sdt->dev.tx_msg->b_wptr = sdt->dev.tx_msg->b_rptr + 3; sdt->dev.tx_msg->b_rptr[2] = 0; sdt->stats.repeated_sus++; } } sdt->dev.tx_buf = sdt->dev.tx_msg->b_rptr; sdt->dev.tx_max = sdt->dev.tx_msg->b_wptr; } static inline void acb56_tx_underrun_eom(sdt_t * sdt) { if (sdt->dev.rr0 & 0x40) { /* set */ sdt->dev.tx_buf = sdt->dev.tx_msg->b_rptr; sdt->dev.tx_error = 1; sdt->sdt.stats.tx_aborts++; sdt->sdt.stats.tx_underruns++; sdt->sdl.state.tx_underruns++; sdt->sdt.stats.tx_sus_in_error++; sdt->sdt.stats.tx_bytes += 3; sdt->dev.rr0 &= ~0x04; /* clear indication */ return; } else { return acb56_tx_setup_next_frame(sdt); } } static inline void acb56_sync_hunt(sdt_t * sdt) { int actrl = sdt->dev.iobase + 1; if (sdt->dev.rx_octet_mode && !sdt->dev.rr0 & 0x10) { sdt->dev.rx_octet_mode = 0; /* we synced */ outb(0x0f, actrl); outb(sdt->dev.regs[0x0f] &= ~0x02, actrl); /* turn of octet mode */ } else if ((sdt->dev.rr0 & 0x10) && (sdt->dev.iface.ifclock != SDL_CLOCK_DPLL)) { sdt->dev.rx_octet_count = 0; sdt->dev.rx_octet_mode = 1; outb(0x0f, actrl); outb(sdt->dev.regs[0x0f] |= 0x02, actrl); /* turn on octet mode */ } sdt->sdt.stats.rx_sync_transitions++; } static inline void acb56_break_abort(sdt_t * sdt) { sdt->sdt.stats.rx_aborts++; } static inline void acb56_dcd(sdt_t * sdt) { if ((++sdt->stats.dcd_transitions) & 0x1) { sdt->sdl.stats.lead_dcd_lost++; sdt->sdt.stats.carrier_dcd_lost++; } } static inline void acb56_cts(sdt_t * sdt) { if ((++sdt->stats.cts_transitions) & 0x1) { sdt->sdl.stats.lead_cts_lost++; sdt->sdt.stats.carrier_cts_lost++; } } static inline void acb56_zero_count(sdt_t * sdt) { if (!sdt->dev.rx_octet_count++ & 0x0000007f || (sdt->dev.iface.ifmode == SDL_MODE_DTE)) { sdt->sdt.stats.rx_sus_in_error++; sdt->dev.ucalls->daedr_N_octets(&sdt->dev); } sdt->sdt.stats.rx_bits_octet_counted++; } static inline void acb56_frame_error(sdt_t * sdt) { sdt->dev.rx_error = 0; sdt->dev.rx_buf = sdt->dev.rx_msg->b_rptr; if (sdt->dev.rx_octet_mode) return; /* only a good frame counts in octet mode */ sdt->dev.cp_msg->b_wptr = sdt->dev.cp_msg->b_rptr; sdt->sdt.stats.rx_sus++; sdt->sdt.stats.rx_bytes += 2; sdt->sdt.stats.rx_sus_in_error++; sdt_daedr_su_in_error(NULL, sdt); } static inline void acb56_parity_error(sdt_t * sdt) { sdt->stats.parity_errors++; sdt->dev.rx_error = 1; } static inline void acb56_rx_overrun(sdt_t * sdt) { sdt->sdt.stats.rx_overruns++; sdt->sdl.stats.rx_overruns++; sdt->dev.rx_error = 1; } static inline void acb56_crc_error(sdt_t * sdt) { if (sdt->dev.rx_octet_mode) return; /* only a good frame counts in octet mode */ sdt->sdt.stats.rx_crc_errors++; acb56_frame_error(sdt); } static inline void acb56_buffer_error(sdt_t * sdt) { if (sdt->dev.rx_octet_mode) return; /* only a good frame counts in octet mode */ sdt->sdt.stats.rx_buffer_overflows++; sdt->sdl.stats.rx_buffer_overflows++; acb56_frame_error(sdt); } static inline void acb56_short_error(sdt_t * sdt) { if (sdt->dev.rx_octet_mode) return; /* only a good frame counts in octet mode */ sdt->sdt.stats.rx_frame_too_short++; acb56_frame_error(sdt); } static inline void acb56_long_error(sdt_t * sdt) { if (sdt->dev.rx_octet_mode) return; /* only a good frame counts in octet mode */ sdt->sdt.stats.rx_frame_too_long++; acb56_frame_error(sdt); } static inline void acb56_length_error(sdt_t * sdt) { if (sdt->dev.rx_octet_mode) return; /* only a good frame counts in octet mode */ sdt->sdt.stats.rx_length_error++; acb56_frame_error(sdt); } static inline void acb56_rx_setup_next_frame(sdt_t * sdt) { int len = sdt->dev.rx_buf - sdt->dev.rx_msg->b_rptr; sdt->dev.rx_buf = sdt->dev.rx_msg->b_rptr; sdt->sdt.stats.rx_sus++; if (len == 3) sdt->stats.recv_fisus++; if (len >= 6) sdt->stats.recv_msus++; if (len >= 4) sdt->stats.recv_lssus++; sdt->sdt.stats.rx_bytes += 2; } static inline void acb56_residue_error(sdt_t * sdt) { sdt->sdt.stats.rx_residue_errors++; acb56_frame_error(sdt); } static inline void acb56_end_of_frame(sdt_t * sdt) { if (sdt->dev.rx_error) return acb56_frame_error(sdt); { mblk_t *mp = sdt->dev.rx_msg, *mc = sdt->dev.cp_msg; unsigned int len = (unsigned int) (sdt->dev.rx_buf - mp->b_rptr); unsigned char li = mp->b_rptr[2] & 0x3f; if (len < 3) return acb56_short_error(sdt); if (len > sdt->sdt.config.m + 4) return acb56_long_error(sdt); if (li != (len > 63 + 3 ? 63 + 3 : len)) return acb56_length_error(sdt); mp->b_wptr = mp->b_rptr + len; if (len < 6) { int clen = mc->b_wptr - mc->b_rptr; if (len == clen && !memcmp(mc->b_rptr, mp->b_rptr, len)) { sdt->stats.compressed_sus++; acb56_rx_setup_next_frame(sdt); sdt_daedr_correct_su(NULL, sdt); return; } else { memcpy(mc->b_rptr, mp->b_rptr, len); mc->b_wptr = mc->b_rptr + len; } } else mc->b_wptr = mc->b_rptr; sdt->dev.rx_octet_mode = 0; acb56_rx_setup_next_frame(sdt); sdt_daedr_received_bits(sdt, mp); if (!(sdt->dev.rx_msg = sdt_allocb(NULL, FASTBUF, BPRI_MED))) return acb56_buffer_error(sdt); sdt->dev.rx_buf = sdt->dev.rx_msg->b_wptr = sdt->dev.rx_msg->b_rptr; sdt->dev.rx_max = sdt->dev.rx_msg->b_datap->db_lim; return; } } static inline void acb56_frame_overflow_check(sdt_t * sdt) { int actrl = sdt->dev.iobase + 1; /* check for frame overflow */ if (sdt->dev.rx_buf > sdt->dev.rx_max) { /* FIGURE 11/Q.703 (sheet 1 of 2) "m+7 octets without flags" */ if (!sdt->dev.rx_octet_mode && sdt->dev.iface.ifclock != SDL_CLOCK_DPLL) { /* can't octet count on DPLL! */ sdt->dev.rx_octet_count = 0; sdt->dev.rx_octet_mode = 1; outb(0x0f, actrl); outb(sdt->dev.regs[0x0f] |= 0x02, actrl); /* octet counting isr */ outb(0x03, actrl); outb(sdt->dev.regs[0x03] |= 0x10, actrl); /* force flag hunt */ acb56_rx_setup_next_frame(sdt); sdt->dev.ucalls->daedr_loss_of_sync(&sdt->dev); } } } static void acb56_isr_cha_tx_buf_empty(sdt_t * sdt) { sdt->stats.cha_tx_buf_empty++; } static void acb56_isr_cha_ext_status(sdt_t * sdt) { unsigned char rr0; register int actrl = sdt->dev.iobase + 1; rr0 = sdt->dev.rr0 & ~0x02; outb(0x00, actrl); sdt->dev.rr0 = inb(actrl); outb(0x00, actrl); outb(0x10, actrl); outb(0x00, actrl); outb(0x10, actrl); /* debounce */ rr0 ^= sdt->dev.rr0 & 0xfa; if (rr0) { if (rr0 & 0x40) acb56_tx_underrun_eom(sdt); if (rr0 & 0x10) acb56_sync_hunt(sdt); if (rr0 & 0x80) acb56_break_abort(sdt); if (rr0 & 0x08) acb56_dcd(sdt); if (rr0 & 0x20) acb56_cts(sdt); } else acb56_zero_count(sdt); sdt->stats.cha_ext_status++; } static void acb56_isr_cha_rx_char_avail(sdt_t * sdt) { register int adata = sdt->dev.iobase; register int i = 0; if (sdt->dev.rx_buf) { /* collect bytes */ for (i = 0; i < 4; i++) *(sdt->dev.rx_buf++) = inb(adata); acb56_frame_overflow_check(sdt); } sdt->sdt.stats.rx_bytes += 4; sdt->stats.cha_rx_char_avail++; } static void acb56_isr_cha_rx_sp_cond(sdt_t * sdt) { unsigned char rr1 = 0; register int adata = sdt->dev.iobase; register int actrl = sdt->dev.iobase + 1; register int i = 0; sdt->stats.cha_rx_sp_cond++; /* collect bytes */ outb(0x00, actrl); for (i = 0; i < 4 && (inb(actrl) & 0x01); i++) { *(sdt->dev.rx_buf++) = inb(adata); sdt->sdt.stats.rx_bytes++; outb(0x00, actrl); } acb56_frame_overflow_check(sdt); /* check for special conditions */ outb(0x07, actrl); if (inb(actrl) & 0x40) { outb(0x01, actrl); if ((rr1 = inb(actrl)) & 0xf0) { outb(0x00, actrl); outb(0x30, actrl); /* reset error */ if (rr1 & 0x10) { return acb56_parity_error(sdt); } if (rr1 & 0x20) { return acb56_rx_overrun(sdt); } if (rr1 & 0x80) { if (rr1 & 0x40) { return acb56_crc_error(sdt); } else { if ((rr1 & 0xe) ^ 0x6) { return acb56_residue_error(sdt); } else { return acb56_end_of_frame(sdt); } } } } } } static void acb56_isr_donothing(sdt_t * sdt) { (void) sdt; }; static void acb56_isr_chb_tx_buf_empty(sdt_t * sdt) { sdt->stats.chb_tx_buf_empty++; } static void acb56_isr_chb_ext_status(sdt_t * sdt) { sdt->stats.chb_ext_status++; } static void acb56_isr_chb_rx_char_avail(sdt_t * sdt) { sdt->stats.chb_rx_char_avail++; } static void acb56_isr_chb_rx_sp_cond(sdt_t * sdt) { sdt->stats.chb_rx_sp_cond++; } static void acb56_isr(int irq, void *dev_id, struct pt_regs *regs) { /* *INDENT-OFF* */ static void (*vector_map[]) (sdt_t *) = { acb56_isr_chb_tx_buf_empty, acb56_isr_chb_ext_status, acb56_isr_chb_rx_char_avail, acb56_isr_chb_rx_sp_cond, acb56_isr_cha_tx_buf_empty, acb56_isr_cha_ext_status, acb56_isr_cha_rx_char_avail, acb56_isr_cha_rx_sp_cond }; /* *INDENT-ON* */ unsigned char rr3; register int i; register int actrl = ((sdt_t *) dev_id)->dev.iobase + 1; for (i = 0, outb(0x03, actrl), rr3 = inb(actrl); i < 4 && rr3; i++, outb(0x03, actrl), rr3 = inb(actrl)) { outb(0x02, actrl + 2); (*vector_map[inb(actrl + 2) >> 1]) (dev_id); /* reset highest interrupt under service */ outb(0x00, actrl); outb(0x38, actrl); } ((sdt_t *) dev_id)->stats.interrupts++; }; /* * ========================================================================= * * DRIVER SERVICE CALLS * * ========================================================================= */ static const unsigned char preamble[] = { 0x09, 0xC0, 0x0F, 0x01, 0x07, 0x6B, 0x0F, 0x00, 0x00, 0x00, 0x04, 0x20, 0x01, 0x00, 0x02, 0x00, 0x03, 0xCA, 0x05, 0x63, 0x06, 0x00, 0x07, 0x7e, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x16, 0x0C, 0x40, 0x0D, 0x00, 0x0E, 0x02, 0x0E, 0x02, 0x0E, 0x02, 0x0E, 0x03, 0x03, 0xCB, 0x05, 0x6B, 0x00, 0x80, 0x00, 0x30, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x10, 0x00, 0x10, 0x01, 0x00, 0x09, 0x00 }; static const unsigned char preset[] = { 0x09, 0xc0, 0x00, 0x00, 0x04, 0x20, 0x03, 0xca, 0x05, 0xe3, 0x07, 0x7e, 0x06, 0x00, 0x0F, 0x01, 0x07, 0x6b, 0x0F, 0x00, 0x01, 0x00, 0x02, 0x00, 0x09, 0x00, 0x0A, 0x80 }; static const unsigned char mode_clock[6][3] = { {0x08, 0x05, 0x7f}, {0x08, 0x56, 0x7f}, {0x50, 0x50, 0x78}, {0x16, 0x50, 0x1f}, {0x50, 0x50, 0x78}, {0x50, 0x50, 0x78} }; static unsigned char irqprobe[] = { 0x01, 0x19, 0x0F, 0xFA, 0x00, 0x10, 0x00, 0x10, 0x09, 0x08, 0x0E, 0x03 }; static void dummy_isr(int irq, void *dev_id, struct pt_regs *regs) { volatile int *p; (void) irq; (void) dev_id; (void) regs; p = NULL; p++; } static int board = 0; static int ports[] = { 0x238, 0x280, 0x2A0, 0x300, 0x328, 0 }; /* * DEVICE-ATTACH: This is the device attach. It should probe for the device * as specified by the minor device number and determine whether the device * is there and all of the resources for the device can be acquired. All * resources associated with the device (except the allocated structure, * which is freed by the caller) should be freed at the end of this routine * so that the device may be used by some other module if required. */ static int acb56_attach(sdt_t * sdt) { int iobase, _irq, actrl, _dma_rx, _dma_tx, i, err; unsigned long time, cookie; board = sdt->u.dev.cminor - 1; if ((iobase = io[board]) == -1) iobase = ports[board]; if ((err = check_region(iobase, 8))) return (-EBUSY); actrl = iobase + 1; outb(0x02, actrl); outb(0x55, actrl); /* write to unallocated 8530 bit in WR2 */ outb(0x02, actrl); if (inb(actrl) != 0x55) return (-ENXIO); /* probably an 8530 */ outb(0x09, actrl); outb(0xc0, actrl); /* force hardware reset */ outb(0x0f, actrl); outb(0x01, actrl); /* Access W7P register */ outb(0x0f, actrl); if (!inb(actrl) & 0x01) return (-ENXIO); /* probably an 8530 */ outb(0x0f, actrl); outb(0x00, actrl); /* Remove accss to W7P register */ outb(0x0f, actrl); if (inb(actrl) & 0x01) return (-ENXIO); /* probably an 8530 */ /* check assigned irq */ if ((_irq = irq[board]) != -1) { if ((err = request_irq(_irq, dummy_isr, SA_SHIRQ, "acb56_dummy", NULL))) return (-EBUSY); else goto acb_probe_dma; } for (i = 0; i < sizeof(preamble);) { /* setup chip */ outb(preamble[i], actrl); i++; outb(preamble[i], actrl); i++; } cookie = probe_irq_on(); for (i = 0; i < sizeof(irqprobe);) { /* setup for guaranteed interrupt */ outb(irqprobe[i], actrl); i++; outb(irqprobe[i], actrl); i++; } /* fill tx fifo to get an interrupt */ outb(0x55, iobase); outb(0x55, iobase); outb(0x55, iobase); outb(0x55, iobase); outb(0x55, iobase); outb(0x55, iobase); outb(0x55, iobase); outb(0x55, iobase); outb(0x55, iobase); for (time = jiffies; jiffies - time < 100; i++) ; if (!(_irq = probe_irq_off(cookie))) return (-EBUSY); /* no irq! */ outb(0x03, actrl); if (!inb(actrl)) return (-EBUSY); /* it wasn't us */ outb(0x09, actrl); outb(0x00, actrl); outb(0x09, actrl); outb(0xc0, actrl); /* force hardware reset */ if ((err = request_irq(_irq, dummy_isr, SA_SHIRQ, "acb56_dummy", NULL))) return (-EBUSY); acb_probe_dma: free_irq(_irq, NULL); /* check for dma */ if ((_dma_rx = dma_rx[board]) && _dma_rx != -1 && !(request_dma(_dma_rx, "acb56_probe"))) free_dma(_dma_rx); else _dma_rx = 0; if ((_dma_tx = dma_tx[board]) && _dma_tx != -1 && !(request_dma(_dma_tx, "acb56_probe"))) free_dma(_dma_tx); else _dma_tx = 0; sdt->dev.ifindex = board; sdt->dev.irq = _irq; sdt->dev.iobase = iobase; sdt->dev.dma_tx = _dma_tx; sdt->dev.dma_rx = _dma_rx; if (mode[board] != -1) sdt->dev.iface.ifmode = mode[board]; if (clock[board] != -1) sdt->dev.iface.ifclock = clock[board]; ptrace(("sucessful:\n")); ptrace((" ifindex = %lu\n", sdt->dev.ifindex)); ptrace((" irq = %lu\n", sdt->dev.irq)); ptrace((" iobase = 0x%lx\n", sdt->dev.iobase)); ptrace((" dma_tx = %lu\n", sdt->dev.dma_tx)); ptrace((" dma_rx = %lu\n", sdt->dev.dma_rx)); return (sdt); } /* * DEVICE-OPEN: Using the information contained in the device structure * created at device attach, this function should reacquire all of the * resources associated with the device (e.g., irqs). The device is left in * the quiescent state. */ static int acb56_open(lmi_t * lmi) { sdt_t *sdt = (sdt_t *) lmi; int err = 0; unsigned long flags; spin_lock_irqsave(&sdt->lock, flags); { MOD_INC_USE_COUNT; /* get io region */ if ((err = check_region(sdt->dev.iobase, 8))) { MOD_DEC_USE_COUNT; spin_unlock_irqrestore(&sdt->lock, flags); return err; } request_region(sdt->dev.iobase, 8, "acb56"); /* get dma channels */ if (sdt->dev.dma_rx && request_dma(sdt->dev.dma_rx, "acb56")) sdt->dev.dma_rx = 0; if (sdt->dev.dma_tx && request_dma(sdt->dev.dma_tx, "acb56")) sdt->dev.dma_tx = 0; /* get interrupt */ if ((err = request_irq(sdt->dev.irq, acb56_isr, SA_SHIRQ, "acb56", sdt))) { if (sdt->dev.dma_rx) free_dma(sdt->dev.dma_rx); if (sdt->dev.dma_tx) free_dma(sdt->dev.dma_tx); MOD_DEC_USE_COUNT; spin_unlock_irqrestore(&sdt->lock, flags); return err; } } spin_unlock_irqrestore(&sdt->lock, flags); return (0); } /* * DEVICE-CLOSE: Using the information contained in the device structure * created at device attach, this function should deallocate all of the * driver resources associated with the device with the exception of the * device structure itself. The device will be in the quiescent state when * the procedure is called. */ static int acb56_close(lmi_t * lmi) { sdt_t *sdt = (sdt_t *) lmi; unsigned long flags; spin_lock_irqsave(&sdt->lock, flags); { free_irq(sdt->dev.irq, sdt); if (sdt->dev.dma_tx) free_dma(sdt->dev.dma_tx); if (sdt->dev.dma_rx) free_dma(sdt->dev.dma_rx); release_region(sdt->dev.iobase, 8); MOD_DEC_USE_COUNT; } spin_unlock_irqrestore(&sdt->lock, flags); return (0); } /* * INFO: This is for Style 2 device drivers for returning PPAs. Since this * is only a single-port card, the Signalling Data Link is determined at open * time and we simply return a zero-length PPA. */ static int acb56_info(lmi_t * lmi, void **ppap, int *lenp) { sdt_t *sdt = (sdt_t *) lmi; unsigned long flags; (void) dev; spin_lock_irqsave(&sdt->lock, flags); { *lenp = 0; *ppap = NULL; } spin_unlock_irqrestore(&sdt->lock, flags); return (0); } /* * ATTACH: This is for Style 2 device drivers. Since this is only a * single-port card, the Signalling Data Link is determined at open time and * we simply agree with any attach command. */ static int acb56_attach(lmi_t * lmi, void *ppa, int len) { sdt_t *sdt = (sdt_t *) lmi; unsigned long flags; (void) dev; (void) ppa; (void) len; spin_lock_irqsave(&sdt->lock, flags); { } spin_unlock_irqrestore(&sdt->lock, flags); return (0); } /* * DETACH: This is for Style 2 device drivers. Since this is only a * single-port card, the Signalling Data LInk was determined at open time and * cannot be detached, so, we simply agree with any detach command. */ static int acb56_detach(lmi_t * lmi) { sdt_t *sdt = (sdt_t *) lmi; unsigned long flags; (void) dev; spin_lock_irqsave(&sdt->lock, flags); { } spin_unlock_irqrestore(&sdt->lock, flags); return (0); } #ifndef abs #define abs(x) ((x)<0 ? -(x):(x)) #endif /* * ENABLE: This should set up the hardware for the device using the resources * which were reserved during the open. The device should be activated and * made ready for operation. Interrupt service routines should be enabled. */ static int acb56_enable(lmi_t * lmi) { sdt_t *sdt = (sdt_t *) lmi; int i, actrl; unsigned long flags; spin_lock_irqsave(&sdt->lock, flags); { actrl = sdt->dev.iobase + 1; for (i = 0; i < 16; i++) sdt->dev.regs[i] = 0; /* register images */ /* setup chip */ for (i = 0; i < sizeof(preset);) { outb(preset[i], actrl); i++; outb(sdt->dev.regs[i >> 1] = preset[i], actrl); i++; } /* setup interface and clock modes */ outb(0x0b, actrl); outb(sdt->dev.regs[0x0b] = mode_clock[mode_map[sdt->sdl.config.ifmode]][clock_map[sdt->sdl.config.ifclock]], actrl); /* setup baud rate generator */ if (sdt->sdl.config.ifmode == SDL_MODE_DTE) { outb(0x0c, actrl); outb(sdt->dev.regs[0xc] = 0xca, actrl); outb(0x0d, actrl); outb(sdt->dev.regs[0xd] = 0x1c, actrl); } else if (sdt->sdl.config.ifmode == SDL_MODE_LOC_LB) { outb(0x0c, actrl); outb(sdt->dev.regs[0xc] = 0x00, actrl); outb(0x0d, actrl); outb(sdt->dev.regs[0xd] = 0x00, actrl); } else { outb(0x0c, actrl); outb(sdt->dev.regs[0xc] = 0x40, actrl); outb(0x0d, actrl); outb(sdt->dev.regs[0xd] = 0x00, actrl); } /* special DPLL modes */ if (sdt->sdl.config.ifclock == SDL_CLOCK_DPLL) { outb(0x0e, actrl); outb(0x60, actrl); outb(0x0e, actrl); outb(0xe0, actrl); if (sdt->sdl.config.ifmode == SDL_MODE_DTE) { outb(0x0e, actrl); outb(0xa0, actrl); } else { outb(0x0e, actrl); outb(0x80, actrl); } outb(0x0e, actrl); outb(0x20, actrl); } outb(0x0e, actrl); outb(sdt->dev.regs[0x0e] = 0x02, actrl); /* setup loopback and echo modes */ switch (sdt->sdl.config.ifmode) { case SDL_MODE_LOC_LB: outb(0x0e, actrl); outb(sdt->dev.regs[0x0e] |= 0x10, actrl); break; case SDL_MODE_ECHO: outb(0x0e, actrl); outb(sdt->dev.regs[0x0e] |= 0x08, actrl); break; case SDL_MODE_LB_ECHO: outb(0x0e, actrl); outb(sdt->dev.regs[0x0e] |= 0x18, actrl); break; } /* set up dma registers */ if (sdt->dev.dma_rx || sdt->dev.dma_tx) { outb(0x0e, actrl); outb(sdt->dev.regs[0x0e] |= 0x04, actrl); if (sdt->dev.dma_rx && sdt->dev.dma_tx) { outb(0x01, actrl); outb(sdt->dev.regs[0x01] |= 0xf9, actrl); } if (sdt->dev.dma_tx) { outb(0x01, actrl); outb(sdt->dev.regs[0x01] |= 0xc1, actrl); } if (sdt->dev.dma_tx) { outb(0x01, actrl); outb(sdt->dev.regs[0x01] |= 0xfb, actrl); } outb(0x80, actrl + 3); if (sdt->dev.dma_rx) enable_dma(sdt->dev.dma_rx); if (sdt->dev.dma_tx) enable_dma(sdt->dev.dma_tx); } else { outb(0x0e, actrl); outb(sdt->dev.regs[0x0e] &= ~0x04, actrl); outb(0x01, actrl); outb(sdt->dev.regs[0x01] |= 0x13, actrl); outb(0x00, actrl + 3); } /* disable status fifo */ outb(0x0f, actrl); outb(0xfc, actrl); outb(0x09, actrl); outb(0x02, actrl); /* reset and enable transmitters and receivers */ outb(0x0E, actrl); outb(sdt->dev.regs[0x0e] |= 0x01, actrl); outb(0x03, actrl); outb(sdt->dev.regs[0x0e] |= 0x01, actrl); outb(0x00, actrl); outb(0x30, actrl); outb(0x05, actrl); outb(sdt->dev.regs[0x0e] |= 0x08, actrl); outb(0x00, actrl); outb(0x80, actrl); outb(0x00, actrl); outb(0xC0, actrl); outb(0x00, actrl); outb(0x10, actrl); outb(0x00, actrl); outb(0x10, actrl); sdt->sdl.config.ifflags |= SDL_IF_TX_RUNNING; sdt->sdl.config.ifflags |= SDL_IF_RX_RUNNING; bufq_init(&sdt->dev.tinputq); sdt->dev.tx_msg = sdt_allocb(q, FASTBUF, BPRI_MED); sdt->dev.rx_msg = sdt_allocb(q, FASTBUF, BPRI_MED); sdt->dev.cp_msg = sdt_allocb(q, FASTBUF, BPRI_MED); if (!sdt->dev.tx_msg || !sdt->dev.rx_msg || !sdt->dev.cp_msg) { if (sdt->dev.tx_msg) freemsg(sdt->dev.tx_msg); if (sdt->dev.rx_msg) freemsg(sdt->dev.rx_msg); if (sdt->dev.cp_msg) freemsg(sdt->dev.cp_msg); spin_unlock_irqrestore(&sdt->lock, flags); return ENOBUFS; } *(sdt->dev.tx_msg->b_wptr)++ = 0x80; /* Initial SIOS */ *(sdt->dev.tx_msg->b_wptr)++ = 0x80; /* Initial SIOS */ *(sdt->dev.tx_msg->b_wptr)++ = 0x01; /* Initial SIOS */ *(sdt->dev.tx_msg->b_wptr)++ = 0x00; /* Initial SIOS */ /* enable master interrupt bit */ outb(0x09, actrl); outb(sdt->dev.regs[0x09] |= 0x08, actrl); /* we're running! phew! */ } spin_unlock_irqrestore(&sdt->lock, flags); return (0); } /* * DISABLE: This should shut down the hardware and deactivate ISRs and other * routines, flush buffers to the point that kernel resource which were * allocated can be deallocated in the close. */ static int acb56_disable(lmi_t * lmi) { sdt_t *sdt = (sdt_t *) lmi; int actrl = sdt->dev.iobase + 1; unsigned long flags; spin_lock_irqsave(&sdt->lock, flags); { sdt->sdl.config.ifflags &= ~SDL_IF_TX_RUNNING; sdt->sdl.config.ifflags &= ~SDL_IF_RX_RUNNING; sdt->sdl.config.ifflags &= ~SDL_IF_SU_COMPRESS; outb(0x09, actrl); outb(0xc0, actrl); /* force hw reset */ outb(0x09, actrl); outb(sdt->dev.regs[0x09] &= ~0x08, actrl); /* stop interrupts */ if (sdt->dev.dma_tx) { outb(0x0e, actrl); outb(sdt->dev.regs[0x0e] &= ~0x04, actrl); /* disable dma */ disable_dma(sdt->dev.dma_tx); } if (sdt->dev.dma_rx) { outb(0x01, actrl); outb(sdt->dev.regs[0x01] &= ~0xc0, actrl); /* disable dma */ disable_dma(sdt->dev.dma_rx); } outb(0x09, actrl); outb(0xc0, actrl); /* force hw reset */ bufq_purge(&sdt->dev.tinputq); } spin_unlock_irqrestore(&sdt->lock, flags); return (0); } /* * IOCTLS: These are ioctls which are general for SDL and which are specific * to device interfaces and which are privdte for the ACB56 drivers. They * are invoked at the stream head. */ static int acb56_ioctl(lmi_t * sdl, int cmd, void *arg) { size_t size = _IOC_SIZE(cmd); sdt_t *sdt = (sdt_t *) sdl->device; sdl_config_t *ureq = NULL; sdl_ulong uarg = 0; if (_IOC_TYPE(cmd) == SDL_IOC_MAGIC) switch (cmd) { case SDL_IOCTCONFIG: case SDL_IOCSCONFIG: if (!arg || size < sizeof(sdl_config_t)) return EINVAL; ureq = arg; break; case SDL_IOCCCONFIG: case SDL_IOCCMRESET: break; default: return EOPNOTSUPP; } if (_IOC_TYPE(cmd) == SDL_IOC_MAGIC) switch (cmd) { case SDL_IOCGIFFLAGS: case SDL_IOCGIFTYPE: case SDL_IOCGGRPTYPE: case SDL_IOCGIFMODE: case SDL_IOCGIFRATE: case SDL_IOCGIFCLOCK: case SDL_IOCGIFCODING: case SDL_IOCGIFLEADS: case SDL_IOCSIFFLAGS: case SDL_IOCSIFTYPE: case SDL_IOCSGRPTYPE: case SDL_IOCSIFMODE: case SDL_IOCSIFRATE: case SDL_IOCSIFCLOCK: case SDL_IOCSIFCODING: case SDL_IOCSIFLEADS: case SDL_IOCCIFLEADS: if (!arg || size < sizeof(dev_ulong)) return EINVAL; uarg = *(dev_ulong *) arg; break; case SDL_IOCCIFRESET: case SDL_IOCCDISCTX: case SDL_IOCCCONNTX: break; default: return EOPNOTSUPP; } if (_IOC_TYPE(cmd) == SDL_IOC_MAGIC) switch (cmd) { case SDL_IOCTCONFIG: case SDL_IOCSCONFIG: case SDL_IOCCCONFIG: case SDL_IOCCMRESET: break; default: return EOPNOTSUPP; } if (_IOC_TYPE(cmd) == SDL_IOC_MAGIC) switch (cmd) { case SDL_IOCCIFRESET: break; /* gets */ case SDL_IOCGIFFLAGS: *(dev_ulong *) arg = sdt->sdl.config.ifflags; return (0); case SDL_IOCGIFTYPE: *(dev_ulong *) arg = sdt->sdl.config.iftype; return (0); case SDL_IOCGGRPTYPE: *(dev_ulong *) arg = sdt->sdl.config.ifgtype; return (0); case SDL_IOCGIFMODE: *(dev_ulong *) arg = sdt->sdl.config.ifmode; return (0); case SDL_IOCGIFRATE: *(dev_ulong *) arg = sdt->sdl.config.ifrate; return (0); case SDL_IOCGIFCLOCK: *(dev_ulong *) arg = sdt->sdl.config.ifclock; return (0); case SDL_IOCGIFCODING: *(dev_ulong *) arg = sdt->sdl.config.ifcoding; return (0); case SDL_IOCGIFLEADS: *(dev_ulong *) arg = sdt->sdl.config.ifleads; return (0); /* sets */ case SDL_IOCSIFFLAGS: if (sdl->state == LMI_ENABLED) return EBUSY; sdt->sdl.config.ifflags = uarg; return (0); case SDL_IOCSIFTYPE: if (sdl->state == LMI_ENABLED) return EBUSY; if (uarg != SDL_TYPE_V35) return EINVAL; return (0); case SDL_IOCSGRPTYPE: if (sdl->state == LMI_ENABLED) return EBUSY; if (uarg != SDL_GTYPE_NONE) return EINVAL; return (0); case SDL_IOCSIFMODE: ptrace(("uarg = %lu\n", uarg)); if (sdl->state == LMI_ENABLED) return EBUSY; switch (uarg) { case SDL_MODE_DTE: case SDL_MODE_DCE: case SDL_MODE_REM_LB: case SDL_MODE_LOC_LB: case SDL_MODE_LB_ECHO: case SDL_MODE_TEST: if (mode_map[uarg] != -1) { sdt->sdl.config.ifmode = uarg; return (0); } default: return EINVAL; } return (0); case SDL_IOCSIFRATE: if (sdl->state == LMI_ENABLED) return EBUSY; if (uarg != 56000) return EINVAL; return (0); case SDL_IOCSIFCLOCK: ptrace(("uarg = %lu\n", uarg)); if (sdl->state == LMI_ENABLED) return EBUSY; switch (uarg) { case SDL_CLOCK_INT: case SDL_CLOCK_EXT: case SDL_CLOCK_DPLL: if (clock_map[uarg] != -1) { sdt->sdl.config.ifclock = uarg; return (0); } default: return EINVAL; } return (0); case SDL_IOCSIFCODING: if (sdl->state == LMI_ENABLED) return EBUSY; if (uarg != SDL_CODING_NRZI) return EINVAL; return (0); case SDL_IOCSIFLEADS: case SDL_IOCCIFLEADS: /* FIXME: control the leads */ if (uarg) return EINVAL; return (0); case SDL_IOCCDISCTX: sdt->sdl.config.ifflags &= ~SDL_IF_TX_RUNNING; return (0); case SDL_IOCCCONNTX: sdt->sdl.config.ifflags |= SDL_IF_TX_RUNNING; return (0); } return EOPNOTSUPP; } static struct lmi_ops acb56_lmi_ops = { { acb56_devatt, /* dev.attach */ acb56_open, /* dev.open */ acb56_close, /* dev.close */ }, { acb56_info, /* lmi.info */ acb56_attach, /* lmi.attach */ acb56_detach, /* lmi.detach */ acb56_enable, /* lmi.enable */ acb56_disable, /* lmi.disable */ acb56_ioctl /* lmi.ioctl */ } }; /* * ========================================================================= * * SERVICE ROUTINES * * ========================================================================= */ static void acb56_xmit(struct dev *dev, mblk_t * mp) { sdt_t *sdt = (sdt_t *) dev; unsigned long flags; spin_lock_irqsave(&sdt->lock, flags); { if (!(sdt->sdl.config.ifflags & SDL_IF_TX_RUNNING)) freemsg(mp); else bufq_queue(&sdt->dev.tinputq, mp); } spin_unlock_irqrestore(&sdt->lock, flags); } static void acb56_tx_start(struct dev *dev) { sdt_t *sdt = (sdt_t *) dev; sdt->sdl.config.ifflags |= SDL_IF_TX_RUNNING; } static void acb56_rx_start(struct dev *dev) { sdt_t *sdt = (sdt_t *) dev; sdt->sdl.config.ifflags |= SDL_IF_RX_RUNNING; } static dev_dcalls_t acb56_dcalls = { acb56_xmit, /* daedt_xmit */ acb56_tx_start, /* daedt_start */ acb56_rx_start /* daedr_start */ }; /* * ======================================================================= * * LiS Module Initialization (For registered driver.) * * ======================================================================= */ static int acb56_initialized = 0; void acb56_init(void) { if (acb56_initialized > 0) return; printk(KERN_INFO ACB56_BANNER); /* console splash */ ptrace(("registering sdl_acb: cmajor %d, nminors %d\n", ACB56_CMAJOR, ACB56_NMINOR)); acb56_initialized = sdl_register_driver(ACB56_CMAJOR, ACB56_NMINOR, "sdl_acb", &acb56_lmi_ops, &acb56_dcalls); ptrace(("return (device major) = %d\n", acb56_initialized)); if (acb56_initialized > 0) acb56_cmajor = acb56_initialized; if (acb56_initialized == 0) acb56_initialized = acb56_cmajor; } void acb56_terminate(void) { if (acb56_initialized <= 0) return; ptrace(("unregistering sdl_acb cmajor %d\n", acb56_cmajor)); acb56_initialized = sdl_unregister_driver(acb56_cmajor); ptrace(("return = %d\n", acb56_initialized)); } /* * ======================================================================= * * Kernel Module Initialization (For unregistered driver.) * * ======================================================================= */ #ifdef MODULE int init_module(void) { acb56_init(); if (acb56_initialized < 0) return acb56_initialized; return (0); } void cleanup_module(void) { acb56_terminate(); } #endif /* * ========================================================================= * * IO Controls * * ========================================================================= * * LM IO Controls * * ------------------------------------------------------------------------- */ STATIC int lmi_test_config(sdt_t * sdt, lmi_config_t * arg) { return (-EOPNOTSUPP); } STATIC int lmi_commit_config(sdt_t * sdt, lmi_config_t * arg) { return (-EOPNOTSUPP); } STATIC int lmi_iocgoptions(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags, ret = 0; lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->option; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int lmi_iocsoptions(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags, ret = 0; lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->option = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int lmi_iocgconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { arg->version = sdt->version; arg->style = sdt->style; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int lmi_iocsconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->version = arg->version; sdt->style = arg->style; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int lmi_ioctconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; return lmi_test_config(sdt, arg); } rare(); return (-EINVAL); } STATIC int lmi_ioccconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; return lmi_commit_config(sdt, arg); } rare(); return (-EINVAL); } STATIC int lmi_iocgstatem(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { arg->state = sdt->state; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int lmi_ioccmreset(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags, ret = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->state = LMI_UNUSABLE; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int lmi_iocgstatsp(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags, ret = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdt.statsp; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int lmi_iocsstatsp(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags, ret = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdt.statsp = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int lmi_iocgstats(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags, ret; lis_spin_lock_irqsave(&sdt->lock, &flags); { ret = -EOPNOTSUPP; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int lmi_ioccstats(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); int flags, ret; (void) mp; lis_spin_lock_irqsave(&sdt->lock, &flags); { ret = -EOPNOTSUPP; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } STATIC int lmi_iocgnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags, ret; lis_spin_lock_irqsave(&sdt->lock, &flags); { ret = -EOPNOTSUPP; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int lmi_iocsnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags, ret; lis_spin_lock_irqsave(&sdt->lock, &flags); { ret = -EOPNOTSUPP; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int lmi_ioccnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); lmi_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags, ret; lis_spin_lock_irqsave(&sdt->lock, &flags); { ret = -EOPNOTSUPP; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } /* * ========================================================================= * * SDT IO Controls * * ------------------------------------------------------------------------- */ STATIC int sdt_test_config(sdt_t * sdt, sdt_config_t * arg) { int ret = 0; int flags = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); do { if (!arg->t8) arg->t8 = sdt->sdt.config.t8; if (!arg->Tin) arg->Tin = sdt->sdt.config.Tin; if (!arg->Tie) arg->Tie = sdt->sdt.config.Tie; if (!arg->T) arg->T = sdt->sdt.config.T; if (!arg->D) arg->D = sdt->sdt.config.D; if (!arg->Te) arg->Te = sdt->sdt.config.Te; if (!arg->De) arg->De = sdt->sdt.config.De; if (!arg->Ue) arg->Ue = sdt->sdt.config.Ue; if (!arg->N) arg->N = sdt->sdt.config.N; if (!arg->m) arg->m = sdt->sdt.config.m; if (!arg->b) arg->b = sdt->sdt.config.b; else if (arg->b != sdt->sdt.config.b) { ret = -EINVAL; break; } } while (0); lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } STATIC int sdt_commit_config(sdt_t * sdt, sdt_config_t * arg) { int flags = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt_test_config(sdt, arg); sdt->sdt.config = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } STATIC int sdt_iocgoptions(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->option; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_iocsoptions(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->option = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_iocgconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdt_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdt.config; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_iocsconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdt_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdt.config = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_ioctconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); sdt_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; return sdt_test_config(sdt, arg); } rare(); return (-EINVAL); } STATIC int sdt_ioccconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); sdt_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; return sdt_commit_config(sdt, arg); } rare(); return (-EINVAL); } STATIC int sdt_iocgstatem(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdt_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdt.statem; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_ioccmreset(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); (void) sdt; (void) mp; fixme(("%s: Master reset\n", SDT_MOD_NAME)); return (-EOPNOTSUPP); } STATIC int sdt_iocgstatsp(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdt.statsp; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_iocsstatsp(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdt.statsp = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_iocgstats(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdt_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdt.stats; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_ioccstats(queue_t * q, mblk_t * mp) { int flags = 0; sdt_t *sdt = SDT_PRIV(q); (void) mp; lis_spin_lock_irqsave(&sdt->lock, &flags); { bzero(&sdt->sdt.stats, sizeof(sdt->sdt.stats)); } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } STATIC int sdt_iocgnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdt_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdt.notify; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_iocsnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdt_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdt.notify = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_ioccnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdt_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdt.notify.events &= ~arg->events; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdt_ioccabort(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); int ret, flags = 0; (void) mp; lis_spin_lock_irqsave(&sdt->lock, &flags); { ret = -EOPNOTSUPP; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } /* * ------------------------------------------------------------------------- * * SDL IO Controls * * ------------------------------------------------------------------------- */ STATIC int sdl_test_config(sdt_t * sdt, sdl_config_t * arg) { (void) sdt; (void) arg; // fixme(("%s: FIXME: write this function\n", SDT_MOD_NAME)); return (0); } STATIC void sdl_commit_config(sdt_t * sdt, sdl_config_t * arg) { long tdiff; sdt->sdl.config = *arg; switch (sdt->sdl.config.ifclock) { case SDL_CLOCK_TICK: case SDL_CLOCK_SHAPER: /* reshape traffic */ if ((tdiff = sdt->sdl.timestamp - jiffies) > 0) sdt->sdl.bytecount += sdt->sdl.tickbytes * tdiff; else sdt->sdl.bytecount = 0; sdt->sdl.timestamp = jiffies; sdt->sdl.tickbytes = sdt->sdl.config.ifrate / HZ / 8; if (sdt->sdl.tickbytes < 1) sdt->sdl.tickbytes = 1; ensure(sdt->sdl.tickbytes > 0, sdt->sdl.tickbytes = 1); while (sdt->sdl.bytecount >= sdt->sdl.tickbytes) { sdt->sdl.bytecount -= sdt->sdl.tickbytes; sdt->sdl.timestamp++; } } } STATIC int sdl_iocgoptions(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->option; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_iocsoptions(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->option = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_iocgconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdl.config; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_iocsconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int ret, flags = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); { if (!(ret = sdl_test_config(sdt, arg))) sdl_commit_config(sdt, arg); } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int sdl_ioctconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int ret, flags = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); { ret = sdl_test_config(sdt, arg); } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (ret); } rare(); return (-EINVAL); } STATIC int sdl_ioccconfig(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; int flags = 0; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdl_commit_config(sdt, arg); } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_iocgstatem(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdl_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdl.statem; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_ioccmreset(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL; (void) sdt; (void) arg; fixme(("%s: FIXME: Support master reset\n", SDT_MOD_NAME)); return (-EOPNOTSUPP); } STATIC int sdl_iocgstatsp(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdl.statsp; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_iocsstatsp(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; lmi_sta_t *arg = (typeof(arg)) mp->b_cont->b_rptr; fixme(("%s: FIXME: check these settings\n", SDT_MOD_NAME)); lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdl.statsp = *arg; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_iocgstats(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdl_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdl.stats; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_ioccstats(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; (void) mp; lis_spin_lock_irqsave(&sdt->lock, &flags); { bzero(&sdt->sdl.stats, sizeof(sdt->sdl.stats)); } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } STATIC int sdl_iocgnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { *arg = sdt->sdl.notify; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_iocsnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdl.notify.events |= arg->events; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_ioccnotify(queue_t * q, mblk_t * mp) { if (mp->b_cont) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdl.notify.events &= ~arg->events; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } rare(); return (-EINVAL); } STATIC int sdl_ioccdisctx(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; (void) mp; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdl.config.ifflags &= ~SDL_IF_TX_RUNNING; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } STATIC int sdl_ioccconntx(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; (void) mp; lis_spin_lock_irqsave(&sdt->lock, &flags); { sdt->sdl.config.ifflags |= SDL_IF_TX_RUNNING; } lis_spin_unlock_irqrestore(&sdt->lock, &flags); return (0); } /* * ========================================================================= * * STREAMS Message Handling * * ========================================================================= * * M_IOCTL Handling * * ------------------------------------------------------------------------- */ STATIC int sdt_w_ioctl(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); struct iocblk *iocp = (struct iocblk *) mp->b_rptr; void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL; int cmd = iocp->ioc_cmd, count = iocp->ioc_count; int type = _IOC_TYPE(cmd), nr = _IOC_NR(cmd), size = _IOC_SIZE(cmd); struct linkblk *lp = (struct linkblk *) arg; int ret = 0; switch (type) { case __SID: { switch (nr) { case _IOC_NR(I_STR): case _IOC_NR(I_LINK): case _IOC_NR(I_PLINK): case _IOC_NR(I_UNLINK): case _IOC_NR(I_PUNLINK): (void) lp; ptrace(("%s: ERROR: Unsupported STREAMS ioctl %d\n", SDT_MOD_NAME, nr)); ret = -EINVAL; break; default: ptrace(("%s: ERROR: Unsupported STREAMS ioctl %d\n", SDT_MOD_NAME, nr)); ret = -EOPNOTSUPP; break; } break; } case LMI_IOC_MAGIC: { if (count < size || SDT_PRIV(q)->state == LMI_UNATTACHED) { ret = -EINVAL; break; } switch (nr) { case _IOC_NR(LMI_IOCGOPTIONS): /* lmi_option_t */ ret = lmi_iocgoptions(q, mp); break; case _IOC_NR(LMI_IOCSOPTIONS): /* lmi_option_t */ ret = lmi_iocsoptions(q, mp); break; case _IOC_NR(LMI_IOCGCONFIG): /* lmi_config_t */ ret = lmi_iocgconfig(q, mp); break; case _IOC_NR(LMI_IOCSCONFIG): /* lmi_config_t */ ret = lmi_iocsconfig(q, mp); break; case _IOC_NR(LMI_IOCTCONFIG): /* lmi_config_t */ ret = lmi_ioctconfig(q, mp); break; case _IOC_NR(LMI_IOCCCONFIG): /* lmi_config_t */ ret = lmi_ioccconfig(q, mp); break; case _IOC_NR(LMI_IOCGSTATEM): /* lmi_statem_t */ ret = lmi_iocgstatem(q, mp); break; case _IOC_NR(LMI_IOCCMRESET): /* lmi_statem_t */ ret = lmi_ioccmreset(q, mp); break; case _IOC_NR(LMI_IOCGSTATSP): /* lmi_sta_t */ ret = lmi_iocgstatsp(q, mp); break; case _IOC_NR(LMI_IOCSSTATSP): /* lmi_sta_t */ ret = lmi_iocsstatsp(q, mp); break; case _IOC_NR(LMI_IOCGSTATS): /* lmi_stats_t */ ret = lmi_iocgstats(q, mp); break; case _IOC_NR(LMI_IOCCSTATS): /* lmi_stats_t */ ret = lmi_ioccstats(q, mp); break; case _IOC_NR(LMI_IOCGNOTIFY): /* lmi_notify_t */ ret = lmi_iocgnotify(q, mp); break; case _IOC_NR(LMI_IOCSNOTIFY): /* lmi_notify_t */ ret = lmi_iocsnotify(q, mp); break; case _IOC_NR(LMI_IOCCNOTIFY): /* lmi_notify_t */ ret = lmi_ioccnotify(q, mp); break; default: ptrace(("%s: ERROR: Unsupported SDT ioctl %d\n", SDT_MOD_NAME, nr)); ret = -EOPNOTSUPP; break; } break; } case SDT_IOC_MAGIC: { if (count < size || SDT_PRIV(q)->state == LMI_UNATTACHED) { ret = -EINVAL; break; } switch (nr) { case _IOC_NR(SDT_IOCGOPTIONS): /* lmi_option_t */ ret = sdt_iocgoptions(q, mp); break; case _IOC_NR(SDT_IOCSOPTIONS): /* lmi_option_t */ ret = sdt_iocsoptions(q, mp); break; case _IOC_NR(SDT_IOCGCONFIG): /* sdt_config_t */ ret = sdt_iocgconfig(q, mp); break; case _IOC_NR(SDT_IOCSCONFIG): /* sdt_config_t */ ret = sdt_iocsconfig(q, mp); break; case _IOC_NR(SDT_IOCTCONFIG): /* sdt_config_t */ ret = sdt_ioctconfig(q, mp); break; case _IOC_NR(SDT_IOCCCONFIG): /* sdt_config_t */ ret = sdt_ioccconfig(q, mp); break; case _IOC_NR(SDT_IOCGSTATEM): /* sdt_statem_t */ ret = sdt_iocgstatem(q, mp); break; case _IOC_NR(SDT_IOCCMRESET): /* sdt_statem_t */ ret = sdt_ioccmreset(q, mp); break; case _IOC_NR(SDT_IOCGSTATSP): /* lmi_sta_t */ ret = sdt_iocgstatsp(q, mp); break; case _IOC_NR(SDT_IOCSSTATSP): /* lmi_sta_t */ ret = sdt_iocsstatsp(q, mp); break; case _IOC_NR(SDT_IOCGSTATS): /* sdt_stats_t */ ret = sdt_iocgstats(q, mp); break; case _IOC_NR(SDT_IOCCSTATS): /* sdt_stats_t */ ret = sdt_ioccstats(q, mp); break; case _IOC_NR(SDT_IOCGNOTIFY): /* sdt_notify_t */ ret = sdt_iocgnotify(q, mp); break; case _IOC_NR(SDT_IOCSNOTIFY): /* sdt_notify_t */ ret = sdt_iocsnotify(q, mp); break; case _IOC_NR(SDT_IOCCNOTIFY): /* sdt_notify_t */ ret = sdt_ioccnotify(q, mp); break; case _IOC_NR(SDT_IOCCABORT): /* */ ret = sdt_ioccabort(q, mp); break; default: ptrace(("%s: ERROR: Unsupported SDT ioctl %d\n", SDT_MOD_NAME, nr)); ret = -EOPNOTSUPP; break; } break; } case SDL_IOC_MAGIC: { if (count < size || sdt->state == LMI_UNATTACHED) { ptrace(("%s: ERROR: ioctl count = %d, size = %d, state = %d\n", SDT_MOD_NAME, count, size, sdt->state)); ret = -EINVAL; break; } switch (nr) { case _IOC_NR(SDL_IOCGOPTIONS): /* lmi_option_t */ ret = sdl_iocgoptions(q, mp); break; case _IOC_NR(SDL_IOCSOPTIONS): /* lmi_option_t */ ret = sdl_iocsoptions(q, mp); break; case _IOC_NR(SDL_IOCGCONFIG): /* sdl_config_t */ ret = sdl_iocgconfig(q, mp); break; case _IOC_NR(SDL_IOCSCONFIG): /* sdl_config_t */ ret = sdl_iocsconfig(q, mp); break; case _IOC_NR(SDL_IOCTCONFIG): /* sdl_config_t */ ret = sdl_ioctconfig(q, mp); break; case _IOC_NR(SDL_IOCCCONFIG): /* sdl_config_t */ ret = sdl_ioccconfig(q, mp); break; case _IOC_NR(SDL_IOCGSTATEM): /* sdl_statem_t */ ret = sdl_iocgstatem(q, mp); break; case _IOC_NR(SDL_IOCCMRESET): /* sdl_statem_t */ ret = sdl_ioccmreset(q, mp); break; case _IOC_NR(SDL_IOCGSTATSP): /* sdl_stats_t */ ret = sdl_iocgstatsp(q, mp); break; case _IOC_NR(SDL_IOCSSTATSP): /* sdl_stats_t */ ret = sdl_iocsstatsp(q, mp); break; case _IOC_NR(SDL_IOCGSTATS): /* sdl_stats_t */ ret = sdl_iocgstats(q, mp); break; case _IOC_NR(SDL_IOCCSTATS): /* sdl_stats_t */ ret = sdl_ioccstats(q, mp); break; case _IOC_NR(SDL_IOCGNOTIFY): /* sdl_notify_t */ ret = sdl_iocgnotify(q, mp); break; case _IOC_NR(SDL_IOCSNOTIFY): /* sdl_notify_t */ ret = sdl_iocsnotify(q, mp); break; case _IOC_NR(SDL_IOCCNOTIFY): /* sdl_notify_t */ ret = sdl_ioccnotify(q, mp); break; case _IOC_NR(SDL_IOCCDISCTX): /* */ ret = sdl_ioccdisctx(q, mp); break; case _IOC_NR(SDL_IOCCCONNTX): /* */ ret = sdl_ioccconntx(q, mp); break; default: ptrace(("%s: ERROR: Unsupported SDL ioctl %d\n", SDT_MOD_NAME, nr)); ret = -EOPNOTSUPP; break; } break; } default: ret = (QR_PASSALONG); break; } if (ret > 0) { return (ret); } else if (ret == 0) { mp->b_datap->db_type = M_IOCACK; iocp->ioc_error = 0; iocp->ioc_rval = 0; } else { mp->b_datap->db_type = M_IOCNAK; iocp->ioc_error = -ret; iocp->ioc_rval = -1; } qreply(q, mp); return (QR_ABSORBED); } /* * ------------------------------------------------------------------------- * * M_PROTO, M_PCPROTO Handling * * ------------------------------------------------------------------------- */ STATIC int sdt_w_proto(queue_t * q, mblk_t * mp) { int rtn; ulong prim; sdt_t *sdt = SDT_PRIV(q); ulong oldstate = sdt->state; /* Fast Path */ if ((prim = *(ulong *) mp->b_rptr) == SDT_DAEDT_TRANSMISSION_REQ) { printd(("%s: %p: -> SDT_DAEDT_TRANSMISSION_REQ\n", SDT_MOD_NAME, sdt)); if ((rtn = sdt_daedt_transmission_req(q, mp)) < 0) sdt->state = oldstate; return (rtn); } switch (prim) { case LMI_INFO_REQ: printd(("%s: %p: -> LMI_INFO_REQ\n", SDT_MOD_NAME, sdt)); rtn = lmi_info_req(q, mp); break; case LMI_ATTACH_REQ: printd(("%s: %p: -> LMI_ATTACH_REQ\n", SDT_MOD_NAME, sdt)); rtn = lmi_attach_req(q, mp); break; case LMI_DETACH_REQ: printd(("%s: %p: -> LMI_DETACH_REQ\n", SDT_MOD_NAME, sdt)); rtn = lmi_detach_req(q, mp); break; case LMI_ENABLE_REQ: printd(("%s: %p: -> LMI_ENABLE_REQ\n", SDT_MOD_NAME, sdt)); rtn = lmi_enable_req(q, mp); break; case LMI_DISABLE_REQ: printd(("%s: %p: -> LMI_DISABLE_REQ\n", SDT_MOD_NAME, sdt)); rtn = lmi_disable_req(q, mp); break; case LMI_OPTMGMT_REQ: printd(("%s: %p: -> LMI_OPTMGMT_REQ\n", SDT_MOD_NAME, sdt)); rtn = lmi_optmgmt_req(q, mp); break; case SDT_DAEDT_TRANSMISSION_REQ: printd(("%s: %p: -> SDT_DAEDT_TRANSMISSION_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_daedt_transmission_req(q, mp); break; case SDT_DAEDT_START_REQ: printd(("%s: %p: -> SDT_DAEDT_START_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_daedt_start_req(q, mp); break; case SDT_DAEDR_START_REQ: printd(("%s: %p: -> SDT_DAEDR_START_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_daedr_start_req(q, mp); break; case SDT_AERM_START_REQ: printd(("%s: %p: -> SDT_AERM_START_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_aerm_start_req(q, mp); break; case SDT_AERM_STOP_REQ: printd(("%s: %p: -> SDT_AERM_STOP_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_aerm_stop_req(q, mp); break; case SDT_AERM_SET_TI_TO_TIN_REQ: printd(("%s: %p: -> SDT_AERM_SET_TI_TO_TIN_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_aerm_set_ti_to_tin_req(q, mp); break; case SDT_AERM_SET_TI_TO_TIE_REQ: printd(("%s: %p: -> SDT_AERM_SET_TI_TO_TIE_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_aerm_set_ti_to_tie_req(q, mp); break; case SDT_SUERM_START_REQ: printd(("%s: %p: -> SDT_SUERM_START_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_suerm_start_req(q, mp); break; case SDT_SUERM_STOP_REQ: printd(("%s: %p: -> SDT_SUERM_STOP_REQ\n", SDT_MOD_NAME, sdt)); rtn = sdt_suerm_stop_req(q, mp); break; default: rtn = -EOPNOTSUPP; break; } if (rtn < 0) sdt->state = oldstate; return (rtn); } STATIC int sdt_r_proto(queue_t * q, mblk_t * mp) { int rtn; ulong prim; sdt_t *sdt = SDT_PRIV(q); ulong oldstate = sdt->t.state; /* Fast Path */ if ((prim = *((ulong *) mp->b_rptr)) == T_UNITDATA_IND) { printd(("%s: %p: T_UNITDATA_IND [%d] <-\n", SDT_MOD_NAME, sdt, msgdsize(mp->b_cont))); if ((rtn = t_unitdata_ind(q, mp)) < 0) sdt->t.state = oldstate; return (rtn); } switch (prim) { case T_CONN_IND: printd(("%s: %p: T_CONN_IND <-\n", SDT_MOD_NAME, sdt)); rtn = t_conn_ind(q, mp); break; case T_CONN_CON: printd(("%s: %p: T_CONN_CON <-\n", SDT_MOD_NAME, sdt)); rtn = t_conn_con(q, mp); break; case T_DISCON_IND: printd(("%s: %p: T_DISCON_IND <-\n", SDT_MOD_NAME, sdt)); rtn = t_discon_ind(q, mp); break; case T_DATA_IND: printd(("%s: %p: T_DATA_IND <-\n", SDT_MOD_NAME, sdt)); rtn = t_data_ind(q, mp); break; case T_EXDATA_IND: printd(("%s: %p: T_EXDATA_IND <-\n", SDT_MOD_NAME, sdt)); rtn = t_exdata_ind(q, mp); break; case T_INFO_ACK: printd(("%s: %p: T_INFO_ACK <-\n", SDT_MOD_NAME, sdt)); rtn = t_info_ack(q, mp); break; case T_BIND_ACK: printd(("%s: %p: T_BIND_ACK <-\n", SDT_MOD_NAME, sdt)); rtn = t_bind_ack(q, mp); break; case T_ERROR_ACK: printd(("%s: %p: T_ERROR_ACK <-\n", SDT_MOD_NAME, sdt)); rtn = t_error_ack(q, mp); break; case T_OK_ACK: printd(("%s: %p: T_OK_ACK <-\n", SDT_MOD_NAME, sdt)); rtn = t_ok_ack(q, mp); break; case T_UNITDATA_IND: printd(("%s: %p: T_UNITDATA_IND [%d] <-\n", SDT_MOD_NAME, sdt, msgdsize(mp->b_cont))); rtn = t_unitdata_ind(q, mp); break; case T_UDERROR_IND: printd(("%s: %p: T_UDERROR_IND <-\n", SDT_MOD_NAME, sdt)); rtn = t_uderror_ind(q, mp); break; case T_OPTMGMT_ACK: printd(("%s: %p: T_OPTMGMT_ACK <-\n", SDT_MOD_NAME, sdt)); rtn = t_optmgmt_ack(q, mp); break; case T_ORDREL_IND: printd(("%s: %p: T_ORDREL_IND <-\n", SDT_MOD_NAME, sdt)); rtn = t_ordrel_ind(q, mp); break; case T_OPTDATA_IND: printd(("%s: %p: T_OPTDATA_IND <-\n", SDT_MOD_NAME, sdt)); rtn = t_optdata_ind(q, mp); break; case T_ADDR_ACK: printd(("%s: %p: T_ADDR_ACK <-\n", SDT_MOD_NAME, sdt)); rtn = t_addr_ack(q, mp); break; default: swerr(); rtn = (-EOPNOTSUPP); break; } if (rtn < 0) sdt->t.state = oldstate; return (rtn); } /* * ------------------------------------------------------------------------- * * M_DATA Handling * * ------------------------------------------------------------------------- */ STATIC int sdt_w_data(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); (void) sdt; printd(("%s: %p: -> M_DATA [%d]\n", SDT_MOD_NAME, sdt, msgdsize(mp))); return sdt_send_data(q, mp); } STATIC int sdt_r_data(queue_t * q, mblk_t * mp) { sdt_t *sdt = SDT_PRIV(q); (void) sdt; printd(("%s: %p: M_DATA [%d] <-\n", SDT_MOD_NAME, sdt, msgdsize(mp))); return sdt_recv_data(q, mp); } /* * ========================================================================= * * PUT and SRV * * ========================================================================= */ STATIC INLINE int sdt_r_prim(queue_t * q, mblk_t * mp) { /* Fast Path */ if (mp->b_datap->db_type == M_DATA) return sdt_r_data(q, mp); switch (mp->b_datap->db_type) { case M_DATA: return sdt_r_data(q, mp); case M_PROTO: case M_PCPROTO: return sdt_r_proto(q, mp); case M_FLUSH: return ss7_r_flush(q, mp); } return (QR_PASSFLOW); } STATIC INLINE int sdt_w_prim(queue_t * q, mblk_t * mp) { /* Fast Path */ if (mp->b_datap->db_type == M_DATA) return sdt_w_data(q, mp); switch (mp->b_datap->db_type) { case M_DATA: return sdt_w_data(q, mp); case M_PROTO: case M_PCPROTO: return sdt_w_proto(q, mp); case M_FLUSH: return ss7_w_flush(q, mp); case M_IOCTL: return sdt_w_ioctl(q, mp); } return (QR_PASSFLOW); } STATIC INT sdt_rput(queue_t * q, mblk_t * mp) { return (INT) ss7_oput(q, mp); } STATIC INT sdt_rsrv(queue_t * q) { return (INT) ss7_osrv(q); } STATIC INT sdt_wput(queue_t * q, mblk_t * mp) { return (INT) ss7_iput(q, mp); } STATIC INT sdt_wsrv(queue_t * q) { return (INT) ss7_isrv(q); } /* * ========================================================================= * * Private Structure allocation, deallocation and cache * * ========================================================================= */ STATIC kmem_cache_t *sdt_priv_cachep = NULL; STATIC int sdt_init_caches(void) { if (!sdt_priv_cachep && !(sdt_priv_cachep = kmem_cache_create ("sdt_priv_cachep", sizeof(sdt_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { cmn_err(CE_PANIC, "%s: Cannot allocate sdt_priv_cachep", __FUNCTION__); return (-ENOMEM); } else printd(("%s: initialized module private structure cace\n", SDT_MOD_NAME)); return (0); } STATIC void sdt_term_caches(void) { if (sdt_priv_cachep) { if (kmem_cache_destroy(sdt_priv_cachep)) cmn_err(CE_WARN, "%s: did not destroy sdt_priv_cachep", __FUNCTION__); else printd(("%s: destroyed sdt_priv_cachep\n", SDT_MOD_NAME)); } return; } STATIC sdt_t *sdt_get(sdt_t * sdt) { if (sdt) atomic_inc(&sdt->refcnt); return (sdt); } STATIC void sdt_put(sdt_t * sdt) { if (sdt) { if (atomic_dec_and_test(&sdt->refcnt)) { kmem_cache_free(sdt_priv_cachep, sdt); printd(("%s: freed module private structure\n", SDT_MOD_NAME)); } } } STATIC sdt_t *sdt_alloc_priv(queue_t * q, sdt_t ** sdtp, ushort cmajor, ushort cminor) { sdt_t *sdt; if ((sdt = kmem_cache_alloc(sdt_priv_cachep, SLAB_ATOMIC))) { printd(("%s: allocated module private structure\n", SDT_MOD_NAME)); bzero(sdt, sizeof(*sdt)); sdt_get(sdt); /* first get */ sdt->cmajor = cmajor; sdt->cminor = cminor; lis_spin_lock_init(&sdt->qlock, "sdt-queue-lock"); (sdt->oq = RD(q))->q_ptr = sdt_get(sdt); (sdt->iq = WR(q))->q_ptr = sdt_get(sdt); sdt->o_prim = &sdt_r_prim; sdt->i_prim = &sdt_w_prim; sdt->o_wakeup = &sdt_rx_wakeup; sdt->i_wakeup = &sdt_tx_wakeup; sdt->version = 1; sdt->state = LMI_UNUSABLE; sdt->style = LMI_STYLE1; lis_spin_lock_init(&sdt->lock, "sdt-priv-lock"); if ((sdt->next = *sdtp)) sdt->next->prev = &sdt->next; sdt->prev = sdtp; *sdtp = sdt_get(sdt); printd(("%s: linked module private structure\n", SDT_MOD_NAME)); /* TPI configuration defaults */ sdt->t.state = TS_NOSTATES; sdt->t.serv_type = T_CLTS; sdt->t.sequence = 0; sdt->t.pdu_size = 272 + 1 + 3; sdt->t.opt_size = -1UL; sdt->t.add_size = sizeof(struct sockaddr); /* LMI configuration defaults */ sdt->option.pvar = SS7_PVAR_ITUT_96; sdt->option.popt = 0; /* DEV initialization */ bufq_init /* SDL configuration defaults */ sdt->sdl.config.ifflags = 0; sdt->sdl.config.iftype = SDL_TYPE_V35; sdt->sdl.config.ifrate = 56000; sdt->sdl.config.ifgtype = SDL_GTYPE_NONE; sdt->sdl.config.ifgrate = 0; sdt->sdl.config.ifmode = SDL_MODE_DTE; sdt->sdl.config.ifgmode = SDL_GMODE_NONE; sdt->sdl.config.ifgcrc = SDL_GCRC_NONE; sdt->sdl.config.ifclock = SDL_CLOCK_DPLL; sdt->sdl.config.ifcoding = SDL_CODING_NRZI; sdt->sdl.config.ifframing = SDL_FRAMING_NONE; sdt->sdl.config.blksize = 0; sdt->sdl.config.ifleads = 0; sdt->sdl.config.ifalarms = 0; sdt->sdl.config.ifrxlevel = 0; sdt->sdl.config.iftxlevel = 0; /* sdt->sdt.confing.ifsyncsrc[i] = 0; */ sdt->sdl.timestamp = jiffies; sdt->sdl.tickbytes = sdt->sdl.config.ifrate / HZ / 8; sdt->sdl.bytecount = 0; /* rest zero */ /* SDT configuration defaults */ bufq_init(&sdt->sdt.tb); sdt->sdt.config.Tin = 4; sdt->sdt.config.Tie = 1; sdt->sdt.config.T = 64; sdt->sdt.config.D = 256; sdt->sdt.config.t8 = 100 * HZ / 1000; sdt->sdt.config.Te = 577169; sdt->sdt.config.De = 9308000; sdt->sdt.config.Ue = 144292000; sdt->sdt.config.N = 16; sdt->sdt.config.m = 272; sdt->sdt.config.b = 8; sdt->sdt.config.f = SDT_FLAGS_ONE; printd(("%s: setting module private structure defaults\n", SDT_MOD_NAME)); } else ptrace(("%s: ERROR: Could not allocate module private structure\n", SDT_MOD_NAME)); return (sdt); } STATIC void sdt_free_priv(queue_t * q) { sdt_t *sdt = SDT_PRIV(q); int flags = 0; ensure(sdt, return); lis_spin_lock_irqsave(&sdt->lock, &flags); { if (sdt->sdt.rx_cmp) freemsg(xchg(&sdt->sdt.rx_cmp, NULL)); if (sdt->sdt.tx_cmp) freemsg(xchg(&sdt->sdt.tx_cmp, NULL)); if (sdt->rbuf) freemsg(xchg(&sdt->rbuf, NULL)); if (sdt->xbuf) freemsg(xchg(&sdt->xbuf, NULL)); sdt_unbufcall(q); sdt_timer_stop(q, t8); sdt_timer_stop(q, t9); bufq_purge(&sdt->sdt.tb); if ((*sdt->prev = sdt->next)) sdt->next->prev = sdt->prev; sdt->next = NULL; sdt->prev = NULL; sdt_put(sdt); sdt->oq->q_ptr = NULL; sdt_put(sdt); sdt->iq->q_ptr = NULL; sdt_put(sdt); } lis_spin_unlock_irqrestore(&sdt->lock, &flags); printd(("%s: unlinked module private structure\n", SDT_MOD_NAME)); if (atomic_read(sdt->refcnt)) { assure(!atomic_read(sdt->refcnt)); printd(("%s: WARNING: sdt->refcnt = %d\n", SDT_MOD_NAME, atomic_read(sdt->refcnt))); } sdt_put(sdt); /* final put */ return; } /* * ========================================================================= * * OPEN and CLOSE * * ========================================================================= * Open is called on the first open of a character special device stream * head; close is called on the last close of the same device. */ sdt_t *sdt_list = NULL; STATIC int sdt_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp) { (void) crp; /* for now */ MOD_INC_USE_COUNT; /* keep module from unloading in our face */ if (q->q_ptr != NULL) { MOD_DEC_USE_COUNT; return (0); /* already open */ } if (sflag == MODOPEN || WR(q)->q_next != NULL) { if (!(sdt_alloc_priv(q, &sdt_list, getmajor(*devp), getminor(*devp)))) { MOD_DEC_USE_COUNT; return ENOMEM; } /* generate immediate information request */ t_info_req(q); return (0); } MOD_DEC_USE_COUNT; return EIO; } STATIC int sdt_close(queue_t * q, int flag, cred_t * crp) { (void) flag; (void) crp; sdt_free_priv(q); MOD_DEC_USE_COUNT; return (0); } /* * ========================================================================= * * LiS Module Initialization (For unregistered driver.) * * ========================================================================= */ STATIC int sdt_initialized = 0; STATIC void sdt_init(void) { unless(sdt_initialized, return); cmn_err(CE_NOTE, SS7IP_BANNER); /* console splash */ if ((sdt_initialized = sdt_init_caches())) return; if (!(sdt_initialized = lis_register_strmod(&sdt_info, SDT_MOD_NAME))) { sdt_term_caches(); cmn_err(CE_WARN, "%s: couldn't register module", SDT_MOD_NAME); return; } sdt_initialized = 1; return; } STATIC void sdt_terminate(void) { int err; ensure(sdt_initialized, return); if ((err = lis_unregister_strmod(&sdt_info))) cmn_err(CE_PANIC, "%s: couldn't unregister module", SDT_MOD_NAME); sdt_initialized = 0; sdt_term_caches(); return; } /* * ========================================================================= * * Kernel Module Initialization * * ========================================================================= */ int init_module(void) { sdt_init(); if (sdt_initialized < 0) return sdt_initialized; return (0); } void cleanup_module(void) { sdt_terminate(); return; }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |