Search code examples
valavapi

Create binding for #define pointer address


Creating some custom vapi defs with the help of the excellent write up in the Vala manual as my guide. But I'm not sure how to translate C function-like macros like these:

// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
#define GET_GPIO(g) (*(gpio+13)&(1<<g)) // 0 if LOW, (1<<g) if HIGH
#define GPIO_PULL *(gpio+37) // Pull up/pull down
#define GPIO_PULLCLK0 *(gpio+38) // Pull up/pull down clock

The C code declares gpio this way:

// I/O access
volatile unsigned *gpio;

Should I declare the macro INP_GPIO(g) as a void function, i.e.

[CCode (cname = "INP_GPIO")]
public void inp_gpio(int val);

or as a delegate, like below?

public delegate void inp_gpio(int val);

Which conditions should I follow when deriving the Vala type from C code for a VAPI file?

Update: As I continue to work on my valaIOT project, the vapis mentioned below are maintained at https://gitlab.com/gpaslanis/valaiot/tree/master/vapis. Please post recommendations/corrections at the site. I hope you find them helpful.


Solution

  • Good question! The code looks like it is directly accessing the GPIO memory address and seems to be from RPi GPIO Code Samples - Direct register access. The C pre-processor is exchanging INP_GPIO(g) for an expression using the &= operator. The expression does a bitwise operation on the memory location calculated on the left hand side of the operator.

    All Vala needs to do is make sure INP_GPIO(g) is written to the C file and then the C pre-processor does the exchange. So the correct binding would be something like:

    [CCode (cname = "INP_GPIO")]
    public void inp_gpio(int pin);
    

    A delegate in Vala is a function pointer in C and the code isn't going to call the memory address, but write a value to it. It's not a function pointer in C and so shouldn't be bound as a delegate in Vala.

    Using GPIOs is a great use case for Vala. You may want to consider using the Linux kernel user space API instead. This recently changed with Linux 4.8 and isn't in the Vala linux.vapi. So a patch would be welcome for using linux/include/uapi/linux/gpio.h with Vala. It's essentially a file interface to /dev/gpiochipx with various IOCTLs to manipulate it. For more details see these slides. If you understand GMainContext and GSource I think it would be possible to write a Vala GSource with g_source_add_unix_fd. This would trigger an event in the GMainContext when there is a change in a GPIO line. An event being just another name for a callback. This would be a good way of implementing higher level application code in response to inputs on GPIO lines. A GMainContext is created behind the scenes when using GMainLoop or GApplication. The documentation for Vala needs to done though.

    There is also libgpiod that provides a user space library to interface with the kernel character device interface. For Vala this would mean writing a libgpiod.vapi to use gpiod.h. libgpiod also has command line tools and this article give a nice summary of those.