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 2 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, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #ifndef STATIC_INLINE_HW_COM
26 #define STATIC_INLINE_HW_COM STATIC_INLINE
29 #include "device_table.h"
49 com - '550 compatible serial device
55 Models the basics of the 8 register '550 serial device. The model
56 includes an interrupt line, input and output fifos, and status
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
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).
72 reg = <address> <size> ... (optional - note 1)
74 List of <address> <size> pairs. Each pair specifies an address for
75 the devices 8 registers. The address should be 8 byte aligned.
78 alternate-reg = <address> <size> ... (optional - note 1)
80 Alternative addreses for the registers.
83 assigned-addresses = <address> <size> ... (optional - note 1)
85 On a PCI bus, this property specifies the addresses assigned to the
86 device. The values reflect the devices configuration base registers.
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.
93 input-file = <file-name> (optional)
95 File to take all serial port input from (instead of the simulation
99 output-file = <file-name> (optional)
101 File to send all output to (instead of the simulation console).
104 input-buffering = "unbuffered" (optional)
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.
111 output-buffering = "unbuffered" (optional)
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.
118 input-delay = <integer-delay> (optional)
120 Specify the number of ticks after the current character has been
121 read from the serial port that the next character becomes
125 output-delay = <integer-delay> (optional)
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.
136 | /iobus@0xf0000000/com@0x3000/reg 0x3000 8
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>>.
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).
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
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.
159 The node <</cpus/cpu@0>> was explicitly specified to ensure that it had
160 been created before any interrupts were attached to it.
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.
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.
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.
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).
185 The input blocks if no data is available.
187 Interrupts have not been tested.
192 max_hw_com_registers = 8,
195 typedef struct _com_port {
202 typedef struct _com_modem {
208 typedef struct _hw_com_device {
213 char reg[max_hw_com_registers];
219 hw_com_device_init_data(device *me)
221 hw_com_device *com = (hw_com_device*)device_data(me);
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));
230 com->output.delay = (device_find_property(me, "output-delay") != NULL
231 ? device_find_integer_property(me, "output-delay")
233 com->input.delay = (device_find_property(me, "input-delay") != NULL
234 ? device_find_integer_property(me, "input-delay")
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);
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);
261 /* ready from the start */
262 com->input.ready = 1;
263 com->modem.carrier = 1;
264 com->output.ready = 1;
269 update_com_interrupts(device *me,
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);
281 if (!com->interrupting) {
282 device_interrupt_event(me, 0 /*port*/, 1 /*value*/, NULL, 0);
285 else /*!interrupting*/ {
286 if (com->interrupting)
287 device_interrupt_event(me, 0 /*port*/, 0 /*value*/, NULL, 0);
289 com->interrupting = interrupting;
294 make_read_ready(void *data)
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);
308 unsigned_word addr = a % 8;
310 /* the divisor latch is special */
311 if (com->reg[3] & 0x8 && addr < 2) {
312 *val = com->dlab[addr];
320 if (!com->modem.carrier)
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;
329 if (fread(val, 1, 1, com->input.file) == 0)
330 com->modem.carrier_changed = 1;
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;
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);
346 /* overflow input fifo? */
352 /* interrupt ident */
353 if (com->interrupting) {
354 if (com->input.interrupting)
356 else if (com->output.interrupting)
358 else if (com->modem.interrupting == 0)
361 device_error(me, "bad elif for interrupts\n");
369 *val = ((com->input.ready ? 0x1 : 0)
370 | (com->output.ready ? 0x60 : 0)
376 *val = ((com->modem.carrier_changed ? 0x08 : 0)
377 | (com->modem.carrier ? 0x80 : 0)
379 com->modem.carrier_changed = 0;
383 *val = com->reg[addr];
387 update_com_interrupts(me, com);
391 hw_com_io_read_buffer_callback(device *me,
399 hw_com_device *com = device_data(me);
401 for (i = 0; i < nr_bytes; i++) {
402 read_com(me, com, addr + i, &((char*)dest)[i]);
409 make_write_ready(void *data)
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);
418 write_com(device *me,
423 unsigned_word addr = a % 8;
425 /* the divisor latch is special */
426 if (com->reg[3] & 0x8 && addr < 2) {
427 com->dlab[addr] = val;
435 if (com->output.file == NULL) {
436 sim_io_write_stdout(&val, 1);
439 fwrite(&val, 1, 1, com->output.file);
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);
449 com->reg[addr] = val;
453 update_com_interrupts(me, com);
457 hw_com_io_write_buffer_callback(device *me,
465 hw_com_device *com = device_data(me);
467 for (i = 0; i < nr_bytes; i++) {
468 write_com(me, com, addr + i, ((char*)source)[i]);
474 /* instances of the hw_com device */
477 hw_com_instance_delete(device_instance *instance)
479 /* nothing to delete, the hw_com is attached to the device */
484 hw_com_instance_read(device_instance *instance,
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);
493 return fread(buf, 1, len, com->input.file);
498 hw_com_instance_write(device_instance *instance,
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);
507 return fwrite(buf, 1, len, com->output.file);
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,
517 static device_instance *
518 hw_com_create_instance(device *me,
522 /* point an instance directly at the device */
523 return device_create_instance_from(me, NULL,
526 &hw_com_instance_callbacks);
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, },
537 { NULL, }, /* interrupt */
538 { NULL, }, /* unit */
539 hw_com_create_instance,
544 hw_com_create(const char *name,
545 const device_unit *unit_address,
548 /* create the descriptor */
549 hw_com_device *hw_com = ZALLOC(hw_com_device);
554 const device_descriptor hw_com_device_descriptor[] = {
555 { "com", hw_com_create, &hw_com_callbacks },
559 #endif /* _HW_COM_C_ */