1/*  This file is part of the program psim.
2
3    Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19    */
20
21
22#ifndef _HW_COM_C_
23#define _HW_COM_C_
24
25#ifndef STATIC_INLINE_HW_COM
26#define STATIC_INLINE_HW_COM STATIC_INLINE
27#endif
28
29#include "device_table.h"
30
31#ifdef HAVE_STRING_H
32#include <string.h>
33#else
34#ifdef HAVE_STRINGS_H
35#include <strings.h>
36#endif
37#endif
38
39#ifdef HAVE_UNISTD_H
40#include <unistd.h>
41#endif
42#ifdef HAVE_STDLIB_H
43#include <stdlib.h>
44#endif
45
46/* DEVICE
47
48
49   com - '550 compatible serial device
50
51
52   DESCRIPTION
53
54
55   Models the basics of the 8 register '550 serial device.  The model
56   includes an interrupt line, input and output fifos, and status
57   information.
58
59   Independent configuration of the devices input and output streams is
60   allowed: use either the console or a file (buffered or unbuffered) as
61   the data source/sink; specify the real-time delay between each character
62   transfer.
63
64   When the devices input stream is being taken from a file, the end of
65   file is signaled by a loss of carrier (the loss of carrier may be
66   incorrectly proceeded by a single null character).
67
68
69   PROPERTIES
70
71
72   reg = <address> <size> ... (optional - note 1)
73
74   List of <address> <size> pairs.  Each pair specifies an address for
75   the devices 8 registers.  The address should be 8 byte aligned.
76
77
78   alternate-reg = <address> <size> ... (optional - note 1)
79
80   Alternative addreses for the registers.
81
82
83   assigned-addresses = <address> <size> ... (optional - note 1)
84
85   On a PCI bus, this property specifies the addresses assigned to the
86   device.  The values reflect the devices configuration base registers.
87
88   Note 1: At least one of "assigned-addresses", "reg" or "alternative-reg"
89   must be specified.  If "assigned-addresses" is specified the other
90   address specifications are ignored.
91
92
93   input-file = <file-name> (optional)
94
95   File to take all serial port input from (instead of the simulation
96   console).
97
98
99   output-file = <file-name> (optional)
100
101   File to send all output to (instead of the simulation console).
102
103
104   input-buffering = "unbuffered" (optional)
105
106   Specifying "unbuffered" buffering disables buffering on the serial
107   devices input stream (all data is immediatly read).  In the future,
108   this option may be used to provide input buffering alternatives.
109
110
111   output-buffering = "unbuffered" (optional)
112
113   Specifying "unbuffered" buffering disables buffering on the serial
114   devices output stream (all data is immediatly written).  In the future,
115   this option may be extended to include other buffering alternatives.
116
117
118   input-delay = <integer-delay> (optional)
119
120   Specify the number of ticks after the current character has been
121   read from the serial port that the next character becomes
122   available.
123
124
125   output-delay = <integer-delay> (optional)
126
127   Specify the number of ticks after a character has been written to
128   the empty output fifo that the fifo finishes draining.  Any
129   characters written to the output fifo before it has drained will
130   not be lost and will still be displayed.
131
132
133   EXAMPLES
134
135
136   |  /iobus@0xf0000000/com@0x3000/reg 0x3000 8
137
138   Create a simple console device at address <<0x3000>> within
139   <<iobus>>.  Since iobus starts at address <<0xf0000000>> the
140   absolute address of the serial port will be <<0xf0003000>>.
141
142   The device will always be ready for I/O (no delay properties specified)
143   and both the input and output streams will use the simulation console
144   (no file properties).
145
146
147   |  $ psim \
148   |    -o '/cpus/cpu@0' \
149   |    -o '/iobus@0xf0000000/com@0x4000/reg 0x4000 8' \
150   |    -o '/iobus@0xf0000000/com@0x4000/input-file /etc/passwd' \
151   |    -o '/iobus@0xf0000000/com@0x4000/input-delay 1000' \
152   |    -o '/iobus@0xf0000000/com@0x4000 > 0 int /cpus/cpu@0x0' \
153   |    psim-test/hw-com/cat.be 0xf0004000
154
155   The serial port (at address <<0xf0004000>> is configured so that it
156   takes its input from the file <</etc/passwd>> while its output is
157   allowed to appear on the simulation console.
158
159   The node <</cpus/cpu@0>> was explicitly specified to ensure that it had
160   been created before any interrupts were attached to it.
161
162   The program <<psim-test/hw-com/cat>> copies any characters on the serial
163   port's input (<</etc/passwd>>) to its output (the console).
164   Consequently, the aove program will display the contents of the file
165   <</etc/passwd>> on the screen.
166
167
168   BUGS
169
170
171   IEEE 1275 requires that a device on a PCI bus have, as its first reg
172   entry, the address of its configuration space registers.  Currently,
173   this device does not even implement configuration registers.
174
175   This model does not attempt to model the '550's input and output fifos.
176   Instead, the input fifo is limited to a single character at a time,
177   while the output fifo is effectivly infinite.  Consequently, unlike the
178   '550, this device will not discard output characters once a stream of 16
179   have been written to the data output register.
180
181   The input and output can only be taken from a file (or the current
182   terminal device).  In the future, the <<com>> device should allow the
183   specification of other data streams (such as an xterm or TK window).
184
185   The input blocks if no data is available.
186
187   Interrupts have not been tested.
188
189   */
190
191enum {
192  max_hw_com_registers = 8,
193};
194
195typedef struct _com_port {
196  int ready;
197  int delay;
198  int interrupting;
199  FILE *file;
200} com_port;
201
202typedef struct _com_modem {
203  int carrier;
204  int carrier_changed;
205  int interrupting;
206} com_modem;
207
208typedef struct _hw_com_device {
209  com_port input;
210  com_port output;
211  com_modem modem;
212  char dlab[2];
213  char reg[max_hw_com_registers];
214  int interrupting;
215} hw_com_device;
216
217
218static void
219hw_com_device_init_data(device *me)
220{
221  hw_com_device *com = (hw_com_device*)device_data(me);
222  /* clean up */
223  if (com->output.file != NULL)
224    fclose(com->output.file);
225  if (com->input.file != NULL)
226    fclose(com->input.file);
227  memset(com, 0, sizeof(hw_com_device));
228
229  /* the fifo speed */
230  com->output.delay = (device_find_property(me, "output-delay") != NULL
231		       ? device_find_integer_property(me, "output-delay")
232		       : 0);
233  com->input.delay = (device_find_property(me, "input-delay") != NULL
234		      ? device_find_integer_property(me, "input-delay")
235		      : 0);
236
237  /* the data source/sink */
238  if (device_find_property(me, "input-file") != NULL) {
239    const char *input_file = device_find_string_property(me, "input-file");
240    com->input.file = fopen(input_file, "r");
241    if (com->input.file == NULL)
242      device_error(me, "Problem opening input file %s\n", input_file);
243    if (device_find_property(me, "input-buffering") != NULL) {
244      const char *buffering = device_find_string_property(me, "input-buffering");
245      if (strcmp(buffering, "unbuffered") == 0)
246	setbuf(com->input.file, NULL);
247    }
248  }
249  if (device_find_property(me, "output-file") != NULL) {
250    const char *output_file = device_find_string_property(me, "output-file");
251    com->output.file = fopen(output_file, "w");
252    if (com->output.file == NULL)
253      device_error(me, "Problem opening output file %s\n", output_file);
254    if (device_find_property(me, "output-buffering") != NULL) {
255      const char *buffering = device_find_string_property(me, "output-buffering");
256      if (strcmp(buffering, "unbuffered") == 0)
257	setbuf(com->output.file, NULL);
258    }
259  }
260
261  /* ready from the start */
262  com->input.ready = 1;
263  com->modem.carrier = 1;
264  com->output.ready = 1;
265}
266
267
268static void
269update_com_interrupts(device *me,
270		      hw_com_device *com)
271{
272  int interrupting;
273  com->modem.interrupting = (com->modem.carrier_changed && (com->reg[1] & 0x80));
274  com->input.interrupting = (com->input.ready && (com->reg[1] & 0x1));
275  com->output.interrupting = (com->output.ready && (com->reg[1] & 0x2));
276  interrupting = (com->input.interrupting
277		  || com->output.interrupting
278		  || com->modem.interrupting);
279
280  if (interrupting) {
281    if (!com->interrupting) {
282      device_interrupt_event(me, 0 /*port*/, 1 /*value*/, NULL, 0);
283    }
284  }
285  else /*!interrupting*/ {
286    if (com->interrupting)
287      device_interrupt_event(me, 0 /*port*/, 0 /*value*/, NULL, 0);
288  }
289  com->interrupting = interrupting;
290}
291
292
293static void
294make_read_ready(void *data)
295{
296  device *me = (device*)data;
297  hw_com_device *com = (hw_com_device*)device_data(me);
298  com->input.ready = 1;
299  update_com_interrupts(me, com);
300}
301
302static void
303read_com(device *me,
304	 hw_com_device *com,
305	 unsigned_word a,
306	 char val[1])
307{
308  unsigned_word addr = a % 8;
309
310  /* the divisor latch is special */
311  if (com->reg[3] & 0x8 && addr < 2) {
312    *val = com->dlab[addr];
313    return;
314  }
315
316  switch (addr) {
317
318  case 0:
319    /* fifo */
320    if (!com->modem.carrier)
321      *val = '\0';
322    if (com->input.ready) {
323      /* read the char in */
324      if (com->input.file == NULL) {
325	if (sim_io_read_stdin(val, 1) < 0)
326	  com->modem.carrier_changed = 1;
327      }
328      else {
329	if (fread(val, 1, 1, com->input.file) == 0)
330	  com->modem.carrier_changed = 1;
331      }
332      /* setup for next read */
333      if (com->modem.carrier_changed) {
334	/* once lost carrier, never ready */
335	com->modem.carrier = 0;
336	com->input.ready = 0;
337	*val = '\0';
338      }
339      else if (com->input.delay > 0) {
340	com->input.ready = 0;
341	device_event_queue_schedule(me, com->input.delay, make_read_ready, me);
342      }
343    }
344    else {
345      /* discard it? */
346      /* overflow input fifo? */
347      *val = '\0';
348    }
349    break;
350
351  case 2:
352    /* interrupt ident */
353    if (com->interrupting) {
354      if (com->input.interrupting)
355	*val = 0x4;
356      else if (com->output.interrupting)
357	*val = 0x2;
358      else if (com->modem.interrupting == 0)
359	*val = 0;
360      else
361	device_error(me, "bad elif for interrupts\n");
362    }
363    else
364      *val = 0x1;
365    break;
366
367  case 5:
368    /* line status */
369    *val = ((com->input.ready ? 0x1 : 0)
370	    | (com->output.ready ? 0x60 : 0)
371	    );
372    break;
373
374  case 6:
375    /* modem status */
376    *val = ((com->modem.carrier_changed ? 0x08 : 0)
377	    | (com->modem.carrier ? 0x80 : 0)
378	    );
379    com->modem.carrier_changed = 0;
380    break;
381
382  default:
383    *val = com->reg[addr];
384    break;
385
386  }
387  update_com_interrupts(me, com);
388}
389
390static unsigned
391hw_com_io_read_buffer_callback(device *me,
392			       void *dest,
393			       int space,
394			       unsigned_word addr,
395			       unsigned nr_bytes,
396			       cpu *processor,
397			       unsigned_word cia)
398{
399  hw_com_device *com = device_data(me);
400  int i;
401  for (i = 0; i < nr_bytes; i++) {
402    read_com(me, com, addr + i, &((char*)dest)[i]);
403  }
404  return nr_bytes;
405}
406
407
408static void
409make_write_ready(void *data)
410{
411  device *me = (device*)data;
412  hw_com_device *com = (hw_com_device*)device_data(me);
413  com->output.ready = 1;
414  update_com_interrupts(me, com);
415}
416
417static void
418write_com(device *me,
419	  hw_com_device *com,
420	  unsigned_word a,
421	  char val)
422{
423  unsigned_word addr = a % 8;
424
425  /* the divisor latch is special */
426  if (com->reg[3] & 0x8 && addr < 2) {
427    com->dlab[addr] = val;
428    return;
429  }
430
431  switch (addr) {
432
433  case 0:
434    /* fifo */
435    if (com->output.file == NULL) {
436      sim_io_write_stdout(&val, 1);
437    }
438    else {
439      fwrite(&val, 1, 1, com->output.file);
440    }
441    /* setup for next write */
442    if (com->output.ready && com->output.delay > 0) {
443      com->output.ready = 0;
444      device_event_queue_schedule(me, com->output.delay, make_write_ready, me);
445    }
446    break;
447
448  default:
449    com->reg[addr] = val;
450    break;
451
452  }
453  update_com_interrupts(me, com);
454}
455
456static unsigned
457hw_com_io_write_buffer_callback(device *me,
458				const void *source,
459				int space,
460				unsigned_word addr,
461				unsigned nr_bytes,
462				cpu *processor,
463				unsigned_word cia)
464{
465  hw_com_device *com = device_data(me);
466  int i;
467  for (i = 0; i < nr_bytes; i++) {
468    write_com(me, com, addr + i, ((char*)source)[i]);
469  }
470  return nr_bytes;
471}
472
473
474/* instances of the hw_com device */
475
476static void
477hw_com_instance_delete(device_instance *instance)
478{
479  /* nothing to delete, the hw_com is attached to the device */
480  return;
481}
482
483static int
484hw_com_instance_read(device_instance *instance,
485		     void *buf,
486		     unsigned_word len)
487{
488  device *me = device_instance_device(instance);
489  hw_com_device *com = device_data(me);
490  if (com->input.file == NULL)
491    return sim_io_read_stdin(buf, len);
492  else {
493    return fread(buf, 1, len, com->input.file);
494  }
495}
496
497static int
498hw_com_instance_write(device_instance *instance,
499		      const void *buf,
500		      unsigned_word len)
501{
502  device *me = device_instance_device(instance);
503  hw_com_device *com = device_data(me);
504  if (com->output.file == NULL)
505    return sim_io_write_stdout(buf, len);
506  else {
507    return fwrite(buf, 1, len, com->output.file);
508  }
509}
510
511static const device_instance_callbacks hw_com_instance_callbacks = {
512  hw_com_instance_delete,
513  hw_com_instance_read,
514  hw_com_instance_write,
515};
516
517static device_instance *
518hw_com_create_instance(device *me,
519		       const char *path,
520		       const char *args)
521{
522  /* point an instance directly at the device */
523  return device_create_instance_from(me, NULL,
524				     device_data(me),
525				     path, args,
526				     &hw_com_instance_callbacks);
527}
528
529
530static device_callbacks const hw_com_callbacks = {
531  { generic_device_init_address,
532    hw_com_device_init_data },
533  { NULL, }, /* address */
534  { hw_com_io_read_buffer_callback,
535      hw_com_io_write_buffer_callback, },
536  { NULL, }, /* DMA */
537  { NULL, }, /* interrupt */
538  { NULL, }, /* unit */
539  hw_com_create_instance,
540};
541
542
543static void *
544hw_com_create(const char *name,
545	      const device_unit *unit_address,
546	      const char *args)
547{
548  /* create the descriptor */
549  hw_com_device *hw_com = ZALLOC(hw_com_device);
550  return hw_com;
551}
552
553
554const device_descriptor hw_com_device_descriptor[] = {
555  { "com", hw_com_create, &hw_com_callbacks },
556  { NULL },
557};
558
559#endif /* _HW_COM_C_ */
560