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/x400p-ss7/sdl_x400p.c#ident "@(#) $RCSfile: sdl_x400p.c,v $ $Name: $($Revision: 0.8.2.8 $) $Date: 2003/04/14 12:13:57 $" static char const ident[] = "$RCSfile: sdl_x400p.c,v $ $Name: $($Revision: 0.8.2.8 $) $Date: 2003/04/14 12:13:57 $"; /* * This is an SDL (Signalling Data Link) kernel module which provides all of * the capabilities of the SDLI for the E400P-SS7 and T400P-SS7 cards. The * SDT and SL modules can be pushed over streams opened on this driver to * implement a complete SS7 MTP Level 2 OpenSS7 implementation. */ #define _DEBUG 1 #define X400P_DOWNLOAD_FIRMWARE #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 <sys/dki.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/ioport.h> #include <asm/io.h> #include <asm/dma.h> #include <linux/pci.h> #include "../debug.h" #include "../priv.h" #include "../lock.h" #include "../queue.h" #include "../allocb.h" #include "../timer.h" #include <ss7/lmi.h> #include <ss7/lmi_ioctl.h> #include <ss7/sdli.h> #include <ss7/sdli_ioctl.h> #ifdef X400P_DOWNLOAD_FIRMWARE #include "x400pfw.h" /* X400P-SS7 firmware load */ #endif #define X400P_DESCRIP "E/T400P-SS7: SS7/SDL (Signalling Data Link) STREAMS DRIVER." #define X400P_COPYRIGHT "Copyright (c) 1997-2002 OpenSS7 Corporation. All Rights Reserved." #define X400P_DEVICE "Supports the T/E400P-SS7 T1/E1 PCI boards." #define X400P_CONTACT "Brian Bidulock <bidulock@openss7.org>" #define X400P_LICENSE "GPL" #define X400P_BANNER X400P_DESCRIP "\n" \ X400P_COPYRIGHT "\n" \ X400P_DEVICE "\n" \ X400P_CONTACT "\n" MODULE_AUTHOR(X400P_CONTACT); MODULE_DESCRIPTION(X400P_DESCRIP); MODULE_SUPPORTED_DEVICE(X400P_DEVICE); #ifdef MODULE_LICENSE MODULE_LICENSE(X400P_LICENSE); #endif #ifndef X400P_SDL_CMAJOR #error "X400P_SDL_CMAJOR must be defined\n" #endif #define X400P_SDL_NMAJOR 4 #define X400P_SDL_NMINOR 255 /* * ======================================================================= * * STREAMS Definitions * * ======================================================================= */ #define X400P_DRV_ID SDL_IOC_MAGIC #define X400P_DRV_NAME "x400p-sdl" STATIC struct module_info xp_rinfo = { mi_idnum:X400P_DRV_ID, /* Module ID number */ mi_idname:X400P_DRV_NAME "-rd", /* Module name */ mi_minpsz:1, /* Min packet size accepted */ mi_maxpsz:128, /* Max packet size accepted */ mi_hiwat:1, /* Hi water mark */ mi_lowat:0, /* Lo water mark */ }; STATIC struct module_info xp_winfo = { mi_idnum:X400P_DRV_ID, /* Module ID number */ mi_idname:X400P_DRV_NAME "-wr", /* Module name */ mi_minpsz:1, /* Min packet size accepted */ mi_maxpsz:280, /* Max packet size accepted */ mi_hiwat:1, /* Hi water mark */ mi_lowat:0, /* Lo water mark */ }; STATIC int xp_open(queue_t *, dev_t *, int, int, cred_t *); STATIC int xp_close(queue_t *, int, cred_t *); STATIC struct qinit xp_rinit = { qi_putp:ss7_oput, /* Read put (message from below) */ qi_srvp:ss7_osrv, /* Read queue service */ qi_qopen:xp_open, /* Each open */ qi_qclose:xp_close, /* Last close */ qi_minfo:&xp_rinfo, /* Information */ }; STATIC struct qinit xp_winit = { qi_putp:ss7_iput, /* Write put (message from above) */ qi_srvp:ss7_isrv, /* Write queue service */ qi_minfo:&xp_winfo, /* Information */ }; STATIC struct streamtab xp_info = { st_rdinit:&xp_rinit, /* Upper read queue */ st_wrinit:&xp_winit, /* Upper write queue */ }; /* * ======================================================================== * * Private structure * * ======================================================================== */ struct xp; struct cd; struct sp; typedef struct xp { STR_DECLARATION (struct xp); /* stream declaration */ struct cd *cd; /* card for this channel */ struct sp *sp; /* span for this channel */ int card; /* index (card) */ int span; /* index (span) */ int chan; /* index (chan) */ int slot; /* index (slot) */ uchar *tx_base; /* chan tx base */ uchar *rx_base; /* chan rx base */ volatile mblk_t *tx_msg; /* chan tx message */ lmi_option_t option; /* LMI protocol and variant options */ volatile sdl_config_t config; /* SDT configuration */ sdl_statem_t statem; /* SDT state machine variables */ sdl_notify_t notify; /* SDT notification options */ sdl_stats_t stats; /* SDT statistics */ sdl_stats_t stamp; /* SDT statistics timestamps */ sdl_stats_t statsp; /* SDT statistics periods */ } xp_t; #define XP_PRIV(__q) ((struct xp *)(__q)->q_ptr) STATIC struct xp *xp_alloc_priv(queue_t *, struct xp **, dev_t *, cred_t *); STATIC void xp_free_priv(struct xp *); STATIC struct xp *xp_get(struct xp *); STATIC void xp_put(struct xp *); typedef struct sp { HEAD_DECLARATION (struct sp); /* head declaration */ struct cd *cd; /* card for this span */ struct xp *slots[32]; /* timeslot structures */ ulong recovertime; /* alarm recover time */ ulong iobase; /* span iobase */ int card; /* index (card) */ int span; /* index (span) */ volatile sdl_config_t config; /* span configuration */ } sp_t; STATIC struct sp *xp_alloc_sp(struct cd *, uint8_t); STATIC void xp_free_sp(struct sp *); STATIC struct sp *sp_get(struct sp *); STATIC void sp_put(struct sp *); typedef struct cd { HEAD_DECLARATION (struct cd); /* head declaration */ ulong xll_region; /* Xilinx 32-bit memory region */ ulong xll_length; /* Xilinx 32-bit memory length */ volatile uint32_t *xll; /* Xilinx 32-bit memory map */ ulong xlb_region; /* Xilinx 8-bit memory region */ ulong xlb_length; /* Xilinx 8-bit memory lenght */ volatile uint8_t *xlb; /* Xilinx 8-bit memory map */ ulong plx_region; /* PLX 9030 memory region */ ulong plx_length; /* PLX 9030 memory length */ volatile uint16_t *plx; /* PLX 9030 memory map */ uint frame; /* frame number */ struct sp *spans[4]; /* structures for spans */ volatile uint32_t *wbuf; /* wr buffer */ volatile uint32_t *rbuf; /* rd buffer */ volatile int eval_syncsrc; /* need to reevaluate sync src */ volatile int leds; /* leds on the card */ int card; /* index (card) */ ulong irq; /* card irq */ ulong iobase; /* card iobase */ struct tasklet_struct tasklet; /* card tasklet */ volatile sdl_config_t config; /* card configuration */ } cd_t; STATIC struct cd *xp_alloc_cd(void); STATIC void xp_free_cd(struct cd *); STATIC struct cd *cd_get(struct cd *); STATIC void cd_put(struct cd *); /* * ------------------------------------------------------------------------ * * Card Structures and Macros * * ------------------------------------------------------------------------ */ #ifdef X400P_DOWNLOAD_FIRMWARE #define GPIOC (0x54 >> 1) /* GPIO control register */ #define GPIO_WRITE 0x4000 /* GPIO4 data */ #define GPIO_PROGRAM 0x20000 /* GPIO5 data */ #define GPIO_INIT 0x100000 /* GPIO6 data */ #define GPIO_DONE 0x800000 /* GPIO7 data */ #endif #define INTCSR (0x4c >> 1) #define PLX_INTENA 0x43 #define SYNREG 0x400 #define CTLREG 0x401 #define LEDREG 0x402 #define STAREG 0x400 #define RIR2 0x31 #define LOOPUP 0x80 #define LOOPDN 0x40 #define INTENA 0x01 #define OUTBIT 0x02 #define E1DIV 0x10 #define INTACK 0x80 #define INTACTIVE 2 #define SYNCSELF 0 #define SYNC1 1 #define SYNC2 2 #define SYNC3 3 #define SYNC4 4 #define LEDBLK 0 #define LEDGRN 1 #define LEDRED 2 #define LEDYEL 3 #define X400_ABIT 8 #define X400_BBIT 4 #define X400P_ALARM_SETTLE_TIME 5000 /* allow alarms to settle for 5 seconds */ /* *INDENT-OFF* */ /* * Mapping of channels 0-23 for T1, 1-31 for E1 into PCM highway timeslots. */ STATIC int xp_t1_chan_map[] = { 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15, 17, 18, 19, 21, 22, 23, 25, 26, 27, 29, 30, 31 }; STATIC int xp_e1_chan_map[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; typedef enum { PLX9030 = 0, PLXDEVBRD, X400P, X400PSS7, } xp_board_t; /* indexed by xp_board_t above */ static struct { char *name; u32 hw_flags; } xp_board_info[] __devinitdata = { { "PLX 9030", 0}, { "PLX Development Board", 0}, { "X400P-SS7", 1}, }; STATIC struct pci_device_id xp_pci_tbl[] __devinitdata = { {PCI_VENDOR_ID_PLX, 0x9030, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PLX9030}, {PCI_VENDOR_ID_PLX, 0x3001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PLXDEVBRD}, {PCI_VENDOR_ID_PLX, 0xD00D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X400P}, {PCI_VENDOR_ID_PLX, 0x0557, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X400PSS7}, {0,} }; STATIC int __devinit xp_probe(struct pci_dev *, const struct pci_device_id *); STATIC void __devexit xp_remove(struct pci_dev *); STATIC int xp_suspend(struct pci_dev *pdev, u32 state); STATIC int xp_resume(struct pci_dev *pdev); STATIC struct pci_driver xp_driver = { name: "x400p-ss7", probe: xp_probe, remove: __devexit_p(xp_remove), id_table: xp_pci_tbl, #ifdef CONFIG_PM suspend: xp_suspend, resume: xp_resume, #endif }; /* *INDENT-ON* */ STATIC int x400p_boards = 0; STATIC struct cd *x400p_cards; #define X400P_EBUFNO (1<<7) /* 128k elastic buffers */ /* * ======================================================================== * * PRIMITIVES Sent Upstream * * ======================================================================== */ /* * M_ERROR * ----------------------------------- */ STATIC int m_error(queue_t *q, struct xp *xp, int err) { 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; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * SDL_RECEIVED_BITS_IND * ----------------------------------- * Actually, we never want to use this function: we want to just send M_DATA. */ STATIC INLINE int sdl_received_bits_ind(queue_t *q, struct xp *xp, mblk_t *dp) { if (canputnext(xp->oq)) { mblk_t *mp; sdl_received_bits_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->sdl_primitive = SDL_RECEIVED_BITS_IND; mp->b_cont = dp; ss7_oput(xp->oq, mp); return (QR_ABSORBED); } rare(); return (-ENOBUFS); } rare(); return (-EBUSY); } /* * SDL_DISCONNECT_IND * ----------------------------------- */ STATIC INLINE int sdl_disconnect_ind(queue_t *q, struct xp *xp) { mblk_t *mp; sdl_disconnect_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->sdl_primitive = SDL_DISCONNECT_IND; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_INFO_ACK * ----------------------------------- */ STATIC INLINE int lmi_info_ack(queue_t *q, struct xp *xp, caddr_t ppa_ptr, size_t ppa_len) { 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 = LMI_UNATTACHED; p->lmi_max_sdu = 8; p->lmi_min_sdu = 8; p->lmi_header_len = 0; p->lmi_ppa_style = LMI_STYLE2; bcopy(ppa_ptr, mp->b_wptr, ppa_len); mp->b_wptr += ppa_len; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_OK_ACK * ----------------------------------- */ STATIC INLINE int lmi_ok_ack(queue_t *q, struct xp *xp, ulong state, long prim) { 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; p->lmi_state = xp->i_state = state; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_ERROR_ACK * ----------------------------------- */ STATIC INLINE int lmi_error_ack(queue_t *q, struct xp *xp, ulong state, long prim, ulong errno, ulong reason) { 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; p->lmi_state = xp->i_state = state; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_ENABLE_CON * ----------------------------------- */ STATIC INLINE int lmi_enable_con(queue_t *q, struct xp *xp) { 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; p->lmi_state = xp->i_state = LMI_ENABLED; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_DISABLE_CON * ----------------------------------- */ STATIC INLINE int lmi_disable_con(queue_t *q, struct xp *xp) { mblk_t *mp; lmi_disable_con_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { struct xp *xp = XP_PRIV(q); mp->b_datap->db_type = M_PCPROTO; p = ((typeof(p)) mp->b_wptr)++; p->lmi_primitive = LMI_DISABLE_CON; p->lmi_state = xp->i_state = LMI_DISABLED; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_OPTMGMT_ACK * ----------------------------------- */ STATIC INLINE int lmi_optmgmt_ack(queue_t *q, struct xp *xp, ulong flags, caddr_t opt_ptr, size_t opt_len) { 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; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_ERROR_IND * ----------------------------------- */ STATIC INLINE int lmi_error_ind(queue_t *q, struct xp *xp, ulong errno, ulong reason) { 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 = xp->i_state; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * LMI_STATS_IND * ----------------------------------- */ STATIC INLINE int lmi_stats_ind(queue_t *q, struct xp *xp, ulong interval) { if (canputnext(xp->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; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } rare(); return (-EBUSY); } /* * LMI_EVENT_IND * ----------------------------------- */ STATIC INLINE int lmi_event_ind(queue_t *q, struct xp *xp, ulong oid, ulong level) { if (canputnext(xp->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; ss7_oput(xp->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } rare(); return (-EBUSY); } /* * ========================================================================= * * EVENTS From Above * * ========================================================================= */ /* * SDL_BITS_FOR_TRANSMISSION_REQ * ----------------------------------- * It is preffered to just send M_DATA here. We just strip the proto block * off and put it back on the queue. */ STATIC int sdl_bits_for_transmission_req(queue_t *q, mblk_t *mp) { (void) q; (void) mp; return (QR_STRIP); } /* * SDL_CONNECT_REQ * ----------------------------------- */ STATIC int sdl_connect_req(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); if (xp->i_state != LMI_ENABLED) return m_error(q, xp, EPROTO); xp->config.ifflags |= (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING); return (QR_DONE); } /* * SDL_DISCONNECT_REQ * ----------------------------------- */ STATIC int sdl_disconnect_req(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); if (xp->i_state != LMI_ENABLED) return m_error(q, xp, EPROTO); xp->config.ifflags &= ~(SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING); return (QR_DONE); } /* * LMI_INFO_REQ * ----------------------------------- */ STATIC int lmi_info_req(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); uint16_t ppa = (xp->chan & 0xff) | ((xp->span & 0x0f) << 8) | ((xp->card & 0x0f) << 12); return lmi_info_ack(q, xp, (caddr_t) & ppa, sizeof(ppa)); } /* * LMI_ATTACH_REQ * ----------------------------------- */ STATIC int lmi_attach_req(queue_t *q, mblk_t *mp) { int err, card, span, chan, slot, byte, span_allocated = 0; struct cd *cd; struct sp *sp = NULL; uint16_t ppa; struct xp *xp = XP_PRIV(q); lmi_attach_req_t *p = ((typeof(p)) mp->b_rptr); if (mp->b_wptr - mp->b_rptr < sizeof(*p) + sizeof(ppa)) { ptrace(("X400P-SS7: ERROR: primitive too small = %d bytes\n", mp->b_wptr - mp->b_rptr)); goto lmi_badprim; } if (xp->i_state != LMI_UNATTACHED) { ptrace(("X400P-SS7: ERROR: interface out of state\n")); goto lmi_outstate; } xp->i_state = LMI_ATTACH_PENDING; ppa = *(typeof(ppa) *) (p + 1); /* check card */ card = (ppa >> 12) & 0x0f; for (cd = x400p_cards; cd && cd->card != card; cd = cd->next) ; if (!cd) { ptrace(("X400P-SS7: ERROR: invalid card %d\n", card)); goto lmi_badppa; } /* check span */ span = (ppa >> 8) & 0x0f; if (span < 0 || span > 3) { ptrace(("X400P-SS7: ERROR: invalid span %d\n", span)); goto lmi_badppa; } else { /* allocate span if required */ if (!(sp = cd->spans[span])) { printd(("X400P-SS7: allocating span %d to card %d\n", span, card)); if (!(sp = xp_alloc_sp(cd, span))) { ptrace(("X400P-SS7: ERROR: can't allocate span\n")); goto enomem; } span_allocated = 1; } } #if defined(__LITTLE_ENDIAN) byte = 3 - span; #elif defined(__BIG_ENDIAN) byte = span; #else #error "__LITTLE_ENDIAN or __BIG_ENDIAN must be defined\n" #endif if (sp->config.ifgtype != SDL_GTYPE_E1 && sp->config.ifgtype != SDL_GTYPE_T1) { swerr(); goto efault; } /* check chan */ chan = (ppa >> 0) & 0xff; if (chan) { /* specific channel indicated */ switch (cd->config.ifgtype) { case SDL_GTYPE_E1: if (chan < 1 || chan > 31) { ptrace(("E400P-SS7: ERROR: invalid chan %d\n", chan)); goto lmi_badppa; } slot = xp_e1_chan_map[chan]; if (sp->slots[slot]) { ptrace(("E400P-SS7: ERROR: slot %d in use\n", slot)); goto lmi_badppa; } if ((err = lmi_ok_ack(q, xp, LMI_DISABLED, LMI_ATTACH_REQ))) goto error; /* commit attach */ printd(("E400P-SS7: attaching card %d, span %d, chan %d, slot %d\n", card, span, chan, slot)); sp->slots[slot] = xp_get(xp); xp->slot = slot; xp->tx_base = ((uchar *) cd->wbuf) + (slot << 2) + byte; xp->rx_base = ((uchar *) cd->rbuf) + (slot << 2) + byte; /* set E1 related channel defaults */ xp->config.iftype = SDL_TYPE_DS0; break; case SDL_GTYPE_T1: if (chan < 1 || chan > 24) { ptrace(("T400P-SS7: ERROR: invalid chan %d\n", chan)); goto lmi_badppa; } slot = xp_t1_chan_map[chan]; if (sp->slots[slot]) { ptrace(("T400P-SS7: ERROR: slot %d in use\n", slot)); goto lmi_badppa; } if ((err = lmi_ok_ack(q, xp, LMI_DISABLED, LMI_ATTACH_REQ))) goto error; /* commit attach */ printd(("T400P-SS7: attaching card %d, span %d, chan %d, slot %d\n", card, span, chan, slot)); sp->slots[slot] = xp_get(xp); xp->slot = slot; xp->tx_base = ((uchar *) cd->wbuf) + (slot << 2) + byte; xp->rx_base = ((uchar *) cd->rbuf) + (slot << 2) + byte; /* set T1 related channel defaults */ xp->config.iftype = SDL_TYPE_DS0; break; } } else { int c; /* entire span indicated */ switch (cd->config.ifgtype) { case SDL_GTYPE_E1: for (c = 0; c < sizeof(xp_e1_chan_map) / sizeof(xp_e1_chan_map[0]); c++) if (sp->slots[xp_e1_chan_map[c]]) { ptrace(("E400P-SS7: ERROR: slot in use for chan %d\n", c)); goto lmi_badppa; } xp->slot = 0; if ((err = lmi_ok_ack(q, xp, LMI_DISABLED, LMI_ATTACH_REQ))) goto error; /* commit attach */ printd(("E400P-SS7: attaching card %d, entire span %d\n", card, span)); for (c = 0; c < sizeof(xp_e1_chan_map) / sizeof(xp_e1_chan_map[0]); c++) { slot = xp_e1_chan_map[c]; sp->slots[slot] = xp_get(xp); } xp->tx_base = ((uchar *) cd->wbuf) + (xp_e1_chan_map[0] << 2) + byte; xp->rx_base = ((uchar *) cd->rbuf) + (xp_e1_chan_map[0] << 2) + byte; xp->config.iftype = SDL_TYPE_E1; break; case SDL_GTYPE_T1: for (c = 0; c < sizeof(xp_t1_chan_map) / sizeof(xp_t1_chan_map[0]); c++) if (sp->slots[xp_t1_chan_map[c]]) { ptrace(("T400P-SS7: ERROR: slot in use for chan %d\n", c)); goto lmi_badppa; } xp->slot = 0; if ((err = lmi_ok_ack(q, xp, LMI_DISABLED, LMI_ATTACH_REQ))) goto error; /* commit attach */ printd(("T400P-SS7: attaching card %d, entire span %d\n", card, span)); for (c = 0; c < sizeof(xp_t1_chan_map) / sizeof(xp_t1_chan_map[0]); c++) { slot = xp_t1_chan_map[c]; sp->slots[slot] = xp_get(xp); } xp->tx_base = ((uchar *) cd->wbuf) + (xp_t1_chan_map[0] << 2) + byte; xp->rx_base = ((uchar *) cd->rbuf) + (xp_t1_chan_map[0] << 2) + byte; xp->config.iftype = SDL_TYPE_T1; break; default: swerr(); goto efault; } } xp->cd = cd_get(cd); cd->spans[span] = sp_get(sp); xp->sp = sp_get(sp); xp->i_state = LMI_DISABLED; xp->card = card; xp->span = span; xp->chan = chan; return (QR_DONE); { int errno, reason; enomem: errno = ENOMEM; reason = LMI_SYSERR; goto error_out; efault: errno = EFAULT; reason = LMI_SYSERR; goto error_out; lmi_outstate: errno = 0; reason = LMI_OUTSTATE; goto error_out; lmi_badprim: errno = 0; reason = LMI_BADPRIM; goto error_out; lmi_badppa: errno = 0; reason = LMI_BADPPA; goto error_out; error_out: if (span_allocated) xp_free_sp(sp); return lmi_error_ack(q, xp, LMI_UNATTACHED, LMI_ATTACH_REQ, errno, reason); } error: if (span_allocated) xp_free_sp(sp); return (err); } /* * LMI_DETACH_REQ * ----------------------------------- */ STATIC int lmi_detach_req(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); int err; /* validate detach */ if (xp->i_state != LMI_DISABLED) return lmi_error_ack(q, xp, xp->i_state, LMI_DETACH_REQ, 0, LMI_OUTSTATE); xp->i_state = LMI_DETACH_PENDING; if ((err = lmi_ok_ack(q, xp, LMI_UNATTACHED, LMI_DETACH_REQ))) return (err); /* commit detach */ if (xp->slot) { /* detaching from specific channel */ switch (xp->sp->config.ifgtype) { case SDL_GTYPE_E1: xp_put(xchg(&xp->sp->slots[xp->slot], NULL)); break; case SDL_GTYPE_T1: xp_put(xchg(&xp->sp->slots[xp->slot], NULL)); break; } } else { int c; /* detaching from entire span */ switch (xp->sp->config.ifgtype) { case SDL_GTYPE_E1: for (c = 0; c < sizeof(xp_e1_chan_map) / sizeof(xp_e1_chan_map[0]); c++) { xp_put(xchg(&xp->sp->slots[xp_e1_chan_map[c]], NULL)); } break; case SDL_GTYPE_T1: for (c = 0; c < sizeof(xp_t1_chan_map) / sizeof(xp_t1_chan_map[0]); c++) { xp_put(xchg(&xp->sp->slots[xp_t1_chan_map[c]], NULL)); } break; } } { // struct sp *sp = xp->sp; xp->tx_base = NULL; xp->rx_base = NULL; sp_put(xchg(&xp->sp, NULL)); cd_put(xchg(&xp->cd, NULL)); xp->card = 0; xp->span = 0; xp->chan = 0; xp->slot = 0; /* only referred to by card */ // if (sp->refcnt == 1) // xp_free_sp(sp); } return (QR_DONE); } /* * LMI_ENABLE_REQ * ----------------------------------- */ STATIC int lmi_enable_req(queue_t *q, mblk_t *mp) { int err, offset, slot = 0; struct xp *xp = XP_PRIV(q); struct cd *cd; struct sp *sp; /* validate enable */ if (xp->i_state != LMI_DISABLED) { ptrace(("X400P-SS7: ERROR: out of state: state = %ld\n", xp->i_state)); goto lmi_outstate; } if (!(cd = xp->cd)) { ptrace(("X400P-SS7: ERROR: out of state: no card pointer\n")); goto lmi_outstate; } if (!(sp = xp->sp)) { ptrace(("X400P-SS7: ERROR: out of state: no span pointer\n")); goto lmi_outstate; } if (xp->config.ifflags & SDL_IF_UP) { ptrace(("X400P-SS7: ERROR: out of state: device already up\n")); goto lmi_outstate; } if ((err = lmi_enable_con(q, xp))) return (err); /* commit enable */ printd(("X400P-SS7: performing enable\n")); xp->i_state = LMI_ENABLE_PENDING; if (!(sp->config.ifflags & SDL_IF_UP)) { /* need to bring up span */ int span = sp->span; int base = span << 8; uint8_t ccr1 = 0, tcr1 = 0; unsigned long timeout; switch (cd->config.ifgtype) { case SDL_GTYPE_E1: { printd(("E400P-SS7: performing enable on E1 span %d\n", span)); lis_spin_lock_irq(&cd->lock); // cd->xlb[SYNREG] = SYNCSELF; /* NO, NO, NO */ /* Tell ISR to re-evaluate the sync source */ cd->eval_syncsrc = 1; cd->xlb[CTLREG] = (E1DIV); cd->xlb[LEDREG] = 0xff; /* zero all span registers */ for (offset = 0; offset < 192; offset++) cd->xlb[base + offset] = 0x00; /* Set up for interleaved serial bus operation, byte mode */ if (span == 0) cd->xlb[base + 0xb5] = 0x09; else cd->xlb[base + 0xb5] = 0x08; cd->xlb[base + 0x1a] = 0x04; /* CCR2: set LOTCMC */ for (offset = 0; offset <= 8; offset++) cd->xlb[base + offset] = 0x00; for (offset = 0x10; offset <= 0x4f; offset++) if (offset != 0x1a) cd->xlb[base + offset] = 0x00; cd->xlb[base + 0x10] = 0x20; /* RCR1: Rsync as input */ cd->xlb[base + 0x11] = 0x06; /* RCR2: Sysclk = 2.048 Mhz */ cd->xlb[base + 0x12] = 0x09; /* TCR1: TSiS mode */ tcr1 = 0x09; /* TCR1: TSiS mode */ switch (sp->config.ifframing) { default: case SDL_FRAMING_CCS: ccr1 |= 0x08; break; case SDL_FRAMING_CAS: /* does this mean DS0A? */ tcr1 |= 0x20; break; } switch (sp->config.ifcoding) { default: case SDL_CODING_HDB3: ccr1 |= 0x44; break; case SDL_CODING_AMI: ccr1 |= 0x00; break; } switch (sp->config.ifgcrc) { case SDL_GCRC_CRC4: ccr1 |= 0x11; break; default: ccr1 |= 0x00; break; } cd->xlb[base + 0x12] = tcr1; cd->xlb[base + 0x14] = ccr1; cd->xlb[base + 0x18] = 0x20; /* 120 Ohm, Normal */ cd->xlb[base + 0x1b] = 0x8a; /* CRC3: LIRST & TSCLKM */ cd->xlb[base + 0x20] = 0x1b; /* TAFR */ cd->xlb[base + 0x21] = 0x5f; /* TNAFR */ cd->xlb[base + 0x40] = 0x0b; /* TSR1 */ for (offset = 0x41; offset <= 0x4f; offset++) cd->xlb[base + offset] = 0x55; for (offset = 0x22; offset <= 0x25; offset++) cd->xlb[base + offset] = 0xff; timeout = jiffies + 100 * HZ / 1000; lis_spin_unlock_irq(&cd->lock); while (jiffies < timeout) ; lis_spin_lock_irq(&cd->lock); cd->xlb[base + 0x1b] = 0x9a; /* CRC3: set ESR as well */ cd->xlb[base + 0x1b] = 0x82; /* CRC3: TSCLKM only */ sp->config.ifflags |= (SDL_IF_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING); /* enable interrupts */ cd->xlb[CTLREG] = (INTENA | E1DIV); lis_spin_unlock_irq(&cd->lock); } break; case SDL_GTYPE_T1: { int byte, val, c; unsigned short mask = 0; printd(("T400P-SS7: performing enable on T1 span %d\n", span)); lis_spin_lock_irq(&cd->lock); // cd->xlb[SYNREG] = SYNCSELF; /* NO, NO, NO */ /* Tell ISR to re-evaluate the sync source */ cd->eval_syncsrc = 1; cd->xlb[CTLREG] = 0; cd->xlb[LEDREG] = 0xff; for (offset = 0; offset < 160; offset++) cd->xlb[base + offset] = 0x00; /* Set up for interleaved serial bus operation, byte mode */ if (span == 0) cd->xlb[base + 0x94] = 0x09; else cd->xlb[base + 0x94] = 0x08; cd->xlb[base + 0x2b] = 0x08; /* Full-on sync required (RCR1) */ cd->xlb[base + 0x2c] = 0x08; /* RSYNC is an input (RCR2) */ cd->xlb[base + 0x35] = 0x10; /* RBS enable (TCR1) */ cd->xlb[base + 0x36] = 0x04; /* TSYNC to be output (TCR2) */ cd->xlb[base + 0x37] = 0x9c; /* Tx & Rx Elastic stor, sysclk(s) = 2.048 mhz, loopback controls (CCR1) */ cd->xlb[base + 0x12] = 0x22; /* Set up received loopup and loopdown codes */ cd->xlb[base + 0x14] = 0x80; cd->xlb[base + 0x15] = 0x80; /* Enable F bits pattern */ switch (sp->config.ifframing) { default: case SDL_FRAMING_SF: val = 0x20; break; case SDL_FRAMING_ESF: val = 0x88; break; } switch (sp->config.ifcoding) { default: case SDL_CODING_AMI: break; case SDL_CODING_B8ZS: val |= 0x44; break; } cd->xlb[base + 0x38] = val; if (sp->config.ifcoding != SDL_CODING_B8ZS) cd->xlb[base + 0x7e] = 0x1c; /* Set FDL register to 0x1c */ cd->xlb[base + 0x7c] = sp->config.iftxlevel << 5; /* LBO */ cd->xlb[base + 0x0a] = 0x80; /* LIRST to reset line interface */ timeout = jiffies + 100 * HZ / 1000; lis_spin_unlock_irq(&cd->lock); while (jiffies < timeout) ; lis_spin_lock_irq(&cd->lock); cd->xlb[base + 0x0a] = 0x30; /* LIRST bask to normal, Resetting elastic buffers */ sp->config.ifflags |= (SDL_IF_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING); /* enable interrupts */ cd->xlb[CTLREG] = (INTENA); lis_spin_unlock_irq(&cd->lock); /* establish which channels are clear channel */ for (c = 0; c < 24; c++) { byte = c >> 3; if (!cd->spans[span]->slots[xp_t1_chan_map[c]] || cd->spans[span]->slots[xp_t1_chan_map[c]]->config.iftype != SDL_TYPE_DS0A) mask |= 1 << (c % 8); if ((c % 8) == 7) cd->xlb[base + 0x39 + byte] = mask; } } break; } } xp->slot = slot; xp->config.ifname = sp->config.ifname; xp->config.ifflags = 0; xp->config.iftype = xp->config.iftype; switch (xp->config.iftype) { case SDL_TYPE_E1: xp->config.ifrate = 2048000; break; case SDL_TYPE_T1: xp->config.ifrate = 1544000; break; case SDL_TYPE_DS0: xp->config.ifrate = 64000; break; case SDL_TYPE_DS0A: xp->config.ifrate = 56000; break; } xp->config.ifmode = SDL_MODE_PEER; xp->config.ifflags |= SDL_IF_UP; xp->i_state = LMI_ENABLED; return (QR_DONE); lmi_outstate: return lmi_error_ack(q, xp, xp->i_state, LMI_ENABLE_REQ, 0, LMI_OUTSTATE); } /* * LMI_DISABLE_REQ * ----------------------------------- */ STATIC int lmi_disable_req(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); int err; /* validate disable */ if (xp->i_state != LMI_ENABLED) goto lmi_outstate; xp->i_state = LMI_DISABLE_PENDING; /* perform disable */ if ((err = lmi_disable_con(q, xp))) return (err); /* commit disable */ xp->config.ifflags = 0; return (QR_DONE); lmi_outstate: return lmi_error_ack(q, xp, xp->i_state, LMI_DISABLE_REQ, 0, LMI_OUTSTATE); } /* * LMI_OPTMGMT_REQ * ----------------------------------- */ STATIC int lmi_optmgmt_req(queue_t *q, mblk_t *mp) { (void) q; (void) mp; fixme(("FIXME: must check for options change\n")); return (QR_PASSALONG); } /* * ========================================================================= * * IO Controls * * ========================================================================= * * Test and Commit configuration settings * * ------------------------------------------------------------------------- */ STATIC int sdl_test_config(struct xp *xp, sdl_config_t * arg) { if (xp) { if (arg->ifflags) { trace(); return (-EINVAL); } switch (arg->iftype) { case SDL_TYPE_NONE: /* unknown/unspecified */ arg->iftype = xp->config.iftype; break; case SDL_TYPE_DS0: /* DS0 channel */ case SDL_TYPE_DS0A: /* DS0A channel */ /* yes we allow DS0A on E1 */ break; case SDL_TYPE_E1: /* full E1 span */ if (xp->sp->config.ifgtype != SDL_GTYPE_E1) { trace(); return (-EINVAL); } break; case SDL_TYPE_T1: /* full T1 span */ if (xp->sp->config.ifgtype != SDL_GTYPE_T1) { trace(); return (-EINVAL); } break; default: trace(); return (-EINVAL); } switch (arg->ifmode) { case SDL_MODE_NONE: /* */ arg->ifmode = xp->config.ifmode; break; case SDL_MODE_PEER: /* */ break; case SDL_MODE_ECHO: /* */ case SDL_MODE_REM_LB: /* */ case SDL_MODE_LOC_LB: /* */ case SDL_MODE_LB_ECHO: /* */ case SDL_MODE_TEST: /* */ break; default: trace(); return (-EINVAL); } } if (xp->sp) { switch (arg->ifgtype) { case SDL_GTYPE_NONE: /* */ arg->ifgtype = xp->sp->config.ifgtype; break; case SDL_GTYPE_T1: /* */ case SDL_GTYPE_E1: /* */ if (arg->ifgtype != xp->sp->config.ifgtype) { trace(); return (-EINVAL); } break; default: trace(); return (-EINVAL); } switch (arg->ifgcrc) { case SDL_GCRC_NONE: /* */ switch (arg->ifgtype) { case SDL_GTYPE_E1: arg->ifgcrc = SDL_GCRC_CRC5; break; case SDL_GTYPE_T1: arg->ifgcrc = SDL_GCRC_CRC6; break; default: trace(); return (-EINVAL); } break; case SDL_GCRC_CRC4: /* */ if (arg->ifgtype != SDL_GTYPE_E1) { trace(); return (-EINVAL); } break; case SDL_GCRC_CRC5: /* */ if (arg->ifgtype != SDL_GTYPE_E1) { trace(); return (-EINVAL); } break; case SDL_GCRC_CRC6: /* */ if (arg->ifgtype != SDL_GTYPE_T1) { trace(); return (-EINVAL); } break; default: trace(); return (-EINVAL); } switch (arg->ifclock) { case SDL_CLOCK_NONE: /* */ arg->ifclock = xp->sp->config.ifclock; break; case SDL_CLOCK_INT: /* */ case SDL_CLOCK_MASTER: /* */ arg->ifclock = SDL_CLOCK_MASTER; break; case SDL_CLOCK_EXT: /* */ case SDL_CLOCK_SLAVE: /* */ arg->ifclock = SDL_CLOCK_SLAVE; break; case SDL_CLOCK_LOOP: /* */ break; default: trace(); return (-EINVAL); } switch (arg->ifcoding) { case SDL_CODING_NONE: /* */ arg->ifcoding = xp->sp->config.ifcoding; break; case SDL_CODING_AMI: /* */ break; case SDL_CODING_B8ZS: /* */ if (arg->ifgtype != SDL_GTYPE_T1) { trace(); return (-EINVAL); } break; case SDL_CODING_HDB3: /* */ if (arg->ifgtype != SDL_GTYPE_E1) { trace(); return (-EINVAL); } break; default: case SDL_CODING_B6ZS: /* */ trace(); return (-EINVAL); } switch (arg->ifframing) { case SDL_FRAMING_NONE: /* */ arg->ifframing = xp->sp->config.ifframing; break; case SDL_FRAMING_CCS: /* */ case SDL_FRAMING_CAS: /* */ if (arg->ifgtype != SDL_GTYPE_E1) { trace(); return (-EINVAL); } break; case SDL_FRAMING_SF: /* */ case SDL_FRAMING_ESF: /* */ if (arg->ifgtype != SDL_GTYPE_T1) { trace(); return (-EINVAL); } break; default: trace(); return (-EINVAL); } switch (arg->iftxlevel) { case 0: arg->iftxlevel = xp->sp->config.iftxlevel; break; case 1: case 2: case 3: case 4: case 5: break; default: trace(); return (-EINVAL); } } if (xp->cd) { int src; for (src = 0; src < SDL_SYNCS; src++) if (arg->ifsyncsrc[src] < 0 || arg->ifsyncsrc[src] > 4) { trace(); return (-EINVAL); } } return (0); } STATIC void sdl_commit_config(struct xp *xp, sdl_config_t * arg) { int chan_reconfig = 0, span_reconfig = 0, card_reconfig = 0; if (xp) { if (xp->config.iftype != arg->iftype) { xp->config.iftype = arg->iftype; chan_reconfig = 1; } switch (arg->iftype) { case SDL_TYPE_DS0A: xp->config.ifrate = 56000; break; case SDL_TYPE_DS0: xp->config.ifrate = 64000; break; case SDL_TYPE_T1: xp->config.ifrate = 1544000; break; case SDL_TYPE_E1: xp->config.ifrate = 2048000; break; } if (xp->config.ifrate != arg->ifrate) { xp->config.ifrate = arg->ifrate; chan_reconfig = 1; } if (xp->config.ifmode != arg->ifmode) { xp->config.ifmode = arg->ifmode; chan_reconfig = 1; } } if (xp->sp) { if (xp->sp->config.ifgcrc != arg->ifgcrc) { xp->sp->config.ifgcrc = arg->ifgcrc; span_reconfig = 1; } if (xp->sp->config.ifclock != arg->ifclock) { xp->sp->config.ifclock = arg->ifclock; span_reconfig = 1; card_reconfig = 1; } if (xp->sp->config.ifcoding != arg->ifcoding) { xp->sp->config.ifcoding = arg->ifcoding; span_reconfig = 1; } if (xp->sp->config.ifframing != arg->ifframing) { xp->sp->config.ifframing = arg->ifframing; span_reconfig = 1; } if (xp->sp->config.iftxlevel != arg->iftxlevel) { xp->sp->config.iftxlevel = arg->iftxlevel; span_reconfig = 1; } } if (xp->cd) { int src; for (src = 0; src < SDL_SYNCS; src++) { if (xp->cd->config.ifsyncsrc[src] != arg->ifsyncsrc[src]) { xp->cd->config.ifsyncsrc[src] = arg->ifsyncsrc[src]; card_reconfig = 1; } } } if (chan_reconfig && xp->config.ifflags & SDL_IF_UP) { fixme(("FIXME: Reconfigure the chan if already running\n")); } if (span_reconfig && xp->sp->config.ifflags & SDL_IF_UP) { fixme(("FIXME: Reconfigure the span if already running\n")); } if (card_reconfig && xp->cd->config.ifflags & SDL_IF_UP) { fixme(("FIXME: Reconfigure the card if already running\n")); } return; } /* * SDL_IOCGOPTIONS: lmi_option_t * ----------------------------------- */ STATIC int sdl_iocgoptions(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); lmi_option_t *arg = (lmi_option_t *) mp->b_cont->b_rptr; *arg = xp->option; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCSOPTIONS: lmi_option_t * ----------------------------------- */ STATIC int sdl_iocsoptions(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); lmi_option_t *arg = (typeof(arg)) mp->b_cont->b_rptr; xp->option = *arg; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCGCONFIG: sdl_config_t * ----------------------------------- */ STATIC int sdl_iocgconfig(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); struct sp *sp; sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; bzero(arg, sizeof(*arg)); if (xp) { arg->ifflags = xp->config.ifflags; arg->iftype = xp->config.iftype; arg->ifrate = xp->config.ifrate; arg->ifmode = xp->config.ifmode; } if ((sp = xp->sp)) { struct cd *cd; arg->ifgtype = sp->config.ifgtype; arg->ifgrate = sp->config.ifgrate; arg->ifgcrc = sp->config.ifgcrc; arg->ifclock = sp->config.ifclock; arg->ifcoding = sp->config.ifcoding; arg->ifframing = sp->config.ifframing; arg->ifalarms = sp->config.ifalarms; arg->ifrxlevel = sp->config.ifrxlevel; arg->iftxlevel = sp->config.iftxlevel; if ((cd = xp->cd) || (cd = sp->cd)) { int src; for (src = 0; src < SDL_SYNCS; src++) arg->ifsyncsrc[src] = cd->config.ifsyncsrc[src]; arg->ifsync = cd->config.ifsync; } } return (0); } rare(); return (-EINVAL); } /* * SDL_IOCSCONFIG: sdl_config_t * ----------------------------------- */ STATIC int sdl_iocsconfig(queue_t *q, mblk_t *mp) { if (mp->b_cont) { int ret; struct xp *xp = XP_PRIV(q); sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; if ((ret = sdl_test_config(xp, arg))) return (ret); sdl_commit_config(xp, arg); return (0); } rare(); return (-EINVAL); } /* * SDL_IOCTCONFIG: sdl_config_t * ----------------------------------- */ STATIC int sdl_ioctconfig(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; return sdl_test_config(xp, arg); } rare(); return (-EINVAL); } /* * SDL_IOCCCONFIG: sdl_config_t * ----------------------------------- */ STATIC int sdl_ioccconfig(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_config_t *arg = (typeof(arg)) mp->b_cont->b_rptr; sdl_commit_config(xp, arg); return (0); } rare(); return (-EINVAL); } /* * SDL_IOCGSTATEM: sdl_statem_t * ----------------------------------- */ STATIC int sdl_iocgstatem(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_statem_t *arg = (typeof(arg)) mp->b_cont->b_rptr; *arg = xp->statem; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCCMRESET: sdl_statem_t * ----------------------------------- */ STATIC int sdl_ioccmreset(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL; (void) xp; (void) arg; fixme(("FIXME: Support master reset\n")); return (-EOPNOTSUPP); } /* * SDL_IOCGSTATSP: sdl_stats_t * ----------------------------------- */ STATIC int sdl_iocgstatsp(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr; *arg = xp->statsp; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCSSTATSP: sdl_stats_t * ----------------------------------- */ STATIC int sdl_iocsstatsp(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr; fixme(("FIXME: check these settings\n")); xp->statsp = *arg; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCGSTATS: sdl_stats_t * ----------------------------------- */ STATIC int sdl_iocgstats(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_stats_t *arg = (typeof(arg)) mp->b_cont->b_rptr; *arg = xp->stats; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCCSTATS: sdl_stats_t * ----------------------------------- */ STATIC int sdl_ioccstats(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); (void) mp; bzero(&xp->stats, sizeof(xp->stats)); return (0); } /* * SDL_IOCGNOTIFY: sdl_notify_t * ----------------------------------- */ STATIC int sdl_iocgnotify(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; *arg = xp->notify; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCSNOTIFY: sdl_notify_t * ----------------------------------- */ STATIC int sdl_iocsnotify(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; xp->notify.events |= arg->events; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCCNOTIFY: sdl_notify_t * ----------------------------------- */ STATIC int sdl_ioccnotify(queue_t *q, mblk_t *mp) { if (mp->b_cont) { struct xp *xp = XP_PRIV(q); sdl_notify_t *arg = (typeof(arg)) mp->b_cont->b_rptr; xp->notify.events &= ~arg->events; return (0); } rare(); return (-EINVAL); } /* * SDL_IOCCDISCTX: * ----------------------------------- */ STATIC int sdl_ioccdisctx(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); (void) mp; xp->config.ifflags &= ~SDL_IF_TX_RUNNING; return (0); } /* * SDL_IOCCONNTX: * ----------------------------------- */ STATIC int sdl_ioccconntx(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); (void) mp; xp->config.ifflags |= SDL_IF_TX_RUNNING; return (0); } /* * ========================================================================= * * EVENTS From Below * * ========================================================================= */ STATIC INLINE void xp_rx_chan_block(struct xp *xp) { if (xp->config.ifflags & SDL_IF_RX_RUNNING) { mblk_t *mp; if (canput(xp->oq) && (mp = allocb(8, BPRI_MED))) { uchar *offset; for (offset = xp->rx_base; offset < 1024 + xp->rx_base; offset += 128) { *(mp->b_wptr)++ = *offset; } ss7_oput(xp->oq, mp); } else { xp->stats.rx_buffer_overflows++; xp->stats.rx_overruns += 8; } xp->stats.rx_octets += 8; } } STATIC INLINE void xp_tx_chan_block(struct xp *xp) { if (xp->config.ifflags & SDL_IF_TX_RUNNING) { mblk_t *mp; uchar *offset; if ((mp = (mblk_t *) xchg(&xp->tx_msg, NULL))) { for (offset = xp->tx_base; offset < 1024 + xp->tx_base; offset += 128) { if (mp->b_rptr < mp->b_wptr) *offset = *(mp->b_rptr)++; else { *offset = 0xff; xp->stats.tx_underruns++; } } freemsg(mp); } else { xp->stats.tx_buffer_overflows++; for (offset = xp->tx_base; offset < 1024 + xp->tx_base; offset += 128) *offset = 0xff; /* mark idle to abort frames */ xp->stats.tx_underruns += 8; } xp->stats.tx_octets += 8; } } STATIC INLINE void xp_rx_t1_span_block(struct xp *xp) { if (xp->config.ifflags & SDL_IF_RX_RUNNING) { mblk_t *mp; if (canput(xp->oq) && (mp = allocb(8 * 24, BPRI_MED))) { int chan; uchar *offset; for (offset = xp->rx_base; offset < 1024 + xp->rx_base; offset += 128) { for (chan = 0; chan < 24; chan++) { *(mp->b_wptr)++ = *(offset + xp_t1_chan_map[chan]); } } ss7_oput(xp->oq, mp); } else { xp->stats.rx_buffer_overflows++; xp->stats.rx_overruns += 8 * 24; } xp->stats.rx_octets += 8 * 24; } } STATIC INLINE void xp_tx_t1_span_block(struct xp *xp) { if (xp->config.ifflags & SDL_IF_TX_RUNNING) { mblk_t *mp; int chan; uchar *offset; if ((mp = (mblk_t *) xchg(&xp->tx_msg, NULL))) { for (offset = xp->tx_base; offset < 1024 + xp->tx_base; offset += 128) { for (chan = 0; chan < 24; chan++) { if (mp->b_rptr < mp->b_wptr) *(offset + xp_t1_chan_map[chan]) = *(mp->b_rptr)++; else { *(offset + xp_t1_chan_map[chan]) = 0xff; xp->stats.tx_underruns++; } } } freemsg(mp); } else { xp->stats.tx_buffer_overflows++; for (offset = xp->tx_base; offset < 1024 + xp->tx_base; offset += 128) for (chan = 0; chan < 24; chan++) *offset = 0xff; /* mark idle to abort frames */ xp->stats.tx_underruns += 8 * 24; } xp->stats.tx_octets += 8 * 24; } } STATIC INLINE void xp_rx_e1_span_block(struct xp *xp) { if (xp->config.ifflags & SDL_IF_RX_RUNNING) { mblk_t *mp; if (canput(xp->oq) && (mp = allocb(8 * 31, BPRI_MED))) { int chan; uchar *offset; for (offset = xp->rx_base; offset < 1024 + xp->rx_base; offset += 128) { for (chan = 0; chan < 31; chan++) { *(mp->b_wptr)++ = *(offset + xp_e1_chan_map[chan]); } } ss7_oput(xp->oq, mp); } else { xp->stats.rx_buffer_overflows++; xp->stats.rx_overruns += 8 * 31; } xp->stats.rx_octets += 8 * 31; } } STATIC INLINE void xp_tx_e1_span_block(struct xp *xp) { if (xp->config.ifflags & SDL_IF_TX_RUNNING) { mblk_t *mp; int chan; uchar *offset; if ((mp = (mblk_t *) xchg(&xp->tx_msg, NULL))) { for (offset = xp->tx_base; offset < 1024 + xp->tx_base; offset += 128) { for (chan = 0; chan < 31; chan++) { if (mp->b_rptr < mp->b_wptr) *(offset + xp_e1_chan_map[chan]) = *(mp->b_rptr)++; else { *(offset + xp_e1_chan_map[chan]) = 0xff; xp->stats.tx_underruns++; } } } freemsg(mp); } else { xp->stats.tx_buffer_overflows++; for (offset = xp->tx_base; offset < 1024 + xp->tx_base; offset += 128) for (chan = 0; chan < 24; chan++) *offset = 0xff; /* mark idle to abort frames */ xp->stats.tx_underruns += 8 * 31; } xp->stats.tx_octets += 8 * 31; } } /* * ========================================================================= * * X400P Interrupt Service Routine * * ========================================================================= * We break this out into E1 and T1 versions for speed. No need to check * card type in the ISR when it is static for the board. That is: boards do * not change type from E1 to T1 or visa versa. */ /* * * X400P Tasklet * ----------------------------------- * This tasklet is scheduled before the ISR returns to feed the next buffer * of data into the write buffer and read the buffer of data from the read * buffer. This will run the soft-HDLC on each channel for 8 more bytes, or * if full span will run the soft-HDLC for 192 bytes (T1) or 256 bytes (E1). */ STATIC void xp_e1_card_tasklet(unsigned long data) { struct cd *cd = (struct cd *) data; int span; /* address transmit and receive first */ for (span = 0; span < 4; span++) { struct sp *sp; if ((sp = cd->spans[span]) && (sp->config.ifflags & SDL_IF_UP)) { struct xp *xp; int base = span << 8; switch (sp->config.iftype) { case SDL_TYPE_DS0: case SDL_TYPE_DS0A: { int chan; /* one chan at a time, 8 frames */ for (chan = 0; chan < 31; chan++) { if ((xp = sp->slots[xp_e1_chan_map[chan]])) { xp_tx_chan_block(xp); xp_rx_chan_block(xp); } } break; } case SDL_TYPE_E1: /* entire span, one frame at a time */ if ((xp = sp->slots[1])) { xp_tx_e1_span_block(xp); xp_rx_e1_span_block(xp); } break; default: swerr(); break; } if (sp->recovertime && !--sp->recovertime) { sp->config.ifalarms &= ~SDL_ALARM_REC; cd->xlb[base + 0x21] = 0x5f; /* turn off yellow */ cd->eval_syncsrc = 1; } /* process status span 1 frame 400/512, span 2 frame 408/512, ... */ if ((((cd->frame >> 3) & 0x3f) - 51) == span) { int status, alarms = 0, leds = 0; cd->xlb[base + 0x06] = 0xff; status = cd->xlb[base + 0x06]; if (status & 0x09) alarms |= SDL_ALARM_RED; if (status & 0x02) alarms |= SDL_ALARM_BLU; if (alarms) { if (!(sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) { /* alarms have just begun */ cd->xlb[base + 0x21] = 0x7f; /* set yellow alarm */ cd->eval_syncsrc = 1; } } else { if ((sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) { /* alarms have just ended */ alarms |= SDL_ALARM_REC; sp->recovertime = X400P_ALARM_SETTLE_TIME; } } if (status & 0x04) alarms |= SDL_ALARM_YEL; sp->config.ifalarms = alarms; /* adjust leds */ if (alarms & SDL_ALARM_RED) leds |= LEDRED; else if (alarms & SDL_ALARM_YEL) leds |= LEDYEL; else leds |= LEDGRN; cd->leds &= ~(LEDYEL << (span << 1)); cd->leds |= leds << (span << 1); cd->xlb[LEDREG] = cd->leds; } if (((cd->frame - 8) % 8000)) sp->config.ifbpv += cd->xlb[base + 0x01] + (cd->xlb[base + 0x00] << 8); } } if (cd->eval_syncsrc) { int src, syncsrc = 0; for (src = 0; src < SDL_SYNCS; src++) { if (cd->config.ifsyncsrc[src] && cd->spans[syncsrc - 1] && !(cd->spans[syncsrc - 1]->config.ifclock == SDL_CLOCK_LOOP) && !(cd->spans[syncsrc - 1]->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) { syncsrc = cd->config.ifsyncsrc[src]; break; } } cd->xlb[SYNREG] = syncsrc; cd->config.ifsync = syncsrc; cd->eval_syncsrc = 0; } } STATIC void xp_t1_card_tasklet(unsigned long data) { struct cd *cd = (struct cd *) data; int span; /* address transmit and receive first */ for (span = 0; span < 4; span++) { struct sp *sp; if ((sp = cd->spans[span]) && (sp->config.ifflags & SDL_IF_UP)) { struct xp *xp; int base = span << 8; switch (sp->config.iftype) { case SDL_TYPE_DS0: case SDL_TYPE_DS0A: { int chan; /* one chan at a time, 8 frames */ for (chan = 0; chan < 24; chan++) { if ((xp = sp->slots[xp_t1_chan_map[chan]])) { xp_tx_chan_block(xp); xp_rx_chan_block(xp); } } break; } case SDL_TYPE_T1: /* entire span, one frame at a time */ if ((xp = sp->slots[1])) { xp_tx_t1_span_block(xp); xp_rx_t1_span_block(xp); } break; default: swerr(); break; } if (sp->recovertime && !--sp->recovertime) { sp->config.ifalarms &= ~SDL_ALARM_REC; cd->xlb[base + 0x35] = 0x10; /* turn off yellow */ cd->eval_syncsrc = 1; } /* process status span 1 frame 400/512, span 2 frame 408/512, ... */ if ((((cd->frame >> 3) & 0x3f) - 51) == span) { int status, alarms = 0, leds = 0; sp->config.ifrxlevel = cd->xlb[base + RIR2] >> 6; cd->xlb[base + 0x20] = 0xff; status = cd->xlb[base + 0x20]; if (status & 0x03) alarms |= SDL_ALARM_RED; if (status & 0x08) alarms |= SDL_ALARM_BLU; if (alarms) { if (!(sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) { /* alarms have just begun */ cd->xlb[base + 0x35] = 0x11; /* set yellow alarm */ cd->eval_syncsrc = 1; } } else { if ((sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) { /* alarms have just ended */ alarms |= SDL_ALARM_REC; sp->recovertime = X400P_ALARM_SETTLE_TIME; } } if (status & 0x04) alarms |= SDL_ALARM_YEL; sp->config.ifalarms = alarms; /* adjust leds */ if (alarms & SDL_ALARM_RED) leds |= LEDRED; else if (alarms & SDL_ALARM_YEL) leds |= LEDYEL; else leds |= LEDGRN; cd->leds &= ~(LEDYEL << (span << 1)); cd->leds |= leds << (span << 1); cd->xlb[LEDREG] = cd->leds; } if (((cd->frame - 8) % 8000)) sp->config.ifbpv += cd->xlb[base + 0x24] + (cd->xlb[base + 0x23] << 8); } } if (cd->eval_syncsrc) { int src, syncsrc = 0; for (src = 0; src < SDL_SYNCS; src++) { if (cd->config.ifsyncsrc[src] && cd->spans[syncsrc - 1] && !(cd->spans[syncsrc - 1]->config.ifclock == SDL_CLOCK_LOOP) && !(cd->spans[syncsrc - 1]->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) { syncsrc = cd->config.ifsyncsrc[src]; break; } } cd->xlb[SYNREG] = syncsrc; cd->config.ifsync = syncsrc; cd->eval_syncsrc = 0; } } STATIC void xp_e1_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct cd *cd = (struct cd *) dev_id; /* active interrupt (otherwise spurious or shared) */ if (cd->xlb[STAREG] & INTACTIVE) { int word; volatile uint32_t *xll, *wbuf, *rbuf; cd->xlb[CTLREG] = (INTENA | OUTBIT | INTACK | E1DIV); /* write/read burst */ for (xll = cd->xll, wbuf = cd->wbuf, rbuf = cd->rbuf, word = 0; word < 256; word++) { *xll = *wbuf++; *rbuf++ = *xll++; } cd->frame += 8; cd->xlb[CTLREG] = (INTENA | E1DIV); tasklet_hi_schedule(&cd->tasklet); } return; } STATIC void xp_t1_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct cd *cd = (struct cd *) dev_id; /* active interrupt (otherwise spurious or shared) */ if (cd->xlb[STAREG] & INTACTIVE) { int word; volatile uint32_t *xll, *wbuf, *rbuf; cd->xlb[CTLREG] = (INTENA | OUTBIT | INTACK); /* write/read burst */ for (xll = cd->xll, wbuf = cd->wbuf, rbuf = cd->rbuf, word = 0; word < 256; word++) { *xll = *wbuf++; *rbuf++ = *xll++; } cd->frame += 8; cd->xlb[CTLREG] = (INTENA); tasklet_hi_schedule(&cd->tasklet); } return; } /* * ========================================================================= * * STREAMS Message Handling * * ========================================================================= * * M_IOCTL Handling * ------------------------------------------------------------------------- */ STATIC int xp_w_ioctl(queue_t *q, mblk_t *mp) { struct xp *xp = XP_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; struct linkblk *lp = (struct linkblk *) arg; int ret = 0; int type = _IOC_TYPE(cmd), nr = _IOC_NR(cmd), size = _IOC_SIZE(cmd); (void) nr; switch (type) { case __SID: switch (cmd) { default: ptrace(("X400P-SS7: ERROR: Unknown IOCTL %d\n", cmd)); case I_STR: case I_LINK: case I_PLINK: case I_UNLINK: case I_PUNLINK: rare(); (void) lp; ret = -EINVAL; break; } ptrace(("X400P-SS7: ERROR: Unsupported STREAMS ioctl\n")); ret = -EOPNOTSUPP; break; case SDL_IOC_MAGIC: if (count < size || xp->i_state == LMI_UNATTACHED) { ptrace(("X400P-SS7: ERROR: ioctl count = %d, size = %d, state = %ld\n", count, size, xp->i_state)); ret = -EINVAL; break; } switch (cmd) { case SDL_IOCGOPTIONS: /* lmi_option_t */ ret = sdl_iocgoptions(q, mp); break; case SDL_IOCSOPTIONS: /* lmi_option_t */ ret = sdl_iocsoptions(q, mp); break; case SDL_IOCGCONFIG: /* sdl_config_t */ ret = sdl_iocgconfig(q, mp); break; case SDL_IOCSCONFIG: /* sdl_config_t */ ret = sdl_iocsconfig(q, mp); break; case SDL_IOCTCONFIG: /* sdl_config_t */ ret = sdl_ioctconfig(q, mp); break; case SDL_IOCCCONFIG: /* sdl_config_t */ ret = sdl_ioccconfig(q, mp); break; case SDL_IOCGSTATEM: /* sdl_statem_t */ ret = sdl_iocgstatem(q, mp); break; case SDL_IOCCMRESET: /* sdl_statem_t */ ret = sdl_ioccmreset(q, mp); break; case SDL_IOCGSTATSP: /* sdl_stats_t */ ret = sdl_iocgstatsp(q, mp); break; case SDL_IOCSSTATSP: /* sdl_stats_t */ ret = sdl_iocsstatsp(q, mp); break; case SDL_IOCGSTATS: /* sdl_stats_t */ ret = sdl_iocgstats(q, mp); break; case SDL_IOCCSTATS: /* sdl_stats_t */ ret = sdl_ioccstats(q, mp); break; case SDL_IOCGNOTIFY: /* sdl_notify_t */ ret = sdl_iocgnotify(q, mp); break; case SDL_IOCSNOTIFY: /* sdl_notify_t */ ret = sdl_iocsnotify(q, mp); break; case SDL_IOCCNOTIFY: /* sdl_notify_t */ ret = sdl_ioccnotify(q, mp); break; case SDL_IOCCDISCTX: /* */ ret = sdl_ioccdisctx(q, mp); break; case SDL_IOCCCONNTX: /* */ ret = sdl_ioccconntx(q, mp); break; default: ptrace(("X400P-SS7: ERROR: Unsupported SDL ioctl\n")); ret = -EOPNOTSUPP; break; } break; default: return (QR_PASSALONG); } 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 xp_w_proto(queue_t *q, mblk_t *mp) { int rtn; ulong prim; struct xp *xp = XP_PRIV(q); ulong oldstate = xp->i_state; switch ((prim = *(ulong *) mp->b_rptr)) { case SDL_BITS_FOR_TRANSMISSION_REQ: rtn = sdl_bits_for_transmission_req(q, mp); break; case SDL_CONNECT_REQ: rtn = sdl_connect_req(q, mp); break; case SDL_DISCONNECT_REQ: rtn = sdl_disconnect_req(q, mp); break; case LMI_INFO_REQ: rtn = lmi_info_req(q, mp); break; case LMI_ATTACH_REQ: rtn = lmi_attach_req(q, mp); break; case LMI_DETACH_REQ: rtn = lmi_detach_req(q, mp); break; case LMI_ENABLE_REQ: rtn = lmi_enable_req(q, mp); break; case LMI_DISABLE_REQ: rtn = lmi_disable_req(q, mp); break; case LMI_OPTMGMT_REQ: rtn = lmi_optmgmt_req(q, mp); break; default: /* pass along anything we don't recognize */ rtn = QR_PASSFLOW; break; } if (rtn < 0) xp->i_state = oldstate; return (rtn); } /* * M_DATA Handling * ------------------------------------------------------------------------- */ STATIC int xp_w_data(queue_t *q, mblk_t *mp) { struct xp *xp = XP_PRIV(q); if (xp->tx_msg) { /* wait for tx-block to remove */ return (-EBUSY); } xp->tx_msg = mp; return (QR_ABSORBED); } STATIC int xp_r_data(queue_t *q, mblk_t *mp) { /* pass it along (but really an error) */ return (QR_PASSFLOW); } /* * ========================================================================= * * PUT and SRV * * ========================================================================= */ STATIC INLINE int xp_w_prim(queue_t *q, mblk_t *mp) { /* Fast Path */ if (mp->b_datap->db_type == M_DATA) return xp_w_data(q, mp); switch (mp->b_datap->db_type) { case M_DATA: return xp_w_data(q, mp); case M_PROTO: case M_PCPROTO: return xp_w_proto(q, mp); case M_FLUSH: return ss7_w_flush(q, mp); case M_IOCTL: return xp_w_ioctl(q, mp); } return (QR_PASSFLOW); } STATIC INLINE int xp_r_prim(queue_t *q, mblk_t *mp) { /* Fast Path */ if (mp->b_datap->db_type == M_DATA) return xp_r_data(q, mp); switch (mp->b_datap->db_type) { case M_DATA: return xp_r_data(q, mp); case M_FLUSH: return ss7_r_flush(q, mp); } return (QR_PASSFLOW); } /* * ========================================================================= * * 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. */ struct xp *x400p_list = NULL; STATIC int xp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp) { int cmajor = getmajor(*devp); int cminor = getminor(*devp); struct xp *xp, **xpp = &x400p_list; (void) crp; 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) { ptrace(("X400P-SS7: ERROR: Can't open as module\n")); MOD_DEC_USE_COUNT; return (EIO); } if (!cminor) sflag = CLONEOPEN; if (sflag == CLONEOPEN) { printd(("X400P-SS7: Clone open in effect on major %d\n", cmajor)); cminor = 1; } for (; *xpp && (*xpp)->u.dev.cmajor < cmajor; xpp = &(*xpp)->next) ; for (; *xpp && cminor <= X400P_SDL_NMINOR; xpp = &(*xpp)->next) { ushort dminor = (*xpp)->u.dev.cminor; if (cminor < dminor) break; if (cminor == dminor) { if (sflag != CLONEOPEN) { ptrace(("X400P-SS7: ERROR: Requested device in use\n")); MOD_DEC_USE_COUNT; return (ENXIO); } cminor++; } } if (cminor > X400P_SDL_NMINOR) { ptrace(("X400P-SS7: ERROR: No device minors left\n")); MOD_DEC_USE_COUNT; return (ENXIO); } printd(("X400P-SS7: Opened character device %d:%d\n", cmajor, cminor)); *devp = makedevice(cmajor, cminor); if (!(xp = xp_alloc_priv(q, xpp, devp, crp))) { ptrace(("X400P-SS7: ERROR: No memory\n")); MOD_DEC_USE_COUNT; return (ENOMEM); } return (0); } STATIC int xp_close(queue_t *q, int flag, cred_t *crp) { struct xp *xp = XP_PRIV(q); (void) flag; (void) crp; (void) xp; printd(("X400P-SS7: Closed character device %d:%d\n", xp->u.dev.cmajor, xp->u.dev.cminor)); xp_free_priv(xp); MOD_DEC_USE_COUNT; return (0); } /* * ========================================================================== * * Private and Card structure allocation and deallocation * * ========================================================================== */ STATIC kmem_cache_t *xp_priv_cachep = NULL; STATIC kmem_cache_t *xp_span_cachep = NULL; STATIC kmem_cache_t *xp_card_cachep = NULL; STATIC kmem_cache_t *xp_xbuf_cachep = NULL; /* * Cache allocation * ------------------------------------------------------------------------- */ STATIC int xp_init_caches(void) { if (!xp_priv_cachep && !(xp_priv_cachep = kmem_cache_create("xp_priv_cachep", sizeof(struct xp), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { cmn_err(CE_PANIC, "%s: Cannot allocate xp_priv_cachep", __FUNCTION__); return (-ENOMEM); } else printd(("X400P-SS7: initialized device private structure cache\n")); if (!xp_span_cachep && !(xp_span_cachep = kmem_cache_create("xp_span_cachep", sizeof(struct sp), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { cmn_err(CE_PANIC, "%s: Cannot allocate xp_span_cachep", __FUNCTION__); kmem_cache_destroy(xchg(&xp_priv_cachep, NULL)); return (-ENOMEM); } else printd(("X400P-SS7: initialized span private structure cache\n")); if (!xp_card_cachep && !(xp_card_cachep = kmem_cache_create("xp_card_cachep", sizeof(struct cd), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { cmn_err(CE_PANIC, "%s: Cannot allocate xp_card_cachep", __FUNCTION__); kmem_cache_destroy(xchg(&xp_span_cachep, NULL)); kmem_cache_destroy(xchg(&xp_priv_cachep, NULL)); return (-ENOMEM); } else printd(("X400P-SS7: initialized card private structure cache\n")); if (!xp_xbuf_cachep && !(xp_xbuf_cachep = kmem_cache_create("xp_xbuf_cachep", 1024, 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { cmn_err(CE_PANIC, "%s: Cannot allocate xp_xbuf_cachep", __FUNCTION__); kmem_cache_destroy(xchg(&xp_card_cachep, NULL)); kmem_cache_destroy(xchg(&xp_span_cachep, NULL)); kmem_cache_destroy(xchg(&xp_priv_cachep, NULL)); return (-ENOMEM); } else printd(("X400P-SS7: initialized card read/write buffer cache\n")); return (0); } STATIC void xp_term_caches(void) { if (xp_xbuf_cachep) { if (kmem_cache_destroy(xp_xbuf_cachep)) cmn_err(CE_WARN, "%s: did not destroy xp_xbuf_cachep", __FUNCTION__); else printd(("X400P-SS7: destroyed xp_xbuf_cache\n")); } if (xp_card_cachep) { if (kmem_cache_destroy(xp_card_cachep)) cmn_err(CE_WARN, "%s: did not destroy xp_card_cachep", __FUNCTION__); else printd(("X400P-SS7: destroyed xp_card_cache\n")); } if (xp_span_cachep) { if (kmem_cache_destroy(xp_span_cachep)) cmn_err(CE_WARN, "%s: did not destroy xp_span_cachep", __FUNCTION__); else printd(("X400P-SS7: destroyed xp_span_cache\n")); } if (xp_priv_cachep) { if (kmem_cache_destroy(xp_priv_cachep)) cmn_err(CE_WARN, "%s: did not destroy xp_priv_cachep", __FUNCTION__); else printd(("X400P-SS7: destroyed xp_priv_cache\n")); } return; } /* * Private structure allocation * ------------------------------------------------------------------------- */ STATIC struct xp *xp_alloc_priv(queue_t *q, struct xp **xpp, dev_t *devp, cred_t *crp) { struct xp *xp; if ((xp = kmem_cache_alloc(xp_priv_cachep, SLAB_ATOMIC))) { printd(("X400P-SS7: allocated device private structure\n")); bzero(xp, sizeof(*xp)); xp_get(xp); /* first get */ xp->u.dev.cmajor = getmajor(*devp); xp->u.dev.cminor = getminor(*devp); xp->cred = *crp; xp->o_prim = &xp_r_prim; xp->i_prim = &xp_w_prim; xp->o_wakeup = NULL; xp->i_wakeup = NULL; (xp->oq = RD(q))->q_ptr = xp_get(xp); (xp->iq = WR(q))->q_ptr = xp_get(xp); lis_spin_lock_init(&xp->qlock, "xp-queue-lock"); xp->i_version = 1; xp->i_style = LMI_STYLE2; xp->i_state = LMI_UNATTACHED; lis_spin_lock_init(&xp->lock, "xp-priv-lock"); if ((xp->next = *xpp)) xp->next->prev = &xp->next; xp->prev = xpp; *xpp = xp_get(xp); printd(("X400P-SS7: linked device private structure\n")); /* LMI configuration defaults */ xp->option.pvar = SS7_PVAR_ITUT_88; xp->option.popt = 0; /* SDL configuration defaults */ xp->config.ifname = NULL; xp->config.ifflags = 0; xp->config.iftype = SDL_TYPE_DS0; xp->config.ifrate = 64000; xp->config.ifmode = SDL_MODE_PEER; xp->config.ifblksize = 8; xp->tx_base = 0; xp->rx_base = 0; printd(("X400P-SS7: setting device private structure defaults\n")); } else ptrace(("X400P-SS7: ERROR: Could not allocate device private structure\n")); return (xp); } STATIC void xp_free_priv(struct xp *xp) { int flags = 0; ensure(xp, return); lis_spin_lock_irqsave(&xp->lock, &flags); { struct sp *sp; ss7_unbufcall((str_t *) xp); if (xp->cd) cd_put(xchg(&xp->cd, NULL)); if ((sp = xp->sp)) { int slot; for (slot = 0; slot < 32; slot++) if (sp->slots[slot] == xp) xp_put(xchg(&sp->slots[slot], NULL)); sp_put(xchg(&xp->sp, NULL)); printd(("X400P-SS7: unlinked device private structure from span\n")); } else ptrace(("X400P-SS7: ERROR: Arrrrgggh! sp = %p\n", sp)); if (xp->tx_msg) freemsg((mblk_t *) xchg(&xp->tx_msg, NULL)); if (xp->next || xp->prev != &xp->next) { if ((*xp->prev = xp->next)) xp->next->prev = xp->prev; xp->next = NULL; xp->prev = &xp->next; xp_put(xp); } xp_put(xchg(&xp->oq->q_ptr, NULL)); xp_put(xchg(&xp->iq->q_ptr, NULL)); flushq(xchg(&xp->oq, NULL), FLUSHALL); flushq(xchg(&xp->iq, NULL), FLUSHALL); printd(("X400P-SS7: unlinked device private structure\n")); } lis_spin_unlock_irqrestore(&xp->lock, &flags); xp_put(xp); /* final put */ return; } STATIC struct xp *xp_get(struct xp *xp) { if (xp) atomic_inc(&xp->refcnt); return (xp); } STATIC void xp_put(struct xp *xp) { if (atomic_dec_and_test(&xp->refcnt)) { kmem_cache_free(xp_priv_cachep, xp); printd(("X400P-SS7: freed device private structure\n")); } } /* * Span allocation and deallocation * ------------------------------------------------------------------------- */ STATIC struct sp *xp_alloc_sp(struct cd *cd, uint8_t span) { struct sp *sp; if ((sp = kmem_cache_alloc(xp_span_cachep, SLAB_ATOMIC))) { printd(("X400P-SS7: allocated span private structure\n")); bzero(sp, sizeof(*sp)); cd_get(cd); /* first get */ /* create linkage */ cd->spans[span] = sp_get(sp); sp->cd = cd_get(cd); lis_spin_lock_init(&sp->lock, "sp-priv-lock"); /* fill out span structure */ printd(("X400P-SS7: linked span private structure\n")); sp->iobase = cd->iobase + (span << 8); sp->card = cd->card; sp->span = span; sp->config.ifname = cd->config.ifname; sp->config.ifflags = 0; sp->config.iftype = cd->config.iftype; sp->config.ifrate = cd->config.ifrate; sp->config.ifgtype = cd->config.ifgtype; sp->config.ifgrate = cd->config.ifgrate; sp->config.ifmode = cd->config.ifmode; sp->config.ifgcrc = cd->config.ifgcrc; sp->config.ifclock = cd->config.ifclock; sp->config.ifcoding = cd->config.ifcoding; sp->config.ifframing = cd->config.ifframing; sp->config.ifalarms = 0; sp->config.ifrxlevel = 0; sp->config.iftxlevel = cd->config.iftxlevel; printd(("X400P-SS7: set span private structure defaults\n")); } else ptrace(("X400P-SS7: ERROR: Could not allocate span private structure\n")); return (sp); } /* Note: called with card interrupts disabled */ STATIC void xp_free_sp(struct sp *sp) { struct cd *cd; ensure(sp, return); if ((cd = sp->cd)) { int slot; /* remove card linkage */ sp_put(xchg(&cd->spans[sp->span], NULL)); cd_put(xchg(&sp->cd, NULL)); sp->span = 0; printd(("X400P-SS7: unlinked span private structure from card\n")); /* remove channel linkage */ for (slot = 0; slot < 32; slot++) { struct xp *xp; if ((xp = sp->slots[slot])) { sp_put(xchg(&xp->sp, NULL)); cd_put(xchg(&xp->cd, NULL)); xp_put(xchg(&sp->slots[slot], NULL)); } } printd(("X400P-SS7: unlinked span private structure from slots\n")); } else ptrace(("X400P-SS7: ERROR: spans cannot exist without cards\n")); } STATIC struct sp *sp_get(struct sp *sp) { if (sp) atomic_inc(&sp->refcnt); return (sp); } STATIC void sp_put(struct sp *sp) { if (atomic_dec_and_test(&sp->refcnt)) { printd(("X400P-SS7: freed span private structure\n")); kmem_cache_free(xp_span_cachep, sp); } } /* * Card allocation and deallocation * ------------------------------------------------------------------------- */ STATIC struct cd *xp_alloc_cd(void) { struct cd *cd; if ((cd = kmem_cache_alloc(xp_card_cachep, SLAB_ATOMIC))) { uint32_t *wbuf; uint32_t *rbuf; printd(("X400P-SS7: allocated card private structure\n")); if (!(wbuf = kmem_cache_alloc(xp_xbuf_cachep, SLAB_ATOMIC))) { ptrace(("X400P-SS7: could not allocate write buffer\n")); kmem_cache_free(xp_card_cachep, cd); return (NULL); } if (!(rbuf = kmem_cache_alloc(xp_xbuf_cachep, SLAB_ATOMIC))) { ptrace(("X400P-SS7: could not allocate read buffer\n")); kmem_cache_free(xp_xbuf_cachep, wbuf); kmem_cache_free(xp_card_cachep, cd); return (NULL); } bzero(cd, sizeof(*cd)); cd_get(cd); /* first get */ if ((cd->next = x400p_cards)) cd->next->prev = &cd->next; x400p_cards = cd_get(cd); cd->prev = &x400p_cards; cd->card = x400p_boards++; cd->wbuf = wbuf; cd->rbuf = rbuf; printd(("X400P-SS7: linked card private structure\n")); } else ptrace(("X400P-SS7: ERROR: Could not allocate card private structure\n")); return (cd); } STATIC void xp_free_cd(struct cd *cd) { int flags; lis_spin_lock_irqsave(&cd->lock, &flags); { int span; ensure(cd, return); /* remove any remaining spans */ for (span = 0; span < 4; span++) { struct sp *sp; if ((sp = cd->spans[span])) xp_free_sp(sp); } /* unlink from board list */ if ((*(cd->prev) = cd->next)) cd->next->prev = cd->prev; cd->next = NULL; cd->prev = &cd->next; cd_put(cd); printd(("X400P-SS7: unlinked card private structure\n")); tasklet_kill(&cd->tasklet); kmem_cache_free(xp_xbuf_cachep, (uint32_t *) xchg(&cd->rbuf, NULL)); kmem_cache_free(xp_xbuf_cachep, (uint32_t *) xchg(&cd->wbuf, NULL)); } lis_spin_unlock_irqrestore(&cd->lock, &flags); cd_put(cd); /* final put */ } STATIC struct cd *cd_get(struct cd *cd) { if (cd) atomic_inc(&cd->refcnt); return (cd); } STATIC void cd_put(struct cd *cd) { if (atomic_dec_and_test(&cd->refcnt)) { kmem_cache_free(xp_card_cachep, cd); printd(("X400P-SS7: freed card private structure\n")); } } /* * ========================================================================= * * PCI Initialization * * ========================================================================= */ /* * X400P-SS7 Probe * ----------------------------------- * Probes will be called for all PCI devices which match our criteria at pci * init time (module load). Successful return from the probe function will * have the device configured for operation. */ STATIC int __devinit xp_probe(struct pci_dev *dev, const struct pci_device_id *id) { int byte, span; struct cd *cd; if (!dev || !id) { ptrace(("X400P-SS7: ERROR: Device or id is null!\n")); return (-ENXIO); } if (id->driver_data != X400PSS7 && id->driver_data != X400P) { ptrace(("X400P-SS7: ERROR: Driver does not support device type %ld\n", id->driver_data)); return (-ENXIO); } if (pci_enable_device(dev)) { ptrace(("X400P-SS7: ERROR: Could not enable pci device\n")); return (-EIO); } printd(("X400P-SS7: enabled x400p-ss7 pci device type %ld\n", id->driver_data)); if (!(cd = xp_alloc_cd())) return (-ENOMEM); if ((cd->irq = dev->irq) < 1) { ptrace(("X400P-SS7: ERROR: No IRQ allocated for device\n")); goto error_free; } printd(("X400P-SS7: device allocated IRQ %ld\n", cd->irq)); if ((pci_resource_flags(dev, 0) & IORESOURCE_IO) || !(cd->plx_region = pci_resource_start(dev, 0)) || !(cd->plx_length = pci_resource_len(dev, 0)) || !(cd->plx = ioremap(cd->plx_region, cd->plx_length))) { ptrace(("X400P-SS7: ERROR: Invalid PLX 9030 base resource\n")); goto error_free; } printd(("X400P-SS7: plx region %ld bytes at %lx, remapped %p\n", cd->plx_length, cd->plx_region, cd->plx)); if ((pci_resource_flags(dev, 2) & IORESOURCE_IO) || !(cd->xll_region = pci_resource_start(dev, 2)) || !(cd->xll_length = pci_resource_len(dev, 2)) || !(cd->xll = ioremap(cd->xll_region, cd->xll_length))) { ptrace(("X400P-SS7: ERROR: Invalid Xilinx 32-bit base resource\n")); goto error_free; } printd(("X400P-SS7: xll region %ld bytes at %lx, remapped %p\n", cd->xll_length, cd->xll_region, cd->xll)); if ((pci_resource_flags(dev, 3) & IORESOURCE_IO) || !(cd->xlb_region = pci_resource_start(dev, 3)) || !(cd->xlb_length = pci_resource_len(dev, 3)) || !(cd->xlb = ioremap(cd->xlb_region, cd->xlb_length))) { ptrace(("X400P-SS7: ERROR: Invalid Xilinx 8-bit base resource\n")); goto error_free; } printd(("X400P-SS7: xlb region %ld bytes at %lx, remapped %p\n", cd->xlb_length, cd->xlb_region, cd->xlb)); cd->config.ifname = xp_board_info[id->driver_data].name; if (!request_mem_region(cd->plx_region, cd->plx_length, cd->config.ifname)) { ptrace(("X400P-SS7: ERROR: Unable to reserve PLX memory\n")); goto error_free; } printd(("X400P-SS7: plx region %lx reserved %ld bytes\n", cd->plx_region, cd->plx_length)); if (!request_mem_region(cd->xll_region, cd->xll_length, cd->config.ifname)) { ptrace(("X400P-SS7: ERROR: Unable to reserve Xilinx 32-bit memory\n")); goto error_free_plx_region; } printd(("X400P-SS7: xll region %lx reserved %ld bytes\n", cd->xll_region, cd->xll_length)); if (!request_mem_region(cd->xlb_region, cd->xlb_length, cd->config.ifname)) { ptrace(("X400P-SS7: ERROR: Unable to reserve Xilinx 8-bit memory\n")); goto error_free_xll_region; } printd(("X400P-SS7: xlb region %lx reserved %ld bytes\n", cd->xlb_region, cd->xlb_length)); pci_set_drvdata(dev, cd); __printd(("X400P-SS7: detected %s at 0x%lx/0x%lx irq %ld\n", cd->config.ifname, cd->xll_region, cd->xlb_region, cd->irq)); #ifdef X400P_DOWNLOAD_FIRMWARE { uint8_t *f = (uint8_t *) x400pfw; volatile unsigned long *data; unsigned long timeout; data = (volatile unsigned long *) &cd->plx[GPIOC]; *data |= GPIO_WRITE; *data &= ~GPIO_PROGRAM; while (*data & (GPIO_INIT | GPIO_DONE)) ; printd(("X400P-SS7: Xilinx Firmware Load: Init and done are low\n")); *data |= GPIO_PROGRAM; while (!(*data & GPIO_INIT)) ; printd(("X400P-SS7: Xilinx Firmware Load: Init is high\n")); *data &= ~GPIO_WRITE; printd(("X400P-SS7: Xilinx Firmware Load: Loading\n")); for (byte = 0; byte < sizeof(x400pfw); byte++) { *cd->xlb = *f++; if (*data & GPIO_DONE) break; if (!(*data & GPIO_INIT)) break; } if (!(*data & GPIO_INIT)) { printd(("X400P-SS7: ERROR: Xilinx Firmware Load: Failed\n")); goto error_free_all; } printd(("X400P-SS7: Xilinx Firmware Load: Loaded %d bytes\n", byte)); timeout = jiffies + 20 * HZ / 1000; while (jiffies < timeout) ; *data |= GPIO_WRITE; printd(("X400P-SS7: Xilinx Firmware Load: Done\n")); timeout = jiffies + 20 * HZ / 1000; while (jiffies < timeout) ; if (!(*data & GPIO_INIT)) { ptrace(("X400P-SS7: ERROR: Xilinx Firmware Load: Failed\n")); goto error_free_all; } if (!(*data & GPIO_DONE)) { ptrace(("X400P-SS7: ERROR: Xilinx Firmware Load: Failed\n")); goto error_free_all; } printd(("X400P-SS7: Xilinx Firmware Load: Successful\n")); } #endif cd->plx[INTCSR] = 0; /* disable interrupts */ cd->xlb[SYNREG] = 0; cd->xlb[CTLREG] = 0; cd->xlb[LEDREG] = 0xff; if (cd->xlb[0x00f] & 0x80) { int word; printd(("E400P-SS7: E400P-SS7 Quad E1 Card\n")); for (word = 0; word < 256; word++) { cd->xll[word] = 0xffffffff; cd->wbuf[word] = 0xffffffff; } tasklet_init(&cd->tasklet, &xp_e1_card_tasklet, (unsigned long) cd); /* setup E1 card defaults */ cd->config.ifgtype = SDL_GTYPE_E1; cd->config.ifgrate = 2048000; cd->config.ifgcrc = SDL_GCRC_CRC4; cd->config.ifclock = SDL_CLOCK_SLAVE; cd->config.ifcoding = SDL_CODING_HDB3; cd->config.ifframing = SDL_FRAMING_CCS; lis_spin_lock_init(&cd->lock, "cd-priv-lock"); if (request_irq(cd->irq, xp_e1_interrupt, SA_INTERRUPT | SA_SHIRQ, "x400p-ss7", cd)) { ptrace(("E400P-SS7: ERROR: Unable to request IRQ %ld\n", cd->irq)); goto error_free_all; } printd(("E400P-SS7: acquired IRQ %ld for T400P-SS7 card\n", cd->irq)); cd->config.ifflags = (SDL_IF_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING); cd->config.iftype = SDL_TYPE_DS0; cd->config.ifrate = 64000; cd->config.ifmode = SDL_MODE_PEER; cd->config.iftxlevel = 0; } else { int word; printd(("T400P-SS7: T400P-SS7 Quad T1 Card\n")); for (word = 0; word < 256; word++) { cd->xll[word] = 0x7f7f7f7f; cd->wbuf[word] = 0x7f7f7f7f; } tasklet_init(&cd->tasklet, &xp_t1_card_tasklet, (unsigned long) cd); /* setup T1 card defaults */ cd->config.ifgtype = SDL_GTYPE_T1; cd->config.ifgrate = 1544000; cd->config.ifgcrc = SDL_GCRC_CRC6; cd->config.ifclock = SDL_CLOCK_SLAVE; cd->config.ifcoding = SDL_CODING_AMI; cd->config.ifframing = SDL_FRAMING_SF; lis_spin_lock_init(&cd->lock, "cd-priv-lock"); if (request_irq(cd->irq, xp_t1_interrupt, SA_INTERRUPT | SA_SHIRQ, "x400p-ss7", cd)) { ptrace(("T400P-SS7: ERROR: Unable to request IRQ %ld\n", cd->irq)); goto error_free_all; } printd(("T400P-SS7: acquired IRQ %ld for T400P-SS7 card\n", cd->irq)); cd->config.ifflags = (SDL_IF_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING); cd->config.iftype = SDL_TYPE_DS0; cd->config.ifrate = 64000; cd->config.ifmode = SDL_MODE_PEER; cd->config.iftxlevel = 0; } /* allocate span structures (if possible now; otherwise, later) */ for (span = 0; span < 4; span++) xp_alloc_sp(cd, span); cd->plx[INTCSR] = PLX_INTENA; /* enable interrupts */ return (0); error_free_all: release_mem_region(cd->xlb_region, cd->xlb_length); printd(("X400P-SS7: released xlb region %lx length %ld\n", cd->xlb_region, cd->xlb_length)); error_free_xll_region: release_mem_region(cd->xll_region, cd->xll_length); printd(("X400P-SS7: released xll region %lx length %ld\n", cd->xll_region, cd->xll_length)); error_free_plx_region: release_mem_region(cd->plx_region, cd->plx_length); printd(("X400P-SS7: released plx region %lx length %ld\n", cd->plx_region, cd->plx_length)); error_free: xp_free_cd(cd); return -ENODEV; } /* * X400P-SS7 Remove * ----------------------------------- * These cards do not support hotplug, so removes only occur after all the * channels have been closed, so we only have to stop interrupts and * deallocate board-level resources. Nevertheless, if we get a hot removal * of a card, we must be prepared to deallocate the span structures. */ STATIC void __devexit xp_remove(struct pci_dev *dev) { struct cd *cd = pci_get_drvdata(dev); struct sp *sp; int i; ensure(cd, return); /* disable interrupts */ if (cd->plx_length) { cd->plx[INTCSR] = 0; } if (cd->xlb_length) { cd->xlb[SYNREG] = 0; cd->xlb[CTLREG] = 0; cd->xlb[LEDREG] = 0; } /* free any lingering span structures */ for (i = 0; i < 4; i++) if ((sp = cd->spans[i])) xp_free_sp(sp); if (cd->irq) { free_irq(cd->irq, cd); cd->irq = 0; } if (cd->xlb_length) { release_mem_region(cd->xlb_region, cd->xlb_length); printd(("X400P-SS7: released xlb region %lx length %ld\n", cd->xlb_region, cd->xlb_length)); cd->xlb_region = 0; cd->xlb_length = 0; } if (cd->xll_length) { release_mem_region(cd->xll_region, cd->xll_length); printd(("X400P-SS7: released xll region %lx length %ld\n", cd->xll_region, cd->xll_length)); cd->xll_region = 0; cd->xll_length = 0; } if (cd->plx_length) { release_mem_region(cd->plx_region, cd->plx_length); cd->plx_region = 0; cd->plx_length = 0; printd(("X400P-SS7: released plx region %lx length %ld\n", cd->plx_region, cd->plx_length)); } if (cd->xlb) { iounmap((void *) cd->xlb); printd(("X400P-SS7: unmapped xlb memory at %p\n", cd->xlb)); cd->xlb = NULL; } if (cd->xll) { iounmap((void *) cd->xll); printd(("X400P-SS7: unmapped xll memory at %p\n", cd->xll)); cd->xll = NULL; } if (cd->plx) { iounmap((void *) cd->plx); printd(("X400P-SS7: unmapped plx memory at %p\n", cd->plx)); cd->plx = NULL; } xp_free_cd(cd); } /* * X400P-SS7 Suspend * ----------------------------------- */ STATIC int xp_suspend(struct pci_dev *pdev, u32 state) { fixme(("Write a suspend routine.\n")); return 0; } /* * X400P-SS7 Resume * ----------------------------------- */ STATIC int xp_resume(struct pci_dev *pdev) { fixme(("Write a resume routine.\n")); return 0; } /* * X400P-SS7 PCI Init * ----------------------------------- * Here we need to scan for all available boards, configure the board-level * structures, and then release all system resources. The purpose of this is * to identify the boards in the system at module startup or LiS * initialization. * * Later, when a stream is opened for any span, the span is configured, the * drivers will be enabled and all channels in the span will have their * power-on sequence completed and each channel will idle SIOS. Closing * channels will result in the transmitters resuming idle SIOS operation. */ STATIC INLINE int xp_pci_init(void) { return pci_module_init(&xp_driver); } /* * X400P-SS7 PCI Cleanup * ----------------------------------- * Because this is a style 2 driver, if there are any boards that are atill * configured, we need to stop the boards and deallocate the board-level * resources and structures. */ STATIC INLINE void xp_pci_cleanup(void) { return pci_unregister_driver(&xp_driver); } /* * ========================================================================= * * LiS Module Initialization (For unregistered driver.) * * ========================================================================= */ STATIC int xp_initialized = 0; STATIC int xp_majors[X400P_SDL_NMAJOR] = { 0, }; STATIC void xp_init(void) { int err, major; unless(xp_initialized, return); cmn_err(CE_NOTE, X400P_BANNER); /* console splash */ if ((err = xp_init_caches())) { cmn_err(CE_PANIC, "X400P-SS7: ERROR: Could not allocate caches"); xp_initialized = err; return; } if ((err = xp_pci_init()) < 0) { cmn_err(CE_WARN, "X400P-SS7: ERROR: No PCI devices found"); xp_term_caches(); xp_initialized = err; return; } for (major = 0; major < X400P_SDL_NMAJOR; major++) { if ((err = lis_register_strdev(X400P_SDL_CMAJOR + major, &xp_info, X400P_SDL_NMINOR, X400P_DRV_NAME)) <= 0) { cmn_err(CE_WARN, "X400P-SS7: ERROR: couldn't register driver for major %d", major + X400P_SDL_CMAJOR); xp_initialized = err; for (major -= 1; major >= 0; major--) lis_unregister_strdev(xp_majors[major]); xp_pci_cleanup(); xp_term_caches(); return; } else xp_majors[major] = err; } xp_initialized = X400P_SDL_CMAJOR; return; } STATIC void xp_terminate(void) { int err, major; ensure(xp_initialized, return); for (major = 0; major < X400P_SDL_NMAJOR; major++) { if (xp_majors[major]) { if ((err = lis_unregister_strdev(xp_majors[major]))) cmn_err(CE_PANIC, "X400P-SS7: couldn't unregister driver for major %d\n", xp_majors[major]); else xp_majors[major] = err; } } xp_initialized = 0; xp_pci_cleanup(); xp_term_caches(); return; } /* * ========================================================================= * * Kernel Module Initialization * * ========================================================================= */ int init_module(void) { xp_init(); if (xp_initialized < 0) return xp_initialized; return (0); } void cleanup_module(void) { xp_terminate(); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |