port-handler: Make it a long-term instead of respawn-per-action handler
[AROS.git] / workbench / fs / port / port-handler.c
blob4bc13f4541fffaf702d08f2d997f8ad944dddf1c
1 /*
2 * Copyright (C) 2011, The AROS Development Team. All rights reserved.
3 * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
5 * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1
6 */
8 #include <string.h>
10 #include <aros/debug.h>
12 #include <intuition/preferences.h> /* DEVNAME_SIZE */
13 #include <devices/serial.h>
14 #include <devices/printer.h>
16 #include <proto/exec.h>
17 #include <proto/dos.h>
18 #include <proto/utility.h>
20 #define NT_PORTARGS (NT_USER - 1)
22 #define UtilityBase (pb->pb_UtilityBase)
24 struct portBase {
25 TEXT pb_DeviceName[DEVNAME_SIZE];
26 LONG pb_DeviceFlags;
27 enum { PORT_SERIAL, PORT_PARALLEL, PORT_PRINTER, PORT_STREAM } pb_Mode;
28 struct Library *pb_UtilityBase;
30 /* Per-open settable arguments */
31 struct portArgs {
32 struct Node pa_Node;
33 IPTR pa_DeviceUnit;
34 union {
35 struct {
36 ULONG ps_Baud;
37 UBYTE ps_LenBits;
38 UBYTE ps_StopBits;
39 UBYTE ps_SerFlags;
40 UBYTE ps_ExtFlags;
41 } pa_Serial;
42 struct {
43 enum { PRT_COOKED, PRT_RAW, PRT_TRANSPARENT } pr_Type;
44 } pa_Printer;
47 /* Only used in the per-open instances */
48 struct MsgPort *pa_IOMsg;
49 union {
50 struct IOStdReq std;
51 struct IOExtSer ser;
52 } *pa_IO;
53 } pb_Defaults;
55 struct List pb_Files;
58 /* Decode the following flags:
59 * Any type:
60 * UNIT=n
61 * PORT_PRINTER:
62 * TRANSPARENT
63 * RAW
64 * PORT_SERIAL:
65 * 9600 (and other baud rates)
66 * [78][NOEMS][12]
68 static SIPTR decodeArgs(struct portBase *pb, struct portArgs *pa, BSTR args)
70 int loc, len = AROS_BSTR_strlen(args);
71 CONST_STRPTR cp = AROS_BSTR_ADDR(args);
73 /* Skip any VOL: prefix */
74 for (loc = 0; loc < len; loc++) {
75 if (cp[loc] == ':')
76 break;
78 if (loc < len)
79 loc++;
80 else
81 loc=0;
83 while (loc < len) {
84 int slen; /* length of section */
86 /* Advance to next section */
87 cp = &cp[loc];
88 len -= loc;
90 /* Find next section */
91 for (loc = 0; loc < len; loc++) {
92 if (cp[loc] == '/')
93 break;
95 slen = loc;
97 if (loc < len)
98 loc++;
100 /* Check for matches.. */
101 if (slen >= 5 && (0 == Strnicmp(cp, "UNIT=", 5))) {
102 CONST_STRPTR unit = cp + 5;
103 int ulen = slen - 5;
104 int i;
105 SIPTR uval = 0;
107 for (i = 0; i < ulen; i++) {
108 if (unit[i] < '0' || unit[i] > '9')
109 break;
110 uval *= 10;
111 uval += unit[i] - '0';
114 if (i == ulen)
115 pa->pa_DeviceUnit = uval;
117 } else if (pb->pb_Mode == PORT_PARALLEL) {
118 /* No parallel.device options to process */
119 } else if (pb->pb_Mode == PORT_PRINTER) {
120 if (slen == 3 && Strnicmp(cp, "RAW", 3) == 0) {
121 pa->pa_Printer.pr_Type = PRT_RAW;
122 } else if (slen == 11 && Strnicmp(cp, "TRANSPARENT", 11) == 0) {
123 pa->pa_Printer.pr_Type = PRT_TRANSPARENT;
125 } else if (pb->pb_Mode == PORT_SERIAL) {
126 int i;
127 ULONG baud = 0;
129 /* Check for all-numeric */
130 for (i = 0; i < slen; i++) {
131 if (cp[i] < '0' || cp[i] > '9')
132 break;
133 baud *= 10;
134 baud += cp[i] - '0';
136 if (i == slen) {
137 pa->pa_Serial.ps_Baud = baud;
138 } else if (slen == 3) {
139 TEXT bits, mode, stop;
140 bits = cp[0];
141 mode = ToUpper(cp[1]);
142 stop = cp[2];
144 if ((bits == '7' || bits == '8') &&
145 (mode == 'N' || mode == 'O' || mode == 'E' ||
146 mode == 'M' || mode == 'S') &&
147 (stop == '1' || stop == '2')) {
148 pa->pa_Serial.ps_StopBits = stop - '0';
149 pa->pa_Serial.ps_LenBits = bits - '0';
150 switch (mode) {
151 case 'N': pa->pa_Serial.ps_SerFlags = 0;
152 pa->pa_Serial.ps_ExtFlags = 0;
153 break;
154 case 'O': pa->pa_Serial.ps_SerFlags = SERF_PARTY_ON | SERF_PARTY_ODD;
155 pa->pa_Serial.ps_ExtFlags = 0;
156 break;
157 case 'E': pa->pa_Serial.ps_SerFlags = SERF_PARTY_ON;
158 pa->pa_Serial.ps_ExtFlags = 0;
159 break;
160 case 'M': pa->pa_Serial.ps_SerFlags = SERF_PARTY_ON;
161 pa->pa_Serial.ps_ExtFlags = SEXTF_MSPON | SEXTF_MARK;
162 break;
163 case 'S': pa->pa_Serial.ps_SerFlags = SERF_PARTY_ON;
164 pa->pa_Serial.ps_ExtFlags = SEXTF_MSPON;
165 break;
173 return RETURN_OK;
176 static void portSerialDefaults(struct portArgs *pa)
178 /* 9600, 8N1 */
179 pa->pa_Serial.ps_StopBits = 1;
180 pa->pa_Serial.ps_Baud = 9600;
181 pa->pa_Serial.ps_SerFlags = 0;
182 pa->pa_Serial.ps_ExtFlags = 0;
185 /* Decode the startup message
187 static SIPTR decodeStartup(struct portBase *pb, BPTR startup)
189 int len;
190 struct FileSysStartupMsg *fssm;
191 struct DosEnvec *env;
192 struct portArgs *pa = &pb->pb_Defaults;
193 SIPTR ret = RETURN_OK;
195 switch ((SIPTR)startup) {
196 case 0:
197 pb->pb_Mode = PORT_SERIAL;
198 strcpy(pb->pb_DeviceName, "serial.device");
199 pa->pa_DeviceUnit = 0;
200 pb->pb_DeviceFlags = 0;
201 portSerialDefaults(pa);
202 break;
203 case 1:
204 pb->pb_Mode = PORT_PARALLEL;
205 strcpy(pb->pb_DeviceName, "parallel.device");
206 pa->pa_DeviceUnit = 0;
207 pb->pb_DeviceFlags = 0;
208 break;
209 case 2:
210 pb->pb_Mode = PORT_PRINTER;
211 pa->pa_Printer.pr_Type = PRT_COOKED;
212 strcpy(pb->pb_DeviceName, "printer.device");
213 pa->pa_DeviceUnit = 0;
214 pb->pb_DeviceFlags = 0;
215 break;
216 default:
217 fssm = BADDR(startup);
218 if (fssm->fssm_Device == BNULL) {
219 ret = ERROR_NO_DISK;
220 break;
223 len = AROS_BSTR_strlen(fssm->fssm_Device);
224 if (len > sizeof(pb->pb_DeviceName)-1) {
225 ret = ERROR_NO_DISK;
226 break;
229 CopyMem(AROS_BSTR_ADDR(fssm->fssm_Device), pb->pb_DeviceName, len);
230 pb->pb_DeviceName[len] = 0;
232 if (0 == Stricmp(pb->pb_DeviceName, "serial.device")) {
233 pb->pb_Mode = PORT_SERIAL;
234 portSerialDefaults(pa);
235 } else if (0 == Stricmp(pb->pb_DeviceName, "parallel.device")) {
236 pb->pb_Mode = PORT_PARALLEL;
237 } else if (0 == Stricmp(pb->pb_DeviceName, "printer.device")) {
238 pb->pb_Mode = PORT_PRINTER;
239 } else {
240 pb->pb_Mode = PORT_STREAM;
242 pa->pa_DeviceUnit = fssm->fssm_Unit;
243 pb->pb_DeviceFlags = fssm->fssm_Flags;
244 if ((env = BADDR(fssm->fssm_Environ)) &&
245 (env->de_TableSize > DE_CONTROL) &&
246 ((BSTR)env->de_Control != BNULL)) {
247 ret = decodeArgs(pb, pa, (BSTR)env->de_Control);
249 break;
252 return ret;
255 /* Open the device for IO
257 static struct portArgs *portOpen(struct portBase *pb, struct portArgs *pa, BPTR name, SIPTR *err)
259 int len = AROS_BSTR_strlen(name);
261 if ((pa = AllocVec(sizeof(*pa) + len + 1, MEMF_ANY))) {
262 CopyMem(&pb->pb_Defaults, pa, sizeof(*pa));
264 pa->pa_Node.ln_Type = NT_PORTARGS;
265 pa->pa_Node.ln_Name = (APTR)(&pa[1]);
266 CopyMem(AROS_BSTR_ADDR(name), pa->pa_Node.ln_Name, len);
267 pa->pa_Node.ln_Name[len] = 0;
269 decodeArgs(pb, pa, name);
271 D(bug("%s: %s device=%s, unit=%d (%d), mode=%d\n",
272 __func__, name, pb->pb_DeviceName,
273 pa->pa_DeviceUnit, pb->pb_DeviceFlags,
274 pb->pb_Mode));
276 if ((pa->pa_IOMsg = CreateMsgPort())) {
277 if ((pa->pa_IO = (APTR)CreateIORequest(pa->pa_IOMsg, sizeof(*pa->pa_IO)))) {
278 if (0 == OpenDevice(pb->pb_DeviceName, pa->pa_DeviceUnit, (struct IORequest *)pa->pa_IO, pb->pb_DeviceFlags)) {
279 D(bug("%s: Device is open\n"));
280 *err = 0;
281 if (pb->pb_Mode != PORT_SERIAL) {
282 AddTail(&pb->pb_Files, &pa->pa_Node);
283 return pa;
286 pa->pa_IO->ser.IOSer.io_Command = SDCMD_SETPARAMS;
287 /* xON xOFF ENQ ACK */
288 pa->pa_IO->ser.io_CtlChar = SER_DEFAULT_CTLCHAR;
289 pa->pa_IO->ser.io_RBufLen = 64;
290 pa->pa_IO->ser.io_SerFlags = pa->pa_Serial.ps_SerFlags;
291 pa->pa_IO->ser.io_ExtFlags = pa->pa_Serial.ps_ExtFlags;
292 pa->pa_IO->ser.io_Baud = pa->pa_Serial.ps_Baud;
293 pa->pa_IO->ser.io_BrkTime = 250000;
294 pa->pa_IO->ser.io_TermArray.TermArray0 = 0;
295 pa->pa_IO->ser.io_TermArray.TermArray1 = 1;
296 pa->pa_IO->ser.io_ReadLen = pa->pa_Serial.ps_LenBits;
297 pa->pa_IO->ser.io_WriteLen = pa->pa_Serial.ps_LenBits;
298 pa->pa_IO->ser.io_StopBits = pa->pa_Serial.ps_StopBits;
300 if (0 == DoIO((struct IORequest *)pa->pa_IO)) {
301 AddTail(&pb->pb_Files, &pa->pa_Node);
302 return pa;
304 CloseDevice((struct IORequest *)pa->pa_IO);
306 DeleteIORequest((struct IORequest *)pa->pa_IO);
308 DeleteMsgPort(pa->pa_IOMsg);
310 FreeVec(pa);
313 D(bug("%s: Didn't open device\n", __func__));
314 *err = ERROR_NO_DISK;
315 return NULL;
318 static void portClose(struct portArgs *pa)
320 D(bug("%s: Close %s\n", pa->pa_Node.ln_Name));
321 Remove(&pa->pa_Node);
322 CloseDevice((struct IORequest *)pa->pa_IO);
323 DeleteIORequest((struct IORequest *)pa->pa_IO);
324 DeleteMsgPort(pa->pa_IOMsg);
325 FreeVec(pa);
328 void replyPkt(struct DosPacket *dp)
330 struct MsgPort *mp;
331 struct Message *mn;
333 D(bug("%s: type=%d res1=%d, res2=0x%p\n", __func__, dp->dp_Type, dp->dp_Res1, dp->dp_Res2));
334 mp = dp->dp_Port;
335 mn = dp->dp_Link;
336 mn->mn_Node.ln_Name = (char*)dp;
337 dp->dp_Port = &((struct Process*)FindTask(NULL))->pr_MsgPort;
338 PutMsg(mp, mn);
341 __startup void port_handler(void)
343 struct DosPacket *dp;
344 struct MsgPort *mp;
345 BOOL dead = FALSE;
346 SIPTR res;
347 struct FileHandle *fh;
348 struct portBase pb = {};
349 struct portArgs *pa;
351 NEWLIST(&pb.pb_Files);
353 mp = &((struct Process *)FindTask(NULL))->pr_MsgPort;
355 WaitPort(mp);
357 dp = (struct DosPacket *)(GetMsg(mp)->mn_Node.ln_Name);
359 res = RETURN_FAIL;
360 if ((pb.pb_UtilityBase = OpenLibrary("utility.library", 0))) {
361 res = decodeStartup(&pb, (BPTR)dp->dp_Arg2);
364 if (res == 0)
365 ((struct DeviceNode *)BADDR(dp->dp_Arg3))->dn_Task = mp;
367 dp->dp_Res2 = res;
368 dp->dp_Res1 = (dp->dp_Res2 == 0) ? DOSTRUE : DOSFALSE;
370 do {
371 replyPkt(dp);
372 WaitPort(mp);
373 dp = (struct DosPacket *)(GetMsg(mp)->mn_Node.ln_Name);
374 D(bug("%s: type=%d\n", __func__, dp->dp_Type));
376 switch (dp->dp_Type) {
377 case ACTION_FINDINPUT:
378 case ACTION_FINDOUTPUT:
379 case ACTION_FINDUPDATE:
380 pa = portOpen(&pb, pa, (BSTR)dp->dp_Arg3, &dp->dp_Res2);
381 if (dp->dp_Res2 == RETURN_OK) {
382 fh = BADDR(dp->dp_Arg1);
383 fh->fh_Arg1 = (SIPTR)pa;
384 fh->fh_Type = mp;
386 dp->dp_Res1 = (dp->dp_Res2 == 0) ? DOSTRUE : DOSFALSE;
387 break;
388 case ACTION_READ:
389 if ((pa = (struct portArgs *)dp->dp_Arg1)) {
390 pa->pa_IO->std.io_Command = CMD_READ;
391 pa->pa_IO->std.io_Data = (APTR)dp->dp_Arg2;
392 pa->pa_IO->std.io_Length = dp->dp_Arg3;
393 pa->pa_IO->std.io_Actual = 0;
394 pa->pa_IO->std.io_Offset = 0;
395 pa->pa_IO->std.io_Message.mn_Length = sizeof(pa->pa_IO->std);
396 res = DoIO((struct IORequest *)pa->pa_IO);
397 if (res == 0) {
398 dp->dp_Res1 = pa->pa_IO->std.io_Actual;
399 dp->dp_Res2 = 0;
400 } else {
401 dp->dp_Res1 = DOSFALSE;
402 dp->dp_Res2 = ERROR_READ_PROTECTED;
404 } else {
405 dp->dp_Res1 = DOSFALSE;
406 dp->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
408 break;
409 case ACTION_WRITE:
410 if ((pa = (struct portArgs *)dp->dp_Arg1)) {
411 if (pb.pb_Mode == PORT_PRINTER &&
412 pa->pa_Printer.pr_Type == PRT_RAW)
413 pa->pa_IO->std.io_Command = PRD_RAWWRITE;
414 else
415 pa->pa_IO->std.io_Command = CMD_WRITE;
416 pa->pa_IO->std.io_Data = (APTR)dp->dp_Arg2;
417 pa->pa_IO->std.io_Length = dp->dp_Arg3;
418 pa->pa_IO->std.io_Actual = 0;
419 pa->pa_IO->std.io_Offset = 0;
420 pa->pa_IO->std.io_Message.mn_Length = sizeof(pa->pa_IO->std);
421 res = DoIO((struct IORequest *)pa->pa_IO);
422 if (res == 0) {
423 dp->dp_Res1 = pa->pa_IO->std.io_Actual;
424 dp->dp_Res2 = 0;
425 } else {
426 dp->dp_Res1 = DOSFALSE;
427 dp->dp_Res2 = ERROR_READ_PROTECTED;
429 } else {
430 dp->dp_Res1 = DOSFALSE;
431 dp->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
433 break;
434 case ACTION_END:
435 if ((pa = (struct portArgs *)dp->dp_Arg1)) {
436 portClose(pa);
437 dp->dp_Res1 = DOSTRUE;
438 dp->dp_Res2 = 0;
439 } else {
440 dp->dp_Res1 = DOSFALSE;
441 dp->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
443 break;
444 case ACTION_DIE:
445 if (IsListEmpty(&pb.pb_Files)) {
446 dp->dp_Res1 = DOSTRUE;
447 dp->dp_Res2 = 0;
448 dead = TRUE;
449 } else {
450 dp->dp_Res1 = DOSFALSE;
451 dp->dp_Res2 = ERROR_OBJECT_IN_USE;
453 break;
454 default:
455 dp->dp_Res1 = DOSFALSE;
456 dp->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
457 break;
459 } while (!dead);
461 /* ACTION_DIE ends up here... */
462 D(bug("%s: Exiting\n"));
464 replyPkt(dp);
466 CloseLibrary(pb.pb_UtilityBase);