// File: DoubleString[P]RtConverter/src/Rtklib/stream.cpp
// Function: bamboo::openserial(char const*, int, char*)
static serial_t *openserial(const char *path, int mode, char *msg)
	{
		const int br[] = {
			300,600,1200,2400,4800,9600,19200,38400,57600,115200,230400
		};
		serial_t *serial;
		int i, brate = 9600, bsize = 8, stopb = 1, tcp_port = 0;
		char parity = 'N', dev[128], port[128], fctr[64] = "", path_tcp[32], msg_tcp[128];
		const char* p;
#ifdef WIN32
		DWORD error, rw = 0, siz = sizeof(COMMCONFIG);
		COMMCONFIG cc = { 0 };
		COMMTIMEOUTS co = { MAXDWORD,0,0,0,0 }; /* non-block-read */
		char dcb[64] = "";
#else
		const speed_t bs[] = {
			B300,B600,B1200,B2400,B4800,B9600,B19200,B38400,B57600,B115200,B230400
		};
		struct termios ios = { 0 };
		int rw = 0;
#endif
		tracet(3, "openserial: path=%s mode=%d\n", path, mode);

		if (!(serial = (serial_t *)calloc(1, sizeof(serial_t)))) return NULL;

		if ((p = strchr(path, ':'))) {
			strncpy(port, path, p - path); port[p - path] = '\0';
			sscanf(p, ":%d:%d:%c:%d:%s", &brate, &bsize, &parity, &stopb, fctr);
		}
		else strcpy(port, path);

		if ((p = strchr(path, '#'))) {
			sscanf(p, "#%d", &tcp_port);
		}
		for (i = 0; i < 11; i++) if (br[i] == brate) break;
		if (i >= 12) {
			sprintf(msg, "bitrate error (%d)", brate);
			tracet(1, "openserial: %s path=%s\n", msg, path);
			free(serial);
			return NULL;
		}
		parity = (char)toupper((int)parity);

#ifdef WIN32
		sprintf(dev, "\\\\.\\%s", port);
		if (mode&STR_MODE_R) rw |= GENERIC_READ;
		if (mode&STR_MODE_W) rw |= GENERIC_WRITE;

		serial->dev = CreateFile(dev, rw, 0, 0, OPEN_EXISTING, 0, NULL);
		if (serial->dev == INVALID_HANDLE_VALUE) {
			sprintf(msg, "%s open error (%d)", port, (int)GetLastError());
			tracet(1, "openserial: %s path=%s\n", msg, path);
			free(serial);
			return NULL;
		}
		if (!GetCommConfig(serial->dev, &cc, &siz)) {
			sprintf(msg, "%s getconfig error (%d)", port, (int)GetLastError());
			tracet(1, "openserial: %s\n", msg);
			CloseHandle(serial->dev);
			free(serial);
			return NULL;
		}
		sprintf(dcb, "baud=%d parity=%c data=%d stop=%d", brate, parity, bsize, stopb);
		if (!BuildCommDCB(dcb, &cc.dcb)) {
			sprintf(msg, "%s buiddcb error (%d)", port, (int)GetLastError());
			tracet(1, "openserial: %s\n", msg);
			CloseHandle(serial->dev);
			free(serial);
			return NULL;
		}
		if (!strcmp(fctr, "rts")) {
			cc.dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
		}
		SetCommConfig(serial->dev, &cc, siz); /* ignore error to support novatel */
		SetCommTimeouts(serial->dev, &co);
		ClearCommError(serial->dev, &error, NULL);
		PurgeComm(serial->dev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

		/* create write thread */
		initlock(&serial->lock);
		serial->state = serial->wp = serial->rp = serial->error = 0;
		serial->buffsize = buffsize;
		if (!(serial->buff = (unsigned char *)malloc(buffsize))) {
			CloseHandle(serial->dev);
			free(serial);
			return NULL;
		}
		serial->state = 1;
		if (!(serial->thread = CreateThread(NULL, 0, serialthread, serial, 0, NULL))) {
			sprintf(msg, "%s serial thread error (%d)", port, (int)GetLastError());
			tracet(1, "openserial: %s\n", msg);
			CloseHandle(serial->dev);
			serial->state = 0;
			free(serial);
			return NULL;
		}
		sprintf(msg, "%s", port);
#else
		sprintf(dev, "/dev/%s", port);

		if ((mode&STR_MODE_R) && (mode&STR_MODE_W)) rw = O_RDWR;
		else if (mode&STR_MODE_R) rw = O_RDONLY;
		else if (mode&STR_MODE_W) rw = O_WRONLY;

		if ((serial->dev = open(dev, rw | O_NOCTTY | O_NONBLOCK)) < 0) {
			sprintf(msg, "%s open error (%d)", dev, errno);
			tracet(1, "openserial: %s dev=%s\n", msg, dev);
			free(serial);
			return NULL;
		}
		tcgetattr(serial->dev, &ios);
		ios.c_iflag = 0;
		ios.c_oflag = 0;
		ios.c_lflag = 0;     /* non-canonical */
		ios.c_cc[VMIN] = 0; /* non-block-mode */
		ios.c_cc[VTIME] = 0;
		cfsetospeed(&ios, bs[i]);
		cfsetispeed(&ios, bs[i]);
		ios.c_cflag |= bsize == 7 ? CS7 : CS8;
		ios.c_cflag |= parity == 'O' ? (PARENB | PARODD) : (parity == 'E' ? PARENB : 0);
		ios.c_cflag |= stopb == 2 ? CSTOPB : 0;
		ios.c_cflag |= !strcmp(fctr, "rts") ? CRTSCTS : 0;
		tcsetattr(serial->dev, TCSANOW, &ios);
		tcflush(serial->dev, TCIOFLUSH);
		sprintf(msg, "%s", dev);
#endif
		serial->tcpsvr = NULL;

		/* open tcp sever to output received stream */
		if (tcp_port > 0) {
			sprintf(path_tcp, ":%d", tcp_port);
			serial->tcpsvr = opentcpsvr(path_tcp, msg_tcp);
		}
		tracet(3, "openserial: dev=%d\n", serial->dev);
		return serial;
	}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x2f0, %rsp            # imm = 0x2F0
movq %rdi, -0x10(%rbp)
movl %esi, -0x14(%rbp)
movq %rdx, -0x20(%rbp)
leaq -0x50(%rbp), %rdi
leaq 0x2b33f(%rip), %rsi     # 0xc8ac0
movl $0x2c, %edx
callq 0x9610
movl $0x2580, -0x60(%rbp)    # imm = 0x2580
movl $0x8, -0x64(%rbp)
movl $0x1, -0x68(%rbp)
movl $0x0, -0x6c(%rbp)
movb $0x4e, -0x6d(%rbp)
leaq -0x1b0(%rbp), %rdi
xorl %esi, %esi
movl $0x40, %edx
callq 0x93d0
leaq -0x290(%rbp), %rdi
leaq 0x2b324(%rip), %rsi     # 0xc8af0
movl $0x2c, %edx
callq 0x9610
leaq -0x2cc(%rbp), %rdi
xorl %esi, %esi
movl $0x3c, %edx
callq 0x93d0
movl $0x0, -0x2d0(%rbp)
movq -0x10(%rbp), %rdx
movl -0x14(%rbp), %ecx
movl $0x3, %edi
leaq 0x2b4dd(%rip), %rsi     # 0xc8ce3
movb $0x0, %al
callq 0x95890
movl $0x1, %edi
movl $0x10, %esi
callq 0x94f0
movq %rax, -0x58(%rbp)
cmpq $0x0, %rax
jne 0x9d833
movq $0x0, -0x8(%rbp)
jmp 0x9dc43
movq -0x10(%rbp), %rdi
movl $0x3a, %esi
callq 0x9280
movq %rax, -0x258(%rbp)
cmpq $0x0, %rax
je 0x9d8b4
leaq -0x170(%rbp), %rdi
movq -0x10(%rbp), %rsi
movq -0x258(%rbp), %rdx
movq -0x10(%rbp), %rax
subq %rax, %rdx
callq 0x93b0
movq -0x258(%rbp), %rax
movq -0x10(%rbp), %rcx
subq %rcx, %rax
movb $0x0, -0x170(%rbp,%rax)
movq -0x258(%rbp), %rdi
leaq -0x1b0(%rbp), %rax
leaq 0x2b469(%rip), %rsi     # 0xc8d00
leaq -0x60(%rbp), %rdx
leaq -0x64(%rbp), %rcx
leaq -0x6d(%rbp), %r8
leaq -0x68(%rbp), %r9
movq %rax, (%rsp)
movb $0x0, %al
callq 0x9750
jmp 0x9d8c4
leaq -0x170(%rbp), %rdi
movq -0x10(%rbp), %rsi
callq 0x96f0
movq -0x10(%rbp), %rdi
movl $0x23, %esi
callq 0x9280
movq %rax, -0x258(%rbp)
cmpq $0x0, %rax
je 0x9d8f8
movq -0x258(%rbp), %rdi
leaq 0x2b423(%rip), %rsi     # 0xc8d10
leaq -0x6c(%rbp), %rdx
movb $0x0, %al
callq 0x9750
movl $0x0, -0x5c(%rbp)
cmpl $0xb, -0x5c(%rbp)
jge 0x9d921
movslq -0x5c(%rbp), %rax
movl -0x50(%rbp,%rax,4), %eax
cmpl -0x60(%rbp), %eax
jne 0x9d914
jmp 0x9d921
jmp 0x9d916
movl -0x5c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x5c(%rbp)
jmp 0x9d8ff
cmpl $0xc, -0x5c(%rbp)
jl 0x9d96d
movq -0x20(%rbp), %rdi
movl -0x60(%rbp), %edx
leaq 0x2b3df(%rip), %rsi     # 0xc8d14
movb $0x0, %al
callq 0x9150
movq -0x20(%rbp), %rdx
movq -0x10(%rbp), %rcx
movl $0x1, %edi
leaq 0x2b3d7(%rip), %rsi     # 0xc8d27
movb $0x0, %al
callq 0x95890
movq -0x58(%rbp), %rdi
callq 0x9900
movq $0x0, -0x8(%rbp)
jmp 0x9dc43
movsbl -0x6d(%rbp), %edi
callq 0x9ec8
movb %al, -0x6d(%rbp)
leaq -0xf0(%rbp), %rdi
leaq -0x170(%rbp), %rdx
leaq 0x2b3b1(%rip), %rsi     # 0xc8d3f
movb $0x0, %al
callq 0x9150
movl -0x14(%rbp), %eax
andl $0x1, %eax
cmpl $0x0, %eax
je 0x9d9b7
movl -0x14(%rbp), %eax
andl $0x2, %eax
cmpl $0x0, %eax
je 0x9d9b7
movl $0x2, -0x2d0(%rbp)
jmp 0x9d9e7
movl -0x14(%rbp), %eax
andl $0x1, %eax
cmpl $0x0, %eax
je 0x9d9ce
movl $0x0, -0x2d0(%rbp)
jmp 0x9d9e5
movl -0x14(%rbp), %eax
andl $0x2, %eax
cmpl $0x0, %eax
je 0x9d9e3
movl $0x1, -0x2d0(%rbp)
jmp 0x9d9e5
jmp 0x9d9e7
leaq -0xf0(%rbp), %rdi
movl -0x2d0(%rbp), %esi
orl $0x100, %esi            # imm = 0x100
orl $0x800, %esi            # imm = 0x800
movb $0x0, %al
callq 0x9580
movq -0x58(%rbp), %rcx
movl %eax, (%rcx)
cmpl $0x0, %eax
jge 0x9da82
movq -0x20(%rbp), %rax
movq %rax, -0x2e0(%rbp)
leaq -0xf0(%rbp), %rax
movq %rax, -0x2d8(%rbp)
callq 0x9080
movq -0x2e0(%rbp), %rdi
movq -0x2d8(%rbp), %rdx
movl (%rax), %ecx
leaq 0x2b300(%rip), %rsi     # 0xc8d47
movb $0x0, %al
callq 0x9150
movq -0x20(%rbp), %rdx
leaq -0xf0(%rbp), %rcx
movl $0x1, %edi
leaq 0x2b2f5(%rip), %rsi     # 0xc8d5a
movb $0x0, %al
callq 0x95890
movq -0x58(%rbp), %rdi
callq 0x9900
movq $0x0, -0x8(%rbp)
jmp 0x9dc43
movq -0x58(%rbp), %rax
movl (%rax), %edi
leaq -0x2cc(%rbp), %rsi
callq 0x9840
movl $0x0, -0x2cc(%rbp)
movl $0x0, -0x2c8(%rbp)
movl $0x0, -0x2c0(%rbp)
movb $0x0, -0x2b5(%rbp)
movb $0x0, -0x2b6(%rbp)
movslq -0x5c(%rbp), %rax
movl -0x290(%rbp,%rax,4), %esi
leaq -0x2cc(%rbp), %rdi
callq 0x9800
movslq -0x5c(%rbp), %rax
movl -0x290(%rbp,%rax,4), %esi
leaq -0x2cc(%rbp), %rdi
callq 0x9440
movl -0x64(%rbp), %edx
movl $0x30, %eax
movl $0x20, %ecx
cmpl $0x7, %edx
cmovel %ecx, %eax
orl -0x2c4(%rbp), %eax
movl %eax, -0x2c4(%rbp)
movsbl -0x6d(%rbp), %eax
cmpl $0x4f, %eax
jne 0x9db23
movl $0x300, %eax            # imm = 0x300
movl %eax, -0x2e4(%rbp)
jmp 0x9db3a
movsbl -0x6d(%rbp), %edx
xorl %eax, %eax
movl $0x100, %ecx            # imm = 0x100
cmpl $0x45, %edx
cmovel %ecx, %eax
movl %eax, -0x2e4(%rbp)
movl -0x2e4(%rbp), %eax
orl -0x2c4(%rbp), %eax
movl %eax, -0x2c4(%rbp)
movl -0x68(%rbp), %edx
xorl %eax, %eax
movl $0x40, %ecx
cmpl $0x2, %edx
cmovel %ecx, %eax
orl -0x2c4(%rbp), %eax
movl %eax, -0x2c4(%rbp)
leaq -0x1b0(%rbp), %rdi
leaq 0x2b1fb(%rip), %rsi     # 0xc8d71
callq 0x9a60
cmpl $0x0, %eax
setne %dl
xorb $-0x1, %dl
xorl %eax, %eax
movl $0x80000000, %ecx       # imm = 0x80000000
testb $0x1, %dl
cmovnel %ecx, %eax
orl -0x2c4(%rbp), %eax
movl %eax, -0x2c4(%rbp)
movq -0x58(%rbp), %rax
movl (%rax), %edi
xorl %esi, %esi
leaq -0x2cc(%rbp), %rdx
callq 0x9960
movq -0x58(%rbp), %rax
movl (%rax), %edi
movl $0x2, %esi
callq 0x9b20
movq -0x20(%rbp), %rdi
leaq -0xf0(%rbp), %rdx
leaq 0x2b214(%rip), %rsi     # 0xc8de7
movb $0x0, %al
callq 0x9150
movq -0x58(%rbp), %rax
movq $0x0, 0x8(%rax)
cmpl $0x0, -0x6c(%rbp)
jle 0x9dc22
leaq -0x1d0(%rbp), %rdi
movl -0x6c(%rbp), %edx
leaq 0x2005b(%rip), %rsi     # 0xbdc58
movb $0x0, %al
callq 0x9150
leaq -0x1d0(%rbp), %rdi
leaq -0x250(%rbp), %rsi
callq 0x9e020
movq %rax, %rcx
movq -0x58(%rbp), %rax
movq %rcx, 0x8(%rax)
movq -0x58(%rbp), %rax
movl (%rax), %edx
movl $0x3, %edi
leaq 0x2b141(%rip), %rsi     # 0xc8d75
movb $0x0, %al
callq 0x95890
movq -0x58(%rbp), %rax
movq %rax, -0x8(%rbp)
movq -0x8(%rbp), %rax
addq $0x2f0, %rsp            # imm = 0x2F0
popq %rbp
retq