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 #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