1 /**************************************************************************
2 Etherboot - BOOTP/TFTP Bootstrap Program
3 Bochs Pseudo NIC driver for Etherboot
4 ***************************************************************************/
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2, or (at
10 * your option) any later version.
12 * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
15 FILE_LICENCE ( GPL2_OR_LATER
);
22 #include <gpxe/if_ether.h>
23 #include <gpxe/ethernet.h>
24 #include <gpxe/iobuf.h>
25 #include <gpxe/netdevice.h>
30 unsigned short ioaddr
;
34 * Utility functions: issue a PNIC command, retrieve result. Use
35 * pnic_command_quiet if you don't want failure codes to be
36 * automatically printed. Returns the PNIC status code.
38 * Set output_length to NULL only if you expect to receive exactly
39 * output_max_length bytes, otherwise it'll complain that you didn't
40 * get enough data (on the assumption that if you not interested in
41 * discovering the output length then you're expecting a fixed amount
45 static uint16_t pnic_command_quiet ( struct pnic
*pnic
, uint16_t command
,
46 const void *input
, uint16_t input_length
,
47 void *output
, uint16_t output_max_length
,
48 uint16_t *output_length
) {
50 uint16_t _output_length
;
52 if ( input
!= NULL
) {
53 /* Write input length */
54 outw ( input_length
, pnic
->ioaddr
+ PNIC_REG_LEN
);
55 /* Write input data */
56 outsb ( pnic
->ioaddr
+ PNIC_REG_DATA
, input
, input_length
);
59 outw ( command
, pnic
->ioaddr
+ PNIC_REG_CMD
);
61 status
= inw ( pnic
->ioaddr
+ PNIC_REG_STAT
);
62 /* Retrieve output length */
63 _output_length
= inw ( pnic
->ioaddr
+ PNIC_REG_LEN
);
64 if ( output_length
== NULL
) {
65 if ( _output_length
!= output_max_length
) {
66 printf ( "pnic_command %#hx: wrong data length "
67 "returned (expected %d, got %d)\n", command
,
68 output_max_length
, _output_length
);
71 *output_length
= _output_length
;
73 if ( output
!= NULL
) {
74 if ( _output_length
> output_max_length
) {
75 printf ( "pnic_command %#hx: output buffer too small "
76 "(have %d, need %d)\n", command
,
77 output_max_length
, _output_length
);
78 _output_length
= output_max_length
;
80 /* Retrieve output data */
81 insb ( pnic
->ioaddr
+ PNIC_REG_DATA
, output
, _output_length
);
86 static uint16_t pnic_command ( struct pnic
*pnic
, uint16_t command
,
87 const void *input
, uint16_t input_length
,
88 void *output
, uint16_t output_max_length
,
89 uint16_t *output_length
) {
90 uint16_t status
= pnic_command_quiet ( pnic
, command
,
92 output
, output_max_length
,
94 if ( status
== PNIC_STATUS_OK
) return status
;
95 printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
96 command
, input_length
, status
);
100 /* Check API version matches that of NIC */
101 static int pnic_api_check ( uint16_t api_version
) {
102 if ( api_version
!= PNIC_API_VERSION
) {
103 printf ( "Warning: API version mismatch! "
104 "(NIC's is %d.%d, ours is %d.%d)\n",
105 api_version
>> 8, api_version
& 0xff,
106 PNIC_API_VERSION
>> 8, PNIC_API_VERSION
& 0xff );
108 if ( api_version
< PNIC_API_VERSION
) {
109 printf ( "** You may need to update your copy of Bochs **\n" );
111 return ( api_version
== PNIC_API_VERSION
);
114 /**************************************************************************
115 POLL - Wait for a frame
116 ***************************************************************************/
117 static void pnic_poll ( struct net_device
*netdev
) {
118 struct pnic
*pnic
= netdev
->priv
;
119 struct io_buffer
*iobuf
;
123 /* Fetch all available packets */
125 if ( pnic_command ( pnic
, PNIC_CMD_RECV_QLEN
, NULL
, 0,
126 &qlen
, sizeof ( qlen
), NULL
)
131 iobuf
= alloc_iob ( ETH_FRAME_LEN
);
133 DBG ( "could not allocate buffer\n" );
134 netdev_rx_err ( netdev
, NULL
, -ENOMEM
);
137 if ( pnic_command ( pnic
, PNIC_CMD_RECV
, NULL
, 0,
138 iobuf
->data
, ETH_FRAME_LEN
, &length
)
139 != PNIC_STATUS_OK
) {
140 netdev_rx_err ( netdev
, iobuf
, -EIO
);
143 iob_put ( iobuf
, length
);
144 netdev_rx ( netdev
, iobuf
);
148 /**************************************************************************
149 TRANSMIT - Transmit a frame
150 ***************************************************************************/
151 static int pnic_transmit ( struct net_device
*netdev
, struct io_buffer
*iobuf
) {
152 struct pnic
*pnic
= netdev
->priv
;
155 iob_pad ( iobuf
, ETH_ZLEN
);
158 pnic_command ( pnic
, PNIC_CMD_XMIT
, iobuf
->data
, iob_len ( iobuf
),
161 netdev_tx_complete ( netdev
, iobuf
);
165 /**************************************************************************
166 OPEN - Open network device
167 ***************************************************************************/
168 static int pnic_open ( struct net_device
*netdev __unused
) {
173 /**************************************************************************
174 CLOSE - Close network device
175 ***************************************************************************/
176 static void pnic_close ( struct net_device
*netdev __unused
) {
180 /**************************************************************************
181 IRQ - Enable/disable interrupts
182 ***************************************************************************/
183 static void pnic_irq ( struct net_device
*netdev
, int enable
) {
184 struct pnic
*pnic
= netdev
->priv
;
185 uint8_t mask
= ( enable
? 1 : 0 );
187 pnic_command ( pnic
, PNIC_CMD_MASK_IRQ
, &mask
, sizeof ( mask
),
191 /**************************************************************************
193 ***************************************************************************/
194 static struct net_device_operations pnic_operations
= {
197 .transmit
= pnic_transmit
,
202 /**************************************************************************
203 DISABLE - Turn off ethernet interface
204 ***************************************************************************/
205 static void pnic_remove ( struct pci_device
*pci
) {
206 struct net_device
*netdev
= pci_get_drvdata ( pci
);
207 struct pnic
*pnic
= netdev
->priv
;
209 unregister_netdev ( netdev
);
210 pnic_command ( pnic
, PNIC_CMD_RESET
, NULL
, 0, NULL
, 0, NULL
);
211 netdev_nullify ( netdev
);
212 netdev_put ( netdev
);
215 /**************************************************************************
216 PROBE - Look for an adapter, this routine's visible to the outside
217 ***************************************************************************/
218 static int pnic_probe ( struct pci_device
*pci
,
219 const struct pci_device_id
*id __unused
) {
220 struct net_device
*netdev
;
222 uint16_t api_version
;
226 /* Allocate net device */
227 netdev
= alloc_etherdev ( sizeof ( *pnic
) );
230 netdev_init ( netdev
, &pnic_operations
);
232 pci_set_drvdata ( pci
, netdev
);
233 netdev
->dev
= &pci
->dev
;
234 pnic
->ioaddr
= pci
->ioaddr
;
236 /* Fix up PCI device */
237 adjust_pci_device ( pci
);
239 /* API version check */
240 status
= pnic_command_quiet ( pnic
, PNIC_CMD_API_VER
, NULL
, 0,
242 sizeof ( api_version
), NULL
);
243 if ( status
!= PNIC_STATUS_OK
) {
244 printf ( "PNIC failed installation check, code %#hx\n",
249 pnic_api_check ( api_version
);
251 /* Get MAC address */
252 status
= pnic_command ( pnic
, PNIC_CMD_READ_MAC
, NULL
, 0,
253 netdev
->hw_addr
, ETH_ALEN
, NULL
);
255 /* Mark as link up; PNIC has no concept of link state */
256 netdev_link_up ( netdev
);
258 /* Register network device */
259 if ( ( rc
= register_netdev ( netdev
) ) != 0 )
265 /* Free net device */
266 netdev_nullify ( netdev
);
267 netdev_put ( netdev
);
271 static struct pci_device_id pnic_nics
[] = {
272 /* genrules.pl doesn't let us use macros for PCI IDs...*/
273 PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor", 0 ),
276 struct pci_driver pnic_driver __pci_driver
= {
278 .id_count
= ( sizeof ( pnic_nics
) / sizeof ( pnic_nics
[0] ) ),
280 .remove
= pnic_remove
,