/* uch11.c --- chaosnet interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chaosd.h" #include "hosttab.h" #include "idle.h" #include "misc.h" #include "uch11.h" #include "uch11-backend.h" #include "ucfg.h" #include "ucode.h" #include "usim.h" #include "utrace.h" int uch11_backend = UCH11_BACKEND_LOCAL; int uch11_myaddr = 0177040; /* LOCAL-CADR */ int uch11_serveraddr = 0177001; /* LOCAL-BRIDGE */ int uch11_csr; int uch11_bit_count; int uch11_lost_count; unsigned short uch11_xmit_buffer[4096]; int uch11_xmit_buffer_size; int uch11_xmit_buffer_ptr; unsigned short uch11_rcv_buffer[4096]; unsigned short uch11_rcv_buffer_toss[4096]; int uch11_rcv_buffer_ptr; int uch11_rcv_buffer_size; bool uch11_rcv_buffer_empty; int reconnect_delay; bool reconnect_chaos; int (*uch11_send)(char *, int); void uch11_force_reconect(void); extern void uch11_reconnect(void); extern void settreeroot(const char *, const char *prefix); pthread_mutex_t recvqueue; pthread_mutex_t recvqueue; struct queue_head queuehead = TAILQ_HEAD_INITIALIZER(queuehead); // udp int chudpopen(void); int chaos_send_to_udp(char *buffer, int size); int chaos_poll_udp(void); // deamon int chaosd_fd; int chaos_send_to_chaosd(char *buffer, int size); int chaos_poll_chaosd(void); // local int chaos_send_to_local(char *buffer, int size); int chaos_poll_local(void); /* * RFC1071: Compute Internet Checksum for COUNT bytes beginning at * location ADDR. */ unsigned short uch11_checksum(const unsigned char *addr, int count) { long sum; sum = 0; while (count > 1) { sum += *(addr) << 8 | *(addr + 1); addr += 2; count -= 2; } /* * Add left-over byte, if any. */ if (count > 0) sum += *(unsigned char *) addr; /* * Fold 32-bit sum to 16 bits. */ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return (~sum) & 0xffff; } /* * Called when we are we have something in uch11_rcv_buffer. */ void uch11_rx_pkt(void) { idle_chaos_activity(); uch11_rcv_buffer_ptr = 0; uch11_bit_count = (uch11_rcv_buffer_size * 2 * 8) - 1; INFO(TRACE_CHAOS, "chaos: receiving %d words\n", uch11_rcv_buffer_size); if (uch11_rcv_buffer_size > 0) { DEBUG(TRACE_CHAOS, "chaos: set csr receive done, generate interrupt\n"); uch11_csr |= CHAOS_CSR_RECEIVE_DONE; if (uch11_csr & CHAOS_CSR_RECEIVE_ENABLE) assert_unibus_interrupt(0270); } else { DEBUG(TRACE_CHAOS, "chaos: recieve buffer empty\n"); } } void uch11_xmit_done_intr(void) { uch11_csr |= CHAOS_CSR_TRANSMIT_DONE; if (uch11_csr & CHAOS_CSR_TRANSMIT_ENABLE) assert_unibus_interrupt(0270); } void uch11_xmit_pkt(void) { idle_chaos_activity(); INFO(TRACE_CHAOS, "chaos: transmitting %d bytes, data len %d\n", uch11_xmit_buffer_ptr * 2, (uch11_xmit_buffer_ptr > 0 ? uch11_xmit_buffer[1] & 0x3f : -1)); uch11_xmit_buffer_size = uch11_xmit_buffer_ptr; /* * Dest. is already in the buffer. */ uch11_xmit_buffer[uch11_xmit_buffer_size++] = (unsigned short) uch11_myaddr; /* Source. */ uch11_xmit_buffer[uch11_xmit_buffer_size] = uch11_checksum((unsigned char *) uch11_xmit_buffer, uch11_xmit_buffer_size * 2); /* Checksum. */ uch11_xmit_buffer_size++; (*uch11_send) ((char *) uch11_xmit_buffer, uch11_xmit_buffer_size * 2); uch11_xmit_buffer_ptr = 0; uch11_xmit_done_intr(); } int uch11_get_bit_count(void) { if (uch11_rcv_buffer_size > 0) return uch11_bit_count; DEBUG(TRACE_CHAOS, "chaos: returned empty bit count\n"); return 07777; } int uch11_get_rcv_buffer(void) { if (uch11_rcv_buffer_ptr < uch11_rcv_buffer_size) { int ret; ret = uch11_rcv_buffer[uch11_rcv_buffer_ptr++]; if (uch11_rcv_buffer_ptr == uch11_rcv_buffer_size) { DEBUG(TRACE_CHAOS, "chaos: marked recieve buffer as empty\n"); uch11_rcv_buffer_empty = true; } return ret; } /* * Read last word, clear receive done. */ DEBUG(TRACE_CHAOS, "chaos: cleared csr receive done\n"); uch11_csr &= ~CHAOS_CSR_RECEIVE_DONE; uch11_rcv_buffer_size = 0; return 0; } void uch11_put_xmit_buffer(int v) { if (uch11_xmit_buffer_ptr < (int) sizeof(uch11_xmit_buffer) / 2) uch11_xmit_buffer[uch11_xmit_buffer_ptr++] = (unsigned short) v; uch11_csr &= ~CHAOS_CSR_TRANSMIT_DONE; } int uch11_get_csr(void) { static int old_csr = 0; if (uch11_csr != old_csr) { DEBUG(TRACE_CHAOS, "chaos: read csr %o\n", uch11_csr); old_csr = uch11_csr; } return uch11_csr | ((uch11_lost_count << 9) & 017); } void uch11_set_csr(int v) { int mask; int old_csr; old_csr = uch11_csr; v &= 0xffff; /* * Writing these don't stick. */ /* *INDENT-OFF* */ mask = CHAOS_CSR_TRANSMIT_DONE | CHAOS_CSR_LOST_COUNT | CHAOS_CSR_CRC_ERROR | CHAOS_CSR_RECEIVE_DONE | CHAOS_CSR_RECEIVER_CLEAR; /* *INDENT-ON* */ uch11_csr = (uch11_csr & mask) | (v & ~mask); if (uch11_csr & CHAOS_CSR_RESET) { uch11_rcv_buffer_ptr = 0; uch11_rcv_buffer_size = 0; uch11_xmit_buffer_ptr = 0; uch11_lost_count = 0; uch11_bit_count = 0; uch11_csr &= ~(CHAOS_CSR_RESET | CHAOS_CSR_RECEIVE_DONE); uch11_csr |= CHAOS_CSR_TRANSMIT_DONE; reconnect_delay = 200; /* Do it right away. */ uch11_force_reconect(); } if (v & CHAOS_CSR_RECEIVER_CLEAR) { uch11_rcv_buffer_ptr = 0; uch11_rcv_buffer_size = 0; uch11_lost_count = 0; uch11_bit_count = 0; uch11_csr &= ~CHAOS_CSR_RECEIVE_DONE; } if (v & (CHAOS_CSR_TRANSMITTER_CLEAR | CHAOS_CSR_TRANSMIT_DONE)) { uch11_xmit_buffer_ptr = 0; uch11_csr &= ~CHAOS_CSR_TRANSMIT_ABORT; uch11_csr |= CHAOS_CSR_TRANSMIT_DONE; } if (uch11_csr & CHAOS_CSR_RECEIVE_ENABLE) { if ((old_csr & CHAOS_CSR_RECEIVE_ENABLE) == 0) { DEBUG(TRACE_CHAOS, "chaos: CSR receive enable\n"); } if (uch11_rcv_buffer_empty) { uch11_rcv_buffer_ptr = 0; uch11_rcv_buffer_size = 0; } /* * If buffer is full, generate status and interrupt again. */ if (uch11_rcv_buffer_size > 0) { DEBUG(TRACE_CHAOS, "chaos: rx-enabled and buffer is full\n"); uch11_rx_pkt(); } } else if (old_csr & CHAOS_CSR_RECEIVE_ENABLE) { DEBUG(TRACE_CHAOS, "chaos: CSR receive DISable\n"); } if (uch11_csr & CHAOS_CSR_TRANSMIT_ENABLE) { if ((old_csr & CHAOS_CSR_TRANSMIT_ENABLE) == 0) { DEBUG(TRACE_CHAOS, "chaos: CSR transmit enable\n"); } uch11_csr |= CHAOS_CSR_TRANSMIT_DONE; } else if (old_csr & CHAOS_CSR_TRANSMIT_ENABLE) { DEBUG(TRACE_CHAOS, "chaos: CSR transmit DISable\n"); } DEBUG(TRACE_CHAOS, "chaos: set csr bits 0%o, old 0%o, new 0%o\n", v, old_csr, uch11_csr); } void uch11_poll(void) { if (uch11_backend == UCH11_BACKEND_LOCAL) chaos_poll_local(); else if (uch11_backend == UCH11_BACKEND_DAEMON) chaos_poll_chaosd(); else if (uch11_backend == UCH11_BACKEND_UDP) chaos_poll_udp(); } void uch11_force_reconect(void) { if (uch11_backend == UCH11_BACKEND_DAEMON) { WARNING(TRACE_CHAOS, "chaos: forcing reconnect to chaosd\n"); close(chaosd_fd); chaosd_fd = -1; reconnect_chaos = true; } } void uch11_reconnect(void) { static int reconnect_time; if (++reconnect_delay < 200) return; reconnect_delay = 0; if (reconnect_time && time(NULL) < (reconnect_time + 5)) /* Try every 5 seconds. */ return; reconnect_time = time(NULL); NOTICE(TRACE_CHAOS, "chaos: reconnecting to chaosd\n"); if (uch11_init() == 0) { INFO(TRACE_CHAOS, "chaos: chaosd reconnected\n"); reconnect_chaos = false; reconnect_delay = 0; } } int uch11_init(void) { char hosts_file[PATH_MAX] = {0}; char root_directory[PATH_MAX] = {0}; if (realpath(ucfg.chaos_hosts, hosts_file) == NULL) { strcpy(hosts_file, "hosts.text"); WARNING(TRACE_USIM, "chaos: no host table; using defaults\n"); } NOTICE(TRACE_USIM, "chaos: using hosts table from \"%s\"\n", hosts_file); readhosts(ucfg.chaos_myname, hosts_file); uch11_myaddr = chaos_addr(ucfg.chaos_myname, 0); uch11_serveraddr = chaos_addr(ucfg.chaos_servername, 0); NOTICE(TRACE_USIM, "chaos: I am %s (0%o)\n", ucfg.chaos_myname, uch11_myaddr); if (uch11_backend == UCH11_BACKEND_LOCAL) { NOTICE(TRACE_USIM, "chaos: backend is \"local\", connecting to %s (0%o)\n", ucfg.chaos_servername, uch11_serveraddr); TAILQ_INIT(&queuehead); uch11_send = &chaos_send_to_local; pthread_mutex_init(&recvqueue, NULL); } else if (uch11_backend == UCH11_BACKEND_DAEMON) { NOTICE(TRACE_USIM, "chaos: backend is \"chaosd\"\n"); uch11_send = &chaos_send_to_chaosd; chaosd_fd = chdopen(); if (chaosd_fd == -1) { close(chaosd_fd); return -1; } } else if (uch11_backend == UCH11_BACKEND_UDP) { if (hybrid_udp_and_local) NOTICE(TRACE_USIM, "chaos: backend is \"udp\" with server %s (%#o)\n", ucfg.chaos_servername, uch11_serveraddr); else NOTICE(TRACE_USIM, "chaos: backend is \"udp\"\n"); uch11_send = &chaos_send_to_udp; chaosd_fd = chudpopen(); if (chaosd_fd == -1) { close(chaosd_fd); return -1; } } uch11_rcv_buffer_empty = true; char *whichconf, *whichdir; if (ucfg.usim_sys_directory != NULL) { // Backwards compat whichconf = "sys_directory"; whichdir = "/tree"; if (realpath(ucfg.usim_sys_directory, root_directory) != NULL) settreeroot(root_directory, whichdir); } else { whichconf = "fs_root_directory"; whichdir = "/"; if (realpath(ucfg.usim_fs_root_directory, root_directory) != NULL) settreeroot(root_directory, NULL); } if (root_directory[0] == 0) err(1, "could not resolve %s", whichconf); NOTICE(TRACE_USIM, "chaos: mapping %s to %s\n", whichdir, root_directory); return 0; } //#include "uch11-chaosd.c" //#include "uch11-local.c" //#include "uch11-udp.c"