OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [packages/] [devs/] [eth/] [synth/] [ecosynth/] [v2_0/] [host/] [rawether.c] - Blame information for rev 377

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 27 unneback
//============================================================================
2
//
3
//     rawether.c
4
//
5
//     A utility program to perform low-level ethernet operations
6
//
7
//============================================================================
8
//####COPYRIGHTBEGIN####
9
//                                                                          
10
// ----------------------------------------------------------------------------
11
// Copyright (C) 2002 Bart Veer
12
//
13
// This file is part of the eCos host tools.
14
//
15
// This program is free software; you can redistribute it and/or modify it 
16
// under the terms of the GNU General Public License as published by the Free 
17
// Software Foundation; either version 2 of the License, or (at your option) 
18
// any later version.
19
// 
20
// This program is distributed in the hope that it will be useful, but WITHOUT 
21
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
22
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
23
// more details.
24
// 
25
// You should have received a copy of the GNU General Public License along with
26
// this program; if not, write to the Free Software Foundation, Inc., 
27
// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28
// ----------------------------------------------------------------------------
29
//                                                                          
30
//####COPYRIGHTEND####
31
//============================================================================
32
//#####DESCRIPTIONBEGIN####
33
//
34
// Author(s):   bartv
35
// Contact(s):  bartv
36
// Date:        2002/08/07
37
// Version:     0.01
38
// Description:
39
//
40
// This program is fork'ed by the ethernet.tcl script running inside
41
// the synthetic target auxiliary. It is responsible for performing
42
// low-level ethernet I/O.
43
//
44
//####DESCRIPTIONEND####
45
//============================================================================
46
 
47
#include <stdio.h>
48
#include <stdlib.h>
49
#include <string.h>
50
#include <time.h>
51
#include <signal.h>
52
#include <limits.h>
53
#include <unistd.h>
54
#include <fcntl.h>
55
#include <errno.h>
56
#include <sys/param.h>
57
#include <sys/types.h>
58
#include <sys/socket.h>
59
#include <sys/ioctl.h>
60
#include <net/if.h>
61
#include <net/if_arp.h>
62
#include <netinet/in.h>
63
#include <netinet/if_ether.h>
64
#include <linux/if_packet.h>
65
#include <linux/if_ether.h>
66
#ifdef HAVE_LINUX_IF_TUN_H
67
# include <linux/if_tun.h>
68
#endif
69
 
70
// The protocol between host and target is defined by a private
71
// target-side header.
72
#include "../src/protocol.h"
73
 
74
// Allow debug builds. Set this flag to 0, 1 or 2
75
#define DEBUG 0
76
 
77
// ----------------------------------------------------------------------------
78
// Statics.
79
 
80
// Are we using a real ethernet device or ethertap?
81
static int real_ether = 0;
82
static int ethertap   = 0;
83
 
84
// The six-byte MAC address, which must be returned to eCos
85
static unsigned char MAC[6];
86
 
87
// Does the driver support multicasting?
88
static int multicast_supported = 0;
89
 
90
// The file descriptor for incoming data ethernet packets.
91
// Used for select() together with fd 0 corresponding to ecosynth
92
static int ether_fd = -1;
93
 
94
// Is the interface up?
95
static int up = 0;
96
 
97
// Space for incoming and outgoing packets. In the case of rx_buffer
98
// there are an extra four bytes at the front for the protocol header.
99
#define MTU 1514
100
static unsigned char tx_buffer[MTU];
101
static unsigned char rx_buffer[MTU+4];
102
 
103
// Indirect to get to the actual implementation functions.
104
static void (*tx_fn)(unsigned char*, int);
105
static void (*rx_fn)(void);
106
static void (*start_fn)(int);
107
static void (*stop_fn)(void);
108
static void (*multicast_fn)(int);
109
 
110
 
111
// ----------------------------------------------------------------------------
112
// A utility buffer for messages.
113
#define MSG_SIZE 256
114
static unsigned char msg[MSG_SIZE];
115
 
116
// Report an error to ecosynth during initialization. This means a
117
// single byte 0, followed by a string.
118
static void
119
report_error(char* msg)
120
{
121
    write(1, "0", 1);
122
    write(1, msg, strlen(msg));
123
    close(1);
124
    exit(0);
125
}
126
 
