17c478bd9Sstevel@tonic-gate /**************************************************************************
27c478bd9Sstevel@tonic-gate Etherboot -  BOOTP/TFTP Bootstrap Program
37c478bd9Sstevel@tonic-gate Bochs Pseudo NIC driver for Etherboot
47c478bd9Sstevel@tonic-gate ***************************************************************************/
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate  * This program is free software; you can redistribute it and/or
87c478bd9Sstevel@tonic-gate  * modify it under the terms of the GNU General Public License as
97c478bd9Sstevel@tonic-gate  * published by the Free Software Foundation; either version 2, or (at
107c478bd9Sstevel@tonic-gate  * your option) any later version.
117c478bd9Sstevel@tonic-gate  *
127c478bd9Sstevel@tonic-gate  * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
137c478bd9Sstevel@tonic-gate  */
147c478bd9Sstevel@tonic-gate 
157c478bd9Sstevel@tonic-gate /* to get some global routines like printf */
167c478bd9Sstevel@tonic-gate #include "etherboot.h"
177c478bd9Sstevel@tonic-gate /* to get the interface to the body of the program */
187c478bd9Sstevel@tonic-gate #include "nic.h"
197c478bd9Sstevel@tonic-gate /* to get the PCI support functions, if this is a PCI NIC */
207c478bd9Sstevel@tonic-gate #include "pci.h"
217c478bd9Sstevel@tonic-gate 
227c478bd9Sstevel@tonic-gate /* PNIC API */
237c478bd9Sstevel@tonic-gate #include "pnic_api.h"
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate /* Private data structure */
267c478bd9Sstevel@tonic-gate typedef struct {
277c478bd9Sstevel@tonic-gate 	uint16_t api_version;
287c478bd9Sstevel@tonic-gate } pnic_priv_data_t;
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /* Function prototypes */
317c478bd9Sstevel@tonic-gate static int pnic_api_check ( uint16_t api_version );
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /* NIC specific static variables go here */
347c478bd9Sstevel@tonic-gate static uint8_t tx_buffer[ETH_FRAME_LEN];
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate /*
377c478bd9Sstevel@tonic-gate  * Utility functions: issue a PNIC command, retrieve result.  Use
387c478bd9Sstevel@tonic-gate  * pnic_command_quiet if you don't want failure codes to be
397c478bd9Sstevel@tonic-gate  * automatically printed.  Returns the PNIC status code.
407c478bd9Sstevel@tonic-gate  *
417c478bd9Sstevel@tonic-gate  * Set output_length to NULL only if you expect to receive exactly
427c478bd9Sstevel@tonic-gate  * output_max_length bytes, otherwise it'll complain that you didn't
437c478bd9Sstevel@tonic-gate  * get enough data (on the assumption that if you not interested in
447c478bd9Sstevel@tonic-gate  * discovering the output length then you're expecting a fixed amount
457c478bd9Sstevel@tonic-gate  * of data).
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate 
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)487c478bd9Sstevel@tonic-gate static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command,
497c478bd9Sstevel@tonic-gate 				     void *input, uint16_t input_length,
507c478bd9Sstevel@tonic-gate 				     void *output, uint16_t output_max_length,
517c478bd9Sstevel@tonic-gate 				     uint16_t *output_length ) {
527c478bd9Sstevel@tonic-gate 	int i;
537c478bd9Sstevel@tonic-gate 	uint16_t status;
547c478bd9Sstevel@tonic-gate 	uint16_t _output_length;
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate 	if ( input != NULL ) {
577c478bd9Sstevel@tonic-gate 		/* Write input length */
587c478bd9Sstevel@tonic-gate 		outw ( input_length, nic->ioaddr + PNIC_REG_LEN );
597c478bd9Sstevel@tonic-gate 		/* Write input data */
607c478bd9Sstevel@tonic-gate 		for ( i = 0; i < input_length; i++ ) {
617c478bd9Sstevel@tonic-gate 			outb( ((char*)input)[i], nic->ioaddr + PNIC_REG_DATA );
627c478bd9Sstevel@tonic-gate 		}
637c478bd9Sstevel@tonic-gate 	}
647c478bd9Sstevel@tonic-gate 	/* Write command */
657c478bd9Sstevel@tonic-gate 	outw ( command, nic->ioaddr + PNIC_REG_CMD );
667c478bd9Sstevel@tonic-gate 	/* Retrieve status */
677c478bd9Sstevel@tonic-gate 	status = inw ( nic->ioaddr + PNIC_REG_STAT );
687c478bd9Sstevel@tonic-gate 	/* Retrieve output length */
697c478bd9Sstevel@tonic-gate 	_output_length = inw ( nic->ioaddr + PNIC_REG_LEN );
707c478bd9Sstevel@tonic-gate 	if ( output_length == NULL ) {
717c478bd9Sstevel@tonic-gate 		if ( _output_length != output_max_length ) {
727c478bd9Sstevel@tonic-gate 			printf ( "pnic_command %#hx: wrong data length "
737c478bd9Sstevel@tonic-gate 				 "returned (expected %d, got %d)\n", command,
747c478bd9Sstevel@tonic-gate 				 output_max_length, _output_length );
757c478bd9Sstevel@tonic-gate 		}
767c478bd9Sstevel@tonic-gate 	} else {
777c478bd9Sstevel@tonic-gate 		*output_length = _output_length;
787c478bd9Sstevel@tonic-gate 	}
797c478bd9Sstevel@tonic-gate 	if ( output != NULL ) {
807c478bd9Sstevel@tonic-gate 		if ( _output_length > output_max_length ) {
817c478bd9Sstevel@tonic-gate 			printf ( "pnic_command %#hx: output buffer too small "
827c478bd9Sstevel@tonic-gate 				 "(have %d, need %d)\n", command,
837c478bd9Sstevel@tonic-gate 				 output_max_length, _output_length );
847c478bd9Sstevel@tonic-gate 			_output_length = output_max_length;
857c478bd9Sstevel@tonic-gate 		}
867c478bd9Sstevel@tonic-gate 		/* Retrieve output data */
877c478bd9Sstevel@tonic-gate 		for ( i = 0; i < _output_length; i++ ) {
887c478bd9Sstevel@tonic-gate 			((char*)output)[i] =
897c478bd9Sstevel@tonic-gate 				inb ( nic->ioaddr + PNIC_REG_DATA );
907c478bd9Sstevel@tonic-gate 		}
917c478bd9Sstevel@tonic-gate 	}
927c478bd9Sstevel@tonic-gate 	return status;
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate 
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)957c478bd9Sstevel@tonic-gate static uint16_t pnic_command ( struct nic *nic, uint16_t command,
967c478bd9Sstevel@tonic-gate 			       void *input, uint16_t input_length,
977c478bd9Sstevel@tonic-gate 			       void *output, uint16_t output_max_length,
987c478bd9Sstevel@tonic-gate 			       uint16_t *output_length ) {
997c478bd9Sstevel@tonic-gate 	pnic_priv_data_t *priv = (pnic_priv_data_t*)nic->priv_data;
1007c478bd9Sstevel@tonic-gate 	uint16_t status = pnic_command_quiet ( nic, command,
1017c478bd9Sstevel@tonic-gate 					       input, input_length,
1027c478bd9Sstevel@tonic-gate 					       output, output_max_length,
1037c478bd9Sstevel@tonic-gate 					       output_length );
1047c478bd9Sstevel@tonic-gate 	if ( status == PNIC_STATUS_OK ) return status;
1057c478bd9Sstevel@tonic-gate 	printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
1067c478bd9Sstevel@tonic-gate 		 command, input_length, status );
1077c478bd9Sstevel@tonic-gate 	if ( priv->api_version ) pnic_api_check(priv->api_version);
1087c478bd9Sstevel@tonic-gate 	return status;
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /* Check API version matches that of NIC */
pnic_api_check(uint16_t api_version)1127c478bd9Sstevel@tonic-gate static int pnic_api_check ( uint16_t api_version ) {
1137c478bd9Sstevel@tonic-gate 	if ( api_version != PNIC_API_VERSION ) {
1147c478bd9Sstevel@tonic-gate 		printf ( "Warning: API version mismatch! "
1157c478bd9Sstevel@tonic-gate 			 "(NIC's is %d.%d, ours is %d.%d)\n",
1167c478bd9Sstevel@tonic-gate 			 api_version >> 8, api_version & 0xff,
1177c478bd9Sstevel@tonic-gate 			 PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
1187c478bd9Sstevel@tonic-gate 	}
1197c478bd9Sstevel@tonic-gate 	if ( api_version < PNIC_API_VERSION ) {
1207c478bd9Sstevel@tonic-gate 		printf ( "*** You may need to update your copy of Bochs ***\n" );
1217c478bd9Sstevel@tonic-gate 	}
1227c478bd9Sstevel@tonic-gate 	return ( api_version == PNIC_API_VERSION );
1237c478bd9Sstevel@tonic-gate }
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate /**************************************************************************
1267c478bd9Sstevel@tonic-gate POLL - Wait for a frame
1277c478bd9Sstevel@tonic-gate ***************************************************************************/
pnic_poll(struct nic * nic,int retrieve)1287c478bd9Sstevel@tonic-gate static int pnic_poll(struct nic *nic, int retrieve)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate 	uint16_t length;
1317c478bd9Sstevel@tonic-gate 	uint16_t qlen;
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate 	/* Check receive queue length to see if there's anything to
1347c478bd9Sstevel@tonic-gate 	 * get.  Necessary since once we've called PNIC_CMD_RECV we
1357c478bd9Sstevel@tonic-gate 	 * have to read out the packet, otherwise it's lost forever.
1367c478bd9Sstevel@tonic-gate 	 */
1377c478bd9Sstevel@tonic-gate 	if ( pnic_command ( nic, PNIC_CMD_RECV_QLEN, NULL, 0,
1387c478bd9Sstevel@tonic-gate 			    &qlen, sizeof(qlen), NULL )
1397c478bd9Sstevel@tonic-gate 	     != PNIC_STATUS_OK ) return ( 0 );
1407c478bd9Sstevel@tonic-gate 	if ( qlen == 0 ) return ( 0 );
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	/* There is a packet ready.  Return 1 if we're only checking. */
1437c478bd9Sstevel@tonic-gate 	if ( ! retrieve ) return ( 1 );
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	/* Retrieve the packet */
1467c478bd9Sstevel@tonic-gate 	if ( pnic_command ( nic, PNIC_CMD_RECV, NULL, 0,
1477c478bd9Sstevel@tonic-gate 			    nic->packet, ETH_FRAME_LEN, &length )
1487c478bd9Sstevel@tonic-gate 	     != PNIC_STATUS_OK ) return ( 0 );
1497c478bd9Sstevel@tonic-gate 	nic->packetlen = length;
1507c478bd9Sstevel@tonic-gate 	return ( 1 );
1517c478bd9Sstevel@tonic-gate }
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate /**************************************************************************
1547c478bd9Sstevel@tonic-gate TRANSMIT - Transmit a frame
1557c478bd9Sstevel@tonic-gate ***************************************************************************/
pnic_transmit(struct nic * nic,const char * dest,unsigned int type,unsigned int size,const char * data)1567c478bd9Sstevel@tonic-gate static void pnic_transmit(
1577c478bd9Sstevel@tonic-gate 	struct nic *nic,
1587c478bd9Sstevel@tonic-gate 	const char *dest,		/* Destination */
1597c478bd9Sstevel@tonic-gate 	unsigned int type,		/* Type */
1607c478bd9Sstevel@tonic-gate 	unsigned int size,		/* size */
1617c478bd9Sstevel@tonic-gate 	const char *data)		/* Packet */
1627c478bd9Sstevel@tonic-gate {
1637c478bd9Sstevel@tonic-gate 	unsigned int nstype = htons ( type );
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) {
1667c478bd9Sstevel@tonic-gate 		printf ( "pnic_transmit: packet too large\n" );
1677c478bd9Sstevel@tonic-gate 		return;
1687c478bd9Sstevel@tonic-gate 	}
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	/* Assemble packet */
1717c478bd9Sstevel@tonic-gate 	memcpy ( tx_buffer, dest, ETH_ALEN );
1727c478bd9Sstevel@tonic-gate 	memcpy ( tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN );
1737c478bd9Sstevel@tonic-gate 	memcpy ( tx_buffer + 2 * ETH_ALEN, &nstype, 2 );
1747c478bd9Sstevel@tonic-gate 	memcpy ( tx_buffer + ETH_HLEN, data, size );
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	pnic_command ( nic, PNIC_CMD_XMIT, tx_buffer, ETH_HLEN + size,
1777c478bd9Sstevel@tonic-gate 		       NULL, 0, NULL );
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate /**************************************************************************
1817c478bd9Sstevel@tonic-gate DISABLE - Turn off ethernet interface
1827c478bd9Sstevel@tonic-gate ***************************************************************************/
pnic_disable(struct dev * dev)1837c478bd9Sstevel@tonic-gate static void pnic_disable(struct dev *dev)
1847c478bd9Sstevel@tonic-gate {
1857c478bd9Sstevel@tonic-gate 	struct nic *nic = (struct nic *)dev;
1867c478bd9Sstevel@tonic-gate 	pnic_command ( nic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
1877c478bd9Sstevel@tonic-gate }
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate /**************************************************************************
1907c478bd9Sstevel@tonic-gate IRQ - Handle card interrupt status
1917c478bd9Sstevel@tonic-gate ***************************************************************************/
pnic_irq(struct nic * nic,irq_action_t action)1927c478bd9Sstevel@tonic-gate static void pnic_irq ( struct nic *nic, irq_action_t action )
1937c478bd9Sstevel@tonic-gate {
1947c478bd9Sstevel@tonic-gate 	uint8_t enabled;
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	switch ( action ) {
1977c478bd9Sstevel@tonic-gate 	case DISABLE :
1987c478bd9Sstevel@tonic-gate 	case ENABLE :
1997c478bd9Sstevel@tonic-gate 		enabled = ( action == ENABLE ? 1 : 0 );
2007c478bd9Sstevel@tonic-gate 		pnic_command ( nic, PNIC_CMD_MASK_IRQ,
2017c478bd9Sstevel@tonic-gate 			       &enabled, sizeof(enabled), NULL, 0, NULL );
2027c478bd9Sstevel@tonic-gate 		break;
2037c478bd9Sstevel@tonic-gate 	case FORCE :
2047c478bd9Sstevel@tonic-gate 		pnic_command ( nic, PNIC_CMD_FORCE_IRQ,
2057c478bd9Sstevel@tonic-gate 			       NULL, 0, NULL, 0, NULL );
2067c478bd9Sstevel@tonic-gate 		break;
2077c478bd9Sstevel@tonic-gate 	}
2087c478bd9Sstevel@tonic-gate }
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate /**************************************************************************
2117c478bd9Sstevel@tonic-gate PROBE - Look for an adapter, this routine's visible to the outside
2127c478bd9Sstevel@tonic-gate ***************************************************************************/
2137c478bd9Sstevel@tonic-gate 
pnic_probe(struct dev * dev,struct pci_device * pci)2147c478bd9Sstevel@tonic-gate static int pnic_probe(struct dev *dev, struct pci_device *pci)
2157c478bd9Sstevel@tonic-gate {
2167c478bd9Sstevel@tonic-gate 	struct nic *nic = (struct nic *)dev;
2177c478bd9Sstevel@tonic-gate 	static pnic_priv_data_t priv;
2187c478bd9Sstevel@tonic-gate 	uint16_t status;
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	printf(" - ");
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	/* Clear private data structure and chain it in */
2237c478bd9Sstevel@tonic-gate 	memset ( &priv, 0, sizeof(priv) );
2247c478bd9Sstevel@tonic-gate 	nic->priv_data = &priv;
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	/* Mask the bit that says "this is an io addr" */
2277c478bd9Sstevel@tonic-gate 	nic->ioaddr = pci->ioaddr & ~3;
2287c478bd9Sstevel@tonic-gate 	nic->irqno = pci->irq;
2297c478bd9Sstevel@tonic-gate 	/* Not sure what this does, but the rtl8139 driver does it */
2307c478bd9Sstevel@tonic-gate 	adjust_pci_device(pci);
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	status = pnic_command_quiet( nic, PNIC_CMD_API_VER, NULL, 0,
2337c478bd9Sstevel@tonic-gate 				     &priv.api_version,
2347c478bd9Sstevel@tonic-gate 				     sizeof(priv.api_version), NULL );
2357c478bd9Sstevel@tonic-gate 	if ( status != PNIC_STATUS_OK ) {
2367c478bd9Sstevel@tonic-gate 		printf ( "PNIC failed installation check, code %#hx\n",
2377c478bd9Sstevel@tonic-gate 			 status );
2387c478bd9Sstevel@tonic-gate 		return 0;
2397c478bd9Sstevel@tonic-gate 	}
2407c478bd9Sstevel@tonic-gate 	pnic_api_check(priv.api_version);
2417c478bd9Sstevel@tonic-gate 	status = pnic_command ( nic, PNIC_CMD_READ_MAC, NULL, 0,
2427c478bd9Sstevel@tonic-gate 				nic->node_addr, ETH_ALEN, NULL );
2437c478bd9Sstevel@tonic-gate 	printf ( "Detected Bochs Pseudo NIC MAC %! (API v%d.%d) at %#hx\n",
2447c478bd9Sstevel@tonic-gate 		 nic->node_addr, priv.api_version>>8, priv.api_version&0xff,
2457c478bd9Sstevel@tonic-gate 		 nic->ioaddr );
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	/* point to NIC specific routines */
2487c478bd9Sstevel@tonic-gate 	dev->disable  = pnic_disable;
2497c478bd9Sstevel@tonic-gate 	nic->poll     = pnic_poll;
2507c478bd9Sstevel@tonic-gate 	nic->transmit = pnic_transmit;
2517c478bd9Sstevel@tonic-gate 	nic->irq      = pnic_irq;
2527c478bd9Sstevel@tonic-gate 	return 1;
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate static struct pci_id pnic_nics[] = {
2567c478bd9Sstevel@tonic-gate /* genrules.pl doesn't let us use macros for PCI IDs...*/
2577c478bd9Sstevel@tonic-gate PCI_ROM(0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor"),
2587c478bd9Sstevel@tonic-gate };
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate struct pci_driver pnic_driver = {
2617c478bd9Sstevel@tonic-gate 	.type     = NIC_DRIVER,
2627c478bd9Sstevel@tonic-gate 	.name     = "PNIC",
2637c478bd9Sstevel@tonic-gate 	.probe    = pnic_probe,
2647c478bd9Sstevel@tonic-gate 	.ids      = pnic_nics,
2657c478bd9Sstevel@tonic-gate 	.id_count = sizeof(pnic_nics)/sizeof(pnic_nics[0]),
2667c478bd9Sstevel@tonic-gate 	.class    = 0,
2677c478bd9Sstevel@tonic-gate };
268