port-handler: Port handler (PAR:, SER:, PRT:, etc) devices
[AROS.git] / workbench / fs / port / port-handler.c
bloba353512835d445b34e3542ef42e7c311b6b83617
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 if ((pa->pa_IOMsg = CreateMsgPort())) {
272 if ((pa->pa_IO = (APTR)CreateIORequest(pa->pa_IOMsg, sizeof(*pa->pa_IO)))) {
273 if (0 == OpenDevice(pb->pb_DeviceName, pa->pa_DeviceUnit, (struct IORequest *)pa->pa_IO, pb->pb_DeviceFlags)) {
274 *err = 0;
275 if (pb->pb_Mode != PORT_SERIAL) {
276 AddTail(&pb->pb_Files, &pa->pa_Node);
277 return pa;
280 pa->pa_IO->ser.IOSer.io_Command = SDCMD_SETPARAMS;
281 /* xON xOFF ENQ ACK */
282 pa->pa_IO->ser.io_CtlChar = SER_DEFAULT_CTLCHAR;
283 pa->pa_IO->ser.io_RBufLen = 64;
284 pa->pa_IO->ser.io_SerFlags = pa->pa_Serial.ps_SerFlags;
285 pa->pa_IO->ser.io_ExtFlags = pa->pa_Serial.ps_ExtFlags;
286 pa->pa_IO->ser.io_Baud = pa->pa_Serial.ps_Baud;
287 pa->pa_IO->ser.io_BrkTime = 250000;
288 pa->pa_IO->ser.io_TermArray.TermArray0 = 0;
289 pa->pa_IO->ser.io_TermArray.TermArray1 = 1;
290 pa->pa_IO->ser.io_ReadLen = pa->pa_Serial.ps_LenBits;
291 pa->pa_IO->ser.io_WriteLen = pa->pa_Serial.ps_LenBits;
292 pa->pa_IO->ser.io_StopBits = pa->pa_Serial.ps_StopBits;
294 if (0 == DoIO((struct IORequest *)pa->pa_IO)) {
295 AddTail(&pb->pb_Files, &pa->pa_Node);
296 return pa;
298 CloseDevice((struct IORequest *)pa->pa_IO);
300 DeleteIORequest((struct IORequest *)pa->pa_IO);
302 DeleteMsgPort(pa->pa_IOMsg);
304 FreeVec(pa);
307 *err = ERROR_NO_DISK;
308 return NULL;
311 static void portClose(struct portArgs *pa)
313 Remove(&pa->pa_Node);
314 CloseDevice((struct IORequest *)pa->pa_IO);
315 DeleteIORequest((struct IORequest *)pa->pa_IO);
316 DeleteMsgPort(pa->pa_IOMsg);
317 FreeVec(pa);
320 __startup void _main(void)
322 struct DosPacket *dp;
323 struct MsgPort *mp;
324 BOOL dead = FALSE;
325 SIPTR res;
326 struct FileHandle *fh;
327 struct portBase pb = {};
328 struct portArgs *pa;
330 NEWLIST(&pb.pb_Files);
332 mp = &((struct Process *)FindTask(NULL))->pr_MsgPort;
334 WaitPort(mp);
336 dp = (struct DosPacket *)(GetMsg(mp)->mn_Node.ln_Name);
338 res = RETURN_FAIL;
339 if ((pb.pb_UtilityBase = OpenLibrary("utility.library", 0))) {
340 res = decodeStartup(&pb, (BPTR)dp->dp_Arg2);
343 dp->dp_Res2 = res;
344 dp->dp_Res1 = (dp->dp_Res2 == 0) ? DOSTRUE : DOSFALSE;
346 do {
347 PutMsg (dp->dp_Port, dp->dp_Link);
348 WaitPort(mp);
349 dp = (struct DosPacket *)(GetMsg(mp)->mn_Node.ln_Name);
351 switch (dp->dp_Type) {
352 case ACTION_FINDINPUT:
353 case ACTION_FINDOUTPUT:
354 case ACTION_FINDUPDATE:
355 pa = portOpen(&pb, pa, (BSTR)dp->dp_Arg3, &dp->dp_Res2);
356 if (dp->dp_Res2 == RETURN_OK) {
357 fh = BADDR(dp->dp_Arg1);
358 fh->fh_Arg1 = (SIPTR)pa;
359 fh->fh_Type = mp;
361 dp->dp_Res1 = (dp->dp_Res2 == 0) ? DOSTRUE : DOSFALSE;
362 break;
363 case ACTION_READ:
364 if ((pa = (struct portArgs *)dp->dp_Arg1)) {
365 pa->pa_IO->std.io_Command = CMD_READ;
366 pa->pa_IO->std.io_Data = (APTR)dp->dp_Arg2;
367 pa->pa_IO->std.io_Length = dp->dp_Arg3;
368 pa->pa_IO->std.io_Actual = 0;
369 pa->pa_IO->std.io_Offset = 0;
370 pa->pa_IO->std.io_Message.mn_Length = sizeof(pa->pa_IO->std);
371 res = DoIO((struct IORequest *)pa->pa_IO);
372 if (res == 0) {
373 dp->dp_Res1 = pa->pa_IO->std.io_Actual;
374 dp->dp_Res2 = 0;
375 } else {
376 dp->dp_Res1 = DOSFALSE;
377 dp->dp_Res2 = ERROR_READ_PROTECTED;
379 } else {
380 dp->dp_Res1 = DOSFALSE;
381 dp->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
383 break;
384 case ACTION_WRITE:
385 if ((pa = (struct portArgs *)dp->dp_Arg1)) {
386 if (pb.pb_Mode == PORT_PRINTER &&
387 pa->pa_Printer.pr_Type == PRT_RAW)
388 pa->pa_IO->std.io_Command = PRD_RAWWRITE;
389 else
390 pa->pa_IO->std.io_Command = CMD_WRITE;
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_END:
410 if ((pa = (struct portArgs *)dp->dp_Arg1)) {
411 portClose(pa);
412 dp->dp_Res1 = DOSTRUE;
413 dp->dp_Res2 = 0;
414 } else {
415 dp->dp_Res1 = DOSFALSE;
416 dp->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
418 break;
419 case ACTION_DIE:
420 if (IsListEmpty(&pb.pb_Files)) {
421 dp->dp_Res1 = DOSTRUE;
422 dp->dp_Res2 = 0;
423 dead = TRUE;
424 } else {
425 dp->dp_Res1 = DOSFALSE;
426 dp->dp_Res2 = ERROR_OBJECT_IN_USE;
428 break;
429 default:
430 dp->dp_Res1 = DOSFALSE;
431 dp->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
432 break;
434 } while (!dead);
436 /* ACTION_DIE ends up here... */
437 PutMsg (dp->dp_Port, dp->dp_Link);
439 CloseLibrary(pb.pb_UtilityBase);