From f5a400b0034253be88cea06dcdeb96afc1f029c2 Mon Sep 17 00:00:00 2001 From: fjoe Date: Wed, 21 Nov 2001 22:29:35 +0000 Subject: Add driver for Granch SBNI12-xx ISA and PCI network adapters. MFC after: 1 week --- sys/dev/sbni/if_sbni.c | 1312 ++++++++++++++++++++++++++++++++++++++++++++ sys/dev/sbni/if_sbni_isa.c | 165 ++++++ sys/dev/sbni/if_sbni_pci.c | 163 ++++++ sys/dev/sbni/if_sbnireg.h | 106 ++++ sys/dev/sbni/if_sbnivar.h | 152 +++++ 5 files changed, 1898 insertions(+) create mode 100644 sys/dev/sbni/if_sbni.c create mode 100644 sys/dev/sbni/if_sbni_isa.c create mode 100644 sys/dev/sbni/if_sbni_pci.c create mode 100644 sys/dev/sbni/if_sbnireg.h create mode 100644 sys/dev/sbni/if_sbnivar.h (limited to 'sys/dev') diff --git a/sys/dev/sbni/if_sbni.c b/sys/dev/sbni/if_sbni.c new file mode 100644 index 0000000..fd06be3 --- /dev/null +++ b/sys/dev/sbni/if_sbni.c @@ -0,0 +1,1312 @@ +/* + * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved. + * Author: Denis I.Timofeev + * + * Redistributon and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Device driver for Granch SBNI12 leased line adapters + * + * Revision 2.0.0 1997/08/06 + * Initial revision by Alexey Zverev + * + * Revision 2.0.1 1997/08/11 + * Additional internal statistics support (tx statistics) + * + * Revision 2.0.2 1997/11/05 + * if_bpf bug has been fixed + * + * Revision 2.0.3 1998/12/20 + * Memory leakage has been eliminated in + * the sbni_st and sbni_timeout routines. + * + * Revision 3.0 2000/08/10 by Yaroslav Polyakov + * Support for PCI cards. 4.1 modification. + * + * Revision 3.1 2000/09/12 + * Removed extra #defines around bpf functions + * + * Revision 4.0 2000/11/23 by Denis Timofeev + * Completely redesigned the buffer management + * + * Revision 4.1 2001/01/21 + * Support for PCI Dual cards and new SBNI12D-10, -11 Dual/ISA cards + * + * Written with reference to NE2000 driver developed by David Greenman. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define ASM_CRC 1 + +static void sbni_init(void *); +static void sbni_start(struct ifnet *); +static int sbni_ioctl(struct ifnet *, u_long, caddr_t); +static void sbni_watchdog(struct ifnet *); +static void sbni_stop(struct sbni_softc *); +static void handle_channel(struct sbni_softc *); + +static void card_start(struct sbni_softc *); +static int recv_frame(struct sbni_softc *); +static void send_frame(struct sbni_softc *); +static int upload_data(struct sbni_softc *, u_int, u_int, u_int, u_int32_t); +static int skip_tail(struct sbni_softc *, u_int, u_int32_t); +static void interpret_ack(struct sbni_softc *, u_int); +static void download_data(struct sbni_softc *, u_int32_t *); +static void prepare_to_send(struct sbni_softc *); +static void drop_xmit_queue(struct sbni_softc *); +static int get_rx_buf(struct sbni_softc *); +static void indicate_pkt(struct sbni_softc *); +static void change_level(struct sbni_softc *); +static int check_fhdr(struct sbni_softc *, u_int *, u_int *, + u_int *, u_int *, u_int32_t *); +static int append_frame_to_pkt(struct sbni_softc *, u_int, u_int32_t); +static void timeout_change_level(struct sbni_softc *); +static void send_frame_header(struct sbni_softc *, u_int32_t *); +static void set_initial_values(struct sbni_softc *, struct sbni_flags); + +static u_int32_t calc_crc32(u_int32_t, caddr_t, u_int); +static timeout_t sbni_timeout; + +static __inline u_char sbni_inb(struct sbni_softc *, enum sbni_reg); +static __inline void sbni_outb(struct sbni_softc *, enum sbni_reg, u_char); +static __inline void sbni_insb(struct sbni_softc *, u_char *, u_int); +static __inline void sbni_outsb(struct sbni_softc *, u_char *, u_int); + +static u_int32_t crc32tab[]; + +#ifdef SBNI_DUAL_COMPOUND +struct sbni_softc *headlist; +#endif + +u_int32_t next_sbni_unit; + +/* -------------------------------------------------------------------------- */ + +static __inline u_char +sbni_inb(struct sbni_softc *sc, enum sbni_reg reg) +{ + return (inb(sc->base_addr + reg)); +} + +static __inline void +sbni_outb(struct sbni_softc *sc, enum sbni_reg reg, u_char value) +{ + outb(sc->base_addr + reg, value); +} + +static __inline void +sbni_insb(struct sbni_softc *sc, u_char *to, u_int len) +{ + insb(sc->base_addr + DAT, to, len); +} + +static __inline void +sbni_outsb(struct sbni_softc *sc, u_char *from, u_int len) +{ + outsb(sc->base_addr + DAT, from, len); +} + + +/* + Valid combinations in CSR0 (for probing): + + VALID_DECODER 0000,0011,1011,1010 + + ; 0 ; - + TR_REQ ; 1 ; + + TR_RDY ; 2 ; - + TR_RDY TR_REQ ; 3 ; + + BU_EMP ; 4 ; + + BU_EMP TR_REQ ; 5 ; + + BU_EMP TR_RDY ; 6 ; - + BU_EMP TR_RDY TR_REQ ; 7 ; + + RC_RDY ; 8 ; + + RC_RDY TR_REQ ; 9 ; + + RC_RDY TR_RDY ; 10 ; - + RC_RDY TR_RDY TR_REQ ; 11 ; - + RC_RDY BU_EMP ; 12 ; - + RC_RDY BU_EMP TR_REQ ; 13 ; - + RC_RDY BU_EMP TR_RDY ; 14 ; - + RC_RDY BU_EMP TR_RDY TR_REQ ; 15 ; - +*/ + +#define VALID_DECODER (2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200) + + +int +sbni_probe(struct sbni_softc *sc) +{ + u_char csr0; + + csr0 = sbni_inb(sc, CSR0); + if (csr0 != 0xff && csr0 != 0x00) { + csr0 &= ~EN_INT; + if (csr0 & BU_EMP) + csr0 |= EN_INT; + + if (VALID_DECODER & (1 << (csr0 >> 4))) + return (0); + } + + return (ENXIO); +} + + +/* + * Install interface into kernel networking data structures + */ +void +sbni_attach(struct sbni_softc *sc, int unit, struct sbni_flags flags) +{ + struct ifnet *ifp; + u_char csr0; + + ifp = &sc->arpcom.ac_if; + sbni_outb(sc, CSR0, 0); + set_initial_values(sc, flags); + + callout_handle_init(&sc->wch); + if (!ifp->if_name) { + /* Initialize ifnet structure */ + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "sbni"; + ifp->if_init = sbni_init; + ifp->if_start = sbni_start; + ifp->if_output = ether_output; + ifp->if_ioctl = sbni_ioctl; + ifp->if_watchdog = sbni_watchdog; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + + /* report real baud rate */ + csr0 = sbni_inb(sc, CSR0); + ifp->if_baudrate = + (csr0 & 0x01 ? 500000 : 2000000) / (1 << flags.rate); + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + } + /* device attach does transition from UNCONFIGURED to IDLE state */ + + printf("%s%d: speed %ld, address %6D, rxl ", ifp->if_name, + ifp->if_unit, ifp->if_baudrate, sc->arpcom.ac_enaddr, ":"); + if (sc->delta_rxl) + printf("auto\n"); + else + printf("%d (fixed)\n", sc->cur_rxl_index); +} + +/* -------------------------------------------------------------------------- */ + +static void +sbni_init(void *xsc) +{ + struct sbni_softc *sc; + struct ifnet *ifp; + int s; + + sc = (struct sbni_softc *)xsc; + ifp = &sc->arpcom.ac_if; + + /* address not known */ + if (TAILQ_EMPTY(&ifp->if_addrhead)) + return; + + /* + * kludge to avoid multiple initialization when more than once + * protocols configured + */ + if (ifp->if_flags & IFF_RUNNING) + return; + + s = splimp(); + ifp->if_timer = 0; + card_start(sc); + sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* attempt to start output */ + sbni_start(ifp); + splx(s); +} + + +static void +sbni_start(struct ifnet *ifp) +{ + struct sbni_softc *sc = ifp->if_softc; + if (sc->tx_frameno == 0) + prepare_to_send(sc); +} + + +static void +sbni_stop(struct sbni_softc *sc) +{ + sbni_outb(sc, CSR0, 0); + drop_xmit_queue(sc); + + if (sc->rx_buf_p) { + m_freem(sc->rx_buf_p); + sc->rx_buf_p = NULL; + } + + untimeout(sbni_timeout, sc, sc->wch); + sc->wch.callout = NULL; +} + +/* -------------------------------------------------------------------------- */ + +/* interrupt handler */ + +/* + * SBNI12D-10, -11/ISA boards within "common interrupt" mode could not + * be looked as two independent single-channel devices. Every channel seems + * as Ethernet interface but interrupt handler must be common. Really, first + * channel ("master") driver only registers the handler. In it's struct softc + * it has got pointer to "slave" channel's struct softc and handles that's + * interrupts too. + * softc of successfully attached ISA SBNI boards is linked to list. + * While next board driver is initialized, it scans this list. If one + * has found softc with same irq and ioaddr different by 4 then it assumes + * this board to be "master". + */ + +void +sbni_intr(void *arg) +{ + struct sbni_softc *sc; + int repeat; + + sc = (struct sbni_softc *)arg; + + do { + repeat = 0; + if (sbni_inb(sc, CSR0) & (RC_RDY | TR_RDY)) { + handle_channel(sc); + repeat = 1; + } + if (sc->slave_sc /* second channel present */ + && (sbni_inb(sc->slave_sc, CSR0) & (RC_RDY | TR_RDY))) { + handle_channel(sc->slave_sc); + repeat = 1; + } + } while (repeat); +} + + +static void +handle_channel(struct sbni_softc *sc) +{ + int req_ans; + u_char csr0; + + sbni_outb(sc, CSR0, (sbni_inb(sc, CSR0) & ~EN_INT) | TR_REQ); + + sc->timer_ticks = CHANGE_LEVEL_START_TICKS; + for (;;) { + csr0 = sbni_inb(sc, CSR0); + if ((csr0 & (RC_RDY | TR_RDY)) == 0) + break; + + req_ans = !(sc->state & FL_PREV_OK); + + if (csr0 & RC_RDY) + req_ans = recv_frame(sc); + + /* + * TR_RDY always equals 1 here because we have owned the marker, + * and we set TR_REQ when disabled interrupts + */ + csr0 = sbni_inb(sc, CSR0); + if ((csr0 & TR_RDY) == 0 || (csr0 & RC_RDY) != 0) + printf("sbni: internal error!\n"); + + /* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */ + if (req_ans || sc->tx_frameno != 0) + send_frame(sc); + else + /* send the marker without any data */ + sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) & ~TR_REQ); + } + + sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | EN_INT); +} + + +/* + * Routine returns 1 if it need to acknoweledge received frame. + * Empty frame received without errors won't be acknoweledged. + */ + +static int +recv_frame(struct sbni_softc *sc) +{ + u_int32_t crc; + u_int framelen, frameno, ack; + u_int is_first, frame_ok; + + crc = CRC32_INITIAL; + if (check_fhdr(sc, &framelen, &frameno, &ack, &is_first, &crc)) { + frame_ok = framelen > 4 + ? upload_data(sc, framelen, frameno, is_first, crc) + : skip_tail(sc, framelen, crc); + if (frame_ok) + interpret_ack(sc, ack); + } else + frame_ok = 0; + + sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) ^ CT_ZER); + if (frame_ok) { + sc->state |= FL_PREV_OK; + if (framelen > 4) + sc->in_stats.all_rx_number++; + } else { + sc->state &= ~FL_PREV_OK; + change_level(sc); + sc->in_stats.all_rx_number++; + sc->in_stats.bad_rx_number++; + } + + return (!frame_ok || framelen > 4); +} + + +static void +send_frame(struct sbni_softc *sc) +{ + u_int32_t crc; + u_char csr0; + + crc = CRC32_INITIAL; + if (sc->state & FL_NEED_RESEND) { + + /* if frame was sended but not ACK'ed - resend it */ + if (sc->trans_errors) { + sc->trans_errors--; + if (sc->framelen != 0) + sc->in_stats.resend_tx_number++; + } else { + /* cannot xmit with many attempts */ + drop_xmit_queue(sc); + goto do_send; + } + } else + sc->trans_errors = TR_ERROR_COUNT; + + send_frame_header(sc, &crc); + sc->state |= FL_NEED_RESEND; + /* + * FL_NEED_RESEND will be cleared after ACK, but if empty + * frame sended then in prepare_to_send next frame + */ + + + if (sc->framelen) { + download_data(sc, &crc); + sc->in_stats.all_tx_number++; + sc->state |= FL_WAIT_ACK; + } + + sbni_outsb(sc, (u_char *)&crc, sizeof crc); + +do_send: + csr0 = sbni_inb(sc, CSR0); + sbni_outb(sc, CSR0, csr0 & ~TR_REQ); + + if (sc->tx_frameno) { + /* next frame exists - request to send */ + sbni_outb(sc, CSR0, csr0 | TR_REQ); + } +} + + +static void +download_data(struct sbni_softc *sc, u_int32_t *crc_p) +{ + struct mbuf *m; + caddr_t data_p; + u_int data_len, pos, slice; + + data_p = NULL; /* initialized to avoid warn */ + pos = 0; + + for (m = sc->tx_buf_p; m != NULL && pos < sc->pktlen; m = m->m_next) { + if (pos + m->m_len > sc->outpos) { + data_len = m->m_len - (sc->outpos - pos); + data_p = mtod(m, caddr_t) + (sc->outpos - pos); + + goto do_copy; + } else + pos += m->m_len; + } + + data_len = 0; + +do_copy: + pos = 0; + do { + if (data_len) { + slice = min(data_len, sc->framelen - pos); + sbni_outsb(sc, data_p, slice); + *crc_p = calc_crc32(*crc_p, data_p, slice); + + pos += slice; + if (data_len -= slice) + data_p += slice; + else { + do m = m->m_next; + while (m != NULL && m->m_len == 0); + + if (m) { + data_len = m->m_len; + data_p = mtod(m, caddr_t); + } + } + } else { + /* frame too short - zero padding */ + + pos = sc->framelen - pos; + while (pos--) { + sbni_outb(sc, DAT, 0); + *crc_p = CRC32(0, *crc_p); + } + return; + } + } while (pos < sc->framelen); +} + + +static int +upload_data(struct sbni_softc *sc, u_int framelen, u_int frameno, + u_int is_first, u_int32_t crc) +{ + int frame_ok; + + if (is_first) { + sc->wait_frameno = frameno; + sc->inppos = 0; + } + + if (sc->wait_frameno == frameno) { + + if (sc->inppos + framelen <= ETHER_MAX_LEN) { + frame_ok = append_frame_to_pkt(sc, framelen, crc); + + /* + * if CRC is right but framelen incorrect then transmitter + * error was occured... drop entire packet + */ + } else if ((frame_ok = skip_tail(sc, framelen, crc)) != 0) { + sc->wait_frameno = 0; + sc->inppos = 0; + sc->arpcom.ac_if.if_ierrors++; + /* now skip all frames until is_first != 0 */ + } + } else + frame_ok = skip_tail(sc, framelen, crc); + + if (is_first && !frame_ok) { + /* + * Frame has been violated, but we have stored + * is_first already... Drop entire packet. + */ + sc->wait_frameno = 0; + sc->arpcom.ac_if.if_ierrors++; + } + + return (frame_ok); +} + + +static __inline void send_complete(struct sbni_softc *); + +static __inline void +send_complete(struct sbni_softc *sc) +{ + m_freem(sc->tx_buf_p); + sc->tx_buf_p = NULL; + sc->arpcom.ac_if.if_opackets++; +} + + +static void +interpret_ack(struct sbni_softc *sc, u_int ack) +{ + if (ack == FRAME_SENT_OK) { + sc->state &= ~FL_NEED_RESEND; + + if (sc->state & FL_WAIT_ACK) { + sc->outpos += sc->framelen; + + if (--sc->tx_frameno) + sc->framelen = min(sc->maxframe, + sc->pktlen - sc->outpos); + else { + send_complete(sc); + prepare_to_send(sc); + } + } + } + + sc->state &= ~FL_WAIT_ACK; +} + + +/* + * Glue received frame with previous fragments of packet. + * Indicate packet when last frame would be accepted. + */ + +static int +append_frame_to_pkt(struct sbni_softc *sc, u_int framelen, u_int32_t crc) +{ + caddr_t p; + + if (sc->inppos + framelen > ETHER_MAX_LEN) + return (0); + + if (!sc->rx_buf_p && !get_rx_buf(sc)) + return (0); + + p = sc->rx_buf_p->m_data + sc->inppos; + sbni_insb(sc, p, framelen); + if (calc_crc32(crc, p, framelen) != CRC32_REMAINDER) + return (0); + + sc->inppos += framelen - 4; + if (--sc->wait_frameno == 0) { /* last frame received */ + indicate_pkt(sc); + sc->arpcom.ac_if.if_ipackets++; + } + + return (1); +} + + +/* + * Prepare to start output on adapter. Current priority must be set to splimp + * before this routine is called. + * Transmitter will be actually activated when marker has been accepted. + */ + +static void +prepare_to_send(struct sbni_softc *sc) +{ + struct mbuf *m; + u_int len; + + /* sc->tx_buf_p == NULL here! */ + if (sc->tx_buf_p) + printf("sbni: memory leak!\n"); + + sc->outpos = 0; + sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); + + for (;;) { + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, sc->tx_buf_p); + if (!sc->tx_buf_p) { + /* nothing to transmit... */ + sc->pktlen = 0; + sc->tx_frameno = 0; + sc->framelen = 0; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + return; + } + + for (len = 0, m = sc->tx_buf_p; m; m = m->m_next) + len += m->m_len; + + if (len != 0) + break; + m_freem(sc->tx_buf_p); + } + + if (len < SBNI_MIN_LEN) + len = SBNI_MIN_LEN; + + sc->pktlen = len; + sc->tx_frameno = (len + sc->maxframe - 1) / sc->maxframe; + sc->framelen = min(len, sc->maxframe); + + sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | TR_REQ); + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + if (sc->arpcom.ac_if.if_bpf) + bpf_mtap(&sc->arpcom.ac_if, sc->tx_buf_p); +} + + +static void +drop_xmit_queue(struct sbni_softc *sc) +{ + struct mbuf *m; + + if (sc->tx_buf_p) { + m_freem(sc->tx_buf_p); + sc->tx_buf_p = NULL; + sc->arpcom.ac_if.if_oerrors++; + } + + for (;;) { + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + if (m == NULL) + break; + m_freem(m); + sc->arpcom.ac_if.if_oerrors++; + } + + sc->tx_frameno = 0; + sc->framelen = 0; + sc->outpos = 0; + sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; +} + + +static void +send_frame_header(struct sbni_softc *sc, u_int32_t *crc_p) +{ + u_int32_t crc; + u_int len_field; + u_char value; + + crc = *crc_p; + len_field = sc->framelen + 6; /* CRC + frameno + reserved */ + + if (sc->state & FL_NEED_RESEND) + len_field |= FRAME_RETRY; /* non-first attempt... */ + + if (sc->outpos == 0) + len_field |= FRAME_FIRST; + + len_field |= (sc->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD; + sbni_outb(sc, DAT, SBNI_SIG); + + value = (u_char)len_field; + sbni_outb(sc, DAT, value); + crc = CRC32(value, crc); + value = (u_char)(len_field >> 8); + sbni_outb(sc, DAT, value); + crc = CRC32(value, crc); + + sbni_outb(sc, DAT, sc->tx_frameno); + crc = CRC32(sc->tx_frameno, crc); + sbni_outb(sc, DAT, 0); + crc = CRC32(0, crc); + *crc_p = crc; +} + + +/* + * if frame tail not needed (incorrect number or received twice), + * it won't store, but CRC will be calculated + */ + +static int +skip_tail(struct sbni_softc *sc, u_int tail_len, u_int32_t crc) +{ + while (tail_len--) + crc = CRC32(sbni_inb(sc, DAT), crc); + + return (crc == CRC32_REMAINDER); +} + + +static int +check_fhdr(struct sbni_softc *sc, u_int *framelen, u_int *frameno, + u_int *ack, u_int *is_first, u_int32_t *crc_p) +{ + u_int32_t crc; + u_char value; + + crc = *crc_p; + if (sbni_inb(sc, DAT) != SBNI_SIG) + return (0); + + value = sbni_inb(sc, DAT); + *framelen = (u_int)value; + crc = CRC32(value, crc); + value = sbni_inb(sc, DAT); + *framelen |= ((u_int)value) << 8; + crc = CRC32(value, crc); + + *ack = *framelen & FRAME_ACK_MASK; + *is_first = (*framelen & FRAME_FIRST) != 0; + + if ((*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3) + return (0); + + value = sbni_inb(sc, DAT); + *frameno = (u_int)value; + crc = CRC32(value, crc); + + crc = CRC32(sbni_inb(sc, DAT), crc); /* reserved byte */ + *framelen -= 2; + + *crc_p = crc; + return (1); +} + + +static int +get_rx_buf(struct sbni_softc *sc) +{ + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("sbni%d: cannot allocate header mbuf\n", + sc->arpcom.ac_if.if_unit); + return (0); + } + + /* + * We always put the received packet in a single buffer - + * either with just an mbuf header or in a cluster attached + * to the header. The +2 is to compensate for the alignment + * fixup below. + */ + if (ETHER_MAX_LEN + 2 > MHLEN) { + /* Attach an mbuf cluster */ + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return (0); + } + } + m->m_pkthdr.len = m->m_len = ETHER_MAX_LEN + 2; + + /* + * The +2 is to longword align the start of the real packet. + * (sizeof ether_header == 14) + * This is important for NFS. + */ + m_adj(m, 2); + sc->rx_buf_p = m; + return (1); +} + + +static void +indicate_pkt(struct sbni_softc *sc) +{ + struct mbuf *m; + struct ether_header *eh; + + m = sc->rx_buf_p; + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; + m->m_pkthdr.len = m->m_len = sc->inppos; + eh = mtod(m, struct ether_header *); + + /* Remove link layer address and indicate packet */ + m_adj(m, sizeof(struct ether_header)); + ether_input(&sc->arpcom.ac_if, eh, m); + sc->rx_buf_p = NULL; +} + +/* -------------------------------------------------------------------------- */ + +/* + * Routine checks periodically wire activity and regenerates marker if + * connect was inactive for a long time. + */ + +static void +sbni_timeout(void *xsc) +{ + struct sbni_softc *sc; + int s; + u_char csr0; + + sc = (struct sbni_softc *)xsc; + s = splimp(); + + csr0 = sbni_inb(sc, CSR0); + if (csr0 & RC_CHK) { + + if (sc->timer_ticks) { + if (csr0 & (RC_RDY | BU_EMP)) + /* receiving not active */ + sc->timer_ticks--; + } else { + sc->in_stats.timeout_number++; + if (sc->delta_rxl) + timeout_change_level(sc); + + sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES); + csr0 = sbni_inb(sc, CSR0); + } + } + + sbni_outb(sc, CSR0, csr0 | RC_CHK); + sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ); + splx(s); +} + +/* -------------------------------------------------------------------------- */ + +static void +card_start(struct sbni_softc *sc) +{ + sc->timer_ticks = CHANGE_LEVEL_START_TICKS; + sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); + sc->state |= FL_PREV_OK; + + sc->inppos = 0; + sc->wait_frameno = 0; + + sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES); + sbni_outb(sc, CSR0, EN_INT); +} + +/* -------------------------------------------------------------------------- */ + +/* + * Device timeout/watchdog routine. Entered if the device neglects to + * generate an interrupt after a transmit has been started on it. + */ + +static void +sbni_watchdog(struct ifnet *ifp) +{ + log(LOG_ERR, "sbni%d: device timeout\n", ifp->if_unit); + ifp->if_oerrors++; +} + + +static u_char rxl_tab[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, + 0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f +}; + +#define SIZE_OF_TIMEOUT_RXL_TAB 4 +static u_char timeout_rxl_tab[] = { + 0x03, 0x05, 0x08, 0x0b +}; + +static void +set_initial_values(struct sbni_softc *sc, struct sbni_flags flags) +{ + if (flags.fixed_rxl) { + sc->delta_rxl = 0; /* disable receive level autodetection */ + sc->cur_rxl_index = flags.rxl; + } else { + sc->delta_rxl = DEF_RXL_DELTA; + sc->cur_rxl_index = DEF_RXL; + } + + sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE; + sc->csr1.rxl = rxl_tab[sc->cur_rxl_index]; + sc->maxframe = DEFAULT_FRAME_LEN; + + /* + * generate Ethernet address (0x00ff01xxxxxx) + */ + *(u_int16_t*)sc->arpcom.ac_enaddr = htons(0x00ff); + if (flags.mac_addr) + *(u_int32_t*)(sc->arpcom.ac_enaddr+2) = + htonl(flags.mac_addr | 0x01000000); + else { + /* reading timer value */ + outb(0x43, 0); + insb(0x40, sc->arpcom.ac_enaddr + 3, 4); + *(u_char*)(sc->arpcom.ac_enaddr + 2) = 0x01; + } +} + + +#ifdef SBNI_DUAL_COMPOUND + +#ifndef offsetof +#define offsetof(type, member) ((u_int32_t)(&((type *)0)->member)) +#endif + + +struct sbni_softc * +connect_to_master(struct sbni_softc *sc) +{ + struct sbni_softc *p; + + p = (struct sbni_softc *)(((char *)&headlist) + - offsetof(struct sbni_softc, link)); + + for (; p->link; p = p->link) { + if (p->link->irq == sc->irq + && (p->link->base_addr == sc->base_addr + 4 + || p->link->base_addr == sc->base_addr - 4)) { + + struct sbni_softc *t = p->link; + t->slave_sc = sc; + p->link = p->link->link; + return (t); + } + } + + return (NULL); +} + +#endif /* SBNI_DUAL_COMPOUND */ + + +/* Receive level auto-selection */ + +static void +change_level(struct sbni_softc *sc) +{ + if (sc->delta_rxl == 0) /* do not auto-negotiate RxL */ + return; + + if (sc->cur_rxl_index == 0) + sc->delta_rxl = 1; + else if (sc->cur_rxl_index == 15) + sc->delta_rxl = -1; + else if (sc->cur_rxl_rcvd < sc->prev_rxl_rcvd) + sc->delta_rxl = -sc->delta_rxl; + + sc->csr1.rxl = rxl_tab[sc->cur_rxl_index += sc->delta_rxl]; + sbni_inb(sc, CSR0); /* it needed for PCI cards */ + sbni_outb(sc, CSR1, *(u_char *)&sc->csr1); + + sc->prev_rxl_rcvd = sc->cur_rxl_rcvd; + sc->cur_rxl_rcvd = 0; +} + + +static void +timeout_change_level(struct sbni_softc *sc) +{ + sc->cur_rxl_index = timeout_rxl_tab[sc->timeout_rxl]; + if (++sc->timeout_rxl >= 4) + sc->timeout_rxl = 0; + + sc->csr1.rxl = rxl_tab[sc->cur_rxl_index]; + sbni_inb(sc, CSR0); + sbni_outb(sc, CSR1, *(u_char *)&sc->csr1); + + sc->prev_rxl_rcvd = sc->cur_rxl_rcvd; + sc->cur_rxl_rcvd = 0; +} + +/* -------------------------------------------------------------------------- */ + +/* + * Process an ioctl request. This code needs some work - it looks + * pretty ugly. + */ + +static int +sbni_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct sbni_softc *sc; + struct ifreq *ifr; + struct proc *p; + struct sbni_in_stats *in_stats; + struct sbni_flags flags; + int error, s; + + sc = ifp->if_softc; + ifr = (struct ifreq *)data; + p = curproc; + error = 0; + + s = splimp(); + + switch (command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + ether_ioctl(ifp, command, data); + break; + + case SIOCSIFFLAGS: + /* + * If the interface is marked up and stopped, then start it. + * If it is marked down and running, then stop it. + */ + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + sbni_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) { + sbni_stop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + error = 0; + /* if (ifr == NULL) + error = EAFNOSUPPORT; */ + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + + /* + * SBNI specific ioctl + */ + case SIOCGHWFLAGS: /* get flags */ + bcopy((caddr_t) sc->arpcom.ac_enaddr+3, (caddr_t) &flags, 3); + flags.rxl = sc->cur_rxl_index; + flags.rate = sc->csr1.rate; + flags.fixed_rxl = (sc->delta_rxl == 0); + flags.fixed_rate = 1; + ifr->ifr_data = *(caddr_t*) &flags; + break; + + case SIOCGINSTATS: + in_stats = (struct sbni_in_stats *)ifr->ifr_data; + bcopy((void *)(&(sc->in_stats)), (void *)in_stats, + sizeof(struct sbni_in_stats)); + break; + + case SIOCSHWFLAGS: /* set flags */ + /* root only */ + error = suser(p); + if (error) + break; + flags = *(struct sbni_flags*)&ifr->ifr_data; + if (flags.fixed_rxl) { + sc->delta_rxl = 0; + sc->cur_rxl_index = flags.rxl; + } else { + sc->delta_rxl = DEF_RXL_DELTA; + sc->cur_rxl_index = DEF_RXL; + } + sc->csr1.rxl = rxl_tab[sc->cur_rxl_index]; + sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE; + if (flags.mac_addr) + bcopy((caddr_t) &flags, + (caddr_t) sc->arpcom.ac_enaddr+3, 3); + + /* Don't be afraid... */ + sbni_outb(sc, CSR1, *(char*)(&sc->csr1) | PR_RES); + break; + + case SIOCRINSTATS: + if (!(error = suser(p))) /* root only */ + bzero(&sc->in_stats, sizeof(struct sbni_in_stats)); + break; + + default: + error = EINVAL; + } + + splx(s); + return (error); +} + +/* -------------------------------------------------------------------------- */ + +#ifdef ASM_CRC + +static u_int32_t +calc_crc32(u_int32_t crc, caddr_t p, u_int len) +{ + register u_int32_t _crc __asm ("ax"); + _crc = crc; + + __asm __volatile ( + "xorl %%ebx, %%ebx\n" + "movl %1, %%esi\n" + "movl %2, %%ecx\n" + "movl $crc32tab, %%edi\n" + "shrl $2, %%ecx\n" + "jz 1f\n" + + ".align 4\n" + "0:\n" + "movb %%al, %%bl\n" + "movl (%%esi), %%edx\n" + "shrl $8, %%eax\n" + "xorb %%dl, %%bl\n" + "shrl $8, %%edx\n" + "xorl (%%edi,%%ebx,4), %%eax\n" + + "movb %%al, %%bl\n" + "shrl $8, %%eax\n" + "xorb %%dl, %%bl\n" + "shrl $8, %%edx\n" + "xorl (%%edi,%%ebx,4), %%eax\n" + + "movb %%al, %%bl\n" + "shrl $8, %%eax\n" + "xorb %%dl, %%bl\n" + "movb %%dh, %%dl\n" + "xorl (%%edi,%%ebx,4), %%eax\n" + + "movb %%al, %%bl\n" + "shrl $8, %%eax\n" + "xorb %%dl, %%bl\n" + "addl $4, %%esi\n" + "xorl (%%edi,%%ebx,4), %%eax\n" + + "decl %%ecx\n" + "jnz 0b\n" + + "1:\n" + "movl %2, %%ecx\n" + "andl $3, %%ecx\n" + "jz 2f\n" + + "movb %%al, %%bl\n" + "shrl $8, %%eax\n" + "xorb (%%esi), %%bl\n" + "xorl (%%edi,%%ebx,4), %%eax\n" + + "decl %%ecx\n" + "jz 2f\n" + + "movb %%al, %%bl\n" + "shrl $8, %%eax\n" + "xorb 1(%%esi), %%bl\n" + "xorl (%%edi,%%ebx,4), %%eax\n" + + "decl %%ecx\n" + "jz 2f\n" + + "movb %%al, %%bl\n" + "shrl $8, %%eax\n" + "xorb 2(%%esi), %%bl\n" + "xorl (%%edi,%%ebx,4), %%eax\n" + "2:\n" + : + : "a" (_crc), "g" (p), "g" (len) + : "ax", "bx", "cx", "dx", "si", "di" + ); + + return (_crc); +} + +#else /* ASM_CRC */ + +static u_int32_t +calc_crc32(u_int32_t crc, caddr_t p, u_int len) +{ + while (len--) + crc = CRC32(*p++, crc); + + return (crc); +} + +#endif /* ASM_CRC */ + + +static u_int32_t crc32tab[] __attribute__ ((aligned(8))) = { + 0xD202EF8D, 0xA505DF1B, 0x3C0C8EA1, 0x4B0BBE37, + 0xD56F2B94, 0xA2681B02, 0x3B614AB8, 0x4C667A2E, + 0xDCD967BF, 0xABDE5729, 0x32D70693, 0x45D03605, + 0xDBB4A3A6, 0xACB39330, 0x35BAC28A, 0x42BDF21C, + 0xCFB5FFE9, 0xB8B2CF7F, 0x21BB9EC5, 0x56BCAE53, + 0xC8D83BF0, 0xBFDF0B66, 0x26D65ADC, 0x51D16A4A, + 0xC16E77DB, 0xB669474D, 0x2F6016F7, 0x58672661, + 0xC603B3C2, 0xB1048354, 0x280DD2EE, 0x5F0AE278, + 0xE96CCF45, 0x9E6BFFD3, 0x0762AE69, 0x70659EFF, + 0xEE010B5C, 0x99063BCA, 0x000F6A70, 0x77085AE6, + 0xE7B74777, 0x90B077E1, 0x09B9265B, 0x7EBE16CD, + 0xE0DA836E, 0x97DDB3F8, 0x0ED4E242, 0x79D3D2D4, + 0xF4DBDF21, 0x83DCEFB7, 0x1AD5BE0D, 0x6DD28E9B, + 0xF3B61B38, 0x84B12BAE, 0x1DB87A14, 0x6ABF4A82, + 0xFA005713, 0x8D076785, 0x140E363F, 0x630906A9, + 0xFD6D930A, 0x8A6AA39C, 0x1363F226, 0x6464C2B0, + 0xA4DEAE1D, 0xD3D99E8B, 0x4AD0CF31, 0x3DD7FFA7, + 0xA3B36A04, 0xD4B45A92, 0x4DBD0B28, 0x3ABA3BBE, + 0xAA05262F, 0xDD0216B9, 0x440B4703, 0x330C7795, + 0xAD68E236, 0xDA6FD2A0, 0x4366831A, 0x3461B38C, + 0xB969BE79, 0xCE6E8EEF, 0x5767DF55, 0x2060EFC3, + 0xBE047A60, 0xC9034AF6, 0x500A1B4C, 0x270D2BDA, + 0xB7B2364B, 0xC0B506DD, 0x59BC5767, 0x2EBB67F1, + 0xB0DFF252, 0xC7D8C2C4, 0x5ED1937E, 0x29D6A3E8, + 0x9FB08ED5, 0xE8B7BE43, 0x71BEEFF9, 0x06B9DF6F, + 0x98DD4ACC, 0xEFDA7A5A, 0x76D32BE0, 0x01D41B76, + 0x916B06E7, 0xE66C3671, 0x7F6567CB, 0x0862575D, + 0x9606C2FE, 0xE101F268, 0x7808A3D2, 0x0F0F9344, + 0x82079EB1, 0xF500AE27, 0x6C09FF9D, 0x1B0ECF0B, + 0x856A5AA8, 0xF26D6A3E, 0x6B643B84, 0x1C630B12, + 0x8CDC1683, 0xFBDB2615, 0x62D277AF, 0x15D54739, + 0x8BB1D29A, 0xFCB6E20C, 0x65BFB3B6, 0x12B88320, + 0x3FBA6CAD, 0x48BD5C3B, 0xD1B40D81, 0xA6B33D17, + 0x38D7A8B4, 0x4FD09822, 0xD6D9C998, 0xA1DEF90E, + 0x3161E49F, 0x4666D409, 0xDF6F85B3, 0xA868B525, + 0x360C2086, 0x410B1010, 0xD80241AA, 0xAF05713C, + 0x220D7CC9, 0x550A4C5F, 0xCC031DE5, 0xBB042D73, + 0x2560B8D0, 0x52678846, 0xCB6ED9FC, 0xBC69E96A, + 0x2CD6F4FB, 0x5BD1C46D, 0xC2D895D7, 0xB5DFA541, + 0x2BBB30E2, 0x5CBC0074, 0xC5B551CE, 0xB2B26158, + 0x04D44C65, 0x73D37CF3, 0xEADA2D49, 0x9DDD1DDF, + 0x03B9887C, 0x74BEB8EA, 0xEDB7E950, 0x9AB0D9C6, + 0x0A0FC457, 0x7D08F4C1, 0xE401A57B, 0x930695ED, + 0x0D62004E, 0x7A6530D8, 0xE36C6162, 0x946B51F4, + 0x19635C01, 0x6E646C97, 0xF76D3D2D, 0x806A0DBB, + 0x1E0E9818, 0x6909A88E, 0xF000F934, 0x8707C9A2, + 0x17B8D433, 0x60BFE4A5, 0xF9B6B51F, 0x8EB18589, + 0x10D5102A, 0x67D220BC, 0xFEDB7106, 0x89DC4190, + 0x49662D3D, 0x3E611DAB, 0xA7684C11, 0xD06F7C87, + 0x4E0BE924, 0x390CD9B2, 0xA0058808, 0xD702B89E, + 0x47BDA50F, 0x30BA9599, 0xA9B3C423, 0xDEB4F4B5, + 0x40D06116, 0x37D75180, 0xAEDE003A, 0xD9D930AC, + 0x54D13D59, 0x23D60DCF, 0xBADF5C75, 0xCDD86CE3, + 0x53BCF940, 0x24BBC9D6, 0xBDB2986C, 0xCAB5A8FA, + 0x5A0AB56B, 0x2D0D85FD, 0xB404D447, 0xC303E4D1, + 0x5D677172, 0x2A6041E4, 0xB369105E, 0xC46E20C8, + 0x72080DF5, 0x050F3D63, 0x9C066CD9, 0xEB015C4F, + 0x7565C9EC, 0x0262F97A, 0x9B6BA8C0, 0xEC6C9856, + 0x7CD385C7, 0x0BD4B551, 0x92DDE4EB, 0xE5DAD47D, + 0x7BBE41DE, 0x0CB97148, 0x95B020F2, 0xE2B71064, + 0x6FBF1D91, 0x18B82D07, 0x81B17CBD, 0xF6B64C2B, + 0x68D2D988, 0x1FD5E91E, 0x86DCB8A4, 0xF1DB8832, + 0x616495A3, 0x1663A535, 0x8F6AF48F, 0xF86DC419, + 0x660951BA, 0x110E612C, 0x88073096, 0xFF000000 +}; diff --git a/sys/dev/sbni/if_sbni_isa.c b/sys/dev/sbni/if_sbni_isa.c new file mode 100644 index 0000000..a4aefbc --- /dev/null +++ b/sys/dev/sbni/if_sbni_isa.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved. + * Author: Denis I.Timofeev + * + * Redistributon and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +static int sbni_probe_isa(device_t); +static int sbni_attach_isa(device_t); + +static device_method_t sbni_isa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbni_probe_isa), + DEVMETHOD(device_attach, sbni_attach_isa), + { 0, 0 } +}; + +static driver_t sbni_isa_driver = { + "sbni", + sbni_isa_methods, + sizeof(struct sbni_softc) +}; + +static devclass_t sbni_isa_devclass; +static struct isa_pnp_id sbni_ids[] = { + { 0, NULL } /* we have no pnp sbni cards atm. */ +}; + +DRIVER_MODULE(sbni, isa, sbni_isa_driver, sbni_isa_devclass, 0, 0); + + +static int +sbni_probe_isa(device_t dev) +{ + struct sbni_softc *sc; + int error; + + error = ISA_PNP_PROBE(device_get_parent(dev), dev, sbni_ids); + if (error && error != ENOENT) + return (error); + + sc = device_get_softc(dev); + bzero(sc, sizeof(struct sbni_softc)); + + sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->io_rid, + 0ul, ~0ul, SBNI_PORTS, RF_ACTIVE); + if (!sc->io_res) { + printf("sbni: cannot allocate io ports!\n"); + return (ENOENT); + } + + sc->base_addr = rman_get_start(sc->io_res); + if (sbni_probe(sc) != 0) { + bus_release_resource(dev, SYS_RES_IOPORT, + sc->io_rid, sc->io_res); + return (ENXIO); + } + + device_quiet(dev); + return (0); +} + + +static int +sbni_attach_isa(device_t dev) +{ + struct sbni_softc *sc; + struct sbni_flags flags; + int error; + + sc = device_get_softc(dev); + + printf("sbni%d: port 0x%x", + next_sbni_unit, sc->base_addr); + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, + 0ul, ~0ul, 1, RF_ACTIVE); + + if (sc->irq_res) { + printf(" irq %ld\n", rman_get_start(sc->irq_res)); + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, + sbni_intr, sc, &sc->irq_handle); + if (error) { + printf("sbni%d: bus_setup_intr\n", next_sbni_unit); + bus_release_resource(dev, SYS_RES_IOPORT, + sc->io_rid, sc->io_res); + return (error); + } + +#ifndef SBNI_DUAL_COMPOUND + + } else { + printf("\nsbni%d: irq conflict!\n", next_sbni_unit); + bus_release_resource(dev, SYS_RES_IOPORT, + sc->io_rid, sc->io_res); + return (ENOENT); + } + +#else /* SBNI_DUAL_COMPOUND */ + + sc->link = headlist; + headlist = sc; + } else { + struct sbni_softc *master; + + if ((master = connect_to_master(sc)) == 0) { + printf("\nsbni%d: failed to alloc irq\n", + next_sbni_unit); + bus_release_resource(dev, SYS_RES_IOPORT, + sc->io_rid, sc->io_res); + return (ENXIO); + } else + printf(" shared irq with sbni%d\n", + master->arpcom.ac_if.if_unit); + } +#endif /* SBNI_DUAL_COMPOUND */ + + *(u_int32_t*)&flags = device_get_flags(dev); + + sbni_attach(sc, next_sbni_unit++, flags); + return (0); +} diff --git a/sys/dev/sbni/if_sbni_pci.c b/sys/dev/sbni/if_sbni_pci.c new file mode 100644 index 0000000..658e943 --- /dev/null +++ b/sys/dev/sbni/if_sbni_pci.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved. + * Author: Denis I.Timofeev + * + * Redistributon and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +static int sbni_pci_probe(device_t); +static int sbni_pci_attach(device_t); + +static device_method_t sbni_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbni_pci_probe), + DEVMETHOD(device_attach, sbni_pci_attach), + { 0, 0 } +}; + +static driver_t sbni_pci_driver = { + "sbni", + sbni_pci_methods, + sizeof(struct sbni_softc) +}; + +static devclass_t sbni_pci_devclass; + +DRIVER_MODULE(sbni, pci, sbni_pci_driver, sbni_pci_devclass, 0, 0); + + +static int +sbni_pci_probe(device_t dev) +{ + struct sbni_softc *sc; + u_int32_t ports; + + ports = SBNI_PORTS; + if (pci_get_vendor(dev) != SBNI_PCI_VENDOR + || pci_get_device(dev) != SBNI_PCI_DEVICE) + return (ENXIO); + + sc = device_get_softc(dev); + bzero(sc, sizeof(struct sbni_softc)); + if (pci_get_subdevice(dev) == 2) { + ports <<= 1; + sc->slave_sc = malloc(sizeof(struct sbni_softc), + M_DEVBUF, M_NOWAIT); + if (!sc->slave_sc) + return (ENOMEM); + bzero(sc->slave_sc, sizeof(struct sbni_softc)); + device_set_desc(dev, "Granch SBNI12/PCI Dual adapter"); + } else + device_set_desc(dev, "Granch SBNI12/PCI adapter"); + + sc->io_rid = PCIR_MAPS; + sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->io_rid, + 0ul, ~0ul, ports, RF_ACTIVE); + if (!sc->io_res) { + printf("sbni: cannot allocate io ports!\n"); + if (sc->slave_sc) + free(sc->slave_sc, M_DEVBUF); + return (ENOENT); + } + + sc->base_addr = rman_get_start(sc->io_res); + if (sc->slave_sc) + sc->slave_sc->base_addr = sc->base_addr + 4; + if (sbni_probe(sc) != 0) { + bus_release_resource(dev, SYS_RES_IOPORT, + sc->io_rid, sc->io_res); + if (sc->slave_sc) + free(sc->slave_sc, M_DEVBUF); + return (ENXIO); + } + + device_quiet(dev); + return (0); +} + +static int +sbni_pci_attach(device_t dev) +{ + struct sbni_softc *sc; + struct sbni_flags flags; + int error; + + sc = device_get_softc(dev); + + printf("sbni%d: port 0x%x", + next_sbni_unit, sc->slave_sc ? " Dual " : " ", sc->base_addr); + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, + 0ul, ~0ul, 1, RF_SHAREABLE); + + if (sc->irq_res) { + printf(" irq %ld\n", rman_get_start(sc->irq_res)); + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, + sbni_intr, sc, &sc->irq_handle); + if (error) { + printf("sbni%d: bus_setup_intr\n", next_sbni_unit); + goto attach_failed; + } + } else { + printf("\nsbni%d: cannot claim irq!\n", next_sbni_unit); + error = ENOENT; + goto attach_failed; + } + + *(u_int32_t*)&flags = 0; + + sbni_attach(sc, next_sbni_unit++, flags); + if (sc->slave_sc) + sbni_attach(sc->slave_sc, next_sbni_unit++, flags); + return (0); + +attach_failed: + bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid, sc->io_res); + if (sc->slave_sc) + free(sc->slave_sc, M_DEVBUF); + return (error); +} diff --git a/sys/dev/sbni/if_sbnireg.h b/sys/dev/sbni/if_sbnireg.h new file mode 100644 index 0000000..70fca44 --- /dev/null +++ b/sys/dev/sbni/if_sbnireg.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved. + * Author: Denis I.Timofeev + * + * Redistributon and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * We don't have registered vendor id yet... + */ +#define SBNI_PCI_VENDOR 0x55 +#define SBNI_PCI_DEVICE 0x9f + +#define ISA_MODE 0x00 +#define PCI_MODE 0x01 + +#define SBNI_PORTS 4 + +enum sbni_reg { + CSR0 = 0, + CSR1 = 1, + DAT = 2 +}; + +/* CSR0 mapping */ +enum { + BU_EMP = 0x02, + RC_CHK = 0x04, + CT_ZER = 0x08, + TR_REQ = 0x10, + TR_RDY = 0x20, + EN_INT = 0x40, + RC_RDY = 0x80 +}; + + +/* CSR1 mapping */ +#define PR_RES 0x80 + +struct sbni_csr1 { + unsigned rxl : 5; + unsigned rate : 2; + unsigned : 1; +}; + + + +#define FRAME_ACK_MASK (u_int16_t)0x7000 +#define FRAME_LEN_MASK (u_int16_t)0x03FF +#define FRAME_FIRST (u_int16_t)0x8000 +#define FRAME_RETRY (u_int16_t)0x0800 + +#define FRAME_SENT_BAD (u_int16_t)0x4000 +#define FRAME_SENT_OK (u_int16_t)0x3000 + + +enum { + FL_WAIT_ACK = 1, + FL_NEED_RESEND = 2, + FL_PREV_OK = 4, + FL_SLOW_MODE = 8 +}; + + +enum { + DEFAULT_IOBASEADDR = 0x210, + DEFAULT_INTERRUPTNUMBER = 5, + DEFAULT_RATE = 0, + DEFAULT_FRAME_LEN = 1012 +}; + +#define DEF_RXL_DELTA -1 +#define DEF_RXL 0xf + +#define SBNI_SIG 0x5a + +#define SBNI_MIN_LEN (ETHER_MIN_LEN - 4) +#define SBNI_MAX_FRAME 1023 + +#define SBNI_HZ 18 /* ticks to wait for pong or packet */ + /* sbni watchdog called SBNI_HZ times per sec. */ + +#define TR_ERROR_COUNT 32 +#define CHANGE_LEVEL_START_TICKS 4 diff --git a/sys/dev/sbni/if_sbnivar.h b/sys/dev/sbni/if_sbnivar.h new file mode 100644 index 0000000..4453251 --- /dev/null +++ b/sys/dev/sbni/if_sbnivar.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved. + * Author: Denis I.Timofeev + * + * Redistributon and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * SBNI12 definitions + */ + +/* + * CONFIGURATION PARAMETER: + * + * Uncomment this if you want to use model SBNI12D-11/ISA with same IRQ + * for both first and second channels. + */ +#define SBNI_DUAL_COMPOUND 1 + +#define SBNI_DEBUG 0 + +#if SBNI_DEBUG +#define DP(A) A +#else +#define DP(A) +#endif + +struct sbni_in_stats { + u_int32_t all_rx_number; + u_int32_t bad_rx_number; + u_int32_t timeout_number; + u_int32_t all_tx_number; + u_int32_t resend_tx_number; +}; + +struct sbni_flags { + u_int mac_addr : 24; + u_int rxl : 4; + u_int rate : 2; + u_int fixed_rxl : 1; + u_int fixed_rate : 1; +}; + + +#ifdef _KERNEL /* to avoid compile this decls with sbniconfig */ + +struct sbni_softc { + struct arpcom arpcom; /* ethernet common */ + + int base_addr; + int irq; + int io_rid; + int irq_rid; + struct resource *io_res; + struct resource *irq_res; + void *irq_handle; + + struct mbuf *rx_buf_p; /* receive buffer ptr */ + struct mbuf *tx_buf_p; /* transmit buffer ptr */ + + u_int pktlen; /* length of transmitting pkt */ + u_int framelen; /* current frame length */ + u_int maxframe; /* maximum valid frame length */ + u_int state; + u_int inppos; /* positions in rx/tx buffers */ + u_int outpos; /* positions in rx/tx buffers */ + + /* transmitting frame number - from frames qty to 1 */ + u_int tx_frameno; + + /* expected number of next receiving frame */ + u_int wait_frameno; + + /* count of failed attempts to frame send - 32 attempts do before + error - while receiver tunes on opposite side of wire */ + u_int trans_errors; + + /* idle time; send pong when limit exceeded */ + u_int timer_ticks; + + /* fields used for receive level autoselection */ + int delta_rxl; + u_int cur_rxl_index; + u_int timeout_rxl; + u_int32_t cur_rxl_rcvd; + u_int32_t prev_rxl_rcvd; + + struct sbni_csr1 csr1; /* current value of CSR1 */ + struct sbni_in_stats in_stats; /* internal statistics */ + + struct callout_handle wch; + + struct sbni_softc *slave_sc; + +#ifdef SBNI_DUAL_COMPOUND + struct sbni_softc *link; +#endif +}; + +void sbni_intr(void *); +int sbni_probe(struct sbni_softc *); +void sbni_attach(struct sbni_softc *, int, struct sbni_flags); + +extern u_int32_t next_sbni_unit; + +#ifdef SBNI_DUAL_COMPOUND +extern struct sbni_softc *headlist; + +struct sbni_softc *connect_to_master(struct sbni_softc *); +#endif +#endif /* _KERNEL */ + +/* + * SBNI socket ioctl params + */ +#define SIOCGHWFLAGS _IOWR('i', 62, struct ifreq) /* get flags */ +#define SIOCSHWFLAGS _IOWR('i', 61, struct ifreq) /* set flags */ +#define SIOCGINSTATS _IOWR('i', 60, struct ifreq) /* get internal stats */ +#define SIOCRINSTATS _IOWR('i', 63, struct ifreq) /* reset internal stats */ + + +/* + * CRC-32 stuff + */ +#define CRC32(c,crc) (crc32tab[((size_t)(crc) ^ (c)) & 0xff] ^ (((crc) >> 8) & 0x00ffffff)) + /* CRC generator EDB88320 */ + /* CRC remainder 2144DF1C */ + /* CRC initial value 0 */ +#define CRC32_REMAINDER 0x2144df1c +#define CRC32_INITIAL 0x00000000 -- cgit v1.1