]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/ppc/hw_com.c
* gdb.texinfo (Set Catchpoints): Remove the documentation of
[thirdparty/binutils-gdb.git] / sim / ppc / hw_com.c
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_ */