]> git.sesse.net Git - pistorm/blob - a314/software-amiga/ethernet_pistorm/device.c
Adapt a314eth to work on PiStorm + A314 emulation
[pistorm] / a314 / software-amiga / ethernet_pistorm / device.c
1 /*
2  * Copyright (c) 2020-2021 Niklas Ekström
3  *
4  * Thanks to Christian Vogelgsang and Mike Sterling for inspiration gained from their SANA-II drivers:
5  * - https://github.com/cnvogelg/plipbox
6  * - https://github.com/mikestir/k1208-drivers
7  */
8
9 #include <exec/types.h>
10 #include <exec/execbase.h>
11 #include <exec/devices.h>
12 #include <exec/errors.h>
13 #include <exec/ports.h>
14 #include <libraries/dos.h>
15 #include <proto/exec.h>
16 #include <proto/dos.h>
17
18 #include <string.h>
19
20 #include <clib/alib_protos.h>
21
22 #include "../../a314device/a314.h"
23 #include "../../a314device/proto_a314.h"
24 #include "sana2.h"
25
26 // Defines.
27
28 #define DEVICE_NAME             "a314eth.device"
29 #define SERVICE_NAME            "ethernet"
30
31 #define TASK_PRIO               10
32
33 #define MACADDR_SIZE            6
34 #define NIC_BPS                 10000000
35
36 #define ETH_MTU                 1500
37 #define RAW_MTU                 1518
38
39 #define READ_FRAME_REQ          1
40 #define WRITE_FRAME_REQ         2
41 #define READ_FRAME_RES          3
42 #define WRITE_FRAME_RES         4
43
44 #define ET_RBUF_CNT             2
45 #define ET_WBUF_CNT             2
46 #define ET_BUF_CNT              (ET_RBUF_CNT + ET_WBUF_CNT)
47
48 // Typedefs.
49
50 typedef BOOL (*buf_copy_func_t)(__reg("a0") void *dst, __reg("a1") void *src, __reg("d0") LONG size);
51
52 // Structs.
53
54 #pragma pack(push, 1)
55 struct EthHdr
56 {
57         unsigned char eh_Dst[MACADDR_SIZE];
58         unsigned char eh_Src[MACADDR_SIZE];
59         unsigned short eh_Type;
60 };
61 #pragma pack(pop)
62
63 #pragma pack(push, 1)
64 struct ServiceMsg
65 {
66         ULONG sm_Address;
67         UWORD sm_Length;
68         UWORD sm_Kind;
69 };
70 #pragma pack(pop)
71
72 struct BufDesc
73 {
74         struct MinNode bd_Node;
75         void *bd_Buffer;
76         int bd_Length;
77 };
78
79 // Constants.
80
81 const char device_name[] = DEVICE_NAME;
82 const char id_string[] = DEVICE_NAME " 1.0 (20 July 2020)";
83
84 static const char service_name[] = SERVICE_NAME;
85 static const char a314_device_name[] = A314_NAME;
86
87 static const unsigned char macaddr[MACADDR_SIZE] = { 0x40, 0x61, 0x33, 0x31, 0x34, 0x65 };
88
89 // Global variables.
90
91 BPTR saved_seg_list;
92 struct ExecBase *SysBase;
93 struct DosLibrary *DOSBase;
94 struct Library *A314Base;
95
96 buf_copy_func_t copyfrom;
97 buf_copy_func_t copyto;
98
99 volatile struct List ut_rbuf_list;
100 volatile struct List ut_wbuf_list;
101
102 struct BufDesc et_bufs[ET_BUF_CNT];
103
104 struct List et_rbuf_free_list;
105 struct List et_rbuf_pending_list;
106 struct List et_rbuf_has_data_list;
107
108 struct List et_wbuf_free_list;
109 struct List et_wbuf_pending_list;
110
111 LONG a314_socket;
112 short last_req_kind;
113
114 struct MsgPort a314_mp;
115
116 struct A314_IORequest read_ior;
117 struct A314_IORequest write_ior;
118 struct A314_IORequest reset_ior;
119
120 BOOL pending_a314_read;
121 BOOL pending_a314_write;
122 BOOL pending_a314_reset;
123
124 struct ServiceMsg a314_read_buf;
125 struct ServiceMsg a314_write_buf;
126
127 volatile ULONG sana2_sigmask;
128 volatile ULONG shutdown_sigmask;
129
130 volatile struct Task *init_task;
131
132 struct Process *device_process;
133 volatile int device_start_error;
134
135 // External declarations.
136
137 extern void device_process_seglist();
138
139 // Procedures.
140
141 static struct Library *init_device(__reg("a6") struct ExecBase *sys_base, __reg("a0") BPTR seg_list, __reg("d0") struct Library *dev)
142 {
143         saved_seg_list = seg_list;
144
145         dev->lib_Node.ln_Type = NT_DEVICE;
146         dev->lib_Node.ln_Name = (char *)device_name;
147         dev->lib_Flags = LIBF_SUMUSED | LIBF_CHANGED;
148         dev->lib_Version = 1;
149         dev->lib_Revision = 0;
150         dev->lib_IdString = (APTR)id_string;
151
152         SysBase = *(struct ExecBase **)4;
153         DOSBase = (struct DosLibrary *)OpenLibrary(DOSNAME, 0);
154
155         return dev;
156 }
157
158 static BPTR expunge(__reg("a6") struct Library *dev)
159 {
160         if (dev->lib_OpenCnt)
161         {
162                 dev->lib_Flags |= LIBF_DELEXP;
163                 return 0;
164         }
165
166         // Shady way of waiting for device process to terminate before unloading.
167         Delay(10);
168
169         CloseLibrary((struct Library *)DOSBase);
170
171         Remove(&dev->lib_Node);
172         FreeMem((char *)dev - dev->lib_NegSize, dev->lib_NegSize + dev->lib_PosSize);
173         return saved_seg_list;
174 }
175
176 static void send_a314_cmd(struct A314_IORequest *ior, UWORD cmd, char *buffer, int length)
177 {
178         ior->a314_Request.io_Command = cmd;
179         ior->a314_Request.io_Error = 0;
180         ior->a314_Socket = a314_socket;
181         ior->a314_Buffer = buffer;
182         ior->a314_Length = length;
183         SendIO((struct IORequest *)ior);
184 }
185
186 static void do_a314_cmd(struct A314_IORequest *ior, UWORD cmd, char *buffer, int length)
187 {
188         ior->a314_Request.io_Command = cmd;
189         ior->a314_Request.io_Error = 0;
190         ior->a314_Socket = a314_socket;
191         ior->a314_Buffer = buffer;
192         ior->a314_Length = length;
193         DoIO((struct IORequest *)ior);
194 }
195
196 static void copy_from_bd_and_reply(struct IOSana2Req *ios2, struct BufDesc *bd)
197 {
198         struct EthHdr *eh = bd->bd_Buffer;
199
200         if (ios2->ios2_Req.io_Flags & SANA2IOF_RAW)
201         {
202                 ios2->ios2_DataLength = bd->bd_Length;
203                 copyto(ios2->ios2_Data, bd->bd_Buffer, ios2->ios2_DataLength);
204                 ios2->ios2_Req.io_Flags = SANA2IOF_RAW;
205         }
206         else
207         {
208                 ios2->ios2_DataLength = bd->bd_Length - sizeof(struct EthHdr);
209                 copyto(ios2->ios2_Data, &eh[1], ios2->ios2_DataLength);
210                 ios2->ios2_Req.io_Flags = 0;
211         }
212
213         memcpy(ios2->ios2_SrcAddr, eh->eh_Src, MACADDR_SIZE);
214         memcpy(ios2->ios2_DstAddr, eh->eh_Dst, MACADDR_SIZE);
215
216         BOOL bcast = TRUE;
217         for (int i = 0; i < MACADDR_SIZE; i++)
218         {
219                 if (eh->eh_Dst[i] != 0xff)
220                 {
221                         bcast = FALSE;
222                         break;
223                 }
224         }
225
226         if (bcast)
227                 ios2->ios2_Req.io_Flags |= SANA2IOF_BCAST;
228
229         ios2->ios2_PacketType = eh->eh_Type;
230
231         ios2->ios2_Req.io_Error = 0;
232         ReplyMsg(&ios2->ios2_Req.io_Message);
233 }
234
235 static void copy_to_bd_and_reply(struct BufDesc *bd, struct IOSana2Req *ios2)
236 {
237         struct EthHdr *eh = bd->bd_Buffer;
238
239         if (ios2->ios2_Req.io_Flags & SANA2IOF_RAW)
240         {
241                 copyfrom(bd->bd_Buffer, ios2->ios2_Data, ios2->ios2_DataLength);
242                 bd->bd_Length = ios2->ios2_DataLength;
243         }
244         else
245         {
246                 eh->eh_Type = ios2->ios2_PacketType;
247                 memcpy(eh->eh_Src, macaddr, sizeof(macaddr));
248                 memcpy(eh->eh_Dst, ios2->ios2_DstAddr, MACADDR_SIZE);
249                 copyfrom(&eh[1], ios2->ios2_Data, ios2->ios2_DataLength);
250                 bd->bd_Length = ios2->ios2_DataLength + sizeof(struct EthHdr);
251         }
252
253         ios2->ios2_Req.io_Error = 0;
254         ReplyMsg(&ios2->ios2_Req.io_Message);
255 }
256
257 static void handle_a314_reply(struct A314_IORequest *ior)
258 {
259         if (ior == &write_ior)
260         {
261                 pending_a314_write = FALSE;
262
263                 if (ior->a314_Request.io_Error == A314_WRITE_OK)
264                 {
265                         // Start new write later.
266                 }
267                 else // A314_WRITE_RESET
268                 {
269                         // TODO: Handle. What if pi-side is shutting down.
270                 }
271         }
272         else if (ior == &read_ior)
273         {
274                 pending_a314_read = FALSE;
275
276                 if (ior->a314_Request.io_Error == A314_READ_OK)
277                 {
278                         if (a314_read_buf.sm_Kind == WRITE_FRAME_RES)
279                         {
280                                 struct BufDesc *bd = (struct BufDesc *)RemHead(&et_wbuf_pending_list);
281                                 AddTail(&et_wbuf_free_list, (struct Node *)bd);
282                         }
283                         else // READ_FRAME_RES
284                         {
285                                 struct BufDesc *bd = (struct BufDesc *)RemHead(&et_rbuf_pending_list);
286                                 bd->bd_Length = a314_read_buf.sm_Length;
287                                 AddTail(&et_rbuf_has_data_list, (struct Node *)bd);
288                         }
289
290                         send_a314_cmd(&read_ior, A314_READ, (void *)&a314_read_buf, sizeof(a314_read_buf));
291                         pending_a314_read = TRUE;
292                 }
293                 else // A314_READ_RESET
294                 {
295                         // TODO: Handle. What if pi-side is shutting down.
296                 }
297         }
298         else if (ior == &reset_ior)
299         {
300                 pending_a314_reset = FALSE;
301         }
302 }
303
304 static struct IOSana2Req *remove_matching_rbuf(ULONG type)
305 {
306         struct Node *node = ut_rbuf_list.lh_Head;
307         while (node->ln_Succ)
308         {
309                 struct IOSana2Req *ios2 = (struct IOSana2Req *)node;
310                 if (ios2->ios2_PacketType == type)
311                 {
312                         Remove(node);
313                         return ios2;
314                 }
315                 node = node->ln_Succ;
316         }
317         return NULL;
318 }
319
320 static void complete_read_reqs()
321 {
322         struct Node *node = et_rbuf_has_data_list.lh_Head;
323         if (!node->ln_Succ)
324                 return;
325
326         Forbid();
327         while (node->ln_Succ)
328         {
329                 struct BufDesc *bd = (struct BufDesc *)node;
330                 struct EthHdr *eh = (struct EthHdr *)bd->bd_Buffer;
331
332                 node = node->ln_Succ;
333
334                 struct IOSana2Req *ios2 = remove_matching_rbuf(eh->eh_Type);
335                 if (ios2)
336                 {
337                         copy_from_bd_and_reply(ios2, bd);
338
339                         Remove((struct Node *)bd);
340                         AddTail(&et_rbuf_free_list, (struct Node *)bd);
341                 }
342         }
343         Permit();
344 }
345
346 static void maybe_write_req()
347 {
348         if (pending_a314_write)
349                 return;
350
351         BOOL free_et_wbuf = et_wbuf_free_list.lh_Head->ln_Succ != NULL;
352         BOOL idle_et_rbuf = et_rbuf_free_list.lh_Head->ln_Succ != NULL;
353
354         Forbid();
355
356         BOOL waiting_ut_wbuf = ut_wbuf_list.lh_Head->ln_Succ != NULL;
357
358         BOOL want_wbuf = free_et_wbuf && waiting_ut_wbuf;
359         BOOL want_rbuf = idle_et_rbuf;
360
361         if (!want_rbuf && !want_wbuf)
362         {
363                 Permit();
364                 return;
365         }
366
367         short next_req_kind = 0;
368
369         if (last_req_kind == WRITE_FRAME_REQ)
370                 next_req_kind = want_rbuf ? READ_FRAME_REQ : WRITE_FRAME_REQ;
371         else
372                 next_req_kind = want_wbuf ? WRITE_FRAME_REQ : READ_FRAME_REQ;
373
374         struct IOSana2Req *ios2 = NULL;
375         if (next_req_kind == WRITE_FRAME_REQ)
376                 ios2 = (struct IOSana2Req*)RemHead((struct List *)&ut_wbuf_list);
377
378         Permit();
379
380         struct BufDesc *bd;
381
382         if (next_req_kind == READ_FRAME_REQ)
383         {
384                 bd = (struct BufDesc *)RemHead(&et_rbuf_free_list);
385                 bd->bd_Length = RAW_MTU;
386                 AddTail(&et_rbuf_pending_list, (struct Node *)&bd->bd_Node);
387         }
388         else // WRITE_FRAME_REQ
389         {
390                 bd = (struct BufDesc *)RemHead(&et_wbuf_free_list);
391                 copy_to_bd_and_reply(bd, ios2);
392                 AddTail(&et_wbuf_pending_list, (struct Node *)bd);
393         }
394
395         a314_write_buf.sm_Address = TranslateAddressA314(bd->bd_Buffer);
396         a314_write_buf.sm_Length = bd->bd_Length;
397         a314_write_buf.sm_Kind = next_req_kind;
398
399         send_a314_cmd(&write_ior, A314_WRITE, (void *)&a314_write_buf, sizeof(a314_write_buf));
400         pending_a314_write = TRUE;
401
402         last_req_kind = next_req_kind;
403 }
404
405 void device_process_run()
406 {
407         ULONG sana2_signal = AllocSignal(-1);
408         sana2_sigmask = 1UL << sana2_signal;
409
410         ULONG shutdown_signal = AllocSignal(-1);
411         shutdown_sigmask = 1UL << shutdown_signal;
412
413         a314_mp.mp_SigBit = AllocSignal(-1);
414         a314_mp.mp_SigTask = FindTask(NULL);
415
416         do_a314_cmd(&reset_ior, A314_CONNECT, (char *)service_name, strlen(service_name));
417         device_start_error = reset_ior.a314_Request.io_Error == A314_CONNECT_OK ? 0 : -1;
418
419         Signal((struct Task *)init_task, SIGF_SINGLE);
420
421         if (device_start_error)
422                 return;
423
424         ULONG a314_sigmask = 1UL << a314_mp.mp_SigBit;
425
426         send_a314_cmd(&read_ior, A314_READ, (void *)&a314_read_buf, sizeof(a314_read_buf));
427         pending_a314_read = TRUE;
428
429         BOOL shutting_down = FALSE;
430
431         while (TRUE)
432         {
433                 complete_read_reqs();
434                 maybe_write_req();
435
436                 if (shutting_down && !pending_a314_read && !pending_a314_write && !pending_a314_reset)
437                         break;
438
439                 ULONG sigs = Wait(a314_sigmask | sana2_sigmask | shutdown_sigmask);
440
441                 if ((sigs & shutdown_sigmask) && !shutting_down)
442                 {
443                         send_a314_cmd(&reset_ior, A314_RESET, NULL, 0);
444                         pending_a314_reset = TRUE;
445                         shutting_down = TRUE;
446                 }
447
448                 if (sigs & a314_sigmask)
449                 {
450                         struct A314_IORequest *ior;
451                         while ((ior = (struct A314_IORequest *)GetMsg(&a314_mp)))
452                                 handle_a314_reply(ior);
453                 }
454         }
455
456         Signal((struct Task *)init_task, SIGF_SINGLE);
457 }
458
459 static struct TagItem *FindTagItem(Tag tagVal, struct TagItem *tagList)
460 {
461         struct TagItem *ti = tagList;
462         while (ti && ti->ti_Tag != tagVal)
463         {
464                 switch (ti->ti_Tag)
465                 {
466                 case TAG_DONE:
467                         return NULL;
468                 case TAG_MORE:
469                         ti = (struct TagItem *)ti->ti_Data;
470                         break;
471                 case TAG_SKIP:
472                         ti += ti->ti_Data + 1;
473                         break;
474                 case TAG_IGNORE:
475                 default:
476                         ti++;
477                         break;
478                 }
479         }
480         return ti;
481 }
482
483 static ULONG GetTagData(Tag tagVal, ULONG defaultData, struct TagItem *tagList)
484 {
485         struct TagItem *ti = FindTagItem(tagVal, tagList);
486         return ti ? ti->ti_Data : defaultData;
487 }
488
489 static void open(__reg("a6") struct Library *dev, __reg("a1") struct IOSana2Req *ios2, __reg("d0") ULONG unitnum, __reg("d1") ULONG flags)
490 {
491         ios2->ios2_Req.io_Error = IOERR_OPENFAIL;
492         ios2->ios2_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
493
494         if (unitnum != 0 || dev->lib_OpenCnt)
495                 return;
496
497         dev->lib_OpenCnt++;
498
499         copyfrom = (buf_copy_func_t)GetTagData(S2_CopyFromBuff, 0, (struct TagItem *)ios2->ios2_BufferManagement);
500         copyto = (buf_copy_func_t)GetTagData(S2_CopyToBuff, 0, (struct TagItem *)ios2->ios2_BufferManagement);
501         ios2->ios2_BufferManagement = (void *)0xdeadbeefUL;
502
503         memset(&a314_mp, 0, sizeof(a314_mp));
504         a314_mp.mp_Node.ln_Pri = 0;
505         a314_mp.mp_Node.ln_Type = NT_MSGPORT;
506         a314_mp.mp_Node.ln_Name = (char *)device_name;
507         a314_mp.mp_Flags = PA_SIGNAL;
508         NewList(&a314_mp.mp_MsgList);
509
510         memset(&write_ior, 0, sizeof(write_ior));
511         write_ior.a314_Request.io_Message.mn_ReplyPort = &a314_mp;
512         write_ior.a314_Request.io_Message.mn_Length = sizeof(write_ior);
513         write_ior.a314_Request.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
514
515         A314Base = NULL;
516         if (OpenDevice((char *)a314_device_name, 0, (struct IORequest *)&write_ior, 0))
517                 goto error;
518
519         A314Base = &(write_ior.a314_Request.io_Device->dd_Library);
520
521         memcpy(&read_ior, &write_ior, sizeof(read_ior));
522         memcpy(&reset_ior, &write_ior, sizeof(reset_ior));
523
524         struct DateStamp ds;
525         DateStamp(&ds);
526         a314_socket = (ds.ds_Minute * 60 * TICKS_PER_SECOND) + ds.ds_Tick;
527
528         last_req_kind = WRITE_FRAME_REQ;
529
530         NewList((struct List *)&ut_rbuf_list);
531         NewList((struct List *)&ut_wbuf_list);
532
533         NewList(&et_rbuf_free_list);
534         NewList(&et_rbuf_pending_list);
535         NewList(&et_rbuf_has_data_list);
536
537         NewList(&et_wbuf_free_list);
538         NewList(&et_wbuf_pending_list);
539
540         for (int i = 0; i < ET_BUF_CNT; i++)
541                 memset(&et_bufs[i], 0, sizeof(struct BufDesc));
542
543         for (int i = 0; i < ET_BUF_CNT; i++)
544         {
545                 struct BufDesc *bd = &et_bufs[i];
546
547                 bd->bd_Buffer = AllocMem(RAW_MTU, MEMF_FAST);
548                 if (!bd->bd_Buffer)
549                         goto error;
550
551                 if (i < ET_RBUF_CNT)
552                         AddTail(&et_rbuf_free_list, (struct Node*)&bd->bd_Node);
553                 else
554                         AddTail(&et_wbuf_free_list, (struct Node*)&bd->bd_Node);
555         }
556
557         init_task = FindTask(NULL);
558
559         struct MsgPort *device_mp = CreateProc((char *)device_name, TASK_PRIO, ((ULONG)device_process_seglist) >> 2, 2048);
560         if (!device_mp)
561                 goto error;
562
563         device_process = (struct Process *)((char *)device_mp - sizeof(struct Task));
564
565         Wait(SIGF_SINGLE);
566
567         if (device_start_error)
568                 goto error;
569
570         ios2->ios2_Req.io_Error = 0;
571         return;
572
573 error:
574         for (int i = ET_BUF_CNT - 1; i >= 0; i--)
575                 if (et_bufs[i].bd_Buffer)
576                         FreeMem(et_bufs[i].bd_Buffer, RAW_MTU);
577
578         if (A314Base)
579         {
580                 CloseDevice((struct IORequest *)&write_ior);
581                 A314Base = NULL;
582         }
583
584         dev->lib_OpenCnt--;
585 }
586
587 static BPTR close(__reg("a6") struct Library *dev, __reg("a1") struct IOSana2Req *ios2)
588 {
589         init_task = FindTask(NULL);
590         Signal(&device_process->pr_Task, shutdown_sigmask);
591         Wait(SIGF_SINGLE);
592
593         for (int i = ET_BUF_CNT - 1; i >= 0; i--)
594                 FreeMem(et_bufs[i].bd_Buffer, RAW_MTU);
595
596         CloseDevice((struct IORequest *)&write_ior);
597         A314Base = NULL;
598
599         ios2->ios2_Req.io_Device = NULL;
600         ios2->ios2_Req.io_Unit = NULL;
601
602         dev->lib_OpenCnt--;
603
604         if (dev->lib_OpenCnt == 0 && (dev->lib_Flags & LIBF_DELEXP))
605                 return expunge(dev);
606
607         return 0;
608 }
609
610 static void device_query(struct IOSana2Req *req)
611 {
612         struct Sana2DeviceQuery *query;
613
614         query = req->ios2_StatData;
615         query->DevQueryFormat = 0;
616         query->DeviceLevel = 0;
617
618         if (query->SizeAvailable >= 18)
619                 query->AddrFieldSize = MACADDR_SIZE * 8;
620
621         if (query->SizeAvailable >= 22)
622                 query->MTU = ETH_MTU;
623
624         if (query->SizeAvailable >= 26)
625                 query->BPS = NIC_BPS;
626
627         if (query->SizeAvailable >= 30)
628                 query->HardwareType = S2WireType_Ethernet;
629
630         query->SizeSupplied = query->SizeAvailable < 30 ? query->SizeAvailable : 30;
631 }
632
633 static void begin_io(__reg("a6") struct Library *dev, __reg("a1") struct IOSana2Req *ios2)
634 {
635         ios2->ios2_Req.io_Error = S2ERR_NO_ERROR;
636         ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
637
638         switch (ios2->ios2_Req.io_Command)
639         {
640         case CMD_READ:
641                 if (!ios2->ios2_BufferManagement)
642                 {
643                         ios2->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
644                         ios2->ios2_WireError = S2WERR_BUFF_ERROR;
645                         break;
646                 }
647
648                 Forbid();
649                 AddTail((struct List *)&ut_rbuf_list, &ios2->ios2_Req.io_Message.mn_Node);
650                 Permit();
651
652                 ios2->ios2_Req.io_Flags &= ~SANA2IOF_QUICK;
653                 ios2 = NULL;
654
655                 Signal(&device_process->pr_Task, sana2_sigmask);
656                 break;
657
658         case S2_BROADCAST:
659                 memset(ios2->ios2_DstAddr, 0xff, MACADDR_SIZE);
660                 /* Fall through */
661
662         case CMD_WRITE:
663                 if (((ios2->ios2_Req.io_Flags & SANA2IOF_RAW) != 0 && ios2->ios2_DataLength > RAW_MTU) ||
664                         ((ios2->ios2_Req.io_Flags & SANA2IOF_RAW) == 0 && ios2->ios2_DataLength > ETH_MTU))
665                 {
666                         ios2->ios2_Req.io_Error = S2ERR_MTU_EXCEEDED;
667                         break;
668                 }
669
670                 if (!ios2->ios2_BufferManagement)
671                 {
672                         ios2->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
673                         ios2->ios2_WireError = S2WERR_BUFF_ERROR;
674                         break;
675                 }
676
677                 Forbid();
678                 AddTail((struct List *)&ut_wbuf_list, &ios2->ios2_Req.io_Message.mn_Node);
679                 Permit();
680
681                 ios2->ios2_Req.io_Flags &= ~SANA2IOF_QUICK;
682                 ios2 = NULL;
683
684                 Signal(&device_process->pr_Task, sana2_sigmask);
685                 break;
686
687         case S2_ONLINE:
688         case S2_OFFLINE:
689         case S2_CONFIGINTERFACE:
690                 break;
691         case S2_GETSTATIONADDRESS:
692                 memcpy(ios2->ios2_SrcAddr, macaddr, sizeof(macaddr));
693                 memcpy(ios2->ios2_DstAddr, macaddr, sizeof(macaddr));
694                 break;
695         case S2_DEVICEQUERY:
696                 device_query(ios2);
697                 break;
698
699         case S2_ONEVENT:
700         case S2_TRACKTYPE:
701         case S2_UNTRACKTYPE:
702         case S2_GETTYPESTATS:
703         case S2_READORPHAN:
704         case S2_GETGLOBALSTATS:
705         case S2_GETSPECIALSTATS:
706                 break;
707
708         default:
709                 ios2->ios2_Req.io_Error = IOERR_NOCMD;
710                 ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
711                 break;
712         }
713
714         if (ios2)
715         {
716                 if (ios2->ios2_Req.io_Flags & SANA2IOF_QUICK)
717                         ios2->ios2_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
718                 else
719                         ReplyMsg(&ios2->ios2_Req.io_Message);
720         }
721 }
722
723 static void remove_from_list(struct List *list, struct Node *node)
724 {
725         for (struct Node *n = list->lh_Head; n->ln_Succ; n = n->ln_Succ)
726         {
727                 if (n == node)
728                 {
729                         Remove(n);
730                         return;
731                 }
732         }
733 }
734
735 static ULONG abort_io(__reg("a6") struct Library *dev, __reg("a1") struct IOSana2Req *ios2)
736 {
737         Forbid();
738         remove_from_list((struct List *)&ut_rbuf_list, &ios2->ios2_Req.io_Message.mn_Node);
739         remove_from_list((struct List *)&ut_wbuf_list, &ios2->ios2_Req.io_Message.mn_Node);
740         Permit();
741
742         ios2->ios2_Req.io_Error = IOERR_ABORTED;
743         ios2->ios2_WireError = 0;
744         ReplyMsg(&ios2->ios2_Req.io_Message);
745
746         return 0;
747 }
748
749 static ULONG device_vectors[] =
750 {
751         (ULONG)open,
752         (ULONG)close,
753         (ULONG)expunge,
754         0,
755         (ULONG)begin_io,
756         (ULONG)abort_io,
757         -1,
758 };
759
760 ULONG auto_init_tables[] =
761 {
762         sizeof(struct Library),
763         (ULONG)device_vectors,
764         0,
765         (ULONG)init_device,
766 };