Home | History | Annotate | Line # | Download | only in ppc
      1 /*  This file is part of the program psim.
      2 
      3     Copyright (C) 1994-1996, Andrew Cagney <cagney (at) 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