ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/Linux/ether_linux.cpp
Revision: 1.2
Committed: 2004-01-12T15:55:51Z (20 years, 10 months ago) by cebix
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * ether_linux.cpp - SheepShaver Ethernet Device Driver (DLPI), Linux specific stuff
3     *
4 cebix 1.2 * SheepShaver (C) 1997-2004 Marc Hellwig and Christian Bauer
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include <pthread.h>
22     #include <semaphore.h>
23     #include <unistd.h>
24     #include <fcntl.h>
25     #include <errno.h>
26     #include <sys/ioctl.h>
27     #include <sys/poll.h>
28     #include <stdio.h>
29     #include <string.h>
30    
31     #include "sysdeps.h"
32     #include "main.h"
33     #include "macos_util.h"
34     #include "prefs.h"
35     #include "user_strings.h"
36     #include "ether.h"
37     #include "ether_defs.h"
38    
39     #define DEBUG 0
40     #include "debug.h"
41    
42     #define STATISTICS 0
43     #define MONITOR 0
44    
45    
46     // Global variables
47     static int fd = -1; // fd of sheep_net device
48    
49     static pthread_t ether_thread; // Packet reception thread
50     static bool thread_active = false; // Flag: Packet reception thread installed
51    
52     static sem_t int_ack; // Interrupt acknowledge semaphore
53     static uint8 ether_addr[6]; // Our Ethernet address
54    
55     static bool net_open = false; // Flag: initialization succeeded, network device open
56     static bool is_ethertap = false; // Flag: Ethernet device is ethertap
57    
58    
59     // Prototypes
60     static void *receive_func(void *arg);
61    
62    
63     /*
64     * Initialize ethernet
65     */
66    
67     void EtherInit(void)
68     {
69     int nonblock = 1;
70     char str[256];
71    
72     // Do nothing if the user disabled the network
73     if (PrefsFindBool("nonet"))
74     return;
75    
76     // Do nothing if no Ethernet device specified
77     const char *name = PrefsFindString("ether");
78     if (name == NULL)
79     return;
80    
81     // Is it Ethertap?
82     is_ethertap = (strncmp(name, "tap", 3) == 0);
83    
84     // Open sheep_net or ethertap device
85     char dev_name[16];
86     if (is_ethertap)
87     sprintf(dev_name, "/dev/%s", name);
88     else
89     strcpy(dev_name, "/dev/sheep_net");
90     fd = open(dev_name, O_RDWR);
91     if (fd < 0) {
92     sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno));
93     WarningAlert(str);
94     goto open_error;
95     }
96    
97     // Attach to selected Ethernet card
98     if (!is_ethertap && ioctl(fd, SIOCSIFLINK, name) < 0) {
99     sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno));
100     WarningAlert(str);
101     goto open_error;
102     }
103    
104     // Set nonblocking I/O
105     ioctl(fd, FIONBIO, &nonblock);
106    
107     // Get Ethernet address
108     if (is_ethertap) {
109     pid_t p = getpid(); // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID
110     ether_addr[0] = 0xfe;
111     ether_addr[1] = 0xfd;
112     ether_addr[2] = p >> 24;
113     ether_addr[3] = p >> 16;
114     ether_addr[4] = p >> 8;
115     ether_addr[5] = p;
116     } else
117     ioctl(fd, SIOCGIFADDR, ether_addr);
118     D(bug("Ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5]));
119    
120     // Start packet reception thread
121     if (sem_init(&int_ack, 0, 0) < 0) {
122     WarningAlert("WARNING: Cannot init semaphore");
123     goto open_error;
124     }
125     thread_active = (pthread_create(&ether_thread, NULL, receive_func, NULL) == 0);
126     if (!thread_active) {
127     WarningAlert("WARNING: Cannot start Ethernet thread");
128     goto open_error;
129     }
130    
131     // Everything OK
132     net_open = true;
133     return;
134    
135     open_error:
136     if (thread_active) {
137     pthread_cancel(ether_thread);
138     pthread_join(ether_thread, NULL);
139     sem_destroy(&int_ack);
140     thread_active = false;
141     }
142     if (fd > 0) {
143     close(fd);
144     fd = -1;
145     }
146     }
147    
148    
149     /*
150     * Exit ethernet
151     */
152    
153     void EtherExit(void)
154     {
155     // Stop reception thread
156     if (thread_active) {
157     pthread_cancel(ether_thread);
158     pthread_join(ether_thread, NULL);
159     sem_destroy(&int_ack);
160     thread_active = false;
161     }
162    
163     // Close sheep_net device
164     if (fd > 0)
165     close(fd);
166    
167     #if STATISTICS
168     // Show statistics
169     printf("%ld messages put on write queue\n", num_wput);
170     printf("%ld error acks\n", num_error_acks);
171     printf("%ld packets transmitted (%ld raw, %ld normal)\n", num_tx_packets, num_tx_raw_packets, num_tx_normal_packets);
172     printf("%ld tx packets dropped because buffer full\n", num_tx_buffer_full);
173     printf("%ld packets received\n", num_rx_packets);
174     printf("%ld packets passed upstream (%ld Fast Path, %ld normal)\n", num_rx_fastpath + num_unitdata_ind, num_rx_fastpath, num_unitdata_ind);
175     printf("EtherIRQ called %ld times\n", num_ether_irq);
176     printf("%ld rx packets dropped due to low memory\n", num_rx_no_mem);
177     printf("%ld rx packets dropped because no stream found\n", num_rx_dropped);
178     printf("%ld rx packets dropped because stream not ready\n", num_rx_stream_not_ready);
179     printf("%ld rx packets dropped because no memory for unitdata_ind\n", num_rx_no_unitdata_mem);
180     #endif
181     }
182    
183    
184     /*
185     * Get ethernet hardware address
186     */
187    
188     void AO_get_ethernet_address(uint8 *addr)
189     {
190     if (net_open) {
191     OTCopy48BitAddress(ether_addr, addr);
192     } else {
193     addr[0] = 0x12;
194     addr[1] = 0x34;
195     addr[2] = 0x56;
196     addr[3] = 0x78;
197     addr[4] = 0x9a;
198     addr[5] = 0xbc;
199     }
200     D(bug("AO_get_ethernet_address: got address %02x%02x%02x%02x%02x%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]));
201     }
202    
203    
204     /*
205     * Enable multicast address
206     */
207    
208     void AO_enable_multicast(uint8 *addr)
209     {
210     D(bug("AO_enable_multicast\n"));
211     if (net_open) {
212     if (ioctl(fd, SIOCADDMULTI, addr) < 0) {
213     D(bug("WARNING: couldn't enable multicast address\n"));
214     }
215     }
216     }
217    
218    
219     /*
220     * Disable multicast address
221     */
222    
223     void AO_disable_multicast(uint8 *addr)
224     {
225     D(bug("AO_disable_multicast\n"));
226     if (net_open) {
227     if (ioctl(fd, SIOCDELMULTI, addr) < 0) {
228     D(bug("WARNING: couldn't disable multicast address\n"));
229     }
230     }
231     }
232    
233    
234     /*
235     * Transmit one packet
236     */
237    
238     void AO_transmit_packet(mblk_t *mp)
239     {
240     D(bug("AO_transmit_packet\n"));
241     if (net_open) {
242    
243     // Copy packet to buffer
244     uint8 packet[1516], *p = packet;
245     int len = 0;
246     if (is_ethertap) {
247     *p++ = 0; // Ethertap discards the first 2 bytes
248     *p++ = 0;
249     len += 2;
250     }
251     while (mp) {
252     uint32 size = mp->b_wptr - mp->b_rptr;
253     memcpy(p, mp->b_rptr, size);
254     len += size;
255     p += size;
256     mp = mp->b_cont;
257     }
258    
259     #if MONITOR
260     bug("Sending Ethernet packet:\n");
261     for (int i=0; i<len; i++) {
262     bug("%02x ", packet[i]);
263     }
264     bug("\n");
265     #endif
266    
267     // Transmit packet
268     if (write(fd, packet, len) < 0) {
269     D(bug("WARNING: couldn't transmit packet\n"));
270     num_tx_buffer_full++;
271     } else
272     num_tx_packets++;
273     }
274     }
275    
276    
277     /*
278     * Packet reception thread
279     */
280    
281     static void *receive_func(void *arg)
282     {
283     for (;;) {
284    
285     // Wait for packets to arrive
286     struct pollfd pf = {fd, POLLIN, 0};
287     int res = poll(&pf, 1, -1);
288     if (res <= 0)
289     break;
290    
291     if (ether_driver_opened) {
292     // Trigger Ethernet interrupt
293     D(bug(" packet received, triggering Ethernet interrupt\n"));
294     SetInterruptFlag(INTFLAG_ETHER);
295     TriggerInterrupt();
296    
297     // Wait for interrupt acknowledge by EtherInterrupt()
298     sem_wait(&int_ack);
299     } else
300     usleep(20000);
301     }
302     return NULL;
303     }
304    
305    
306     /*
307     * Ethernet interrupt
308     */
309    
310     void EtherIRQ(void)
311     {
312     D(bug("EtherIRQ\n"));
313     num_ether_irq++;
314     OTEnterInterrupt();
315    
316     // Send received packets to OpenTransport
317     uint8 packet[1516];
318     for (;;) {
319    
320     if (is_ethertap) {
321    
322     // Read packet from ethertap device
323     ssize_t size = read(fd, packet, 1516);
324     if (size < 14)
325     break;
326    
327     #if MONITOR
328     bug("Receiving Ethernet packet:\n");
329     for (int i=0; i<size; i++) {
330     bug("%02x ", packet[i]);
331     }
332     bug("\n");
333     #endif
334    
335     // Pointer to packet data (Ethernet header)
336     uint8 *p = packet + 2; // Ethertap has two random bytes before the packet
337     size -= 2;
338    
339     // Wrap packet in message block
340     num_rx_packets++;
341     mblk_t *mp;
342     if ((mp = allocb(size, 0)) != NULL) {
343     D(bug(" packet data at %p\n", mp->b_rptr));
344     memcpy(mp->b_rptr, p, size);
345     mp->b_wptr += size;
346     ether_packet_received(mp);
347     } else {
348     D(bug("WARNING: Cannot allocate mblk for received packet\n"));
349     num_rx_no_mem++;
350     }
351    
352     } else {
353    
354     // Get size of first packet
355     int size = 0;
356     if (ioctl(fd, FIONREAD, &size) < 0 || size == 0)
357     break;
358    
359     // Discard packets which are too short
360     if (size < 14) {
361     uint8 dummy[14];
362     read(fd, dummy, size);
363     continue;
364     }
365    
366     // Truncate packets which are too long
367     if (size > 1514)
368     size = 1514;
369    
370     // Read packet and wrap it in message block
371     num_rx_packets++;
372     mblk_t *mp;
373     if ((mp = allocb(size, 0)) != NULL) {
374     D(bug(" packet data at %p\n", mp->b_rptr));
375     read(fd, mp->b_rptr, 1514);
376     #if MONITOR
377     bug("Receiving Ethernet packet:\n");
378     for (int i=0; i<size; i++) {
379     bug("%02x ", ((uint8 *)mp->b_rptr)[i]);
380     }
381     bug("\n");
382     #endif
383     mp->b_wptr += size;
384     ether_packet_received(mp);
385     } else {
386     D(bug("WARNING: Cannot allocate mblk for received packet\n"));
387     num_rx_no_mem++;
388     }
389     }
390     }
391     OTLeaveInterrupt();
392    
393     // Acknowledge interrupt to reception thread
394     D(bug(" EtherIRQ done\n"));
395     sem_post(&int_ack);
396     }