127
// Report success to ecosynth. This means a byte 1 followed by
128
// the MAC address.
129
static void
130
report_success(void)
131
{
132
    write(1, "1", 1);
133
    memcpy(msg, MAC, 6);
134
    msg[6] = multicast_supported;
135
    write(1, msg, 7);
136
}
137
 
138
 
139
// ----------------------------------------------------------------------------
140
// Real ethernet. This involves creating a SOCK_RAW socket and binding it
141
// to the appropriate interface. Relevant documentation can be found in
142
// the man pages (packet(7) and netdevice(7)).
143
 
144
// The device name. Needed for various ioctl()'s.
145
static char real_devname[IFNAMSIZ];
146
 
147
// The interface index.
148
static int real_ifindex = -1;
149
 
150
// Transmit a single ethernet frame. The socket should be set up so a
151
// simple send() operation should do the trick. Errors such as EAGAIN,
152
// indicating that the network device is still busy, are ignored.
153
// Ethernet is not a reliable communication medium.
154
static void
155
real_handle_tx(unsigned char* buffer, int size)
156
{
157
    int result;
158
 
159
    result = send(ether_fd, buffer, size, MSG_DONTWAIT);
160
    if (result < 0) {
161
        // It appears that one retry is worthwhile, to clear pending
162
        // errors or something.
163
        result = send(ether_fd, buffer, size, MSG_DONTWAIT);
164
    }
165
#if (DEBUG > 0)
166
    fprintf(stderr, "rawether dbg: tx %d bytes -> %d\n", size, result);
167
#endif
168
#if (DEBUG > 1)
169
    fprintf(stderr, "    %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
170
            buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5],
171
            buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11],
172
            buffer[12], buffer[13]);
173
#endif    
174
}
175
 
176
// Receive a single ethernet frame, using the static rxbuffer. If the
177
// interface is not currently up discard it. Otherwise forward it on
178
// to ecosynth.
179
 
180
static void
181
real_handle_rx(void)
182
{
183
    int             size;
184
    int             result;
185
 
186
    size = recv(ether_fd, rx_buffer + 4, MTU, MSG_TRUNC);
187
 
188
#if (DEBUG > 0)
189
    fprintf(stderr, "rawether dbg: rx returned %d, errno %s (%d)\n", size, (errno < sys_nerr) ? sys_errlist[errno] : "<unknown>", errno);
190
#endif
191
 
192
    if (size < 0) {
193
        return;     // Ignore errors, just go around the main loop again.
194
    }
195
    if ((size < 14) || (size > MTU)) {
196
        return;     // Invalid packet size. Discard the packet.
197
    }
198
 
199
#if (DEBUG > 1)
200
    fprintf(stderr, "    %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
201
            rx_buffer[4], rx_buffer[5], rx_buffer[6], rx_buffer[7], rx_buffer[8], rx_buffer[9],
202
            rx_buffer[10], rx_buffer[11], rx_buffer[12], rx_buffer[13], rx_buffer[14], rx_buffer[15],
203
            rx_buffer[16], rx_buffer[17]);
204
#endif    
205
 
206
    if (!up) {
207
        // eCos is not currently expecting packets, so discard them.
208
        // This may not actually be necessary because the interface
209
        // is only up when eCos wants it to be up.
210
        return;
211
    }
212
 
213
    // It looks this packet should get forwarded to eCos.
214
    rx_buffer[0] = SYNTH_ETH_RX;
215
    rx_buffer[1] = 0;
216
    rx_buffer[2] = size & 0x00FF;
217
    rx_buffer[3] = (size >> 8) & 0x00FF;
218
    do {
219
        result = write(1, rx_buffer, 4 + size);
220
    } while ((-1 == result) && (EINTR == errno));
221
 
222
    if (result != (size + 4)) {
223
        fprintf(stderr, "rawether(%s): failed to send ethernet packet to I/O auxiliary, exiting.\n", real_devname);
224
        exit(1);
225
    }
226
}
227
 
