Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Super Serial Card receive char and proper rxbuf fixes #204

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

rogueeve575
Copy link

@rogueeve575 rogueeve575 commented Oct 2, 2023

I used Linapple today as part of a development-environment-for-a-development-environment to test a little program I'm developing to upload code to my IIc.

During this work, I noticed that the serial-port passthrough feature does not seem to process any chars sent from the real port to the emulated Apple II.

This appears to be because the Super Serial Card code is only a partially-finished port of a Windows version which among other things depended on a receive thread which wasn't ever ported, thus the code to check for new chars is never called.

  • This patch fixes that scenario so you can now run serial-port applications in Linapple and get functioning passthrough in both directions to a real or virtual port; very useful for testing the Linux-side of programs that will eventually be talking to their other half on a real Apple ][.

  • I've also gone ahead and copied in a proper ring-buffer implementation from another of my projects and linked it up to replace the broken receive-buffer array that the comments were noting would blow up if more than one char was ever inserted, as the broken/invalid access patterns to this were driving me nutty :P. The emulator can now read() more than a single char at a time as soon as they become available to the Linux host and correctly pass the buffered chars in FIFO order as the Apple requests them.

This also patches the hardcoded serial-port device name from /dev/ttySxx to the now far-more-common /dev/ttyUSBxx. This is still kind of a hack and we should really probably update the config file parser to accept a proper device name instead of the bizarre "this number port - 1" integer setting probably inherited from Windows COM ports.

This is a one-evening fix for my other development efforts so there are probably some things that aren't perfect, but it does have the big advantage that unlike HEAD, this version of the serial-passthrough feature really works.

Test procedure:

  • Two USB-serial adapters on /dev/ttyUSB0 and /dev/ttyUSB1 were connected in a loopback configuration via a null modem.

  • Configure linapple.conf to use serial port "1" (i.e. /dev/ttyUSB0 in the current weird config scheme)

  • Boot Linapple to BASIC, "IN#2", then <Ctrl+A>, 14B, Enter (sets 9600 baud)

  • Back on Linux host use another Terminal: picocom -b 9600 /dev/ttyUSB1

  • You can now type commands into picocom and execute them on the emulated A2!

  • By issuing a PR#2 now from either console you can get a cool little full-duplex A2 terminal in the Linux shell, but note that the Apple II sets the high bit of every ASCII char it transmits just like the font on the real monitor, so you will need to design/modify your terminal client to strip these.

  • Note that the Apple also expects and sends a lone ASCII 13 as the carriage return sequence so you'll need to take that into account in the terminal settings. The original code didn't quite set up the serial port termios correctly which caused every CR received to be translated into a LF by the kernel before making it to Linapple so you could never press Enter! A 1-line fix included in this patch corrects this, also.

  • Dumb-terminal communication via emulated ProTerm also appears to work. You'll need to skip past ProTerm's modem init attempt which of course will fail in the particular setup described in this test as there's no modem (you could probably also just type back "OK" but I didn't have success with this as ProTerm's timeout seems pretty fast).

Caitlin Shaw added 2 commits October 1, 2023 23:55
…dd a

proper ring buffer-based receive buffer rather than the array that looks like
a buffer but is accessed in ways that would blow up if more than one char were
ever inserted

this also patches the device name opened from /dev/ttySxx to the now more-common
/dev/ttyUSBxx. This is still kind of a hack and we should *really* change the
config file parser to accept a proper device name instead of the bizarre
"this number port - 1" integer setting probably inherited from Windows COM ports.

Test procedure:
  Connect two USB-serial adapters on /dev/ttyUSB0 and /dev/ttyUSB1, connected
  in a loopback configuration via a null modem

  Configure linapple to use serial port "1" (/dev/ttyUSB0)

  Boot to BASIC, "IN#2", then <Ctrl+A>, 14B, Enter (sets 9600 baud)

  Back on Linux box use another Terminal: picocom -b 9600 /dev/ttyUSB1

  You can now type commands into picocom and execute them on the emulated A2.

  By also issuing a PR#2 you can also get a cool little full-duplex A2 terminal
  in the Linux shell, but note that the Apple II sets the high bit of every
  ASCII char it transmits, so you will need to modify your client to strip
  these.

  Dumb-terminal communication via emulated ProTerm also appears to work.
  You'll need to skip past ProTerm's modem init attempt which of course will
  fail.
@colinleroy
Copy link

This looks like #201 but better done :)

@rogueeve575
Copy link
Author

I didn't see that one :P. Looks like #201 also includes config-file fixes so we're not stuck with the stupid hardcoded /dev/ttySxx or /dev/ttyUSBxx prefix though! And @colinleroy's fix added a basic proper receive thread like the Windows version had, I like it.

The only thing IMHO is in #201 as the author kind of alluded to, once the serial port is accessed the thread will be spinning polling read() 10 times per millisecond; if we start a proper thread we should be able to go back to normal blocking I/O and/or use poll() pretty easily I'd think. An eventfd can be created and added to the poll, it can then be signaled when the terminate flag is set to ensure the thread is woken up to see the flag.

int wakeupfd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
...

void eventfd_wakeup(int efd) {
	uint64_t count = 1;
	write(efd, &count, sizeof(count));
}

void eventfd_consume(int efd) {
	uint64_t count;
	read(efd, &count, sizeof(count));
}

void *CSuperSerialCard::CommThread(LPVOID lpParameter) {
	while(!comm_thread_terminate) {
		struct pollfd fds[2];
		fds[0].fd = wakeupfd;
		fds[0].events = POLLIN;
		fds[1].fd = m_hCommHandle;
		fds[1].events = POLLIN;

		int r = poll(fds, 2, -1);
		if (r <= 0) {
			if (r < 0) perror("poll error");
		}
		else {
			if (fds[0].revents & POLLIN)
				eventfd_consume(wakeupfd);

			if (fds[1].revents & POLLIN)
				CheckCommEvent(0);
		}
	}

	return NULL;
}

void CSuperSerialCard::CommThUninit() {
	void *result = NULL;
	if (comm_thread_started) {
		comm_thread_terminate = 1;
		eventfd_wakeup(wakeupfd);
		pthread_join(comm_thread_, &result);
		comm_thread_started = 0;
	}
}

It looks like it doesn't get into fixing the broken 1-byte-only "array" buffer which admittedly the old buffer works fine but it was bleh.

Maybe parts from both should be merged.

@colinleroy
Copy link

I didn't see that one :P. Looks like #201 also includes config-file fixes so we're not stuck with the stupid hardcoded /dev/ttySxx or /dev/ttyUSBxx prefix though! And @colinleroy's fix added a basic proper receive thread like the Windows version had, I like it.

The only thing IMHO is in #201 as the author kind of alluded to, once the serial port is accessed the thread will be spinning polling read() 10 times per millisecond; if we start a proper thread we should be able to go back to normal blocking I/O and/or use poll() pretty easily I'd think. An eventfd can be created and added to the poll, it can then be signaled when the terminate flag is set to ensure the thread is woken up to see the flag.

Maybe parts from both should be merged.

This looks like a good idea :) Do you think you'd be willing to do it? You'd be welcome to rip out my PR, I don't have much extra time in the moment!

@maxolasersquad
Copy link
Member

@rogueeve575, if you can fix the conflicts, I'll go ahead and give this a merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants