⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i82586.c

📁 i386的bootloader源码grub
💻 C
📖 第 1 页 / 共 2 页
字号:
/**************************************************************************Etherboot -  BOOTP/TFTP Bootstrap Programi82586 NIC driver for EtherbootKen Yap, January 1998***************************************************************************//* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. */#include "etherboot.h"#include "nic.h"#include "cards.h"#include "timer.h"#define	udelay(n)	waiton_timer2(((n)*TICKS_PER_MS)/1000)/* Sources of information:   Donald Becker's excellent 3c507 driver in Linux   Intel 82596 data sheet (yes, 82596; it has a 586 compatibility mode)*//* Code below mostly stolen wholesale from 3c507.c driver in Linux *//*		Details of the i82586.   You'll really need the databook to understand the details of this part,   but the outline is that the i82586 has two separate processing units.   Both are started from a list of three configuration tables, of which only   the last, the System Control Block (SCB), is used after reset-time.  The SCB   has the following fields:		Status word		Command word		Tx/Command block addr.		Rx block addr.   The command word accepts the following controls for the Tx and Rx units:  */#define	CUC_START	0x0100#define	CUC_RESUME	0x0200#define	CUC_SUSPEND	0x0300#define	RX_START	0x0010#define	RX_RESUME	0x0020#define	RX_SUSPEND	0x0030/* The Rx unit uses a list of frame descriptors and a list of data buffer   descriptors.  We use full-sized (1518 byte) data buffers, so there is   a one-to-one pairing of frame descriptors to buffer descriptors.   The Tx ("command") unit executes a list of commands that look like:	Status word	Written by the 82586 when the command is done.	Command word	Command in lower 3 bits, post-command action in upper 3	Link word	The address of the next command.	Parameters	(as needed).	Some definitions related to the Command Word are: */#define CMD_EOL		0x8000		/* The last command of the list, stop. */#define CMD_SUSP	0x4000		/* Suspend after doing cmd. */#define CMD_INTR	0x2000		/* Interrupt after doing cmd. */enum commands {	CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};/*		Details of the EtherLink16 Implementation  The 3c507 and NI5210 are generic shared-memory i82586 implementations.  3c507: The host can map 16K, 32K, 48K, or 64K of the 64K memory into  0x0[CD][08]0000, or all 64K into 0xF[02468]0000.  NI5210: The host can map 8k or 16k at 0x[CDE][048C]000 but we  assume 8k because to have 16k you cannot put a ROM on the NIC.  *//* Offsets from the base I/O address. */#ifdef	INCLUDE_3C507#define	SA_DATA		0	/* Station address data, or 3Com signature. */#define MISC_CTRL	6	/* Switch the SA_DATA banks, and bus config bits. */#define RESET_IRQ	10	/* Reset the latched IRQ line. */#define I82586_ATTN	11	/* Frob the 82586 Channel Attention line. */#define ROM_CONFIG	13#define MEM_CONFIG	14#define IRQ_CONFIG	15#define EL16_IO_EXTENT	16/* The ID port is used at boot-time to locate the ethercard. */#define ID_PORT		0x100#endif#ifdef	INCLUDE_NI5210#define	NI52_RESET	0  /* writing to this address, resets the i82586 */#define	I82586_ATTN	1  /* channel attention, kick the 586 */#endif#ifdef	INCLUDE_EXOS205#define	EXOS205_RESET	0  /* writing to this address, resets the i82586 */#define	I82586_ATTN	1  /* channel attention, kick the 586 */#endif/* Offsets to registers in the mailbox (SCB). */#define iSCB_STATUS	0x8#define iSCB_CMD	0xA#define iSCB_CBL	0xC	/* Command BLock offset. */#define iSCB_RFA	0xE	/* Rx Frame Area offset. *//*  Since the 3c507 maps the shared memory window so that the last byte isat 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.We can account for this be setting the 'SBC Base' entry in the ISCP tablebelow for all the 16 bit offset addresses, and also adding the 'SCB Base'value to all 24 bit physical addresses (in the SCP table and the TX and RXBuffer Descriptors).				-Mark*//*  What follows in 'init_words[]' is the "program" that is downloaded to the  82586 memory.  It's mostly tables and command blocks, and starts at the  reset address 0xfffff6.  This is designed to be similar to the EtherExpress,  thus the unusual location of the SCB at 0x0008.  Even with the additional "don't care" values, doing it this way takes less  program space than initializing the individual tables, and I feel it's much  cleaner.  The databook is particularly useless for the first two structures, I had  to use the Crynwr driver as an example.  The memory setup is as follows:*/#define CONFIG_CMD	0x18#define SET_SA_CMD	0x24#define SA_OFFSET	0x2A#define IDLELOOP	0x30#define TDR_CMD		0x38#define TDR_TIME	0x3C#define DUMP_CMD	0x40#define DIAG_CMD	0x48#define SET_MC_CMD	0x4E#define DUMP_DATA	0x56	/* A 170 byte buffer for dump and Set-MC into. */#define TX_BUF_START	0x0100#define TX_BUF_SIZE	(1518+14+20+16) /* packet+header+TBD */#define RX_BUF_START	0x1000#define RX_BUF_SIZE	(1518+14+18)	/* packet+header+RBD */#define RX_BUF_END	(mem_end - mem_start - 20)/*  That's it: only 86 bytes to set up the beast, including every extra  command available.  The 170 byte buffer at DUMP_DATA is shared between the  Dump command (called only by the diagnostic program) and the SetMulticastList  command.  To complete the memory setup you only have to write the station address at  SA_OFFSET and create the Tx & Rx buffer lists.  The Tx command chain and buffer list is setup as follows:  A Tx command table, with the data buffer pointing to...  A Tx data buffer descriptor.  The packet is in a single buffer, rather than	chaining together several smaller buffers.  A NoOp command, which initially points to itself,  And the packet data.  A transmit is done by filling in the Tx command table and data buffer,  re-writing the NoOp command, and finally changing the offset of the last  command to point to the current Tx command.  When the Tx command is finished,  it jumps to the NoOp, when it loops until the next Tx command changes the  "link offset" in the NoOp.  This way the 82586 never has to go through the  slow restart sequence.  The Rx buffer list is set up in the obvious ring structure.  We have enough  memory (and low enough interrupt latency) that we can avoid the complicated  Rx buffer linked lists by alway associating a full-size Rx data buffer with  each Rx data frame.  I currently use one transmit buffer starting at TX_BUF_START (0x0100), and  use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.  */static unsigned short init_words[] = {	/*	System Configuration Pointer (SCP). */#if	defined(INCLUDE_3C507)	0x0000,					/* Set bus size to 16 bits. */#else	0x0001,					/* Set bus size to 8 bits */#endif	0,0,					/* pad words. */	0x0000,0x0000,				/* ISCP phys addr, set in init_82586_mem(). */	/*	Intermediate System Configuration Pointer (ISCP). */	0x0001,					/* Status word that's cleared when init is done. */	0x0008,0,0,				/* SCB offset, (skip, skip) */	/* System Control Block (SCB). */	0,0xf000|RX_START|CUC_START,		/* SCB status and cmd. */	CONFIG_CMD,				/* Command list pointer, points to Configure. */	RX_BUF_START,				/* Rx block list. */	0,0,0,0,				/* Error count: CRC, align, buffer, overrun. */	/* 0x0018: Configure command.  Change to put MAC data with packet. */	0, CmdConfigure,			/* Status, command. */	SET_SA_CMD,				/* Next command is Set Station Addr. */	0x0804,					/* "4" bytes of config data, 8 byte FIFO. */	0x2e40,					/* Magic values, including MAC data location. */	0,					/* Unused pad word. */	/* 0x0024: Setup station address command. */	0, CmdSASetup,	SET_MC_CMD,				/* Next command. */	0xaa00,0xb000,0x0bad,	/* Station address (to be filled in) */	/* 0x0030: NOP, looping back to itself.  Point to first Tx buffer to Tx. */	0, CmdNOp, IDLELOOP, 0 /* pad */,	/* 0x0038: A unused Time-Domain Reflectometer command. */	0, CmdTDR, IDLELOOP, 0,	/* 0x0040: An unused Dump State command. */	0, CmdDump, IDLELOOP, DUMP_DATA,	/* 0x0048: An unused Diagnose command. */	0, CmdDiagnose, IDLELOOP,	/* 0x004E: An empty set-multicast-list command. */	0, CmdMulticastList, IDLELOOP, 0,};/* NIC specific static variables go here */static unsigned short		ioaddr, irq, scb_base;static Address			mem_start, mem_end;static unsigned short		rx_head, rx_tail;#define	read_mem(m,s)	fmemcpy((char *)s, m, sizeof(s))static void setup_rx_buffers(struct nic *nic){	Address			write_ptr;	unsigned short		cur_rx_buf;	static unsigned short	rx_cmd[16] = {		0x0000,			/* Rx status */		0x0000,			/* Rx command, only and last */		RX_BUF_START,		/* Link (will be adjusted) */		RX_BUF_START + 22,	/* Buffer offset (will be adjusted) */		0x0000, 0x0000, 0x0000,	/* Pad for dest addr */		0x0000, 0x0000, 0x0000,	/* Pad for source addr */		0x0000,			/* Pad for protocol */		0x0000,			/* Buffer: Actual count */		-1,			/* Buffer: Next (none) */		RX_BUF_START + 0x20,	/* Buffer: Address low (+ scb_base) (will be adjusted) */		0x0000,			/* Buffer: Address high */		0x8000 | (RX_BUF_SIZE - 0x20)	};	cur_rx_buf = rx_head = RX_BUF_START;	do {		/* While there is room for one more buffer */		write_ptr = mem_start + cur_rx_buf;		/* adjust some contents */		rx_cmd[1] = 0x0000;		rx_cmd[2] = cur_rx_buf + RX_BUF_SIZE;		rx_cmd[3] = cur_rx_buf + 22;		rx_cmd[13] = cur_rx_buf + 0x20 + scb_base;		memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(rx_cmd));		rx_tail = cur_rx_buf;		cur_rx_buf += RX_BUF_SIZE;	} while (cur_rx_buf <= RX_BUF_END - RX_BUF_SIZE);	/* Terminate the list by setting the EOL bit and wrap ther pointer	   to make the list a ring. */	write_ptr = mem_start + rx_tail;	rx_cmd[1] = 0xC000;	rx_cmd[2] = rx_head;	memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(unsigned short) * 3);}static void ack_status(void){	unsigned short	cmd, status;	unsigned short	*shmem = (short *)mem_start;	cmd = (status = shmem[iSCB_STATUS>>1]) & 0xf000;	if (status & 0x100)		/* CU suspended? */		cmd |= CUC_RESUME;	if ((status & 0x200) == 0)	/* CU not active? */		cmd |= CUC_START;	if (status & 0x010)		/* RU suspended? */		cmd |= RX_RESUME;	else if ((status & 0x040) == 0)	/* RU not active? */		cmd |= RX_START;	if (cmd == 0)			/* Nothing to do */		return;	shmem[iSCB_CMD>>1] = cmd;#if	defined(DEBUG)	printf("Status %hX Command %hX\n", status, cmd);#endif	outb(0, ioaddr + I82586_ATTN);}/**************************************************************************RESET - Reset adapter***************************************************************************/static void i82586_reset(struct nic *nic){	unsigned long	time;	unsigned short	*shmem = (short *)mem_start;	/* put the card in its initial state */#ifdef	INCLUDE_3C507	/* Enable loopback to protect the wire while starting up,	   and hold the 586 in reset during the memory initialisation. */	outb(0x20, ioaddr + MISC_CTRL);#endif	/* Fix the ISCP address and base. */	init_words[3] = scb_base;	init_words[7] = scb_base;	/* Write the words at 0xfff6. */	/* Write the words at 0x0000. */	/* Fill in the station address. */	memcpy((char *)(mem_end - 10), (char *)init_words, 10);	memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10);	memcpy((char *)mem_start + SA_OFFSET, nic->node_addr, ETH_ALEN);	setup_rx_buffers(nic);#ifdef	INCLUDE_3C507	/* Start the 586 by releasing the reset line, but leave loopback. */	outb(0xA0, ioaddr + MISC_CTRL);#endif	/* This was time consuming to track down; you need to give two channel	   attention signals to reliably start up the i82586. */	outb(0, ioaddr + I82586_ATTN);	time = currticks() + TICKS_PER_SEC;	/* allow 1 second to init */	while (			shmem[iSCB_STATUS>>1] == 0)	{		if (currticks() > time)		{			printf("i82586 initialisation timed out with status %hX, cmd %hX\n",					shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);			break;		}	}	/* Issue channel-attn -- the 82586 won't start. */	outb(0, ioaddr + I82586_ATTN);#ifdef	INCLUDE_3C507	/* Disable loopback. */	outb(0x80, ioaddr + MISC_CTRL);#endif#if	defined(DEBUG)	printf("i82586 status %hX, cmd %hX\n",			shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);#endif}/**************************************************************************  POLL - Wait for a frame ***************************************************************************/static int i82586_poll(struct nic *nic){	int		status;	unsigned short	rfd_cmd, next_rx_frame, data_buffer_addr,	frame_status, pkt_len;	unsigned short	*shmem = (short *)mem_start + rx_head;	/* return true if there's an ethernet packet ready to read */	if (			((frame_status = shmem[0]) & 0x8000) == 0)		return (0);		/* nope */	rfd_cmd = shmem[1];	next_rx_frame = shmem[2];	data_buffer_addr = shmem[3];	pkt_len = shmem[11];	status = 0;	if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22			|| (pkt_len & 0xC000) != 0xC000)		printf("\nRx frame corrupt, discarded");	else if ((frame_status & 0x2000) == 0)		printf("\nRx frame had error");	else	{		/* We have a frame, copy it to our buffer */		pkt_len &= 0x3FFF;		memcpy(nic->packet, (char *)mem_start + rx_head + 0x20, pkt_len);		/* Only packets not from ourself */		if (memcmp(nic->packet + ETH_ALEN, nic->node_addr, ETH_ALEN) != 0)		{			nic->packetlen = pkt_len;			status = 1;		}	}	/* Clear the status word and set EOL on Rx frame */	shmem[0] = 0;	shmem[1] = 0xC000;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -