Using GPIO with C ( permission problems )

Peter
Peter New Member Posts: 3
Hello,

I'm currently making a project where I have to use GPIO ports provided by the Upboard.
On this board Ubuntu Linux 4.4.0-2-upboard is installed.
The SPI, I2C and Serial ports are all working fine in a C program, after giving the user permissions in a startup script.

Also LIBMRAA is installed and when using this library in terminal, I can control the GPIO ports. ( after elevating with sudo )
But when using LIBMRAA in a C program I have permission problems.

I have searched many hours and tried a lot of things, but I cannot get this to work.
Is there anyone that can point me in the right direction how to solve GPIO permission problems?

Thanks in advance.
«1

Comments

  • Dan O'Donovan
    Dan O'Donovan Administrator, Moderator, Emutex Posts: 241 admin
    I would have thought that running your C program with sudo would be enough to solve the permissions issue.
    Have you tried one of the MRAA C/C++ examples? If that works, and your C program doesn't, then I'd suggest that the problem is somewhere in your C program.
    https://github.com/intel-iot-devkit/mraa/blob/master/examples/c++/Blink-IO.cpp

    In all cases, you will need root-level permission to use the GPIO ports on UP.
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    It has been awhile since I played with this.

    But I ran into issues earlier where running the command under sudo did not work.

    However if I did sudo sh
    and then ran the command, it would work.

    I hope to get back to this soon. Hopefully there will be a better solution. Like maybe some how use udev rules to give better permissions?
  • Dan O'Donovan
    Dan O'Donovan Administrator, Moderator, Emutex Posts: 241 admin
    We did, at one point, play with udev rules to set the permissions on the GPIO nodes under /sys. It worked, but not perfectly, and we decided not to keep it under ubilinux in the end.

    The problem with the udev approach is that the GPIO nodes are created dynamically when the pin is exported. The udev rule is fired asynchronously, a short time after the nodes for a newly exported pin (e.g. /sys/class/gpio/gpioN/*) are created. There is a short window of time where the application may try to use those nodes, immediately after the pin is exported before the udev rules have had a chance to run, and the application will fail to open the files in that case (permission error).

    So, rather than having users run into that confusing race condition, we felt that it was more straightforward to simply suggest that the application should be run as root when needing to access GPIO pins.
  • WereCatf
    WereCatf New Member Posts: 201
    It's really not such a big deal. Simply export all the pins you intend to use, then wait e.g. 200ms to let udev set the permissions correctly and off you go. I do a similar thing on my two C.H.I.P.s, with the following udev-rule:
    SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c '\
            chown -R root:gpio /sys/class/gpio && chmod -R 770 /sys/class/gpio;\
            chown -R root:gpio /sys/devices/virtual/gpio && chmod -R 770 /sys/devices/virtual/gpio;\
            chown -R root:gpio /sys$devpath && chmod -R 770 /sys$devpath\
    '"
    

    Any user who belongs in the gpio-group will be able to make use of the gpio-pins.
  • Dan O'Donovan
    Dan O'Donovan Administrator, Moderator, Emutex Posts: 241 admin
    I agree. When you are aware of the workaround that you need to adopt in your application, such as the one that you described above, then it generally works fine in that way.
  • WereCatf
    WereCatf New Member Posts: 201
    In a reply to myself: if any of you other readers wishes to try this, create a group called "gpio", copypaste my udev-rule to e.g. /etc/udev/rules.d/50-gpio.rules and reboot, and finally add your user to the gpio-group -- you should be all set and now you can use the gpio-pins from bash-scripts, libmraa etc. as a normal user. Just remember to add a small delay after exporting the pins you plan to use.
  • Peter
    Peter New Member Posts: 3
    Even the standard blink program didn't work for me.
    I tried a lot of fiddeling around with user rights, but none of that did work.
    Now I made the current user root by editing the /etc/passwd file, this seems to do the trick :)
    Can use the GPIO now without any problems.

    Thank you for all the replies.
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    Again thanks, I remember doing something like that earlier, although I don't remember having to run a shell...

    But if that works, it might be nice to for example update the MRAA library to maybe know this and see if group/owner can be setup properly, like add the export logical file to GPIO, and then have the code that does the write to export to create the new GPIO object try to recover if the next access fails, but doing a pause and then try again...
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    I played around with the blink-io example with the above udev rule and was able to make it work :D

    Couple of things that might want to be mentioned. At least on my system the group gpio did not exist, so in addition to adding the above rule file (/etc/udev/rules.d/50-gpio.rules), you need to create the group:

    sudo addgroup gpio

    And as mentioned use adduser to add your user(s) to the gpio group.

    I tested it using the example Blink-IO.cpp where the default code currently fails, when it tries to call
    gpio->dir(mraa::DIR_OUT);

    I had the code test for failure, than put in a sleep(1) and then tried again...

    If it was desired to get a work around back to the MRAA project, obviously would want a shorter delay, but not sure with MRAA what is desired: usleep? or if it should go to c++11, where I think it would be something like:
    std::this_thread::sleep_for(std::chrono::millseconds(250));

    But that would require changes to make script...
  • WereCatf
    WereCatf New Member Posts: 201
    Here's a snippet that should get you going, just call e.g.
    sleepMillis(250);
    
    -- doesn't require C++, either.
    #include<time.h>
    
    void sleepMillis(uint32_t millis) {
      struct timespec sleep;
      sleep.tv_sec = millis / 1000;
      sleep.tv_nsec = (millis % 1000) * 1000000L;
      while(clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, &sleep) && errno == EINTR);
    }
    
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    edited July 2017
    Thanks,

    Before I was using usleep, but noticed that it is depreciated to use nanasleep instead.
    So will probably update my current linux code base, where I have some Arduino like wrappers and convert from:
    #define delay(x) usleep((x)*1000)
    #define delayMicroseconds(us) usleep((us))
    To using somthing simlar to what you have here.

    But was sort of wondering MRAA might be moving toward c++11 or newer for any c++ stuff...

    Thanks again

    Edit: I updated my Raspberry Pi library code to use the clock_nanosleep (thanks again).
    Should mention you also need to include errno.h
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    Was wondering if anyone has also made any udev rules to allow an app to use SPI in user mode?

    Thanks
  • WereCatf
    WereCatf New Member Posts: 201
    Similar to the earlier udev-rule I posted, create the "spi" user-group, add your user to that group and place this line in /etc/udev/rules.d/50-spi.rules
    SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"
    
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    Thanks, I thought I would ask as a way to then have it documented as a possible thing to do.

    @dan (or other Up person) - Might be good to add this information to WIKI for the hardware
  • WereCatf
    WereCatf New Member Posts: 201
    I would've added it to the Wiki myself already, but I ain't got permissions to do that. This is to say, I do agree that this would be good stuff to have there, as quite many people seem to want this.
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    edited July 2017
    Thanks again, Yesterday was making progress, getting SPI and GPIO to work. Had the beginning of ILI5341 TFT display working (had it sort of working earlier on other boards like Edison).

    But looks like I am out of commission for awhile. I have my own version of HAT that I use with the board which has DC to DC converter for 12v to handle Dynamixel servos... Well this morning, I plugged the 12v wall wart into the wrong DC input (i.e. into the UP boards power input) and heard a pop and I think a little magic smoke... So now will need to probably wait until UP2 arrives... :(

    And looking around at other posts like: https://up-community.org/forum/general-discussion-up/1865-damaged-board#4814
    I assume it is DOA and not repairable.
  • WereCatf
    WereCatf New Member Posts: 201
    Well, that sucks! I'd be pissed at myself, if I made a similar mistake. :(

    Btw, if you mean ILI9341 (I am not aware of them having made ILI5341) you could also just use fbtft to run it as a regular framebuffer, allowing you to use any bog-standard graphical-framebuffer applications and even run X on it, if you wanted to. Just saying in case you weren't aware. If you were, then just ignore me :)
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    edited July 2017
    Sorry for the slight hijack of thread...
    WereCatf wrote:
    Well, that sucks! I'd be pissed at myself, if I made a similar mistake. :(
    I am! :angry:
    WereCatf wrote:
    Btw, if you mean ILI9341 (I am not aware of them having made ILI5341) you could also just use fbtft to run it as a regular framebuffer, allowing you to use any bog-standard graphical-framebuffer applications and even run X on it, if you wanted to.
    Thanks - Yes(ILI9341) was aware of fbtft, although I have not tried it with up... Have played it with Odroid boards. Not sure on UP if would need to rebuild kernel... to enable...

    What I was/am curious about is the performance of the spidev and the GPIO. And I then was going to experiment with with my HAT board to control PhantomX hexapod, would I gain anything having the two processors talk using SPI versus currently Hardware UART. The HAT has a Teensy 3.6 processor board on it.

    So looks like that is on hold for now. Will probably wait until Sep? for UP2 to arrive. In the mean time will probably move back over to using Odroid XU4.
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    Quick update: New UP board arrived yesterday. Now trying to remember all of the stuff I did earlier.
    So far I have Ubuntu 16.04 installed, plus updated kernel.

    Side notes: might help to have more stuff on installing ubuntu on wiki - like proper settings for rufus (use ubilinux instructions). Also what to do with scary warning about boot loader... Also I It appeared like I needed to update the wifi driver as was loosing connection. So built driver plus added the disable power... But now looks like I am able to boot headless again.

    Also created the udev rules mentioned in this thread, plus added groups and groups to user, and edited blink-io.c that if the set direction call fails to do a sleep and then retry again and that is now working again.
  • WereCatf
    WereCatf New Member Posts: 201
    KurtE wrote:
    Side notes: might help to have more stuff on installing ubuntu on wiki - like proper settings for rufus (use ubilinux instructions).

    Seems like a useful addition, but alas, the wiki is in a pretty bad shape, with very little useful content there. I'd like to contribute to it, but I am still waiting to receive my board and I do not have permission to edit the wiki anyways :unsure: Oh well, maybe they'll let me contribute one day.
  • DCleri
    DCleri Administrator, AAEON Posts: 1,213 admin
    Hi WereCatf,

    Thanks for your contributions to the UP Community.

    We are now working on the UP Wiki to update the software and the content to include information for UP Squared, UP Core and the new ubilinux 4.0.

    We are going also to enable you and other top contributors who are willing to participate to improve the Community documentation and tutorials.

    Thanks again for your effort!
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    Thanks @WereCatf and @dcleri - When I originally setup my first Up board, I thought there was a page that had the additional information (or was it a video?) Although I might have confused it with some other boards I have played with, like the Edison, or Odroids or RPI... But currently the only instructions for actually installing the Ubuntu from the Wiki (https://up-community.org/wiki/Ubuntu) has one line with a link to an ISO file:
    [Install the latest Trusty desktop-amd64 release

    There is information on the ubilinux page talking about installing from linux or windows. For windows it suggests using rufus and make sure some settings are...

    But back on subject: Using MRAA to do GPIO and permissions. Again I would highly recommend that the udev rules be added to the standard downloads.

    Or at a minimum, that information about them are given in Wiki in a few different places, like probably at the pinout page: .../wiki/Pinout and maybe with the MRAA page: .../wiki/MRAA/UPM

    Other side note: I have used MRAA for C/C++ interface on Edison... Not for Python or Java... Not sure if the software page, should mention more about MRAA or RPI GPIO are also valid for C/C++....

    But again back to MRAA. Wondering if some of the error recovery stuff should be added into MRAA (and maybe other frameworks as well).
    That is for example on my machine I updated the blink-io.c example to make it work.
    That is I know that if a GPIO init works, but the next call fails than maybe try waiting. Something like:
    mraa_gpio_context gpio;
        gpio = mraa_gpio_init(iopin);
        if (gpio == NULL) {
            fprintf(stderr, "Are you sure that pin%d you requested is valid on your platform?", iopin);
            exit(1);
        }
        printf("Initialised pin%d\n", iopin);
    
        // set direction to OUT
        r = mraa_gpio_dir(gpio, MRAA_GPIO_OUT);
        if (r != MRAA_SUCCESS) {
            // try delay to let system handle udev rules if appropriate
            sleepMillis(250);
            r = mraa_gpio_dir(gpio, MRAA_GPIO_OUT);
            if (r != MRAA_SUCCESS) {
                mraa_result_print(r);
            }
        }
    

    In the past in almost all cases (actually probably all), the next call I would do was set the direction of the pin. So for example wondering if maybe the mraa_gpio_dir code should check for error and if appropriate maybe do delay?

    Question is, with the end of products for Edison/Galileo... Will MRAA continue?

    But in the mean time back to playing :D
  • WereCatf
    WereCatf New Member Posts: 201
    edited August 2017
    Yes, as soon as I get write-permissions to the Wiki I can add the GPIO-permissions stuff there. As for the workaround: I do not believe it belongs in the libMRAA. The proper way of fixing this would be setting it up in the kernel so the permissions are correct the instant the pins are exported -- perhaps someone could take it up with the kernel-devs and submit a patch for it? Not me, I don't want to deal with those guys; they can be terribly toxic.

    Also, the way you're handling it is a tad inefficient. How about simply checking your EUID, e.g.:
    /* PSEUDOCODE-TIME */
    #include <unistd.h>
    #include <sys/types.h>
    #include<time.h>
    
    void sleepMillis(uint32_t millis) {
      struct timespec sleep;
      sleep.tv_sec = millis / 1000;
      sleep.tv_nsec = (millis % 1000) * 1000000L;
      while(clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, &sleep) && errno == EINTR);
    }
    
    void someFunction() {
      mraa_gpio_context gpio;
      gpio = mraa_gpio_init(iopin);
      if (gpio == NULL) {
        fprintf(stderr, "Are you sure that pin%d you requested is valid on your platform?\n", iopin);
        exit(1);
      }
    
      /* We use geteuid() to check which user's permissions
        we are trying to access the pins as, ie. if this is just a
        regular binary and you're non-root, it'll return non-zero.
        If this binary has the setuid-bit set, geteuid() will return
        0, ie. you're accessing the pins with root-permissions.
        Also, if you're simply running the binary as root,
        geteuid() will again return 0 for root, so there's no need
        to have the delay. */
    
      if(geteuid()!=0) sleepMillis(50);
    
      printf("Initialized pin%d\n", iopin);
    
      ... //INSERT CODE HERE
    }
    

    Also, for performance-reasons, it's a good idea to export all the pins you're planning to use at once, if possible, and then have a single delay -- there's no need to wait after every single pin if they're all exported at once:
    //INSERT HEADERS, SLEEPMILLIS() ETC.
    
    int numPinsToExport = 3
    int pinsToExport[] = 1, 5, 9;
    mraa_gpio_context mraaGpios[numPinsToExport];
    
    int exportPins() {
      int i=0;
      for(i=0; i<numPinsToExport; i++){
        mraaGpios[i] = mraa_gpio_init(pinsToExport[i]);
        if (mraaGpios[i] == NULL) {
          fprintf(stderr, "Encountered an error while exporting pin %d!\n", pinsToExport[i]);
          return -1;
        }
      printf("Exported pin %d.\n", pinsToExport[i]);
      }
    
      if(geteuid()!=0) sleepMillis(50);
      return 0;
    }
    
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    Thanks again,

    I totally agree with you that you are better off exporting all the pins first, such that you only need to wait once.

    I guess my question would be: In the code I put in, I only waited if I received an error on a call, in this case the call to set the direction to Input or Output. In that way I hopefully did not delay when it was not needed. Examples would be if I did like I used to do an potentially run an external script that would pre export those pins before I ran the actual program. Or potentially I would have other delays in the code.

    As for doing it in the Kernel, yes that would be great, But would probably need to get it done by more than one group. That is even with UP board not sure if same group doing Ubuntu as does Ubilinux. Then there is (or was) the group responsible for Edison and my guess is there will be no new kernel releases for those platforms.

    But now to get back to what I was playing with on the other board... Hopefully can reproduce as I had not uploaded my changes to github nor to my main dev machine.
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    edited August 2017
    Quick update: I continued doing some of the same tests I was trying to do with first board to test out SPI and GPIO. And I have a version of the Adafruit_ili9341 library ported over. Yes using FBTFT is probably a better approach, but more of a test.

    Probably should do new thread... But was wondering about spidev limits... In particular, my version of the library is sort of rolling it's own Frame buffer and only when you call update does it write the contents of the memory buffer to the display. There are some Preamble bytes, that need to be written out separate, as you need to change DC pin between the groups of 1 byte, 4 bytes, 1 byte, 4 bytes, 1 byte... And then you can write out the whole screen of bytes which consists of 153600 bytes.

    My earlier code assumed I could write a max of 4096 bytes per call to spidev. I believe you can get the buffer size by:
    cat /sys/module/spidev/parameters/bufsiz
    
    Which when I boot does show 4096 as expected.

    Looking around it appears like you should be able to change this. One location that talks about this is at the raspberrypi org forums viewtopic.php?p=309582
    So I tried the commands like they mentioned: 
    # rmmod spidev
    # modprobe spidev bufsiz=65536
    
    # cat /sys/module/spidev/parameters/bufsiz
    65536
    

    I also then edited my test program to change the count I passed to SPIdev to 65536, and the code ran, and the display updated, but looking at the output from my Logic Analyzer, I did not notice much of a difference in time. Nor much difference in how many clumps of data are output, I did an estimate counting part of the number of bytes that appeared to be output at once and my guess is it is about 256 bytes.

    You can see the whole screen update in the image


    With 64K writes it should write the main part of the display with 3 calls. But if you look at a portion of the update, you see the clumps i mentioned.


    So my guess is updating SPIdev in BUFSIZ parameter to a larger value probably won't gain much.

    Am I missing something obvious?
  • WereCatf
    WereCatf New Member Posts: 201
    I haven't dug into the specifics of Linux's SPI-subsystem, but as far as I know, setting bufsiz for spidev-module just alters the kernel-module's internal variables, but the actual hardware-limit of how many bytes the SoC can transfer in one go remains the same and, as such, the module still performs the transfer in bouts defined by the hardware-limit. An 8-bit limit, ie. 256 bytes, sounds about right -- it's the same for many other devices, like e.g. this one MIPS-based board I have.
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    Thanks again,

    Again this is more out of curiosity than necessity, If later I do decide to leave a display like this on the system, it would not be used for very graphic intense usage, but more for displaying feedback information, like maybe the current battery voltage, maybe a few touch screen buttons, to change a setting... Current code looks like it can display a whole page in about .2 seconds or about 5 frames per second. Which compared to the Edison is doing great, as compared to a Teensy like the T3.6, pretty slow...

    My guess is the hardware is not completely limited to 256 byte transfers? That is if I installed the FBTFT driver, my assumption is that it would work a lot faster...

    Actually the main thing I am doing this experimenting for, is to get an idea of how best to have the two boards communicate on my Trossen PhantomX hexapod. Where I have the Up board with a Teensy 3.6 hat. The teensy will be controlling the dynamixel servos and maybe IMU and ... Up till now I have mostly done this either using USB or by using the UART at 2 or 3 mbs, which is still probably the easiest approach. But SPI might work OK as I could probably easily limit packets of data to under 256 bytes.

    Thanks again
  • WereCatf
    WereCatf New Member Posts: 201
    You're using Up1, yes? Since the SPI-bus is limited to 25MHz, the theoretical maximum framerate you can push on the display is about 20 FPS. Maybe you could just optimize your code, I'm sure you could squeeze a few more frames per second out of it than just 5 FPS, but trying to hit the theoretical maximum from userland would be a fool's errand.
  • Kurt
    Kurt New Member Posts: 146 ✭✭
    Thanks,

    Yes - I currently have the SPI buss set to 8mhz, could go faster. I know on Teensy we push on some boards up to 30mhz... Some of the ili9341 displays don't like that fast, but 25 should work.

    As for code, the update display is pretty simple:
    void Adafruit_ILI9341::update(void) {
    
      uint8_t *prxData = (uint8_t*)_fbtft;
      //printf("CBPW: %d\n\r", CNT_BYTES_PER_WRITE);
      spi_begin();
      setAddr(0, 0, _width-1, _height-1);
      writecommand_cont(ILI9341_RAMWR);
      uint32_t count_bytes_left = 320*240*2;
      uint32_t cbWrite = CNT_BYTES_PER_WRITE;
    
      DCHigh();  // make sure we are in data mode
      while (count_bytes_left) {
        mraa_spi_transfer_buf(SPI, prxData, NULL, cbWrite);
        prxData += cbWrite;
        count_bytes_left -= cbWrite;
        if (count_bytes_left < cbWrite)
          cbWrite = count_bytes_left;
      }
      CSHigh();
      spi_end();
    }
    
    The SetAddr will does the code to set ROW and Column limits, These have to split up into separate writes, as the Command byte has to have the DC pin asserted and the data bytes have to have it not asserted... Then output the command byted for RAM write Again DC must be asserted, followed by the data...

    This is where the Teensy 3.x board work great (when you go to native registers), as they have a FIFO queue and each queue entry has information on which of up to 6 CS pins should be asserted or not, so DC pin on hardware CS pin and you can output at the full SPI buss speed... Sorry for shameless plug of Teensy.

    Maybe I will find my RPI TFT and temporarily enable the FBTFT and watch the buss to get an idea of how well it works.... I have the older Adafruit version with ili9341 and STMPE610
  • WereCatf
    WereCatf New Member Posts: 201
    KurtE wrote:
    Yes - I currently have the SPI buss set to 8mhz, could go faster. I know on Teensy we push on some boards up to 30mhz... Some of the ili9341 displays don't like that fast, but 25 should work.

    The theoretical maximum with an 8MHz SPI-bus is ~6.5 FPS, so you're pretty close to that already. Why don't you just set the bus to 25MHz? Also, the official documentation for ILI9341 says it supports up to 40MHz. Hell, I can perfectly well push pixels to the several ILI9341's I have at 80MHz without an issue, it's only when reading from the display that I have to drop to 40MHz. I do not believe the claim that the displays don't like going at 30MHz when even the official docs list a much higher spec; either you have faulty displays or there's something else at play.