Automatic date update in version.in
[binutils-gdb.git] / sim / ppc / hw_com.c
blob768d1ca6a3c55c0d06b2ce913e47b0f2594b3ee2
1 /* This file is part of the program psim.
3 Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
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.
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.
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/>.
21 #ifndef _HW_COM_C_
22 #define _HW_COM_C_
24 #ifndef STATIC_INLINE_HW_COM
25 #define STATIC_INLINE_HW_COM STATIC_INLINE
26 #endif
28 #include "device_table.h"
30 #include <string.h>
31 #include <unistd.h>
32 #include <stdlib.h>
34 /* DEVICE
37 com - '550 compatible serial device
40 DESCRIPTION
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.
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.
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).
57 PROPERTIES
60 reg = <address> <size> ... (optional - note 1)
62 List of <address> <size> pairs. Each pair specifies an address for
63 the devices 8 registers. The address should be 8 byte aligned.
66 alternate-reg = <address> <size> ... (optional - note 1)
68 Alternative addreses for the registers.
71 assigned-addresses = <address> <size> ... (optional - note 1)
73 On a PCI bus, this property specifies the addresses assigned to the
74 device. The values reflect the devices configuration base registers.
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.
81 input-file = <file-name> (optional)
83 File to take all serial port input from (instead of the simulation
84 console).
87 output-file = <file-name> (optional)
89 File to send all output to (instead of the simulation console).
92 input-buffering = "unbuffered" (optional)
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.
99 output-buffering = "unbuffered" (optional)
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.
106 input-delay = <integer-delay> (optional)
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.
113 output-delay = <integer-delay> (optional)
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.
121 EXAMPLES
124 | /iobus@0xf0000000/com@0x3000/reg 0x3000 8
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>>.
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).
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
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.
147 The node <</cpus/cpu@0>> was explicitly specified to ensure that it had
148 been created before any interrupts were attached to it.
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.
156 BUGS
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.
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.
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).
173 The input blocks if no data is available.
175 Interrupts have not been tested.
179 enum {
180 max_hw_com_registers = 8,
183 typedef struct _com_port {
184 int ready;
185 int delay;
186 int interrupting;
187 FILE *file;
188 } com_port;
190 typedef struct _com_modem {
191 int carrier;
192 int carrier_changed;
193 int interrupting;
194 } com_modem;
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;
206 static void
207 hw_com_device_init_data(device *me)
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));
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);
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);
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);
249 /* ready from the start */
250 com->input.ready = 1;
251 com->modem.carrier = 1;
252 com->output.ready = 1;
256 static void
257 update_com_interrupts(device *me,
258 hw_com_device *com)
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);
268 if (interrupting) {
269 if (!com->interrupting) {
270 device_interrupt_event(me, 0 /*port*/, 1 /*value*/, NULL, 0);
273 else /*!interrupting*/ {
274 if (com->interrupting)
275 device_interrupt_event(me, 0 /*port*/, 0 /*value*/, NULL, 0);
277 com->interrupting = interrupting;
281 static void
282 make_read_ready(void *data)
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);
290 static void
291 read_com(device *me,
292 hw_com_device *com,
293 unsigned_word a,
294 char val[1])
296 unsigned_word addr = a % 8;
298 /* the divisor latch is special */
299 if (com->reg[3] & 0x8 && addr < 2) {
300 *val = com->dlab[addr];
301 return;
304 switch (addr) {
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;
316 else {
317 if (fread(val, 1, 1, com->input.file) == 0)
318 com->modem.carrier_changed = 1;
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';
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);
332 else {
333 /* discard it? */
334 /* overflow input fifo? */
335 *val = '\0';
337 break;
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");
351 else
352 *val = 0x1;
353 break;
355 case 5:
356 /* line status */
357 *val = ((com->input.ready ? 0x1 : 0)
358 | (com->output.ready ? 0x60 : 0)
360 break;
362 case 6:
363 /* modem status */
364 *val = ((com->modem.carrier_changed ? 0x08 : 0)
365 | (com->modem.carrier ? 0x80 : 0)
367 com->modem.carrier_changed = 0;
368 break;
370 default:
371 *val = com->reg[addr];
372 break;
375 update_com_interrupts(me, com);
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)
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]);
392 return nr_bytes;
396 static void
397 make_write_ready(void *data)
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);
405 static void
406 write_com(device *me,
407 hw_com_device *com,
408 unsigned_word a,
409 char val)
411 unsigned_word addr = a % 8;
413 /* the divisor latch is special */
414 if (com->reg[3] & 0x8 && addr < 2) {
415 com->dlab[addr] = val;
416 return;
419 switch (addr) {
421 case 0:
422 /* fifo */
423 if (com->output.file == NULL) {
424 sim_io_write_stdout(&val, 1);
426 else {
427 fwrite(&val, 1, 1, com->output.file);
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);
434 break;
436 default:
437 com->reg[addr] = val;
438 break;
441 update_com_interrupts(me, com);
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)
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]);
458 return nr_bytes;
462 /* instances of the hw_com device */
464 static void
465 hw_com_instance_delete(device_instance *instance)
467 /* nothing to delete, the hw_com is attached to the device */
468 return;
471 static int
472 hw_com_instance_read(device_instance *instance,
473 void *buf,
474 unsigned_word len)
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);
485 static int
486 hw_com_instance_write(device_instance *instance,
487 const void *buf,
488 unsigned_word len)
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);
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,
505 static device_instance *
506 hw_com_create_instance(device *me,
507 const char *path,
508 const char *args)
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);
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,
531 static void *
532 hw_com_create(const char *name,
533 const device_unit *unit_address,
534 const char *args)
536 /* create the descriptor */
537 hw_com_device *hw_com = ZALLOC(hw_com_device);
538 return hw_com;
542 const device_descriptor hw_com_device_descriptor[] = {
543 { "com", hw_com_create, &hw_com_callbacks },
544 { NULL },
547 #endif /* _HW_COM_C_ */