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