home *** CD-ROM | disk | FTP | other *** search
- /*
- File: loopback.c
-
- Contains: * Simple loopback driver utilizing Mentat DLPI Template Code (dlpiether.c)
- * This file, combined with dlpiether.c (or linked against the Shared LIbrary
- * containing dlpiether.c) is a functioning loopback driver for Open Transport.
- * This file also serves as a template for writing the hardware-specific
- * code for a driver which will use dlpiether.c. Comments in the code and
- * the accompanying documentation, Mentat DLPI Driver Template, provide
- * necessary information.
-
- ** loopback.c 1.2 Last Changed 29 Mar 1996
-
- Copyright: © 1996 by Mentat Inc.
-
- To Do:
- */
-
- static char sccs_loop_id[] = "@(#)loopback.c 1.2";
-
- /*
- * "BoardX:" in comments refers to a typical hardware driver. These are hints
- * and suggestions for converting this code to an actual hardware driver.
- */
- #include <OpenTptModule.h>
- #include <dlpi.h>
- #include "NewDevices.h"
- #include <NameRegistry.h>
- #include <OpenTptLinks.h>
- #include <OpenTptPCISupport.h>
- #include <dlpiuser.h>
- #include <dlpiether.h>
-
- /*
- * The following defines are inserted here for convenience for the
- * loopback driver. A "real" driver may place these in separate header
- * files and/or use system-defined values.
- */
- #define LOOPBACK_DRIVER /* conditionally compiles loopback code */
-
- /* Some useful MacBug debugging macros */
-
- #define mps_printf OTKernelPrintfForMentat
- #define loop_error(a) mps_printf a
- #define loop_trace(a) mps_printf a
- #define loop_debug(a) if(loop_debug) {mps_printf a;} else {;}
- #define loop_debug2(a) if(loop_debug >= 2) {mps_printf a;} else {;}
- #define trace_args " "
- #define trace0 "0 " /* Fatal */
- #define trace1 "1 " /* General */
- #define trace2 "2 " /* Out of Memory */
- #define trace3 "3 " /* Event */
- #define trace_eol
- extern int OTKernelPrintfForMentat(char * fmt, ...);
- static int loop_debug = 1;
-
- #define MAX_PACKET_SIZE 1500 /* We're ethernet, remember :-) */
- #define MIN_PACKET_SIZE 60 /* We'll pad short packets Ethernet style */
- #define LOOP_HIWAT 2048 /* Flow control high water mark */
- #define LOOP_LOWAT 64 /* Flow control low water mark */
-
- /* just so DL_INFO_ACK has something to report */
- static unsigned char my_hardware_addr[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
-
- /*
- * See OpnTptLinks.h for more
- */
- #define kLoopbackModuleID 7199 /* STREAMS module_info mi_idnum */
- #define kLoopbackName "loopback" /* mi_idname */
-
-
- /* Driver information 'per board', e.g., internal control structure. */
- typedef struct board_s {
- dle_t board_dle; /* Must be first in structure. */
- /* BoardX: Fields needed for controlling hardware go here */
- } board_t;
-
- /*
- * STREAMS instance data (q_ptr)
- */
- typedef struct loop_s {
- dcl_t loop_dcl; /* Must be first in structure */
- /* BoardX: Any 'per stream' fields needed by board go here */
- } loop_t;
- #define loop_dle loop_dcl.dcl_hw /* pointer to board_t structure */
-
- static void loop_bcopy_to_noncached(unsigned char * src, unsigned char * dst
- , unsigned int count);
- static int loop_close(queue_t * q, int flag, cred_t * credp);
- static void loop_hw_address_filter_reset(void * loopvp, dle_addr_t * addr_list
- , ulong addr_count, ulong promisc_count
- , ulong multi_promisc_count, ulong accept_broadcast
- , ulong accept_error);
- static void loop_hw_start(void * loopvp);
- static void loop_hw_stop(void * loopvp);
- static int loop_open(queue_t * q, dev_t * devp, int flag, int sflag
- , cred_t * credp);
- static mblk_t * loop_reallocb(mblk_t * mp, int new_size);
- static int loop_rsrv(queue_t * q);
- static int loop_wput(queue_t * q, mblk_t * mp);
- static mblk_t * loop_wput_process(queue_t * q, mblk_t * mp, long * total);
-
- /*
- * Global pointer to the board_t structure for this device instantiation. This
- * structure is allocated in the InitStreamModule routine and freed in
- * TerminateStreamModule. CFM guarantees us a separate data space for all
- * instances of the device, so this pointer is always unique.
- */
- static board_t * board_global;
-
- /* STREAMS configuration structures. */
- static struct module_info info = {
- kLoopbackModuleID, kLoopbackName
- , MIN_PACKET_SIZE, MAX_PACKET_SIZE
- , LOOP_HIWAT, LOOP_LOWAT
- };
-
- /*
- * STREAMS qinit structures. Drivers have no rput.
- */
- static struct qinit rinit = {
- NULL, loop_rsrv, loop_open, loop_close, NULL, &info
- };
-
- static struct qinit winit = {
- loop_wput, NULL, loop_open, loop_close, NULL, &info
- };
-
- struct streamtab loopback_info = { &rinit, &winit };
-
- /*
- * BoardX: Hardware drivers would typically add 'kOTModUsesInterrupts' to
- * flags entry of following.
- */
- static install_info loopback_install_info =
- {
- &loopback_info,
- kOTModIsDriver | kOTModUpperIsDLPI,
- SQLVL_MODULE,
- NULL,
- 0,
- 0
- };
-
- #ifndef LOOPBACK_DRIVER
- static void
- board_bcopy_to_noncached (unsigned char * src, unsigned char * dst, unsigned int count)
- {
- unsigned int iterations;
-
- /* Do short copies with a simple loop. */
- if (count <= 15) {
- do {
- --count;
- *dst++ = *src++;
- } while (count);
- return;
- }
-
- /* Align on 8 byte dst boundary */
- iterations = (8 - ((unsigned int)dst & 0x7)) & 0x7;
- count -= iterations;
- if (iterations) {
- do {
- --iterations;
- *dst++ = *src++;
- } while (iterations);
- }
-
- /* Copy 32 byte chunks */
- iterations = count >> 5;
- count &= 0x1F;
- if (iterations) {
- double * dsrc = (double *)src;
- double * ddst = (double *)dst;
- do {
- iterations--;
- ddst[0] = dsrc[0];
- ddst[1] = dsrc[1];
- ddst[2] = dsrc[2];
- ddst[3] = dsrc[3];
- ddst += 4;
- dsrc += 4;
- } while (iterations);
- src = (unsigned char *)dsrc;
- dst = (unsigned char *)ddst;
- }
-
- /* Copy 4 byte chunks */
- iterations = count >> 2;
- count &= 0x3;
- if (iterations) {
- unsigned long * lsrc = (unsigned long *)src;
- unsigned long * ldst = (unsigned long *)dst;
- do {
- --iterations;
- *ldst++ = *lsrc++;
- } while (iterations);
- src = (unsigned char *)lsrc;
- dst = (unsigned char *)ldst;
- }
-
- /* Copy the rest */
- while (count--)
- *dst++ = *src++;
- }
-
- /*
- * Template interrupt routine for processing inbound packets.
- */
- long board_intr ()
- {
- board_t * board = board_global;
- void * cookie;
- dle_t * dle = &board->board_dle;
- mblk_t * mp;
-
- OTEnterInterrupt();
-
- /* Handle transmit done interrupts, if necessary. */
-
- /* Handle receive interrupts. */
- while (receive_packets_available) {
- /* Perhaps use esballoc() to avoid data copy? */
- mp = allocb(packet_length, BPRI_LO);
- if (!mp) {
- dle->dle_istatus.receive_discards++;
- continue;
- }
-
- /* Copy the data into mp->b_rptr. */
-
- mp->b_wptr = mp->b_rptr + packet_length;
-
- /* Deliver the packet. */
- dle_inbound(dle, mp);
- }
-
- OTLeaveInterrupt();
- return 1;
- }
- #endif /* LOOPBACK_DRIVER */
-
- /* This function must be exported; see loop.exp */
- install_info *
- GetOTInstallInfo ()
- {
- loop_debug((trace_args "GetOTInstallInfo" trace_eol));
- return &loopback_install_info;
- }
-
- Boolean
- InitStreamModule (void * systemDependent)
- {
- board_t * board;
- dle_t * dle;
-
- loop_debug((trace_args "InitStreamModule" trace_eol));
-
- /* Allocate the board_t structure for this device */
- board = (board_t *)OTAllocMem(sizeof(board_t));
- if (!board)
- return false;
-
- /* These are the hardware start/stop/reset functions */
- bzero((char *)board, sizeof(board_t));
- dle = &board->board_dle;
- dle->dle_hw.dlehw_start = loop_hw_start;
- dle->dle_hw.dlehw_stop = loop_hw_stop;
- dle->dle_hw.dlehw_address_filter_reset = loop_hw_address_filter_reset;
- dle->dle_hw.dlehw_send_error = NULL;
- dle->dle_hw.dlehw_recv_error_flags = 0;
-
- /*
- * Suggestions:
- * - Install interrupt vectors in case any memory allocations are
- * required. Interrupts should not be enabled until the
- * board start routine is called.
- * - Reset the hardware to a known state. If the hardware does
- * not respond, then return false.
- * - Read the Ethernet address from the board's ROM area. This
- * address should be copied into both the dle_factory_addr
- * field and the dle_current_addr field.
- */
- bcopy(my_hardware_addr, dle->dle_factory_addr, 6);
- bcopy(my_hardware_addr, dle->dle_current_addr, 6);
-
- /*
- * Allow the DLPI common code to initialize the dle fields. Once
- * dle_init is called, dle_terminate must be called before freeing
- * the dle structure. There is private memory allocated for each
- * dle that needs to be freed.
- */
- dle_init(dle, 0);
- board_global = board;
-
- return true;
- }
-
- void
- TerminateStreamModule (void)
- {
- board_t * board = board_global;
-
- loop_debug((trace_args "TerminateStreamModule" trace_eol));
-
- /*
- * Suggestions:
- * - Remove interrupt vectors.
- * - Reset the hardware to a known state.
- */
-
- board_global = (board_t *)NULL;
- dle_terminate(&board->board_dle);
- OTFreeMem(board);
- }
-
- OTResult
- ValidateHardware (RegEntryID * our_id)
- {
- loop_debug((trace_args "ValidateHardware" trace_eol));
- return kOTNoError;
- }
-
- /* STREAMS close routine. */
- static int
- loop_close (queue_t * q, int flag, cred_t * credp)
- {
- loop_debug((trace_args "loop_close(%x), dcl_dle %x" trace_eol
- , q, ((dcl_t *)q->q_ptr)->dcl_hw ));
- /*
- * NOTE: there is probably not much else that needs to be done
- * in this routine. If you need to know about the number of
- * open instances, dle_refcount is the number of streams still
- * referencing the device (decremented in dle_close).
- */
- return dle_close(q);
- }
-
- static void
- loop_hw_address_filter_reset (void * boardvp, dle_addr_t * addr_list
- , ulong addr_count, ulong promisc_count
- , ulong multi_promisc_count, ulong accept_broadcast
- , ulong accept_error)
- {
- board_t * board = boardvp;
- dle_t * dle = &board->board_dle;
- dle_addr_t * dlea;
- unsigned char * first_phys_addr = 0;
- uint phys_addr_count = 0;
-
- /* Calculate the new logical address filter for multicast addresses. */
- for (dlea = addr_list; dlea; dlea = dlea->dlea_next) {
- if (dlea->dlea_addr[0] & 0x1) {
- /*
- * If the address is a multicast address, then set
- * the right bits in the new filter.
- */
- } else {
- /*
- * Additional physical address. This does not happen
- * with the first version of the DLPI template code.
- * However, at some future time, we may want to
- * support multiple physical addresses on one
- * hardware tap.
- */
- if (!first_phys_addr)
- first_phys_addr = dlea->dlea_addr;
- phys_addr_count++;
- }
- }
-
- if (first_phys_addr) {
- /*
- * If there were any physical, non-multicast addresses in
- * the list, then check to see if the first one is different
- * from the current one. If so, copy the new address into
- * dle_current_addr and take whatever steps are necessary
- * with the hardware to change the physical address.
- */
- ;
- }
-
- /*
- * Compare the new address filter with the old one. If the new
- * one is different, then set the hardware appropriately.
- */
-
- /*
- * If there are multiple physical addresses, then the board
- * probably needs to be put in a promiscuous state.
- */
- if (phys_addr_count > 1)
- promisc_count |= 1;
-
- if (promisc_count || multi_promisc_count)
- /* Set the board into promiscuous mode. */;
- else
- /* Clear any promiscuous mode that may be set*/;
- /*
- * Save the promiscuous setting in the board structure so that
- * updates to the hardware registers from loop_start or other
- * routines will be correct.
- */
- }
-
- /* Called by dlpiether code through dlehw_start. */
- static void
- loop_hw_start (void * boardvp)
- {
- board_t * board = boardvp;
-
- /* Kick the board alive and allow receive interrupts. */
- }
-
- /* Called by dlpiether code through dlehw_stop. */
- static void
- loop_hw_stop (void * boardvp)
- {
- /* Turn off interrupts and leave the hardware disabled. */
- }
-
-
- /* STREAMS open routine. */
- static int
- loop_open (queue_t * q, dev_t * devp, int flag, int sflag, cred_t * credp)
- {
- int ret_code;
-
- loop_debug((trace_args "loop_open()" trace_eol));
- /*
- * This routine probably does not need to do anything other
- * than call dle_open. dle_refcnt is incremented.
- */
- ret_code = dle_open(&board_global->board_dle, q, devp, flag, sflag, credp
- , sizeof(loop_t));
- loop_debug((trace_args "dle_open(%x) = %d, dcl_hw %x" trace_eol
- , &board_global->board_dle
- , ret_code
- , ((dcl_t *)q->q_ptr)->dcl_hw ));
- return ret_code;
- }
-
- /*
- * Function used only by loopback driver; allocate or re-allocate
- * a message block to assure minimum size.
- */
- #ifdef LOOPBACK_DRIVER
- static mblk_t *
- loop_reallocb (mblk_t * mp, int new_size)
- {
- mblk_t * mp1;
- dblk_t * db;
- int our_size;
- int need;
- unsigned char * base;
-
- our_size = mp->b_wptr - mp->b_rptr;
- need = (new_size > our_size) ? new_size : our_size;
- db = mp->b_datap;
- base = db->db_base;
-
- if (db->db_ref == 1) {
- if ((db->db_lim - mp->b_rptr) >= need)
- return mp;
- if ((db->db_lim - base) >= need) {
- BlockMoveData((char *)mp->b_rptr, (char *)base, our_size);
- mp->b_rptr = base;
- mp->b_wptr = base + our_size;
- return mp;
- }
- }
- mp1 = allocb(need, BPRI_MED);
- if (mp1) {
- mp1->b_wptr = mp1->b_rptr + our_size;
- mp1->b_datap->db_type = db->db_type;
- mp1->b_cont = mp->b_cont;
- bcopy((char *)mp->b_rptr, (char *)mp1->b_rptr, our_size );
- }
- freeb(mp);
- return mp1;
- }
- #endif /* LOOPBACK_DRIVER */
-
- /*
- * Read-side service routine.
- * Pass all inbound packets upstream. Messages are placed on
- * the read-side queue by dle_inbound. Unless the hardware
- * requires something special, no additional code should be
- * required. NOTE: Additional messages may be added to the
- * queue while this routine is running. If canputnext() fails,
- * messages are freed rather than put back on the queue.
- * This is necessary since dle_inbound() will continue to add
- * messages without checking flow control (it can't call canputnext()
- * from interrupt context).
- */
- static int
- loop_rsrv (q)
- queue_t * q;
- {
- mblk_t * mp;
-
- while (mp = getq(q)) {
- /*
- * Private message to be passed back to the dlpiether
- * code. This interface is required for supporting
- * 802.2 XID and Test packets.
- */
- if (mp->b_datap->db_type == M_CTL) {
- dle_rsrv_ctl(q, mp);
- } else if (canputnext(q))
- putnext(q, mp);
- else {
- freemsg(mp);
- flushq(q, FLUSHDATA);
- break;
- }
- }
- return 0;
- }
-
- /* Write-side put routine. Only handles M_DATA, handing others to dlpiether. */
- static int
- loop_wput (queue_t * q, mblk_t * mp)
- {
- loop_t * loop;
- mblk_t * first_mp;
- long len;
- long remaining;
- long total;
- unsigned char * xmt_buf;
-
- loop = (loop_t *)q->q_ptr;
- if (mp->b_datap->db_type != M_DATA) {
- mp = dle_wput(q, mp);
- if (!mp)
- return 0;
- switch (mp->b_datap->db_type) {
- case M_DATA:
- break; /* it's ready to send */
- case M_IOCNAK:
- /*
- * Any driver private ioctl's come back from
- * dle_wput() as an M_IOCNAK with ioc_error
- * set to EINVAL; the rest of the original M_IOCTL
- * is intact (including ioc_cmd & trailing M_DATAs).
- * It may be processed here.
- */
- qreply(q,mp);
- return 0;
- default:
- /* dle_wput() has formatted the reply for us */
- qreply(q, mp);
- return 0;
- }
- }
-
- /* Transmit the packet defined by mp->b_rptr to mp->b_wptr. */
-
- #ifndef LOOPBACK_DRIVER
- if (Can't transmit for some reason) {
- ((dle_t *)loop->loop_dle)->dle_istatus.transmit_discards++;
- freemsg(mp);
- return 0;
- }
- /*
- * Copy the packet to transmit buffer. This is an example
- * showing the copies and the calculation of the length
- * of the packet. Code for actual hardware devices will
- * obviously need to be updated, at least to initialize
- * xmt_buf to point to the hardware transmit area.
- */
- remaining = MAX_PACKET_SIZE;
- first_mp = mp;
- xmt_buf = ??; /* BoardX: replace this */
- do {
- unsigned char * rptr = mp->b_rptr;
- int len = mp->b_wptr - rptr;
- if (len <= 0)
- continue;
- if (remaining < len) {
- /* packet too large */
- mp = dle_wput_ud_error(first_mp, DL_UNDELIVERABLE, 0);
- if (mp)
- qreply(q, mp);
- return 0;
- }
- remaining -= len;
- xmt_buf += len;
- board_bcopy_to_noncached(rptr, xmt_buf - len, len);
- } while ((mp = mp->b_cont) && remaining);
-
- total = MAX_PACKET_SIZE - remaining;
-
- /* Fill in the 802 length field if it is zero. */
- {
- unsigned char * up;
- up = &xmt_buf[-total];
- if (!(up[12] | up[13])) {
- uint adjusted_total = total - 14;
- up[12] = (unsigned char)(adjusted_total >> 8);
- up[13] = (unsigned char)(adjusted_total & 0xff);
- }
- }
-
- if (total < MIN_PACKET_SIZE) {
- /*
- * If the packet is less than the minimum transmit
- * size, then you need to pad the packet. Hopefully
- * this is just of matter of setting the hardware
- * to pad automatically.
- */
- ;
- }
-
- /* Trigger the hardware transmit. */
- #else
- /* Loopback driver processing */
- first_mp = loop_wput_process(q, mp, &total);
- if (!first_mp) {
- ((dle_t *)loop->loop_dle)->dle_istatus.transmit_discards++;
- return 0;
- }
-
- #endif
- /* Increment the appropriate MIB interface statistics. */
- ((dle_t *)loop->loop_dle)->dle_istatus.bytes_sent += total;
- if (first_mp->b_rptr[0] & 0x1) {
- unsigned char * rptr = first_mp->b_rptr;
- if ((rptr[0] & rptr[1] & rptr[2] & rptr[3] & rptr[4]
- & rptr[5]) == 0xFF)
- ((dle_t *)loop->loop_dle)->dle_istatus.broadcast_frames_sent++;
- else
- ((dle_t *)loop->loop_dle)->dle_istatus.multicast_frames_sent++;
- } else
- ((dle_t *)loop->loop_dle)->dle_istatus.unicast_frames_sent++;
-
- #ifndef LOOPBACK_DRIVER
- freemsg(first_mp);
- #else
- /*
- * Loop it around - dle_inbound() call's putq(), so stack depth not
- * an issue here.
- */
- dle_inbound((dle_t *)loop->loop_dle, first_mp);
- return 0;
- #endif
- }
-
- /*
- * For the loopback driver, we turn around the message, filling in 802
- * length/type field. This function will not be used by hardware code
- * modifying this template.
- */
- #ifdef LOOPBACK_DRIVER
- static mblk_t *
- loop_wput_process (queue_t * q, mblk_t * mp, long * total)
- {
- long len;
- long first_len;
- mblk_t * mp1;
- mblk_t * mp2;
- unsigned char * up;
-
- first_len = mp->b_wptr - mp->b_rptr;
- *total = first_len;
- for (mp1 = mp; mp2 = mp1->b_cont; ) {
- len = mp2->b_wptr - mp2->b_rptr;
- if (len <= 0 || mp2->b_datap->db_type != M_DATA) {
- mp1->b_cont = mp2->b_cont;
- freeb(mp2);
- continue;
- }
- mp1 = mp2;
- *total += len;
- }
-
- if (*total > MAX_PACKET_SIZE) {
- mp = dle_wput_ud_error(mp, DL_UNDELIVERABLE, 0);
- if (mp)
- qreply(q, mp);
- return NULL;
- }
- if (*total < MIN_PACKET_SIZE) { /* we need to pad */
- if (mp->b_cont && !pullupmsg(mp, -1)) {
- mp = dle_wput_ud_error(mp, DL_SYSERR, ENOMEM);
- if (mp)
- qreply(q, mp);
- return NULL;
- }
- mp = loop_reallocb(mp, MIN_PACKET_SIZE);
- if (!mp)
- return NULL;
-
- bzero((char *)mp->b_wptr, (MIN_PACKET_SIZE - *total));
- mp->b_wptr += (MIN_PACKET_SIZE - *total);
- first_len = MIN_PACKET_SIZE;
- }
- /*
- * db_ref note: db_ref may be > 1 for some packets (we know some
- * IP packets will have db_ref > 1). We assume that the portion
- * of the packet we are jamming is opaque to upper levels and since
- * we always write it here, things should be fine. If db_ref > 2,
- * this could be a problem, but this we know is 'never' true.
- */
- if (first_len < 14 && !pullupmsg(mp, 14)) {
- mp = dle_wput_ud_error(mp, DL_SYSERR, ENOMEM);
- if (mp)
- qreply(q, mp);
- return NULL;
- }
- up = &mp->b_rptr[12];
- if (!(up[12] | up[13])) {
- uint adjusted_total = *total - 14;
- up[12] = (unsigned char)(adjusted_total >> 8);
- up[13] = (unsigned char)(adjusted_total & 0xff);
- }
- return mp;
- }
- #endif /* LOOPBACK_DRIVER */
-