xref: /onnv-gate/usr/src/grub/grub-0.97/netboot/pnic.c (revision 8044:b3af80bbf173)
1*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
2*8044SWilliam.Kucharski@Sun.COM Etherboot -  BOOTP/TFTP Bootstrap Program
3*8044SWilliam.Kucharski@Sun.COM Bochs Pseudo NIC driver for Etherboot
4*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
5*8044SWilliam.Kucharski@Sun.COM 
6*8044SWilliam.Kucharski@Sun.COM /*
7*8044SWilliam.Kucharski@Sun.COM  * This program is free software; you can redistribute it and/or
8*8044SWilliam.Kucharski@Sun.COM  * modify it under the terms of the GNU General Public License as
9*8044SWilliam.Kucharski@Sun.COM  * published by the Free Software Foundation; either version 2, or (at
10*8044SWilliam.Kucharski@Sun.COM  * your option) any later version.
11*8044SWilliam.Kucharski@Sun.COM  *
12*8044SWilliam.Kucharski@Sun.COM  * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
13*8044SWilliam.Kucharski@Sun.COM  */
14*8044SWilliam.Kucharski@Sun.COM 
15*8044SWilliam.Kucharski@Sun.COM /* to get some global routines like printf */
16*8044SWilliam.Kucharski@Sun.COM #include "etherboot.h"
17*8044SWilliam.Kucharski@Sun.COM /* to get the interface to the body of the program */
18*8044SWilliam.Kucharski@Sun.COM #include "nic.h"
19*8044SWilliam.Kucharski@Sun.COM /* to get the PCI support functions, if this is a PCI NIC */
20*8044SWilliam.Kucharski@Sun.COM #include "pci.h"
21*8044SWilliam.Kucharski@Sun.COM 
22*8044SWilliam.Kucharski@Sun.COM /* PNIC API */
23*8044SWilliam.Kucharski@Sun.COM #include "pnic_api.h"
24*8044SWilliam.Kucharski@Sun.COM 
25*8044SWilliam.Kucharski@Sun.COM /* Private data structure */
26*8044SWilliam.Kucharski@Sun.COM typedef struct {
27*8044SWilliam.Kucharski@Sun.COM 	uint16_t api_version;
28*8044SWilliam.Kucharski@Sun.COM } pnic_priv_data_t;
29*8044SWilliam.Kucharski@Sun.COM 
30*8044SWilliam.Kucharski@Sun.COM /* Function prototypes */
31*8044SWilliam.Kucharski@Sun.COM static int pnic_api_check ( uint16_t api_version );
32*8044SWilliam.Kucharski@Sun.COM 
33*8044SWilliam.Kucharski@Sun.COM /* NIC specific static variables go here */
34*8044SWilliam.Kucharski@Sun.COM static uint8_t tx_buffer[ETH_FRAME_LEN];
35*8044SWilliam.Kucharski@Sun.COM 
36*8044SWilliam.Kucharski@Sun.COM /*
37*8044SWilliam.Kucharski@Sun.COM  * Utility functions: issue a PNIC command, retrieve result.  Use
38*8044SWilliam.Kucharski@Sun.COM  * pnic_command_quiet if you don't want failure codes to be
39*8044SWilliam.Kucharski@Sun.COM  * automatically printed.  Returns the PNIC status code.
40*8044SWilliam.Kucharski@Sun.COM  *
41*8044SWilliam.Kucharski@Sun.COM  * Set output_length to NULL only if you expect to receive exactly
42*8044SWilliam.Kucharski@Sun.COM  * output_max_length bytes, otherwise it'll complain that you didn't
43*8044SWilliam.Kucharski@Sun.COM  * get enough data (on the assumption that if you not interested in
44*8044SWilliam.Kucharski@Sun.COM  * discovering the output length then you're expecting a fixed amount
45*8044SWilliam.Kucharski@Sun.COM  * of data).
46*8044SWilliam.Kucharski@Sun.COM  */
47*8044SWilliam.Kucharski@Sun.COM 
pnic_command_quiet(struct nic * nic,uint16_t command,void * input,uint16_t input_length,void * output,uint16_t output_max_length,uint16_t * output_length)48*8044SWilliam.Kucharski@Sun.COM static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command,
49*8044SWilliam.Kucharski@Sun.COM 				     void *input, uint16_t input_length,
50*8044SWilliam.Kucharski@Sun.COM 				     void *output, uint16_t output_max_length,
51*8044SWilliam.Kucharski@Sun.COM 				     uint16_t *output_length ) {
52*8044SWilliam.Kucharski@Sun.COM 	int i;
53*8044SWilliam.Kucharski@Sun.COM 	uint16_t status;
54*8044SWilliam.Kucharski@Sun.COM 	uint16_t _output_length;
55*8044SWilliam.Kucharski@Sun.COM 
56*8044SWilliam.Kucharski@Sun.COM 	if ( input != NULL ) {
57*8044SWilliam.Kucharski@Sun.COM 		/* Write input length */
58*8044SWilliam.Kucharski@Sun.COM 		outw ( input_length, nic->ioaddr + PNIC_REG_LEN );
59*8044SWilliam.Kucharski@Sun.COM 		/* Write input data */
60*8044SWilliam.Kucharski@Sun.COM 		for ( i = 0; i < input_length; i++ ) {
61*8044SWilliam.Kucharski@Sun.COM 			outb( ((char*)input)[i], nic->ioaddr + PNIC_REG_DATA );
62*8044SWilliam.Kucharski@Sun.COM 		}
63*8044SWilliam.Kucharski@Sun.COM 	}
64*8044SWilliam.Kucharski@Sun.COM 	/* Write command */
65*8044SWilliam.Kucharski@Sun.COM 	outw ( command, nic->ioaddr + PNIC_REG_CMD );
66*8044SWilliam.Kucharski@Sun.COM 	/* Retrieve status */
67*8044SWilliam.Kucharski@Sun.COM 	status = inw ( nic->ioaddr + PNIC_REG_STAT );
68*8044SWilliam.Kucharski@Sun.COM 	/* Retrieve output length */
69*8044SWilliam.Kucharski@Sun.COM 	_output_length = inw ( nic->ioaddr + PNIC_REG_LEN );
70*8044SWilliam.Kucharski@Sun.COM 	if ( output_length == NULL ) {
71*8044SWilliam.Kucharski@Sun.COM 		if ( _output_length != output_max_length ) {
72*8044SWilliam.Kucharski@Sun.COM 			printf ( "pnic_command %#hx: wrong data length "
73*8044SWilliam.Kucharski@Sun.COM 				 "returned (expected %d, got %d)\n", command,
74*8044SWilliam.Kucharski@Sun.COM 				 output_max_length, _output_length );
75*8044SWilliam.Kucharski@Sun.COM 		}
76*8044SWilliam.Kucharski@Sun.COM 	} else {
77*8044SWilliam.Kucharski@Sun.COM 		*output_length = _output_length;
78*8044SWilliam.Kucharski@Sun.COM 	}
79*8044SWilliam.Kucharski@Sun.COM 	if ( output != NULL ) {
80*8044SWilliam.Kucharski@Sun.COM 		if ( _output_length > output_max_length ) {
81*8044SWilliam.Kucharski@Sun.COM 			printf ( "pnic_command %#hx: output buffer too small "
82*8044SWilliam.Kucharski@Sun.COM 				 "(have %d, need %d)\n", command,
83*8044SWilliam.Kucharski@Sun.COM 				 output_max_length, _output_length );
84*8044SWilliam.Kucharski@Sun.COM 			_output_length = output_max_length;
85*8044SWilliam.Kucharski@Sun.COM 		}
86*8044SWilliam.Kucharski@Sun.COM 		/* Retrieve output data */
87*8044SWilliam.Kucharski@Sun.COM 		for ( i = 0; i < _output_length; i++ ) {
88*8044SWilliam.Kucharski@Sun.COM 			((char*)output)[i] =
89*8044SWilliam.Kucharski@Sun.COM 				inb ( nic->ioaddr + PNIC_REG_DATA );
90*8044SWilliam.Kucharski@Sun.COM 		}
91*8044SWilliam.Kucharski@Sun.COM 	}
92*8044SWilliam.Kucharski@Sun.COM 	return status;
93*8044SWilliam.Kucharski@Sun.COM }
94*8044SWilliam.Kucharski@Sun.COM 
pnic_command(struct nic * nic,uint16_t command,void * input,uint16_t input_length,void * output,uint16_t output_max_length,uint16_t * output_length)95*8044SWilliam.Kucharski@Sun.COM static uint16_t pnic_command ( struct nic *nic, uint16_t command,
96*8044SWilliam.Kucharski@Sun.COM 			       void *input, uint16_t input_length,
97*8044SWilliam.Kucharski@Sun.COM 			       void *output, uint16_t output_max_length,
98*8044SWilliam.Kucharski@Sun.COM 			       uint16_t *output_length ) {
99*8044SWilliam.Kucharski@Sun.COM 	pnic_priv_data_t *priv = (pnic_priv_data_t*)nic->priv_data;
100*8044SWilliam.Kucharski@Sun.COM 	uint16_t status = pnic_command_quiet ( nic, command,
101*8044SWilliam.Kucharski@Sun.COM 					       input, input_length,
102*8044SWilliam.Kucharski@Sun.COM 					       output, output_max_length,
103*8044SWilliam.Kucharski@Sun.COM 					       output_length );
104*8044SWilliam.Kucharski@Sun.COM 	if ( status == PNIC_STATUS_OK ) return status;
105*8044SWilliam.Kucharski@Sun.COM 	printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
106*8044SWilliam.Kucharski@Sun.COM 		 command, input_length, status );
107*8044SWilliam.Kucharski@Sun.COM 	if ( priv->api_version ) pnic_api_check(priv->api_version);
108*8044SWilliam.Kucharski@Sun.COM 	return status;
109*8044SWilliam.Kucharski@Sun.COM }
110*8044SWilliam.Kucharski@Sun.COM 
111*8044SWilliam.Kucharski@Sun.COM /* Check API version matches that of NIC */
pnic_api_check(uint16_t api_version)112*8044SWilliam.Kucharski@Sun.COM static int pnic_api_check ( uint16_t api_version ) {
113*8044SWilliam.Kucharski@Sun.COM 	if ( api_version != PNIC_API_VERSION ) {
114*8044SWilliam.Kucharski@Sun.COM 		printf ( "Warning: API version mismatch! "
115*8044SWilliam.Kucharski@Sun.COM 			 "(NIC's is %d.%d, ours is %d.%d)\n",
116*8044SWilliam.Kucharski@Sun.COM 			 api_version >> 8, api_version & 0xff,
117*8044SWilliam.Kucharski@Sun.COM 			 PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
118*8044SWilliam.Kucharski@Sun.COM 	}
119*8044SWilliam.Kucharski@Sun.COM 	if ( api_version < PNIC_API_VERSION ) {
120*8044SWilliam.Kucharski@Sun.COM 		printf ( "*** You may need to update your copy of Bochs ***\n" );
121*8044SWilliam.Kucharski@Sun.COM 	}
122*8044SWilliam.Kucharski@Sun.COM 	return ( api_version == PNIC_API_VERSION );
123*8044SWilliam.Kucharski@Sun.COM }
124*8044SWilliam.Kucharski@Sun.COM 
125*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
126*8044SWilliam.Kucharski@Sun.COM POLL - Wait for a frame
127*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pnic_poll(struct nic * nic,int retrieve)128*8044SWilliam.Kucharski@Sun.COM static int pnic_poll(struct nic *nic, int retrieve)
129*8044SWilliam.Kucharski@Sun.COM {
130*8044SWilliam.Kucharski@Sun.COM 	uint16_t length;
131*8044SWilliam.Kucharski@Sun.COM 	uint16_t qlen;
132*8044SWilliam.Kucharski@Sun.COM 
133*8044SWilliam.Kucharski@Sun.COM 	/* Check receive queue length to see if there's anything to
134*8044SWilliam.Kucharski@Sun.COM 	 * get.  Necessary since once we've called PNIC_CMD_RECV we
135*8044SWilliam.Kucharski@Sun.COM 	 * have to read out the packet, otherwise it's lost forever.
136*8044SWilliam.Kucharski@Sun.COM 	 */
137*8044SWilliam.Kucharski@Sun.COM 	if ( pnic_command ( nic, PNIC_CMD_RECV_QLEN, NULL, 0,
138*8044SWilliam.Kucharski@Sun.COM 			    &qlen, sizeof(qlen), NULL )
139*8044SWilliam.Kucharski@Sun.COM 	     != PNIC_STATUS_OK ) return ( 0 );
140*8044SWilliam.Kucharski@Sun.COM 	if ( qlen == 0 ) return ( 0 );
141*8044SWilliam.Kucharski@Sun.COM 
142*8044SWilliam.Kucharski@Sun.COM 	/* There is a packet ready.  Return 1 if we're only checking. */
143*8044SWilliam.Kucharski@Sun.COM 	if ( ! retrieve ) return ( 1 );
144*8044SWilliam.Kucharski@Sun.COM 
145*8044SWilliam.Kucharski@Sun.COM 	/* Retrieve the packet */
146*8044SWilliam.Kucharski@Sun.COM 	if ( pnic_command ( nic, PNIC_CMD_RECV, NULL, 0,
147*8044SWilliam.Kucharski@Sun.COM 			    nic->packet, ETH_FRAME_LEN, &length )
148*8044SWilliam.Kucharski@Sun.COM 	     != PNIC_STATUS_OK ) return ( 0 );
149*8044SWilliam.Kucharski@Sun.COM 	nic->packetlen = length;
150*8044SWilliam.Kucharski@Sun.COM 	return ( 1 );
151*8044SWilliam.Kucharski@Sun.COM }
152*8044SWilliam.Kucharski@Sun.COM 
153*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
154*8044SWilliam.Kucharski@Sun.COM TRANSMIT - Transmit a frame
155*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pnic_transmit(struct nic * nic,const char * dest,unsigned int type,unsigned int size,const char * data)156*8044SWilliam.Kucharski@Sun.COM static void pnic_transmit(
157*8044SWilliam.Kucharski@Sun.COM 	struct nic *nic,
158*8044SWilliam.Kucharski@Sun.COM 	const char *dest,		/* Destination */
159*8044SWilliam.Kucharski@Sun.COM 	unsigned int type,		/* Type */
160*8044SWilliam.Kucharski@Sun.COM 	unsigned int size,		/* size */
161*8044SWilliam.Kucharski@Sun.COM 	const char *data)		/* Packet */
162*8044SWilliam.Kucharski@Sun.COM {
163*8044SWilliam.Kucharski@Sun.COM 	unsigned int nstype = htons ( type );
164*8044SWilliam.Kucharski@Sun.COM 
165*8044SWilliam.Kucharski@Sun.COM 	if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) {
166*8044SWilliam.Kucharski@Sun.COM 		printf ( "pnic_transmit: packet too large\n" );
167*8044SWilliam.Kucharski@Sun.COM 		return;
168*8044SWilliam.Kucharski@Sun.COM 	}
169*8044SWilliam.Kucharski@Sun.COM 
170*8044SWilliam.Kucharski@Sun.COM 	/* Assemble packet */
171*8044SWilliam.Kucharski@Sun.COM 	memcpy ( tx_buffer, dest, ETH_ALEN );
172*8044SWilliam.Kucharski@Sun.COM 	memcpy ( tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN );
173*8044SWilliam.Kucharski@Sun.COM 	memcpy ( tx_buffer + 2 * ETH_ALEN, &nstype, 2 );
174*8044SWilliam.Kucharski@Sun.COM 	memcpy ( tx_buffer + ETH_HLEN, data, size );
175*8044SWilliam.Kucharski@Sun.COM 
176*8044SWilliam.Kucharski@Sun.COM 	pnic_command ( nic, PNIC_CMD_XMIT, tx_buffer, ETH_HLEN + size,
177*8044SWilliam.Kucharski@Sun.COM 		       NULL, 0, NULL );
178*8044SWilliam.Kucharski@Sun.COM }
179*8044SWilliam.Kucharski@Sun.COM 
180*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
181*8044SWilliam.Kucharski@Sun.COM DISABLE - Turn off ethernet interface
182*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pnic_disable(struct dev * dev)183*8044SWilliam.Kucharski@Sun.COM static void pnic_disable(struct dev *dev)
184*8044SWilliam.Kucharski@Sun.COM {
185*8044SWilliam.Kucharski@Sun.COM 	struct nic *nic = (struct nic *)dev;
186*8044SWilliam.Kucharski@Sun.COM 	pnic_command ( nic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
187*8044SWilliam.Kucharski@Sun.COM }
188*8044SWilliam.Kucharski@Sun.COM 
189*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
190*8044SWilliam.Kucharski@Sun.COM IRQ - Handle card interrupt status
191*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
pnic_irq(struct nic * nic,irq_action_t action)192*8044SWilliam.Kucharski@Sun.COM static void pnic_irq ( struct nic *nic, irq_action_t action )
193*8044SWilliam.Kucharski@Sun.COM {
194*8044SWilliam.Kucharski@Sun.COM 	uint8_t enabled;
195*8044SWilliam.Kucharski@Sun.COM 
196*8044SWilliam.Kucharski@Sun.COM 	switch ( action ) {
197*8044SWilliam.Kucharski@Sun.COM 	case DISABLE :
198*8044SWilliam.Kucharski@Sun.COM 	case ENABLE :
199*8044SWilliam.Kucharski@Sun.COM 		enabled = ( action == ENABLE ? 1 : 0 );
200*8044SWilliam.Kucharski@Sun.COM 		pnic_command ( nic, PNIC_CMD_MASK_IRQ,
201*8044SWilliam.Kucharski@Sun.COM 			       &enabled, sizeof(enabled), NULL, 0, NULL );
202*8044SWilliam.Kucharski@Sun.COM 		break;
203*8044SWilliam.Kucharski@Sun.COM 	case FORCE :
204*8044SWilliam.Kucharski@Sun.COM 		pnic_command ( nic, PNIC_CMD_FORCE_IRQ,
205*8044SWilliam.Kucharski@Sun.COM 			       NULL, 0, NULL, 0, NULL );
206*8044SWilliam.Kucharski@Sun.COM 		break;
207*8044SWilliam.Kucharski@Sun.COM 	}
208*8044SWilliam.Kucharski@Sun.COM }
209*8044SWilliam.Kucharski@Sun.COM 
210*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
211*8044SWilliam.Kucharski@Sun.COM PROBE - Look for an adapter, this routine's visible to the outside
212*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
213*8044SWilliam.Kucharski@Sun.COM 
pnic_probe(struct dev * dev,struct pci_device * pci)214*8044SWilliam.Kucharski@Sun.COM static int pnic_probe(struct dev *dev, struct pci_device *pci)
215*8044SWilliam.Kucharski@Sun.COM {
216*8044SWilliam.Kucharski@Sun.COM 	struct nic *nic = (struct nic *)dev;
217*8044SWilliam.Kucharski@Sun.COM 	static pnic_priv_data_t priv;
218*8044SWilliam.Kucharski@Sun.COM 	uint16_t status;
219*8044SWilliam.Kucharski@Sun.COM 
220*8044SWilliam.Kucharski@Sun.COM 	printf(" - ");
221*8044SWilliam.Kucharski@Sun.COM 
222*8044SWilliam.Kucharski@Sun.COM 	/* Clear private data structure and chain it in */
223*8044SWilliam.Kucharski@Sun.COM 	memset ( &priv, 0, sizeof(priv) );
224*8044SWilliam.Kucharski@Sun.COM 	nic->priv_data = &priv;
225*8044SWilliam.Kucharski@Sun.COM 
226*8044SWilliam.Kucharski@Sun.COM 	/* Mask the bit that says "this is an io addr" */
227*8044SWilliam.Kucharski@Sun.COM 	nic->ioaddr = pci->ioaddr & ~3;
228*8044SWilliam.Kucharski@Sun.COM 	nic->irqno = pci->irq;
229*8044SWilliam.Kucharski@Sun.COM 	/* Not sure what this does, but the rtl8139 driver does it */
230*8044SWilliam.Kucharski@Sun.COM 	adjust_pci_device(pci);
231*8044SWilliam.Kucharski@Sun.COM 
232*8044SWilliam.Kucharski@Sun.COM 	status = pnic_command_quiet( nic, PNIC_CMD_API_VER, NULL, 0,
233*8044SWilliam.Kucharski@Sun.COM 				     &priv.api_version,
234*8044SWilliam.Kucharski@Sun.COM 				     sizeof(priv.api_version), NULL );
235*8044SWilliam.Kucharski@Sun.COM 	if ( status != PNIC_STATUS_OK ) {
236*8044SWilliam.Kucharski@Sun.COM 		printf ( "PNIC failed installation check, code %#hx\n",
237*8044SWilliam.Kucharski@Sun.COM 			 status );
238*8044SWilliam.Kucharski@Sun.COM 		return 0;
239*8044SWilliam.Kucharski@Sun.COM 	}
240*8044SWilliam.Kucharski@Sun.COM 	pnic_api_check(priv.api_version);
241*8044SWilliam.Kucharski@Sun.COM 	status = pnic_command ( nic, PNIC_CMD_READ_MAC, NULL, 0,
242*8044SWilliam.Kucharski@Sun.COM 				nic->node_addr, ETH_ALEN, NULL );
243*8044SWilliam.Kucharski@Sun.COM 	printf ( "Detected Bochs Pseudo NIC MAC %! (API v%d.%d) at %#hx\n",
244*8044SWilliam.Kucharski@Sun.COM 		 nic->node_addr, priv.api_version>>8, priv.api_version&0xff,
245*8044SWilliam.Kucharski@Sun.COM 		 nic->ioaddr );
246*8044SWilliam.Kucharski@Sun.COM 
247*8044SWilliam.Kucharski@Sun.COM 	/* point to NIC specific routines */
248*8044SWilliam.Kucharski@Sun.COM 	dev->disable  = pnic_disable;
249*8044SWilliam.Kucharski@Sun.COM 	nic->poll     = pnic_poll;
250*8044SWilliam.Kucharski@Sun.COM 	nic->transmit = pnic_transmit;
251*8044SWilliam.Kucharski@Sun.COM 	nic->irq      = pnic_irq;
252*8044SWilliam.Kucharski@Sun.COM 	return 1;
253*8044SWilliam.Kucharski@Sun.COM }
254*8044SWilliam.Kucharski@Sun.COM 
255*8044SWilliam.Kucharski@Sun.COM static struct pci_id pnic_nics[] = {
256*8044SWilliam.Kucharski@Sun.COM /* genrules.pl doesn't let us use macros for PCI IDs...*/
257*8044SWilliam.Kucharski@Sun.COM PCI_ROM(0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor"),
258*8044SWilliam.Kucharski@Sun.COM };
259*8044SWilliam.Kucharski@Sun.COM 
260*8044SWilliam.Kucharski@Sun.COM struct pci_driver pnic_driver = {
261*8044SWilliam.Kucharski@Sun.COM 	.type     = NIC_DRIVER,
262*8044SWilliam.Kucharski@Sun.COM 	.name     = "PNIC",
263*8044SWilliam.Kucharski@Sun.COM 	.probe    = pnic_probe,
264*8044SWilliam.Kucharski@Sun.COM 	.ids      = pnic_nics,
265*8044SWilliam.Kucharski@Sun.COM 	.id_count = sizeof(pnic_nics)/sizeof(pnic_nics[0]),
266*8044SWilliam.Kucharski@Sun.COM 	.class    = 0,
267*8044SWilliam.Kucharski@Sun.COM };
268