228
// Utility to manipulate interface flags. This involves retrieving the
229
// current flags, or'ing in some bits, and'ing out others, and updating.
230
static void
231
real_update_ifflags(int set_bits, int clear_bits)
232
{
233
    struct ifreq    request;
234
    int             flags;
235
 
236
    strncpy(request.ifr_name, real_devname, IFNAMSIZ);
237
    if (ioctl(ether_fd, SIOCGIFFLAGS, &request) < 0) {
238
        fprintf(stderr, "rawether (%s): failed to get interface flags, exiting\n", real_devname);
239
        exit(1);
240
    }
241
 
242
    flags = request.ifr_flags;
243
 
244
    flags |= set_bits;
245
    flags &= ~clear_bits;
246
 
247
    if (flags == request.ifr_flags) {
248
        // Nothing is changing.
249
        return;
250
    }
251
 
252
    strncpy(request.ifr_name, real_devname, IFNAMSIZ);
253
    request.ifr_flags = flags;
254
    if (ioctl(ether_fd, SIOCSIFFLAGS, &request) < 0) {
255
        fprintf(stderr, "rawether (%s): failed to update interface flags, exiting\n", real_devname);
256
        exit(1);
257
    }
258
}
259
 
260
 
261
// Starting an interface. This involves bringing the interface up,
262
// and optionally setting promiscuous mode.
263
// NOTE: is UP really the right thing here? There is no IP address
264
// for this interface. In theory this should not matter because
265
// we have a bound socket which should receive all packets for
266
// this interface.
267
 
268
static void
269
real_handle_start(int promiscuous)
270
{
271
    if (promiscuous) {
272
        real_update_ifflags(IFF_UP | IFF_PROMISC, 0);
273
    } else {
274
        real_update_ifflags(IFF_UP, IFF_PROMISC);
275
    }
276
    up = 1;
277
}
278
 
279
// Stopping an interface means clearing the UP flag
280
static void
281
real_handle_stop(void)
282
{
283
    up = 0;
284
    real_update_ifflags(0, IFF_UP | IFF_PROMISC);
285
}
286
 
287
// Enabling/disabling multicast support.
288
static void
289
real_handle_multiall(int on)
290
{
291
    struct packet_mreq req;
292
 
293
    req.mr_ifindex  = real_ifindex;
294
    req.mr_type     = PACKET_MR_ALLMULTI;
295
    req.mr_alen     = 0;
296
    if (setsockopt(ether_fd, SOL_PACKET, on ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, (void*)&req, sizeof(req)) < 0) {
297
        fprintf(stderr, "rawether (%s): failed to manipulate multicast-all flag, exiting\n", real_devname);
298
        exit(1);
299
    }
300
}
301
 
302
// When the application exists make sure that the interface goes down again.
303
 
304
static void
305
real_atexit(void)
306
{
307
    if (up) {
308
        real_update_ifflags(0, IFF_UP | IFF_PROMISC);
309
    }
310
}
311
 
