|
@@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
20
|
20
|
|
21
|
21
|
#include <string.h>
|
22
|
22
|
#include <unistd.h>
|
|
23
|
+#include <byteswap.h>
|
23
|
24
|
#include <pxe.h>
|
24
|
25
|
#include <realmode.h>
|
25
|
26
|
#include <pic8259.h>
|
|
@@ -166,6 +167,10 @@ static int undinet_isr_triggered ( void ) {
|
166
|
167
|
static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
|
167
|
168
|
#define undinet_tbd __use_data16 ( undinet_tbd )
|
168
|
169
|
|
|
170
|
+/** UNDI transmit destination address */
|
|
171
|
+static uint8_t __data16_array ( undinet_destaddr, [ETH_ALEN] );
|
|
172
|
+#define undinet_destaddr __use_data16 ( undinet_destaddr )
|
|
173
|
+
|
169
|
174
|
/**
|
170
|
175
|
* Transmit packet
|
171
|
176
|
*
|
|
@@ -175,8 +180,14 @@ static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
|
175
|
180
|
*/
|
176
|
181
|
static int undinet_transmit ( struct net_device *netdev,
|
177
|
182
|
struct io_buffer *iobuf ) {
|
|
183
|
+ struct undi_nic *undinic = netdev->priv;
|
178
|
184
|
struct s_PXENV_UNDI_TRANSMIT undi_transmit;
|
179
|
|
- size_t len = iob_len ( iobuf );
|
|
185
|
+ const void *ll_dest;
|
|
186
|
+ const void *ll_source;
|
|
187
|
+ uint16_t net_proto;
|
|
188
|
+ unsigned int flags;
|
|
189
|
+ uint8_t protocol;
|
|
190
|
+ size_t len;
|
180
|
191
|
int rc;
|
181
|
192
|
|
182
|
193
|
/* Technically, we ought to make sure that the previous
|
|
@@ -189,15 +200,49 @@ static int undinet_transmit ( struct net_device *netdev,
|
189
|
200
|
* transmit the next packet.
|
190
|
201
|
*/
|
191
|
202
|
|
|
203
|
+ /* Some PXE stacks are unable to cope with P_UNKNOWN, and will
|
|
204
|
+ * always try to prepend a link-layer header. Work around
|
|
205
|
+ * these stacks by stripping the existing link-layer header
|
|
206
|
+ * and allowing the PXE stack to (re)construct the link-layer
|
|
207
|
+ * header itself.
|
|
208
|
+ */
|
|
209
|
+ if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
|
|
210
|
+ &net_proto, &flags ) ) != 0 ) {
|
|
211
|
+ DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
|
|
212
|
+ "%s\n", undinic, strerror ( rc ) );
|
|
213
|
+ return rc;
|
|
214
|
+ }
|
|
215
|
+ memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
|
|
216
|
+ switch ( net_proto ) {
|
|
217
|
+ case htons ( ETH_P_IP ) :
|
|
218
|
+ protocol = P_IP;
|
|
219
|
+ break;
|
|
220
|
+ case htons ( ETH_P_ARP ) :
|
|
221
|
+ protocol = P_ARP;
|
|
222
|
+ break;
|
|
223
|
+ case htons ( ETH_P_RARP ) :
|
|
224
|
+ protocol = P_RARP;
|
|
225
|
+ break;
|
|
226
|
+ default:
|
|
227
|
+ /* Unknown protocol; restore the original link-layer header */
|
|
228
|
+ iob_push ( iobuf, sizeof ( struct ethhdr ) );
|
|
229
|
+ protocol = P_UNKNOWN;
|
|
230
|
+ break;
|
|
231
|
+ }
|
|
232
|
+
|
192
|
233
|
/* Copy packet to UNDI I/O buffer */
|
|
234
|
+ len = iob_len ( iobuf );
|
193
|
235
|
if ( len > sizeof ( basemem_packet ) )
|
194
|
236
|
len = sizeof ( basemem_packet );
|
195
|
237
|
memcpy ( &basemem_packet, iobuf->data, len );
|
196
|
238
|
|
197
|
239
|
/* Create PXENV_UNDI_TRANSMIT data structure */
|
198
|
240
|
memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
|
|
241
|
+ undi_transmit.Protocol = protocol;
|
|
242
|
+ undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
|
|
243
|
+ XMT_BROADCAST : XMT_DESTADDR );
|
199
|
244
|
undi_transmit.DestAddr.segment = rm_ds;
|
200
|
|
- undi_transmit.DestAddr.offset = __from_data16 ( &undinet_tbd );
|
|
245
|
+ undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
|
201
|
246
|
undi_transmit.TBD.segment = rm_ds;
|
202
|
247
|
undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
|
203
|
248
|
|