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