312
static void
313
real_init(char* devname)
314
{
315
    struct sockaddr_ll  addr;
316
    struct ifreq        request;
317
 
318
    tx_fn           = &real_handle_tx;
319
    rx_fn           = &real_handle_rx;
320
    start_fn        = &real_handle_start;
321
    stop_fn         = &real_handle_stop;
322
    multicast_fn    = &real_handle_multiall;
323
 
324
    if (strlen(devname) >= IFNAMSIZ) {
325
        snprintf(msg, MSG_SIZE, "Invalid real network device name \"%s\", too long.\n", devname);
326
        report_error(msg);
327
    }
328
    strcpy(real_devname, devname);
329
 
330
    // All ioctl() operations need a socket. We might as well create the
331
    // raw socket immediately and use that.
332
    ether_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
333
    if (ether_fd < 0) {
334
        snprintf(msg, MSG_SIZE, "Unable to create a raw socket for accessing network device\n"
335
                 "    Error %s (errno %d)\n", (errno < sys_nerr) ? sys_errlist[errno] : "unknown", errno);
336
        report_error(msg);
337
    }
338
 
339
    strncpy(request.ifr_name, real_devname, IFNAMSIZ);
340
    if (ioctl(ether_fd, SIOCGIFINDEX, &request) < 0) {
341
        snprintf(msg, MSG_SIZE, "Device %s does not correspond to a valid interface.\n"
342
                 "    Error %s (errno %d)\n", real_devname, (errno < sys_nerr) ? sys_errlist[errno] : "unknown", errno);
343
        report_error(msg);
344
    }
345
    real_ifindex = request.ifr_ifindex;
346
 
347
    // The interface exists. Now check that it is usable.
348
    strncpy(request.ifr_name, real_devname, IFNAMSIZ);
349
    if (ioctl(ether_fd, SIOCGIFFLAGS, &request) < 0) {
350
        snprintf(msg, MSG_SIZE, "Failed to get current interface flags for %s\n"
351
                 "    Error %s (errno %d)\n", real_devname, (errno < sys_nerr) ? sys_errlist[errno] : "unknown", errno);
352
        report_error(msg);
353
    }
354
 
355
    if (request.ifr_flags & (IFF_UP | IFF_RUNNING)) {
356
        snprintf(msg, MSG_SIZE, "Network device %s is already up and running.\n"
357
                 "    Exclusive access is required\n", real_devname);
358
        report_error(msg);
359
    }
360
    if (request.ifr_flags & IFF_LOOPBACK) {
361
        report_error("Loopback devices cannot be used for synthetic target ethernet emulation.\n");
362
    }
363
    if (request.ifr_flags & IFF_POINTOPOINT) {
364
        report_error("Point-to-point devices cannot be used for synthetic target ethernet emulation.\n");
365
    }
366
    if (request.ifr_flags & IFF_MULTICAST) {
367
        multicast_supported = 1;
368
    }
369
 
370
    // Make sure the interface is down. There is no point in receiving packets just yet.
371
    real_update_ifflags(0, IFF_UP | IFF_PROMISC);
372
 
373
    // The flags look ok. Now get hold of the hardware address.
374
    strncpy(request.ifr_name, real_devname, IFNAMSIZ);
375
    if (ioctl(ether_fd, SIOCGIFHWADDR, &request) < 0) {
376
        snprintf(msg, MSG_SIZE, "Failed to get hardware address for %s\n"
377
                 "    Error %s (errno %d)\n", real_devname, (errno < sys_nerr) ? sys_errlist[errno] : "unknown", errno);
378
        report_error(msg);
379
    }
380
    if (ARPHRD_ETHER != request.ifr_hwaddr.sa_family) {
381
        snprintf(msg, MSG_SIZE, "Device %s is not an ethernet device.\n", real_devname);
382
        report_error(msg);
383
    }
384
    memcpy(MAC, request.ifr_hwaddr.sa_data, 6);
385
 
386
    // The device is useable. Now just bind the socket to the appropriate address.
387
    addr.sll_family     = AF_PACKET;
388
    addr.sll_protocol   = htons(ETH_P_ALL);
389
    addr.sll_ifindex    = real_ifindex;
390
    if (bind(ether_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
391
        snprintf(msg, MSG_SIZE, "Failed to bind socket for direct hardware address to %s\n"
392
                 "    Error %s (errno %d)\n", real_devname, (errno < sys_nerr) ? sys_errlist[errno] : "unknown", errno);
393
        report_error(msg);
394
    }
395
 
396
    // Make sure the interface gets shut down when rawether exits.
397
    atexit(real_atexit);
398
 
399
    // And that should be it.
400
}
401
 
402
// ----------------------------------------------------------------------------
403
// Ethertap device.
404
//
405
// See /usr/src/linux-2.x.y/Documentation/networking/tuntap.txt for more
406
// information on the tun/tap driver.
407
//
408
// Basically during initialization this code opens /dev/net/tun, then
409
// performs a TUNSETIFF ioctl() to initialize it. This causes a
410
// new network device tap?? to appear. Any ethernet frames written
411
// by the Linux kernel to this device can be read from the
412
// dev/net/tun file descriptor, and ethernet frames can be written to
413
// the same descriptor. The net effect is a virtual ethernet segment
414
// with one interface managed by the Linux kernel and another
415
// interface (or, theoretically, several) accessible via the file
416
// descriptor.
417
//
418
// The Linux kernel will invent a MAC address for its interface. An
419
// additional one is needed for eCos. This is either invented or
420
// specified in the target definition file.
421
//
422
// Old Linux kernels may not have the required support. This is detected
423
// by an autoconf test for <linux/if_tun.h>
424
#ifdef HAVE_LINUX_IF_TUN_H
425
 
426
static void
427
tap_handle_tx(unsigned char* buffer, int size)
428
{
429
    int result;
430
 
431
    result = write(ether_fd, buffer, size);
432
#if (DEBUG > 0)
433
    fprintf(stderr, "rawether dbg: tx %d bytes -> %d\n", size, result);
434
#endif
435
#if (DEBUG > 1)
436
    fprintf(stderr, "    %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
437
            buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5],
438
            buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11],
439
            buffer[12], buffer[13]);
