From ae02f720cf0e400d302a2ae064cdb2b20a492812 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Fri, 26 Jan 2018 21:53:37 +0100 Subject: [PATCH] Initial commit of xHDServer source code. Depends on https://sigrok.org/wiki/Libserialport --- server/main.cpp | 363 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 server/main.cpp diff --git a/server/main.cpp b/server/main.cpp new file mode 100644 index 0000000..8b3ce72 --- /dev/null +++ b/server/main.cpp @@ -0,0 +1,363 @@ +// +// main.cpp +// xHDServer +// +// Created by John Brooks on 7/1/16. +// Copyright (c) 2016 JB. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT_STR "usbserial" + +#define VERBOSE (0) +#define LOG (1) + +#if 0 +//TODO: Switch xHD client to Z8530 built-in CRC calc +/* + * http://wiki.synchro.net/ref:xmodem + * + * This function calculates the CRC used by the XMODEM/CRC Protocol + * The first argument is a pointer to the message block. + * The second argument is the number of bytes in the message block. + * The function returns an integer which contains the CRC. + * The low order 16 bits are the coefficients of the CRC. + */ +static unsigned +crc_xmodem(char *ptr, unsigned count) +{ + int crc, i; + + crc = 0; + while (count-- > 0) { + crc = crc ^ (unsigned)*ptr++ << 8; + for (i = 0; i < 8; ++i) + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + return (crc & 0xFFFF); +} +#endif + +int main(void) +{ +// TODO: Parse cmd line for switches & disk image filenames + + unsigned char auReadBuf[256] = {0}; + enum sp_return eResult; + + // Map virtual drives + const char *apFilename[2] = {"/Applications/GSport/HD/P1NEW.PO", "/Applications/GSport/HD/P2.PO"}; + //const char *apFilename[2] = {"/Applications/GSport/HD/P1NEW.PO", "/Applications/GSport/Disk5.25/ProDOS_2_5a1.PO"}; + + char * apFileData[2]; + for (int iDrive=0; iDrive<2; iDrive++) + { + int fd = open(apFilename[iDrive], O_RDWR, S_IWRITE | S_IREAD); + struct stat stats; + fstat(fd, &stats); + unsigned int uFilesize = (unsigned int)stats.st_size; + apFileData[iDrive] = (char*) mmap(0, uFilesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + } + + // Create a configuration for the serial ports + struct sp_port_config *pSerialConfig=0; + { + eResult = sp_new_config(&pSerialConfig); + assert(eResult == SP_OK); + eResult = sp_set_config_baudrate(pSerialConfig, 230400); + assert(eResult == SP_OK); + eResult = sp_set_config_bits(pSerialConfig, 8); + assert(eResult == SP_OK); + eResult = sp_set_config_parity(pSerialConfig, SP_PARITY_NONE ); + assert(eResult == SP_OK); + eResult = sp_set_config_stopbits(pSerialConfig, 1); + assert(eResult == SP_OK); + eResult = sp_set_config_flowcontrol(pSerialConfig, SP_FLOWCONTROL_RTSCTS ); + assert(eResult == SP_OK); + } + + // Find and configure valid serial ports + const int MAX_PORTS = 2; + int iValidPortCount = 0; + struct sp_port * apValidPorts[MAX_PORTS]; + { + struct sp_port **ports; + + eResult = sp_list_ports(&ports); + assert(eResult == SP_OK); + + for (int i = 0; ports[i]; i++) + { + struct sp_port *pPort = ports[i]; + + if (strstr(sp_get_port_name(pPort), PORT_STR) ) + { + if (VERBOSE) + { + printf("Valid port %d: %s\n", iValidPortCount, sp_get_port_name(pPort) ); + printf("\tDescr: %s\n", sp_get_port_description(pPort)); + int iUsbBus = -1; + int iUsbAddress = -1; + eResult = sp_get_port_usb_bus_address(pPort, &iUsbBus, &iUsbAddress); + assert(eResult == SP_OK); + printf("\tUSB: Bus=%d, Address=%d\n", iUsbBus, iUsbAddress); + int iUsbVid = -1; + int iUsbPid = -1; + eResult = sp_get_port_usb_vid_pid(pPort, &iUsbVid, &iUsbPid); + assert(eResult == SP_OK); + printf("\tUSB: VendorID=$%04X, ProductID=$%04X\n", iUsbVid, iUsbPid); + printf("\tManufacturer: %s\n", sp_get_port_usb_manufacturer(pPort)); + printf("\tProduct: %s\n", sp_get_port_usb_product(pPort)); + printf("\tSerial #: %s\n", sp_get_port_usb_serial(pPort)); + + } + + // Retain the port and configure it's settings + if (iValidPortCount < MAX_PORTS) + { + // Copy the valid port since the port list will be freed + eResult = sp_copy_port(pPort, &apValidPorts[iValidPortCount++]); + assert(eResult == SP_OK); + + // Use the copied port + pPort = apValidPorts[iValidPortCount-1]; + + // See if the port can be opened and configured + enum sp_return eResultOpen; + enum sp_return eResultConfig; + eResultOpen = sp_open(pPort, SP_MODE_READ_WRITE); + eResultConfig = sp_set_config(pPort, pSerialConfig); + if (eResultOpen != SP_OK || eResultConfig != SP_OK) + { + // Port is not valid + iValidPortCount--; + sp_close(pPort); + sp_free_port(pPort); + if (VERBOSE) + { + printf("\n\t--- Could not open this port ---\n\n"); + } + } + } + } + else // ! usbserial + { + if (VERBOSE) + printf("\tskip\t'%s'.\n", sp_get_port_name(pPort)); + } + } + sp_free_port_list(ports); + } + +// TODO: Add server shutdown + while (1) + { + for (int iIndex = 0; iIndex < iValidPortCount; iIndex++) + { + struct sp_port *pPort = apValidPorts[iIndex]; + + int iBytesToRead = sp_input_waiting(pPort); + int iWriteLen = 0; +#if 0 + if (iBytesToRead == 0) + { + usleep(100); + } + if (iBytesToRead > 0) +#endif + { + int iBytesRead; +#if 1 + auReadBuf[0] = 0; + iBytesRead = sp_blocking_read(pPort, auReadBuf+1, 1, 1/*ms timeout*/); + + if (iBytesRead > 0) + { + { + if (VERBOSE) + { + printf("P%d) Cmd\n", iIndex); + } + iBytesRead = 2 + sp_blocking_read(pPort, auReadBuf+2, 3, 1/*ms timeout*/); + if (iBytesRead != 5) continue; +#endif +#if 0 + //iBytesRead = sp_blocking_read(pPort, auReadBuf, 1, 10/*ms timeout*/); + iBytesRead = sp_blocking_read(pPort, auReadBuf, 1, 0/*ms timeout*/); + assert(iBytesRead == 1); if (iBytesRead != 1) continue; + + switch(auReadBuf[0]) + { + case 0xC5: // E + { + if (VERBOSE) + { + printf("P%d) Cmd E\n", iIndex); + } + iBytesRead = sp_blocking_read(pPort, auReadBuf+1, 4, 10/*ms timeout*/); + assert(iBytesRead == 4); if (iBytesRead != 4) continue; + iBytesRead++; +#endif + if (VERBOSE) + { + for (int i = 1; i < iBytesRead; i++) + { + printf("\t%02X '%c'\n", auReadBuf[i], auReadBuf[i] & 0x7f); + } + } + int iChksum = auReadBuf[0] ^ auReadBuf[1] ^ auReadBuf[2] ^ auReadBuf[3]; + if (iChksum != auReadBuf[4]) + { + printf ("--- Chksum failed --- read=%02X,%02X,%02X,%02X,%02X, calc=%02X\n", + auReadBuf[0],auReadBuf[1],auReadBuf[2],auReadBuf[3],auReadBuf[4], + iChksum); + continue; + } + else + { + if (VERBOSE) + { + printf("\tChksum ok: %02X\n", auReadBuf[4]); + } + + int iBlock = auReadBuf[2] | (auReadBuf[3] << 8); + char *pDriveData = apFileData[(auReadBuf[1] >> 2) & 1]; + pDriveData += iBlock * 512; + + // Read block + if (auReadBuf[1] & 1) + { +#if 1 + iChksum = 0; + for (int i = 1; i < 4; i++) + iChksum ^= auReadBuf[i]; + auReadBuf[4] = iChksum; + + iWriteLen = sp_blocking_write(pPort, auReadBuf+1, 4, 10/*ms timeout*/); + assert(iWriteLen == 4); if (iWriteLen != 4) continue; +#endif +#if 0 + iChksum = 0; + for (int i = 0; i < 4; i++) + iChksum ^= auReadBuf[i]; + auReadBuf[4] = iChksum; + + iWriteLen = sp_blocking_write(pPort, auReadBuf, 5, 10/*ms timeout*/); + assert(iWriteLen == 5); if (iWriteLen != 5) continue; +#endif +#if 0 + time_t t = time(0); // get time now + struct tm * pDateTime = localtime( & t ); + int iDate = ((pDateTime->tm_year-100) << 9) + | (pDateTime->tm_mon << 5) + | pDateTime->tm_mday; + + auReadBuf[4] = pDateTime->tm_min; // TimeLo=Minute + auReadBuf[5] = pDateTime->tm_hour; // TimeHi=Hour + auReadBuf[6] = iDate & 0xff; // DateLo + auReadBuf[7] = (iDate >> 8); // DateHi + + iChksum = 0; + for (int i = 0; i < 8; i++) + iChksum ^= auReadBuf[i]; + auReadBuf[8] = iChksum; + + iWriteLen = sp_blocking_write(pPort, auReadBuf, 9, 10/*ms timeout*/); + assert(iWriteLen == 9); if (iWriteLen != 9) continue; +#endif + + static unsigned char auSendBuf[513]; + iWriteLen = sp_blocking_write(pPort, pDriveData, 512, 50/*ms timeout*/); + if (iWriteLen < 512) + printf("WriteLen=%d\n",iWriteLen); + assert(iWriteLen == 512); if (iWriteLen != 512) continue; + + if (iWriteLen == 512) + { + iChksum = 0; + for (int i = 0; i < 512; i++) + iChksum ^= pDriveData[i]; + iWriteLen = sp_blocking_write(pPort, &iChksum, 1, 10/*ms timeout*/); + assert(iWriteLen == 1); if (iWriteLen != 1) continue; + + if (LOG) + { + printf("%d\tR %5d\r", (auReadBuf[1] >> 2) & 1, iBlock); + fflush(stdout); + } + } + } + else // Write block + { + static unsigned char auBlockBuf[513]; + int iReadLen = sp_blocking_read(pPort, auBlockBuf, 512+1, 30/*ms timeout*/); + assert(iReadLen == 512+1); + + iChksum = 0; + for (int i = 0; i < 512; i++) + iChksum ^= auBlockBuf[i]; + auReadBuf[4] = iChksum; + +#if 1 + iWriteLen = sp_blocking_write(pPort, auReadBuf+1, 4, 1/*ms timeout*/); + printf ("Wrt Hdr=%02X,%02X,%02X,%02X,%02X, calc=%02X, rcv=%02x\n", + auReadBuf[0],auReadBuf[1],auReadBuf[2],auReadBuf[3],auReadBuf[4], + iChksum, auBlockBuf[512]); + //assert(iWriteLen == 4); +#endif +#if 0 + iWriteLen = sp_blocking_write(pPort, auReadBuf, 5, 10/*ms timeout*/); + assert(iWriteLen == 5); +#endif + + // Block data checksum matches, write it to disk + if (iChksum == auBlockBuf[512]) + { + memcpy(pDriveData, auBlockBuf, 512); + } + + if (LOG) + { + printf("%d\t\t\t\tW %5d\r", (auReadBuf[1] >> 2) & 1, iBlock); + fflush(stdout); + } + } + } + break; + } +#if 0 + default: + { + printf("P%d) Bad cmd: %02X\n", iIndex, auReadBuf[0]); + sp_flush(pPort, SP_BUF_INPUT); + break; + } +#endif + + } + } + + } + } + + // Close and free valid ports + sp_free_config (pSerialConfig); + for (int i = 0; i < iValidPortCount; i++) + { + struct sp_port *pPort = apValidPorts[i]; + sp_close(pPort); + sp_free_port(pPort); + } +}