summaryrefslogtreecommitdiffstats
path: root/src/drivers/net/legacy.c
blob: 73a80194fd661714a3e1766a206a3900b03ff4be (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <ipxe/if_ether.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/iobuf.h>
#include <nic.h>

/*
 * Quick and dirty compatibility layer
 *
 * This should allow old-API PCI drivers to at least function until
 * they are updated.  It will not help non-PCI drivers.
 *
 * No drivers should rely on this code.  It will be removed asap.
 *
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

struct nic nic;

static int legacy_registered = 0;

static int legacy_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
	struct nic *nic = netdev->priv;
	struct ethhdr *ethhdr;

	DBG ( "Transmitting %zd bytes\n", iob_len ( iobuf ) );
	iob_pad ( iobuf, ETH_ZLEN );
	ethhdr = iobuf->data;
	iob_pull ( iobuf, sizeof ( *ethhdr ) );
	nic->nic_op->transmit ( nic, ( const char * ) ethhdr->h_dest,
				ntohs ( ethhdr->h_protocol ),
				iob_len ( iobuf ), iobuf->data );
	netdev_tx_complete ( netdev, iobuf );
	return 0;
}

static void legacy_poll ( struct net_device *netdev ) {
	struct nic *nic = netdev->priv;
	struct io_buffer *iobuf;

	iobuf = alloc_iob ( ETH_FRAME_LEN );
	if ( ! iobuf )
		return;

	nic->packet = iobuf->data;
	if ( nic->nic_op->poll ( nic, 1 ) ) {
		DBG ( "Received %d bytes\n", nic->packetlen );
		iob_put ( iobuf, nic->packetlen );
		netdev_rx ( netdev, iobuf );
	} else {
		free_iob ( iobuf );
	}
}

static int legacy_open ( struct net_device *netdev __unused ) {
	/* Nothing to do */
	return 0;
}

static void legacy_close ( struct net_device *netdev __unused ) {
	/* Nothing to do */
}

static void legacy_irq ( struct net_device *netdev __unused, int enable ) {
	struct nic *nic = netdev->priv;

	nic->nic_op->irq ( nic, ( enable ? ENABLE : DISABLE ) );
}

static struct net_device_operations legacy_operations = {
	.open		= legacy_open,
	.close		= legacy_close,
	.transmit	= legacy_transmit,
	.poll		= legacy_poll,
	.irq   		= legacy_irq,
};

int legacy_probe ( void *hwdev,
		   void ( * set_drvdata ) ( void *hwdev, void *priv ),
		   struct device *dev,
		   int ( * probe ) ( struct nic *nic, void *hwdev ),
		   void ( * disable ) ( struct nic *nic, void *hwdev ) ) {
	struct net_device *netdev;
	int rc;

	if ( legacy_registered )
		return -EBUSY;
	
	netdev = alloc_etherdev ( 0 );
	if ( ! netdev )
		return -ENOMEM;
	netdev_init ( netdev, &legacy_operations );
	netdev->priv = &nic;
	memset ( &nic, 0, sizeof ( nic ) );
	set_drvdata ( hwdev, netdev );
	netdev->dev = dev;

	nic.node_addr = netdev->hw_addr;
	nic.irqno = dev->desc.irq;

	if ( ! probe ( &nic, hwdev ) ) {
		rc = -ENODEV;
		goto err_probe;
	}

	/* Overwrite the IRQ number.  Some legacy devices set
	 * nic->irqno to 0 in the probe routine to indicate that they
	 * don't support interrupts; doing this allows the timer
	 * interrupt to be used instead.
	 */
	dev->desc.irq = nic.irqno;

	if ( ( rc = register_netdev ( netdev ) ) != 0 )
		goto err_register;

	/* Mark as link up; legacy devices don't handle link state */
	netdev_link_up ( netdev );

	/* Do not remove this message */
	printf ( "WARNING: Using legacy NIC wrapper on %s\n",
		 netdev->ll_protocol->ntoa ( nic.node_addr ) );

	legacy_registered = 1;
	return 0;

 err_register:
	disable ( &nic, hwdev );
 err_probe:
	netdev_nullify ( netdev );
	netdev_put ( netdev );
	return rc;
}

void legacy_remove ( void *hwdev,
		     void * ( * get_drvdata ) ( void *hwdev ),
		     void ( * disable ) ( struct nic *nic, void *hwdev ) ) {
	struct net_device *netdev = get_drvdata ( hwdev );
	struct nic *nic = netdev->priv;

	unregister_netdev ( netdev );
	disable ( nic, hwdev );
	netdev_nullify ( netdev );
	netdev_put ( netdev );
	legacy_registered = 0;
}

int dummy_connect ( struct nic *nic __unused ) {
	return 1;
}

void dummy_irq ( struct nic *nic __unused, irq_action_t irq_action __unused ) {
	return;
}