440
#endif    
441
}
442
 
443
// Receive a single packet from the socket. It is assumed that the
444
// tuntap code inside the kernel will preserve packet boundaries.
445
//
446
// For now it is assumed that all incoming packets are intended for
447
// eCos. That may not be accurate, and additional filtering in
448
// software might be appropriate. In promiscuous mode all packets
449
// should be accepted, obviously. Otherwise all broadcasts should
450
// be accepted, as should all messages intended for this specific
451
// interface's MAC address. Multicasts should be accepted only if
452
// enabled.
453
static void
454
tap_handle_rx(void)
455
{
456
    int             size;
457
    int             result;
458
 
459
    // select() has succeeded so this read() should never block.
460
    size = read(ether_fd, rx_buffer + 4, MTU);
461
#if (DEBUG > 0)
462
    fprintf(stderr, "rawether dbg: rx returned %d, errno %s (%d)\n", size, (errno < sys_nerr) ? sys_errlist[errno] : "<unknown>", errno);
463
#endif
464
 
465
    if (size < 0) {
466
        return;     // Ignore errors, just go around the main loop again.
467
    }
468
    if ((size < 14) || (size > MTU)) {
469
        return;     // Invalid packet size. Discard the packet.
470
    }
471
 
472
#if (DEBUG > 1)
473
    fprintf(stderr, "    %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
474
            rx_buffer[4], rx_buffer[5], rx_buffer[6], rx_buffer[7], rx_buffer[8], rx_buffer[9],
475
            rx_buffer[10], rx_buffer[11], rx_buffer[12], rx_buffer[13], rx_buffer[14], rx_buffer[15],
476
            rx_buffer[16], rx_buffer[17]);
477
#endif    
478
 
479
    if (!up) {
480
        // eCos is not currently expecting packets, so discard them.
481
        return;
482
    }
483
 
484
    // It looks this packet should get forwarded to eCos.
485
    rx_buffer[0] = SYNTH_ETH_RX;
486
    rx_buffer[1] = 0;
487
    rx_buffer[2] = size & 0x00FF;
488
    rx_buffer[3] = (size >> 8) & 0x00FF;
489
    do {
490
        result = write(1, rx_buffer, 4 + size);
491
    } while ((-1 == result) && (EINTR == errno));
492
 
493
    if (result != (size + 4)) {
494
        fprintf(stderr, "rawether(%s): failed to send ethernet packet to I/O auxiliary, exiting.\n", real_devname);
495
        exit(1);
496
    }
497
}
498
 
499
// Nothing much can be done for start or stop. Just set the flag and
500
// let the rx and tx code discard packets when appropriate.
501
//
502
// For now the device is implicitly promiscuous and accepts all
503
// multicasts. Given the nature of a tap device it is unlikely that
504
// any packets will arrive which are not destined here.
505
// FIXME: this may have to change if bridging is enabled.
506
static void
507
tap_handle_start(int promiscuous)
508
{
509
    up = 1;
510
}
511
 
512
static void
513
tap_handle_stop(void)
514
{
515
    up = 0;
516
}
517
 
518
static void
519
tap_handle_multiall(int on)
520
{
521
}
522
 
