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

libticables: Introduce a cable for using GPIO pins on RPi SBCs #63

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

Conversation

Vogtinator
Copy link
Contributor

With this cable, a calculator can be connected to pins 16 (Tip/Red) and 18
(Ring/White) ideally through a ~1k resistor. Transmission happens by switching
the lines between tristate input and 0V output states.

In case the voltage of the calculator exceeds 3.3V, diodes should be used
in addition to the resistors to limit the line voltage to 3V3 to avoid
damage.

With this cable I can receive files with speeds up to 4.0kB/s on a RPi2. The bottleneck is the slow mechanism to read and write GPIO values.

Depends on #62

TODO:

  • Some error conditions are not propagated upwards yet. What are the right codes for e.g. failure to read a GPIO?
  • No integration with autotools

The port from strcpy to strncpy errorneously used sizeof on a heap allocated
array, which doesn't have the expected result. This broke e.g. saving variables
to files, which were empty beyond the header.
The constness needs to be identical.
With this cable, a calculator can be connected to pins 16 (Tip/Red) and 18
(Ring/White) ideally through a ~1k resistor. Transmission happens by switching
the lines between tristate input and 0V output states.

In case the voltage of the calculator exceeds 3.3V, diodes should be used
in addition to the resistors to limit the line voltage to 3V3 to avoid
damage.
@Vogtinator
Copy link
Contributor Author

With this cable I can receive files with speeds up to 4.0kB/s on a RPi2. The bottleneck is the slow mechanism to read and write GPIO values.

For completeness, here's a branch which replaces use of libgpiod with direct mapped access to the GPIO controller: https://github.com/Vogtinator/tilibs/tree/gpiommap. With that I get transfer speeds of 8.4kB/s, still using the simple setup with just two 1k resistors.

@debrouxl
Copy link
Owner

Thanks :)
I don't really have access to IRC tonight.

Several notes:

  • the TI-Z80 series uses 5V, while the TI-68k series uses 3.3V but is 5V tolerant, and the two series are interoperable, if one uses software such as X-Link on the TI-Z80 side. But maybe you meant that the RPi isn't 5V tolerant, and that it's the side which needs protection from the calculators ?
  • I think that while the main target for such a GPIO cable would arguably be the RPi series, given how widespread it is, a libgpiod-based GPIO cable shouldn't be limited to the RPi. If nothing else, the device name and GPIO pin numbers should be constants defined at the beginning of the file, so that it's (slightly) easier to redefine them at compile time if needed. But see the next item and the bottom of this comment;
  • given that it's significantly faster, rivaling or beating a SilverLink (!), it would be sad not to keep the mmap()-based approach around under some form. IMO, that hints towards a second GPIO cable... this time, certainly a GPIO_MMAP_RPI platform-specific cable, since I have a feeling that it's insane to impose on you, me or anybody else the burden of trying to come up with a generic MMGPIO cable able to deal with whichever set of quirks a random GPIO controller might have :)
  • you can add a couple error codes for open and close in libticables/trunk/src/error.h; ERR_READ_ERROR and ERR_WRITE_ERROR coupled with ticables_warning() usage, as you've started doing, could arguably be reasonable ways to communicate GPIO R/W failure. The mmap()-based cable would require at least another specific error code for the inability to map memory, both the TIEmu and VTI cables already have such error codes.

For item 2 (and 3, if you want to provide the ability to use other GPIO pins ?), bonus points for implementing, say, a struct containing a version number + extra parameters to be passed by a new API similar to ticables_cable_set_device() to the cable implementation before the cable open function is called. ticables_cable_set_device() could become a deprecated wrapper for the new API, sending a struct version 1 limited to the device, while struct version 2 would be device + new parameters.
This approach would kill two birds with one stone, as such advanced setup functionality is also needed for run-time handling of the several third-party GrayLink-like cables which have been springing around in the past year or so, and have their own quirks: device, speed, need to enable RTS/CTS are the ones we saw so far.
No obligation to implement a UI for setting up these special parameters: something along of the line of new "cable_gpio_*" entries in the DEVICE section of the TILP config file (~/.tilp), several lines of code for parsing these and storing the config information into TILP's internal config structures, and calling the new libticables API I described before ticables_cable_open() shall do the job just fine, in my book.

