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