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 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
25 FILE_LICENCE ( GPL2_OR_LATER );
32 #include <ipxe/if_ether.h>
33 #include <ipxe/ethernet.h>
34 #include <ipxe/iobuf.h>
35 #include <ipxe/netdevice.h>
40 unsigned short ioaddr;
44 * Utility functions: issue a PNIC command, retrieve result. Use
45 * pnic_command_quiet if you don't want failure codes to be
46 * automatically printed. Returns the PNIC status code.
48 * Set output_length to NULL only if you expect to receive exactly
49 * output_max_length bytes, otherwise it'll complain that you didn't
50 * get enough data (on the assumption that if you not interested in
51 * discovering the output length then you're expecting a fixed amount
55 static uint16_t pnic_command_quiet ( struct pnic *pnic, uint16_t command,
56 const void *input, uint16_t input_length,
57 void *output, uint16_t output_max_length,
58 uint16_t *output_length ) {
60 uint16_t _output_length;
62 if ( input != NULL ) {
63 /* Write input length */
64 outw ( input_length, pnic->ioaddr + PNIC_REG_LEN );
65 /* Write input data */
66 outsb ( pnic->ioaddr + PNIC_REG_DATA, input, input_length );
69 outw ( command, pnic->ioaddr + PNIC_REG_CMD );
71 status = inw ( pnic->ioaddr + PNIC_REG_STAT );
72 /* Retrieve output length */
73 _output_length = inw ( pnic->ioaddr + PNIC_REG_LEN );
74 if ( output_length == NULL ) {
75 if ( _output_length != output_max_length ) {
76 printf ( "pnic_command %#hx: wrong data length "
77 "returned (expected %d, got %d)\n", command,
78 output_max_length, _output_length );
81 *output_length = _output_length;
83 if ( output != NULL ) {
84 if ( _output_length > output_max_length ) {
85 printf ( "pnic_command %#hx: output buffer too small "
86 "(have %d, need %d)\n", command,
87 output_max_length, _output_length );
88 _output_length = output_max_length;
90 /* Retrieve output data */
91 insb ( pnic->ioaddr + PNIC_REG_DATA, output, _output_length );
96 static uint16_t pnic_command ( struct pnic *pnic, uint16_t command,
97 const void *input, uint16_t input_length,
98 void *output, uint16_t output_max_length,
99 uint16_t *output_length ) {
100 uint16_t status = pnic_command_quiet ( pnic, command,
102 output, output_max_length,
104 if ( status == PNIC_STATUS_OK ) return status;
105 printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
106 command, input_length, status );
110 /* Check API version matches that of NIC */
111 static int pnic_api_check ( uint16_t api_version ) {
112 if ( api_version != PNIC_API_VERSION ) {
113 printf ( "Warning: API version mismatch! "
114 "(NIC's is %d.%d, ours is %d.%d)\n",
115 api_version >> 8, api_version & 0xff,
116 PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
118 if ( api_version < PNIC_API_VERSION ) {
119 printf ( "** You may need to update your copy of Bochs **\n" );
121 return ( api_version == PNIC_API_VERSION );
124 /**************************************************************************
125 POLL - Wait for a frame
126 ***************************************************************************/
127 static void pnic_poll ( struct net_device *netdev ) {
128 struct pnic *pnic = netdev->priv;
129 struct io_buffer *iobuf;
133 /* Fetch all available packets */
135 if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0,
136 &qlen, sizeof ( qlen ), NULL )
141 iobuf = alloc_iob ( ETH_FRAME_LEN );
143 DBG ( "could not allocate buffer\n" );
144 netdev_rx_err ( netdev, NULL, -ENOMEM );
147 if ( pnic_command ( pnic, PNIC_CMD_RECV, NULL, 0,
148 iobuf->data, ETH_FRAME_LEN, &length )
149 != PNIC_STATUS_OK ) {
150 netdev_rx_err ( netdev, iobuf, -EIO );
153 iob_put ( iobuf, length );
154 netdev_rx ( netdev, iobuf );
158 /**************************************************************************
159 TRANSMIT - Transmit a frame
160 ***************************************************************************/
161 static int pnic_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
162 struct pnic *pnic = netdev->priv;
165 iob_pad ( iobuf, ETH_ZLEN );
168 pnic_command ( pnic, PNIC_CMD_XMIT, iobuf->data, iob_len ( iobuf ),
171 netdev_tx_complete ( netdev, iobuf );
175 /**************************************************************************
176 OPEN - Open network device
177 ***************************************************************************/
178 static int pnic_open ( struct net_device *netdev __unused ) {
183 /**************************************************************************
184 CLOSE - Close network device
185 ***************************************************************************/
186 static void pnic_close ( struct net_device *netdev __unused ) {
190 /**************************************************************************
191 IRQ - Enable/disable interrupts
192 ***************************************************************************/
193 static void pnic_irq ( struct net_device *netdev, int enable ) {
194 struct pnic *pnic = netdev->priv;
195 uint8_t mask = ( enable ? 1 : 0 );
197 pnic_command ( pnic, PNIC_CMD_MASK_IRQ, &mask, sizeof ( mask ),
201 /**************************************************************************
203 ***************************************************************************/
204 static struct net_device_operations pnic_operations = {
207 .transmit = pnic_transmit,
212 /**************************************************************************
213 DISABLE - Turn off ethernet interface
214 ***************************************************************************/
215 static void pnic_remove ( struct pci_device *pci ) {
216 struct net_device *netdev = pci_get_drvdata ( pci );
217 struct pnic *pnic = netdev->priv;
219 unregister_netdev ( netdev );
220 pnic_command ( pnic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
221 netdev_nullify ( netdev );
222 netdev_put ( netdev );
225 /**************************************************************************
226 PROBE - Look for an adapter, this routine's visible to the outside
227 ***************************************************************************/
228 static int pnic_probe ( struct pci_device *pci ) {
229 struct net_device *netdev;
231 uint16_t api_version;
235 /* Allocate net device */
236 netdev = alloc_etherdev ( sizeof ( *pnic ) );
239 netdev_init ( netdev, &pnic_operations );
241 pci_set_drvdata ( pci, netdev );
242 netdev->dev = &pci->dev;
243 pnic->ioaddr = pci->ioaddr;
245 /* Fix up PCI device */
246 adjust_pci_device ( pci );
248 /* API version check */
249 status = pnic_command_quiet ( pnic, PNIC_CMD_API_VER, NULL, 0,
251 sizeof ( api_version ), NULL );
252 if ( status != PNIC_STATUS_OK ) {
253 printf ( "PNIC failed installation check, code %#hx\n",
258 pnic_api_check ( api_version );
260 /* Get MAC address */
261 status = pnic_command ( pnic, PNIC_CMD_READ_MAC, NULL, 0,
262 netdev->hw_addr, ETH_ALEN, NULL );
264 /* Register network device */
265 if ( ( rc = register_netdev ( netdev ) ) != 0 )
268 /* Mark as link up; PNIC has no concept of link state */
269 netdev_link_up ( netdev );
274 /* Free net device */
275 netdev_nullify ( netdev );
276 netdev_put ( netdev );
280 static struct pci_device_id pnic_nics[] = {
281 /* genrules.pl doesn't let us use macros for PCI IDs...*/
282 PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor", 0 ),
285 struct pci_driver pnic_driver __pci_driver = {
287 .id_count = ( sizeof ( pnic_nics ) / sizeof ( pnic_nics[0] ) ),
289 .remove = pnic_remove,