/* Accept a TCP connection */ static int vtty_tcp_conn_accept(vtty_t *vtty, int nsock) { int fd,*fd_slot; u_int i; if (fd_pool_get_free_slot(&vtty->fd_pool,&fd_slot) < 0) { vm_error(vtty->vm,"unable to create a new VTTY TCP connection\n"); return(-1); } if ((fd = accept(vtty->fd_array[nsock],NULL,NULL)) < 0) { vm_error(vtty->vm,"vtty_tcp_conn_accept: accept on port %d failed %s\n", vtty->tcp_port,strerror(errno)); return(-1); } /* Register the new FD */ *fd_slot = fd; vm_log(vtty->vm,"VTTY","%s is now connected (accept_fd=%d,conn_fd=%d)\n", vtty->name,vtty->fd_array[nsock],fd); /* Adapt Telnet settings */ if (vtty->terminal_support) { vtty_telnet_do_ttype(fd); vtty_telnet_will_echo(fd); vtty_telnet_will_suppress_go_ahead(fd); vtty_telnet_dont_linemode(fd); vtty->input_state = VTTY_INPUT_TEXT; } if (telnet_message_ok == 1) { fd_printf(fd,0, "Connected to Dynamips VM \"%s\" (ID %u, type %s) - %s\r\n" "Press ENTER to get the prompt.\r\n", vtty->vm->name, vtty->vm->instance_id, vm_get_type(vtty->vm), vtty->name); /* replay old text */ for (i = vtty->replay_ptr; i < VTTY_BUFFER_SIZE; i++) { if (vtty->replay_buffer[i] != 0) { send(fd,&vtty->replay_buffer[i],VTTY_BUFFER_SIZE-i,0); break; } } for (i = 0; i < vtty->replay_ptr; i++) { if (vtty->replay_buffer[i] != 0) { send(fd,&vtty->replay_buffer[i],vtty->replay_ptr-i,0); break; } } /* warn if not running */ if (vtty->vm->status != VM_STATUS_RUNNING) fd_printf(fd,0,"\r\n!!! WARNING - VM is not running, will be unresponsive (status=%d) !!!\r\n",vtty->vm->status); vtty_flush(vtty); } return(0); }
/* Put a buffer to vtty */ void vtty_put_buffer(vtty_t *vtty,char *buf,size_t len) { size_t i; for(i=0;i<len;i++) vtty_put_char(vtty,buf[i]); vtty_flush(vtty); }
/* VTTY thread */ static void *vtty_thread_main(void *arg) { vtty_t *vtty; struct timeval tv; int fd_max,fd_tcp,res; fd_set rfds; int i; for(;;) { VTTY_LIST_LOCK(); /* Build the FD set */ FD_ZERO(&rfds); fd_max = -1; for(vtty=vtty_list;vtty;vtty=vtty->next) { switch(vtty->type) { case VTTY_TYPE_TCP: for(i=0;i<vtty->fd_count;i++) if (vtty->fd_array[i] != -1) { FD_SET(vtty->fd_array[i],&rfds); if (vtty->fd_array[i] > fd_max) fd_max = vtty->fd_array[i]; } fd_tcp = fd_pool_set_fds(&vtty->fd_pool,&rfds); fd_max = m_max(fd_tcp,fd_max); break; default: if (vtty->fd_array[0] != -1) { FD_SET(vtty->fd_array[0],&rfds); fd_max = m_max(vtty->fd_array[0],fd_max); } } } VTTY_LIST_UNLOCK(); /* Wait for incoming data */ tv.tv_sec = 0; tv.tv_usec = 50 * 1000; /* 50 ms */ res = select(fd_max+1,&rfds,NULL,NULL,&tv); if (res == -1) { if (errno != EINTR) { perror("vtty_thread: select"); } continue; } /* Examine active FDs and call user handlers */ VTTY_LIST_LOCK(); for(vtty=vtty_list;vtty;vtty=vtty->next) { switch(vtty->type) { case VTTY_TYPE_TCP: /* check incoming connection */ for(i=0;i<vtty->fd_count;i++) { if (vtty->fd_array[i] == -1) continue; if (!FD_ISSET(vtty->fd_array[i],&rfds)) continue; vtty_tcp_conn_accept(vtty, i); } /* check established connection */ fd_pool_check_input(&vtty->fd_pool,&rfds,vtty_tcp_input,vtty); break; /* Term, Serial */ default: if (vtty->fd_array[0] != -1 && FD_ISSET(vtty->fd_array[0],&rfds)) { vtty_read_and_store(vtty,&vtty->fd_array[0]); vtty->input_pending = TRUE; } } if (vtty->input_pending) { if (vtty->read_notifier != NULL) vtty->read_notifier(vtty); vtty->input_pending = FALSE; } /* Flush any pending output */ if (!vtty->managed_flush) vtty_flush(vtty); } VTTY_LIST_UNLOCK(); } return NULL; }
/* * dev_ns16552_access() */ void *dev_ns16552_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct ns16552_data *d = dev->priv_data; int channel = 0; u_char odata; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"NS16552","read from 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"NS16552","write to 0x%x, value=0x%llx, pc=0x%llx\n", offset,*data,cpu_get_pc(cpu)); } #endif offset >>= d->reg_div; if (offset >= 0x08) channel = 1; // From the NS16552V datasheet, the following is known about the registers // Bit 4 is channel // Value 0 Receive or transmit buffer // Value 1 Interrupt enable // Value 2 Interrupt identification (READ), FIFO Config (Write) // Value 3 Line Control (Appears in IOS) // 0x1 - Word Length Selector bit 0 // 0x2 - Word Length Selector bit 1 // 0x4 - Num stop bits // 0x8 - Parity Enable // 0x16 - Parity even // 0x32 - Stick Parity // 0x64 - Set Break // 0x128 - Division Latch // Value 4 Modem Control (Appears in IOS) // Value 5 Line status // Value 6 Modem Status // Value 7 Scratch switch(offset) { /* Receiver Buffer Reg. (RBR) / Transmitting Holding Reg. (THR) */ case 0x00: case 0x08: if (d->div_latch == 0) { if (op_type == MTS_WRITE) { vtty_put_char(d->channel[channel].vtty,(char)*data); if (d->channel[channel].ier & IER_ETXRDY) vm_set_irq(d->vm,d->irq); d->channel[channel].output = TRUE; } else *data = vtty_get_char(d->channel[channel].vtty); } else { if (op_type == MTS_WRITE) d->baud_divisor = ((*data) & 0x00ff) | (d->baud_divisor & 0xff00); } break; /* Interrupt Enable Register (IER) */ case 0x01: case 0x09: if (d->div_latch == 0) { if (op_type == MTS_READ) { *data = d->channel[channel].ier; } else { d->channel[channel].ier = *data & 0xFF; if ((*data & 0x02) == 0) { /* transmit holding register */ d->channel[channel].vtty->managed_flush = TRUE; vtty_flush(d->channel[channel].vtty); } } } else { if (op_type == MTS_WRITE) d->baud_divisor = (((*data) & 0xff)<<8)|(d->baud_divisor & 0xff); } break; /* Interrupt Ident Register (IIR) */ case 0x02: case 0x0A: if (d->div_latch == 0) { vm_clear_irq(d->vm,d->irq); if (op_type == MTS_READ) { odata = IIR_NPENDING; if (vtty_is_char_avail(d->channel[channel].vtty)) { odata = IIR_RXRDY; } else { if (d->channel[channel].output) { odata = IIR_TXRDY; d->channel[channel].output = 0; } } *data = odata; } } break; case 0x03: case 0x0B: if (op_type == MTS_READ) { *data = d->line_control_reg; } else { d->line_control_reg = (uint)*data; uint bits = 5; __maybe_unused char *stop = "1"; __maybe_unused char *parity = "no "; __maybe_unused char *parityeven = "odd"; if (*data & LCR_WRL0) bits+=1; if (*data & LCR_WRL1) bits+=2; if (*data & LCR_NUMSTOP) { if ( bits >= 6) { stop = "2"; } else { stop = "1.5"; } } if (*data & LCR_PARITYON) parity=""; //Parity on if (*data & LCR_PARITYEV) parityeven="even"; // DIV LATCH changes the behavior of 0x0,0x1,and 0x2 if (*data & LCR_DIVLATCH) { d->div_latch = 1; } else { __maybe_unused uint baud; d->div_latch = 0; // 1200 divisor was 192 // 9600 divisor was 24 // 19200 divisor was 12 // Suggests a crystal of 3686400 hz if (d->baud_divisor > 0) { baud = 3686400 / (d->baud_divisor * 16); } else { baud = 0; } } } break; case 0x04: case 0x0C: if (op_type != MTS_READ) { __maybe_unused char *f1 = ""; __maybe_unused char *f2 = ""; __maybe_unused char *f3 = ""; __maybe_unused char *f4 = ""; __maybe_unused char *f5 = ""; if (*data & MCR_DTR) f1 = "DTR "; if (*data & MCR_RTS) f2 = "RTS "; if (*data & MCR_OUT1) f3 = "OUT1 "; if (*data & MCR_OUT2) f4 = "OUT2 "; if (*data & MCR_LOOP) f5 = "LOOP "; } break; /* Line Status Register (LSR) */ case 0x05: case 0x0D: if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(d->channel[channel].vtty)) odata |= LSR_RXRDY; odata |= LSR_TXRDY|LSR_TXEMPTY; *data = odata; } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"NS16552","read from addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu, "NS16552","write to addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; }