jawline / ocamlGpiod

Ocaml binding auto-generation for libgpiod
BSD 3-Clause "New" or "Revised" License
7 stars 0 forks source link

Should Gpiod structs be defined and sealed by lib - or there exist an example? #2

Open rand00 opened 11 months ago

rand00 commented 11 months ago

I needed to use the Gpiod.line_event_read_multiple and found that I needed to construct the C struct of the Gpiod.gpiod_line_event myself, to read the different types of events.

I didn't know Ctypes much beforehand, so needed to take a dive into it to find out what to do - should the struct maybe be defined and sealed by the library? Otherwise I think it would be nice with an example for users.

The Cstruct I ended up with was:

module Gpiod_struct = struct

  module Timespec = struct

    type t

    let t : t Ctypes.structure Ctypes.typ = Ctypes.structure "timespec"

    let tv_sec  = Ctypes.field t "tv_sec" PosixTypes.time_t

    let tv_nsec = Ctypes.field t "tv_nsec" Ctypes.long 

    let () = Ctypes.seal t

  end

  module Line_event = struct

    let t = Gpiod.gpiod_line_event

    let ts = Ctypes.field t "ts" Timespec.t

    let event_type = Ctypes.field t "event_type" Ctypes.int

    let () = Ctypes.seal t

  end

end
jawline commented 11 months ago

I think this is a good idea, I didn't put much effort into wrapping the input structures when I wrote the library. I'll try and find some time to update it.

rand00 commented 11 months ago

Earlier today I also had some success defining a full bulk loop. I needed the following struct for gpiod_line_bulk:

module Line_bulk = struct

  let t = Gpiod.gpiod_line_bulk

  let lines = Ctypes.field t "lines" (Ctypes.ptr_opt Line.t)

  let num_lines = Ctypes.field t "num_lines" Ctypes.uint

  let () = Ctypes.seal t

end

And also needed these procedures / getters (couldn't bind to the field-getters for gpiod_line_bulk, so defined them in Ctypes):

(*
   int gpiod_line_event_wait_bulk (
     struct gpiod_line_bulk *   bulk,
     const struct timespec *    timeout,
     struct gpiod_line_bulk *   event_bulk 
   )    
*)
let line_event_wait_bulk =
  Foreign.foreign "gpiod_line_event_wait_bulk" 
    Ctypes.(
      ptr_opt Struct.Line_bulk.t
      @-> ptr_opt Struct.Timespec.t
      @-> ptr_opt Struct.Line_bulk.t
      @-> returning int
    )

(*> fix?: Error: undefined symbol*)
(*static unsigned int gpiod_line_bulk_num_lines (
    struct gpiod_line_bulk * bulk
  )
*)
(* let line_bulk_num_lines = *)
(*   Foreign.foreign "gpiod_line_bulk_num_lines" *)
(*     Ctypes.( *)
(*       ptr_opt Struct.Line_bulk.t *)
(*       @-> returning uint *)
(*     ) *)

let line_bulk_num_lines line_bulk =
  Ctypes.getf
    line_bulk
    Struct.Line_bulk.num_lines

(*
   static struct gpiod_line* gpiod_line_bulk_get_line (
     struct gpiod_line_bulk * bulk,
     unsigned int             offset 
   )        
*)
(* let line_bulk_get_line = *)
(*   Foreign.foreign "gpiod_line_bulk_get_line" *)
(*     Ctypes.( *)
(*       ptr_opt Struct.Line_bulk.t *)
(*       @-> uint  *)
(*       @-> returning (ptr_opt Struct.Line.t) *)
(*     ) *)

let line_bulk_get_line line_bulk ~len i =
  let lines_ptr =
    Ctypes.getf line_bulk Struct.Line_bulk.lines
    |> CCOption.get_exn_or "ERROR: Gpiod_aux.line_bulk_get_line: lines was None"
  in
  let lines_arr = Ctypes.CArray.from_ptr lines_ptr len in
  Ctypes.CArray.get lines_arr i

(*
   int gpiod_chip_get_lines (
     struct gpiod_chip *  chip,
     unsigned int *       offsets,
     unsigned int         num_offsets,
     struct gpiod_line_bulk *  bulk
   )
*)
let chip_get_lines =
  Foreign.foreign "gpiod_chip_get_lines"
    Ctypes.(
      ptr_opt Struct.Chip.t
      @-> ptr_opt uint
      @-> uint
      @-> ptr_opt Struct.Line_bulk.t
      @-> returning int
    )

(*
  int gpiod_line_get_value_bulk (
    struct gpiod_line_bulk *  bulk,
    int *                     values 
  )
*)
let line_get_value_bulk =
  Foreign.foreign "gpiod_line_get_value_bulk"
    Ctypes.(
      ptr_opt Struct.Line_bulk.t
      @-> ptr_opt int
      @-> returning int
    )

It's not fully tested yet, but seemed to work. Btw. I e.g. rebound the Gpiod.line_get_value_bulk because it had a ptr void instead of ptr int as second argument - why does it have this interface?