/***************************************************************************** @(#) $Id: acb56.c,v 0.7.2.4 2000/10/09 02:40:40 brian Exp $ ----------------------------------------------------------------------------- Copyright (C) 1997, 1998, 1999, 2000 Brian Bidulock All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Last Modified $Date: 2000/10/09 02:40:40 $ by $Author: brian $ ----------------------------------------------------------------------------- $Log: acb56.c,v $ Revision 0.7.2.4 2000/10/09 02:40:40 brian Pre-release 1. Revision 0.7.2.3 2000/09/08 11:12:32 brian Added some minor stuff: modules load and unload fine. Revision 0.7.2.2 2000/09/08 10:45:09 brian Now it compiles. Revision 0.7.2.1 2000/09/08 10:03:08 brian Compiles. Revision 0.7 2000/09/08 08:07:05 brian Initial port of ACB56 driver for OpenSS7 stack. *****************************************************************************/ static char const ident[] = "$Name: Prerelease1 $($Revision: 0.7.2.4 $) $Date: 2000/10/09 02:40:40 $"; #include #include #include #include #include /* check_ request_ and release_region */ #include #include #include "ss7if.h" #include /* for TC_PRIO_CONTROL */ #define ACB56_DESCRIP "ACB56: SS7 LEVEL 2 DRIVER FOR THE SEALEVEL ACB56(TM) BOARD." #define ACB56_COPYRIGHT "Copyright (C) 1997-2000 Brian Bidulock All Rights Reserved." #define ACB56_DEVICES "Supports the Sealevel ACB56(tm) V.35 boards." #define ACB56_CONTACT "Brian F. G. Bidulock " #define ACB56_BANNER ACB56_DESCRIP "\n" \ ACB56_COPYRIGHT "\n" \ ACB56_DEVICES "\n" \ ACB56_CONTACT "\n" MODULE_AUTHOR(ACB56_CONTACT); MODULE_DESCRIPTION(ACB56_DESCRIP); MODULE_SUPPORTED_DEVICE(ACB56_DEVICES); #ifdef ACB56_DEBUG int acb56_debug = ACB56_DEBUG; #else int acb56_debug = 2; #endif #define outb writeb #define inb readb struct acb56_dev; struct acb56_dev* acb56_devices = NULL; static int debug = -1; static int io [] = { -1, -1, -1, -1 }; static int irq [] = { -1, -1, -1, -1 }; static int dma_rx[] = { -1, -1, -1, -1 }; static int dma_tx[] = { -1, -1, -1, -1 }; static int mode [] = { -1, -1, -1, -1 }; static int clock [] = { -1, -1, -1, -1 }; MODULE_PARM(debug, "i"); /* debug flag */ MODULE_PARM(io , "1-4i"); /* i/o port for the i'th card */ MODULE_PARM(irq, "1-4i"); /* irq for the i'th card */ MODULE_PARM(dma_rx,"1-4i"); /* dma for the i'th card */ MODULE_PARM(dma_tx,"1-4i"); /* dma for the i'th card */ MODULE_PARM(mode, "i-4i"); /* interface mode */ MODULE_PARM(clock, "i-4i"); /* clock source */ enum { ACB56_MODE_DTE, ACB56_MODE_DCE, ACB56_MODE_LOOP, ACB56_MODE_JACK, ACB56_MODE_ECHO, ACB56_MODE_BOTH }; enum { ACB56_CLOCK_EXT, ACB56_CLOCK_INT, ACB56_CLOCK_DPLL }; struct acb56_stats { unsigned long transmitted_sus; /* count of SUs setn */ unsigned long sent_fisus; /* count of FISUs sent */ unsigned long sent_lssus; /* count of LSSUs sent */ unsigned long sent_msus; /* count of MSUs sent */ unsigned long repeated_sus; /* count of sus repeated for lack of data */ unsigned long tx_underruns; /* count of destructive transmitter underruns */ unsigned long breaks_or_aborts; /* count of breaks or aborts received */ unsigned long dcd_transitions; /* count of transitions on the DCD lead */ unsigned long cts_transitions; /* count of transitions on the CTS lead */ unsigned long received_sus; /* count of SUs received */ unsigned long recv_fisus; /* count of received fisus */ unsigned long recv_lssus; /* count of received lssus */ unsigned long recv_msus; /* count of received msus */ unsigned long sus_in_error; /* count of SUs received in error */ unsigned long bits_octet_counted; /* count of bits counted in octet mode */ unsigned long sync_transitions; /* count of flag detection transitions */ unsigned long su_length_errors; /* count of SUs with erroneous lengths */ unsigned long parity_errors; /* count of parity errors */ unsigned long rx_overruns; /* count of reciver overruns */ unsigned long crc_errors; /* count of reciver CRC errors */ unsigned long frame_errors; /* count of reciver frame errors */ unsigned long residue_errors; /* count of reciver residue errors */ unsigned long rx_buffer_overflows;/* count of RB overflows */ unsigned long rx_frame_overflows; /* count of bytes overflowing frame */ unsigned long compressed_sus; /* count of sus compressed */ unsigned long cha_rx_sp_cond; /* count of this vectored interrupt */ unsigned long cha_rx_char_avail; /* count of this vectored interrupt */ unsigned long cha_ext_status; /* count of this vectored interrupt */ unsigned long cha_tx_buf_empty; /* count of this vectored interrupt */ unsigned long chb_rx_sp_cond; /* count of this vectored interrupt */ unsigned long chb_rx_char_avail; /* count of this vectored interrupt */ unsigned long chb_ext_status; /* count of this vectored interrupt */ unsigned long chb_tx_buf_empty; /* count of this vectored interrupt */ unsigned long interrupts; /* count of interrupts */ unsigned long rx_bytes; /* count of bytes received */ unsigned long tx_bytes; /* count of bytes transmitted */ unsigned long tx_eoms; /* count of normally completed frames */ unsigned long tx_aborts; /* count of aborted frames */ unsigned long buffer_errors; /* count of times could get no buffer */ }; struct acb56_dev { struct device dev; char name[sizeof("acb56%d ")]; struct device* next; /* next acb56 device */ int index; /* which card? */ int if_mode; /* interface mode */ int if_clock; /* interface clock */ int dma_tx; /* Tx DMA Channel Number */ int rx_octet_mode; /* Is octet counting on? */ int rx_octet_count; /* bits counted */ unsigned char regs[16]; /* Register Images */ unsigned char rr0; /* Image of Read Register 0 (RR0) */ struct sk_buff* tx_skb; /* transmit buffer */ struct sk_buff* rx_skb; /* receive buffer */ struct sk_buff* comp_skb; /* compressed su buffer */ struct sk_buff_head input_q;/* transmits queueud */ struct acb56_stats stats; unsigned char *tx_buf; unsigned char *tx_max; int tx_error; unsigned char *rx_buf; unsigned char *rx_max; int rx_error; struct net_device_stats nstats; /* NET4 version of stats */ }; static struct net_device_stats *acb56_get_stats(struct device *dev) { struct acb56_dev *p = (struct acb56_dev *)dev; p->nstats.rx_packets = p->stats.received_sus; p->nstats.tx_packets = p->stats.transmitted_sus; p->nstats.rx_bytes = p->stats.rx_bytes; p->nstats.tx_bytes = p->stats.tx_bytes; p->nstats.rx_errors = p->stats.sus_in_error; p->nstats.tx_errors = p->stats.tx_underruns; p->nstats.rx_dropped = 0; p->nstats.tx_dropped = 0; p->nstats.rx_length_errors = p->stats.su_length_errors; p->nstats.rx_over_errors = p->stats.rx_buffer_overflows; p->nstats.rx_crc_errors = p->stats.crc_errors; p->nstats.rx_frame_errors = p->stats.frame_errors; p->nstats.rx_fifo_errors = p->stats.rx_overruns; p->nstats.rx_missed_errors = 0; p->nstats.tx_aborted_errors = p->stats.tx_aborts; p->nstats.tx_carrier_errors = p->stats.dcd_transitions; p->nstats.tx_fifo_errors = p->stats.tx_underruns; p->nstats.tx_heartbeat_errors = 0; p->nstats.tx_window_errors = 0; p->nstats.rx_compressed = p->stats.compressed_sus; p->nstats.tx_compressed = p->stats.repeated_sus; return &p->nstats; } static int acb56_hard_start_xmit(struct sk_buff* skb, struct device *dev) { struct acb56_dev *p = (struct acb56_dev *)dev; int ret = skb_queue_len(&p->input_q); skb_queue_tail(&p->input_q,skb); return ret; } static __inline void acb56_tx_setup_next_frame(struct acb56_dev *p) { p->stats.tx_eoms++; p->stats.tx_bytes+=4; if (skb_queue_len(&p->input_q)) { kfree_skb(p->tx_skb); p->tx_skb = skb_dequeue(&p->input_q); } else { if (p->tx_skb->len>5 || (p->tx_skb->len>3 && p->tx_skb->data[3]==LSSU_SIB)) { skb_trim(p->tx_skb,3); /* if it's an MSU or SIB */ p->tx_skb->data[2]=0; /* make a FISU out of it */ p->stats.repeated_sus++; } } p->tx_buf = p->tx_skb->data; p->tx_max = p->tx_skb->tail; } static __inline void acb56_tx_underrun_eom (struct acb56_dev *p) { if (p->rr0&0x40) { /* set */ p->tx_buf = p->tx_skb->data; p->tx_error = 1; p->stats.tx_aborts++; p->stats.tx_underruns++; p->stats.tx_bytes+=3; p->rr0&=~0x04; /* clear indication */ return; } else { /* cleared */ return acb56_tx_setup_next_frame(p); } } static __inline void acb56_sync_hunt(struct acb56_dev *p) { int actrl = p->dev.base_addr+1; if (p->rx_octet_mode && !p->rr0&0x10) { p->rx_octet_mode = 0; /* we synced */ outb(0x0f,actrl); outb(p->regs[0x0f]&=~0x02,actrl); /* turn of octet mode */ } else if ((p->rr0&0x10) && (p->if_clock!=2)) { p->rx_octet_count = 0; p->rx_octet_mode = 1; outb(0x0f,actrl); outb(p->regs[0x0f]|=0x02,actrl); /* turn on octet mode */ } p->stats.sync_transitions++; } static __inline void acb56_break_abort(struct acb56_dev *p) { p->stats.breaks_or_aborts++; } static __inline void acb56_dcd(struct acb56_dev *p) { p->stats.dcd_transitions++; } static __inline void acb56_cts(struct acb56_dev *p) { p->stats.cts_transitions++; } static __inline void acb56_zero_count(struct acb56_dev *p) { if (!p->rx_octet_count++&0x0000007f || (p->if_mode==0)) { p->stats.sus_in_error++; ss7if_err(&p->dev); } p->stats.bits_octet_counted++; } static __inline void acb56_frame_error(struct acb56_dev *p) { p->rx_error = 0; p->rx_buf = p->rx_skb->data; if (p->rx_octet_mode) return; /* only a good frame counts in octet mode */ p->comp_skb->len=0; p->stats.received_sus++; p->stats.rx_bytes+=2; p->stats.sus_in_error++; ss7if_err(&p->dev); } static __inline void acb56_parity_error(struct acb56_dev *p) { p->stats.parity_errors++; p->rx_error = 1; } static __inline void acb56_rx_overrun(struct acb56_dev *p) { p->stats.rx_overruns++; p->rx_error = 1; } static __inline void acb56_crc_error(struct acb56_dev *p) { if (p->rx_octet_mode) return; /* only a good frame counts in octet mode */ p->stats.crc_errors++; acb56_frame_error(p); } static __inline void acb56_buffer_error(struct acb56_dev *p) { if (p->rx_octet_mode) return; /* only a good frame counts in octet mode */ p->stats.buffer_errors++; acb56_frame_error(p); } static __inline void acb56_length_error(struct acb56_dev *p) { if (p->rx_octet_mode) return; /* only a good frame counts in octet mode */ p->stats.su_length_errors++; acb56_frame_error(p); } static __inline void acb56_rx_setup_next_frame(struct acb56_dev *p) { int len = p->rx_buf - p->rx_skb->data; p->rx_buf = p->rx_skb->data; p->stats.received_sus++; if (len==3) p->stats.recv_fisus++; if (len>=6) p->stats.recv_msus++; if (len>=4) p->stats.recv_lssus++; p->stats.rx_bytes+=2; } static __inline void acb56_residue_error(struct acb56_dev *p) { p->stats.residue_errors++; acb56_frame_error(p); } static __inline void acb56_end_of_frame(struct acb56_dev *p) { if (p->rx_error) return acb56_frame_error(p); { struct sk_buff *skb2, *skb = p->rx_skb, *skbc = p->comp_skb; unsigned int len = (unsigned int)(p->rx_buf - skb->data); unsigned char li = skb->data[2]&0x3f; if ((len<3)||(li!=(len>63+3?63+3:len))) return acb56_length_error(p); skb_trim(skb,len); if (len<6) { if (len==skbc->len && !memcmp(skbc->data,skb->data,len)) { p->stats.compressed_sus++; acb56_rx_setup_next_frame(p); ss7if_rpt(&p->dev); return; } else { memcpy(skbc->data,skb->data,len); skbc->len = len; } } else skbc->len = 0; p->rx_octet_mode = 0; if ((skb2 = skb_clone(skb,GFP_KERNEL))==NULL) return acb56_buffer_error(p); acb56_rx_setup_next_frame(p); ss7if_rx(skb2); return; } } static __inline void acb56_frame_overflow_check(struct acb56_dev *p) { int actrl = p->dev.base_addr+1; /* check for frame overflow */ if (p->rx_buf>p->rx_max) { /* FIGURE 11/Q.703 (sheet 1 of 2) "m+7 octets without flags" */ if (!p->rx_octet_mode && p->if_clock!=2) { /* can't octet count on DPLL! */ p->rx_octet_count = 0; p->rx_octet_mode = 1; outb(0x0f,actrl); outb(p->regs[0x0f]|=0x02,actrl); /* octet counting isr */ outb(0x03,actrl); outb(p->regs[0x03]|=0x10,actrl); /* force flag hunt */ acb56_rx_setup_next_frame(p); ss7if_los(&p->dev); } } } static void acb56_isr_cha_tx_buf_empty(struct acb56_dev *p) { p->stats.cha_tx_buf_empty++; } static void acb56_isr_cha_ext_status(struct acb56_dev *p) { unsigned char rr0; int actrl = p->dev.base_addr+1; rr0 = p->rr0&~0x02 ; outb(0x00,actrl); p->rr0=inb(actrl); outb(0x00,actrl); outb(0x10,actrl); outb(0x00,actrl); outb(0x10,actrl); /* debounce */ rr0 ^= p->rr0 & 0xfa; if (rr0) { if (rr0 & 0x40) acb56_tx_underrun_eom (p); if (rr0 & 0x10) acb56_sync_hunt (p); if (rr0 & 0x80) acb56_break_abort (p); if (rr0 & 0x08) acb56_dcd (p); if (rr0 & 0x20) acb56_cts (p); } else acb56_zero_count (p); p->stats.cha_ext_status++; } static void acb56_isr_cha_rx_char_avail(struct acb56_dev *p) { register int adata = p->dev.base_addr; register int i=0; /* collect bytes */ for (i=0; i<4; i++) *(p->rx_buf++) = inb(adata); acb56_frame_overflow_check(p); p->stats.rx_bytes+=4; p->stats.cha_rx_char_avail++; } static void acb56_isr_cha_rx_sp_cond(struct acb56_dev *p) { unsigned char rr1 = 0; register int adata = p->dev.base_addr; register int actrl = p->dev.base_addr+1; register int i=0; p->stats.cha_rx_sp_cond++; /* collect bytes */ for (i=0; i<4 && (outb(0x00,actrl)||(inb(actrl)&0x01)); i++) { *(p->rx_buf++) = inb(adata); p->stats.rx_bytes++; } acb56_frame_overflow_check(p); /* check for special conditions */ outb(0x07,actrl); if (inb(actrl)&0x40) { outb(0x01,actrl); if ((rr1=inb(actrl))&0xf0) { outb(0x00,actrl); outb(0x30,actrl); /* reset error */ if (rr1 & 0x10 ) { return acb56_parity_error (p); } if (rr1 & 0x20 ) { return acb56_rx_overrun (p); } if (rr1 & 0x80 ) { if (rr1 & 0x40 ) { return acb56_crc_error (p); } else { if((rr1&0xe)^0x6){ return acb56_residue_error (p); } else { return acb56_end_of_frame (p); } } } } } } static void acb56_isr_donothing(struct acb56_dev *p) { (void)p; }; static void acb56_isr_chb_tx_buf_empty(struct acb56_dev* p) { p->stats.chb_tx_buf_empty++; } static void acb56_isr_chb_ext_status(struct acb56_dev* p) { p->stats.chb_ext_status++; } static void acb56_isr_chb_rx_char_avail(struct acb56_dev* p){ p->stats.chb_rx_char_avail++; } static void acb56_isr_chb_rx_sp_cond(struct acb56_dev* p) { p->stats.chb_rx_sp_cond++; } static void acb56_isr(int irq, void *dev_id, struct pt_regs *regs) { static void (*vector_map[])(struct acb56_dev *) = { acb56_isr_chb_tx_buf_empty, acb56_isr_chb_ext_status, acb56_isr_chb_rx_char_avail, acb56_isr_chb_rx_sp_cond, acb56_isr_cha_tx_buf_empty, acb56_isr_cha_ext_status, acb56_isr_cha_rx_char_avail, acb56_isr_cha_rx_sp_cond }; unsigned char rr3; register int i; register int actrl = ((struct device *)dev_id)->base_addr+1; for (i=0,outb(0x03,actrl),rr3=inb(actrl);i<4&&rr3;i++,outb(0x03,actrl),rr3=inb(actrl)) { outb(0x02,actrl+2); (*vector_map[inb(actrl+2)>>1])(dev_id); /* reset highest interrupt under service */ outb(0x00,actrl); outb(0x38,actrl); } ((struct acb56_dev *)dev_id)->stats.interrupts++; }; static int acb56_close(struct device *dev) { struct acb56_dev *p = (struct acb56_dev *)dev; int actrl = dev->base_addr+1; outb(0x09,actrl); outb(0xc0,actrl); /* force hw reset */ outb(0x09,actrl); outb(p->regs[0x09]&=~0x08,actrl); /* stop interrupts */ free_irq(p->dev.irq,NULL); if (p->dma_tx) { outb(0x0e,actrl); outb(p->regs[0x0e]&=~0x04,actrl); /* disable dma */ disable_dma(p->dma_tx); free_dma(p->dma_tx); } if (p->dev.dma) { outb(0x01,actrl); outb(p->regs[0x01]&=~0xc0,actrl); /* disable dma */ disable_dma(p->dev.dma); free_dma(p->dev.dma); } outb(0x09,actrl); outb(0xc0,actrl); /* force hw reset */ skb_queue_purge(&p->input_q); return(0); } static const unsigned char preamble[] = { 0x09, 0xC0, 0x0F, 0x01, 0x07, 0x6B, 0x0F, 0x00, 0x00, 0x00, 0x04, 0x20, 0x01, 0x00, 0x02, 0x00, 0x03, 0xCA, 0x05, 0x63, 0x06, 0x00, 0x07, 0x7e, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x16, 0x0C, 0x40, 0x0D, 0x00, 0x0E, 0x02, 0x0E, 0x02, 0x0E, 0x02, 0x0E, 0x03, 0x03, 0xCB, 0x05, 0x6B, 0x00, 0x80, 0x00, 0x30, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x10, 0x00, 0x10, 0x01, 0x00, 0x09, 0x00 }; static const unsigned char preset[] = { 0x09, 0xc0, 0x00, 0x00, 0x04, 0x20, 0x03, 0xca, 0x05, 0xe3, 0x07, 0x7e, 0x06, 0x00, 0x0F, 0x01, 0x07, 0x6b, 0x0F, 0x00, 0x01, 0x00, 0x02, 0x00, 0x09, 0x00, 0x0A, 0x80 }; static const unsigned char mode_clock[6][3] = { { 0x08, 0x05, 0x7f }, { 0x08, 0x56, 0x7f }, { 0x50, 0x50, 0x78 }, { 0x16, 0x50, 0x1f }, { 0x50, 0x50, 0x78 }, { 0x50, 0x50, 0x78 } }; static int acb56_open(struct device *dev) { int err = 0, i, actrl; struct acb56_dev *p = (struct acb56_dev*)dev; if (dev->dma && request_dma(dev->dma ,"acb56")) dev->dma = 0; if (p->dma_tx && request_dma(p->dma_tx,"acb56")) p->dma_tx = 0; if ((err=request_irq(dev->irq,acb56_isr,SA_SHIRQ,"acb56",p))) goto irqerror; actrl = dev->base_addr+1; for (i=0;i<16;i++) p->regs[i] = 0; /* register images */ /* setup chip */ for (i=0;iregs[i>>1]=preset[i],actrl); i++; } /* setup interface and clock modes */ outb(0x0b,actrl); outb(p->regs[0x0b]=mode_clock[p->if_mode][p->if_clock],actrl); /* setup baud rate generator */ if (p->if_mode==ACB56_MODE_DTE) { outb(0x0c,actrl);outb(p->regs[0xc]=0xca,actrl); outb(0x0d,actrl);outb(p->regs[0xd]=0x1c,actrl); } else if (p->if_mode==ACB56_MODE_LOOP) { outb(0x0c,actrl);outb(p->regs[0xc]=0x00,actrl); outb(0x0d,actrl);outb(p->regs[0xd]=0x00,actrl); } else { outb(0x0c,actrl);outb(p->regs[0xc]=0x40,actrl); outb(0x0d,actrl);outb(p->regs[0xd]=0x00,actrl); } /* special DPLL modes */ if (p->if_clock==ACB56_CLOCK_DPLL) { outb(0x0e,actrl);outb(0x60,actrl); outb(0x0e,actrl);outb(0xe0,actrl); if (p->if_mode==ACB56_MODE_DTE) { outb(0x0e,actrl);outb(0xa0,actrl); } else { outb(0x0e,actrl);outb(0x80,actrl); } outb(0x0e,actrl);outb(0x20,actrl); } outb(0x0e,actrl);outb(p->regs[0x0e]=0x02,actrl); /* setup loopback and echo modes */ switch(p->if_mode) { case ACB56_MODE_LOOP: outb(0x0e,actrl); outb(p->regs[0x0e]|=0x10,actrl); break; case ACB56_MODE_ECHO: outb(0x0e,actrl); outb(p->regs[0x0e]|=0x08,actrl); break; case ACB56_MODE_BOTH: outb(0x0e,actrl); outb(p->regs[0x0e]|=0x18,actrl); break; } /* set up dma registers */ if (dev->dma||p->dma_tx) { outb(0x0e,actrl); outb(p->regs[0x0e]|=0x04,actrl); if (dev->dma&&p->dma_tx) outb(0x01,actrl); outb(p->regs[0x01]|=0xf9,actrl); if (p->dma_tx) outb(0x01,actrl); outb(p->regs[0x01]|=0xc1,actrl); if (p->dma_tx) outb(0x01,actrl); outb(p->regs[0x01]|=0xfb,actrl); outb(0x80,actrl+3); } else { outb(0x0e,actrl); outb(p->regs[0x0e]&=~0x04,actrl); outb(0x01,actrl); outb(p->regs[0x01]|=0x13,actrl); outb(0x00,actrl+3); } /* disable status fifo */ outb(0x0f,actrl); outb(0xfc,actrl); outb(0x09,actrl); outb(0x02,actrl); /* reset and enable transmitters and receivers */ outb(0x0E,actrl); outb(p->regs[0x0e]|=0x01,actrl); outb(0x03,actrl); outb(p->regs[0x0e]|=0x01,actrl); outb(0x00,actrl); outb(0x30,actrl); outb(0x05,actrl); outb(p->regs[0x0e]|=0x08,actrl); outb(0x00,actrl); outb(0x80,actrl); outb(0x00,actrl); outb(0xC0,actrl); outb(0x00,actrl); outb(0x10,actrl); outb(0x00,actrl); outb(0x10,actrl); if ((p->tx_skb = alloc_skb(SS7_MTU+4,GFP_ATOMIC)) == NULL) return -ENOBUFS; p->tx_skb->protocol = AF_SS7; p->tx_skb->pkt_type = ETH_P_SS7; p->tx_skb->priority = TC_PRIO_BESTEFFORT; p->tx_skb->h.raw = p->tx_skb->nh.raw = p->tx_skb->mac.raw = p->tx_skb->data; p->rx_skb = skb_copy(p->tx_skb,GFP_ATOMIC); p->comp_skb = skb_copy(p->tx_skb,GFP_ATOMIC); p->comp_skb->len = 0; p->tx_skb->len = 4; p->tx_skb->data[0] = 0x80; /* Initial SIOS */ p->tx_skb->data[1] = 0x80; p->tx_skb->data[2] = 0x01; p->tx_skb->data[3] = 0x00; /* enable master interrupt bit */ outb(0x09,actrl); outb(p->regs[0x09]|=0x08,actrl); /* we're running! phew! */ return(0); irqerror: if (dev->dma ) free_dma(dev->dma ); if (p->dma_tx) free_dma(p->dma_tx); return err; } static int acb56_init(struct device *dev) { dev->type = ARPHRD_SS7; /* identifies us to ss7 link module */ dev->tx_queue_len = TX_Q_MAX; /* sets up buffers */ dev->open = &acb56_open; /* service routines for ss7 link module */ dev->hard_start_xmit = &acb56_hard_start_xmit; /* service routines for ss7 link module */ dev->stop = &acb56_close; /* service routines for ss7 link module */ dev->get_stats = &acb56_get_stats; /* service routines for ss7 link module */ return (0); #if 0 dev->do_ioctl = &acb56_ioctl; /* io controls */ #endif } static void acb56_destructor(struct device *dev) { release_region(dev->base_addr,8); kfree(dev); } static unsigned char irqprobe[] = { 0x01, 0x19, 0x0F, 0xFA, 0x00, 0x10, 0x00, 0x10, 0x09, 0x08, 0x0E, 0x03 }; static void dummy_isr(int irq, void *dev_id, struct pt_regs *regs) { volatile int *p; (void)irq; (void)dev_id; (void)regs; p = NULL; p++; } static int board = 0; static int ports[] = { 0x238, 0x280, 0x2A0, 0x300, 0x328, 0 }; int acb56_probe(struct device* dev) { int *test, iobase, _irq, actrl, _dma_rx, _dma_tx, i, err; unsigned long time, cookie; struct acb56_dev *p = NULL; if ((iobase=io[board])!=-1 && !(err=check_region(iobase,8))) goto probe_irq; if (iobase!=-1) return err; /* probe for iobase */ for (test = &ports[0]; *test; test++) { iobase = *test; actrl = iobase+1; if (check_region(iobase,8)) continue; /* device conflict */ outb(0x02,actrl); outb(0x55,actrl); /* write to unallocated 8530 bit in WR2 */ outb(0x02,actrl); if (inb(actrl)!=0x55) continue; /* probably an 8530 */ outb(0x09,actrl); outb(0xc0,actrl); /* force hardware reset */ outb(0x0f,actrl); outb(0x01,actrl); /* Access W7P register */ outb(0x0f,actrl); if (!inb(actrl)&0x01) continue; /* probably an 8530 */ outb(0x0f,actrl); outb(0x00,actrl); /* Remove accss to W7P register */ outb(0x0f,actrl); if (inb(actrl)&0x01) continue; /* probably an 8530 */ goto probe_irq; } return -ENODEV; probe_irq: if ((_irq=irq[board])!=-1 && !(err = request_irq(_irq,dummy_isr,SA_SHIRQ,"acb56_dummy",NULL))) goto probe_dma; if (_irq!=-1) return err; /* probe for irq */ actrl = iobase+1; for (i=0;iname = (char *)(dev+1); if ((err=dev_alloc_name(dev,"escc%d"))) { kfree(dev); return err; } if ((err=register_netdev(dev))) { kfree(dev); return err; } } request_region(iobase,8,"acb56"); /* setup major operating modes */ p = (struct acb56_dev *)dev; if ((p->if_mode =mode [board])==-1) p->if_mode = ACB56_MODE_LOOP; if ((p->if_clock=clock[board])==-1) p->if_clock = ACB56_CLOCK_EXT; { static const char *if_modes [] = { "DTE", "DCE", "LOOP", "JACK", "ECHO", "LOOP&/ECHO" }; static const char *if_clocks[] = { "EXT", "INT", "DPLL" }; printk("%s: acb56 at %04xH, irq %d, dma %d rx %d tx, %s clk %s\n", dev->name, iobase, _irq, _dma_rx, _dma_tx, if_modes[p->if_mode], if_clocks[p->if_clock]); } p->index = board; p->dma_tx = _dma_tx; dev->priv = p; dev->base_addr = iobase; dev->irq = _irq; dev->dma = _dma_rx; dev->init = &acb56_init; dev->destructor = &acb56_destructor; if ((err=register_netdev(dev))) { kfree(dev); return err; } acb56_devices = p; return(0); } int init_module(void) { #ifdef CONGIG_KMOD request_module("ss7link"); #endif printk(KERN_INFO ACB56_BANNER); board = 0; if (debug >= 0) acb56_debug = debug; while (acb56_probe(0) == 0) { board++; } if (!board) printk(KERN_INFO "No acb56 devices found.\n"); return board ? 0 : -ENODEV; } void cleanup_module(void) { struct acb56_dev *card_next = acb56_devices; while ((acb56_devices = card_next)) { card_next = (struct acb56_dev *)acb56_devices->next; unregister_netdevice((struct device *)acb56_devices); } }