/* Hey EMACS -*- linux-c -*- */ /* libcalccables - link cable library, a part of the CalcForge project * Copyright (C) 1999-2005 Romain Lievin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* "VTi" virtual link cable unit */ #ifdef HAVE_CONFIG_H #include #endif /* This part use Shared Memory Segment for implementing two circular buffers between 2 program which use this lib. Each segment is attached 2 times. Convention used: 0 is an emulator and 1 is a linking program. One shm is used for transferring information from 0 to 1 and the other shm is used for transferring from 1 to 0. shm0: W -> R shm1: R <- W */ #include #include #include #include #include #include "../calccables.h" #include "../logging.h" #include "../error.h" #include "../gettext.h" #include "detect.h" #define BUF_SIZE 1*1024 /* Circular buffer (0: Emu-TIGCC, 1: CalcForgeLP) */ typedef struct { uint8_t buf[BUF_SIZE]; int start; int end; } LinkBuffer; /* Shm variables */ static key_t ipc_key[2]; // IPC key static int shmid[2]; // Shm ID static LinkBuffer *shm[2]; // Shm address static LinkBuffer* send_buf[2]; // Swapped buffer static LinkBuffer* recv_buf[2]; static int vti_prepare(CableHandle *h) { // in fact, address & device are unused switch(h->port) { case PORT_0: // automatic setting h->address = 0; //ref_cnt; break; case PORT_1: // forced setting, for compatibility case PORT_3: h->address = 0; h->device = strdup("0->1"); break; case PORT_2: case PORT_4: h->address = 1; h->device = strdup("1->0"); break; default: return ERR_ILLEGAL_ARG; } return 0; } static int vti_open(CableHandle *h) { int i; /* Get a unique (if possible) key */ for (i = 0; i < 2; i++) { if ((ipc_key[i] = ftok("/tmp", i)) == -1) { calccables_warning("unable to get unique key (ftok).\n"); return ERR_VTI_IPCKEY; } } /* Open a shared memory segment */ for (i = 0; i < 2; i++) { if ((shmid[i] = shmget(ipc_key[i], sizeof(LinkBuffer), IPC_CREAT | 0666)) == -1) { calccables_warning("unable to open shared memory (shmget).\n"); return ERR_VTI_SHMGET; } } /* Attach the shm */ for (i = 0; i < 2; i++) { if ((shm[i] = shmat(shmid[i], NULL, 0)) == NULL) { calccables_warning("unable to attach shared memory (shmat).\n"); return ERR_VTI_SHMAT; } } /* Swap shm */ send_buf[0] = shm[0]; // 0 -> 1: writing recv_buf[0] = shm[1]; // 0 <- 1: reading send_buf[1] = shm[1]; // 1 -> 0: writing recv_buf[1] = shm[0]; // 1 <- 0: reading for (i = 0; i < 2; i++) shm[i]->start = shm[i]->end = 0; return 0; } static int vti_close(CableHandle *h) { int i; /* Detach segment */ for (i = 0; i < 2; i++) { if (shmdt(shm[i]) == -1) { calccables_warning("shmdt\n"); return ERR_VTI_SHMDT; } /* and destroy it */ if (shmctl(shmid[i], IPC_RMID, NULL) == -1) { calccables_warning("shmctl\n"); return ERR_VTI_SHMCTL; } } return 0; } static int vti_reset(CableHandle *h) { int i; for (i = 0; i < 2; i++) shm[i]->start = shm[i]->end = 0; return 0; } static int vti_put(CableHandle *h, uint8_t *data, uint32_t len) { int p = h->address; int i; tiTIME clk; for(i = 0; i < len; i++) { TO_START(clk); do { if (TO_ELAPSED(clk, h->timeout)) return ERR_WRITE_TIMEOUT; } while (((send_buf[p]->end + 1) & 255) == send_buf[p]->start); send_buf[p]->buf[send_buf[p]->end] = data[i]; send_buf[p]->end = (send_buf[p]->end + 1) & 255; } return 0; } static int vti_get(CableHandle *h, uint8_t *data, uint32_t len) { int p = h->address; int i; tiTIME clk; for(i = 0; i < len; i++) { TO_START(clk); do { if (TO_ELAPSED(clk, h->timeout)) return ERR_READ_TIMEOUT; } while (recv_buf[p]->start == recv_buf[p]->end); data[i] = recv_buf[p]->buf[recv_buf[p]->start]; recv_buf[p]->start = (recv_buf[p]->start + 1) & 255; } return 0; } static int vti_probe(CableHandle *h) { return 0; } static int vti_check(CableHandle *h, int *status) { int p = h->address; *status = !(recv_buf[p]->start == recv_buf[p]->end); return 0; } static int vti_set_red_wire(CableHandle *h, int b) { return 0; } static int vti_set_white_wire(CableHandle *h, int b) { return 0; } static int vti_get_red_wire(CableHandle *h) { return 1; } static int vti_get_white_wire(CableHandle *h) { return 1; } const CableFncts cable_vti = { CABLE_VTI, "VTI", N_("Virtual TI"), N_("Virtual link for VTi"), 0, &vti_prepare, &vti_open, &vti_close, &vti_reset, &vti_probe, NULL, &vti_put, &vti_get, &vti_check, &vti_set_red_wire, &vti_set_white_wire, &vti_get_red_wire, &vti_get_white_wire, };