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