1

I have a C library, which has gpio implementation. There's gpio_type which is target specific, each MCU has different definition for gpio_type. One of the functions in the library:

void gpio_init(gpio_type *object, int32_t pin);

I want to write abstraction of Gpio object in Rust, using C library functions. Therefore need something like opaque pointer type (in C++ I would just create a member variable with type: gpio_type). I figured I would create an empty enum (or struct), allocate a space needed for the object and transmute it to match the type in C layer.

pub enum gpio_type {}

#[link(name = "gpio_lib", kind = "static")]
extern {
    pub fn gpio_init(obj: *mut gpio_type, value: i32);
}

pub struct Gpio {
    gpio : *mut gpio_type,
}

impl Gpio {
    pub fn new(pin: u32) -> Gpio {
        unsafe {
            let mut gpio_ptr : &'static [u8; 4] = init(); // size of gpio in C is 4 bytes for one target, will be changed later to obtain it dynamically
            let gpio_out = Gpio { gpio: transmute(gpio_ptr)};
            gpio_init(gpio_out.gpio, pin);
            gpio_out
        }
    }
}

This targets embedded devices, therefore no std, no libc. I don't want to redefine gpio_type for each target in rust (copy the C declaration for each target), looking for something to just allocate memory for the object which C will handle.

The following snippet below produces pointer to address 0 according to disassembly. Disassembly for Gpio new method:

 45c:   b580        push    {r7, lr}
 45e:   466f        mov r7, sp
 460:   4601        mov r1, r0
 462:   2000        movs    r0, #0
 464:   f000 fae6   bl  a34 <gpio_init>
 468:   2000        movs    r0, #0
 46a:   bd80        pop {r7, pc}

Any ideas why 462 is 0 ?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
c0170
  • 58
  • 4
  • 1
    Wild stab in the dark: `gpio_type` is zero-sized, which can cause some weird stuff to happen. Try using a `struct gpio_type(u8)` instead to give it a size and see what happens. In fact, there's an opaque `c_void` type in the `libc` crate, which has two private enum variants, presumably to avoid the same issue. – DK. Jan 26 '15 at 17:00

1 Answers1

3

looking for something to just allocate memory for the object which C will handle

What about something like this? Give the struct an actual size (in this case by giving it a fixed-size array of byte-sized items), allocate that space on the heap, then treat that as a raw pointer.

use std::mem;

#[allow(missing_copy_implementations)]
pub struct Gpio([u8; 4]);

impl Gpio {
    fn new() -> Gpio { Gpio([0,0,0,0]) }
}

fn main() {
    // Allocate some bytes and get a raw pointer
    let a: *mut u8 = unsafe { mem::transmute(Box::new(Gpio::new())) };

    // Use it here!

    // When done... back to a box
    let b: Box<Gpio> = unsafe { mem::transmute(a) };

    // Now it will be dropped automatically (and free the allocated memory)

    // Or you can be explicit
    drop(b);
}

However, I'd suggest doing something like this; it's a lot more obvious and doesn't need a heap allocation:

#[allow(missing_copy_implementations)]
pub struct Gpio([u8; 4]);

impl Gpio {
    fn new() -> Gpio { Gpio([0,0,0,0]) }

    fn as_mut_ptr(&mut self) -> *mut u8 {
        self.0.as_mut_ptr()
    }
}

fn main() {
    let mut g = Gpio::new();
    let b = g.as_mut_ptr();
}

As a bonus, you get a nice place to hang some methods on. Potentially as_mut_ptr wouldn't need to be public, and could be hidden behind public methods on the Gpio struct.

(might also be able to use uninitialized instead of [0,0,0,0])

An expanded example of the second suggestion

// This depends on your library, check the FFI guide for details
extern {
    fn gpio_init(gpio: *mut u8, pin: u8);
    fn gpio_pin_on(gpio: *mut u8);
    fn gpio_pin_off(gpio: *mut u8);
}

#[allow(missing_copy_implementations)]
pub struct Gpio([u8; 4]);

impl Gpio {
    fn new(pin: u8) -> Gpio {
        let mut g = Gpio([0,0,0,0]);
        g.init(pin);
        g
    }

    fn as_mut_ptr(&mut self) -> *mut u8 {
        self.0.as_mut_ptr()
    }

    fn init(&mut self, pin: u8) { unsafe { gpio_init(self.as_mut_ptr(), pin) } }
    pub fn on(&mut self) { unsafe { gpio_pin_on(self.as_mut_ptr()) } }
    pub fn off(&mut self) { unsafe { gpio_pin_off(self.as_mut_ptr()) } }
}

static BLUE_LED_PIN: u8 = 0x4;

fn main() {
    let mut g = Gpio::new(BLUE_LED_PIN);
    g.on();
    g.off();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thanks for some pointers but still does not solve the above problem. C library is using gpio_type object (always the first parameter). The rust layer needs to define this gpio_type. How big is the gpio_type object? Depends on the target. I can retrieve the number in the runtime or via the global C constant. I would like to create an array of u8, cast it to gpio_type object, so can be used within any gpio function in the C layer. I emphasized this is for embedded devices, preferrably no std, libc is neither defined yet. – c0170 Jan 27 '15 at 12:13
  • @0xc0170 Could you expand on a bit on what is missing so that I can add it? My example *does* create an array of `u8` of length 4, then passes a pointer to that chunk of memory to the gpio functions. As you point out, the `4` is going to be target platform specific, but you seem to indicate that you already know how to deal with that. – Shepmaster Jan 27 '15 at 14:05
  • It should have a member gpio which would be allocated space. So it can be expanded by other members. How is it with *mut u8 and C function expects *gpio_type object, will that link? We are getting closer – c0170 Jan 27 '15 at 18:08
  • In C, a pointer is a pointer. So long as it points to a chunk of memory that is available to the process (either on the stack or on the heap) and has an appropriate size and alignment, then it should work. My struct `Gpio` is a [*tuple struct*](http://doc.rust-lang.org/book/compound-data-types.html#tuple-structs-and-newtypes) which has a single (unnamed) member that occupies 4 bytes. That's what `self.0` is referring to. Can you explain more about what you mean by "expanded by other members"? – Shepmaster Jan 27 '15 at 18:13
  • Gpio is not a good example, Spi - should contain an allocation for C library spi struct, then probably settings of a transfer like frequency, bit width... – c0170 Jan 27 '15 at 19:45
  • @0xc0170 But wouldn't the C struct already be the authoritative source for that data, and you should just use the accessors provided by the library? If you want the Rust structure to mirror the C structure, that's a *very* different question, but you said you don't want to rewrite it for every architecture. – Shepmaster Jan 27 '15 at 19:55
  • True, dont mirror it. It was just as an illustration, a struct would be more appropriate than tuple struct. Could you update the extended example? I am failing in "casting" the array to *mut u8. – c0170 Jan 27 '15 at 20:41
  • @0xc0170 You don't have to cast it - just refer to `fn as_mut_ptr` that is defined on `Gpio`. That's how you go from the fixed-size array to a mutable slice to a mutable pointer. – Shepmaster Jan 27 '15 at 22:07