How does that sound ? It shouldn't be too much of a burden in terms of development time, plus you don't have to do it entirely alone.

@Vogtinator
Copy link
Contributor Author

Thanks :) I don't really have access to IRC tonight.

Several notes:

  • the TI-Z80 series uses 5V, while the TI-68k series uses 3.3V but is 5V tolerant, and the two series are interoperable, if one uses software such as X-Link on the TI-Z80 side. But maybe you meant that the RPi isn't 5V tolerant, and that it's the side which needs protection from the calculators ?

Yep.

  • I think that while the main target for such a GPIO cable would arguably be the RPi series, given how widespread it is, a libgpiod-based GPIO cable shouldn't be limited to the RPi. If nothing else, the device name and GPIO pin numbers should be constants defined at the beginning of the file, so that it's (slightly) easier to redefine them at compile time if needed. But see the next item and the bottom of this comment;

Can be done, but it's hardcoded in only a single place, so moving that elsewhere wouldn't really help much.

  • given that it's significantly faster, rivaling or beating a SilverLink (!), it would be sad not to keep the mmap()-based approach around under some form. IMO, that hints towards a second GPIO cable... this time, certainly a GPIO_MMAP_RPI platform-specific cable, since I have a feeling that it's insane to impose on you, me or anybody else the burden of trying to come up with a generic MMGPIO cable able to deal with whichever set of quirks a random GPIO controller might have :)

Yep. One issue is that the addresses are different depending on which RPi it is, so that would need some extra code and testing. It also needs root privs more than the libgpiod cable and a special kernel parameter (depending on kernel config).

  • you can add a couple error codes for open and close in libticables/trunk/src/error.h; ERR_READ_ERROR and ERR_WRITE_ERROR coupled with ticables_warning() usage, as you've started doing, could arguably be reasonable ways to communicate GPIO R/W failure. The mmap()-based cable would require at least another specific error code for the inability to map memory, both the TIEmu and VTI cables already have such error codes.

Ok.

For item 2 (and 3, if you want to provide the ability to use other GPIO pins ?), bonus points for implementing, say, a struct containing a version number + extra parameters to be passed by a new API similar to ticables_cable_set_device() to the cable implementation before the cable open function is called. ticables_cable_set_device() could become a deprecated wrapper for the new API, sending a struct version 1 limited to the device, while struct version 2 would be device + new parameters. This approach would kill two birds with one stone, as such advanced setup functionality is also needed for run-time handling of the several third-party GrayLink-like cables which have been springing around in the past year or so, and have their own quirks: device, speed, need to enable RTS/CTS are the ones we saw so far. No obligation to implement a UI for setting up these special parameters: something along of the line of new "cable_gpio_*" entries in the DEVICE section of the TILP config file (~/.tilp), several lines of code for parsing these and storing the config information into TILP's internal config structures, and calling the new libticables API I described before ticables_cable_open() shall do the job just fine, in my book.

One issue is that all of those parameters are highly cable specific and basically need hardcoded integration on the library user side as well, which means that it can't really be abstracted easily. Maybe the best way is to just not abstract it at all and have some cable specific structs...

How does that sound ? It shouldn't be too much of a burden in terms of development time, plus you don't have to do it entirely alone.

Once we have an idea of how the API could look like that's probably worth a try.

@Vogtinator
Copy link
Contributor Author

given that it's significantly faster, rivaling or beating a SilverLink (!), it would be sad not to keep the mmap()-based approach around under some form.

FWIW, I got around ~9.2kB/s by optimizing the algorithm a bit. The bottleneck appears to be the calculator (V200 in this case) which needs ~10µS between receiving bits. So it's unfortunately not possible to go beyond 10kB/s.

The speed calculation also slows transfers down a bit, as it forces transfers to happen with a maximum block size of total/20:

handle->priv.progress_blk_size = (length + 6) / 20; // 5%

