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/tc/tc.c#ident "@(#) $RCSfile: tc.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/14 12:13:44 $" static char const ident[] = "$RCSfile: tc.c,v $ $Name: $($Revision: 0.8.2.2 $) $Date: 2003/04/14 12:13:44 $"; /* * This is a TC (Transaction Capabilities) mulitplexing driver for SS7 TCAP. * It is opened and SCCP streams are I_LINKed under the driver by the TC User * or I_PLINKed under the driver by the TC control stream for use by TC * Users. TC Users are associated with lower SCCP streams via the TC bind * commands. * * This includes a management stream for module reporting and configuration * management. */ #define TC_DESCRIP "TC STREAMS MULTIPLEXING DRIVER." #define TC_COPYRIGHT "Copyright (c) 1997-2002 OpenSS7 Corporation. All Rights Reserved." #define TC_DEVICE "Part of the OpenSS7 Stack for LiS STREAMS." #define TC_CONTACT "Brian Bidulock <bidulock@openss7.org>" #define TC_LICENSE "GPL" #define TC_BANNER TC_DESCRIP "\n" \ TC_COPYRIGHT "\n" \ TC_DEVICE "\n" \ TC_CONTACT "\n" #ifdef MODULE MODULE_AUTHOR(TC_CONTACT); MODULE_DESCRIPTION(TC_DESCRIP); MODULE_SUPPORTED_DEVICE(TC_DEVICE); #ifdef MODULE_LICENSE MODULE_LICENSE(TC_LICENSE); #endif #define MODULE_STATIC static #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT #define MODULE_STATIC #endif #ifdef TC_DEBUG static int tc_debug = TC_DEBUG; #else static int tc_debug = 2; #endif #ifndef TC_CMAJOR #define TC_CMAJOR 210 #endif #define TC_NMINOR 255 #define TC_MODULE_ID ('s'<<24|'s'<<16|'7'<<8|TC_IOC_MAGIC) #ifdef LIS_2_12 #define INT int #else #define INT void #endif /* * ========================================================================= * * STREAMS Definitions * * ========================================================================= */ static struct module_info tc_minfo = { TC_MODULE_ID, /* Module ID number */ "tcap", /* Module name */ 1, /* Min packet size accepted *//* XXX */ 254, /* Max packet size accepted *//* XXX */ 8 * 512, /* Hi water mark *//* XXX */ 1 * 512 /* Lo water mark *//* XXX */ }; static int tc_open(queue_t *, dev_t *, int, int, cred_t *); static int tc_close(queue_t *, int, cred_t *); static INT tc_u_rsrv(queue_t *); static INT tc_u_wput(queue_t *, mblk_t *); static INT tc_u_wsrv(queue_t *); static struct qinit tc_u_rinit = { NULL, /* Read put (msg from below) */ tc_u_rsrv, /* Read queue service */ tc_open, /* Each open */ tc_close, /* Last close */ NULL, /* Admin (not used) */ &tc_minfo, /* Information */ NULL /* Statistics */ }; static struct qinit tc_u_wrinit = { tc_u_wput, /* Write put (msg from above) */ tc_u_wsrv, /* Write queue service */ NULL, /* Each open */ NULL, /* Last close */ NULL, /* Admin (not used) */ &tc_info, /* Information */ NULL /* Statistics */ }; static INT tc_l_wsrv(queue_t *); static INT tc_l_rput(queue_t *, mblk_t *); static INT tc_l_rsrv(queue_t *); static struct qinit tc_l_winit = { NULL, /* Write put (msg from above) */ tc_l_wsrv, /* Write queue service */ NULL, /* Each open */ NULL, /* Last close */ NULL, /* Admin (not used) */ &tc_info, /* Information */ NULL /* Statistics */ }; static struct qinit tc_l_rinit = { tc_l_rput, /* Read put (msg from above) */ tc_l_rsrv, /* Read queue service */ NULL, /* Each open */ NULL, /* Last close */ NULL, /* Admin (not used) */ &tc_info, /* Information */ NULL /* Statistics */ }; MODULE_STATIC struct streamtab tc_info = { &tc_u_rinit, /* Upper read queue */ &tc_u_winit, /* Upper write queue */ &tc_l_rinit, /* Lower read queue */ &tc_l_winit /* Lower write queue */ }; /* * ========================================================================= * * TC Private Structures * * ========================================================================= */ struct tc; struct sccp; typedef struct tc { struct tc *next; /* list of all TC-Users */ struct tc **prev; /* list of all TC-Users */ queue_t *q; /* associated read queue */ dev_t devid; /* device id at open */ uint state; /* interface state */ } sccp_t; static uint tc_next_transaction_id = 1; /* * ========================================================================= * * TC Message Structures * * ========================================================================= */ // #include "tc_msg.h" /* * ========================================================================= * * Module --> Module (Control Messages) * * ========================================================================= */ static inline mblk_t *m_error(int r_error, int w_error) { mblk_t *mp; if ((mp = allocb(2, BPRI_HI))) { mp->b_datap->db_type = M_ERROR; *(mp->b_wptr)++ = r_error; *(mp->b_wptr)++ = w_error; } return (mp); } static inline mblk_t *m_hangup(void) { mblk_t *mp; if ((mp = allogb(0, BPRI_HI))) { mp->b_datap->db_type = M_HANGUP; } return (mp); } static inline mblk_t *m_flush(int flags, int band) { mblk_t *mp; if ((mp = allocb(2, BPRI_HI))) { mp->b_datap->db_type = M_FLUSH; *(mp->b_wptr)++ = flags; *(mp->b_wptr)++ = band; } return (mp); } /* * ========================================================================= * * TC --> TC-User (Upstream Primitives sent upstream) * * ========================================================================= */ /* * TC_INFO_ACK Information acknowledgement * ------------------------------------------------------------------------- */ static inline mblk_t *tc_info_ack(void) { mblk_t *mp; TC_info_ack_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_info_ack_t *) mp->b_wptr; p->PRIM_type = TC_INFO_ACK; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_BIND_ACK Bound to global title * ------------------------------------------------------------------------- */ static inline mblk_t *tc_bind_ack(void) { mblk_t *mp; TC_bind_ack_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_bind_ack_t *) mp->b_wptr; p->PRIM_type = TC_BIND_ACK; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_OK_ACK Success acknowledgement * ------------------------------------------------------------------------- */ static inline mblk_t *tc_ok_ack(void) { mblk_t *mp; TC_ok_ack_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_ok_ack_t *) mp->b_wptr; p->PRIM_type = TC_OK_ACK; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_ERROR_ACK Error acknowledgement * ------------------------------------------------------------------------- */ static inline mblk_t *tc_error_ack(void) { mblk_t *mp; TC_error_ack_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_error_ack_t *) mp->b_wptr; p->PRIM_type = TC_ERROR_ACK; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_UNIDIR_IND Unidirectional Indication * ------------------------------------------------------------------------- */ static inline mblk_t *tc_unidir_ind(void) { mblk_t *mp; TC_unidir_ind_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_unidir_ind_t *) mp->b_wptr; p->PRIM_type = TC_UNIDIR_IND; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_QUERY_W_PERM_IND Incoming Query with permission to continue * ------------------------------------------------------------------------- */ static inline mblk_t *tc_query_w_perm_ind(void) { mblk_t *mp; TC_query_w_perm_ind_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_query_w_perm_ind_t *) mp->b_wptr; p->PRIM_type = TC_QUERY_W_PERM_IND; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_QUERY_WO_PERM_IND Incoming Query without permission to continue * ------------------------------------------------------------------------- */ static inline mblk_t *tc_query_wo_perm_ind(void) { mblk_t *mp; TC_query_wo_perm_ind_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_query_wo_perm_ind_t *) mp->b_wptr; p->PRIM_type = TC_QUERY_WO_PERM_IND; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_RESP_IND Incoming Response * ------------------------------------------------------------------------- */ static inline mblk_t *tc_resp_ind(void) { mblk_t *mp; TC_resp_ind_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_resp_ind_t *) mp->b_wptr; p->PRIM_type = TC_RESP_IND; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_CONV_W_PERM_IND Incoming Conversation with permission to continue * ------------------------------------------------------------------------- */ static inline mblk_t *tc_conv_w_perm_ind(void) { mblk_t *mp; TC_conv_w_perm_ind_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_conv_w_perm_ind_t *) mp->b_wptr; p->PRIM_type = TC_CONV_W_PERM_IND; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_CONV_WO_PERM_IND Incoming Conversation without permission to continue * ------------------------------------------------------------------------- */ static inline mblk_t *tc_conv_wo_perm_ind(void) { mblk_t *mp; TC_conv_wo_perm_ind_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_conv_wo_perm_ind_t *) mp->b_wptr; p->PRIM_type = TC_CONV_WO_PERM_IND; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_CONV_W_PERM_CON Incoming Conversation with permission to continue * ------------------------------------------------------------------------- */ static inline mblk_t *tc_conv_w_perm_con(void) { mblk_t *mp; TC_conv_w_perm_con_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_conv_w_perm_con_t *) mp->b_wptr; p->PRIM_type = TC_CONV_W_PERM_CON; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_CONV_WO_PERM_CON Incoming Conversation without permission to continue * ------------------------------------------------------------------------- */ static inline mblk_t *tc_conv_wo_perm_con(void) { mblk_t *mp; TC_conv_wo_perm_con_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_conv_wo_perm_con_t *) mp->b_wptr; p->PRIM_type = TC_CONV_WO_PERM_CON; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * TC_ABORT_IND Abort indication * ------------------------------------------------------------------------- */ static inline mblk_t *tc_abort_ind(void) { mblk_t *mp; TC_abort_ind_t *p; if ((mp = allocb(sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (T_abort_ind_t *) mp->b_wptr; p->PRIM_type = TC_ABORT_IND; /* * FIXME: more... */ mp->b_wptr += sizeof(*p); } return (mp); } /* * ========================================================================= * * TC --> SCCP (Downstream Primitives sent downstream) * * ========================================================================= */ #include "../npi/npi_user.h" /* * N_INFO_REQ 5 - Information Request * ------------------------------------------------------------------------- */ static int sccp_info_req(void) { return n_info_req(); } /* * N_BIND_REQ 6 - Bind a NS user to network address * ------------------------------------------------------------------------- */ static int sccp_bind_req(uint flags, sccp_addr_t * bnd, uint32_t ssn) { return n_bind_req(flags, 0, (caddr_t) bnd, bnd ? sizeof(*bind) : 0, (caddr_t) & ssn, sizeof(ssn)); } /* * N_UNBIND_REQ 7 - Unbind NS user from network address * ------------------------------------------------------------------------- */ static int sccp_unbind_req(void) { return n_unbind_req(); } /* * N_UNITDATA_REQ 8 - Connection-less data send request * ------------------------------------------------------------------------- */ static int sccp_unitdata_req(sccp_addr_t * dst, mblk_t * dp) { return n_unitdata_req((caddr_t) dst, dst ? sizeof(*dst) : 0, dp); } /* * N_OPTMGMT_REQ 9 - Options Management request * ------------------------------------------------------------------------- */ static int sccp_optmgmt_req(uint flags, caddr_t qos_ptr, size_t qos_len) { return n_optmgmt_req(flags, qos_ptr, qos_len); } /* * ========================================================================= * * TC SEND Peer Messages * * ========================================================================= */ static inline mblk_t *tc_enc_uni(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } static inline mblk_t *tc_enc_qry_invoke(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } static inline mblk_t *tc_enc_qwp_invoke(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } static inline mblk_t *tc_enc_res_reply(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } static inline mblk_t *tc_enc_cnv_invoke(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } static inline mblk_t *tc_enc_cwp_invoke(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } static inline mblk_t *tc_enc_cnv_reply(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } static inline mblk_t *tc_enc_cwp_reply(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } static inline mblk_t *tc_enc_abort_reject(mblk_t * dp) { mblk_t *mp; size_t mlen = FIXME; if ((mp = allocb(mlen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; /* * FIXME: build message... */ mp->b_cont = dp; } return (mp); } /* * ========================================================================= * * TC OUTPUT EVENT Translator * * ========================================================================= */ static int (*tc_o_events[]) (queue_t *, mblk_t *) = { NULL, /* TCE_NONE 0x00 - (reserved) */ tc_o_uni_inv, /* TCE_UNI_INV 0x01 - Unidirectional Invoke */ tc_o_qry_inv, /* TCE_QRY_INV 0x02 - Query w/o perm (Invoke) */ tc_o_qwp_inv, /* TCE_QWP_INV 0x03 - Query w/ perm (Invoke) */ tc_o_rsp_rep, /* TCE_RSP_REP 0x04 - Response (Reply) */ tc_o_cnv_inv, /* TCE_CNV_INV 0x05 - Conv w/o perm (Invoke) */ tc_o_cwp_inv, /* TCE_CWP_INV 0x06 - Conv w perm (Invoke) */ tc_o_cnv_rep, /* TCE_CNV_REP 0x07 - Conv w/o perm (Reply) */ tc_o_cwp_rep, /* TCE_CWP_REP 0x08 - Conv w perm (Reply) */ tc_o_abt_rej /* TCE_ABT_REJ 0x09 - Abort (Reject) */ }; static inline int tc_o_proto(queue_t * q, mblk_t * mp) { tc_event_t *m = (tc_event_t *) mp->b_wptr; return (*tc_o_events[m->event]) (q, mp); } /* * ========================================================================= * * TC INPUT EVENT State Machines * * ========================================================================= * These functions take an input event at the TC stream and run the state * machine associated with the TC stream. */ /* * TCE_UNI_INV 0x01 - Unidirectional (Invoke) * ---------------------------------------------------- */ static int tc_i_uni_inv(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } /* * TCE_QRY_INV 0x02 - Query w/o perm (Invoke) * ---------------------------------------------------- * ANSI: TID * * Open a new transaction id and assign it to the stream (or provide it to * the TC-User on single-threaded streams). Mark without permission and * reject TC-User attempts at conversation. */ static int tc_i_qry_inv(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } /* * TCE_QWP_INV 0x03 - Query w/ perm (Invoke) * ---------------------------------------------------- * ANSI: TID * * Open a new transaction id and assign it to the stream (or provide it to * the TC-User on single-threaded streams). Mark with permission and permit * TC-User attempts at conversation. */ static int tc_i_qwp_inv(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } /* * TCE_RSP_REP 0x04 - Response (Reply) * ---------------------------------------------------- * ANSI: TID * * Close a transaction. Pass response to user (with transaction id on * multiple-threaded streams). */ static int tc_i_rsp_rep(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } /* * TCE_CNV_INV 0x05 - Conv w/o perm (Invoke) * ---------------------------------------------------- * ANSI: Originating TID, Responding TID * * Continue a transaction. Assign a correlation id to this conversation. * Pass conversation to user (with transaction id on multiple-threaded * streams.) Mark conversation without permission and reject additional * conversational attempts from TC-User on this transaction. * * If conversation is not permitted, reject the invoke and abort the * transaction. Pass an abort to the user. */ static int tc_i_cnv_inv(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } /* * TCE_CWP_INV 0x06 - Conv w perm (Invoke) * ---------------------------------------------------- * ANSI: Originating TID, Responding TID * * Continue a transaction. Assign a correlation to this conversation. * Pass conversation to user (with correlation id and transaction id on * multiple-threaded streams.) Mark conversation with permission and permit * additional convesration attempts from TC-user on this conversation. * * If conversation is not permitted, reject the invoke and abort the * transaction. Pass an abort to the user. */ static int tc_i_cwp_inv(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } /* * TCE_CNV_REP 0x07 - Conv w/o perm (Reply) * ---------------------------------------------------- * ANSI: Originating TID, Responding TID * * Close the conversation and deallocate the correlation id. */ static int tc_i_cnv_rep(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } /* * TCE_CWP_REP 0x08 - Conv w perm (Reply) * ---------------------------------------------------- * ANSI: Originating TID, Responding TID * * Close the conversation and deallocate the correlation id. */ static int tc_i_cwp_rep(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } /* * TCE_ABT_REJ 0x09 - Abort (Reject) * ---------------------------------------------------- * ANSI: TID * * Abort the conversation and the transaction. Deallocate transaction id and * all correlation ids. */ static int tc_i_abt_rej(queue_t * q, mblk_t * mp) { tc_t *tc = (tc_t *) q->q_ptr; tc_event_t *e = (tc_event_t *) mp->b_rptr; /* * FIXME: process event... */ return (0); } static int (*tc_i_events[]) (queue_t *, mblk_t *) = { NULL, /* TCE_NONE 0x00 - (reserved) */ tc_i_uni_inv, /* TCE_UNI_INV 0x01 - Unidirectional Invoke */ tc_i_qry_inv, /* TCE_QRY_INV 0x02 - Query w/o perm (Invoke) */ tc_i_qwp_inv, /* TCE_QWP_INV 0x03 - Query w/ perm (Invoke) */ tc_i_rsp_rep, /* TCE_RSP_REP 0x04 - Response (Reply) */ tc_i_cnv_inv, /* TCE_CNV_INV 0x05 - Conv w/o perm (Invoke) */ tc_i_cwp_inv, /* TCE_CWP_INV 0x06 - Conv w perm (Invoke) */ tc_i_cnv_rep, /* TCE_CNV_REP 0x07 - Conv w/o perm (Reply) */ tc_i_cwp_rep, /* TCE_CWP_REP 0x08 - Conv w perm (Reply) */ tc_i_abt_rej /* TCE_ABT_REJ 0x09 - Abort (Reject) */ }; static inline int tc_i_proto(queue_t * q, mblk_t * mp) { tc_event_t *e = (tc_event_t *) mp->b_rptr; return (*tc_i_events[e->event]) (q, mp); } /* * ========================================================================= * * TC RECV Peer Messages * * ========================================================================= */ static inline int sccp_dec_uni(caddr_t p, caddr_t e, tc_event_t * e) { return (0); } static inline int sccp_dec_qry(caddr_t p, caddr_t e, tc_event_t * e) { return (0); } static inline int sccp_dec_qwp(caddr_t p, caddr_t e, tc_event_t * e) { return (0); } static inline int sccp_dec_cnv(caddr_t p, caddr_t e, tc_event_t * e) { return (0); } static inline int sccp_dec_cwp(caddr_t p, caddr_t e, tc_event_t * e) { return (0); } static inline int sccp_dec_res(caddr_t p, caddr_t e, tc_event_t * e) { return (0); } static inline int sccp_dec_abt(caddr_t p, caddr_t e, tc_event_t * e) { return (0); } /* * ========================================================================= * * SCCP --> TC (Upstream Primitives received from downstream) * * ========================================================================= */ /* * N_INFO_ACK 16 - Information Acknowledgement * ---------------------------------------------------------------- */ static int sccp_info_ack(queue_t * q, mblk_t * pdu) { sccp_t *sccp = (sccp_t *) q->q_ptr; N_info_ack_t *p = (N_info_ack_t *) pdu->b_rptr; if (pdu->b_wptr = pdu->b_rptr < sizeof(*p)) return m_error_all(q, pdu, EFAULT, EFAULT); sccp->flags = p->OPTIONS_flags; sccp->nsdu = p->NSDU_size; sccp->nidu = p->NIDU_size; sccp->nodu = p->NODU_size; if (p->ADDR_length >= sizeof(sccp_addr_t)) { sccp_addr_t *add = (sccp_addr_t *) (pdu->b_rptr + p->ADDR_offset); bcopy(add, sccp->bnd, sizeof(*add) + add->alen); } if (p->PROTOID_length == 1) sccp->ssn = *((uint8_t &) (pdu->b_rptr + p->PROTOID_offset)); sccp->state = p->CURRENT_state; freemsg(pdu); return (0); } /* * N_BIND_ACK 17 - NS User bound to network address * ---------------------------------------------------------------- */ static int sccp_bind_ack(queue_t * q, mblk_t * pdu) { sccp_t *sccp = (sccp_t *) q->q_ptr; N_bind_ack_t *p = (N_bind_ack_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return m_error_ack(q, pdu, EFAULT, EFAULT); if (sccp->state != NS_WACK_BREQ) return m_error_ack(q, pdu, EPROTO, EPROTO); if (p->ADDR_length >= sizeof(sccp_addr_t)) { sccp_addr_t *add = (sccp_addr_t *) (pdu->b_rptr + p->ADDR_offset); bcopy(add, sccp->bnd, sizeof(*add) + add->alen); } if (p->PROTOID_length == 1) sccp->ssn = *((uint8_t &) (pdu->b_rptr + p->PROTOID_offset)); sccp->state = NS_IDLE; freemsg(pdu); return (0); } /* * N_ERROR_ACK 18 - Error Acknowledgement * ---------------------------------------------------------------- */ static int sccp_error_ack(queue_t * q, mblk_t * pdu) { sccp_t *sccp = (sccp_t *) q->q_ptr; N_error_ack_t *p = (N_error_ack_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return m_error_ack(q, pdu, EFAULT, EFAULT); switch (sccp->state) { case NS_WACK_BREQ: if (p->ERROR_prim == N_BIND_REQ) sccp->state = NS_UNBND; break; case NS_WACK_UREQ: if (p->ERROR_prim == N_UNBIND_REQ) sccp->state = NS_IDLE; break; case NS_WACK_OPTREQ: if (p->ERROR_prim == N_OPTMGMT_REQ) sccp->state = sccp->state; break; case NS_WACK_CREQ: if (p->ERROR_prim == N_CONN_REQ) sccp->state = NS_IDLE; break; case NS_WACK_CIND: if (p->ERROR_prim == N_CONN_RES) sccp->state = sccp->state; break; case NS_WACK_CRES: case NS_DATA_XFER: case NS_WCON_RREQ: case NS_WRES_RIND: case NS_WACK_DREQ6: case NS_WACK_DREQ7: case NS_WACK_DREQ9: case NS_WACK_DREQ10: case NS_WACK_DREQ11: default: case NS_NOSTATES: } freemsg(pdu); return (0) } /* * N_OK_ACK 19 - Success Acknowledgement * ---------------------------------------------------------------- */ static int sccp_ok_ack(queue_t * q, mblk_t * mp) { sccp_t *sccp = (sccp_t *) q->q_ptr; N_ok_ack_t *p = (N_ok_ack_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return m_error_ack(q, pdu, EFAULT, EFAULT); switch (sccp->state) { case NS_WACK_BREQ: case NS_WACK_UREQ: case NS_WACK_OPTREQ: case NS_WACK_CREQ: case NS_WACK_CIND: case NS_WACK_CRES: case NS_DATA_XFER: case NS_WCON_RREQ: case NS_WRES_RIND: case NS_WACK_DREQ6: case NS_WACK_DREQ7: case NS_WACK_DREQ9: case NS_WACK_DREQ10: case NS_WACK_DREQ11: default: case NS_NOSTATES: } /* * TODO: more... */ freemsg(pdu); return (0) } /* * N_UNITDATA_IND 20 - Connection-less data receive indication * ---------------------------------------------------------------- */ static int sccp_unitdata_ind(queue_t * q, mblk_t * pdu) { sccp_t *sccp = (sccp_t *) q->q_ptr; N_unitdata_ind_t *p = (N_unitdata_ind_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return m_error_all(q, pdu, EFAULT, EFAULT); if (p->SRC_length < sizeof(sccp_addr_t)) return m_error_all(q, pdu, EFAULT, EFAULT); if (p->DEST_length < sizeof(sccp_addr_t)) return m_error_all(q, pdu, EFAULT, EFAULT); if (!pdu->b_cont) return (-EINVAL); return tc_recv_msg(q, pdu); } /* * N_UDERROR_IND 21 - UNITDATA Error Indication * ---------------------------------------------------------------- */ static int sccp_uderror_ind(queue_t * q, mblk_t * pdu) { tc_t *tc; int ecode; sccp_t *sccp = (sccp_t *) q->q_ptr; N_uderror_ind_t *p = (N_uderror_ind_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return m_error_all(q, pdu, EFAULT, EFAULT); if (p->DEST_length < sizeof(sccp_addr_t)) return m_error_all(q, pdu, EFAULT, EFAULT); /* * TODO: have to propagate error to TC-Users. Most of these turn * into aborts (TC_ABORT_IND). We still have to generate a * TCE_ABT_RES event. */ for (tc = sccp->tc; tc; tc = tc->sccp_next) { if (tc->outcnt > 0) { /* * Send TCE_ABT_RES event to tc user.... */ } } freemsg(pdu); return (0); } int (*sccp_dprim[]) (queue_t *, mblk_t *) = { #define SCCP_DSTR_FIRST N_CONN_REQ NULL, /* N_CONN_REQ 0 - NC request */ NULL, /* N_CONN_RES 1 - Accept prev NC indication */ NULL, /* N_DISOCN_REQ 2 - NC disconnect request */ NULL, /* N_DATA_REQ 3 - CO data transfer request */ NULL, /* N_EXDATA_REQ 4 - CO data transfer request */ NULL, /* N_INFO_REQ 5 - Information request */ NULL, /* N_BIND_REQ 6 - Bind an NS user */ NULL, /* N_UNBIND_REQ 7 - Unbind an NS user */ NULL, /* N_UNITDATA_REQ 8 - CL data transfer request */ NULL, /* N_OPTMGMT_REQ 9 - Options mgmt request */ NULL, /* (not used) 10 - (not used) */ NULL, /* N_CONN_IND 11 - Incoming NC indication */ NULL, /* N_CONN_CON 12 - NC established */ NULL, /* N_DISCON_IND 13 - NC disconnected */ NULL, /* N_DATA_IND 14 - CO data transfer indication */ NULL, /* N_EXDATA_IND 15 - CO data transfer indication */ sccp_info_ack, /* N_INFO_ACK 16 - Information Acknowledgement */ sccp_bind_ack, /* N_BIND_ACK 17 - NS User bound Ack */ sccp_error_ack, /* N_ERROR_ACK 18 - Error Acknowledgement */ sccp_ok_ack, /* N_OK_ACK 19 - Success Acknowledgement */ sccp_unitdata_ind, /* N_UNITDATA_IND 20 - CL data transfer indication */ sccp_uderror_ind, /* N_UDERROR_IND 21 - CL data error indication */ NULL, /* (not used) 22 - (not used) */ NULL, /* N_DATACK_REQ 23 - Data ack request */ NULL, /* N_DATACK_IND 24 - Data ack indication */ NULL, /* N_RESET_REQ 25 - NC reset request */ NULL, /* N_RESET_IND 26 - Incoming NC reset ind */ NULL, /* N_RESET_RES 27 - Reset processing accepted */ NULL /* N_RESET_CON 28 - Reset processing complete */ #define SCCP_DSTR_LAST N_RESET_CON }; static int tc_r_data(queue_t * q, mblk_t * mp) { /* * This is an SCCP unitdata message from below. We don't get SCCP * unitdata messages as M_DATA, so if this happens we ignore it. */ freemsg(mp); return (0); } static int tc_r_proto(queue_t * q, mblk_t * mp); { } /* * ========================================================================= * * TC-User --> TC (Downstream Primitives received from upstream) * * ========================================================================= */ /* * TC Options parsing. */ static int parse_options(queue_t * q, mblk_t * pdu, int prim, caddr_t opt_ptr, size_t opt_len) { int err; tc_t *tc = (tc_t *) q->q_ptr; if (opt_len && opt_len < sizeof(uint32_t)) return tc_error_ack_reply(q, pdu, prim, TCBADOPT); /* * Decode the options.... */ return (0); } /* * TC_INFO_REQ 0 - Information request * --------------------------------------------------------------- */ static int user_info_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_info_req_t *p = (TC_info_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_INFO_REQ, -EMSGSIZE); /* * TODO: process primitive... */ freemsg(pdu); return (0); } /* * TC_BIND_REQ 1 - Bind to network address * --------------------------------------------------------------- */ static int user_bind_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_bind_req_t *p = (TC_bind_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_UNBIND_REQ 2 - Unbind from network address * --------------------------------------------------------------- */ static int user_unbind_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_unbind_req_t *p = (TC_unbind_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_UNIDIR_REQ 3 - Unidirectional request * --------------------------------------------------------------- */ static int user_unidir_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_unidir_req_t *p = (TC_unidir_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_BEGIN_REQ 4 - Begin transaction request * --------------------------------------------------------------- */ static int user_begin_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_begin_req_t *p = (TC_begin_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_QUERY_W_PERM_REQ 5 - Begin transaction request * --------------------------------------------------------------- */ static int user_query_w_perm_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_query_w_perm_req_t *p = (TC_query_w_perm_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_QUERY_WO_PERM_REQ 6 - Begin transaction request * --------------------------------------------------------------- */ static int user_query_wo_perm_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_query_wo_perm_req_t *p = (TC_query_wo_perm_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_END_REQ 7 - End transaction request * --------------------------------------------------------------- */ static int user_end_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_end_req_t *p = (TC_end_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_RESP_REQ 8 - End transaction request * --------------------------------------------------------------- */ static int user_resp_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_resp_req_t *p = (TC_resp_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_CONTINUE_REQ 9 - Continue transaction request * --------------------------------------------------------------- */ static int user_continue_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_continue_req_t *p = (TC_continue_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_CONTINUE_RES 10 - Continue transaction response * --------------------------------------------------------------- */ static int user_continue_res(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_continue_res_t *p = (TC_continue_res_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_CONV_W_PERM_REQ 11 - Continue transaction request * --------------------------------------------------------------- */ static int user_conv_w_perm_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_conv_w_perm_req_t *p = (TC_conv_w_perm_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_CONV_WO_PERM_REQ 12 - Continue transaction request * --------------------------------------------------------------- */ static int user_conv_wo_perm_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_conv_wo_perm_req_t *p = (TC_conv_wo_perm_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_CONV_W_PERM_RES 13 - Continue transaction response * --------------------------------------------------------------- */ static int user_conv_w_perm_res(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_conv_w_perm_res_t *p = (TC_conv_w_perm_res_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_CONV_WO_PERM_RES 14 - Continue transaction response * --------------------------------------------------------------- */ static int user_conv_wo_perm_res(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_conv_wo_perm_res_t *p = (TC_conv_wo_perm_res_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } /* * TC_ABORT_REQ 15 - Abort transaction request * --------------------------------------------------------------- */ static int user_abort_req(queue_t * q, mblk_t * pdu) { int err; tc_t *tc = (tc_t *) q->q_ptr; TC_abort_req_t *p = (TC_abort_req_t *) pdu->b_rptr; if (pdu->b_wptr - pdu->b_rptr < sizeof(*p)) return tc_error_ack_reply(q, TC_BIND_REQ, -EMSGSIZE); /* * TODO: process primitive... */ tc->state = NS_IDLE; freemsg(pdu); return (0); } int (*user_dprim[]) (queue_t *, mblk_t *) = { #define USER_DSTR_FIRST TC_INFO_REQ tc_info_req, /* TC_INFO_REQ 0 - Information request */ tc_bind_req, /* TC_BIND_REQ 1 - Bind to network address */ tc_unbind_req, /* TC_UNBIND_REQ 2 - Unbind from network address */ tc_unidir_req, /* TC_UNIDIR_REQ 3 - Unidirectional request */ tc_begin_req, /* TC_BEGIN_REQ 4 - Begin transaction request */ tc_qwp_req, /* TC_QUERY_W_PERM_REQ 5 - Begin transaction request */ tc_qwop_req, /* TC_QUERY_WO_PERM_REQ 6 - Begin transaction request */ tc_end_req, /* TC_END_REQ 7 - End transaction request */ tc_resp_req, /* TC_RESP_REQ 8 - End transaction request */ tc_cont_req, /* TC_CONTINUE_REQ 9 - Continue transaction request */ tc_cont_res, /* TC_CONTINUE_RES 10 - Continue transaction response */ tc_cwp_req, /* TC_CONV_W_PERM_REQ 11 - Continue transaction request */ tc_cwop_req, /* TC_CONV_WO_PERM_REQ 12 - Continue transaction request */ tc_cwp_res, /* TC_CONV_W_PERM_RES 13 - Continue transaction response */ tc_cwop_res, /* TC_CONV_WO_PERM_RES 14 - Continue transaction response */ tc_abort_req /* TC_ABORT_REQ 15 - Abort transaction request */ #define USER_DSTR_LAST TC_ABORT_REQ }; static int tc_w_data(queue_t * q, mblk_t * pdu) { tc_t *tc = (tc_t *) q->q_ptr; if (tc->state != TCS_XACT_OPEN) { /* * If we are not in a connection oriented state, we return an * M_ERROR on all subsequent write operations on the stream * will fail. */ mblk_t *mp; if (!(mp = m_error(0, EPROTO))) return (-ENOBUFS); qreply(q, mp); freemsg(pdu); return (0); } /* * This is an TC data message from above. This is only valid in * connection-oriented states. */ /* * TODO: pass data message to TC. */ return (0); } static int tc_w_proto(queue_t * q, mblk_t * mp) { uint32_t prim = *((uint32_t *) mp->b_rptr); if (USER_DSTR_FIRST <= prim && prim <= USER_DSTR_LAST && user_dprim[prim]) return (*user_dprim[prim]) (q, mp); /* * FIXME: We should probably send an M_ERROR here. */ return (-EOPNOTSUPP); } /* * ========================================================================= * * M_CTL Processing * * ========================================================================= * We have no defined module to module controls for either TC or SCCP. */ static int tc_r_ctl(queue_t * q, mblk_t * pdu) { int err; mblk_t *mp; if ((err = m_error_all(q, NULL, EFAULT, EFAULT))) return (err); if (!(mp = sccp_unbind_req())) return (-ENOBUFS); qreply(q, mp); freemsg(pdu); return (0); } static int tc_w_ctl(queue_t * q, mblk_t * pdu) { /* * FIXME: Remove this TC-User from the SCCP-Provider... */ return m_error_reply(q, pdu, EFAULT, EFAULT); } /* * ========================================================================= * * M_ERROR Processing * * ========================================================================= * We propagate any M_ERROR(s) received on the lower stream from the SCCP * Provider to every TC-User upper stream which is using the SCCP provider. * We also set states here to indicate the error. This is because if we run * out of mblks to propagate the M_ERROR, we will discard the priority * message (M_ERROR) if we are in the read service routine. Setting the * error ensures that we will respond with an M_ERROR later when resources * are available. */ static int tc_r_error(queue_t * q, mblk_t * pdu) { /* * FIXME: Notify this SCCP-Provider's management stream that the * SCCP-Provider has errored out... */ return m_error_all(q, pdu, *(pdu->r_ptr + 4), *(pdu->w_ptr - 1)); } /* * ========================================================================= * * M_HANGUP Processing * * ========================================================================= * We propagate any M_HANGUP(s) received on the lower stream from the SCCP * Provider to every TC-User upper stream which is using the SCCP provider. * We also set states here to indicate the error. This is because if we run * out of mblks to propagate the M_HANGUP, we will discard the priority * message (M_HANGUP) if we are in the read service routine. Setting the * error ensures that we will respond with an M_HANGUP later when resources * are available. */ static int tc_r_hangup(queue_t * q, mblk_t * pdu) { /* * FIXME: Notify this SCCP-Provider's management stream that the * SCCP-Provider has hung up... */ return m_hangup_all(q, pdu); } /* * ========================================================================= * * M_FLUSH Processing * * ========================================================================= * We turn flushes around at the multiplexing driver. When we receive * flushes from above we turn them back around upwards. When we receive * flushes from below we turn them back around downwards. */ /* * M_FLUSH from below (i.e. from SCCP Provider) * ------------------------------------------------------------------------- * We want to take flushes from below and turn them back around back down to * the SCCP provider. */ static int tc_r_flush(queue_t * q, mblk_t * pdu) { int err; if (*mp->b_rptr & FLUSHR) { if (*mp->b_rptr & FLUSHBAND) flushband(q, mp->b_rptr[1], FLUSHALL); else flushq(q, FLUSHALL); /* flush all TC-Users upwards */ m_flush_all(q, NULL, mp->b_rptr[0], mp->b_rptr[1]); *mp->b_rptr &= ~FLUSHR; } if (!(mp->b_flag & MSGNOLOOP)) { if (*mp->b_rptr & FLUSHW) { if (*mp->b_rptr & FLUSHBAND) flushband(WR(q), mp->b_rptr[1], FLUSHALL); else flushq(WR(q), FLUSHALL); mp->b_flag |= MSGNOLOOP; qreply(q, mp); /* flush all the way back down */ return (0); } } freemsg(mp); return (0); } /* * M_FLUSH from above (i.e. from TC User) * ------------------------------------------------------------------------- * We want to take flushes from above and turn them back around back up to * the TC User. */ static int tc_w_flush(queue_t * q, mblk_t * pdu) { if (*mp->b_rptr & FLUSHW) { if (*mp->b_rptr & FLUSHBAND) flushband(q, mp->b_rptr[1], FLUSHALL); else flushq(q, FLUSHALL); *mp->b_rptr &= ~FLUSHW; } if (!(mp->b_flag & MSGNOLOOP)) { if (*mp->b_rptr & FLUSHR) { if (*mp->b_rptr & FLUSHBAND) flushband(RD(q), mp->b_rptr[1], FLUSHALL); else flushq(RD(q), FLUSHALL); mp->b_flag |= MSGNOLOOP; qreply(q, mp); /* flush all the way back up */ return (0); } } freemsg(mp); return (0); } /* * M_FLUSH internal from below. * ------------------------------------------------------------------------- */ static int tc_i_flush(queue_t * q, mblk_t * pdu) { if (*mp->b_rptr & FLUSHR) { if (*mp->b_rptr & FLUSHBAND) flushband(q, mp->b_rptr[1], FLUSHALL); else flushq(q, FLUSHALL); } putnext(q, mp); } /* * M_FLUSH internal from above. * ------------------------------------------------------------------------- */ static int tc_o_flush(queue_t * q, mblk_t * pdu) { if (*mp->b_rptr & FLUSHW) { if (*mp->b_rptr & FLUSHBAND) flushband(q, mp->b_rptr[1], FLUSHALL); else flushq(q, FLUSHALL); } putnext(q, mp); } /* * ========================================================================= * * M_IOCTL Processing * * ========================================================================= * * I_LINK, I_PLINK Handling * ------------------------------------------------------------------------- * SCCP Provider streams can be linked under the TC multiplexing driver. We * permit normal TC streams to perform an I_LINK of SCCP Provider streams * under the SCCP driver, but we only permit the TC control stream to * perform an I_PLINK of SCCP Provider streams. This is so that if the * configuration daemon crashes it can come back an the configuration has not * been destroyed. * * When linked, we generate an N_INFO_REQ downstream to the newly linked SCCP * Provider. The purpose of this is to discover the response from the * provider as to which state the provider is currently in and which * addresses the SCCP Provider might be currently bound to. This permits the * caller to bind the SCCP Provider. */ static inline int tc_i_link(queue_t * q, mblk_t * mp, struct linkblk *lp) { mblk_t *mp; sccp_t *sccp; queue_t *lq; ensure(lp, return (-EFAULT)); lq = RD(lp->l_qbot); if (!(mp = sccp_info_req())) return (-ENOBUFS); if (!(sccp = kmalloc(sizeof(*sccp), GFP_KERNEL))) { freemsg(mp); return (-ENOMEM); } bzero(sccp, sizeof(*sccp)); sccp->q = RD(lq); sccp->lmq = RD(q); sccp->muxid = lp->l_index; sccp->state = NS_UNBND; RD(lq)->q_ptr = WR(lq)->q_ptr = sccp; putnext(WR(lq), mp); /* get info on SCCP provider */ if (sccp->state != NS_IDLE) { /* sccp providers must be prebound */ RD(lq)->q_ptr = WR(lq)->q_ptr = NULL; kfree(sccp); return (-EINVAL); } if ((sccp->next = sccp_links)) sccp->next->prev = &sccp->next; sccp->prev = &sccp_links; return (lp->l_index); } /* * I_UNLINK, I_PUNLINK Handling * ------------------------------------------------------------------------- * When we unlink, if we still have referencing upper TC-User streams, we * send each of the a M_HANGUP message indicating the loss of the connection. * This might result in a SIG_PIPE signal being sent to the process if the * TC-User is a stream head. */ static inline int tc_i_unlink(queue_t * q, struct linkblk *lp) { int err; sccp_t *sccp; uint muxid; ensure(lp, return (-EFAULT)); muxid = lp->l_index; for (sccp = sccp_links; sccp && sccp->muxid != muxid; sccp = sccp->next); if (!sccp) return (-EINVAL); if ((err = m_flush_all(q, NULL, FLUSHW, 0))) return (err); if ((err = m_hangup_all(q, NULL))) return (err); if (sccp->state == NS_IDLE) { mblk_t *mp; if (!(mp = sccp_unbind_req())) return (-ENOBUFS); sccp->state = NS_WACK_UREQ; putnext(WR(sccp->q), mp); } if ((*(sccp->prev) = sccp->next)) sccp->next->prev = sccp->prev; sccp->q->q_ptr = WR(sccp->q)->q_ptr = NULL; kfree(sccp); return (0); } static int tc_w_ioctl(queue_t * q, mblk_t * pdu) { int ret = -EINVAL; void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL; struct iocblk *iocp = (struct iocblk *) mp->b_wptr; int cmd = iocp->ioc_cmd; switch (_IOC_TYPE(cmd)) { case __SID: switch (cmd) { case I_PLINK: if (RD(q) != tc_lmq) { ret = -EPERM; break; } case I_LINK: ret = tc_i_link(q, arg); break; case I_PUNLINK: if (RD(q) != tc_lmq) { ret = -EPERM; break; } case I_UNLINK: ret = tc_i_unlink(q, arg); break; } break; case TC_IOC_MAGIC: if (iocp->ioc_count >= _IOC_SIZE(cmd)) { int nr = _IOC_NR(cmd); ret = -EOPNOTSUPP; if (0 <= nr && nr < sizeof(tc_ioctl) / sizeof(int (*)(void)) && tc_ioctl[nr]) ret = (*tc_ioctl[nr]) (q, cmd, arg); } break; default: ret = -EOPNOTSUPP; break; } mp->b_datap->db_type = ret < 0 ? M_IOCNAK : M_IOCACK; iocp->ioc_error = ret < 0 ? -ret : 0; iocp->ioc_rval = ret < 0 ? -1 : ret; qreply(q, mp); return (0); } /* * ========================================================================= * * QUEUE PUT and SERVICE routines * * ========================================================================= */ static INT tc_u_wput(queue_t *, mblk_t *); static INT tc_u_wsrv(queue_t *); static INT tc_l_wsrv(queue_t *); static INT tc_l_rput(queue_t *, mblk_t *); static INT tc_u_rsrv(queue_t *); static INT tc_l_rsrv(queue_t *); static inline int tc_recover(queue_t * q, mblk_t * mp, int err) { switch (err) { case -EBUSY: case -EAGAIN: case -ENOMEM: case -ENOBUFS: putq(q, mp); return (0); } freemsg(mp); return (err); } /* * READ PUT and SERVICE (Message from below SCCP-Provider --> TC) * ------------------------------------------------------------------------- */ static inline int tc_rd(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return tc_r_data(q, mp); case M_PROTO: case M_PCPROTO: return tc_r_proto(q, mp); case M_CTL: return tc_r_ctl(q, mp); case M_ERROR: return tc_r_error(q, mp); case M_FLUSH: return tc_r_flush(q, mp); case M_HANGUP: return tc_r_hangup(q, mp); } return (-EOPNOTSUPP); } static INT tc_l_rput(queue_t * q, mblk_t * mp) { int err; if (mp->b_datap->db_type < QPCTL && (q->q_count || !canputnext(q))) { putq(q, mp); return (INT) (0); } if ((err = tc_rd(q, mp))) return (INT) (tc_recover(q, mp, err)); return (INT) (0); } static INT tc_l_rsrv(queue_t * q) { mblk_t *mp; while ((mp = getq(q))) { int err; if (!(err = tc_rd(q, mp))) continue; if (mp->b_datap->db_type < QPCTL) return (INT) (tc_recover(q, mp, err)); freemsg(mp); return (INT) (err); } return (INT) (0); } /* * WRITE PUT and SERVICE (Message from above TC-User --> TC) * ------------------------------------------------------------------------- */ static inline int tc_wr(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db - type) { case M_DATA: return tc_w_data(q, mp); case M_PROTO: case M_PCPROTO: return tc_w_proto(q, mp); case M_CTL: return tc_w_ctl(q, mp); case M_FLUSH: return tc_w_flush(q, mp); case M_IOCTL: return ttcioctl(q, mp); } return (-EOPNOTSUPP); } static INT tc_u_wput(queue_t * q, mblk_t * mp) { int err; if (mp->b_datap->db_type < QPCTL && (q->q_count || !canputnext(q))) { putq(q, mp); return (INT) (0); } if ((err = tc_wr(q, mp))) return (INT) (tc_recover(q, mp, err)); return (INT) (0); } static INT tc_u_wsrv(queue_t * q) { mblk_t *mp; while ((mp = getq(q))) { int err; if (!(err = tc_wr(q, mp))) continue; if (mp->b_datap->db_type < QPCTL) return (INT) (tc_recover(q, mp, err)); freemsg(mp); return (INT) (err); } return (INT) (0); } /* * READ SERVICE (Event from below as input to TC State Machines) * ------------------------------------------------------------------------- */ static inline int tc_input(queue_t * q, mblk_t * mp) { uint type = mp->b_datap->db_type; if (M_DATA <= type && type <= M_STOPI && tc_input_ops[type]) return (*tc_input_ops[type]) (q, mp); putnext(q, mp); return (0); } static inline int tc_input(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return (-EOPNOTSUPP); case M_PROTO: case M_PCPROTO: return tc_i_proto(q, mp); case M_FLUSH: return tc_i_flush(q, mp); } putnext(q, mp); return (0); } static INT tc_u_rsrv(queue_t * q) { mblk_t *mp; while ((mp = getq(q))) { int err; if (!(err = tc_input(q, mp))) continue; if (mp->b_datap->db_type < QPCTL) return (INT) (tc_recover(q, mp, err)); freemsg(mp); return (INT) (err); } } /* * WRITE SERVICE (Event from above as output from TC State Machines) * ------------------------------------------------------------------------- */ static inline int tc_output(queue_t * q, mblk_t * mp) { switch (mp->b_datap->db_type) { case M_DATA: return (-EOPNOTSUPP); case M_PROTO: case M_PCPROTO: return tc_o_proto(q, mp); case M_FLUSH: return tc_o_flush(q, mp); } putnext(q, mp); return (0); } static INT tc_l_wsrv(queue_t * q) { mblk_t *mp; while ((mp = getq(q))) { int err; if (!(err = tc_output(q, mp))) continue; if (mp->b_datap->db_type < QPCTL) return (INT) (tc_recover(q, mp, err)); freemsg(mp); return (INT) (err); } } /* * ========================================================================= * * OPEN and CLOSE * * ========================================================================= */ static int tc_open(queue_t *, dev_t *, int, int, cred_t *); static int tc_close(queue_t *, int, cred_t *); static int tc_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp); { tc_t *tc, **tcp = &tc_opens; int cmajor = getmajor(*devp); int cminor = getminor(*devp); if (q->q_ptr != NULL) return (0); /* already open */ if (sflag == MODOPEN || WR(q)->q_next) return (EIO); /* cannot push as module */ if (!cminor && !tc_lmq) { if (crp->cr_ruid != 0) return (EPERM); /* need r00t */ tc_lmq = q; } else if (!cminor || sflag == CLONEOPEN) { cminor = 1; } for (; cminor <= TC_NMINOR && *tcp; tcp = &(*tcp)->next) { int dminor = getminor((*tcp)->devid); if (cminor < dminor) break; if (cminor == dminor) { if (sflag == CLONEOPEN) { cminor++; continue; } return (EIO); } } if (cminor > TC_NMINOR) return (ENXIO); *devp = makedevice(cmajor, cminor); if (!(tc = kmalloc(sizeof(*tc), GFP_KERNEL))) return (ENOMEM); bzero(tc, sizeof(*tc)); q->q_ptr = WR(q)->q_ptr = tc; tc->q = q; tc->devid = *devp; tc->state = 0; if ((tc->next = *tcp)) tc->next->prev = &tc->next; tc->prev = tcp; *tcp = tc; return (0); } static int tc_close(queue_t * q, int flag, cred_t * crp) { tc_t *tc = (tc_t *) q->q_ptr; if (!tc) return (EIO); if (q == tc_lmq) tc_lmq = NULL; if (tc->sccp) { /* * Detach this TC stream from whatever SCCP streams it is * using on the lower half. */ sccp_t *sccp = tc->sccp; if ((--tc->sccp->use_count) <= 0) { /* * Unbind the SCCP stream if it is not being used by * any TC streams. */ mblk_t *mp; N_unbind_req_t *p; if (!(mp = allocb(sizeof(*p), BPRI_MED))) return (ENOSR); mp->b_datap->db_type = M_PCPROTO; p = (N_unbind_req_t *) mp->b_wptr; p->PRIM_type = N_UNBIND_REQ; mp->b_wptr += sizeof(*p); sccp->state = NS_WACK_UREQ; putnext(WR(sccp->q), mp); sccp->use_count = 0; } if ((*(tc->sccp_prev) = tc->sccp_next)) tc->sccp_next->sccp_prev = tc->sccp_prev; } /* * TODO: Might want to do some more things to deallocate some TC * structures associated with the module. I.e. some TC bindings. */ if ((*(tc->prev) = tc->next)) tc->next->prev = tc->prev; WR(q)->q_ptr = RD(q)->q_ptr = NULL; kfree(tc); return (0); } /* * ========================================================================= * * LIS STREAMS INITIALIZATION * * ========================================================================= */ static int tc_initialized = 0; #ifndef LIS_REGISTERED static inline int tc_init(void) #else __initfunc(int tc_init(void)) #endif { int err; if (tc_initialized) return (0); tc_initialized = 1; cmn_err(CE_NOTE, TC_BANNER); /* console splash */ #ifndef LIS_REGISTERED if ((err = lis_register_strmod(&tc_info, tc_minfo.ni_name)) < 0) cmn_err(CE_WARN "tc: couldn't register module!\n"); return (err); #endif } #ifndef LIS_REGISTERED static inline void tc_terminate(void) #else __initfunc(void tc_terminate(void)) #endif { if (!tc_initialized) return; tc_initialized = 0; #ifndef LIS_REGISTERED if (lis_unregister_strmod(&tc_info)) cmn_err(CE_WARN "tc: couldn't unregister module!\n"); #endif return; } /* * ========================================================================= * * LINUX MODULE INITIALIZATION * * ========================================================================= */ #ifdef MODULE int init_module(void) { return tc_init(); } void cleanup_module(void) { tc_terminate(); } #endif
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |