Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
EnetDrvrSrc.c
/* |
File: EnetDrvrSrc.c |
Contains: A sample Ethernet driver source sample based on the sample loopback driver |
which is supplied as part of the OT Module Driver SDK. This source file |
extends the loopback driver to demonstrate |
1. the installation of the interrupt handler, |
2. how to access the kAAPLDeviceLogicalAddress property in order to obtain |
the base address registers for the PC Card. |
3. how to read the Ethernet burned in address (BIA) from either |
attribute memory, if not in the CIS, or from the CISTPL_FUNCE if it is |
in the CIS. |
Original comments: |
* 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. |
Written by: Mentat Inc. |
Change History (most recent first): |
8/16/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
char sccs_loop_id[] = "@(#)EnetDrvrSrc.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 "Devices.h" |
#include <NameRegistry.h> |
#include <DriverServices.h> |
#include <Interrupts.h> |
#include <OpenTptLinks.h> |
#include <OpenTptPCISupport.h> |
#include "dlpiuser.h" |
#include "dlpiether.h" |
#define ulong UInt32 |
#define uint UInt16 |
#include <PCCard.h> |
#include <PCCardTuples.h> |
// enter the name of the module as registered using the OTRegisterPort call. |
// Use the PCCardDisplayNameRegistry utility to find the module name for the |
// This name MUST match else TCP/IP services WILL not work. |
#define kEnetCardName "myEnetDriverName" |
/* |
* 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 */ |
#define ENETADDRINATTRIBUTE /* conditionally compiles code to obtain the ethernet |
burned in address from attribute memory |
comment out this define to had code access BIA |
from CIS |
*/ |
/* 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, ...); |
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 |
*/ |
/* 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 int loop_rsrv(queue_t * q); |
static int loop_wput(queue_t * q, mblk_t * mp); |
install_info* GetOTInstallInfo (); |
Boolean InitStreamModule (RegEntryID * our_id); |
void board_bcopy_to_noncached (unsigned char * src, unsigned char * dst, unsigned int count); |
// DRF start: added for PCCard Kitchen |
InterruptMemberNumber board_intr(InterruptSetMember ISTmember, void *refCon, UInt32 theIntCount); |
void TerminateStreamModule (void); |
OTResult ValidateHardware (RegEntryID * our_id); |
InterruptEnabler gInterruptEnabler; |
InterruptDisabler gInterruptDisabler; |
volatile void * gCardBaseAddress; |
unsigned char gEthernetHardwareAddress[6]; |
// DRF end |
/* |
* 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. |
*/ |
board_t * board_global; |
/* STREAMS configuration structures. */ |
struct module_info info = { |
kEnetModuleID, kEnetCardName |
, MIN_PACKET_SIZE, MAX_PACKET_SIZE |
, LOOP_HIWAT, LOOP_LOWAT |
}; |
/* |
* STREAMS qinit structures. Drivers have no rput. |
*/ |
struct qinit rinit = { |
NULL, loop_rsrv, loop_open, loop_close, NULL, &info |
}; |
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. |
*/ |
install_info loopback_install_info = |
{ |
&loopback_info, |
kOTModIsDriver | kOTModUpperIsDLPI, |
SQLVL_MODULE, |
NULL, |
0, |
0 |
}; |
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. |
*/ |
InterruptMemberNumber board_intr (InterruptSetMember ISTmember, void *refCon, UInt32 theIntCount) |
{ |
#pragma unused(ISTmember,refCon,theIntCount) |
board_t * board = board_global; |
dle_t * dle = &board->board_dle; |
OTEnterInterrupt(); |
loop_debug((trace_args "entered" trace_eol)); |
OTLeaveInterrupt(); |
return 1; |
} |
/* This function must be exported; see loop.exp */ |
install_info * |
GetOTInstallInfo () |
{ |
DebugStr("\p entered GetOTInstallInfo "); |
loop_debug((trace_args "GetOTInstallInfo" trace_eol)); |
return &loopback_install_info; |
} |
Boolean |
InitStreamModule (RegEntryID * our_id) |
{ |
board_t * board; |
dle_t * dle; |
// DRF start: added for PCCard Kitchen |
RegPropertyValueSize propertySize; |
InterruptSetMember our_ist; |
InterruptHandler old_interrupt_handler; |
void * refCon; |
OSStatus status; |
#ifdef ENETADDRINATTRIBUTE |
// ethernet address is in attribute memory, but not CIS |
LogicalAddress attribute_window_base; |
ByteCount attribute_window_size; |
PCCardAccessSpeed attribute_window_speed; |
PCCardWindowOffset attribute_window_offset; |
PCCardWindowID attribute_window_id; |
#else |
// ethernet address is in CISTPL_FUNCE |
PCCardTupleIterator tupleIterator; |
char tupleBuffer[MAX_TUPLE_SIZE]; |
UInt32 tupleBufferSize; |
PCCardTupleKind foundTuple; |
UInt32 foundTupleDataSize; |
#endif |
// DRF end |
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. |
*/ |
// DRF start: added for PCCard Kitchen |
/* install interrupt handler */ |
loop_debug((trace_args "about to install interrupt handler!" trace_eol)); |
propertySize = sizeof(InterruptSetMember); |
status = RegistryPropertyGet(our_id, kISTPropertyName, &our_ist, &propertySize); |
if (status != noErr) { |
loop_debug((trace_args "InitStreamModule install interrupt vector: can't get driver-ist property." trace_eol)); |
return status; |
} |
status = GetInterruptFunctions(our_ist.setID, our_ist.member, |
&refCon, &old_interrupt_handler, &gInterruptEnabler, &gInterruptDisabler ); |
if (status != noErr) { |
loop_debug((trace_args "InitStreamModule install interrupt vector: can't get interrupt functions." trace_eol)); |
return status; |
} |
status = InstallInterruptFunctions(our_ist.setID, our_ist.member, |
NULL, board_intr, gInterruptEnabler, gInterruptDisabler ); |
if (status != noErr) { |
loop_debug((trace_args "InitStreamModule install interrupt vector: can't set interrupt functions." trace_eol)); |
return status; |
} |
/* get base address */ |
// DebugStr("\p about to get base address!"); |
propertySize = sizeof(gCardBaseAddress); |
status = RegistryPropertyGet(our_id, kAAPLDeviceLogicalAddress, &gCardBaseAddress, &propertySize); |
if (status != noErr) { |
loop_debug((trace_args "InitStreamModule install interrupt vector: can't get AAPL,address property." trace_eol)); |
return status; |
} |
// Get Ethernet Hardware Address |
#ifdef ENETADDRINATTRIBUTE |
// If your card has ethernet hardware address in attribute memory, but NOT in a tuple, then use the following: |
/* IMPORTANT NOTE */ |
// the following values are for compilation purposes only. When accessing the ethernet address |
// from attribute memory, the following values must be set as appropriate for your card. |
attribute_window_size = 4096; |
attribute_window_speed = 600; |
attribute_window_offset = 0; |
status = PCCardRequestWindow(our_id, kPCCardAttributeMemorySpace, &attribute_window_base, &attribute_window_size, |
&attribute_window_speed, &attribute_window_offset, &attribute_window_id); |
if (status != noErr) { |
loop_debug((trace_args "InitStreamModule get ethernet address: can't request attribute window" trace_eol)); |
return status; |
} |
status = PCCardReleaseWindow(attribute_window_id); |
// save the Ethernet BIA into the dle structure as specified in the DLPI template note |
bcopy(dle->dle_factory_addr, attribute_window_base, 6); |
bcopy(dle->dle_current_addr, attribute_window_base, 6); |
#else |
// if your card has ethernet hardware address in CISTPL_FUNCE, it is much easier |
tupleIterator = PCCardNewTupleIterator(); |
if (tupleIterator == NULL) { |
loop_debug((trace_args "InitStreamModule get ethernet address: can't make tuple iterator" trace_eol)); |
return status; |
} |
tupleBufferSize = sizeof(tupleBuffer); |
status = PCCardGetFirstTuple(our_id, CISTPL_FUNCE, tupleIterator, &tupleBuffer[0], &tupleBufferSize, |
&foundTuple, &foundTupleDataSize); |
while (status == noErr) { |
// we found a CISTPL_FUNCE tuple |
DebugStr("\pFound CISTPL_FUNCE"); // for RATOC card, the address is stored starting at the 5th byte |
if (1) { |
my_hardware_addr[0] = tupleBuffer[5]; |
my_hardware_addr[1] = tupleBuffer[6]; |
my_hardware_addr[2] = tupleBuffer[7]; |
my_hardware_addr[3] = tupleBuffer[8]; |
my_hardware_addr[4] = tupleBuffer[9]; |
my_hardware_addr[5] = tupleBuffer[10]; |
DebugStr("\pgot hardware address"); |
break; |
} |
tupleBufferSize = sizeof(tupleBuffer); |
status = PCCardGetNextTuple(our_id, CISTPL_FUNCE, tupleIterator, &tupleBuffer[0], &tupleBufferSize, |
&foundTuple, &foundTupleDataSize); |
} |
PCCardDisposeTupleIterator(tupleIterator); |
// save the Ethernet BIA into the dle structure as specified in the DLPI template note |
bcopy(dle->dle_factory_addr, my_hardware_addr, 6); |
bcopy(dle->dle_current_addr, my_hardware_addr, 6); |
#endif |
// DRF end |
/* |
* 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) |
{ |
#pragma unused(our_id) |
loop_debug((trace_args "ValidateHardware" trace_eol)); |
return kOTNoError; |
} |
/* STREAMS close routine. */ |
int loop_close (queue_t * q, int flag, cred_t * credp) |
{ |
#pragma unused(flag,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); |
} |
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) |
{ |
#pragma unused(addr_count,accept_broadcast,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. */ |
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. */ |
void loop_hw_stop (void * boardvp) |
{ |
#pragma unused(boardvp) |
/* Turn off interrupts and leave the hardware disabled. */ |
} |
/* STREAMS open routine. */ |
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; |
} |
/* |
* 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). |
*/ |
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. */ |
int |
loop_wput (queue_t * q, mblk_t * mp) |
{ |
loop_t * loop; |
mblk_t * first_mp; |
long remaining; |
long total; |
unsigned char * xmt_buf=nil; |
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; |
} |
} |
/* |
* 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; |
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. */ |
/* 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++; |
freemsg(first_mp); |
return 0; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22