This only really affects smaller files though, particularly screenshots.

IMO, that hints towards a second GPIO cable... this time, certainly a GPIO_MMAP_RPI platform-specific cable, since

Question is how to share common code between those cables. It's essentially just the open/close and set_line/get_raw_masked functions which differ. It might even be possible to deduplicate code with the serial/parallel cables that way...

@debrouxl
Copy link
Owner

Thanks.

  • I think that while the main target for such a GPIO cable would arguably be the RPi series, given how widespread it is, a libgpiod-based GPIO cable shouldn't be limited to the RPi. If nothing else, the device name and GPIO pin numbers should be constants defined at the beginning of the file, so that it's (slightly) easier to redefine them at compile time if needed. But see the next item and the bottom of this comment;

Can be done, but it's hardcoded in only a single place, so moving that elsewhere wouldn't really help much.

And infrastructure for advanced cable parameters makes it moot anyway.

  • given that it's significantly faster, rivaling or beating a SilverLink (!), it would be sad not to keep the mmap()-based approach around under some form. IMO, that hints towards a second GPIO cable... this time, certainly a GPIO_MMAP_RPI platform-specific cable, since I have a feeling that it's insane to impose on you, me or anybody else the burden of trying to come up with a generic MMGPIO cable able to deal with whichever set of quirks a random GPIO controller might have :)

Yep. One issue is that the addresses are different depending on which RPi it is, so that would need some extra code and testing. It also needs root privs more than the libgpiod cable and a special kernel parameter (depending on kernel config).

Good points, I didn't think about that.

For item 2 (and 3, if you want to provide the ability to use other GPIO pins ?), bonus points for implementing, say, a struct containing a version number + extra parameters to be passed by a new API similar to ticables_cable_set_device() to the cable implementation before the cable open function is called. [ ... ]

One issue is that all of those parameters are highly cable specific and basically need hardcoded integration on the library user side as well, which means that it can't really be abstracted easily. Maybe the best way is to just not abstract it at all and have some cable specific structs...

In fact, libticables already has a struct CableOptions, but it doesn't use it at all. The only known consumer is TilEm, for its internal purposes, and it doesn't use the calc field (which, nowadays, should have CalcModel type anyway, since that now lives in libticonv). This gives significant freedom in expanding CableOptions; source-level and backwards compatibility doesn't have to be broken.

How does that sound ? It shouldn't be too much of a burden in terms of development time, plus you don't have to do it entirely alone.

Once we have an idea of how the API could look like that's probably worth a try.

An early proposal for the new CableOptions struct, and a setter + getter for it, at https://github.com/debrouxl/tilibs/tree/ticables_cable_options . I developed it against master, but it applies equally well to experimental2: most of the action in libti* happens in libticalcs.
Thoughts ?

given that it's significantly faster, rivaling or beating a SilverLink (!), it would be sad not to keep the mmap()-based approach around under some form.

FWIW, I got around ~9.2kB/s by optimizing the algorithm a bit. The bottleneck appears to be the calculator (V200 in this case) which needs ~10µS between receiving bits. So it's unfortunately not possible to go beyond 10kB/s.

The speed calculation also slows transfers down a bit, as it forces transfers to happen with a maximum block size of total/20:

handle->priv.progress_blk_size = (length + 6) / 20; // 5%

This only really affects smaller files though, particularly screenshots.

Good point. If only this were the only issue with speed calculation, though :)

IMO, that hints towards a second GPIO cable... this time, certainly a GPIO_MMAP_RPI platform-specific cable, since

Question is how to share common code between those cables. It's essentially just the open/close and set_line/get_raw_masked functions which differ. It might even be possible to deduplicate code with the serial/parallel cables that way...

There's already some code sharing for e.g. the USB cables, so go ahead for the GPIO cables :)

@debrouxl
Copy link
Owner

RFS: https://gist.github.com/rvalles/f937889712d24ac6824f1358c936b3e2 is the patch to deal with one of the "weird GrayLink" implementations, this one needs all three of speed, device and RTS / CTS enablement quirks.

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

2 participants