523
static void
524
tap_init(int argc, char** argv)
525
{
526
    char* devname   = NULL;
527
    struct ifreq    ifr;
528
 
529
    tx_fn           = &tap_handle_tx;
530
    rx_fn           = &tap_handle_rx;
531
    start_fn        = &tap_handle_start;
532
    stop_fn         = &tap_handle_stop;
533
    multicast_fn    = &tap_handle_multiall;
534
 
535
    // Which device? By default let the system pick one, but the user
536
    // can override this.
537
    if (0 != argc) {
538
        devname = argv[0];
539
    }
540
 
541
    // Work out the MAC address. By default a random one is generated,
542
    // but the user can specify one to avoid a source of randomness.
543
    // This MAC address is not actually needed by any of the code here,
544
    // but should be returned to eCos.
545
    if (2 == argc) {
546
        unsigned int mac_data[6];   // sscanf() needs unsigned ints
547
        int result = sscanf(argv[1], "%x:%x:%x:%x:%x:%x",
548
                            &(mac_data[0]), &(mac_data[1]), &(mac_data[2]),
549
                            &(mac_data[3]), &(mac_data[4]), &(mac_data[5]));
550
        if (6 != result) {
551
            snprintf(msg, MSG_SIZE, "Invalid MAC address %s\n", argv[1]);
552
            report_error(msg);
553
        }
554
        MAC[0] = mac_data[0];
555
        MAC[1] = mac_data[1];
556
        MAC[2] = mac_data[2];
557
        MAC[3] = mac_data[3];
558
        MAC[4] = mac_data[4];
559
        MAC[5] = mac_data[5];
560
    } else {
561
        srand(time(NULL));
562
        MAC[0] = 0;
563
        MAC[1] = 0x0FF;
564
        MAC[2] = rand() & 0x0FF;
565
        MAC[3] = rand() & 0x0FF;
566
        MAC[4] = rand() & 0x0FF;
567
        MAC[5] = rand() & 0x0FF;
568
    }
569
 
570
    ether_fd = open("/dev/net/tun", O_RDWR);
571
    if (ether_fd < 0) {
572
        snprintf(msg, MSG_SIZE, "Failed to open /dev/net/tun, errno %s (%d)\n", (errno < sys_nerr) ? sys_errlist[errno] : "<unknown>", errno);
573
        report_error(msg);
574
    }
575
 
576
    memset(&ifr, 0, sizeof(ifr));
577
    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
578
    if (NULL != devname) {
579
        strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
580
    }
581
    if (ioctl(ether_fd, TUNSETIFF, (void*)&ifr) < 0) {
582
        snprintf(msg, MSG_SIZE, "Failed to initialize /dev/net/tun, errno %s (%d)\n", (errno < sys_nerr) ? sys_errlist[errno] : "<unknown>", errno);
583
        report_error(msg);
584
    }
585
 
586
    // Supporting multicasts is a no-op
587
    multicast_supported = 1;
588
 
589
    // All done.
590
}
591
#else
592
static void
593
tap_init(int argc, char** argv)
594
{
595
    snprintf(msg, MSG_SIZE, "Ethertap support was not available when the host-side support was built\n");
596
    report_error(msg);
597
}
598
#endif  // HAVE_LINUX_IF_TUN_H
599
 
600
// ----------------------------------------------------------------------------
601
// Receive a single request from ecosynth. This consists of a four-byte
602
// header, optionally followed by a tx packet. EOF indicates that
603
// ecosynth has exited, so this process should just exit immediately
604
// as well. Any problems should be reported to stderr, followed by
605
// termination.
606
//
607
// Currently rawether is single-threaded. Theoretically this could
608
// cause a deadlock situation where the I/O auxiliary is trying to send
609
// rawether a request and is blocked on the write, while rawether is trying
610
// to send data to the I/O auxiliary. In practice the pipes should do
611
// enough buffering to avoid complications, especially since rawether
612
// gives priority to requests from the auxiliary.
613
 
