make.tmpl: add missing compiler attribute to build_progs
[AROS.git] / arch / all-unix / devs / networks / eth / iotask.c
blobe80fae18457a277f65d13f868769b1903c341e1b
1 /*
2 * eth - TUN/ETH network driver for AROS
3 * Copyright (c) 2007 Robert Norris. All rights reserved.
4 * Copyright (c) 2010-2011 The AROS Development Team. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the same terms as AROS itself.
8 */
10 #include <hidd/unixio.h>
12 #include "eth.h"
14 #include <signal.h>
16 /* this fires whenever data is waiting to be read on the eth descriptor */
17 static void eth_receive(struct eth_base *ETHBase, struct eth_unit *unit)
19 unsigned char buf[ETH_FRAME_LEN], *packet;
20 int nread, ioerr;
21 struct ethhdr *eth;
22 char *dest;
23 WORD packet_type;
24 struct eth_opener *opener, *opener_next;
25 struct IOSana2Req *req, *req_next;
26 BOOL bcast = FALSE, mcast = FALSE;
27 BOOL accepted = 0;
28 struct eth_tracker *tracker, *tracker_next;
30 D(bug("[eth] [io:%d] got a packet\n", unit->num));
32 /* Try to read the packet */
33 nread = Hidd_UnixIO_RecvPacket(ETHBase->unixio, unit->pd, buf, ETH_FRAME_LEN, &ioerr);
34 if (nread == -1)
36 D(bug("[eth] [io:%d] read failed (%d)\n", unit->num, ioerr));
37 return;
40 if (!(unit->flags & eu_ONLINE)) {
41 D(bug("[eth] [io:%d] we're offline, dropping it\n", unit->num));
42 return;
45 D(bug("[eth] [io:%d] packet dump (%d bytes):\n", unit->num, nread));
46 D(eth_hexdump(buf, nread));
48 eth = (struct ethhdr *) buf;
49 packet_type = AROS_BE2WORD(eth->h_proto); /* AROS_BE2WORD === ntohs */
51 D(bug("[eth] [io:%d] source address: %02x:%02x:%02x:%02x:%02x:%02x\n", unit->num,
52 eth->h_source[0], eth->h_source[1], eth->h_source[2],
53 eth->h_source[3], eth->h_source[4], eth->h_source[5]));
54 D(bug("[eth] [io:%d] dest address: %02x:%02x:%02x:%02x:%02x:%02x\n", unit->num,
55 eth->h_dest[0], eth->h_dest[1], eth->h_dest[2],
56 eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]));
57 D(bug("[eth] [io:%d] packet type: 0x%04x\n", unit->num, packet_type));
59 /* broadcast packets have the top 40 bits (5 bytes) set */
60 dest = eth->h_dest;
61 if ((*((ULONG *) (dest)) == 0xffffffff) &&
62 (*((UWORD *) (dest + 4)) == 0xffff)) {
63 D(bug("[eth] [io:%d] broadcast packet\n"));
64 bcast = TRUE;
67 /* multicasts have the top bit set */
68 else if ((eth->h_dest[0] & 0x1) != 0) {
69 D(bug("[eth] [io:%d] multicast packet\n"));
70 mcast = TRUE;
73 /* drop multicast packets (until we have support for them) */
74 if (mcast) {
75 D(bug("[eth] [io:%d] no support for multicast packets, dropping it\n", unit->num));
76 return;
79 /* now we loop through our openers, seeing if anyone wants the packet */
80 ForeachNodeSafe(&(unit->openers), opener, opener_next) {
82 /* loop pending read requests */
83 ForeachNodeSafe(&(opener->read_pending.mp_MsgList), req, req_next) {
85 if (req->ios2_PacketType == packet_type) {
86 D(bug("[eth] [io:%d] found a request that wants this packet, sending it\n", unit->num));
88 /* record broadcast/multicast status */
89 req->ios2_Req.io_Flags &= ~(SANA2IOF_BCAST | SANA2IOF_MCAST);
90 if (bcast)
91 req->ios2_Req.io_Flags |= SANA2IOF_BCAST;
92 if (mcast)
93 req->ios2_Req.io_Flags |= SANA2IOF_MCAST;
95 /* copy source, dest, type */
96 CopyMem(eth->h_source, req->ios2_SrcAddr, ETH_ALEN);
97 CopyMem(eth->h_dest, req->ios2_DstAddr, ETH_ALEN);
98 req->ios2_PacketType = packet_type;
100 /* if they want the raw header, then use as-is */
101 if (req->ios2_Req.io_Flags & SANA2IOF_RAW) {
102 req->ios2_DataLength = nread;
103 packet = buf;
106 /* otherwise rip the header off */
107 else {
108 req->ios2_DataLength = nread - ETH_HLEN;
109 packet = &(buf[ETH_HLEN]);
112 /* user filter. the packet gets blocked if
113 * - the request is CMD_READ, and
114 * - they provided a filter, and
115 * - the filter returns false
117 if (req->ios2_Req.io_Command == CMD_READ &&
118 opener->filter != NULL &&
119 ! CallHookPkt(opener->filter, req, packet)) {
120 D(bug("[eth] [io:%d] packet blocked by opener filter\n", unit->num));
122 continue;
125 /* hand it to the opener via the provided rx function */
126 if (! opener->rx(req->ios2_Data, packet, req->ios2_DataLength)) {
127 D(bug("[eth] [io:%d] opener signaled error during rx copy\n", unit->num));
129 req->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
130 req->ios2_WireError = S2WERR_BUFF_ERROR;
133 else {
134 D(bug("[eth] [io:%d] packet copied successfully\n", unit->num));
137 Disable();
138 Remove((APTR) req);
139 Enable();
140 ReplyMsg((APTR) req);
142 accepted = TRUE;
145 if (accepted)
146 break;
149 if (accepted)
150 break;
153 if (! accepted) {
154 D(bug("[eth] [io:%d] no one claimed the packet, orphaning\n", unit->num));
156 /* XXX handle orphans */
159 /* update tracked stats */
160 ForeachNodeSafe(&(unit->trackers), tracker, tracker_next) {
161 if (tracker->packet_type == packet_type) {
162 tracker->stats.PacketsReceived ++;
163 tracker->stats.BytesReceived += nread;
164 if (! accepted)
165 tracker->stats.PacketsDropped ++;
169 /* update global stats too */
170 unit->stats.PacketsReceived ++;
171 if (! accepted)
172 unit->stats.UnknownTypesReceived ++;
175 /* this fires whenever we can write to the eth descriptor */
176 static void eth_send(struct eth_base *ETHBase, struct eth_unit *unit)
178 struct IOSana2Req *req;
179 int packet_length;
180 struct eth_opener *opener;
181 unsigned char buf[ETH_FRAME_LEN], *packet;
182 struct ethhdr *eth;
183 int ioerr, nwritten = 0;
184 struct eth_tracker *tracker, *tracker_next;
186 if (!unit->write_queue->mp_MsgList.lh_Head->ln_Succ)
187 return;
189 /* grab the first pending request */
190 req = (struct IOSana2Req *) unit->write_queue->mp_MsgList.lh_Head;
192 eth = (struct ethhdr *) buf;
194 D(bug("[eth] [io:%d] buffer has %d bytes%s\n", unit->num, req->ios2_DataLength, req->ios2_Req.io_Flags & SANA2IOF_RAW ? " (RAW is set)" : ""));
196 /* build a header if they didn't send us one */
197 if (! (req->ios2_Req.io_Flags & SANA2IOF_RAW)) {
198 D(bug("[eth] [io:%d] adding ethernet header\n", unit->num));
200 CopyMem(unit->hwaddr, eth->h_source, ETH_ALEN);
201 CopyMem(req->ios2_DstAddr, eth->h_dest, ETH_ALEN);
202 eth->h_proto = AROS_WORD2BE(req->ios2_PacketType); /* AROS_WORD2BE === htons */
204 packet = &(buf[ETH_HLEN]);
205 packet_length = ETH_HLEN + req->ios2_DataLength;
208 /* otherwise just use what they give us */
209 else {
210 packet = buf;
211 packet_length = req->ios2_DataLength;
214 /* user magic functions */
215 opener = (struct eth_opener *) req->ios2_BufferManagement;
217 /* get the opener to magick into a buffer we can use */
218 if (! opener->tx(packet, req->ios2_Data, req->ios2_DataLength)) {
219 D(bug("[eth] [io:%d] opener signaled error during tx copy\n", unit->num));
221 req->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
222 req->ios2_WireError = S2WERR_BUFF_ERROR;
225 else {
226 D(bug("[eth] [io:%d] packet dump (%d bytes):\n", unit->num, packet_length));
227 D(eth_hexdump(buf, packet_length));
229 D(bug("[eth] [io:%d] source address: %02x:%02x:%02x:%02x:%02x:%02x\n", unit->num,
230 eth->h_source[0], eth->h_source[1], eth->h_source[2],
231 eth->h_source[3], eth->h_source[4], eth->h_source[5]));
232 D(bug("[eth] [io:%d] dest address: %02x:%02x:%02x:%02x:%02x:%02x\n", unit->num,
233 eth->h_dest[0], eth->h_dest[1], eth->h_dest[2],
234 eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]));
235 D(bug("[eth] [io:%d] packet type: 0x%04x\n", unit->num, AROS_BE2WORD(eth->h_proto)));
237 /* got a viable buffer, send it out */
238 nwritten = Hidd_UnixIO_SendPacket(ETHBase->unixio, unit->pd, buf, packet_length, &ioerr);
239 if (nwritten < 0) {
240 D(bug("[eth] [io:%d] write failed (%d)\n", unit->num, ioerr));
241 req->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
243 else if (nwritten < packet_length) {
244 D(bug("[eth] [io:%d] short write, only %d bytes written, reporting error\n", unit->num, nwritten));
245 req->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
247 else
248 D(bug("[eth] [io:%d] wrote %d bytes\n", unit->num, nwritten));
251 /* update stats */
252 if (req->ios2_Req.io_Error == 0) {
254 /* tracked */
255 ForeachNodeSafe(&(unit->trackers), tracker, tracker_next) {
256 if (tracker->packet_type == req->ios2_PacketType) {
257 tracker->stats.PacketsSent ++;
258 tracker->stats.BytesSent += nwritten;
262 /* global */
263 unit->stats.PacketsSent ++;
266 /* remove and reply to the request */
267 Disable();
268 Remove((APTR) req);
269 Enable();
270 ReplyMsg((APTR) req);
273 static void IOHandler(int fd, int mode, void *data)
275 struct eth_unit *unit = data;
276 D(bug("[eth] IO interrupt received\n"));
277 Signal(unit->iotask, 1 << unit->io_signal);
281 void eth_iotask(struct eth_base *ETHBase, struct eth_unit *unit)
283 struct MsgPort *reply_port;
284 struct Message *msg;
285 ULONG write_signal_mask, abort_signal_mask, io_signal_mask, signal_mask;
286 ULONG signaled = 0;
287 short write_flag = 0;
289 D(bug("[eth] [io:%d] iotask starting up\n", unit->num));
291 /* Prepare write queue */
292 unit->write_queue = CreateMsgPort();
293 D(bug("[eth] Write port %p\n", unit->write_queue));
295 /* make some signals, one of them to shut us down, and one for I/O */
296 unit->abort_signal = AllocSignal(-1);
297 unit->io_signal = AllocSignal(-1);
298 write_signal_mask = 1 << unit->write_queue->mp_SigBit;
299 abort_signal_mask = 1 << unit->abort_signal;
300 io_signal_mask = 1 << unit->io_signal;
302 /* bundle all the signals together */
303 signal_mask = write_signal_mask | abort_signal_mask | io_signal_mask;
304 D(bug("[eth] signal mask 0x%08X\n", signal_mask));
306 /* start waiting for read events */
307 unit->irq.fd = unit->fd;
308 unit->irq.mode = vHidd_UnixIO_RW;
309 unit->irq.handler = IOHandler;
310 unit->irq.handlerData = unit;
311 Hidd_UnixIO_AddInterrupt(ETHBase->unixio, &unit->irq);
313 reply_port = CreateMsgPort();
314 msg = (struct Message *) AllocVec(sizeof(struct Message), MEMF_PUBLIC | MEMF_CLEAR);
315 msg->mn_ReplyPort = reply_port;
316 msg->mn_Length = sizeof(struct Message);
317 PutMsg(unit->iosyncport, msg);
318 WaitPort(reply_port);
319 GetMsg(reply_port);
320 FreeVec(msg);
321 DeleteMsgPort(reply_port);
323 D(bug("[eth] [io:%d] iotask entering loop\n", unit->num));
325 while (1)
327 int events;
331 events = Hidd_UnixIO_Poll(ETHBase->unixio, unit->fd, vHidd_UnixIO_Read | write_flag, NULL);
333 if (events == -1)
334 continue;
336 if (events & vHidd_UnixIO_Read)
338 D(bug("[eth] [io:%d] ready for read\n", unit->num));
339 eth_receive(ETHBase, unit);
342 if (events & vHidd_UnixIO_Write)
344 D(bug("[eth] [io:%d] ready for write\n", unit->num));
345 eth_send(ETHBase, unit);
347 if (IsMsgPortEmpty(unit->write_queue)) {
348 D(bug("[eth] [io:%d] all packets sent, will ignore future write events\n", unit->num));
349 write_flag = 0;
352 } while (events);
354 D(bug("[eth] [io:%d] waiting for an event\n", unit->num));
356 signaled = Wait(signal_mask);
358 D(bug("[eth] [io:%d] iotask signaled\n", unit->num));
359 if (signaled & write_signal_mask)
361 D(bug("[eth] [io:%d] write requested, enabling\n", unit->num));
362 write_flag = vHidd_UnixIO_Write;
365 if (signaled & abort_signal_mask) {
366 D(bug("[eth] [io:%d] iotask received abort signal\n", unit->num));
367 break;
370 D(bug("[eth] [io:%d] event processed, replying and looping\n", unit->num));
373 D(bug("[eth] [io:%d] iotask exiting\n", unit->num));
375 Hidd_UnixIO_RemInterrupt(ETHBase->unixio, &unit->irq);
377 FreeSignal(unit->abort_signal);
378 FreeSignal(unit->io_signal);
380 DeleteMsgPort(unit->write_queue);
382 msg = (struct Message *) AllocVec(sizeof(struct Message), MEMF_PUBLIC | MEMF_CLEAR);
383 // FIXME: message may be freed before receiver gets it!
384 PutMsg(unit->iosyncport, msg);
385 FreeVec(msg);