xref: /netbsd-src/external/gpl3/gdb/dist/sim/ppc/hw_com.c (revision 7788a0781fe6ff2cce37368b4578a7ade0850cb1)
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 
191 enum {
192   max_hw_com_registers = 8,
193 };
194 
195 typedef struct _com_port {
196   int ready;
197   int delay;
198   int interrupting;
199   FILE *file;
200 } com_port;
201 
202 typedef struct _com_modem {
203   int carrier;
204   int carrier_changed;
205   int interrupting;
206 } com_modem;
207 
208 typedef 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 
218 static void
219 hw_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 
268 static void
269 update_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 
293 static void
294 make_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 
302 static void
303 read_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 
390 static unsigned
391 hw_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 
408 static void
409 make_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 
417 static void
418 write_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 
456 static unsigned
457 hw_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 
476 static void
477 hw_com_instance_delete(device_instance *instance)
478 {
479   /* nothing to delete, the hw_com is attached to the device */
480   return;
481 }
482 
483 static int
484 hw_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 
497 static int
498 hw_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 
511 static 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 
517 static device_instance *
518 hw_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 
530 static 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 
543 static void *
544 hw_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 
554 const device_descriptor hw_com_device_descriptor[] = {
555   { "com", hw_com_create, &hw_com_callbacks },
556   { NULL },
557 };
558 
559 #endif /* _HW_COM_C_ */
560