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