1use futures::executor::block_on;
39use pico_de_gallo_lib::{
40 self as lib, AdcChannel, AdcError, GpioError, I2cError, OneWireError, PicoDeGalloError,
41 PwmError, SpiError, UartError,
42};
43use std::ffi::CStr;
44use std::os::raw::c_char;
45
46pub struct PicoDeGallo(lib::PicoDeGallo);
53
54const _: () = {
58 const fn assert_send_sync<T: Send + Sync>() {}
59 assert_send_sync::<PicoDeGallo>();
60};
61
62#[repr(i32)]
69#[derive(Debug, PartialEq)]
70pub enum Status {
71 Ok = 0,
73 I2cReadFailed = -1,
75 I2cWriteFailed = -2,
77 InvalidResponse = -3,
79 Uninitialized = -4,
81 InvalidArgument = -5,
83 PingFailed = -6,
85 SpiReadFailed = -7,
87 SpiWriteFailed = -8,
89 SpiFlushFailed = -9,
91 GpioGetFailed = -10,
93 GpioPutFailed = -11,
95 GpioWaitFailed = -12,
97 SetConfigFailed = -13,
99 VersionFailed = -14,
101 I2cWriteReadFailed = -15,
103 I2cSetConfigFailed = -16,
105 SpiSetConfigFailed = -17,
107 I2cNack = -18,
109 I2cBusError = -19,
111 I2cArbitrationLoss = -20,
113 I2cOverrun = -21,
115 BufferTooLong = -22,
117 I2cAddressOutOfRange = -23,
119 GpioInvalidPin = -24,
121 CommsFailed = -25,
123 I2cScanFailed = -26,
125 GpioSetConfigFailed = -27,
127 GpioWrongDirection = -28,
129 I2cGetConfigFailed = -29,
131 SpiGetConfigFailed = -30,
133 UartReadFailed = -31,
135 UartWriteFailed = -32,
137 UartFlushFailed = -33,
139 UartOverrun = -34,
141 UartBreak = -35,
143 UartParity = -36,
145 UartFraming = -37,
147 UartInvalidBaudRate = -38,
149 UartSetConfigFailed = -39,
151 UartGetConfigFailed = -40,
153 PwmSetDutyCycleFailed = -41,
155 PwmGetDutyCycleFailed = -42,
157 PwmEnableFailed = -43,
159 PwmDisableFailed = -44,
161 PwmSetConfigFailed = -45,
163 PwmGetConfigFailed = -46,
165 PwmInvalidChannel = -47,
167 PwmInvalidDutyCycle = -48,
169 PwmInvalidConfiguration = -49,
171 AdcReadFailed = -50,
173 AdcGetConfigFailed = -51,
175 AdcConversionFailed = -52,
177 GpioPinMonitored = -53,
179 GpioPinNotMonitored = -54,
181 GpioSubscribeFailed = -55,
183 GpioUnsubscribeFailed = -56,
185 OneWireNoPresence = -57,
187 OneWireBusError = -58,
189 OneWireReadFailed = -59,
191 OneWireWriteFailed = -60,
193 OneWireSearchFailed = -61,
195 DeviceInfoFailed = -62,
197 SchemaMismatch = -63,
199 LegacyFirmware = -64,
201 Unsupported = -65,
203}
204
205fn i2c_error_to_status(e: PicoDeGalloError<I2cError>) -> Status {
208 match e {
209 PicoDeGalloError::Endpoint(I2cError::NoAcknowledge) => Status::I2cNack,
210 PicoDeGalloError::Endpoint(I2cError::Bus) => Status::I2cBusError,
211 PicoDeGalloError::Endpoint(I2cError::ArbitrationLoss) => Status::I2cArbitrationLoss,
212 PicoDeGalloError::Endpoint(I2cError::Overrun) => Status::I2cOverrun,
213 PicoDeGalloError::Endpoint(I2cError::BufferTooLong) => Status::BufferTooLong,
214 PicoDeGalloError::Endpoint(I2cError::AddressOutOfRange) => Status::I2cAddressOutOfRange,
215 PicoDeGalloError::Endpoint(I2cError::Other) => Status::I2cReadFailed,
216 PicoDeGalloError::Comms(_) => Status::CommsFailed,
217 }
218}
219
220fn spi_error_to_status(e: PicoDeGalloError<SpiError>) -> Status {
221 match e {
222 PicoDeGalloError::Endpoint(SpiError::BufferTooLong) => Status::BufferTooLong,
223 PicoDeGalloError::Endpoint(SpiError::Other) => Status::SpiReadFailed,
224 PicoDeGalloError::Comms(_) => Status::CommsFailed,
225 }
226}
227
228fn gpio_error_to_status(e: PicoDeGalloError<GpioError>) -> Status {
229 match e {
230 PicoDeGalloError::Endpoint(GpioError::InvalidPin) => Status::GpioInvalidPin,
231 PicoDeGalloError::Endpoint(GpioError::WrongDirection) => Status::GpioWrongDirection,
232 PicoDeGalloError::Endpoint(GpioError::PinMonitored) => Status::GpioPinMonitored,
233 PicoDeGalloError::Endpoint(GpioError::PinNotMonitored) => Status::GpioPinNotMonitored,
234 PicoDeGalloError::Endpoint(GpioError::Other) => Status::GpioGetFailed,
235 PicoDeGalloError::Comms(_) => Status::CommsFailed,
236 }
237}
238
239fn uart_error_to_status(e: PicoDeGalloError<UartError>) -> Status {
240 match e {
241 PicoDeGalloError::Endpoint(UartError::BufferTooLong) => Status::BufferTooLong,
242 PicoDeGalloError::Endpoint(UartError::Overrun) => Status::UartOverrun,
243 PicoDeGalloError::Endpoint(UartError::Break) => Status::UartBreak,
244 PicoDeGalloError::Endpoint(UartError::Parity) => Status::UartParity,
245 PicoDeGalloError::Endpoint(UartError::Framing) => Status::UartFraming,
246 PicoDeGalloError::Endpoint(UartError::InvalidBaudRate) => Status::UartInvalidBaudRate,
247 PicoDeGalloError::Endpoint(UartError::Other) => Status::UartReadFailed,
248 PicoDeGalloError::Endpoint(UartError::Unsupported) => Status::Unsupported,
249 PicoDeGalloError::Comms(_) => Status::CommsFailed,
250 }
251}
252
253fn pwm_error_to_status(e: PicoDeGalloError<PwmError>) -> Status {
254 match e {
255 PicoDeGalloError::Endpoint(PwmError::InvalidChannel) => Status::PwmInvalidChannel,
256 PicoDeGalloError::Endpoint(PwmError::InvalidDutyCycle) => Status::PwmInvalidDutyCycle,
257 PicoDeGalloError::Endpoint(PwmError::InvalidConfiguration) => {
258 Status::PwmInvalidConfiguration
259 }
260 PicoDeGalloError::Endpoint(PwmError::Other) => Status::PwmSetDutyCycleFailed,
261 PicoDeGalloError::Comms(_) => Status::CommsFailed,
262 }
263}
264
265fn adc_error_to_status(e: PicoDeGalloError<AdcError>) -> Status {
266 match e {
267 PicoDeGalloError::Endpoint(AdcError::ConversionFailed) => Status::AdcConversionFailed,
268 PicoDeGalloError::Endpoint(AdcError::Other) => Status::AdcReadFailed,
269 PicoDeGalloError::Endpoint(AdcError::Unsupported) => Status::Unsupported,
270 PicoDeGalloError::Comms(_) => Status::CommsFailed,
271 }
272}
273
274fn onewire_error_to_status(e: PicoDeGalloError<OneWireError>) -> Status {
275 match e {
276 PicoDeGalloError::Endpoint(OneWireError::NoPresence) => Status::OneWireNoPresence,
277 PicoDeGalloError::Endpoint(OneWireError::BusError) => Status::OneWireBusError,
278 PicoDeGalloError::Endpoint(OneWireError::BufferTooLong) => Status::BufferTooLong,
279 PicoDeGalloError::Endpoint(OneWireError::Other) => Status::OneWireReadFailed,
280 PicoDeGalloError::Endpoint(OneWireError::Unsupported) => Status::Unsupported,
281 PicoDeGalloError::Comms(_) => Status::CommsFailed,
282 }
283}
284
285#[unsafe(no_mangle)]
292pub extern "C" fn gallo_init() -> *const PicoDeGallo {
293 let gallo = Box::new(PicoDeGallo(lib::PicoDeGallo::new()));
294
295 Box::into_raw(gallo) as *const PicoDeGallo
296}
297
298#[unsafe(no_mangle)]
309pub unsafe extern "C" fn gallo_init_with_serial_number(
310 c_serial_number: *const c_char,
311) -> *const PicoDeGallo {
312 if c_serial_number.is_null() {
313 eprintln!("NULL serial number received");
314 return std::ptr::null();
315 }
316
317 let serial_number = unsafe { CStr::from_ptr(c_serial_number).to_str() };
320
321 if serial_number.is_err() {
322 eprintln!("Invalid UTF-8 string");
323 return std::ptr::null();
324 }
325
326 let gallo = Box::new(PicoDeGallo(lib::PicoDeGallo::new_with_serial_number(
327 serial_number.unwrap(),
328 )));
329
330 Box::into_raw(gallo) as *const PicoDeGallo
331}
332
333#[unsafe(no_mangle)]
340pub unsafe extern "C" fn gallo_free(gallo: *const PicoDeGallo) {
341 if !gallo.is_null() {
342 drop(unsafe { Box::from_raw(gallo as *mut PicoDeGallo) });
345 }
346}
347
348#[unsafe(no_mangle)]
359pub unsafe extern "C" fn gallo_ping(gallo: *mut PicoDeGallo, id: *mut u32) -> Status {
360 if gallo.is_null() {
361 eprintln!("Unexpected NULL context");
362 return Status::Uninitialized;
363 }
364
365 if id.is_null() {
366 eprintln!("Unexpected NULL id pointer");
367 return Status::InvalidArgument;
368 }
369
370 let gallo = unsafe { &*gallo };
373
374 let id_val = unsafe { *id };
376
377 let result = block_on(gallo.0.ping(id_val));
378 match result {
379 Ok(back) => {
380 unsafe { *id = back };
381 Status::Ok
382 }
383 Err(_) => Status::PingFailed,
384 }
385}
386
387#[unsafe(no_mangle)]
399pub unsafe extern "C" fn gallo_i2c_read(
400 gallo: *mut PicoDeGallo,
401 address: u8,
402 buf: *mut u8,
403 len: usize,
404) -> Status {
405 if gallo.is_null() {
406 eprintln!("Unexpected NULL context");
407 return Status::Uninitialized;
408 }
409
410 if buf.is_null() {
411 eprintln!("Unexpected NULL buffer");
412 return Status::InvalidArgument;
413 }
414
415 if len > u16::MAX.into() {
416 eprintln!("Buffer is too large");
417 return Status::InvalidArgument;
418 }
419
420 let gallo = unsafe { &*gallo };
423
424 let buf = unsafe { std::slice::from_raw_parts_mut(buf, len) };
426
427 let result = block_on(gallo.0.i2c_read(address, len as u16));
428
429 match result {
430 Ok(data) => {
431 if data.len() != buf.len() {
432 eprintln!(
433 "Firmware returned {} bytes, expected {}",
434 data.len(),
435 buf.len()
436 );
437 return Status::InvalidResponse;
438 }
439 buf.copy_from_slice(&data);
440 Status::Ok
441 }
442 Err(e) => i2c_error_to_status(e),
443 }
444}
445
446#[unsafe(no_mangle)]
456pub unsafe extern "C" fn gallo_i2c_write(
457 gallo: *mut PicoDeGallo,
458 address: u8,
459 buf: *const u8,
460 len: usize,
461) -> Status {
462 if gallo.is_null() {
463 eprintln!("Unexpected NULL context");
464 return Status::Uninitialized;
465 }
466
467 if buf.is_null() {
468 eprintln!("Unexpected NULL buffer");
469 return Status::InvalidArgument;
470 }
471
472 if len > u16::MAX.into() {
473 eprintln!("Buffer is too large");
474 return Status::InvalidArgument;
475 }
476
477 let gallo = unsafe { &*gallo };
480
481 let buf = unsafe { std::slice::from_raw_parts(buf, len) };
483
484 let result = block_on(gallo.0.i2c_write(address, buf));
485
486 match result {
487 Ok(()) => Status::Ok,
488 Err(e) => i2c_error_to_status(e),
489 }
490}
491
492#[unsafe(no_mangle)]
502pub unsafe extern "C" fn gallo_i2c_write_read(
503 gallo: *mut PicoDeGallo,
504 address: u8,
505 txbuf: *const u8,
506 txlen: usize,
507 rxbuf: *mut u8,
508 rxlen: usize,
509) -> Status {
510 if gallo.is_null() {
511 eprintln!("Unexpected NULL context");
512 return Status::Uninitialized;
513 }
514
515 if txbuf.is_null() || rxbuf.is_null() {
516 eprintln!("Unexpected NULL buffer");
517 return Status::InvalidArgument;
518 }
519
520 if txlen > u16::MAX.into() || rxlen > u16::MAX.into() {
521 eprintln!("Buffer is too large");
522 return Status::InvalidArgument;
523 }
524
525 let gallo = unsafe { &*gallo };
528
529 let txbuf = unsafe { std::slice::from_raw_parts(txbuf, txlen) };
531
532 let rxbuf = unsafe { std::slice::from_raw_parts_mut(rxbuf, rxlen) };
534
535 let result = block_on(gallo.0.i2c_write_read(address, txbuf, rxlen as u16));
536 match result {
537 Ok(data) => {
538 if data.len() != rxbuf.len() {
539 eprintln!(
540 "Firmware returned {} bytes, expected {}",
541 data.len(),
542 rxbuf.len()
543 );
544 return Status::InvalidResponse;
545 }
546 rxbuf.copy_from_slice(&data);
547 Status::Ok
548 }
549 Err(e) => i2c_error_to_status(e),
550 }
551}
552
553#[unsafe(no_mangle)]
571pub unsafe extern "C" fn gallo_i2c_scan(
572 gallo: *mut PicoDeGallo,
573 include_reserved: bool,
574 buf: *mut u8,
575 buf_len: usize,
576 found: *mut usize,
577) -> Status {
578 if gallo.is_null() {
579 eprintln!("Unexpected NULL context");
580 return Status::Uninitialized;
581 }
582
583 if buf.is_null() || found.is_null() {
584 eprintln!("Unexpected NULL pointer");
585 return Status::InvalidArgument;
586 }
587
588 let gallo = unsafe { &*gallo };
591
592 let buf = unsafe { std::slice::from_raw_parts_mut(buf, buf_len) };
594
595 let result = block_on(gallo.0.i2c_scan(include_reserved));
596
597 match result {
598 Ok(addresses) => {
599 let copy_len = addresses.len().min(buf.len());
600 buf[..copy_len].copy_from_slice(&addresses[..copy_len]);
601 unsafe { *found = addresses.len() };
602 Status::Ok
603 }
604 Err(e) => i2c_error_to_status(e),
605 }
606}
607
608#[unsafe(no_mangle)]
620pub unsafe extern "C" fn gallo_spi_read(
621 gallo: *mut PicoDeGallo,
622 buf: *mut u8,
623 len: usize,
624) -> Status {
625 if gallo.is_null() {
626 eprintln!("Unexpected NULL context");
627 return Status::Uninitialized;
628 }
629
630 if buf.is_null() {
631 eprintln!("Unexpected NULL buffer");
632 return Status::InvalidArgument;
633 }
634
635 if len > u16::MAX.into() {
636 eprintln!("Buffer is too large");
637 return Status::InvalidArgument;
638 }
639
640 let gallo = unsafe { &*gallo };
643
644 let buf = unsafe { std::slice::from_raw_parts_mut(buf, len) };
646
647 let result = block_on(gallo.0.spi_read(len as u16));
648
649 match result {
650 Ok(data) => {
651 if data.len() != buf.len() {
652 eprintln!(
653 "Firmware returned {} bytes, expected {}",
654 data.len(),
655 buf.len()
656 );
657 return Status::InvalidResponse;
658 }
659 buf.copy_from_slice(&data);
660 Status::Ok
661 }
662 Err(e) => spi_error_to_status(e),
663 }
664}
665
666#[unsafe(no_mangle)]
676pub unsafe extern "C" fn gallo_spi_write(
677 gallo: *mut PicoDeGallo,
678 buf: *const u8,
679 len: usize,
680) -> Status {
681 if gallo.is_null() {
682 eprintln!("Unexpected NULL context");
683 return Status::Uninitialized;
684 }
685
686 if buf.is_null() {
687 eprintln!("Unexpected NULL buffer");
688 return Status::InvalidArgument;
689 }
690
691 if len > u16::MAX.into() {
692 eprintln!("Buffer is too large");
693 return Status::InvalidArgument;
694 }
695
696 let gallo = unsafe { &*gallo };
699
700 let buf = unsafe { std::slice::from_raw_parts(buf, len) };
702
703 let result = block_on(gallo.0.spi_write(buf));
704
705 match result {
706 Ok(()) => Status::Ok,
707 Err(e) => spi_error_to_status(e),
708 }
709}
710
711#[unsafe(no_mangle)]
720pub unsafe extern "C" fn gallo_spi_flush(gallo: *mut PicoDeGallo) -> Status {
721 if gallo.is_null() {
722 eprintln!("Unexpected NULL context");
723 return Status::Uninitialized;
724 }
725
726 let gallo = unsafe { &*gallo };
729
730 let result = block_on(gallo.0.spi_flush());
731
732 match result {
733 Ok(()) => Status::Ok,
734 Err(e) => spi_error_to_status(e),
735 }
736}
737
738#[unsafe(no_mangle)]
749pub unsafe extern "C" fn gallo_gpio_get(
750 gallo: *mut PicoDeGallo,
751 pin: u8,
752 state: *mut bool,
753) -> Status {
754 if gallo.is_null() {
755 eprintln!("Unexpected NULL context");
756 return Status::Uninitialized;
757 }
758
759 if state.is_null() {
760 eprintln!("Unexpected NULL state pointer");
761 return Status::InvalidArgument;
762 }
763
764 let gallo = unsafe { &*gallo };
767
768 let result = block_on(gallo.0.gpio_get(pin));
769
770 match result {
771 Ok(s) => {
772 unsafe { *state = s == lib::GpioState::High };
773 Status::Ok
774 }
775 Err(e) => gpio_error_to_status(e),
776 }
777}
778
779#[unsafe(no_mangle)]
788pub unsafe extern "C" fn gallo_gpio_put(gallo: *mut PicoDeGallo, pin: u8, state: bool) -> Status {
789 if gallo.is_null() {
790 eprintln!("Unexpected NULL context");
791 return Status::Uninitialized;
792 }
793
794 let gallo = unsafe { &*gallo };
797
798 let s = if state {
799 lib::GpioState::High
800 } else {
801 lib::GpioState::Low
802 };
803 let result = block_on(gallo.0.gpio_put(pin, s));
804
805 match result {
806 Ok(()) => Status::Ok,
807 Err(e) => gpio_error_to_status(e),
808 }
809}
810
811#[unsafe(no_mangle)]
820pub unsafe extern "C" fn gallo_gpio_wait_for_high(gallo: *mut PicoDeGallo, pin: u8) -> Status {
821 if gallo.is_null() {
822 eprintln!("Unexpected NULL context");
823 return Status::Uninitialized;
824 }
825
826 let gallo = unsafe { &*gallo };
829
830 let result = block_on(gallo.0.gpio_wait_for_high(pin));
831
832 match result {
833 Ok(()) => Status::Ok,
834 Err(e) => gpio_error_to_status(e),
835 }
836}
837
838#[unsafe(no_mangle)]
847pub unsafe extern "C" fn gallo_gpio_wait_for_low(gallo: *mut PicoDeGallo, pin: u8) -> Status {
848 if gallo.is_null() {
849 eprintln!("Unexpected NULL context");
850 return Status::Uninitialized;
851 }
852
853 let gallo = unsafe { &*gallo };
856
857 let result = block_on(gallo.0.gpio_wait_for_low(pin));
858
859 match result {
860 Ok(()) => Status::Ok,
861 Err(e) => gpio_error_to_status(e),
862 }
863}
864
865#[unsafe(no_mangle)]
874pub unsafe extern "C" fn gallo_gpio_wait_for_rising_edge(
875 gallo: *mut PicoDeGallo,
876 pin: u8,
877) -> Status {
878 if gallo.is_null() {
879 eprintln!("Unexpected NULL context");
880 return Status::Uninitialized;
881 }
882
883 let gallo = unsafe { &*gallo };
886
887 let result = block_on(gallo.0.gpio_wait_for_rising_edge(pin));
888
889 match result {
890 Ok(()) => Status::Ok,
891 Err(e) => gpio_error_to_status(e),
892 }
893}
894
895#[unsafe(no_mangle)]
904pub unsafe extern "C" fn gallo_gpio_wait_for_falling_edge(
905 gallo: *mut PicoDeGallo,
906 pin: u8,
907) -> Status {
908 if gallo.is_null() {
909 eprintln!("Unexpected NULL context");
910 return Status::Uninitialized;
911 }
912
913 let gallo = unsafe { &*gallo };
916
917 let result = block_on(gallo.0.gpio_wait_for_falling_edge(pin));
918
919 match result {
920 Ok(()) => Status::Ok,
921 Err(e) => gpio_error_to_status(e),
922 }
923}
924
925#[unsafe(no_mangle)]
934pub unsafe extern "C" fn gallo_gpio_wait_for_any_edge(gallo: *mut PicoDeGallo, pin: u8) -> Status {
935 if gallo.is_null() {
936 eprintln!("Unexpected NULL context");
937 return Status::Uninitialized;
938 }
939
940 let gallo = unsafe { &*gallo };
943
944 let result = block_on(gallo.0.gpio_wait_for_any_edge(pin));
945
946 match result {
947 Ok(()) => Status::Ok,
948 Err(e) => gpio_error_to_status(e),
949 }
950}
951
952#[unsafe(no_mangle)]
971pub unsafe extern "C" fn gallo_gpio_set_config(
972 gallo: *mut PicoDeGallo,
973 pin: u8,
974 direction: u8,
975 pull: u8,
976) -> Status {
977 if gallo.is_null() {
978 eprintln!("Unexpected NULL context");
979 return Status::Uninitialized;
980 }
981
982 let dir = match direction {
983 0 => lib::GpioDirection::Input,
984 1 => lib::GpioDirection::Output,
985 _ => {
986 eprintln!("Invalid direction value: {direction}");
987 return Status::InvalidArgument;
988 }
989 };
990
991 let pull_cfg = match pull {
992 0 => lib::GpioPull::None,
993 1 => lib::GpioPull::Up,
994 2 => lib::GpioPull::Down,
995 _ => {
996 eprintln!("Invalid pull value: {pull}");
997 return Status::InvalidArgument;
998 }
999 };
1000
1001 let gallo = unsafe { &*gallo };
1004
1005 let result = block_on(gallo.0.gpio_set_config(pin, dir, pull_cfg));
1006
1007 match result {
1008 Ok(()) => Status::Ok,
1009 Err(e) => gpio_error_to_status(e),
1010 }
1011}
1012
1013#[unsafe(no_mangle)]
1030pub unsafe extern "C" fn gallo_gpio_subscribe(
1031 gallo: *mut PicoDeGallo,
1032 pin: u8,
1033 edge: u8,
1034) -> Status {
1035 if gallo.is_null() {
1036 eprintln!("Unexpected NULL context");
1037 return Status::Uninitialized;
1038 }
1039
1040 let edge_val = match edge {
1041 0 => lib::GpioEdge::Rising,
1042 1 => lib::GpioEdge::Falling,
1043 2 => lib::GpioEdge::Any,
1044 _ => {
1045 eprintln!("Invalid edge value: {edge}");
1046 return Status::InvalidArgument;
1047 }
1048 };
1049
1050 let gallo = unsafe { &*gallo };
1053
1054 let result = block_on(gallo.0.gpio_subscribe(pin, edge_val));
1055
1056 match result {
1057 Ok(()) => Status::Ok,
1058 Err(e) => gpio_error_to_status(e),
1059 }
1060}
1061
1062#[unsafe(no_mangle)]
1074pub unsafe extern "C" fn gallo_gpio_unsubscribe(gallo: *mut PicoDeGallo, pin: u8) -> Status {
1075 if gallo.is_null() {
1076 eprintln!("Unexpected NULL context");
1077 return Status::Uninitialized;
1078 }
1079
1080 let gallo = unsafe { &*gallo };
1083
1084 let result = block_on(gallo.0.gpio_unsubscribe(pin));
1085
1086 match result {
1087 Ok(()) => Status::Ok,
1088 Err(e) => gpio_error_to_status(e),
1089 }
1090}
1091
1092#[unsafe(no_mangle)]
1106pub unsafe extern "C" fn gallo_i2c_set_config(gallo: *mut PicoDeGallo, frequency: u8) -> Status {
1107 if gallo.is_null() {
1108 eprintln!("Unexpected NULL context");
1109 return Status::Uninitialized;
1110 }
1111
1112 let freq = match frequency {
1113 0 => lib::I2cFrequency::Standard,
1114 1 => lib::I2cFrequency::Fast,
1115 2 => lib::I2cFrequency::FastPlus,
1116 _ => return Status::InvalidArgument,
1117 };
1118
1119 let gallo = unsafe { &*gallo };
1122
1123 let result = block_on(gallo.0.i2c_set_config(freq));
1124
1125 match result {
1126 Ok(()) => Status::Ok,
1127 Err(e) => i2c_error_to_status(e),
1128 }
1129}
1130
1131#[unsafe(no_mangle)]
1148pub unsafe extern "C" fn gallo_spi_set_config(
1149 gallo: *mut PicoDeGallo,
1150 frequency: u32,
1151 spi_phase: bool,
1152 spi_polarity: bool,
1153) -> Status {
1154 if gallo.is_null() {
1155 eprintln!("Unexpected NULL context");
1156 return Status::Uninitialized;
1157 }
1158
1159 let gallo = unsafe { &*gallo };
1162
1163 let phase = if spi_phase {
1164 lib::SpiPhase::CaptureOnSecondTransition
1165 } else {
1166 lib::SpiPhase::CaptureOnFirstTransition
1167 };
1168
1169 let polarity = if spi_polarity {
1170 lib::SpiPolarity::IdleHigh
1171 } else {
1172 lib::SpiPolarity::IdleLow
1173 };
1174
1175 let result = block_on(gallo.0.spi_set_config(frequency, phase, polarity));
1176
1177 match result {
1178 Ok(()) => Status::Ok,
1179 Err(e) => spi_error_to_status(e),
1180 }
1181}
1182
1183#[unsafe(no_mangle)]
1198pub unsafe extern "C" fn gallo_i2c_get_config(
1199 gallo: *mut PicoDeGallo,
1200 out_frequency: *mut u8,
1201) -> Status {
1202 if gallo.is_null() {
1203 eprintln!("Unexpected NULL context");
1204 return Status::Uninitialized;
1205 }
1206
1207 if out_frequency.is_null() {
1208 eprintln!("Unexpected NULL out_frequency pointer");
1209 return Status::InvalidArgument;
1210 }
1211
1212 let gallo = unsafe { &*gallo };
1215
1216 let result = block_on(gallo.0.i2c_get_config());
1217
1218 match result {
1219 Ok(freq) => {
1220 unsafe {
1221 *out_frequency = freq as u8;
1222 }
1223 Status::Ok
1224 }
1225 Err(_) => Status::I2cGetConfigFailed,
1226 }
1227}
1228
1229#[unsafe(no_mangle)]
1246pub unsafe extern "C" fn gallo_spi_get_config(
1247 gallo: *mut PicoDeGallo,
1248 out_frequency: *mut u32,
1249 out_phase: *mut bool,
1250 out_polarity: *mut bool,
1251) -> Status {
1252 if gallo.is_null() {
1253 eprintln!("Unexpected NULL context");
1254 return Status::Uninitialized;
1255 }
1256
1257 if out_frequency.is_null() || out_phase.is_null() || out_polarity.is_null() {
1258 eprintln!("Unexpected NULL output pointer");
1259 return Status::InvalidArgument;
1260 }
1261
1262 let gallo = unsafe { &*gallo };
1265
1266 let result = block_on(gallo.0.spi_get_config());
1267
1268 match result {
1269 Ok(info) => {
1270 unsafe {
1271 *out_frequency = info.spi_frequency;
1272 *out_phase = matches!(info.spi_phase, lib::SpiPhase::CaptureOnSecondTransition);
1273 *out_polarity = matches!(info.spi_polarity, lib::SpiPolarity::IdleHigh);
1274 }
1275 Status::Ok
1276 }
1277 Err(_) => Status::SpiGetConfigFailed,
1278 }
1279}
1280
1281#[unsafe(no_mangle)]
1298pub unsafe extern "C" fn gallo_uart_read(
1299 gallo: *mut PicoDeGallo,
1300 buf: *mut u8,
1301 count: u16,
1302 timeout_ms: u32,
1303 out_len: *mut u16,
1304) -> Status {
1305 if gallo.is_null() {
1306 eprintln!("Unexpected NULL context");
1307 return Status::Uninitialized;
1308 }
1309
1310 if buf.is_null() || out_len.is_null() {
1311 eprintln!("Unexpected NULL pointer");
1312 return Status::InvalidArgument;
1313 }
1314
1315 let gallo = unsafe { &*gallo };
1318
1319 let result = block_on(gallo.0.uart_read(count, timeout_ms));
1320
1321 match result {
1322 Ok(data) => {
1323 let len = data.len().min(count as usize);
1324 unsafe {
1325 std::ptr::copy_nonoverlapping(data.as_ptr(), buf, len);
1326 *out_len = len as u16;
1327 }
1328 Status::Ok
1329 }
1330 Err(e) => uart_error_to_status(e),
1331 }
1332}
1333
1334#[unsafe(no_mangle)]
1350pub unsafe extern "C" fn gallo_uart_write(
1351 gallo: *mut PicoDeGallo,
1352 buf: *const u8,
1353 len: u16,
1354) -> Status {
1355 if gallo.is_null() {
1356 eprintln!("Unexpected NULL context");
1357 return Status::Uninitialized;
1358 }
1359
1360 if buf.is_null() {
1361 eprintln!("Unexpected NULL buf pointer");
1362 return Status::InvalidArgument;
1363 }
1364
1365 let gallo = unsafe { &*gallo };
1368
1369 let data = unsafe { std::slice::from_raw_parts(buf, len as usize) };
1370
1371 let result = block_on(gallo.0.uart_write(data));
1372
1373 match result {
1374 Ok(()) => Status::Ok,
1375 Err(e) => uart_error_to_status(e),
1376 }
1377}
1378
1379#[unsafe(no_mangle)]
1392pub unsafe extern "C" fn gallo_uart_flush(gallo: *mut PicoDeGallo) -> Status {
1393 if gallo.is_null() {
1394 eprintln!("Unexpected NULL context");
1395 return Status::Uninitialized;
1396 }
1397
1398 let gallo = unsafe { &*gallo };
1401
1402 let result = block_on(gallo.0.uart_flush());
1403
1404 match result {
1405 Ok(()) => Status::Ok,
1406 Err(e) => uart_error_to_status(e),
1407 }
1408}
1409
1410#[unsafe(no_mangle)]
1424pub unsafe extern "C" fn gallo_uart_set_config(gallo: *mut PicoDeGallo, baud_rate: u32) -> Status {
1425 if gallo.is_null() {
1426 eprintln!("Unexpected NULL context");
1427 return Status::Uninitialized;
1428 }
1429
1430 if baud_rate == 0 {
1431 eprintln!("Invalid baud rate: 0");
1432 return Status::InvalidArgument;
1433 }
1434
1435 let gallo = unsafe { &*gallo };
1438
1439 let result = block_on(gallo.0.uart_set_config(baud_rate));
1440
1441 match result {
1442 Ok(()) => Status::Ok,
1443 Err(e) => uart_error_to_status(e),
1444 }
1445}
1446
1447#[unsafe(no_mangle)]
1461pub unsafe extern "C" fn gallo_uart_get_config(
1462 gallo: *mut PicoDeGallo,
1463 out_baud_rate: *mut u32,
1464) -> Status {
1465 if gallo.is_null() {
1466 eprintln!("Unexpected NULL context");
1467 return Status::Uninitialized;
1468 }
1469
1470 if out_baud_rate.is_null() {
1471 eprintln!("Unexpected NULL out_baud_rate pointer");
1472 return Status::InvalidArgument;
1473 }
1474
1475 let gallo = unsafe { &*gallo };
1478
1479 let result = block_on(gallo.0.uart_get_config());
1480
1481 match result {
1482 Ok(info) => {
1483 unsafe {
1484 *out_baud_rate = info.baud_rate;
1485 }
1486 Status::Ok
1487 }
1488 Err(e) => uart_error_to_status(e),
1489 }
1490}
1491
1492#[unsafe(no_mangle)]
1504pub unsafe extern "C" fn gallo_pwm_set_duty_cycle(
1505 gallo: *mut PicoDeGallo,
1506 channel: u8,
1507 duty: u16,
1508) -> Status {
1509 if gallo.is_null() {
1510 eprintln!("Unexpected NULL context");
1511 return Status::Uninitialized;
1512 }
1513
1514 let gallo = unsafe { &*gallo };
1515 match block_on(gallo.0.pwm_set_duty_cycle(channel, duty)) {
1516 Ok(()) => Status::Ok,
1517 Err(e) => pwm_error_to_status(e),
1518 }
1519}
1520
1521#[unsafe(no_mangle)]
1532pub unsafe extern "C" fn gallo_pwm_get_duty_cycle(
1533 gallo: *mut PicoDeGallo,
1534 channel: u8,
1535 out_duty: *mut u16,
1536 out_max_duty: *mut u16,
1537) -> Status {
1538 if gallo.is_null() {
1539 eprintln!("Unexpected NULL context");
1540 return Status::Uninitialized;
1541 }
1542
1543 if out_duty.is_null() || out_max_duty.is_null() {
1544 eprintln!("Unexpected NULL output pointer");
1545 return Status::InvalidArgument;
1546 }
1547
1548 let gallo = unsafe { &*gallo };
1549 match block_on(gallo.0.pwm_get_duty_cycle(channel)) {
1550 Ok(info) => {
1551 unsafe {
1552 *out_duty = info.current_duty;
1553 *out_max_duty = info.max_duty;
1554 }
1555 Status::Ok
1556 }
1557 Err(e) => pwm_error_to_status(e),
1558 }
1559}
1560
1561#[unsafe(no_mangle)]
1570pub unsafe extern "C" fn gallo_pwm_enable(gallo: *mut PicoDeGallo, channel: u8) -> Status {
1571 if gallo.is_null() {
1572 eprintln!("Unexpected NULL context");
1573 return Status::Uninitialized;
1574 }
1575
1576 let gallo = unsafe { &*gallo };
1577 match block_on(gallo.0.pwm_enable(channel)) {
1578 Ok(()) => Status::Ok,
1579 Err(e) => pwm_error_to_status(e),
1580 }
1581}
1582
1583#[unsafe(no_mangle)]
1592pub unsafe extern "C" fn gallo_pwm_disable(gallo: *mut PicoDeGallo, channel: u8) -> Status {
1593 if gallo.is_null() {
1594 eprintln!("Unexpected NULL context");
1595 return Status::Uninitialized;
1596 }
1597
1598 let gallo = unsafe { &*gallo };
1599 match block_on(gallo.0.pwm_disable(channel)) {
1600 Ok(()) => Status::Ok,
1601 Err(e) => pwm_error_to_status(e),
1602 }
1603}
1604
1605#[unsafe(no_mangle)]
1615pub unsafe extern "C" fn gallo_pwm_set_config(
1616 gallo: *mut PicoDeGallo,
1617 channel: u8,
1618 frequency_hz: u32,
1619 phase_correct: bool,
1620) -> Status {
1621 if gallo.is_null() {
1622 eprintln!("Unexpected NULL context");
1623 return Status::Uninitialized;
1624 }
1625
1626 let gallo = unsafe { &*gallo };
1627 match block_on(gallo.0.pwm_set_config(channel, frequency_hz, phase_correct)) {
1628 Ok(()) => Status::Ok,
1629 Err(e) => pwm_error_to_status(e),
1630 }
1631}
1632
1633#[unsafe(no_mangle)]
1645pub unsafe extern "C" fn gallo_pwm_get_config(
1646 gallo: *mut PicoDeGallo,
1647 channel: u8,
1648 out_frequency_hz: *mut u32,
1649 out_phase_correct: *mut bool,
1650 out_enabled: *mut bool,
1651) -> Status {
1652 if gallo.is_null() {
1653 eprintln!("Unexpected NULL context");
1654 return Status::Uninitialized;
1655 }
1656
1657 if out_frequency_hz.is_null() || out_phase_correct.is_null() || out_enabled.is_null() {
1658 eprintln!("Unexpected NULL output pointer");
1659 return Status::InvalidArgument;
1660 }
1661
1662 let gallo = unsafe { &*gallo };
1663 match block_on(gallo.0.pwm_get_config(channel)) {
1664 Ok(info) => {
1665 unsafe {
1666 *out_frequency_hz = info.frequency_hz;
1667 *out_phase_correct = info.phase_correct;
1668 *out_enabled = info.enabled;
1669 }
1670 Status::Ok
1671 }
1672 Err(e) => pwm_error_to_status(e),
1673 }
1674}
1675
1676#[unsafe(no_mangle)]
1689pub unsafe extern "C" fn gallo_adc_read(
1690 gallo: *mut PicoDeGallo,
1691 channel: u8,
1692 out_value: *mut u16,
1693) -> Status {
1694 if gallo.is_null() {
1695 eprintln!("Unexpected NULL context");
1696 return Status::Uninitialized;
1697 }
1698
1699 if out_value.is_null() {
1700 eprintln!("Unexpected NULL output pointer");
1701 return Status::InvalidArgument;
1702 }
1703
1704 let adc_channel = match channel {
1705 0 => AdcChannel::Adc0,
1706 1 => AdcChannel::Adc1,
1707 2 => AdcChannel::Adc2,
1708 3 => AdcChannel::Adc3,
1709 _ => {
1710 eprintln!("Invalid ADC channel: {channel}");
1711 return Status::InvalidArgument;
1712 }
1713 };
1714
1715 let gallo = unsafe { &*gallo };
1716 match block_on(gallo.0.adc_read(adc_channel)) {
1717 Ok(raw) => {
1718 unsafe { *out_value = raw };
1719 Status::Ok
1720 }
1721 Err(e) => adc_error_to_status(e),
1722 }
1723}
1724
1725#[unsafe(no_mangle)]
1736pub unsafe extern "C" fn gallo_adc_get_config(
1737 gallo: *mut PicoDeGallo,
1738 out_resolution_bits: *mut u8,
1739 out_nominal_reference_mv: *mut u16,
1740 out_num_gpio_channels: *mut u8,
1741) -> Status {
1742 if gallo.is_null() {
1743 eprintln!("Unexpected NULL context");
1744 return Status::Uninitialized;
1745 }
1746
1747 if out_resolution_bits.is_null()
1748 || out_nominal_reference_mv.is_null()
1749 || out_num_gpio_channels.is_null()
1750 {
1751 eprintln!("Unexpected NULL output pointer");
1752 return Status::InvalidArgument;
1753 }
1754
1755 let gallo = unsafe { &*gallo };
1756 match block_on(gallo.0.adc_get_config()) {
1757 Ok(info) => {
1758 unsafe {
1759 *out_resolution_bits = info.resolution_bits;
1760 *out_nominal_reference_mv = info.nominal_reference_mv;
1761 *out_num_gpio_channels = info.num_gpio_channels;
1762 }
1763 Status::Ok
1764 }
1765 Err(e) => adc_error_to_status(e),
1766 }
1767}
1768
1769#[unsafe(no_mangle)]
1772pub unsafe extern "C" fn gallo_onewire_reset(
1782 gallo: *mut PicoDeGallo,
1783 out_present: *mut bool,
1784) -> Status {
1785 if gallo.is_null() {
1786 eprintln!("Unexpected NULL context");
1787 return Status::Uninitialized;
1788 }
1789
1790 if out_present.is_null() {
1791 eprintln!("Unexpected NULL output pointer");
1792 return Status::InvalidArgument;
1793 }
1794
1795 let gallo = unsafe { &*gallo };
1796 match block_on(gallo.0.onewire_reset()) {
1797 Ok(present) => {
1798 unsafe {
1799 *out_present = present;
1800 }
1801 Status::Ok
1802 }
1803 Err(e) => onewire_error_to_status(e),
1804 }
1805}
1806
1807#[unsafe(no_mangle)]
1808pub unsafe extern "C" fn gallo_onewire_read(
1818 gallo: *mut PicoDeGallo,
1819 buf: *mut u8,
1820 len: u16,
1821 out_len: *mut u16,
1822) -> Status {
1823 if gallo.is_null() {
1824 eprintln!("Unexpected NULL context");
1825 return Status::Uninitialized;
1826 }
1827
1828 if buf.is_null() || out_len.is_null() {
1829 eprintln!("Unexpected NULL pointer");
1830 return Status::InvalidArgument;
1831 }
1832
1833 let gallo = unsafe { &*gallo };
1834 match block_on(gallo.0.onewire_read(len)) {
1835 Ok(data) => {
1836 let copy_len = data.len().min(len as usize);
1837 unsafe {
1838 std::ptr::copy_nonoverlapping(data.as_ptr(), buf, copy_len);
1839 *out_len = copy_len as u16;
1840 }
1841 Status::Ok
1842 }
1843 Err(e) => onewire_error_to_status(e),
1844 }
1845}
1846
1847#[unsafe(no_mangle)]
1848pub unsafe extern "C" fn gallo_onewire_write(
1854 gallo: *mut PicoDeGallo,
1855 buf: *const u8,
1856 len: u16,
1857) -> Status {
1858 if gallo.is_null() {
1859 eprintln!("Unexpected NULL context");
1860 return Status::Uninitialized;
1861 }
1862
1863 if buf.is_null() && len > 0 {
1864 eprintln!("Unexpected NULL buffer pointer");
1865 return Status::InvalidArgument;
1866 }
1867
1868 let gallo = unsafe { &*gallo };
1869 let data = if len > 0 {
1870 unsafe { std::slice::from_raw_parts(buf, len as usize) }
1871 } else {
1872 &[]
1873 };
1874
1875 match block_on(gallo.0.onewire_write(data)) {
1876 Ok(()) => Status::Ok,
1877 Err(e) => onewire_error_to_status(e),
1878 }
1879}
1880
1881#[unsafe(no_mangle)]
1882pub unsafe extern "C" fn gallo_onewire_write_pullup(
1891 gallo: *mut PicoDeGallo,
1892 buf: *const u8,
1893 len: u16,
1894 pullup_duration_ms: u16,
1895) -> Status {
1896 if gallo.is_null() {
1897 eprintln!("Unexpected NULL context");
1898 return Status::Uninitialized;
1899 }
1900
1901 if buf.is_null() && len > 0 {
1902 eprintln!("Unexpected NULL buffer pointer");
1903 return Status::InvalidArgument;
1904 }
1905
1906 let gallo = unsafe { &*gallo };
1907 let data = if len > 0 {
1908 unsafe { std::slice::from_raw_parts(buf, len as usize) }
1909 } else {
1910 &[]
1911 };
1912
1913 match block_on(gallo.0.onewire_write_pullup(data, pullup_duration_ms)) {
1914 Ok(()) => Status::Ok,
1915 Err(e) => onewire_error_to_status(e),
1916 }
1917}
1918
1919#[unsafe(no_mangle)]
1920pub unsafe extern "C" fn gallo_onewire_search(
1930 gallo: *mut PicoDeGallo,
1931 out_rom_ids: *mut u64,
1932 max_count: u16,
1933 out_count: *mut u16,
1934) -> Status {
1935 if gallo.is_null() {
1936 eprintln!("Unexpected NULL context");
1937 return Status::Uninitialized;
1938 }
1939
1940 if out_rom_ids.is_null() || out_count.is_null() {
1941 eprintln!("Unexpected NULL pointer");
1942 return Status::InvalidArgument;
1943 }
1944
1945 let gallo = unsafe { &*gallo };
1946
1947 let first = match block_on(gallo.0.onewire_search()) {
1949 Ok(Some(id)) => id,
1950 Ok(None) => {
1951 unsafe {
1952 *out_count = 0;
1953 }
1954 return Status::Ok;
1955 }
1956 Err(e) => return onewire_error_to_status(e),
1957 };
1958
1959 unsafe {
1960 *out_rom_ids = first;
1961 }
1962 let mut count: u16 = 1;
1963
1964 while count < max_count {
1966 match block_on(gallo.0.onewire_search_next()) {
1967 Ok(Some(id)) => {
1968 unsafe {
1969 *out_rom_ids.add(count as usize) = id;
1970 }
1971 count += 1;
1972 }
1973 Ok(None) => break,
1974 Err(e) => return onewire_error_to_status(e),
1975 }
1976 }
1977
1978 unsafe {
1979 *out_count = count;
1980 }
1981 Status::Ok
1982}
1983
1984#[unsafe(no_mangle)]
1987pub unsafe extern "C" fn gallo_version(
1996 gallo: *mut PicoDeGallo,
1997 major: *mut u16,
1998 minor: *mut u16,
1999 patch: *mut u32,
2000) -> Status {
2001 if gallo.is_null() {
2002 eprintln!("Unexpected NULL context");
2003 return Status::Uninitialized;
2004 }
2005
2006 if major.is_null() || minor.is_null() || patch.is_null() {
2007 eprintln!("Unexpected NULL version pointer");
2008 return Status::InvalidArgument;
2009 }
2010
2011 let gallo = unsafe { &*gallo };
2014
2015 let result = block_on(gallo.0.version());
2016
2017 match result {
2018 Ok(lib::VersionInfo {
2019 major: a,
2020 minor: b,
2021 patch: c,
2022 }) => {
2023 unsafe {
2024 *major = a;
2025 *minor = b;
2026 *patch = c;
2027 }
2028
2029 Status::Ok
2030 }
2031 Err(_) => Status::VersionFailed,
2032 }
2033}
2034
2035pub const GALLO_CAP_I2C: u64 = 1 << 0;
2039pub const GALLO_CAP_SPI: u64 = 1 << 1;
2041pub const GALLO_CAP_UART: u64 = 1 << 2;
2043pub const GALLO_CAP_GPIO: u64 = 1 << 3;
2045pub const GALLO_CAP_PWM: u64 = 1 << 4;
2047pub const GALLO_CAP_ADC: u64 = 1 << 5;
2049pub const GALLO_CAP_ONEWIRE: u64 = 1 << 6;
2051
2052#[repr(C)]
2064#[derive(Debug)]
2065pub struct GalloDeviceInfo {
2066 pub fw_major: u16,
2068 pub fw_minor: u16,
2070 pub fw_patch: u32,
2072 pub schema_major: u16,
2074 pub schema_minor: u16,
2076 pub schema_patch: u32,
2078 pub hw_version: u8,
2080 pub capabilities: u64,
2085}
2086
2087#[unsafe(no_mangle)]
2088pub unsafe extern "C" fn gallo_get_device_info(
2105 gallo: *mut PicoDeGallo,
2106 out: *mut GalloDeviceInfo,
2107) -> Status {
2108 if gallo.is_null() {
2109 eprintln!("Unexpected NULL context");
2110 return Status::Uninitialized;
2111 }
2112
2113 if out.is_null() {
2114 eprintln!("Unexpected NULL device info pointer");
2115 return Status::InvalidArgument;
2116 }
2117
2118 let gallo = unsafe { &*gallo };
2119
2120 let result = block_on(gallo.0.validate());
2121
2122 match result {
2123 Ok(info) => {
2124 unsafe {
2125 (*out).fw_major = info.fw_major;
2126 (*out).fw_minor = info.fw_minor;
2127 (*out).fw_patch = info.fw_patch;
2128 (*out).schema_major = info.schema_major;
2129 (*out).schema_minor = info.schema_minor;
2130 (*out).schema_patch = info.schema_patch;
2131 (*out).hw_version = info.hw_version;
2132 (*out).capabilities = info.capabilities.bits();
2133 }
2134 Status::Ok
2135 }
2136 Err(lib::ValidateError::SchemaMismatch { .. }) => Status::SchemaMismatch,
2137 Err(lib::ValidateError::LegacyFirmware) => Status::LegacyFirmware,
2138 Err(lib::ValidateError::Comms(_)) => Status::DeviceInfoFailed,
2139 }
2140}
2141
2142#[cfg(test)]
2143mod tests {
2144 use super::*;
2145 use std::collections::HashSet;
2146
2147 #[test]
2150 fn ok_is_zero() {
2151 assert_eq!(Status::Ok as i32, 0);
2152 }
2153
2154 #[test]
2155 fn all_errors_are_negative() {
2156 let error_codes = [
2157 Status::I2cReadFailed as i32,
2158 Status::I2cWriteFailed as i32,
2159 Status::InvalidResponse as i32,
2160 Status::Uninitialized as i32,
2161 Status::InvalidArgument as i32,
2162 Status::PingFailed as i32,
2163 Status::SpiReadFailed as i32,
2164 Status::SpiWriteFailed as i32,
2165 Status::SpiFlushFailed as i32,
2166 Status::GpioGetFailed as i32,
2167 Status::GpioPutFailed as i32,
2168 Status::GpioWaitFailed as i32,
2169 Status::SetConfigFailed as i32,
2170 Status::VersionFailed as i32,
2171 Status::I2cWriteReadFailed as i32,
2172 Status::I2cSetConfigFailed as i32,
2173 Status::SpiSetConfigFailed as i32,
2174 Status::I2cNack as i32,
2175 Status::I2cBusError as i32,
2176 Status::I2cArbitrationLoss as i32,
2177 Status::I2cOverrun as i32,
2178 Status::BufferTooLong as i32,
2179 Status::I2cAddressOutOfRange as i32,
2180 Status::GpioInvalidPin as i32,
2181 Status::CommsFailed as i32,
2182 Status::I2cScanFailed as i32,
2183 Status::GpioSetConfigFailed as i32,
2184 Status::GpioWrongDirection as i32,
2185 Status::I2cGetConfigFailed as i32,
2186 Status::SpiGetConfigFailed as i32,
2187 Status::UartReadFailed as i32,
2188 Status::UartWriteFailed as i32,
2189 Status::UartFlushFailed as i32,
2190 Status::UartOverrun as i32,
2191 Status::UartBreak as i32,
2192 Status::UartParity as i32,
2193 Status::UartFraming as i32,
2194 Status::UartInvalidBaudRate as i32,
2195 Status::UartSetConfigFailed as i32,
2196 Status::UartGetConfigFailed as i32,
2197 Status::PwmSetDutyCycleFailed as i32,
2198 Status::PwmGetDutyCycleFailed as i32,
2199 Status::PwmEnableFailed as i32,
2200 Status::PwmDisableFailed as i32,
2201 Status::PwmSetConfigFailed as i32,
2202 Status::PwmGetConfigFailed as i32,
2203 Status::PwmInvalidChannel as i32,
2204 Status::PwmInvalidDutyCycle as i32,
2205 Status::PwmInvalidConfiguration as i32,
2206 Status::AdcReadFailed as i32,
2207 Status::AdcGetConfigFailed as i32,
2208 Status::AdcConversionFailed as i32,
2209 Status::GpioPinMonitored as i32,
2210 Status::GpioPinNotMonitored as i32,
2211 Status::GpioSubscribeFailed as i32,
2212 Status::GpioUnsubscribeFailed as i32,
2213 Status::OneWireNoPresence as i32,
2214 Status::OneWireBusError as i32,
2215 Status::OneWireReadFailed as i32,
2216 Status::OneWireWriteFailed as i32,
2217 Status::OneWireSearchFailed as i32,
2218 Status::DeviceInfoFailed as i32,
2219 Status::SchemaMismatch as i32,
2220 Status::LegacyFirmware as i32,
2221 ];
2222 for code in error_codes {
2223 assert!(code < 0, "error code {code} should be negative");
2224 }
2225 }
2226
2227 #[test]
2228 fn status_codes_are_distinct() {
2229 let codes = [
2230 Status::Ok as i32,
2231 Status::I2cReadFailed as i32,
2232 Status::I2cWriteFailed as i32,
2233 Status::InvalidResponse as i32,
2234 Status::Uninitialized as i32,
2235 Status::InvalidArgument as i32,
2236 Status::PingFailed as i32,
2237 Status::SpiReadFailed as i32,
2238 Status::SpiWriteFailed as i32,
2239 Status::SpiFlushFailed as i32,
2240 Status::GpioGetFailed as i32,
2241 Status::GpioPutFailed as i32,
2242 Status::GpioWaitFailed as i32,
2243 Status::SetConfigFailed as i32,
2244 Status::VersionFailed as i32,
2245 Status::I2cWriteReadFailed as i32,
2246 Status::I2cSetConfigFailed as i32,
2247 Status::SpiSetConfigFailed as i32,
2248 Status::I2cNack as i32,
2249 Status::I2cBusError as i32,
2250 Status::I2cArbitrationLoss as i32,
2251 Status::I2cOverrun as i32,
2252 Status::BufferTooLong as i32,
2253 Status::I2cAddressOutOfRange as i32,
2254 Status::GpioInvalidPin as i32,
2255 Status::CommsFailed as i32,
2256 Status::I2cScanFailed as i32,
2257 Status::GpioSetConfigFailed as i32,
2258 Status::GpioWrongDirection as i32,
2259 Status::I2cGetConfigFailed as i32,
2260 Status::SpiGetConfigFailed as i32,
2261 Status::UartReadFailed as i32,
2262 Status::UartWriteFailed as i32,
2263 Status::UartFlushFailed as i32,
2264 Status::UartOverrun as i32,
2265 Status::UartBreak as i32,
2266 Status::UartParity as i32,
2267 Status::UartFraming as i32,
2268 Status::UartInvalidBaudRate as i32,
2269 Status::UartSetConfigFailed as i32,
2270 Status::UartGetConfigFailed as i32,
2271 Status::PwmSetDutyCycleFailed as i32,
2272 Status::PwmGetDutyCycleFailed as i32,
2273 Status::PwmEnableFailed as i32,
2274 Status::PwmDisableFailed as i32,
2275 Status::PwmSetConfigFailed as i32,
2276 Status::PwmGetConfigFailed as i32,
2277 Status::PwmInvalidChannel as i32,
2278 Status::PwmInvalidDutyCycle as i32,
2279 Status::PwmInvalidConfiguration as i32,
2280 Status::AdcReadFailed as i32,
2281 Status::AdcGetConfigFailed as i32,
2282 Status::AdcConversionFailed as i32,
2283 Status::GpioPinMonitored as i32,
2284 Status::GpioPinNotMonitored as i32,
2285 Status::GpioSubscribeFailed as i32,
2286 Status::GpioUnsubscribeFailed as i32,
2287 Status::OneWireNoPresence as i32,
2288 Status::OneWireBusError as i32,
2289 Status::OneWireReadFailed as i32,
2290 Status::OneWireWriteFailed as i32,
2291 Status::OneWireSearchFailed as i32,
2292 Status::DeviceInfoFailed as i32,
2293 Status::SchemaMismatch as i32,
2294 Status::LegacyFirmware as i32,
2295 ];
2296 let unique: HashSet<i32> = codes.iter().copied().collect();
2297 assert_eq!(codes.len(), unique.len(), "duplicate status codes found");
2298 }
2299
2300 #[test]
2303 fn ping_null_device_returns_uninitialized() {
2304 let mut id = 42u32;
2305 let status = unsafe { gallo_ping(std::ptr::null_mut(), &mut id as *mut u32) };
2306 assert_eq!(status, Status::Uninitialized);
2307 }
2308
2309 #[test]
2310 fn i2c_read_null_device_returns_uninitialized() {
2311 let mut buf = [0u8; 4];
2312 let status =
2313 unsafe { gallo_i2c_read(std::ptr::null_mut(), 0x48, buf.as_mut_ptr(), buf.len()) };
2314 assert_eq!(status, Status::Uninitialized);
2315 }
2316
2317 #[test]
2318 fn i2c_write_null_device_returns_uninitialized() {
2319 let buf = [0u8; 4];
2320 let status =
2321 unsafe { gallo_i2c_write(std::ptr::null_mut(), 0x48, buf.as_ptr(), buf.len()) };
2322 assert_eq!(status, Status::Uninitialized);
2323 }
2324
2325 #[test]
2326 fn i2c_write_read_null_device_returns_uninitialized() {
2327 let txbuf = [0u8; 2];
2328 let mut rxbuf = [0u8; 4];
2329 let status = unsafe {
2330 gallo_i2c_write_read(
2331 std::ptr::null_mut(),
2332 0x48,
2333 txbuf.as_ptr(),
2334 txbuf.len(),
2335 rxbuf.as_mut_ptr(),
2336 rxbuf.len(),
2337 )
2338 };
2339 assert_eq!(status, Status::Uninitialized);
2340 }
2341
2342 #[test]
2343 fn spi_read_null_device_returns_uninitialized() {
2344 let mut buf = [0u8; 4];
2345 let status = unsafe { gallo_spi_read(std::ptr::null_mut(), buf.as_mut_ptr(), buf.len()) };
2346 assert_eq!(status, Status::Uninitialized);
2347 }
2348
2349 #[test]
2350 fn spi_write_null_device_returns_uninitialized() {
2351 let buf = [0u8; 4];
2352 let status = unsafe { gallo_spi_write(std::ptr::null_mut(), buf.as_ptr(), buf.len()) };
2353 assert_eq!(status, Status::Uninitialized);
2354 }
2355
2356 #[test]
2357 fn spi_flush_null_device_returns_uninitialized() {
2358 let status = unsafe { gallo_spi_flush(std::ptr::null_mut()) };
2359 assert_eq!(status, Status::Uninitialized);
2360 }
2361
2362 #[test]
2363 fn gpio_get_null_device_returns_uninitialized() {
2364 let mut state = false;
2365 let status = unsafe { gallo_gpio_get(std::ptr::null_mut(), 0, &mut state as *mut bool) };
2366 assert_eq!(status, Status::Uninitialized);
2367 }
2368
2369 #[test]
2370 fn gpio_put_null_device_returns_uninitialized() {
2371 let status = unsafe { gallo_gpio_put(std::ptr::null_mut(), 0, true) };
2372 assert_eq!(status, Status::Uninitialized);
2373 }
2374
2375 #[test]
2376 fn gpio_wait_for_high_null_device_returns_uninitialized() {
2377 let status = unsafe { gallo_gpio_wait_for_high(std::ptr::null_mut(), 0) };
2378 assert_eq!(status, Status::Uninitialized);
2379 }
2380
2381 #[test]
2382 fn gpio_wait_for_low_null_device_returns_uninitialized() {
2383 let status = unsafe { gallo_gpio_wait_for_low(std::ptr::null_mut(), 0) };
2384 assert_eq!(status, Status::Uninitialized);
2385 }
2386
2387 #[test]
2388 fn gpio_wait_for_rising_edge_null_device_returns_uninitialized() {
2389 let status = unsafe { gallo_gpio_wait_for_rising_edge(std::ptr::null_mut(), 0) };
2390 assert_eq!(status, Status::Uninitialized);
2391 }
2392
2393 #[test]
2394 fn gpio_wait_for_falling_edge_null_device_returns_uninitialized() {
2395 let status = unsafe { gallo_gpio_wait_for_falling_edge(std::ptr::null_mut(), 0) };
2396 assert_eq!(status, Status::Uninitialized);
2397 }
2398
2399 #[test]
2400 fn gpio_wait_for_any_edge_null_device_returns_uninitialized() {
2401 let status = unsafe { gallo_gpio_wait_for_any_edge(std::ptr::null_mut(), 0) };
2402 assert_eq!(status, Status::Uninitialized);
2403 }
2404
2405 #[test]
2406 fn gpio_set_config_null_device_returns_uninitialized() {
2407 let status = unsafe { gallo_gpio_set_config(std::ptr::null_mut(), 0, 0, 0) };
2408 assert_eq!(status, Status::Uninitialized);
2409 }
2410
2411 #[test]
2412 fn gpio_subscribe_null_device_returns_uninitialized() {
2413 let status = unsafe { gallo_gpio_subscribe(std::ptr::null_mut(), 0, 0) };
2414 assert_eq!(status, Status::Uninitialized);
2415 }
2416
2417 #[test]
2418 fn gpio_subscribe_invalid_edge_returns_uninitialized() {
2419 let status = unsafe { gallo_gpio_subscribe(std::ptr::null_mut(), 0, 99) };
2421 assert_eq!(status, Status::Uninitialized);
2422 }
2423
2424 #[test]
2425 fn gpio_unsubscribe_null_device_returns_uninitialized() {
2426 let status = unsafe { gallo_gpio_unsubscribe(std::ptr::null_mut(), 0) };
2427 assert_eq!(status, Status::Uninitialized);
2428 }
2429
2430 #[test]
2431 fn i2c_set_config_null_device_returns_uninitialized() {
2432 let status = unsafe { gallo_i2c_set_config(std::ptr::null_mut(), 1) };
2433 assert_eq!(status, Status::Uninitialized);
2434 }
2435
2436 #[test]
2437 fn i2c_set_config_invalid_frequency_returns_invalid_argument() {
2438 let status = unsafe { gallo_i2c_set_config(std::ptr::null_mut(), 99) };
2444 assert_eq!(status, Status::Uninitialized);
2446 }
2447
2448 #[test]
2449 fn spi_set_config_null_device_returns_uninitialized() {
2450 let status = unsafe { gallo_spi_set_config(std::ptr::null_mut(), 1_000_000, false, false) };
2451 assert_eq!(status, Status::Uninitialized);
2452 }
2453
2454 #[test]
2455 fn i2c_get_config_null_device_returns_uninitialized() {
2456 let mut freq = 0u8;
2457 let status = unsafe { gallo_i2c_get_config(std::ptr::null_mut(), &mut freq as *mut u8) };
2458 assert_eq!(status, Status::Uninitialized);
2459 }
2460
2461 #[test]
2462 fn spi_get_config_null_device_returns_uninitialized() {
2463 let mut freq = 0u32;
2464 let mut phase = false;
2465 let mut polarity = false;
2466 let status = unsafe {
2467 gallo_spi_get_config(
2468 std::ptr::null_mut(),
2469 &mut freq as *mut u32,
2470 &mut phase as *mut bool,
2471 &mut polarity as *mut bool,
2472 )
2473 };
2474 assert_eq!(status, Status::Uninitialized);
2475 }
2476
2477 #[test]
2478 fn version_null_device_returns_uninitialized() {
2479 let mut major = 0u16;
2480 let mut minor = 0u16;
2481 let mut patch = 0u32;
2482 let status = unsafe {
2483 gallo_version(
2484 std::ptr::null_mut(),
2485 &mut major as *mut u16,
2486 &mut minor as *mut u16,
2487 &mut patch as *mut u32,
2488 )
2489 };
2490 assert_eq!(status, Status::Uninitialized);
2491 }
2492
2493 #[test]
2496 fn ping_null_id_returns_invalid_argument() {
2497 let status = unsafe { gallo_ping(std::ptr::null_mut(), std::ptr::null_mut()) };
2498 assert_eq!(status, Status::Uninitialized);
2500 }
2501
2502 #[test]
2503 fn gpio_get_null_state_returns_invalid_argument() {
2504 let status = unsafe { gallo_gpio_get(std::ptr::null_mut(), 0, std::ptr::null_mut()) };
2505 assert_eq!(status, Status::Uninitialized);
2506 }
2507
2508 #[test]
2509 fn version_null_major_returns_invalid_argument() {
2510 let mut minor = 0u16;
2511 let mut patch = 0u32;
2512 let status = unsafe {
2513 gallo_version(
2514 std::ptr::null_mut(),
2515 std::ptr::null_mut(),
2516 &mut minor as *mut u16,
2517 &mut patch as *mut u32,
2518 )
2519 };
2520 assert_eq!(status, Status::Uninitialized);
2521 }
2522
2523 #[test]
2524 fn device_info_null_device_returns_uninitialized() {
2525 let mut info = std::mem::MaybeUninit::<GalloDeviceInfo>::uninit();
2526 let status = unsafe { gallo_get_device_info(std::ptr::null_mut(), info.as_mut_ptr()) };
2527 assert_eq!(status, Status::Uninitialized);
2528 }
2529
2530 #[test]
2531 fn device_info_null_out_returns_invalid_argument() {
2532 let status = unsafe { gallo_get_device_info(std::ptr::null_mut(), std::ptr::null_mut()) };
2534 assert_eq!(status, Status::Uninitialized);
2535 }
2536
2537 #[test]
2540 fn pwm_set_duty_cycle_null_device_returns_uninitialized() {
2541 let status = unsafe { gallo_pwm_set_duty_cycle(std::ptr::null_mut(), 0, 100) };
2542 assert_eq!(status, Status::Uninitialized);
2543 }
2544
2545 #[test]
2546 fn pwm_get_duty_cycle_null_device_returns_uninitialized() {
2547 let status = unsafe {
2548 gallo_pwm_get_duty_cycle(
2549 std::ptr::null_mut(),
2550 0,
2551 std::ptr::null_mut(),
2552 std::ptr::null_mut(),
2553 )
2554 };
2555 assert_eq!(status, Status::Uninitialized);
2556 }
2557
2558 #[test]
2559 fn pwm_enable_null_device_returns_uninitialized() {
2560 let status = unsafe { gallo_pwm_enable(std::ptr::null_mut(), 0) };
2561 assert_eq!(status, Status::Uninitialized);
2562 }
2563
2564 #[test]
2565 fn pwm_disable_null_device_returns_uninitialized() {
2566 let status = unsafe { gallo_pwm_disable(std::ptr::null_mut(), 0) };
2567 assert_eq!(status, Status::Uninitialized);
2568 }
2569
2570 #[test]
2571 fn pwm_set_config_null_device_returns_uninitialized() {
2572 let status = unsafe { gallo_pwm_set_config(std::ptr::null_mut(), 0, 1000, false) };
2573 assert_eq!(status, Status::Uninitialized);
2574 }
2575
2576 #[test]
2577 fn pwm_get_config_null_device_returns_uninitialized() {
2578 let status = unsafe {
2579 gallo_pwm_get_config(
2580 std::ptr::null_mut(),
2581 0,
2582 std::ptr::null_mut(),
2583 std::ptr::null_mut(),
2584 std::ptr::null_mut(),
2585 )
2586 };
2587 assert_eq!(status, Status::Uninitialized);
2588 }
2589
2590 #[test]
2591 fn adc_read_null_device_returns_uninitialized() {
2592 let status = unsafe { gallo_adc_read(std::ptr::null_mut(), 0, std::ptr::null_mut()) };
2593 assert_eq!(status, Status::Uninitialized);
2594 }
2595
2596 #[test]
2597 fn adc_get_config_null_device_returns_uninitialized() {
2598 let status = unsafe {
2599 gallo_adc_get_config(
2600 std::ptr::null_mut(),
2601 std::ptr::null_mut(),
2602 std::ptr::null_mut(),
2603 std::ptr::null_mut(),
2604 )
2605 };
2606 assert_eq!(status, Status::Uninitialized);
2607 }
2608
2609 #[test]
2612 fn i2c_read_null_buffer_returns_invalid_argument() {
2613 let status = unsafe { gallo_i2c_read(std::ptr::null_mut(), 0x48, std::ptr::null_mut(), 4) };
2616 assert_eq!(status, Status::Uninitialized);
2617 }
2618
2619 #[test]
2620 fn spi_read_null_buffer_returns_invalid_argument() {
2621 let status = unsafe { gallo_spi_read(std::ptr::null_mut(), std::ptr::null_mut(), 4) };
2622 assert_eq!(status, Status::Uninitialized);
2623 }
2624
2625 #[test]
2628 fn free_null_is_safe() {
2629 unsafe { gallo_free(std::ptr::null()) };
2630 }
2631
2632 #[test]
2633 fn init_with_null_serial_returns_null() {
2634 let ptr = unsafe { gallo_init_with_serial_number(std::ptr::null()) };
2635 assert!(ptr.is_null());
2636 }
2637
2638 #[test]
2641 fn uart_read_null_device_returns_uninitialized() {
2642 let mut buf = [0u8; 4];
2643 let mut out_len = 0u16;
2644 let status = unsafe {
2645 gallo_uart_read(
2646 std::ptr::null_mut(),
2647 buf.as_mut_ptr(),
2648 4,
2649 1000,
2650 &mut out_len as *mut u16,
2651 )
2652 };
2653 assert_eq!(status, Status::Uninitialized);
2654 }
2655
2656 #[test]
2657 fn uart_read_null_buf_returns_uninitialized() {
2658 let status = unsafe {
2659 gallo_uart_read(
2660 std::ptr::null_mut(),
2661 std::ptr::null_mut(),
2662 4,
2663 1000,
2664 std::ptr::null_mut(),
2665 )
2666 };
2667 assert_eq!(status, Status::Uninitialized);
2668 }
2669
2670 #[test]
2671 fn uart_write_null_device_returns_uninitialized() {
2672 let data = [0x48, 0x65, 0x6c, 0x6c, 0x6f];
2673 let status = unsafe { gallo_uart_write(std::ptr::null_mut(), data.as_ptr(), 5) };
2674 assert_eq!(status, Status::Uninitialized);
2675 }
2676
2677 #[test]
2678 fn uart_write_null_buf_returns_uninitialized() {
2679 let status = unsafe { gallo_uart_write(std::ptr::null_mut(), std::ptr::null(), 5) };
2680 assert_eq!(status, Status::Uninitialized);
2681 }
2682
2683 #[test]
2684 fn uart_flush_null_device_returns_uninitialized() {
2685 let status = unsafe { gallo_uart_flush(std::ptr::null_mut()) };
2686 assert_eq!(status, Status::Uninitialized);
2687 }
2688
2689 #[test]
2690 fn uart_set_config_null_device_returns_uninitialized() {
2691 let status = unsafe { gallo_uart_set_config(std::ptr::null_mut(), 115200) };
2692 assert_eq!(status, Status::Uninitialized);
2693 }
2694
2695 #[test]
2696 fn uart_set_config_zero_baud_returns_uninitialized() {
2697 let status = unsafe { gallo_uart_set_config(std::ptr::null_mut(), 0) };
2699 assert_eq!(status, Status::Uninitialized);
2700 }
2701
2702 #[test]
2703 fn uart_get_config_null_device_returns_uninitialized() {
2704 let mut baud = 0u32;
2705 let status = unsafe { gallo_uart_get_config(std::ptr::null_mut(), &mut baud as *mut u32) };
2706 assert_eq!(status, Status::Uninitialized);
2707 }
2708
2709 #[test]
2710 fn uart_error_to_status_maps_all_variants() {
2711 assert_eq!(
2712 uart_error_to_status(PicoDeGalloError::Endpoint(UartError::BufferTooLong)),
2713 Status::BufferTooLong
2714 );
2715 assert_eq!(
2716 uart_error_to_status(PicoDeGalloError::Endpoint(UartError::Overrun)),
2717 Status::UartOverrun
2718 );
2719 assert_eq!(
2720 uart_error_to_status(PicoDeGalloError::Endpoint(UartError::Break)),
2721 Status::UartBreak
2722 );
2723 assert_eq!(
2724 uart_error_to_status(PicoDeGalloError::Endpoint(UartError::Parity)),
2725 Status::UartParity
2726 );
2727 assert_eq!(
2728 uart_error_to_status(PicoDeGalloError::Endpoint(UartError::Framing)),
2729 Status::UartFraming
2730 );
2731 assert_eq!(
2732 uart_error_to_status(PicoDeGalloError::Endpoint(UartError::InvalidBaudRate)),
2733 Status::UartInvalidBaudRate
2734 );
2735 assert_eq!(
2736 uart_error_to_status(PicoDeGalloError::Endpoint(UartError::Other)),
2737 Status::UartReadFailed
2738 );
2739 assert_eq!(
2740 uart_error_to_status(PicoDeGalloError::Endpoint(UartError::Unsupported)),
2741 Status::Unsupported
2742 );
2743 }
2744
2745 #[test]
2746 fn adc_error_mapping() {
2747 assert_eq!(
2748 adc_error_to_status(PicoDeGalloError::Endpoint(AdcError::ConversionFailed)),
2749 Status::AdcConversionFailed
2750 );
2751 assert_eq!(
2752 adc_error_to_status(PicoDeGalloError::Endpoint(AdcError::Other)),
2753 Status::AdcReadFailed
2754 );
2755 assert_eq!(
2756 adc_error_to_status(PicoDeGalloError::Endpoint(AdcError::Unsupported)),
2757 Status::Unsupported
2758 );
2759 }
2760
2761 #[test]
2762 fn gpio_error_to_status_maps_pin_monitored() {
2763 assert_eq!(
2764 gpio_error_to_status(PicoDeGalloError::Endpoint(GpioError::PinMonitored)),
2765 Status::GpioPinMonitored
2766 );
2767 }
2768
2769 #[test]
2770 fn gpio_error_to_status_maps_pin_not_monitored() {
2771 assert_eq!(
2772 gpio_error_to_status(PicoDeGalloError::Endpoint(GpioError::PinNotMonitored)),
2773 Status::GpioPinNotMonitored
2774 );
2775 }
2776
2777 #[test]
2778 fn gpio_subscribe_status_codes_are_stable() {
2779 assert_eq!(Status::GpioPinMonitored as i32, -53);
2780 assert_eq!(Status::GpioPinNotMonitored as i32, -54);
2781 assert_eq!(Status::GpioSubscribeFailed as i32, -55);
2782 assert_eq!(Status::GpioUnsubscribeFailed as i32, -56);
2783 }
2784
2785 #[test]
2788 fn onewire_reset_null_device_returns_uninitialized() {
2789 let status = unsafe { gallo_onewire_reset(std::ptr::null_mut(), std::ptr::null_mut()) };
2790 assert_eq!(status, Status::Uninitialized);
2791 }
2792
2793 #[test]
2794 fn onewire_read_null_device_returns_uninitialized() {
2795 let status = unsafe {
2796 gallo_onewire_read(
2797 std::ptr::null_mut(),
2798 std::ptr::null_mut(),
2799 9,
2800 std::ptr::null_mut(),
2801 )
2802 };
2803 assert_eq!(status, Status::Uninitialized);
2804 }
2805
2806 #[test]
2807 fn onewire_write_null_device_returns_uninitialized() {
2808 let data = [0xCC, 0x44];
2809 let status = unsafe { gallo_onewire_write(std::ptr::null_mut(), data.as_ptr(), 2) };
2810 assert_eq!(status, Status::Uninitialized);
2811 }
2812
2813 #[test]
2814 fn onewire_write_pullup_null_device_returns_uninitialized() {
2815 let data = [0xCC, 0x44];
2816 let status =
2817 unsafe { gallo_onewire_write_pullup(std::ptr::null_mut(), data.as_ptr(), 2, 750) };
2818 assert_eq!(status, Status::Uninitialized);
2819 }
2820
2821 #[test]
2822 fn onewire_search_null_device_returns_uninitialized() {
2823 let status = unsafe {
2824 gallo_onewire_search(
2825 std::ptr::null_mut(),
2826 std::ptr::null_mut(),
2827 10,
2828 std::ptr::null_mut(),
2829 )
2830 };
2831 assert_eq!(status, Status::Uninitialized);
2832 }
2833
2834 #[test]
2837 fn onewire_error_mapping() {
2838 assert_eq!(
2839 onewire_error_to_status(PicoDeGalloError::Endpoint(OneWireError::NoPresence)),
2840 Status::OneWireNoPresence
2841 );
2842 assert_eq!(
2843 onewire_error_to_status(PicoDeGalloError::Endpoint(OneWireError::BusError)),
2844 Status::OneWireBusError
2845 );
2846 assert_eq!(
2847 onewire_error_to_status(PicoDeGalloError::Endpoint(OneWireError::BufferTooLong)),
2848 Status::BufferTooLong
2849 );
2850 assert_eq!(
2851 onewire_error_to_status(PicoDeGalloError::Endpoint(OneWireError::Other)),
2852 Status::OneWireReadFailed
2853 );
2854 assert_eq!(
2855 onewire_error_to_status(PicoDeGalloError::Endpoint(OneWireError::Unsupported)),
2856 Status::Unsupported
2857 );
2858 }
2859
2860 #[test]
2861 fn onewire_status_codes_are_stable() {
2862 assert_eq!(Status::OneWireNoPresence as i32, -57);
2863 assert_eq!(Status::OneWireBusError as i32, -58);
2864 assert_eq!(Status::OneWireReadFailed as i32, -59);
2865 assert_eq!(Status::OneWireWriteFailed as i32, -60);
2866 assert_eq!(Status::OneWireSearchFailed as i32, -61);
2867 assert_eq!(Status::DeviceInfoFailed as i32, -62);
2868 assert_eq!(Status::SchemaMismatch as i32, -63);
2869 assert_eq!(Status::LegacyFirmware as i32, -64);
2870 assert_eq!(Status::Unsupported as i32, -65);
2871 }
2872}