diff --git a/sys/dev/ep/if_epreg.h b/sys/dev/ep/if_epreg.h index 5685832ccfee..1bef6154eac7 100644 --- a/sys/dev/ep/if_epreg.h +++ b/sys/dev/ep/if_epreg.h @@ -1,462 +1,462 @@ /* * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved. * * Redistribution 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, this list of conditions and the following disclaimer. 2. The name * of the author may not be used to endorse or promote products derived from * this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by: * October 2, 1994 Modified by: Andres Vega Garcia INRIA - Sophia Antipolis, France e-mail: avega@sophia.inria.fr finger: avega@pax.inria.fr */ /* - * $Id: if_epreg.h,v 1.16 1996/08/06 21:14:06 phk Exp $ + * $Id: if_epreg.h,v 1.17 1996/09/08 10:44:10 phk Exp $ * * Promiscuous mode added and interrupt logic slightly changed * to reduce the number of adapter failures. Transceiver select * logic changed to use value from EEPROM. Autoconfiguration * features added. * Done by: * Serge Babkin * Chelindbank (Chelyabinsk, Russia) * babkin@hq.icb.chel.su */ /* * Pccard support for 3C589 by: * HAMADA Naoki * nao@tom-yam.or.jp */ /* * Ethernet software status per interface. */ struct ep_softc { struct arpcom arpcom; /* Ethernet common part */ - short ep_io_addr; /* i/o bus address */ + int ep_io_addr; /* i/o bus address */ #define MAX_MBS 8 /* # of mbufs we keep around */ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ int next_mb; /* Which mbuf to use next. */ int last_mb; /* Last mbuf. */ struct mbuf *top, *mcur; short tx_start_thresh; /* Current TX_start_thresh. */ short tx_rate; short tx_counter; short rx_early_thresh; /* Current RX_early_thresh. */ short rx_latency; short rx_avg_pkt; short cur_len; u_short ep_connectors; /* Connectors on this card. */ u_char ep_connector; /* Configured connector. */ int stat; /* some flags */ int gone; /* adapter is not present (for PCCARD) */ #define F_RX_FIRST 0x1 #define F_WAIT_TRAIL 0x2 #define F_RX_TRAILER 0x4 #define F_PROMISC 0x8 #define F_ACCESS_32_BITS 0x100 struct ep_board *epb; int unit; #ifdef EP_LOCAL_STATS short tx_underrun; short rx_no_first; short rx_no_mbuf; short rx_bpf_disc; short rx_overrunf; short rx_overrunl; #endif }; struct ep_board { int epb_addr; /* address of this board */ char epb_used; /* was this entry already used for configuring ? */ /* data from EEPROM for later use */ u_short eth_addr[3]; /* Ethernet address */ u_short prod_id; /* product ID */ u_short res_cfg; /* resource configuration */ }; /* * Some global constants */ #define TX_INIT_RATE 16 #define TX_INIT_MAX_RATE 64 #define RX_INIT_LATENCY 64 #define RX_INIT_EARLY_THRESH 64 #define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */ #define MIN_RX_EARLY_THRESHL 4 #define EEPROMSIZE 0x40 #define MAX_EEPROMBUSY 1000 #define EP_LAST_TAG 0xd7 #define EP_MAX_BOARDS 16 #define EP_ID_PORT 0x100 #define EP_IOSIZE 16 /* 16 bytes of I/O space used. */ /* * some macros to acces long named fields */ #define IS_BASE (is->id_iobase) #define BASE (sc->ep_io_addr) /* * Commands to read/write EEPROM trough EEPROM command register (Window 0, * Offset 0xa) */ #define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */ #define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */ #define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */ #define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */ #define EEPROM_BUSY (1<<15) #define EEPROM_TST_MODE (1<<14) /* * Some short functions, worth to let them be a macro */ #define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY) #define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x)) /************************************************************************** * * * These define the EEPROM data structure. They are used in the probe * function to verify the existence of the adapter after having sent * the ID_Sequence. * * There are others but only the ones we use are defined here. * **************************************************************************/ #define EEPROM_NODE_ADDR_0 0x0 /* Word */ #define EEPROM_NODE_ADDR_1 0x1 /* Word */ #define EEPROM_NODE_ADDR_2 0x2 /* Word */ #define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ #define EEPROM_MFG_ID 0x7 /* 0x6d50 */ #define EEPROM_ADDR_CFG 0x8 /* Base addr */ #define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ /************************************************************************** * * * These are the registers for the 3Com 3c509 and their bit patterns when * * applicable. They have been taken out the the "EtherLink III Parallel * * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual * * from 3com. * * * **************************************************************************/ #define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a * command reg. */ #define EP_STATUS 0x0e /* Read. BASE+0x0e is always status * reg. */ #define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window * reg. */ /* * Window 0 registers. Setup. */ /* Write */ #define EP_W0_EEPROM_DATA 0x0c #define EP_W0_EEPROM_COMMAND 0x0a #define EP_W0_RESOURCE_CFG 0x08 #define EP_W0_ADDRESS_CFG 0x06 #define EP_W0_CONFIG_CTRL 0x04 /* Read */ #define EP_W0_PRODUCT_ID 0x02 #define EP_W0_MFG_ID 0x00 /* * Window 1 registers. Operating Set. */ /* Write */ #define EP_W1_TX_PIO_WR_2 0x02 #define EP_W1_TX_PIO_WR_1 0x00 /* Read */ #define EP_W1_FREE_TX 0x0c #define EP_W1_TX_STATUS 0x0b /* byte */ #define EP_W1_TIMER 0x0a /* byte */ #define EP_W1_RX_STATUS 0x08 #define EP_W1_RX_PIO_RD_2 0x02 #define EP_W1_RX_PIO_RD_1 0x00 /* * Window 2 registers. Station Address Setup/Read */ /* Read/Write */ #define EP_W2_ADDR_5 0x05 #define EP_W2_ADDR_4 0x04 #define EP_W2_ADDR_3 0x03 #define EP_W2_ADDR_2 0x02 #define EP_W2_ADDR_1 0x01 #define EP_W2_ADDR_0 0x00 /* * Window 3 registers. FIFO Management. */ /* Read */ #define EP_W3_FREE_TX 0x0c #define EP_W3_FREE_RX 0x0a /* * Window 4 registers. Diagnostics. */ /* Read/Write */ #define EP_W4_MEDIA_TYPE 0x0a #define EP_W4_CTRLR_STATUS 0x08 #define EP_W4_NET_DIAG 0x06 #define EP_W4_FIFO_DIAG 0x04 #define EP_W4_HOST_DIAG 0x02 #define EP_W4_TX_DIAG 0x00 /* * Window 5 Registers. Results and Internal status. */ /* Read */ #define EP_W5_READ_0_MASK 0x0c #define EP_W5_INTR_MASK 0x0a #define EP_W5_RX_FILTER 0x08 #define EP_W5_RX_EARLY_THRESH 0x06 #define EP_W5_TX_AVAIL_THRESH 0x02 #define EP_W5_TX_START_THRESH 0x00 /* * Window 6 registers. Statistics. */ /* Read/Write */ #define TX_TOTAL_OK 0x0c #define RX_TOTAL_OK 0x0a #define TX_DEFERRALS 0x08 #define RX_FRAMES_OK 0x07 #define TX_FRAMES_OK 0x06 #define RX_OVERRUNS 0x05 #define TX_COLLISIONS 0x04 #define TX_AFTER_1_COLLISION 0x03 #define TX_AFTER_X_COLLISIONS 0x02 #define TX_NO_SQE 0x01 #define TX_CD_LOST 0x00 /**************************************** * * Register definitions. * ****************************************/ /* * Command register. All windows. * * 16 bit register. * 15-11: 5-bit code for command to be executed. * 10-0: 11-bit arg if any. For commands with no args; * this can be set to anything. */ #define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms * after issuing */ #define WINDOW_SELECT (u_short) (0x1<<11) #define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to * determine whether * this is needed. If * so; wait 800 uSec * before using trans- * ceiver. */ #define RX_DISABLE (u_short) (0x3<<11) /* state disabled on * power-up */ #define RX_ENABLE (u_short) (0x4<<11) #define RX_RESET (u_short) (0x5<<11) #define RX_DISCARD_TOP_PACK (u_short) (0x8<<11) #define TX_ENABLE (u_short) (0x9<<11) #define TX_DISABLE (u_short) (0xa<<11) #define TX_RESET (u_short) (0xb<<11) #define REQ_INTR (u_short) (0xc<<11) #define SET_INTR_MASK (u_short) (0xe<<11) #define SET_RD_0_MASK (u_short) (0xf<<11) #define SET_RX_FILTER (u_short) (0x10<<11) #define FIL_INDIVIDUAL (u_short) (0x1) #define FIL_GROUP (u_short) (0x2) #define FIL_BRDCST (u_short) (0x4) #define FIL_ALL (u_short) (0x8) #define SET_RX_EARLY_THRESH (u_short) (0x11<<11) #define SET_TX_AVAIL_THRESH (u_short) (0x12<<11) #define SET_TX_START_THRESH (u_short) (0x13<<11) #define STATS_ENABLE (u_short) (0x15<<11) #define STATS_DISABLE (u_short) (0x16<<11) #define STOP_TRANSCEIVER (u_short) (0x17<<11) /* * The following C_* acknowledge the various interrupts. Some of them don't * do anything. See the manual. */ #define ACK_INTR (u_short) (0x6800) #define C_INTR_LATCH (u_short) (ACK_INTR|0x1) #define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) #define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) #define C_TX_AVAIL (u_short) (ACK_INTR|0x8) #define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) #define C_RX_EARLY (u_short) (ACK_INTR|0x20) #define C_INT_RQD (u_short) (ACK_INTR|0x40) #define C_UPD_STATS (u_short) (ACK_INTR|0x80) #define C_MASK (u_short) 0xFF /* mask of C_* */ /* * Status register. All windows. * * 15-13: Window number(0-7). * 12: Command_in_progress. * 11: reserved. * 10: reserved. * 9: reserved. * 8: reserved. * 7: Update Statistics. * 6: Interrupt Requested. * 5: RX Early. * 4: RX Complete. * 3: TX Available. * 2: TX Complete. * 1: Adapter Failure. * 0: Interrupt Latch. */ #define S_INTR_LATCH (u_short) (0x1) #define S_CARD_FAILURE (u_short) (0x2) #define S_TX_COMPLETE (u_short) (0x4) #define S_TX_AVAIL (u_short) (0x8) #define S_RX_COMPLETE (u_short) (0x10) #define S_RX_EARLY (u_short) (0x20) #define S_INT_RQD (u_short) (0x40) #define S_UPD_STATS (u_short) (0x80) #define S_MASK (u_short) 0xFF /* mask of S_* */ #define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\ S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY) #define S_COMMAND_IN_PROGRESS (u_short) (0x1000) /* Address Config. Register. * Window 0/Port 06 */ #define ACF_CONNECTOR_BITS 14 #define ACF_CONNECTOR_UTP 0 #define ACF_CONNECTOR_AUI 1 #define ACF_CONNECTOR_BNC 3 /* Resource configuration register. * Window 0/Port 08 * */ #define SET_IRQ(base,irq) outw((base) + EP_W0_RESOURCE_CFG, \ ((inw((base) + EP_W0_RESOURCE_CFG) & 0x0fff) | \ ((u_short)(irq)<<12)) ) /* set IRQ i */ /* * FIFO Registers. * RX Status. Window 1/Port 08 * * 15: Incomplete or FIFO empty. * 14: 1: Error in RX Packet 0: Incomplete or no error. * 13-11: Type of error. * 1000 = Overrun. * 1011 = Run Packet Error. * 1100 = Alignment Error. * 1101 = CRC Error. * 1001 = Oversize Packet Error (>1514 bytes) * 0010 = Dribble Bits. * (all other error codes, no errors.) * * 10-0: RX Bytes (0-1514) */ #define ERR_RX_INCOMPLETE (u_short) (0x1<<15) #define ERR_RX (u_short) (0x1<<14) #define ERR_RX_OVERRUN (u_short) (0x8<<11) #define ERR_RX_RUN_PKT (u_short) (0xb<<11) #define ERR_RX_ALIGN (u_short) (0xc<<11) #define ERR_RX_CRC (u_short) (0xd<<11) #define ERR_RX_OVERSIZE (u_short) (0x9<<11) #define ERR_RX_DRIBBLE (u_short) (0x2<<11) /* * FIFO Registers. * TX Status. Window 1/Port 0B * * Reports the transmit status of a completed transmission. Writing this * register pops the transmit completion stack. * * Window 1/Port 0x0b. * * 7: Complete * 6: Interrupt on successful transmission requested. * 5: Jabber Error (TP Only, TX Reset required. ) * 4: Underrun (TX Reset required. ) * 3: Maximum Collisions. * 2: TX Status Overflow. * 1-0: Undefined. * */ #define TXS_COMPLETE 0x80 #define TXS_SUCCES_INTR_REQ 0x40 #define TXS_JABBER 0x20 #define TXS_UNDERRUN 0x10 #define TXS_MAX_COLLISION 0x8 #define TXS_STATUS_OVERFLOW 0x4 /* * Configuration control register. * Window 0/Port 04 */ /* Read */ #define IS_AUI (1<<13) #define IS_BNC (1<<12) #define IS_UTP (1<<9) /* Write */ #define ENABLE_DRQ_IRQ 0x0001 #define W0_P4_CMD_RESET_ADAPTER 0x4 #define W0_P4_CMD_ENABLE_ADAPTER 0x1 /* * Media type and status. * Window 4/Port 0A */ #define ENABLE_UTP 0xc0 #define DISABLE_UTP 0x0 /* * Misc defines for various things. */ #define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */ #define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */ #define PROD_ID 0x9150 #define AUI 0x1 #define BNC 0x2 #define UTP 0x4 #define RX_BYTES_MASK (u_short) (0x07ff) extern struct ep_board ep_board[]; extern int ep_boards; extern u_long ep_unit; extern struct ep_softc *ep_alloc __P((int unit, struct ep_board *epb)); extern void ep_free __P((struct ep_softc *sc)); extern void ep_intr __P((void *sc)); extern int ep_attach __P((struct ep_softc *sc)); extern u_int16_t get_e __P((struct ep_softc *sc, int offset)); diff --git a/sys/i386/isa/ctx.c b/sys/i386/isa/ctx.c index b35f2a315f68..20b9d8224161 100644 --- a/sys/i386/isa/ctx.c +++ b/sys/i386/isa/ctx.c @@ -1,459 +1,459 @@ /* * CORTEX-I Frame Grabber driver V1.0 * * Copyright (C) 1994, Paul S. LaFollette, Jr. This software may be used, * modified, copied, distributed, and sold, in both source and binary form * provided that the above copyright and these terms are retained. Under * no circumstances is the author responsible for the proper functioning * of this software, nor does the author assume any responsibility * for damages incurred with its use. * - * $Id: ctx.c,v 1.19 1996/06/25 20:29:52 bde Exp $ + * $Id: ctx.c,v 1.20 1996/09/06 23:07:14 phk Exp $ */ /* * * * * Device Driver for CORTEX-I Frame Grabber * Made by ImageNation Corporation * 1200 N.E. Keyues Road * Vancouver, WA 98684 (206) 944-9131 * (I have no ties to this company, just thought you might want * to know how to get in touch with them.) * * In order to understand this device, you really need to consult the * manual which ImageNation provides when you buy the board. (And * what a pleasure it is to buy something for a PC and actually get * programming information along with it.) I will limit myself here to * a few comments which are specific to this driver. See also the file * ctxreg.h for definitions of registers and control bits. * * 1. Although the hardware supports low resolution (256 x 256) * acqusition and display, I have not implemented access to * these modes in this driver. There are some fairly quirky * aspects to the way this board works in low resolution mode, * and I don't want to deal with them. Maybe later. * * 2. Choosing the base address for the video memory: This is set * using a combination of hardware and software, using the left * most dip switch on the board, and the AB_SELECT bit of control * port 1, according to the chart below: * * Left DIP switch || DOWN | UP | * ================================================= * AB_SELECT = 0 || 0xA0000 | 0xB0000 | * ------------------------------------------------- * AB_SELECT = 1 || 0xD0000 | 0xE0000 | * ------------------------------------------------ * * When the RAM_ENABLE bit of control port 1 is clear (0), the * video ram is disconnected from the computer bus. This makes * it possible, in principle, to share memory space with other * devices (such as VGA) which can also disconnect themselves * from the bus. It also means that multiple CORTEX-I boards * can share the same video memory space. Disconnecting from the * bus does not affect the video display of the video ram contents, * so that one needs only set the RAM_ENABLE bit when actually * reading or writing to memory. The cost of this is low, * the benefits to me are great (I need more than one board * in my machine, and 0xE0000 is the only address choice that * doesn't conflict with anything) so I adopt this strategy here. * * XXX-Note... this driver has only been tested for the * XXX base = 0xE0000 case! * * 3) There is a deficiency in the documentation from ImageNation, I * think. In order to successfully load the lookup table, it is * necessary to clear SEE_STORED_VIDEO in control port 0 as well as * setting LUT_LOAD_ENABLE in control port 1. * * 4) This driver accesses video memory through read or write operations. * Other functionality is provided through ioctl's, manifest * constants for which are defined in ioctl_ctx.h. The ioctl's * include: * CTX_LIVE Display live video * CTX_GRAB Grab a frame of video data * CTX_H_ORGANIZE Set things up so that sequential read * operations access horizontal lines of * pixels. * CTX_V_ORGANIZE Set things up so that sequential read * operations access vertical lines of * pixels. * CTX_SET_LUT Set the lookup table from an array * of 256 unsigned chars passed as the * third parameter to ioctl. * CTX_GET_LUT Return the current lookup table to * the application as an array of 256 * unsigned chars. Again the third * parameter to the ioctl call. * * Thus, * ioctl(fi, CTX_H_ORGANIZE, 0); * lseek(fi, y*512, SEEK_SET); * read(fi, buffer, 512); * * will fill buffer with 512 pixels (unsigned chars) which represent * the y-th horizontal line of the image. * Similarly, * ioctl(fi, CTX_V_ORGANIZE, 0: * lseek(fi, x*512+y, SEEK_SET); * read(fi, buffer, 10); * * will read 10 a vertical line of 10 pixels starting at (x,y). * * Obviously, this sort of ugliness needs to be hidden away from * the casual user, with an appropriate set of higher level * functions. * */ #include "ctx.h" #if NCTX > 0 #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include -static int waitvb(short); +static int waitvb(int port); /* state flags */ #define OPEN (0x01) /* device is open */ #define UNIT(x) ((x) & 0x07) static int ctxprobe __P((struct isa_device *devp)); static int ctxattach __P((struct isa_device *devp)); struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"}; static d_open_t ctxopen; static d_close_t ctxclose; static d_read_t ctxread; static d_write_t ctxwrite; static d_ioctl_t ctxioctl; #define CDEV_MAJOR 40 static struct cdevsw ctx_cdevsw = { ctxopen, ctxclose, ctxread, ctxwrite, /*40*/ ctxioctl, nostop, nullreset, nodevtotty,/* cortex */ seltrue, nommap, NULL, "ctx", NULL, -1 }; #define LUTSIZE 256 /* buffer size for Look Up Table (LUT) */ #define PAGESIZE 65536 /* size of one video page, 1/4 of the screen */ /* * Per unit shadow registers (because the dumb hardware is RO) */ static struct ctx_soft_registers { u_char *lutp; u_char cp0; u_char cp1; u_char flag; - short iobase; + int iobase; caddr_t maddr; int msize; void *devfs_token; } ctx_sr[NCTX]; static int ctxprobe(struct isa_device * devp) { int status; if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */ status = 0; else { status = 1; /*XXX uses only one port? */ } return (status); } static int ctxattach(struct isa_device * devp) { struct ctx_soft_registers *sr; sr = &(ctx_sr[devp->id_unit]); sr->cp0 = 0; /* zero out the shadow registers */ sr->cp1 = 0; /* and the open flag. wait for */ sr->flag = 0; /* open to malloc the LUT space */ sr->iobase = devp->id_iobase; sr->maddr = devp->id_maddr; sr->msize = devp->id_msize; return (1); #ifdef DEVFS sr->devfs_token = devfs_add_devswf(&ctx_cdevsw, 0, DV_CHR, 0, 0, 0600, "ctx%d", devp->id_unit); #endif /* DEVFS */ } static int ctxopen(dev_t dev, int flags, int fmt, struct proc *p) { struct ctx_soft_registers *sr; u_char unit; int i; unit = UNIT(minor(dev)); /* minor number out of range? */ if (unit >= NCTX) return (ENXIO); sr = &(ctx_sr[unit]); if (sr->flag != 0) /* someone has already opened us */ return (EBUSY); /* get space for the LUT buffer */ sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK); if (sr->lutp == NULL) return (ENOMEM); sr->flag = OPEN; /* Set up the shadow registers. We don't actually write these values to the control ports until after we finish loading the lookup table. */ sr->cp0 |= SEE_STORED_VIDEO; if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000)) sr->cp1 |= AB_SELECT; /* map to B or E if necessary */ /* but don't enable RAM */ /* Set up the lookup table initially so that it is transparent. */ outb(sr->iobase + ctx_cp0, (u_char) 0); outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY)); for (i = 0; i < LUTSIZE; i++) { outb(sr->iobase + ctx_lutaddr, (u_char) i); sr->lutp[i] = (u_char) i; outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]); } /* Disable LUT loading, and push the data in the shadow registers into the control ports. */ outb(sr->iobase + ctx_cp0, sr->cp0); outb(sr->iobase + ctx_cp1, sr->cp1); return (0); /* successful open. All ready to go. */ } static int ctxclose(dev_t dev, int flags, int fmt, struct proc *p) { int unit; unit = UNIT(minor(dev)); ctx_sr[unit].flag = 0; free(ctx_sr[unit].lutp, M_DEVBUF); ctx_sr[unit].lutp = NULL; return (0); } static int ctxwrite(dev_t dev, struct uio * uio, int ioflag) { int unit, status = 0; int page, count, offset; struct ctx_soft_registers *sr; unit = UNIT(minor(dev)); sr = &(ctx_sr[unit]); page = uio->uio_offset / PAGESIZE; offset = uio->uio_offset % PAGESIZE; count = min(uio->uio_resid, PAGESIZE - offset); while ((page >= 0) && (page <= 3) && (count > 0)) { sr->cp0 &= ~3; sr->cp0 |= page; outb(sr->iobase + ctx_cp0, sr->cp0); /* Before doing the uiomove, we need to "connect" the frame buffer ram to the machine bus. This is done here so that we can have several different boards installed, all sharing the same memory space... each board is only "connected" to the bus when its memory is actually being read or written. All my instincts tell me that I should disable interrupts here, so I have done so. */ disable_intr(); sr->cp1 |= RAM_ENABLE; outb(sr->iobase + ctx_cp1, sr->cp1); status = uiomove(sr->maddr + offset, count, uio); sr->cp1 &= ~RAM_ENABLE; outb(sr->iobase + ctx_cp1, sr->cp1); enable_intr(); page = uio->uio_offset / PAGESIZE; offset = uio->uio_offset % PAGESIZE; count = min(uio->uio_resid, PAGESIZE - offset); } if (uio->uio_resid > 0) return (ENOSPC); else return (status); } static int ctxread(dev_t dev, struct uio * uio, int ioflag) { int unit, status = 0; int page, count, offset; struct ctx_soft_registers *sr; unit = UNIT(minor(dev)); sr = &(ctx_sr[unit]); page = uio->uio_offset / PAGESIZE; offset = uio->uio_offset % PAGESIZE; count = min(uio->uio_resid, PAGESIZE - offset); while ((page >= 0) && (page <= 3) && (count > 0)) { sr->cp0 &= ~3; sr->cp0 |= page; outb(sr->iobase + ctx_cp0, sr->cp0); /* Before doing the uiomove, we need to "connect" the frame buffer ram to the machine bus. This is done here so that we can have several different boards installed, all sharing the same memory space... each board is only "connected" to the bus when its memory is actually being read or written. All my instincts tell me that I should disable interrupts here, so I have done so. */ disable_intr(); sr->cp1 |= RAM_ENABLE; outb(sr->iobase + ctx_cp1, sr->cp1); status = uiomove(sr->maddr + offset, count, uio); sr->cp1 &= ~RAM_ENABLE; outb(sr->iobase + ctx_cp1, sr->cp1); enable_intr(); page = uio->uio_offset / PAGESIZE; offset = uio->uio_offset % PAGESIZE; count = min(uio->uio_resid, PAGESIZE - offset); } if (uio->uio_resid > 0) return (ENOSPC); else return (status); } static int ctxioctl(dev_t dev, int cmd, caddr_t data, int flags, struct proc *p) { int error; int unit, i; struct ctx_soft_registers *sr; error = 0; unit = UNIT(minor(dev)); sr = &(ctx_sr[unit]); switch (cmd) { case CTX_LIVE: sr->cp0 &= ~SEE_STORED_VIDEO; outb(sr->iobase + ctx_cp0, sr->cp0); break; case CTX_GRAB: sr->cp0 &= ~SEE_STORED_VIDEO; outb(sr->iobase + ctx_cp0, sr->cp0); sr->cp0 |= ACQUIRE; if (waitvb(sr->iobase)) /* wait for vert blank to start * acquire */ error = ENODEV; outb(sr->iobase + ctx_cp0, sr->cp0); if (waitvb(sr->iobase)) /* wait for two more to finish acquire */ error = ENODEV; if (waitvb(sr->iobase)) error = ENODEV; sr->cp0 &= ~ACQUIRE; /* turn off acquire and turn on * display */ sr->cp0 |= SEE_STORED_VIDEO; outb(sr->iobase + ctx_cp0, sr->cp0); break; case CTX_H_ORGANIZE: sr->cp0 &= ~PAGE_ROTATE; outb(sr->iobase + ctx_cp0, sr->cp0); break; case CTX_V_ORGANIZE: sr->cp0 |= PAGE_ROTATE; outb(sr->iobase + ctx_cp0, sr->cp0); break; case CTX_SET_LUT: bcopy((u_char *) data, sr->lutp, LUTSIZE); outb(sr->iobase + ctx_cp0, (u_char) 0); outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY)); for (i = 0; i < LUTSIZE; i++) { outb(sr->iobase + ctx_lutaddr, i); outb(sr->iobase + ctx_lutdata, sr->lutp[i]); } outb(sr->iobase + ctx_cp0, sr->cp0); /* restore control * registers */ outb(sr->iobase + ctx_cp1, sr->cp1); break; case CTX_GET_LUT: bcopy(sr->lutp, (u_char *) data, LUTSIZE); break; default: error = ENODEV; } return (error); } static int -waitvb(short port) +waitvb(int port) { /* wait for a vertical blank, */ if (inb(port) == 0xff) /* 0xff means no board present */ return (1); while ((inb(port) & VERTICAL_BLANK) != 0) { } while ((inb(port) & VERTICAL_BLANK) == 0) { } return (0); } static ctx_devsw_installed = 0; static void ctx_drvinit(void *unused) { dev_t dev; if( ! ctx_devsw_installed ) { dev = makedev(CDEV_MAJOR,0); cdevsw_add(&dev,&ctx_cdevsw,NULL); ctx_devsw_installed = 1; } } SYSINIT(ctxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ctx_drvinit,NULL) #endif /* NCTX > 0 */ diff --git a/sys/i386/isa/if_epreg.h b/sys/i386/isa/if_epreg.h index 5685832ccfee..1bef6154eac7 100644 --- a/sys/i386/isa/if_epreg.h +++ b/sys/i386/isa/if_epreg.h @@ -1,462 +1,462 @@ /* * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved. * * Redistribution 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, this list of conditions and the following disclaimer. 2. The name * of the author may not be used to endorse or promote products derived from * this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by: * October 2, 1994 Modified by: Andres Vega Garcia INRIA - Sophia Antipolis, France e-mail: avega@sophia.inria.fr finger: avega@pax.inria.fr */ /* - * $Id: if_epreg.h,v 1.16 1996/08/06 21:14:06 phk Exp $ + * $Id: if_epreg.h,v 1.17 1996/09/08 10:44:10 phk Exp $ * * Promiscuous mode added and interrupt logic slightly changed * to reduce the number of adapter failures. Transceiver select * logic changed to use value from EEPROM. Autoconfiguration * features added. * Done by: * Serge Babkin * Chelindbank (Chelyabinsk, Russia) * babkin@hq.icb.chel.su */ /* * Pccard support for 3C589 by: * HAMADA Naoki * nao@tom-yam.or.jp */ /* * Ethernet software status per interface. */ struct ep_softc { struct arpcom arpcom; /* Ethernet common part */ - short ep_io_addr; /* i/o bus address */ + int ep_io_addr; /* i/o bus address */ #define MAX_MBS 8 /* # of mbufs we keep around */ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ int next_mb; /* Which mbuf to use next. */ int last_mb; /* Last mbuf. */ struct mbuf *top, *mcur; short tx_start_thresh; /* Current TX_start_thresh. */ short tx_rate; short tx_counter; short rx_early_thresh; /* Current RX_early_thresh. */ short rx_latency; short rx_avg_pkt; short cur_len; u_short ep_connectors; /* Connectors on this card. */ u_char ep_connector; /* Configured connector. */ int stat; /* some flags */ int gone; /* adapter is not present (for PCCARD) */ #define F_RX_FIRST 0x1 #define F_WAIT_TRAIL 0x2 #define F_RX_TRAILER 0x4 #define F_PROMISC 0x8 #define F_ACCESS_32_BITS 0x100 struct ep_board *epb; int unit; #ifdef EP_LOCAL_STATS short tx_underrun; short rx_no_first; short rx_no_mbuf; short rx_bpf_disc; short rx_overrunf; short rx_overrunl; #endif }; struct ep_board { int epb_addr; /* address of this board */ char epb_used; /* was this entry already used for configuring ? */ /* data from EEPROM for later use */ u_short eth_addr[3]; /* Ethernet address */ u_short prod_id; /* product ID */ u_short res_cfg; /* resource configuration */ }; /* * Some global constants */ #define TX_INIT_RATE 16 #define TX_INIT_MAX_RATE 64 #define RX_INIT_LATENCY 64 #define RX_INIT_EARLY_THRESH 64 #define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */ #define MIN_RX_EARLY_THRESHL 4 #define EEPROMSIZE 0x40 #define MAX_EEPROMBUSY 1000 #define EP_LAST_TAG 0xd7 #define EP_MAX_BOARDS 16 #define EP_ID_PORT 0x100 #define EP_IOSIZE 16 /* 16 bytes of I/O space used. */ /* * some macros to acces long named fields */ #define IS_BASE (is->id_iobase) #define BASE (sc->ep_io_addr) /* * Commands to read/write EEPROM trough EEPROM command register (Window 0, * Offset 0xa) */ #define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */ #define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */ #define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */ #define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */ #define EEPROM_BUSY (1<<15) #define EEPROM_TST_MODE (1<<14) /* * Some short functions, worth to let them be a macro */ #define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY) #define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x)) /************************************************************************** * * * These define the EEPROM data structure. They are used in the probe * function to verify the existence of the adapter after having sent * the ID_Sequence. * * There are others but only the ones we use are defined here. * **************************************************************************/ #define EEPROM_NODE_ADDR_0 0x0 /* Word */ #define EEPROM_NODE_ADDR_1 0x1 /* Word */ #define EEPROM_NODE_ADDR_2 0x2 /* Word */ #define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ #define EEPROM_MFG_ID 0x7 /* 0x6d50 */ #define EEPROM_ADDR_CFG 0x8 /* Base addr */ #define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ /************************************************************************** * * * These are the registers for the 3Com 3c509 and their bit patterns when * * applicable. They have been taken out the the "EtherLink III Parallel * * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual * * from 3com. * * * **************************************************************************/ #define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a * command reg. */ #define EP_STATUS 0x0e /* Read. BASE+0x0e is always status * reg. */ #define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window * reg. */ /* * Window 0 registers. Setup. */ /* Write */ #define EP_W0_EEPROM_DATA 0x0c #define EP_W0_EEPROM_COMMAND 0x0a #define EP_W0_RESOURCE_CFG 0x08 #define EP_W0_ADDRESS_CFG 0x06 #define EP_W0_CONFIG_CTRL 0x04 /* Read */ #define EP_W0_PRODUCT_ID 0x02 #define EP_W0_MFG_ID 0x00 /* * Window 1 registers. Operating Set. */ /* Write */ #define EP_W1_TX_PIO_WR_2 0x02 #define EP_W1_TX_PIO_WR_1 0x00 /* Read */ #define EP_W1_FREE_TX 0x0c #define EP_W1_TX_STATUS 0x0b /* byte */ #define EP_W1_TIMER 0x0a /* byte */ #define EP_W1_RX_STATUS 0x08 #define EP_W1_RX_PIO_RD_2 0x02 #define EP_W1_RX_PIO_RD_1 0x00 /* * Window 2 registers. Station Address Setup/Read */ /* Read/Write */ #define EP_W2_ADDR_5 0x05 #define EP_W2_ADDR_4 0x04 #define EP_W2_ADDR_3 0x03 #define EP_W2_ADDR_2 0x02 #define EP_W2_ADDR_1 0x01 #define EP_W2_ADDR_0 0x00 /* * Window 3 registers. FIFO Management. */ /* Read */ #define EP_W3_FREE_TX 0x0c #define EP_W3_FREE_RX 0x0a /* * Window 4 registers. Diagnostics. */ /* Read/Write */ #define EP_W4_MEDIA_TYPE 0x0a #define EP_W4_CTRLR_STATUS 0x08 #define EP_W4_NET_DIAG 0x06 #define EP_W4_FIFO_DIAG 0x04 #define EP_W4_HOST_DIAG 0x02 #define EP_W4_TX_DIAG 0x00 /* * Window 5 Registers. Results and Internal status. */ /* Read */ #define EP_W5_READ_0_MASK 0x0c #define EP_W5_INTR_MASK 0x0a #define EP_W5_RX_FILTER 0x08 #define EP_W5_RX_EARLY_THRESH 0x06 #define EP_W5_TX_AVAIL_THRESH 0x02 #define EP_W5_TX_START_THRESH 0x00 /* * Window 6 registers. Statistics. */ /* Read/Write */ #define TX_TOTAL_OK 0x0c #define RX_TOTAL_OK 0x0a #define TX_DEFERRALS 0x08 #define RX_FRAMES_OK 0x07 #define TX_FRAMES_OK 0x06 #define RX_OVERRUNS 0x05 #define TX_COLLISIONS 0x04 #define TX_AFTER_1_COLLISION 0x03 #define TX_AFTER_X_COLLISIONS 0x02 #define TX_NO_SQE 0x01 #define TX_CD_LOST 0x00 /**************************************** * * Register definitions. * ****************************************/ /* * Command register. All windows. * * 16 bit register. * 15-11: 5-bit code for command to be executed. * 10-0: 11-bit arg if any. For commands with no args; * this can be set to anything. */ #define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms * after issuing */ #define WINDOW_SELECT (u_short) (0x1<<11) #define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to * determine whether * this is needed. If * so; wait 800 uSec * before using trans- * ceiver. */ #define RX_DISABLE (u_short) (0x3<<11) /* state disabled on * power-up */ #define RX_ENABLE (u_short) (0x4<<11) #define RX_RESET (u_short) (0x5<<11) #define RX_DISCARD_TOP_PACK (u_short) (0x8<<11) #define TX_ENABLE (u_short) (0x9<<11) #define TX_DISABLE (u_short) (0xa<<11) #define TX_RESET (u_short) (0xb<<11) #define REQ_INTR (u_short) (0xc<<11) #define SET_INTR_MASK (u_short) (0xe<<11) #define SET_RD_0_MASK (u_short) (0xf<<11) #define SET_RX_FILTER (u_short) (0x10<<11) #define FIL_INDIVIDUAL (u_short) (0x1) #define FIL_GROUP (u_short) (0x2) #define FIL_BRDCST (u_short) (0x4) #define FIL_ALL (u_short) (0x8) #define SET_RX_EARLY_THRESH (u_short) (0x11<<11) #define SET_TX_AVAIL_THRESH (u_short) (0x12<<11) #define SET_TX_START_THRESH (u_short) (0x13<<11) #define STATS_ENABLE (u_short) (0x15<<11) #define STATS_DISABLE (u_short) (0x16<<11) #define STOP_TRANSCEIVER (u_short) (0x17<<11) /* * The following C_* acknowledge the various interrupts. Some of them don't * do anything. See the manual. */ #define ACK_INTR (u_short) (0x6800) #define C_INTR_LATCH (u_short) (ACK_INTR|0x1) #define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) #define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) #define C_TX_AVAIL (u_short) (ACK_INTR|0x8) #define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) #define C_RX_EARLY (u_short) (ACK_INTR|0x20) #define C_INT_RQD (u_short) (ACK_INTR|0x40) #define C_UPD_STATS (u_short) (ACK_INTR|0x80) #define C_MASK (u_short) 0xFF /* mask of C_* */ /* * Status register. All windows. * * 15-13: Window number(0-7). * 12: Command_in_progress. * 11: reserved. * 10: reserved. * 9: reserved. * 8: reserved. * 7: Update Statistics. * 6: Interrupt Requested. * 5: RX Early. * 4: RX Complete. * 3: TX Available. * 2: TX Complete. * 1: Adapter Failure. * 0: Interrupt Latch. */ #define S_INTR_LATCH (u_short) (0x1) #define S_CARD_FAILURE (u_short) (0x2) #define S_TX_COMPLETE (u_short) (0x4) #define S_TX_AVAIL (u_short) (0x8) #define S_RX_COMPLETE (u_short) (0x10) #define S_RX_EARLY (u_short) (0x20) #define S_INT_RQD (u_short) (0x40) #define S_UPD_STATS (u_short) (0x80) #define S_MASK (u_short) 0xFF /* mask of S_* */ #define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\ S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY) #define S_COMMAND_IN_PROGRESS (u_short) (0x1000) /* Address Config. Register. * Window 0/Port 06 */ #define ACF_CONNECTOR_BITS 14 #define ACF_CONNECTOR_UTP 0 #define ACF_CONNECTOR_AUI 1 #define ACF_CONNECTOR_BNC 3 /* Resource configuration register. * Window 0/Port 08 * */ #define SET_IRQ(base,irq) outw((base) + EP_W0_RESOURCE_CFG, \ ((inw((base) + EP_W0_RESOURCE_CFG) & 0x0fff) | \ ((u_short)(irq)<<12)) ) /* set IRQ i */ /* * FIFO Registers. * RX Status. Window 1/Port 08 * * 15: Incomplete or FIFO empty. * 14: 1: Error in RX Packet 0: Incomplete or no error. * 13-11: Type of error. * 1000 = Overrun. * 1011 = Run Packet Error. * 1100 = Alignment Error. * 1101 = CRC Error. * 1001 = Oversize Packet Error (>1514 bytes) * 0010 = Dribble Bits. * (all other error codes, no errors.) * * 10-0: RX Bytes (0-1514) */ #define ERR_RX_INCOMPLETE (u_short) (0x1<<15) #define ERR_RX (u_short) (0x1<<14) #define ERR_RX_OVERRUN (u_short) (0x8<<11) #define ERR_RX_RUN_PKT (u_short) (0xb<<11) #define ERR_RX_ALIGN (u_short) (0xc<<11) #define ERR_RX_CRC (u_short) (0xd<<11) #define ERR_RX_OVERSIZE (u_short) (0x9<<11) #define ERR_RX_DRIBBLE (u_short) (0x2<<11) /* * FIFO Registers. * TX Status. Window 1/Port 0B * * Reports the transmit status of a completed transmission. Writing this * register pops the transmit completion stack. * * Window 1/Port 0x0b. * * 7: Complete * 6: Interrupt on successful transmission requested. * 5: Jabber Error (TP Only, TX Reset required. ) * 4: Underrun (TX Reset required. ) * 3: Maximum Collisions. * 2: TX Status Overflow. * 1-0: Undefined. * */ #define TXS_COMPLETE 0x80 #define TXS_SUCCES_INTR_REQ 0x40 #define TXS_JABBER 0x20 #define TXS_UNDERRUN 0x10 #define TXS_MAX_COLLISION 0x8 #define TXS_STATUS_OVERFLOW 0x4 /* * Configuration control register. * Window 0/Port 04 */ /* Read */ #define IS_AUI (1<<13) #define IS_BNC (1<<12) #define IS_UTP (1<<9) /* Write */ #define ENABLE_DRQ_IRQ 0x0001 #define W0_P4_CMD_RESET_ADAPTER 0x4 #define W0_P4_CMD_ENABLE_ADAPTER 0x1 /* * Media type and status. * Window 4/Port 0A */ #define ENABLE_UTP 0xc0 #define DISABLE_UTP 0x0 /* * Misc defines for various things. */ #define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */ #define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */ #define PROD_ID 0x9150 #define AUI 0x1 #define BNC 0x2 #define UTP 0x4 #define RX_BYTES_MASK (u_short) (0x07ff) extern struct ep_board ep_board[]; extern int ep_boards; extern u_long ep_unit; extern struct ep_softc *ep_alloc __P((int unit, struct ep_board *epb)); extern void ep_free __P((struct ep_softc *sc)); extern void ep_intr __P((void *sc)); extern int ep_attach __P((struct ep_softc *sc)); extern u_int16_t get_e __P((struct ep_softc *sc, int offset)); diff --git a/sys/i386/isa/if_zp.c b/sys/i386/isa/if_zp.c index be676c1d4eae..f4b1a7bd3c0e 100644 --- a/sys/i386/isa/if_zp.c +++ b/sys/i386/isa/if_zp.c @@ -1,1158 +1,1158 @@ /* * This code is based on * (1) FreeBSD implementation on ISA/EISA Ethelink III by Herb Peyerl * (2) Linux implementation on PCMCIA Etherlink III by Devid Hinds * (3) FreeBSD implementation on PCMCIA IBM Ethernet Card I/II * by David Greenman * (4) RT-Mach implementation on PCMCIA/ISA/EISA Etherlink III * by Seiji Murata * * Copyright (c) by HOSOKAWA, Tatsumi * Copyright (c) by Seiji Murata */ /* * Copyright (c) 1993 Herb Peyerl * All rights reserved. * * Redistribution 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, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $ - * $Id: if_zp.c,v 1.25 1996/09/07 17:52:56 nate Exp $ + * $Id: if_zp.c,v 1.26 1996/09/11 16:11:21 nate Exp $ */ /*- * TODO: * [1] integrate into current if_ed.c * [2] parse tuples to find out where to map the shared memory buffer, * and what to write into the configuration register * [3] move pcic-specific code into a separate module. * * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet, * if_ze.c * * Based on the Device driver for National Semiconductor DS8390 ethernet * adapters by David Greenman. Modifications for PCMCIA by Keith Moore. * Adapted for FreeBSD 1.1.5 by Jordan Hubbard. * * Currently supports only the IBM Credit Card Adapter for Ethernet, but * could probably work with other PCMCIA cards also, if it were modified * to get the locations of the PCMCIA configuration option register (COR) * by parsing the configuration tuples, rather than by hard-coding in * the value expected by IBM's card. * * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver: * * [1] _Local Area Network Credit Card Adapters Technical Reference_, * IBM Corp., SC30-3585-00, part # 33G9243. * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan. * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel * Order Number 290423-002 * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network * Interface Controller for Twisted Pair data sheet. * * * Copyright (C) 1993, David Greenman. This software may be used, modified, * copied, distributed, and sold, in both source and binary form provided * that the above copyright and these terms are retained. Under no * circumstances is the author responsible for the proper functioning * of this software, nor does the author assume any responsibility * for damages incurred with its use. */ /*====================================================================== A PCMCIA ethernet driver for the 3com 3c589 card. Written by David Hinds, dhinds@allegro.stanford.edu The network driver code is based on Donald Becker's 3c589 code: Written 1994 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. Donald Becker may be reached at becker@cesdis1.gsfc.nasa.gov ======================================================================*/ /* * I doubled delay loops in this file because it is not enough for some * laptop machines' PCIC (especially, on my Chaplet ILFA 350 ^^;). * HOSOKAWA, Tatsumi */ /* * Very small patch for IBM Ethernet PCMCIA Card II and IBM ThinkPad230Cs. * ETO, Toshihisa */ #include "zp.h" #include "bpfilter.h" #include #if defined(__FreeBSD__) #include #include #include #endif #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #ifdef IPX #include #include #endif #ifdef NS #include #include #endif #if NBPFILTER > 0 #include #include #endif #include #include #include #include #include #include "apm.h" #if NAPM > 0 #include #endif /* NAPM > 0 */ /***************************************************************************** * Driver for Ethernet Adapter * *****************************************************************************/ /* * zp_softc: per line info and status */ static struct zp_softc { struct arpcom arpcom; /* Ethernet common part */ #define MAX_MBS 8 /* # of mbufs we keep around */ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ int next_mb; /* Which mbuf to use next. */ int last_mb; /* Last mbuf. */ - short ep_io_addr; /* i/o bus address */ + int ep_io_addr; /* i/o bus address */ char ep_connectors; /* Connectors on this card. */ int tx_start_thresh;/* Current TX_start_thresh. */ char bus32bit; /* 32bit access possible */ u_short if_port; u_char last_alive; /* information for reconfiguration */ u_char last_up; /* information for reconfiguration */ int slot; /* PCMCIA slot */ #if NAPM > 0 struct apmhook s_hook; /* reconfiguration support */ struct apmhook r_hook; /* reconfiguration support */ #endif /* NAPM > 0 */ } zp_softc[NZP]; static int zpprobe __P((struct isa_device *)); static int zpattach __P((struct isa_device *)); static int zp_suspend __P((void *visa_dev)); static int zp_resume __P((void *visa_dev)); static int zpioctl __P((struct ifnet * ifp, int, caddr_t)); static u_short read_eeprom_data __P((int, int)); static void zpinit __P((int)); static void zpmbuffill __P((void *)); static void zpmbufempty __P((struct zp_softc *)); static void zpread __P((struct zp_softc *)); static void zpreset __P((int)); static void zpstart __P((struct ifnet *)); static void zpstop __P((int)); static void zpwatchdog __P((struct ifnet *)); struct isa_driver zpdriver = { zpprobe, zpattach, "zp" }; #define CARD_INFO "3Com Corporation~3C589" static unsigned char card_info[256]; /* * scan the card information structure looking for the version/product info * tuple. when we find it, compare it to the string we are looking for. * return 1 if we find it, 0 otherwise. */ static int zp_check_cis(unsigned char *scratch) { int i, j, k; card_info[0] = '\0'; i = 0; while (scratch[i] != 0xff && i < 1024) { unsigned char link = scratch[i + 2]; if (scratch[i] == 0x15) { /* level 1 version/product info copy to card_info, * translating '\0' to '~' */ k = 0; for (j = i + 8; scratch[j] != 0xff; j += 2) card_info[k++] = scratch[j] == '\0' ? '~' : scratch[j]; card_info[k++] = '\0'; return (bcmp(card_info, CARD_INFO, sizeof(CARD_INFO) - 1) == 0); } i += 4 + 2 * link; } return 0; } /* * Probe each slot looking for an IBM Credit Card Adapter for Ethernet * For each card that we find, map its card information structure * into system memory at 'scratch' and see whether it's one of ours. * Return the slot number if we find a card, or -1 otherwise. * * Side effects: * + On success, leaves CIS mapped into memory at 'scratch'; * caller must free it. * + On success, leaves ethernet address in enet_addr. * + Leaves product/vendor id of last card probed in 'card_info' */ static int prev_slot = 0; static int zp_find_adapter(unsigned char *scratch, int reconfig) { int slot; for (slot = prev_slot; slot < MAXSLOT; ++slot) { /* see if there's a PCMCIA controller here Intel PCMCIA * controllers use 0x82 and 0x83 IBM clone chips use 0x88 and * 0x89, apparently */ /* IBM ThinkPad230Cs use 0x84. */ unsigned char idbyte = pcic_getb(slot, PCIC_ID_REV); if (idbyte != 0x82 && idbyte != 0x83 && idbyte != 0x84 && /* for IBM ThinkPad 230Cs */ idbyte != 0x88 && idbyte != 0x89) { continue; } if ((pcic_getb(slot, PCIC_STATUS) & PCIC_CD) != PCIC_CD) { if (!reconfig) { printf("zp: slot %d: no card in slot\n", slot); } else { log(LOG_NOTICE, "zp: slot %d: no card in slot\n", slot); } /* no card in slot */ continue; } pcic_power_on(slot); pcic_reset(slot); /* map the card's attribute memory and examine its card * information structure tuples for something we recognize. */ pcic_map_memory(slot, 0, kvtop(scratch), 0L, 0xFFFL, ATTRIBUTE, 1); if ((zp_check_cis(scratch)) > 0) { /* found it */ if (!reconfig) { printf("zp: found card in slot %d\n", slot); } else { log(LOG_NOTICE, "zp: found card in slot %d\n", slot); } prev_slot = (prev_slot == MAXSLOT - 1) ? 0 : prev_slot + 1; return slot; } else { if (!reconfig) { printf("zp: pcmcia slot %d: %s\n", slot, card_info); } else { log(LOG_NOTICE, "zp: pcmcia slot %d: %s\n", slot, card_info); } } pcic_unmap_memory(slot, 0); } prev_slot = 0; return -1; } /* * macros to handle casting unsigned long to (char *) so we can * read/write into physical memory space. */ #define PEEK(addr) (*((unsigned char *)(addr))) #define POKE(addr,val) do { PEEK(addr) = (val); } while (0) /* * Determine if the device is present * * on entry: * a pointer to an isa_device struct * on exit: * NULL if device not found * or # of i/o addresses used (if found) */ static int zpprobe(struct isa_device * isa_dev) { struct zp_softc *sc = &zp_softc[isa_dev->id_unit]; int slot; u_short k; int re_init_flag; if ((slot = zp_find_adapter(isa_dev->id_maddr, isa_dev->id_reconfig)) < 0) return 0; /* okay, we found a card, so set it up */ /* Inhibit 16 bit memory delay. POINTETH.SYS apparently does this, for * what reason I don't know. */ pcic_putb(slot, PCIC_CDGC, pcic_getb(slot, PCIC_CDGC) | PCIC_16_DL_INH); /* things to map (1) card's EEPROM is already mapped by the * find_adapter routine but we still need to get the card's ethernet * address. after that we unmap that part of attribute memory. (2) * card configuration registers need to be mapped in so we can set the * configuration and socket # registers. (3) shared memory packet * buffer (4) i/o ports (5) IRQ */ #ifdef notdef /* Sigh. Location of the ethernet address isn't documented in [1]. It * was derived by doing a hex dump of all of attribute memory and * looking for the IBM vendor prefix. */ enet_addr[0] = PEEK(isa_dev->id_maddr + 0xff0); enet_addr[1] = PEEK(isa_dev->id_maddr + 0xff2); enet_addr[2] = PEEK(isa_dev->id_maddr + 0xff4); enet_addr[3] = PEEK(isa_dev->id_maddr + 0xff6); enet_addr[4] = PEEK(isa_dev->id_maddr + 0xff8); enet_addr[5] = PEEK(isa_dev->id_maddr + 0xffa); #endif re_init_flag = 0; re_init: /* (2) map card configuration registers. these are offset in card * memory space by 0x20000. normally we could get this offset from * the card information structure, but I'm too lazy and am not quite * sure if I understand the CIS anyway. * * XXX IF YOU'RE TRYING TO PORT THIS DRIVER FOR A DIFFERENT PCMCIA CARD, * the most likely thing to change is the constant 0x20000 in the next * statement. Oh yes, also change the card id string that we probe * for. */ pcic_map_memory(slot, 0, kvtop(isa_dev->id_maddr), 0x10000, 8L, ATTRIBUTE, 1); #if OLD_3C589B_CARDS POKE(isa_dev->id_maddr, 0x80); /* reset the card (how long?) */ DELAY(40000); #endif /* Set the configuration index. According to [1], the adapter won't * respond to any i/o signals until we do this; it uses the Memory * Only interface (whatever that is; it's not documented). Also turn * on "level" (not pulse) interrupts. * * XXX probably should init the socket and copy register also, so that we * can deal with multiple instances of the same card. */ POKE(isa_dev->id_maddr, 0x41); pcic_unmap_memory(slot, 0); /* (4) map i/o ports. * * XXX is it possible that the config file leaves this unspecified, in * which case we have to pick one? * * At least one PCMCIA device driver I'v seen maps a block of 32 * consecutive i/o ports as two windows of 16 ports each. Maybe some * other pcic chips are restricted to 16-port windows; the 82365SL * doesn't seem to have that problem. But since we have an extra * window anyway... */ pcic_map_io(slot, 0, isa_dev->id_iobase, 16, 2); /* (5) configure the card for the desired interrupt * * XXX is it possible that the config file leaves this unspecified? */ pcic_map_irq(slot, ffs(isa_dev->id_irq) - 1); /* tell the PCIC that this is an I/O card (not memory) */ pcic_putb(slot, PCIC_INT_GEN, pcic_getb(slot, PCIC_INT_GEN) | PCIC_CARDTYPE); sc->ep_io_addr = isa_dev->id_iobase; GO_WINDOW(0); k = read_eeprom_data(BASE, EEPROM_ADDR_CFG); /* get addr cfg */ sc->if_port = k >> 14; k = (k & 0x1f) * 0x10 + 0x200; /* decode base addr. */ if (k != (u_short) isa_dev->id_iobase) { if (!re_init_flag) { re_init_flag++; goto re_init; } return (0); } k = read_eeprom_data(BASE, EEPROM_RESOURCE_CFG); k >>= 12; if (isa_dev->id_irq != (1 << ((k == 2) ? 9 : k))) return (0); outb(BASE, ACTIVATE_ADAPTER_TO_CONFIG); /* information for reconfiguration */ sc->last_alive = 0; sc->last_up = 0; sc->slot = slot; return (0x10); /* 16 bytes of I/O space used. */ } #if NAPM > 0 static int zp_suspend(visa_dev) void *visa_dev; { #if 0 struct isa_device *isa_dev = visa_dev; struct zp_softc *sc = &zp_softc[isa_dev->id_unit]; pcic_power_off(sc->slot); #endif return 0; } static int zp_resume(visa_dev) void *visa_dev; { struct isa_device *isa_dev = visa_dev; prev_slot = 0; reconfig_isadev(isa_dev, &net_imask); return 0; } #endif /* NAPM > 0 */ /* * Install interface into kernel networking data structures */ static int zpattach(isa_dev) struct isa_device *isa_dev; { struct zp_softc *sc = &zp_softc[isa_dev->id_unit]; struct ifnet *ifp = &sc->arpcom.ac_if; u_short i; int pl; /* PCMCIA card can be offlined. Reconfiguration is required */ if (isa_dev->id_reconfig) { if (!isa_dev->id_alive && sc->last_alive) { pl = splimp(); sc->last_up = (ifp->if_flags & IFF_UP); if_down(ifp); splx(pl); sc->last_alive = 0; } if (isa_dev->id_alive && !sc->last_alive) { zpreset(isa_dev->id_unit); if (sc->last_up) { pl = splimp(); if_up(ifp); splx(pl); } sc->last_alive = 1; } return 1; } else { sc->last_alive = 1; } sc->ep_io_addr = isa_dev->id_iobase; printf("zp%d: ", isa_dev->id_unit); sc->ep_connectors = 0; i = inw(isa_dev->id_iobase + EP_W0_CONFIG_CTRL); if (i & IS_AUI) { printf("aui"); sc->ep_connectors |= AUI; } if (i & IS_BNC) { if (sc->ep_connectors) printf("/"); printf("bnc"); sc->ep_connectors |= BNC; } if (i & IS_UTP) { if (sc->ep_connectors) printf("/"); printf("utp"); sc->ep_connectors |= UTP; } if (!sc->ep_connectors) printf("no connectors!"); GO_WINDOW(0); { short tmp_addr[3]; int i; for (i = 0; i < 3; i++) { tmp_addr[i] = htons(read_eeprom_data(BASE, i)); } bcopy(tmp_addr, sc->arpcom.ac_enaddr, 6); } printf(" address %6D\n", sc->arpcom.ac_enaddr, ":"); ifp->if_softc = sc; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; ifp->if_unit = isa_dev->id_unit; ifp->if_name = "zp"; ifp->if_output = ether_output; ifp->if_start = zpstart; ifp->if_ioctl = zpioctl; ifp->if_watchdog = zpwatchdog; /* Select connector according to board setting. */ ifp->if_flags |= IFF_LINK0; if_attach(ifp); ether_ifattach(ifp); #if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif #if NAPM > 0 sc->s_hook.ah_fun = zp_suspend; sc->s_hook.ah_arg = (void *) isa_dev; sc->s_hook.ah_name = "3Com PCMCIA Etherlink III 3C589"; sc->s_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_SUSPEND, &sc->s_hook); sc->r_hook.ah_fun = zp_resume; sc->r_hook.ah_arg = (void *) isa_dev; sc->r_hook.ah_name = "3Com PCMCIA Etherlink III 3C589"; sc->r_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME, &sc->r_hook); #endif /* NAPM > 0 */ return 1; } /* * The order in here seems important. Otherwise we may not receive * interrupts. ?! */ static void zpinit(unit) int unit; { register struct zp_softc *sc = &zp_softc[unit]; register struct ifnet *ifp = &sc->arpcom.ac_if; int s, i; if (ifp->if_addrlist == (struct ifaddr *) 0) return; s = splimp(); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); GO_WINDOW(0); /* Disable the card */ outw(BASE + EP_W0_CONFIG_CTRL, 0); /* Enable the card */ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); GO_WINDOW(2); /* Reload the ether_addr. */ for (i = 0; i < 6; i++) outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); outw(BASE + EP_COMMAND, RX_RESET); outw(BASE + EP_COMMAND, TX_RESET); /* Window 1 is operating window */ GO_WINDOW(1); for (i = 0; i < 31; i++) inb(BASE + EP_W1_TX_STATUS); /* get rid of stray intr's */ outw(BASE + EP_COMMAND, ACK_INTR | 0xff); outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); #ifndef IFF_MULTICAST #define IFF_MULTICAST 0x10000 #endif outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | ((sc->arpcom.ac_if.if_flags & IFF_MULTICAST) ? FIL_GROUP : 0) | FIL_BRDCST | ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) ? FIL_ALL : 0)); /* you can `ifconfig (link0|-link0) ep0' to get the following * behaviour: -link0 disable AUI/UTP. enable BNC. link0 disable * BNC. enable AUI. if the card has a UTP connector, that is enabled * too. not sure, but it seems you have to be careful to not plug * things into both AUI & UTP. */ if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) { GO_WINDOW(0); /* set the xcvr */ outw(BASE + EP_W0_ADDRESS_CFG, 3 << 14); GO_WINDOW(2); outw(BASE + EP_COMMAND, START_TRANSCEIVER); GO_WINDOW(1); } #if defined(__NetBSD__) || defined(__FreeBSD__) if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) { #else if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) { #endif GO_WINDOW(4); outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); GO_WINDOW(1); } outw(BASE + EP_COMMAND, RX_ENABLE); outw(BASE + EP_COMMAND, TX_ENABLE); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ sc->tx_start_thresh = 20; /* probably a good starting point. */ /* Store up a bunch of mbuf's for use later. (MAX_MBS). First we free * up any that we had in case we're being called from intr or * somewhere else. */ sc->last_mb = 0; sc->next_mb = 0; zpmbuffill(sc); zpstart(ifp); splx(s); } static const char padmap[] = {0, 3, 2, 1}; static void zpstart(ifp) struct ifnet *ifp; { register struct zp_softc *sc = ifp->if_softc; struct mbuf *m, *top; int s, len, pad; s = splimp(); if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { splx(s); return; } startagain: /* Sneak a peek at the next packet */ m = sc->arpcom.ac_if.if_snd.ifq_head; if (m == 0) { splx(s); return; } for (len = 0, top = m; m; m = m->m_next) len += m->m_len; pad = padmap[len & 3]; /* The 3c509 automatically pads short packets to minimum ethernet * length, but we drop packets that are too large. Perhaps we should * truncate them instead? */ if (len + pad > ETHER_MAX_LEN) { /* packet is obviously too large: toss it */ ++sc->arpcom.ac_if.if_oerrors; IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); m_freem(m); goto readcheck; } if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { /* no room in FIFO */ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; splx(s); return; } IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); if (m == 0) { /* not really needed */ splx(s); return; } outw(BASE + EP_COMMAND, SET_TX_START_THRESH | (len / 4 + sc->tx_start_thresh)); outw(BASE + EP_W1_TX_PIO_WR_1, len); outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */ for (top = m; m != 0; m = m->m_next) { if (sc->bus32bit) { outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 4); if (m->m_len & 3) outsb(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t) + (m->m_len & (~3)), m->m_len & 3); } else { outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2); if (m->m_len & 1) outb(BASE + EP_W1_TX_PIO_WR_1, *(mtod(m, caddr_t) + m->m_len - 1)); } } while (pad--) outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ #if NBPFILTER > 0 if (sc->arpcom.ac_if.if_bpf) { bpf_mtap(&sc->arpcom.ac_if, top); } #endif m_freem(top); ++sc->arpcom.ac_if.if_opackets; /* Is another packet coming in? We don't want to overflow the tiny RX * fifo. */ readcheck: if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { splx(s); return; } goto startagain; } void zpintr(unit) int unit; { int status, i; register struct zp_softc *sc = &zp_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; status = 0; checkintr: status = inw(BASE + EP_STATUS) & (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE); checkintr2: if (status == 0) { /* No interrupts. */ outw(BASE + EP_COMMAND, C_INTR_LATCH); status = inw(BASE + EP_STATUS) & (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE); if (status) goto checkintr2; return; } /* important that we do this first. */ outw(BASE + EP_COMMAND, ACK_INTR | status); if (status & S_TX_AVAIL) { status &= ~S_TX_AVAIL; inw(BASE + EP_W1_FREE_TX); sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; zpstart(&sc->arpcom.ac_if); } if (status & S_RX_COMPLETE) { status &= ~S_RX_COMPLETE; zpread(sc); } if (status & S_CARD_FAILURE) { printf("zp%d: reset (status: %x)\n", unit, status); outw(BASE + EP_COMMAND, C_INTR_LATCH); zpinit(unit); return; } if (status & S_TX_COMPLETE) { status &= ~S_TX_COMPLETE; /* We need to read TX_STATUS until we get a 0 status in order * to turn off the interrupt flag. */ while ((i = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { outw(BASE + EP_W1_TX_STATUS, 0x0); if (i & (TXS_MAX_COLLISION | TXS_JABBER | TXS_UNDERRUN)) { if (i & TXS_MAX_COLLISION) ++sc->arpcom.ac_if.if_collisions; if (i & (TXS_JABBER | TXS_UNDERRUN)) { outw(BASE + EP_COMMAND, TX_RESET); if (i & TXS_UNDERRUN) { if (sc->tx_start_thresh < ETHER_MAX_LEN) { sc->tx_start_thresh += 20; outw(BASE + EP_COMMAND, SET_TX_START_THRESH | sc->tx_start_thresh); } } } outw(BASE + EP_COMMAND, TX_ENABLE); ++sc->arpcom.ac_if.if_oerrors; } } zpstart(ifp); } goto checkintr; } static void zpread(sc) register struct zp_softc *sc; { struct ether_header *eh; struct mbuf *mcur, *m, *m0, *top; int totlen, lenthisone; int save_totlen; int off; totlen = inw(BASE + EP_W1_RX_STATUS); off = 0; top = 0; if (totlen & ERR_RX) { ++sc->arpcom.ac_if.if_ierrors; goto out; } save_totlen = totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ m = sc->mb[sc->next_mb]; sc->mb[sc->next_mb] = 0; if (m == 0) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == 0) goto out; } else { /* Convert one of our saved mbuf's */ sc->next_mb = (sc->next_mb + 1) % MAX_MBS; m->m_data = m->m_pktdat; m->m_flags = M_PKTHDR; } top = m0 = m; /* We assign top so we can "goto out" */ #define EROUND ((sizeof(struct ether_header) + 3) & ~3) #define EOFF (EROUND - sizeof(struct ether_header)) m0->m_data += EOFF; /* Read what should be the header. */ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m0, caddr_t), sizeof(struct ether_header) / 2); m->m_len = sizeof(struct ether_header); totlen -= sizeof(struct ether_header); /* mostly deal with trailer here. (untested) We do this in a couple * of parts. First we check for a trailer, if we have one we convert * the mbuf back to a regular mbuf and set the offset and subtract * sizeof(struct ether_header) from the pktlen. After we've read the * packet off the interface (all except for the trailer header, we * then get a header mbuf, read the trailer into it, and fix up the * mbuf pointer chain. */ eh = mtod(m, struct ether_header *); while (totlen > 0) { lenthisone = min(totlen, M_TRAILINGSPACE(m)); if (lenthisone == 0) { /* no room in this one */ mcur = m; m = sc->mb[sc->next_mb]; sc->mb[sc->next_mb] = 0; if (!m) { MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) goto out; } else { timeout(zpmbuffill, sc, 0); sc->next_mb = (sc->next_mb + 1) % MAX_MBS; } if (totlen >= MINCLSIZE) MCLGET(m, M_DONTWAIT); m->m_len = 0; mcur->m_next = m; lenthisone = min(totlen, M_TRAILINGSPACE(m)); } if (sc->bus32bit) { insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 4); m->m_len += (lenthisone & ~3); if (lenthisone & 3) insb(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone & 3); m->m_len += (lenthisone & 3); } else { insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 2); m->m_len += lenthisone; if (lenthisone & 1) *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); } totlen -= lenthisone; } if (off) { top = sc->mb[sc->next_mb]; sc->mb[sc->next_mb] = 0; if (top == 0) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (top == 0) { top = m0; goto out; } } else { /* Convert one of our saved mbuf's */ sc->next_mb = (sc->next_mb + 1) % MAX_MBS; top->m_data = top->m_pktdat; top->m_flags = M_PKTHDR; } insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), sizeof(struct ether_header)); top->m_next = m0; top->m_len = sizeof(struct ether_header); /* XXX Accomodate for type and len from beginning of trailer */ top->m_pkthdr.len = save_totlen - (2 * sizeof(u_short)); } else { top = m0; top->m_pkthdr.len = save_totlen; } top->m_pkthdr.rcvif = &sc->arpcom.ac_if; outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); ++sc->arpcom.ac_if.if_ipackets; #if NBPFILTER > 0 if (sc->arpcom.ac_if.if_bpf) { bpf_mtap(&sc->arpcom.ac_if, top); /* Note that the interface cannot be in promiscuous mode if * there are no BPF listeners. And if we are in promiscuous * mode, we have to check if this packet is really ours. */ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && (eh->ether_dhost[0] & 1) == 0 && bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 && bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { m_freem(top); return; } } #endif m_adj(top, sizeof(struct ether_header)); ether_input(&sc->arpcom.ac_if, eh, top); return; out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); if (top) m_freem(top); } /* * Look familiar? */ static int zpioctl(ifp, cmd, data) register struct ifnet *ifp; int cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *) data; struct zp_softc *sc = ifp->if_softc; int error = 0; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: zpinit(ifp->if_unit); /* before arpwhohas */ arp_ifinit((struct arpcom *) ifp, ifa); break; #endif #ifdef IPX case AF_IPX: { register struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); if (ipx_nullhost(*ina)) ina->x_host = *(union ipx_host *) (sc->arpcom.ac_enaddr); else { ifp->if_flags &= ~IFF_RUNNING; bcopy((caddr_t) ina->x_host.c_host, (caddr_t) sc->arpcom.ac_enaddr, sizeof(sc->arpcom.ac_enaddr)); } zpinit(ifp->if_unit); break; } #endif #ifdef NS case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *) (sc->arpcom.ac_enaddr); else { ifp->if_flags &= ~IFF_RUNNING; bcopy((caddr_t) ina->x_host.c_host, (caddr_t) sc->arpcom.ac_enaddr, sizeof(sc->arpcom.ac_enaddr)); } zpinit(ifp->if_unit); break; } #endif default: zpinit(ifp->if_unit); break; } break; case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { ifp->if_flags &= ~IFF_RUNNING; zpstop(ifp->if_unit); zpmbufempty(sc); break; } zpinit(ifp->if_unit); break; default: error = EINVAL; } return (error); } static void zpreset(unit) int unit; { int s = splimp(); zpstop(unit); zpinit(unit); splx(s); } static void zpwatchdog(ifp) struct ifnet *ifp; { log(LOG_ERR, "zp%d: watchdog\n", ifp->if_unit); ifp->if_oerrors++; zpreset(ifp->if_unit); } static void zpstop(unit) int unit; { struct zp_softc *sc = &zp_softc[unit]; outw(BASE + EP_COMMAND, RX_DISABLE); outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); outw(BASE + EP_COMMAND, TX_DISABLE); outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); outw(BASE + EP_COMMAND, RX_RESET); outw(BASE + EP_COMMAND, TX_RESET); outw(BASE + EP_COMMAND, C_INTR_LATCH); outw(BASE + EP_COMMAND, SET_RD_0_MASK); outw(BASE + EP_COMMAND, SET_INTR_MASK); outw(BASE + EP_COMMAND, SET_RX_FILTER); } static u_short read_eeprom_data(id_port, offset) int id_port; int offset; { outb(id_port + 10, 0x80 + offset); DELAY(1000); return inw(id_port + 12); } static void zpmbuffill(sp) void *sp; { struct zp_softc *sc = (struct zp_softc *) sp; int s, i; s = splimp(); i = sc->last_mb; do { if (sc->mb[i] == NULL) MGET(sc->mb[i], M_DONTWAIT, MT_DATA); if (sc->mb[i] == NULL) break; i = (i + 1) % MAX_MBS; } while (i != sc->next_mb); sc->last_mb = i; splx(s); } static void zpmbufempty(sc) struct zp_softc *sc; { int s, i; s = splimp(); for (i = 0; i < MAX_MBS; i++) { if (sc->mb[i]) { m_freem(sc->mb[i]); sc->mb[i] = NULL; } } sc->last_mb = sc->next_mb = 0; untimeout(zpmbuffill, sc); splx(s); }