How are registrations of GPIO board types managed
Pages: 1 2
Charles Ferguson (8243) 427 posts |
Hiya, I’ve implemented a new GPIO module to access a USB GPIO/I2C device. To identify it, there is a call GPIO_GetBoard which returns the board types which can be returned as a number. According to the documentation, values 0 to 19 are defined. To identify the controller, I should use a new GPIO board type so that it’s recognised as a different type of device, but the GPIO board types are not standard registered values. For now I’ve just used 64, but it would be good to have a registered type for the device (and to know how to register future GPIO registrations). The GPIO device is an MCP2221 – one of these: https://thepihut.com/products/usb-uart-i2c-debugger The device has been tested with the Blinkt! RGB lights – https://thepihut.com/products/blinkt Example source to use blinkt! with the GPIO board: 10REM >blinkt 20REM Blinkt light, with GPIO 0 wired to DATA, GPIO 1 wired to CLOCK 30 40REM Report the board details 50SYS "GPIO_GetBoard" TO board%, board_name$ 60PRINT "Board type: ";board% 70PRINT "Board name: ";board_name$ 80 90pinDAT = 0 100pinCLK = 1 110 120REM Enable pins as outputs 130SYS "GPIO_WriteOE", pinDAT, 0 140SYS "GPIO_WriteOE", pinCLK, 0 150 160REM Write the pins, clocking the data to the device. 170PROCstart_of_frame 180brightness=0.05 190level = 48 200base = 8 210FOR I = 0 TO 7 220 PROCwritecolour(base + level * (I AND 1), base + level * (I AND 2)/2, base + level * (I AND 4)/4, brightness) 230NEXT 240PROCend_of_frame 250END 260: 270REM Functions... 280DEFPROCstart_of_frame 290SYS "GPIO_WriteData", pinDAT, 0 300FOR I% = 1 TO 32 310 SYS "GPIO_WriteData", pinCLK, 1 320 SYS "GPIO_WriteData", pinCLK, 0 330NEXT 340ENDPROC 350: 360DEFPROCend_of_frame 370SYS "GPIO_WriteData", pinDAT, 0 380FOR I% = 1 TO 32 390 SYS "GPIO_WriteData", pinCLK, 1 400 SYS "GPIO_WriteData", pinCLK, 0 410NEXT 420ENDPROC 430: 440DEFPROCwritebyte(b%) 450FOR bit% = 7 TO 0 STEP -1 460 value% = ABS(b% AND (1<<bit%)) 470 SYS "GPIO_WriteData", pinDAT, value% 480 SYS "GPIO_WriteData", pinCLK, 1 490 SYS "GPIO_WriteData", pinCLK, 0 500NEXT 510ENDPROC 520: 530DEFPROCwritecolour(r%, g%, b%, brightness) 540brightness = (brightness * 31) AND 31 550PROCwritebyte(%11100000 OR brightness) 560PROCwritebyte(b%) 570PROCwritebyte(g%) 580PROCwritebyte(r%) 590ENDPROC … and output from running the command: charles@laputa ~/projects/RO/pyromaniac (gpio-experiment)> pyrodev --basic --load-module modules/Banner,ffa --config kernel.reset_banner=true --config gpio.implementation=mcp2221 --config mcp2221.reset_delay=3 --config mcp2221.reset_on_use=false RISC OS 7.34 (05 Apr 2022) 3MB RISC OS Pyromaniac BASIC V version 1.36 © RISCOS Ltd Starting with 1044732 bytes free >TEXTLOAD "Blinkt" Program renumbered >RUN Board type: 64 Board name: MCP2221 >*GPIOMachine GPIO hardware: MCP2221 GPIO type number: 64 The lights show different colours on each LED. |
Julie Stamp (8365) 474 posts |
The API for the GPIO module was changed in 2017. The SWI GPIO_GetBoard has been combined into an extended GPIO_Info. The details are returned as an OS_Hardware device. The latest version of the module, complete with updated StrongHelp manual, is available here. |
Charles Ferguson (8243) 427 posts |
Sigh. Ok so the interface defined within the manual and module in the source repository is wrong ? So the question remains how to register a new board for the module – it isn’t a matter of registering anything in Os_hardware because a) it has nothing to do with the machine that is running and b) there is no OS_hardware calls on the system. I’ll look at the new documentation later. |
Charles Ferguson (8243) 427 posts |
Having looked at the implementation – haven’t had the time to extract the SH manual into text yet – the new implementation is even less useful than the original to me. It directly exposes the HAL entries as part of the API (which don’t exist for the device), and the information returned by *GPIODevices is not accessible programatically because the GetBoard call isn’t implemented and there are no other interfaces to return that information. |
Tank (53) 375 posts |
You could try OS_ReadSysInfo 9 with a reason of 0 and compare the returned string to the list of boards I have added to the !GPIOCofig app which is available from here |
Rick Murray (539) 13850 posts |
It would appear that The Powers That Be don’t like people doing things like proving the hardware type, notwithstanding there sometimes being valid reasons to do so. There’s the CPUID… Or, as mentioned above, try to interpret the board ID… It would be much better to have a platform/revision ID like before, but that’s too close to forbidden fruit. ;) |
Charles Ferguson (8243) 427 posts |
As stated, this is not a hardware board, but a USB card… running on RISC OS Pyromaniac on macOS. So…
The answers here (and the implementation of the GPIO module) assume only that the GPIO will be provided by the HAL and that it is a function of the base board, and can never be extended beyond that. |
Rick Murray (539) 13850 posts |
If it’s a USB device, shouldn’t you be looking at using the USB subsystem instead? And, no, the current GPIO module won’t help as it’s a friendly interface to the onboard GPIO pins that are handled by the HAL. That being said, the idea of add-on GPIO is interesting. <dream>wouldn’t it be nice if you could plug that device in and automatically have an extra IIC bus, four more GPIO pins, and an extra serial port?</dream> |
Charles Ferguson (8243) 427 posts |
I’m providing a GPIO device, so that the interface is USB is irrelevant. The device that it is providing is GPIO (and IIC). And just like on RISC OS, the transport is abstracted so nobody using the GPIO should care that it’s implemeted through USB. On in my case, implemented through a USB device managed by the HID system running on macOS, which is then accessible by the Python that provides the implementation of the GPIO module.
That’s pretty much exactly what I’m saying – the interface that is provided by the GPIO module is naive in that it assumes that the only thing that will provide the GPIO access is the base board. It’s the same trap that the IIC module fell into (although that’s much easier to fix because its SWI interface is easy to extend). And it’s surprising that this hasn’t been designed in, really. RISC OS, being a modular and extensible system, should be able to handle this. In RISC OS Pyromaniac, when this device is configured, you get IIC_Control wired to the device, GPIO_* SWIs controls the pins. I’ve not implemented any serial access in RISC OS Pyromaniac yet, but that will be doable too. I love the fact that creating the IIC implementation that talked to the MCP2221 in Pyromaniac tool around 50 minutes, at which point I could control my little LCD panel. The GPIO implementation took a little longer due to bugs in the MCP2221 library and having to find the correct pins to connect to my lights. And not realising that having ground connected was important slowed things a little. Trying to understand the GPIO module SWIs on the other hand… Sigh. |
Alan Adams (2486) 1149 posts |
And even that is incomplete – the iMX6 board used in the ARMX6 computers has GPIO hardware, but the GPIO module ignores it. |
Sprow (202) 1158 posts |
the interface that is provided by the GPIO module is naive in that it assumes that the only thing that will provide the GPIO access is the base board The iMX6’s HAL doesn’t implement API 1.00, it’s gone its own path with a different API and didn’t get updated yet so the module goes dormant rather than randomly call incompatible HAL functions. |
Charles Ferguson (8243) 427 posts |
Ok, having now returned home and looked at the text of the StrongHelp manual and the source in the implementation, it looks the the question is redundant because the ability to identify the manner in which the device is connected has been removed, and the abilty to therefore detect what type of hardware you are controlling is thus not possible. The original API looked to me to be a dump of a bunch of features which were specific to a group of boards, without much thought for the manner in which this would work for devices which provide other forms of access to such pins – ie if a different provider of GPIO pins was to add new mappings, because the SWIs were specific to the prior hardware’s use and didn’t offer a flags word for example, or and had been even named for specific perposes, it meant that more and more specific SWIs needed to be added. I suspect that that was recognised as being unsustainable and the new API created… but the new API looks blinkered and still does not address the ability for hardware to be dynamic. The problem I have with the API at the current time is that… a) I cannot provide the new API. It’s just not possible to provide the ‘FULL’ Info API completely because it assumes that you will have a ‘HAL descriptor’, which does not exist. The ‘FULL’ API appears to be reasonable with the exception of exposing the HAL entries, which is what precludes the system being dynamic and precludes it being used on a system without a HAL. The purpose of an interface module is to provide a means of accessing a set of functions without knowledge of how they’re implemented – passing HAL information straight through negates that entirely. Since the FULL API is not usable as it stands, I’ve got a choice of…
I’m going to choose the second of these options, because clients trying to use the new interface would find that the value of 0 for the descriptor was unexpected, and the enumeration interface doesn’t really allow for this (because the HAL descriptor is explicitly used as the value that would usually be the opaque handle, and therefore has to be valid as something). NOTE: The prior sentence is wrong – I misread the API and it looks like it’s probably unsafe, but not impossible to return a HAL descriptor of 0. As such, I’m going to use the identifier 64 for the MCP2221’s board type, as I have been doing, and 65 for the WxWidgets UI interface which I’m currently working on. Presumably anyone building an expansion board or similar will hit exactly the same problems, but someone’s going to have to rework the interface again in order to make that functional and remove the baseline assumptions that the old and new APIs have. |
Julie Stamp (8365) 474 posts |
The API allows GPIO devices to be added and removed; the current version of the module does not.
Wherever you see ‘HAL descriptor’ in the StrongHelp manual, read it as ‘device descriptor’. It’s the struct called gpiodevice_t in GPIO.h.GPIODevice.
Device information is passed straight through. It so happens that until now, all GPIO devices have come via the board’s HAL.
The API 1.00 tells you what the GPIO devices are: from a device descriptor you may read the id and description (roughly the information previously obtained by GPIO_GetBoard, but the id numbering is not the same), and also where the GPIO device is connected. There is no presently defined value for a connection through USB; that will be an addition required to support use of the MCP2221. The other is to add an id for that device. |
Charles Ferguson (8243) 427 posts |
As I’ve gone through it, I may as well mention a few of the issues that I’ve seen in the interface (a couple of which will be repeating points that I mentioned in the prose of the prior posting)…
This and the general way that it’s provided makes it hard for me to understand how you might use this in a delivered product. Consider a product which had a GPIO pin wired to systems which are not intended to be used as GPIO – if you’ve provided a preconfigured system for example you might have GPIO pin X and Y wired to a mincer power control (let’s say, for the purposes of discussion – if you don’t like that, maybe consider it as a ‘case open’ control, or a floppy eject pin or something). You would usually then have a driver module which controlled this in a structured way – for example a ‘MincerControl_Enable’ SWI to turn on that pin. It doesn’t make sense to have that pin available for general use, because in the device it’s not actually available for the user to use. In such cases I would have thought that the driver module on initialising (or on seeing that the GPIO module started through the service announcing its arrival) would reserve the pins that it knows it’s going to use to prevent people from accessing them. It could then program the Mincer through those pins to the initial state now that it has started. And at any time it would be safe in knowing that it had exclusive access to those pins (maybe through a lock out). Maybe that’s more advanced than you need for simple devices, but I chose ‘mincer’ because it might be something you never want to be accidentally accessed or probed by something else whilst the driver is using it. Maybe a simpler example would make more sense – if a collection of GPIO pins can be switched from being GPIO to being a collection of IIC pins you want to lock them out from being used as GPIO. Once the IIC module starts, it recognises that the hardware it is running on wants to use those pins as GPIO (or your tell the driver that’s what you want to do) and the GPIO module locks out those pins from being used as GPIO. This isn’t possible in the current interface. I think it’s part of what the original interface was trying to do, but tried to mix user access to pins and configuration of devices into the same system. Maybe that’s not a bad thing, but it didn’t feel so nice. Coding errors:
Documentation errors:
|
Charles Ferguson (8243) 427 posts |
It really doesn’t allow dynamic devices. The HAL descriptor is static memory pointers – it’s not returned as a copy into the user’s memory, and in any case it contains pointers to interfaces. You cannot know the lifetime of these pointers, or that they have been removed dynamically. Without having services to tell clients that the devices have changed, they cannot know that the memory is invalid, or that the devices that they are trying to control are not there again. Or that they have come back.
Ah, I’d not seen this. It’s not described in the documentation either in form or use. It’s still not possible in that structure to say what the device is (either as a human readable description or as a programatically defined value) in that structure.
API 1.00 ? I don’t see anything in the documentation which describes the API version for the GPIO system, or any way to read what API it conforms to? Are you meaning that this is the FULL Info API defines it as 1.00 ? or does a Features value of 1.00 define it as GPIO API 1.00?
The device descriptor you referenced doesn’t have any id or description – it is described as an opaque structure (‘struct device’) in that header. If it’s defined in the HAL then I refer back to the assumption that this is a HAL device, and these values are being passed straight through, not being abstracted as a user interface – and that it’s not documented. |
Charles Ferguson (8243) 427 posts |
Utterly unexciting, but here’s the Blinkt! lights in use… |
Andrew Conroy (370) 740 posts |
Yay, nice one! I do like the Blinkt! board, I’ve used a few on RISC OS so far.
Certainly for the Pi, if you read the part of the StrongHelp documentation for the earlier module, that lists the mode numbers and these appear to be still correct. At least for the Pi0-3 anyway, there’s an (unresolved) issue on the Pi4 that means changing mode doesn’t actually have the desired effect (I’ve been unable to convert pins from GPIO to PWM, for instance, and it works fine on the Pi0-3). The documentation for the new module is woefully inadequate/lacking.
The earlier module had a GPIO_EnableI2C which allowed you to lock the IIC pins against being used as GPIO!
The Pi generates its own ‘events’ to tell you if a pin went high whilst you weren’t looking. Next time you look at the pin (GPIO_ReadEvent), you can see there’s an event there which you can then clear.
The powers that be deemed wanting to know what board you were running on to be a bad thing. Anyone dissenting from this was deemed a heretic! |
Julie Stamp (8365) 474 posts |
Good spot!
My mistake – that’ll be FULL. The API 1.00 refers to the GPIO-specific part of the device descriptor. The device type, id and connection are read from the generic part.
In terms of the memory, meaning the pin info structure pointed to on return by R0, and the device descriptor returned in R2. Writing for the MCP2221, the pin info isn’t going to change, so I’d make that static, and then have the module refuse to die until all devices have been completely Deactivat’ed. There’d be one device descriptor for each device, so again I’d hang on to that until it’s been completely Deactivat’ed. In terms of knowing the device has gone, I guess that’s a bit trickier because you don’t want logical pin numbers to be reused straight away if a different USB dongle is inserted say. I hope we could work out a way to combine that neatly with pin locking, providing a convenient way for user-mode progams to discover changes. As it stands though, Service_Hardware is the thing to look out for. What I forgot to put in the post yesterday was that we need to put in a means as well for a device to indicate that it doesn’t have a permanently assigned location in the space of logical pins. Perhaps this should be indicated by setting the ‘number’ field to &FFFFFFFF? The existing module ignores such a device.
Eeek! Pin locking sounds like a good thing to carry over from the old GPIO API in some form. Fortunately there’s a GPIO_Features SWI, so a bit of that can be used to indicate if it’s available. |
Julie Stamp (8365) 474 posts |
Going back to the original question, can some clever person who knows about USB indicate what bus type to put USB under so we can make the header file ready for an MCP2221 module? (I also found add-ons based around the FT232H). Here’s the guidance:
Subtypes for buses are listed here |
Charles Ferguson (8243) 427 posts |
I’m not sure I understand what you’re saying. As a GPIO driver module providing access to the device, nobody is going to be accessing the device independant of the driver – that’s the point of a driver, in abstracting away any of that access. No user of the GPIO module should ever care about activating or deactivating a device. They should locate the interface that they wish to use, and use it. At least that’s the design that has been provided here – as stated, there’s no opening or claiming of the device for use for the users of the GPIO module. So you cannot say that you’re not doing to let the module die until there are no devices left activated, because it’s not the responsibility of the user of the module to claim, open or otherwise activate its use (or vice-versa, to say that they’re no longer using it).
Logical pin handles should be matter for the GPIO module – the existing GPIO HAL implementation (not the master one) still assumes the limit of 255 total logical pins, which map in chunks of 32 to physical pins provided by a device – but that 32 chunking is an implementation detail, and the GPIO module can handle it in a different way if it wishes. Sacrificing the compatibility to the old interface, the number of pins can be increased – since the only part of the new interface that retains the reference to there being only 255 logical pins is the legacy implementation of the _Info SWI call (and then only in the extended descriptors) this is relatively trivial to ignore as a limitation. I would suggest each device be allocated separate pin identifiers, by the GPIO system and not reused. The current implementation doesn’t do this, but again, that is just an implementation detail. If it were me, I would do something along the lines of…
I would assume that changing the configuration of the hardware so that a set of pins in a device were now assigned from being GPIO to being a dedicated function such as IIC or SerialDeviceDriver would essentially be a locking operation that removed those pins from play whilst the IIC module, or SerialDeviceDriver managed them. However, this is only one small part of the system and I was merely tinkering with things to try to provide a suitable interface so that Pyromaniac can use it – so I don’t care all that much, beyond the fact that the interface is limited and hasn’t been designed in a manner which allows safe extensible use.
With a dynamic system, and pin locking, the most likely way that the user mode application would discover the change would be that the calls it performed returned an error (eg ‘Device not available’ or something, with a suitable number that distinguishes it from other physical errors). If there needed to be more general way of polling that the device was present, a new ‘Probe’ call could be added to check that a pin existed, so that you could check for a pin without being required to read or write it. Actually if you were doing that, you could that call return the descriptor for the pin if it was there, and error if it wasn’t – thus providing a nice chunk of information if it exists, and fail if not. Hmm, although that means populating buffers and doing work… so maybe not… anyhow, that’s kinda how I’d think about it.
Cannot answer that, I’m afraid. The data path isn’t really relevant In my case, the data path to the MCP module is: python MCP2221a module → python hidapi module → hidapi driver → macOS USB system. In the same vein, the new device that I’m creating is a UI control that shows the state, and its data path is simpler: python wxWidgets → macOS controls on the screen. And the other device that I already have is the null device, whose data path is even easier…
… but there are no gaps for the case where some device that doesn’t fit into the scheme can be fitted in? In my case, if you were doing anything in this area, I’d suggest that if I were using such descriptors (which I’m not) I’d declare them as virtual devices, because they don’t fit into the categories you have, but with a configuration option to override the value reported to allow it to be tested. I feel like I’m being whittery and awkward because it’s not now I’d have done it, so take my complaints and design comments with a grain of salt. I know people don’t really like the way that I have done things in the past, so I’m going to try not to be precious about my way of doing things. But with that in mind, remember that RISC OS Pyromaniac is an operating system that does not expose the hardware implementations. It only exposes the external interfaces. Which is why this user-facing interface that is exposing hardware implementation details is so problematic. |
Dave Higton (1515) 3534 posts |
Charles, your suggestions look entirely sensible to me. One thing you’re suggesting is like a more advanced version of the resource reservation system, working down to the level of individual GPIO pins. This has been mentioned several times over the years. Concerns I have: Reserving and relinquishing resources is fine when nothing goes wrong. But if some code crashes after reserving pins, what happens to the reservation? Can the pins be reserved again (perhaps for the same app when it is re-run) without having to reboot the whole system? I can imagine the reservation system knowing when a Wimp app has died, but what about non-Wimp apps and modules? (Is there any other class of code?) A service call to announce arrival and departure of resources works well for modules, but I’m not aware of any way for applications to receive service calls. But apps have just as much right to use resources as modules. |
Charles Ferguson (8243) 427 posts |
Absolutely this is a problem, in the same way as it causes a problem for file handles and socket handles, and econet transmission handles, and dynamic areas (which aren’t application bound), and so on. The way to address it is exactly the same as that for the applications – you ignore the problem, because the OS has no way of dealing with it. The only reliable way to fix that is to introduce a process model which allows the system to understand and deal with the lifetime of user space applications. Fortunately, things crashing should only be a problem for the developer, who is used to handling that (because this is RISC OS, after all), and application users will clearly not see the problem because developers will have tested their software well enough that the problem won’t happen. Yes, that’s a little silly, but that is the way that things are. Knowing about wimp applications really isn’t any help whatsoever because nobody should ever be relying on those being the only kind of user space clients 1, and in any case, a process may have crashed inside a wimp application but that doesn’t mean that the wimp application exits. In the majority of cases, device drivers which control the hardware won’t be written in user space, so they will have the access to the service calls, and it is sufficient to worry about those. Attempting to fix an architectural flaw to address this issue just for GPIO is missing the point of the problem – that a process model is required for many classes of resources, and should be addressed at that level and not just for the specific instance. 1 Yup, this is sarcasm – I’ve done this before as well. Bad Gerph. |
Rick Murray (539) 13850 posts |
It’s okay, most of us here are British. Scathing sarcasm is a way of life… ;) |
Simon Willcocks (1499) 519 posts |
What’s the chance of a CRC32 of a module’s name AND &7ff80 clashing with anothers’? |
Charles Ferguson (8243) 427 posts |
Wrong topic? |
Pages: 1 2