fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/drivers/char/char.c *
7 * Created: 2009-03-06 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2009-2020 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <config.h>
24
25#include <stdlib.h>
26#include <string.h>
27
28#include <drivers/options.h>
29#include <drivers/char/char.h>
30
31
32struct driver_list {
33 struct driver_list *next;
34 const char *name;
35 chr_open_f open;
36};
37
38struct driver_tab {
39 const char *name;
40 chr_open_f open;
41};
42
43
44char_drv_t *chr_mouse_open (const char *name);
45char_drv_t *chr_null_open (const char *name);
46char_drv_t *chr_posix_open (const char *name);
47char_drv_t *chr_ppp_open (const char *name);
48char_drv_t *chr_pty_open (const char *name);
49char_drv_t *chr_slip_open (const char *name);
50char_drv_t *chr_stdio_open (const char *name);
51char_drv_t *chr_tcp_open (const char *name);
52char_drv_t *chr_tios_open (const char *name);
53char_drv_t *chr_wincom_open (const char *name);
54
55
56static struct driver_list *drivers = NULL;
57
58
59static struct driver_tab default_drivers[] = {
60#ifdef PCE_ENABLE_CHAR_POSIX
61 { "posix", chr_posix_open },
62 { "sercon", chr_posix_open },
63#endif
64#ifdef PCE_ENABLE_CHAR_PPP
65 { "ppp", chr_ppp_open },
66#endif
67#ifdef PCE_ENABLE_CHAR_PTY
68 { "pty", chr_pty_open },
69#endif
70#ifdef PCE_ENABLE_CHAR_SLIP
71 { "slip", chr_slip_open },
72#endif
73#ifdef PCE_ENABLE_CHAR_TCP
74 { "tcp", chr_tcp_open },
75#endif
76#ifdef PCE_ENABLE_CHAR_TIOS
77 { "tios", chr_tios_open },
78#endif
79#ifdef PCE_ENABLE_CHAR_WINCOM
80 { "wincom", chr_wincom_open },
81#endif
82 { "stdio", chr_stdio_open },
83 { "mouse", chr_mouse_open },
84 { "null", chr_null_open }
85};
86
87
88static
89void chr_log_bytes (char_drv_t *cdrv, int out, const unsigned char *buf, unsigned cnt)
90{
91 int c;
92 unsigned i, n;
93
94 while (cnt > 0) {
95 n = (cnt < 16) ? cnt : 16;
96
97 fprintf (cdrv->log_fp, "%s %02X", out ? "->" : "<-", *buf);
98
99 for (i = 1; i < n; i++) {
100 fprintf (cdrv->log_fp, " %02X", buf[i]);
101 }
102
103 for (i = n; i < 16; i++) {
104 fputs (" ", cdrv->log_fp);
105 }
106
107 fputs (" ", cdrv->log_fp);
108
109 for (i = 0; i < n; i++) {
110 c = buf[i];
111
112 if ((c < 0x20) || (c > 0x7e)) {
113 c = '.';
114 }
115
116 fputc (c, cdrv->log_fp);
117 }
118
119 fputs ("\n", cdrv->log_fp);
120
121 buf += n;
122 cnt -= n;
123 }
124
125 fflush (cdrv->log_fp);
126}
127
128static
129void chr_log_flush (char_drv_t *cdrv)
130{
131 if (cdrv->log_cnt > 0) {
132 chr_log_bytes (cdrv, cdrv->log_out, cdrv->log_buf, cdrv->log_cnt);
133 }
134
135 cdrv->log_cnt = 0;
136 cdrv->log_out = 0;
137}
138
139static
140void chr_log_data (char_drv_t *cdrv, int out, const unsigned char *buf, unsigned cnt)
141{
142 unsigned n;
143
144 if (cdrv->log_fp == NULL) {
145 return;
146 }
147
148 if (cnt == 0) {
149 return;
150 }
151
152 if ((cdrv->log_cnt > 0) && (cdrv->log_out != out)) {
153 chr_log_flush (cdrv);
154 }
155
156 while (cnt > 0) {
157 n = 16 - cdrv->log_cnt;
158
159 if (cnt < n) {
160 n = cnt;
161 }
162
163 memcpy (cdrv->log_buf + cdrv->log_cnt, buf, n);
164
165 cdrv->log_cnt += n;
166 cdrv->log_out = out;
167
168 if (cdrv->log_cnt >= 16) {
169 chr_log_flush (cdrv);
170 }
171
172 buf += n;
173 cnt -= n;
174 }
175}
176
177static
178void chr_log_params (char_drv_t *cdrv)
179{
180 const char *par;
181
182 if (cdrv->log_fp == NULL) {
183 return;
184 }
185
186 chr_log_flush (cdrv);
187
188 switch (cdrv->parity) {
189 case 0:
190 par = "N";
191 break;
192
193 case 1:
194 par = "O";
195 break;
196
197 case 2:
198 par = "E";
199 break;
200
201 default:
202 par = "?";
203 break;
204 }
205
206 fprintf (cdrv->log_fp, "-- %lu %u%s%u\n",
207 cdrv->bps, cdrv->bpc, par, cdrv->stop
208 );
209
210 fflush (cdrv->log_fp);
211}
212
213static
214void chr_log_signal (char_drv_t *cdrv, const char *name, unsigned msk, unsigned old, unsigned new)
215{
216 if ((old ^ new) & msk) {
217 fprintf (cdrv->log_fp, "-- %s=%d\n", name, (new & msk) != 0);
218 }
219}
220
221static
222void chr_log_ctl (char_drv_t *cdrv, unsigned old, unsigned new)
223{
224 if ((cdrv->log_fp == NULL) || (old == new)) {
225 return;
226 }
227
228 chr_log_flush (cdrv);
229
230 chr_log_signal (cdrv, "CTS", PCE_CHAR_CTS, old, new);
231 chr_log_signal (cdrv, "DTR", PCE_CHAR_DTR, old, new);
232 chr_log_signal (cdrv, "RTS", PCE_CHAR_RTS, old, new);
233 chr_log_signal (cdrv, "DSR", PCE_CHAR_DSR, old, new);
234 chr_log_signal (cdrv, "CD", PCE_CHAR_CD, old, new);
235 chr_log_signal (cdrv, "RI", PCE_CHAR_RI, old, new);
236}
237
238static
239void chr_cap_data (char_drv_t *cdrv, const unsigned char *buf, unsigned cnt)
240{
241 if (cdrv->cap_fp == NULL) {
242 return;
243 }
244
245 if (cnt == 0) {
246 return;
247 }
248
249 fwrite (buf, 1, cnt, cdrv->cap_fp);
250 fflush (cdrv->cap_fp);
251}
252
253void chr_init (char_drv_t *cdrv, void *ext)
254{
255 cdrv->ext = ext;
256
257 cdrv->bps = 0;
258 cdrv->bpc = 0;
259 cdrv->parity = 0;
260 cdrv->stop = 0;
261
262 cdrv->ctl_inp = 0;
263 cdrv->ctl_out = 0;
264
265 cdrv->log_cnt = 0;
266 cdrv->log_out = 0;
267 cdrv->log_fp = NULL;
268
269 cdrv->cap_fp = NULL;
270
271 cdrv->close = NULL;
272
273 cdrv->read = NULL;
274 cdrv->write = NULL;
275
276 cdrv->get_ctl = NULL;
277 cdrv->set_ctl = NULL;
278
279 cdrv->set_params = NULL;
280}
281
282static
283int chr_match_name (struct driver_list *lst, const char *name)
284{
285 const char *s, *d;
286
287 s = name;
288 d = lst->name;
289
290 while ((*d != 0) && (*d == *s)) {
291 d += 1;
292 s += 1;
293 }
294
295 if ((*d == 0) && ((*s == ':') || (*s == 0))) {
296 return (1);
297 }
298
299 return (0);
300}
301
302static
303char_drv_t *chr_open_cdrv (char_drv_t *cdrv, const char *name)
304{
305 char *str;
306
307 if (cdrv == NULL) {
308 return (NULL);
309 }
310
311 str = drv_get_option (name, "log");
312
313 if (str != NULL) {
314 chr_set_log (cdrv, str);
315
316 free (str);
317 }
318
319 str = drv_get_option (name, "cap");
320
321 if (str != NULL) {
322 chr_set_cap (cdrv, str);
323 free (str);
324 }
325
326 return (cdrv);
327}
328
329char_drv_t *chr_open (const char *name)
330{
331 struct driver_list *lst;
332
333 if (drivers == NULL) {
334 /*
335 * If no drivers have been registered yet, register the
336 * default set.
337 */
338 chr_register_default();
339 }
340
341 lst = drivers;
342
343 while (lst != NULL) {
344 if (chr_match_name (lst, name)) {
345 return (chr_open_cdrv (lst->open (name), name));
346 }
347
348 lst = lst->next;
349 }
350
351 return (NULL);
352}
353
354void chr_close (char_drv_t *cdrv)
355{
356 if (cdrv == NULL) {
357 return;
358 }
359
360 if (cdrv->cap_fp != NULL) {
361 fclose (cdrv->cap_fp);
362 }
363
364 if (cdrv->log_fp != NULL) {
365 chr_log_flush (cdrv);
366 fclose (cdrv->log_fp);
367 }
368
369 if (cdrv->close == NULL) {
370 return;
371 }
372
373 cdrv->close (cdrv);
374}
375
376int chr_register (const char *name, chr_open_f open)
377{
378 struct driver_list *lst;
379
380 if ((lst = malloc (sizeof (struct driver_list))) == NULL) {
381 return (1);
382 }
383
384 lst->next = drivers;
385 lst->name = name;
386 lst->open = open;
387
388 drivers = lst;
389
390 return (0);
391}
392
393int chr_unregister (const char *name)
394{
395 int r;
396 struct driver_list *src, *dst, *drv, *next;
397
398 r = 1;
399
400 src = drivers;
401 dst = NULL;
402 drv = NULL;
403
404 while (src != NULL) {
405 next = src->next;
406
407 if ((name == NULL) || (strcmp (src->name, name) == 0)) {
408 free (src);
409 r = 0;
410 }
411 else {
412 if (dst != NULL) {
413 dst->next = src;
414 }
415 else {
416 drv = src;
417 }
418
419 dst = src;
420 }
421
422 src = next;
423 }
424
425 if (dst != NULL) {
426 dst->next = NULL;
427 }
428
429 drivers = drv;
430
431 return (r);
432}
433
434int chr_register_default (void)
435{
436 int r;
437 unsigned i, n;
438
439 r = 0;
440 n = sizeof (default_drivers) / sizeof (struct driver_tab);
441
442 for (i = 0; i < n; i++) {
443 r |= chr_register (default_drivers[i].name, default_drivers[i].open);
444 }
445
446 return (r);
447}
448
449unsigned chr_read (char_drv_t *cdrv, void *buf, unsigned cnt)
450{
451 unsigned ret;
452
453 if ((cdrv == NULL) || (cdrv->read == NULL)) {
454 return (0);
455 }
456
457 ret = cdrv->read (cdrv, buf, cnt);
458
459 chr_log_data (cdrv, 0, buf, ret);
460
461 return (ret);
462}
463
464unsigned chr_write (char_drv_t *cdrv, const void *buf, unsigned cnt)
465{
466 unsigned ret;
467
468 if ((cdrv == NULL) || (cdrv->write == NULL)) {
469 return (cnt);
470 }
471
472 ret = cdrv->write (cdrv, buf, cnt);
473
474 if (cdrv->cap_fp != NULL) {
475 chr_cap_data (cdrv, buf, ret);
476 }
477
478 chr_log_data (cdrv, 1, buf, ret);
479
480 return (ret);
481}
482
483int chr_get_ctl (char_drv_t *cdrv, unsigned *ctl)
484{
485 if (cdrv == NULL) {
486 return (1);
487 }
488
489 if (cdrv->get_ctl == NULL) {
490 *ctl = PCE_CHAR_CD;
491
492 if (cdrv->ctl_out & PCE_CHAR_DTR) {
493 *ctl |= PCE_CHAR_DSR;
494 }
495
496 if (cdrv->ctl_out & PCE_CHAR_RTS) {
497 *ctl |= PCE_CHAR_CTS;
498 }
499 }
500 else {
501 if (cdrv->get_ctl (cdrv, ctl)) {
502 return (1);
503 }
504 }
505
506 chr_log_ctl (cdrv, cdrv->ctl_inp, *ctl);
507
508 cdrv->ctl_inp = *ctl;
509
510 return (0);
511}
512
513int chr_set_ctl (char_drv_t *cdrv, unsigned ctl)
514{
515 if (cdrv == NULL) {
516 return (1);
517 }
518
519 if (cdrv->ctl_out == ctl) {
520 return (0);
521 }
522
523 chr_log_ctl (cdrv, cdrv->ctl_out, ctl);
524
525 cdrv->ctl_out = ctl;
526
527 if (cdrv->set_ctl == NULL) {
528 return (0);
529 }
530
531 return (cdrv->set_ctl (cdrv, ctl));
532}
533
534static
535int chr_check_params (char_drv_t *cdrv, unsigned long bps, unsigned bpc, unsigned parity, unsigned stop)
536{
537 if (cdrv->bps != bps) {
538 return (0);
539 }
540
541 if (cdrv->bpc != bpc) {
542 return (0);
543 }
544
545 if (cdrv->parity != parity) {
546 return (0);
547 }
548
549 if (cdrv->stop != stop) {
550 return (0);
551 }
552
553 return (1);
554}
555
556int chr_set_params (char_drv_t *cdrv, unsigned long bps, unsigned bpc, unsigned parity, unsigned stop)
557{
558 if (cdrv == NULL) {
559 return (1);
560 }
561
562 if (chr_check_params (cdrv, bps, bpc, parity, stop)) {
563 return (0);
564 }
565
566 cdrv->bps = bps;
567 cdrv->bpc = bpc;
568 cdrv->parity = parity;
569 cdrv->stop = stop;
570
571 chr_log_params (cdrv);
572
573 if (cdrv->set_params == NULL) {
574 return (1);
575 }
576
577 return (cdrv->set_params (cdrv, bps, bpc, parity, stop));
578}
579
580FILE *chr_open_file (const char *name, const char *mode, const char *modeapp)
581{
582 if (name == NULL) {
583 return (NULL);
584 }
585
586 if (*name == '@') {
587 name += 1;
588 return (fopen (name, modeapp));
589 }
590
591 return (fopen (name, mode));
592}
593
594int chr_set_log (char_drv_t *cdrv, const char *fname)
595{
596 if (cdrv->log_fp != NULL) {
597 chr_log_flush (cdrv);
598 fclose (cdrv->log_fp);
599 }
600
601 if ((cdrv->log_fp = chr_open_file (fname, "w", "a")) == NULL) {
602 return (1);
603 }
604
605 return (0);
606}
607
608int chr_set_cap (char_drv_t *cdrv, const char *fname)
609{
610 if (cdrv->cap_fp != NULL) {
611 fclose (cdrv->cap_fp);
612 }
613
614 if ((cdrv->cap_fp = chr_open_file (fname, "wb", "ab")) == NULL) {
615 return (1);
616 }
617
618 return (0);
619}