614
static void
615
handle_ecosynth_request(void)
616
{
617
    unsigned char   req[4];
618
    int             result;
619
    int             code, arg, size;
620
 
621
    result = read(0, req, 4);
622
    if (result == 0) {
623
        // select() succeeded but no data. EOF. So exit
624
        exit(0);
625
    }
626
    if (result < 0) {
627
        // EINTR? EAGAIN? The latter should not happen since the pipe
628
        // has not been put into non-blocking mode.
629
        if ((EINTR == errno) || (EAGAIN == errno)) {
630
            return;
631
        } else {
632
            fprintf(stderr, "rawether: unexpected error reading request from ecosynth\n");
633
            if (errno < sys_nerr) {
634
                fprintf(stderr, "    %s\n", sys_errlist[errno]);
635
            }
636
            exit(1);
637
        }
638
    }
639
    if (result < 4) {
640
        fprintf(stderr, "rawether: unexpected error reading request from ecosynth\n    Expected 4 bytes, only received %d\n", result);
641
        exit(1);
642
    }
643
 
644
    code = req[0];
645
    arg  = req[1];
646
    size = req[2] + (req[3] << 8);
647
 
648
#if (DEBUG > 1)
649
    fprintf(stderr, "rawether dbg: request %x from auxiliary\n", code);
650
#endif    
651
 
652
    switch(code) {
653
      case SYNTH_ETH_TX:
654
        {
655
            if (size < 14) {
656
                fprintf(stderr, "rawether: attempt to send invalid ethernet packet of only %d bytes\n"
657
                        "Ethernet packets should be at least 14 bytes.\n", size);
658
                exit(1);
659
            }
660
            if (size > MTU) {
661
                fprintf(stderr, "rawether: attempt to send invalid ethernet packet of %d bytes\n"
662
                        "Only packets of up to %d bytes are supported.\n", size, MTU);
663
                exit(1);
664
            }
665
            do {
666
                result = read(0, tx_buffer, size);
667
            } while ((-1 == result) && (EINTR == errno));
668
            if (0 == result) {
669
                // EOF, at an inopportune moment
670
                exit(0);
671
            }
672
            if (result < size) {
673
                fprintf(stderr, "rawether: error reading ethernet packet from I/O auxiliary\n"
674
                        "Expected %d bytes but only read %d\n", size, result);
675
                exit(1);
676
            }
677
 
678
            (*tx_fn)(tx_buffer, size);
679
            break;
680
        }
681
      case SYNTH_ETH_START:
682
        {
683
            (*start_fn)(arg);
684
            break;
685
        }
686
 
687
      case SYNTH_ETH_STOP:
688
        {
689
            (*stop_fn)();
690
            break;
691
        }
692
 
693
      case SYNTH_ETH_MULTIALL:
694
        {
695
            (*multicast_fn)(arg);
696
            break;
697
        }
698
 
699
        // SYNTH_ETH_RX and SYNTH_ETH_GETPARAMS are handled inside ethernet.tcl
700
 
701
      default:
702
        fprintf(stderr, "rawether: protocol violation, received unknown request %d\n", code);
703
        exit(1);
704
    }
705
}
706
 
707
// The main loop. This waits for an event either from ecosynth or from
708
// the underlying ethernet device, using select. Requests from
709
// ecosynth are handled, and take priority to prevent the connecting
710
// pipe from filling up and ecosynth blocking. Incoming ethernet
711
// frames are forwarded to ecosynth.
712
 
713
static void
714
mainloop(void)
715
{
716
    fd_set          read_set;
717
    struct timeval  timeout;
718
    int result;
719
 
720
    for ( ; ; ) {
721
        FD_ZERO(&read_set);
722
        FD_SET(0, &read_set);
723
        FD_SET(ether_fd, &read_set);
724
        timeout.tv_sec  = 24 * 60 * 60;
725
        timeout.tv_usec = 0;
726
 
727
        result = select(ether_fd + 1, &read_set, NULL, NULL, &timeout);
728
        if (result <= 0) {
729
            continue;
730
        }
731
 
732
        if (FD_ISSET(0, &read_set)) {
733
            handle_ecosynth_request();
734
        } else if (FD_ISSET(ether_fd, &read_set)) {
735
            (*rx_fn)();
736
        }
737
    }
738
}
739
 
740
// ----------------------------------------------------------------------------
741
 
742
int
743
main(int argc, char**argv)
744
{
745
    // Ignore incoming ctrl-C's. We are in the same process group as the
746
    // eCos application which may sensibly be ctrl-C'd, but that should
747
    // result in the auxiliary detecting EOF and closing the pipe to
748
    // this process, which in turn causes this process to exit completely.
749
    signal(SIGINT, SIG_IGN);
750
 
751
    if (2 > argc ) {
752
        report_error("Expected at least one argument, \"real\" or \"ethertap\"\n");
753
    }
754
    if (0 == strcmp("real", argv[1])) {
755
        real_ether = 1;
756
        real_init(argv[2]);
757
    } else if (0 == strcmp("ethertap", argv[1])) {
758
        ethertap = 1;
759
        tap_init(argc - 2, argv + 2);
760
    } else {
761
        snprintf(msg, MSG_SIZE, "Invalid argument %s, expected \"real\" or \"ethertap\"\n", argv[1]);
762
        report_error(msg);
763
    }
764
 
765
    // If the device-specific initialization succeeded we must be set.
766
    report_success();
767
 
768
    mainloop();
769
 
770
    return 0;
771
}
772
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.