From 3af76b93b261dfd4c62b9663a143df06d8fb19fa Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Thu, 23 Oct 2025 16:26:17 +0200 Subject: [PATCH] First Version of IO --- bsp_linux/hardware/hardware.c | 4 +- bsp_linux/hardware/serial.c | 15 +- mission/freeRTOS_rust_helper.c | 5 + mission/mission.c | 2 +- mission_rust/.cargo/config.toml | 2 + mission_rust/src/fsrc/osal/error.rs | 653 +++++++++++++++++++++++++++- mission_rust/src/fsrc/osal/ffi.rs | 49 ++- mission_rust/src/fsrc/osal/io.rs | 112 ++++- mission_rust/src/fsrc/sif.rs | 2 +- mission_rust/src/lib.rs | 26 +- 10 files changed, 809 insertions(+), 61 deletions(-) diff --git a/bsp_linux/hardware/hardware.c b/bsp_linux/hardware/hardware.c index e29bcb8..890e6e0 100644 --- a/bsp_linux/hardware/hardware.c +++ b/bsp_linux/hardware/hardware.c @@ -46,7 +46,7 @@ int hw_device_open(const char *path, size_t path_len) { int serial_fd = serial_open(path, path_len); - if (serial_fd != -1) { + if (serial_fd >= -1) { return serial_fd; } @@ -78,7 +78,7 @@ int hw_device_open(const char *path, size_t path_len) { for (current_candidate = addr_candidates; current_candidate != NULL; current_candidate = current_candidate->ai_next) { - sock = socket(current_candidate->ai_family, current_candidate->ai_socktype, + sock = socket(current_candidate->ai_family, current_candidate->ai_socktype | SOCK_NONBLOCK, current_candidate->ai_protocol); if (sock == -1) { continue; diff --git a/bsp_linux/hardware/serial.c b/bsp_linux/hardware/serial.c index 82bb1e5..7e2ece2 100644 --- a/bsp_linux/hardware/serial.c +++ b/bsp_linux/hardware/serial.c @@ -18,12 +18,13 @@ int convert_errno(int errno_value) { } } -// returns fd if ok, converted errno if error +// TODO: can we extend errno safely? +// returns fd if ok, -1 on error 0 if no match int serial_open_actual(const char *path, speed_t speed) { // open serial int fd = open(path, O_RDWR | O_NOCTTY | O_SYNC); if (fd < 0) { - return convert_errno(errno); + return -1; } struct termios termios; @@ -31,7 +32,7 @@ int serial_open_actual(const char *path, speed_t speed) { // initialize termios struct int ret = tcgetattr(fd, &termios); if (ret < 0) { - return convert_errno(errno); + return -1; } // configure for raw input @@ -44,19 +45,19 @@ int serial_open_actual(const char *path, speed_t speed) { // set speed ret = cfsetspeed(&termios, speed); if (ret < 0) { - return convert_errno(errno); + return -1; } ret = tcsetattr(fd, TCSANOW, &termios); if (ret < 0) { - return convert_errno(errno); + return -1; } return fd; } -// returns fd if success, -1 if no path match <-1 if error +// returns fd if success, -1 on error, -2 if no match int serial_open(const char *path, size_t path_len) { if (compare_string_chars("ps/uart_mtg", path, path_len) == 1) { return serial_open_actual("/dev/ttyUSB0", B921600); @@ -64,5 +65,5 @@ int serial_open(const char *path, size_t path_len) { if (compare_string_chars("debug/uart🚀", path, path_len) == 1) { return serial_open_actual("/dev/ttyUSB0", B115200); } - return -1; + return -2; } \ No newline at end of file diff --git a/mission/freeRTOS_rust_helper.c b/mission/freeRTOS_rust_helper.c index ac3cf81..37cf13a 100644 --- a/mission/freeRTOS_rust_helper.c +++ b/mission/freeRTOS_rust_helper.c @@ -290,4 +290,9 @@ void *freertos_mutex_create_static(char *mutex_data, uint32_t mutex_data_len) { return NULL; } return xSemaphoreCreateRecursiveMutexStatic((StaticSemaphore_t *)mutex_data); +} + +// TODO: might be the wrong place +int freertos_get_sys_error(){ + return errno; } \ No newline at end of file diff --git a/mission/mission.c b/mission/mission.c index 4c5b2ea..c151dd0 100644 --- a/mission/mission.c +++ b/mission/mission.c @@ -62,7 +62,7 @@ void mission(void) { write(1, "\n", 1); - test_hardware(); + //test_hardware(); freertos_init_and_start_scheduling(init_task); diff --git a/mission_rust/.cargo/config.toml b/mission_rust/.cargo/config.toml index e69de29..b697f11 100644 --- a/mission_rust/.cargo/config.toml +++ b/mission_rust/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.armv7a-none-eabihf] +rustflags = ['-A', 'explicit_builtin_cfgs_in_flags','--cfg', 'target_env="newlib"'] # We use gcc/newlib to link this lib crate \ No newline at end of file diff --git a/mission_rust/src/fsrc/osal/error.rs b/mission_rust/src/fsrc/osal/error.rs index 88dc0b2..c9a7d1a 100644 --- a/mission_rust/src/fsrc/osal/error.rs +++ b/mission_rust/src/fsrc/osal/error.rs @@ -1,14 +1,651 @@ -#[derive(Debug)] -pub enum UnixError{ - NoSuchFileOrDirectory, - Unknown(i32) +use core::fmt; + +use crate::fsrc::osal::ffi; + +// wrap helper shim +pub fn errno() -> i32 { + unsafe { ffi::freertos_get_sys_error() } } -impl From for UnixError{ +#[cfg(target_os = "linux")] +mod libc { + // We assume a gnu x86_64: + use core::ffi::c_int; + pub const EPERM: c_int = 1; + pub const ENOENT: c_int = 2; + pub const ESRCH: c_int = 3; + pub const EINTR: c_int = 4; + pub const EIO: c_int = 5; + pub const ENXIO: c_int = 6; + pub const E2BIG: c_int = 7; + pub const ENOEXEC: c_int = 8; + pub const EBADF: c_int = 9; + pub const ECHILD: c_int = 10; + pub const EAGAIN: c_int = 11; + //pub const EWOULDBLOCK: c_int = 11; + pub const ENOMEM: c_int = 12; + pub const EACCES: c_int = 13; + pub const EFAULT: c_int = 14; + pub const ENOTBLK: c_int = 15; + pub const EBUSY: c_int = 16; + pub const EEXIST: c_int = 17; + pub const EXDEV: c_int = 18; + pub const ENODEV: c_int = 19; + pub const ENOTDIR: c_int = 20; + pub const EISDIR: c_int = 21; + pub const EINVAL: c_int = 22; + pub const ENFILE: c_int = 23; + pub const EMFILE: c_int = 24; + pub const ENOTTY: c_int = 25; + pub const ETXTBSY: c_int = 26; + pub const EFBIG: c_int = 27; + pub const ENOSPC: c_int = 28; + pub const ESPIPE: c_int = 29; + pub const EROFS: c_int = 30; + pub const EMLINK: c_int = 31; + pub const EPIPE: c_int = 32; + pub const EDOM: c_int = 33; + pub const ERANGE: c_int = 34; + pub const EDEADLOCK: c_int = 35; + pub const ENAMETOOLONG: c_int = 36; + pub const ENOLCK: c_int = 37; + pub const ENOSYS: c_int = 38; + pub const ENOTEMPTY: c_int = 39; + pub const ELOOP: c_int = 40; + pub const ENOMSG: c_int = 42; + pub const EIDRM: c_int = 43; + pub const ECHRNG: c_int = 44; + pub const ELNRNG: c_int = 48; + pub const EUNATCH: c_int = 49; + pub const ENOCSI: c_int = 50; + pub const EBADE: c_int = 52; + pub const EBADR: c_int = 53; + pub const EXFULL: c_int = 54; + pub const ENOANO: c_int = 55; + pub const EBADRQC: c_int = 56; + pub const EBADSLT: c_int = 57; + pub const EBFONT: c_int = 59; + pub const ENOSTR: c_int = 60; + pub const ENODATA: c_int = 61; + pub const ETIME: c_int = 62; + pub const ENOSR: c_int = 63; + pub const ENONET: c_int = 64; + pub const ENOPKG: c_int = 65; + pub const EREMOTE: c_int = 66; + pub const ENOLINK: c_int = 67; + pub const EADV: c_int = 68; + pub const ESRMNT: c_int = 69; + pub const ECOMM: c_int = 70; + pub const EPROTO: c_int = 71; + pub const EMULTIHOP: c_int = 72; + pub const EDOTDOT: c_int = 73; + pub const EBADMSG: c_int = 74; + pub const EOVERFLOW: c_int = 75; + pub const ENOTUNIQ: c_int = 76; + pub const EBADFD: c_int = 77; + pub const EREMCHG: c_int = 78; + pub const ELIBACC: c_int = 79; + pub const ELIBBAD: c_int = 80; + pub const ELIBSCN: c_int = 81; + pub const ELIBMAX: c_int = 82; + pub const ELIBEXEC: c_int = 83; + pub const EILSEQ: c_int = 84; + pub const ERESTART: c_int = 85; + pub const ESTRPIPE: c_int = 86; + pub const EUSERS: c_int = 87; + pub const ENOTSOCK: c_int = 88; + pub const EDESTADDRREQ: c_int = 89; + pub const EMSGSIZE: c_int = 90; + pub const EPROTOTYPE: c_int = 91; + pub const ENOPROTOOPT: c_int = 92; + pub const EPROTONOSUPPORT: c_int = 93; + pub const ESOCKTNOSUPPORT: c_int = 94; + pub const EOPNOTSUPP: c_int = 95; + pub const EPFNOSUPPORT: c_int = 96; + pub const EAFNOSUPPORT: c_int = 97; + pub const EADDRINUSE: c_int = 98; + pub const EADDRNOTAVAIL: c_int = 99; + pub const ENETDOWN: c_int = 100; + pub const ENETUNREACH: c_int = 101; + pub const ENETRESET: c_int = 102; + pub const ECONNABORTED: c_int = 103; + pub const ECONNRESET: c_int = 104; + pub const ENOBUFS: c_int = 105; + pub const EISCONN: c_int = 106; + pub const ENOTCONN: c_int = 107; + pub const ESHUTDOWN: c_int = 108; + pub const ETOOMANYREFS: c_int = 109; + pub const ETIMEDOUT: c_int = 110; + pub const ECONNREFUSED: c_int = 111; + pub const EHOSTDOWN: c_int = 112; + pub const EHOSTUNREACH: c_int = 113; + pub const EALREADY: c_int = 114; + pub const EINPROGRESS: c_int = 115; + pub const ESTALE: c_int = 116; + pub const EUCLEAN: c_int = 117; + pub const ENOTNAM: c_int = 118; + pub const ENAVAIL: c_int = 119; + pub const EISNAM: c_int = 120; + pub const EREMOTEIO: c_int = 121; + pub const EDQUOT: c_int = 122; + pub const ENOMEDIUM: c_int = 123; + pub const EMEDIUMTYPE: c_int = 124; + pub const ECANCELED: c_int = 125; + pub const ENOKEY: c_int = 126; + pub const EKEYEXPIRED: c_int = 127; + pub const EKEYREVOKED: c_int = 128; + pub const EKEYREJECTED: c_int = 129; + pub const EOWNERDEAD: c_int = 130; + pub const ENOTRECOVERABLE: c_int = 131; + pub const ERFKILL: c_int = 132; + pub const EHWPOISON: c_int = 133; +} + +#[cfg(target_env = "newlib")] +mod libc { + // taken from rust-lang/libc/src/unix/newlib/mod.rs + use core::ffi::c_int; + pub const EPERM: c_int = 1; + pub const ENOENT: c_int = 2; + pub const ESRCH: c_int = 3; + pub const EINTR: c_int = 4; + pub const EIO: c_int = 5; + pub const ENXIO: c_int = 6; + pub const E2BIG: c_int = 7; + pub const ENOEXEC: c_int = 8; + pub const EBADF: c_int = 9; + pub const ECHILD: c_int = 10; + pub const EAGAIN: c_int = 11; + pub const ENOMEM: c_int = 12; + pub const EACCES: c_int = 13; + pub const EFAULT: c_int = 14; + pub const EBUSY: c_int = 16; + pub const EEXIST: c_int = 17; + pub const EXDEV: c_int = 18; + pub const ENODEV: c_int = 19; + pub const ENOTDIR: c_int = 20; + pub const EISDIR: c_int = 21; + pub const EINVAL: c_int = 22; + pub const ENFILE: c_int = 23; + pub const EMFILE: c_int = 24; + pub const ENOTTY: c_int = 25; + pub const ETXTBSY: c_int = 26; + pub const EFBIG: c_int = 27; + pub const ENOSPC: c_int = 28; + pub const ESPIPE: c_int = 29; + pub const EROFS: c_int = 30; + pub const EMLINK: c_int = 31; + pub const EPIPE: c_int = 32; + pub const EDOM: c_int = 33; + pub const ERANGE: c_int = 34; + pub const ENOMSG: c_int = 35; + pub const EIDRM: c_int = 36; + pub const EDEADLK: c_int = 45; + pub const ENOLCK: c_int = 46; + pub const ENOSTR: c_int = 60; + pub const ENODATA: c_int = 61; + pub const ETIME: c_int = 62; + pub const ENOSR: c_int = 63; + pub const ENOLINK: c_int = 67; + pub const EPROTO: c_int = 71; + pub const EMULTIHOP: c_int = 74; + pub const EBADMSG: c_int = 77; + pub const EFTYPE: c_int = 79; + pub const ENOSYS: c_int = 88; + pub const ENOTEMPTY: c_int = 90; + pub const ENAMETOOLONG: c_int = 91; + pub const ELOOP: c_int = 92; + pub const EOPNOTSUPP: c_int = 95; + pub const EPFNOSUPPORT: c_int = 96; + pub const ECONNRESET: c_int = 104; + pub const ENOBUFS: c_int = 105; + pub const EAFNOSUPPORT: c_int = 106; + pub const EPROTOTYPE: c_int = 107; + pub const ENOTSOCK: c_int = 108; + pub const ENOPROTOOPT: c_int = 109; + pub const ECONNREFUSED: c_int = 111; + pub const EADDRINUSE: c_int = 112; + pub const ECONNABORTED: c_int = 113; + pub const ENETUNREACH: c_int = 114; + pub const ENETDOWN: c_int = 115; + pub const ETIMEDOUT: c_int = 116; + pub const EHOSTDOWN: c_int = 117; + pub const EHOSTUNREACH: c_int = 118; + pub const EINPROGRESS: c_int = 119; + pub const EALREADY: c_int = 120; + pub const EDESTADDRREQ: c_int = 121; + pub const EMSGSIZE: c_int = 122; + pub const EPROTONOSUPPORT: c_int = 123; + pub const EADDRNOTAVAIL: c_int = 125; + pub const ENETRESET: c_int = 126; + pub const EISCONN: c_int = 127; + pub const ENOTCONN: c_int = 128; + pub const ETOOMANYREFS: c_int = 129; + pub const EDQUOT: c_int = 132; + pub const ESTALE: c_int = 133; + pub const ENOTSUP: c_int = 134; + pub const EILSEQ: c_int = 138; + pub const EOVERFLOW: c_int = 139; + pub const ECANCELED: c_int = 140; + pub const ENOTRECOVERABLE: c_int = 141; + pub const EOWNERDEAD: c_int = 142; + pub const EWOULDBLOCK: c_int = 11; +} + +#[derive(Debug, PartialEq)] +pub enum OsError { + OperationNotPermitted, + NoSuchFileOrDirectory, + NoSuchProcess, + InterruptedSystemCall, + InputOutputError, + NoSuchDeviceOrAddress, + ArgumentListTooLong, + ExecFormatError, + BadFileDescriptor, + NoChildProcesses, + ResourceTemporarilyUnavailable, + CannotAllocateMemory, + PermissionDenied, + BadAddress, + BlockDeviceRequired, + DeviceOrResourceBusy, + FileExists, + InvalidCrossDeviceLink, + NoSuchDevice, + NotADirectory, + IsADirectory, + InvalidArgument, + TooManyOpenFilesInSystem, + TooManyOpenFiles, + InappropriateIoctlForDevice, + TextFileBusy, + FileTooLarge, + NoSpaceLeftOnDevice, + IllegalSeek, + ReadOnlyFileSystem, + TooManyLinks, + BrokenPipe, + NumericalArgumentOutOfDomain, + NumericalResultOutOfRange, + ResourceDeadlockAvoided, + FileNameTooLong, + NoLocksAvailable, + FunctionNotImplemented, + DirectoryNotEmpty, + TooManyLevelsOfSymbolicLinks, + NoMessageOfDesiredType, + IdentifierRemoved, + ChannelNumberOutOfRange, + LinkNumberOutOfRange, + ProtocolDriverNotAttached, + NoCsiStructureAvailable, + InvalidExchange, + InvalidRequestDescriptor, + ExchangeFull, + NoAnode, + InvalidRequestCode, + InvalidSlot, + BadFontFileFormat, + DeviceNotAStream, + NoDataAvailable, + TimerExpired, + OutOfStreamsResources, + MachineIsNotOnTheNetwork, + PackageNotInstalled, + ObjectIsRemote, + LinkHasBeenSevered, + AdvertiseError, + SrmountError, + CommunicationErrorOnSend, + ProtocolError, + MultihopAttempted, + RfsSpecificError, + BadMessage, + ValueTooLargeForDefinedDataType, + NameNotUniqueOnNetwork, + FileDescriptorInBadState, + RemoteAddressChanged, + CanNotAccessANeededSharedLibrary, + AccessingACorruptedSharedLibrary, + LibSectionCorrupted, + AttemptingToLinkInTooManySharedLibraries, + CannotExecASharedLibraryDirectly, + InvalidOrIncompleteMultibyteOrWideCharacter, + InterruptedSystemCallShouldBeRestarted, + StreamsPipeError, + TooManyUsers, + SocketOperationOnNonSocket, + DestinationAddressRequired, + MessageTooLong, + ProtocolWrongTypeForSocket, + ProtocolNotAvailable, + ProtocolNotSupported, + SocketTypeNotSupported, + OperationNotSupported, + ProtocolFamilyNotSupported, + AddressFamilyNotSupportedByProtocol, + AddressAlreadyInUse, + CannotAssignRequestedAddress, + NetworkIsDown, + NetworkIsUnreachable, + NetworkDroppedConnectionOnReset, + SoftwareCausedConnectionAbort, + ConnectionResetByPeer, + NoBufferSpaceAvailable, + TransportEndpointIsAlreadyConnected, + TransportEndpointIsNotConnected, + CannotSendAfterTransportEndpointShutdown, + TooManyReferencesCannotSplice, + ConnectionTimedOut, + ConnectionRefused, + HostIsDown, + NoRouteToHost, + OperationAlreadyInProgress, + OperationNowInProgress, + StaleFileHandle, + StructureNeedsCleaning, + NotAXenixNamedTypeFile, + NoXenixSemaphoresAvailable, + IsANamedTypeFile, + RemoteIOError, + DiskQuotaExceeded, + NoMediumFound, + WrongMediumType, + OperationCanceled, + RequiredKeyNotAvailable, + KeyHasExpired, + KeyHasBeenRevoked, + KeyWasRejectedByService, + OwnerDied, + StateNotRecoverable, + OperationNotPossibleDueToRfKill, + MemoryPageHasHardwareError, + Other(i32), +} + +impl From for OsError { fn from(value: i32) -> Self { + use crate::osal::io::OsError::*; match value { - 2 => UnixError::NoSuchFileOrDirectory, - any => Self::Unknown(any) + libc::EPERM => OperationNotPermitted, + libc::ENOENT => NoSuchFileOrDirectory, + libc::ESRCH => NoSuchProcess, + libc::EINTR => InterruptedSystemCall, + libc::EIO => InputOutputError, + libc::ENXIO => NoSuchDeviceOrAddress, + libc::E2BIG => ArgumentListTooLong, + libc::ENOEXEC => ExecFormatError, + libc::EBADF => BadFileDescriptor, + libc::ECHILD => NoChildProcesses, + libc::EAGAIN => ResourceTemporarilyUnavailable, + //libc::EWOULDBLOCK => ResourceTemporarilyUnavailable, == EAGAIN + libc::ENOMEM => CannotAllocateMemory, + libc::EACCES => PermissionDenied, + libc::EFAULT => BadAddress, + libc::ENOTBLK => BlockDeviceRequired, + libc::EBUSY => DeviceOrResourceBusy, + libc::EEXIST => FileExists, + libc::EXDEV => InvalidCrossDeviceLink, + libc::ENODEV => NoSuchDevice, + libc::ENOTDIR => NotADirectory, + libc::EISDIR => IsADirectory, + libc::EINVAL => InvalidArgument, + libc::ENFILE => TooManyOpenFilesInSystem, + libc::EMFILE => TooManyOpenFiles, + libc::ENOTTY => InappropriateIoctlForDevice, + libc::ETXTBSY => TextFileBusy, + libc::EFBIG => FileTooLarge, + libc::ENOSPC => NoSpaceLeftOnDevice, + libc::ESPIPE => IllegalSeek, + libc::EROFS => ReadOnlyFileSystem, + libc::EMLINK => TooManyLinks, + libc::EPIPE => BrokenPipe, + libc::EDOM => NumericalArgumentOutOfDomain, + libc::ERANGE => NumericalResultOutOfRange, + libc::EDEADLOCK => ResourceDeadlockAvoided, + libc::ENAMETOOLONG => FileNameTooLong, + libc::ENOLCK => NoLocksAvailable, + libc::ENOSYS => FunctionNotImplemented, + libc::ENOTEMPTY => DirectoryNotEmpty, + libc::ELOOP => TooManyLevelsOfSymbolicLinks, + libc::ENOMSG => NoMessageOfDesiredType, + libc::EIDRM => IdentifierRemoved, + libc::ECHRNG => ChannelNumberOutOfRange, + libc::ELNRNG => LinkNumberOutOfRange, + libc::EUNATCH => ProtocolDriverNotAttached, + libc::ENOCSI => NoCsiStructureAvailable, + libc::EBADE => InvalidExchange, + libc::EBADR => InvalidRequestDescriptor, + libc::EXFULL => ExchangeFull, + libc::ENOANO => NoAnode, + libc::EBADRQC => InvalidRequestCode, + libc::EBADSLT => InvalidSlot, + libc::EBFONT => BadFontFileFormat, + libc::ENOSTR => DeviceNotAStream, + libc::ENODATA => NoDataAvailable, + libc::ETIME => TimerExpired, + libc::ENOSR => OutOfStreamsResources, + libc::ENONET => MachineIsNotOnTheNetwork, + libc::ENOPKG => PackageNotInstalled, + libc::EREMOTE => ObjectIsRemote, + libc::ENOLINK => LinkHasBeenSevered, + libc::EADV => AdvertiseError, + libc::ESRMNT => SrmountError, + libc::ECOMM => CommunicationErrorOnSend, + libc::EPROTO => ProtocolError, + libc::EMULTIHOP => MultihopAttempted, + libc::EDOTDOT => RfsSpecificError, + libc::EBADMSG => BadMessage, + libc::EOVERFLOW => ValueTooLargeForDefinedDataType, + libc::ENOTUNIQ => NameNotUniqueOnNetwork, + libc::EBADFD => FileDescriptorInBadState, + libc::EREMCHG => RemoteAddressChanged, + libc::ELIBACC => CanNotAccessANeededSharedLibrary, + libc::ELIBBAD => AccessingACorruptedSharedLibrary, + libc::ELIBSCN => LibSectionCorrupted, + libc::ELIBMAX => AttemptingToLinkInTooManySharedLibraries, + libc::ELIBEXEC => CannotExecASharedLibraryDirectly, + libc::EILSEQ => InvalidOrIncompleteMultibyteOrWideCharacter, + libc::ERESTART => InterruptedSystemCallShouldBeRestarted, + libc::ESTRPIPE => StreamsPipeError, + libc::EUSERS => TooManyUsers, + libc::ENOTSOCK => SocketOperationOnNonSocket, + libc::EDESTADDRREQ => DestinationAddressRequired, + libc::EMSGSIZE => MessageTooLong, + libc::EPROTOTYPE => ProtocolWrongTypeForSocket, + libc::ENOPROTOOPT => ProtocolNotAvailable, + libc::EPROTONOSUPPORT => ProtocolNotSupported, + libc::ESOCKTNOSUPPORT => SocketTypeNotSupported, + libc::EOPNOTSUPP => OperationNotSupported, + // libc::ENOTSUP => OperationNotSupported, == EOPNOTSUPP + libc::EPFNOSUPPORT => ProtocolFamilyNotSupported, + libc::EAFNOSUPPORT => AddressFamilyNotSupportedByProtocol, + libc::EADDRINUSE => AddressAlreadyInUse, + libc::EADDRNOTAVAIL => CannotAssignRequestedAddress, + libc::ENETDOWN => NetworkIsDown, + libc::ENETUNREACH => NetworkIsUnreachable, + libc::ENETRESET => NetworkDroppedConnectionOnReset, + libc::ECONNABORTED => SoftwareCausedConnectionAbort, + libc::ECONNRESET => ConnectionResetByPeer, + libc::ENOBUFS => NoBufferSpaceAvailable, + libc::EISCONN => TransportEndpointIsAlreadyConnected, + libc::ENOTCONN => TransportEndpointIsNotConnected, + libc::ESHUTDOWN => CannotSendAfterTransportEndpointShutdown, + libc::ETOOMANYREFS => TooManyReferencesCannotSplice, + libc::ETIMEDOUT => ConnectionTimedOut, + libc::ECONNREFUSED => ConnectionRefused, + libc::EHOSTDOWN => HostIsDown, + libc::EHOSTUNREACH => NoRouteToHost, + libc::EALREADY => OperationAlreadyInProgress, + libc::EINPROGRESS => OperationNowInProgress, + libc::ESTALE => StaleFileHandle, + libc::EUCLEAN => StructureNeedsCleaning, + libc::ENOTNAM => NotAXenixNamedTypeFile, + libc::ENAVAIL => NoXenixSemaphoresAvailable, + libc::EISNAM => IsANamedTypeFile, + libc::EREMOTEIO => RemoteIOError, + libc::EDQUOT => DiskQuotaExceeded, + libc::ENOMEDIUM => NoMediumFound, + libc::EMEDIUMTYPE => WrongMediumType, + libc::ECANCELED => OperationCanceled, + libc::ENOKEY => RequiredKeyNotAvailable, + libc::EKEYEXPIRED => KeyHasExpired, + libc::EKEYREVOKED => KeyHasBeenRevoked, + libc::EKEYREJECTED => KeyWasRejectedByService, + libc::EOWNERDEAD => OwnerDied, + libc::ENOTRECOVERABLE => StateNotRecoverable, + libc::ERFKILL => OperationNotPossibleDueToRfKill, + libc::EHWPOISON => MemoryPageHasHardwareError, + value => Other(value), } } -} \ No newline at end of file +} + +impl fmt::Display for OsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use OsError::*; + match self { + OperationNotPermitted => write!(f, "Operation not permitted"), + NoSuchFileOrDirectory => write!(f, "No such file or directory"), + NoSuchProcess => write!(f, "No such process"), + InterruptedSystemCall => write!(f, "Interrupted system call"), + InputOutputError => write!(f, "Input/output error"), + NoSuchDeviceOrAddress => write!(f, "No such device or address"), + ArgumentListTooLong => write!(f, "Argument list too long"), + ExecFormatError => write!(f, "Exec format error"), + BadFileDescriptor => write!(f, "Bad file descriptor"), + NoChildProcesses => write!(f, "No child processes"), + ResourceTemporarilyUnavailable => write!(f, "Resource temporarily unavailable"), + CannotAllocateMemory => write!(f, "Cannot allocate memory"), + PermissionDenied => write!(f, "Permission denied"), + BadAddress => write!(f, "Bad address"), + BlockDeviceRequired => write!(f, "Block device required"), + DeviceOrResourceBusy => write!(f, "Device or resource busy"), + FileExists => write!(f, "File exists"), + InvalidCrossDeviceLink => write!(f, "Invalid cross-device link"), + NoSuchDevice => write!(f, "No such device"), + NotADirectory => write!(f, "Not a directory"), + IsADirectory => write!(f, "Is a directory"), + InvalidArgument => write!(f, "Invalid argument"), + TooManyOpenFilesInSystem => write!(f, "Too many open files in system"), + TooManyOpenFiles => write!(f, "Too many open files"), + InappropriateIoctlForDevice => write!(f, "Inappropriate ioctl for device"), + TextFileBusy => write!(f, "Text file busy"), + FileTooLarge => write!(f, "File too large"), + NoSpaceLeftOnDevice => write!(f, "No space left on device"), + IllegalSeek => write!(f, "Illegal seek"), + ReadOnlyFileSystem => write!(f, "Read-only file system"), + TooManyLinks => write!(f, "Too many links"), + BrokenPipe => write!(f, "Broken pipe"), + NumericalArgumentOutOfDomain => write!(f, "Numerical argument out of domain"), + NumericalResultOutOfRange => write!(f, "Numerical result out of range"), + ResourceDeadlockAvoided => write!(f, "Resource deadlock avoided"), + FileNameTooLong => write!(f, "File name too long"), + NoLocksAvailable => write!(f, "No locks available"), + FunctionNotImplemented => write!(f, "Function not implemented"), + DirectoryNotEmpty => write!(f, "Directory not empty"), + TooManyLevelsOfSymbolicLinks => write!(f, "Too many levels of symbolic links"), + NoMessageOfDesiredType => write!(f, "No message of desired type"), + IdentifierRemoved => write!(f, "Identifier removed"), + ChannelNumberOutOfRange => write!(f, "Channel number out of range"), + LinkNumberOutOfRange => write!(f, "Link number out of range"), + ProtocolDriverNotAttached => write!(f, "Protocol driver not attached"), + NoCsiStructureAvailable => write!(f, "No CSI structure available"), + InvalidExchange => write!(f, "Invalid exchange"), + InvalidRequestDescriptor => write!(f, "Invalid request descriptor"), + ExchangeFull => write!(f, "Exchange full"), + NoAnode => write!(f, "No anode"), + InvalidRequestCode => write!(f, "Invalid request code"), + InvalidSlot => write!(f, "Invalid slot"), + BadFontFileFormat => write!(f, "Bad font file format"), + DeviceNotAStream => write!(f, "Device not a stream"), + NoDataAvailable => write!(f, "No data available"), + TimerExpired => write!(f, "Timer expired"), + OutOfStreamsResources => write!(f, "Out of streams resources"), + MachineIsNotOnTheNetwork => write!(f, "Machine is not on the network"), + PackageNotInstalled => write!(f, "Package not installed"), + ObjectIsRemote => write!(f, "Object is remote"), + LinkHasBeenSevered => write!(f, "Link has been severed"), + AdvertiseError => write!(f, "Advertise error"), + SrmountError => write!(f, "Srmount error"), + CommunicationErrorOnSend => write!(f, "Communication error on send"), + ProtocolError => write!(f, "Protocol error"), + MultihopAttempted => write!(f, "Multihop attempted"), + RfsSpecificError => write!(f, "RFS specific error"), + BadMessage => write!(f, "Bad message"), + ValueTooLargeForDefinedDataType => write!(f, "Value too large for defined data type"), + NameNotUniqueOnNetwork => write!(f, "Name not unique on network"), + FileDescriptorInBadState => write!(f, "File descriptor in bad state"), + RemoteAddressChanged => write!(f, "Remote address changed"), + CanNotAccessANeededSharedLibrary => write!(f, "Can not access a needed shared library"), + AccessingACorruptedSharedLibrary => write!(f, "Accessing a corrupted shared library"), + LibSectionCorrupted => write!(f, ".lib section in a.out corrupted"), + AttemptingToLinkInTooManySharedLibraries => { + write!(f, "Attempting to link in too many shared libraries") + } + CannotExecASharedLibraryDirectly => write!(f, "Cannot exec a shared library directly"), + InvalidOrIncompleteMultibyteOrWideCharacter => { + write!(f, "Invalid or incomplete multibyte or wide character") + } + InterruptedSystemCallShouldBeRestarted => { + write!(f, "Interrupted system call should be restarted") + } + StreamsPipeError => write!(f, "Streams pipe error"), + TooManyUsers => write!(f, "Too many users"), + SocketOperationOnNonSocket => write!(f, "Socket operation on non-socket"), + DestinationAddressRequired => write!(f, "Destination address required"), + MessageTooLong => write!(f, "Message too long"), + ProtocolWrongTypeForSocket => write!(f, "Protocol wrong type for socket"), + ProtocolNotAvailable => write!(f, "Protocol not available"), + ProtocolNotSupported => write!(f, "Protocol not supported"), + SocketTypeNotSupported => write!(f, "Socket type not supported"), + OperationNotSupported => write!(f, "Operation not supported"), + ProtocolFamilyNotSupported => write!(f, "Protocol family not supported"), + AddressFamilyNotSupportedByProtocol => { + write!(f, "Address family not supported by protocol") + } + AddressAlreadyInUse => write!(f, "Address already in use"), + CannotAssignRequestedAddress => write!(f, "Cannot assign requested address"), + NetworkIsDown => write!(f, "Network is down"), + NetworkIsUnreachable => write!(f, "Network is unreachable"), + NetworkDroppedConnectionOnReset => write!(f, "Network dropped connection on reset"), + SoftwareCausedConnectionAbort => write!(f, "Software caused connection abort"), + ConnectionResetByPeer => write!(f, "Connection reset by peer"), + NoBufferSpaceAvailable => write!(f, "No buffer space available"), + TransportEndpointIsAlreadyConnected => { + write!(f, "Transport endpoint is already connected") + } + TransportEndpointIsNotConnected => write!(f, "Transport endpoint is not connected"), + CannotSendAfterTransportEndpointShutdown => { + write!(f, "Cannot send after transport endpoint shutdown") + } + TooManyReferencesCannotSplice => write!(f, "Too many references: cannot splice"), + ConnectionTimedOut => write!(f, "Connection timed out"), + ConnectionRefused => write!(f, "Connection refused"), + HostIsDown => write!(f, "Host is down"), + NoRouteToHost => write!(f, "No route to host"), + OperationAlreadyInProgress => write!(f, "Operation already in progress"), + OperationNowInProgress => write!(f, "Operation now in progress"), + StaleFileHandle => write!(f, "Stale file handle"), + StructureNeedsCleaning => write!(f, "Structure needs cleaning"), + NotAXenixNamedTypeFile => write!(f, "Not a XENIX named type file"), + NoXenixSemaphoresAvailable => write!(f, "No XENIX semaphores available"), + IsANamedTypeFile => write!(f, "Is a named type file"), + RemoteIOError => write!(f, "Remote I/O error"), + DiskQuotaExceeded => write!(f, "Disk quota exceeded"), + NoMediumFound => write!(f, "No medium found"), + WrongMediumType => write!(f, "Wrong medium type"), + OperationCanceled => write!(f, "Operation canceled"), + RequiredKeyNotAvailable => write!(f, "Required key not available"), + KeyHasExpired => write!(f, "Key has expired"), + KeyHasBeenRevoked => write!(f, "Key has been revoked"), + KeyWasRejectedByService => write!(f, "Key was rejected by service"), + OwnerDied => write!(f, "Owner died"), + StateNotRecoverable => write!(f, "State not recoverable"), + OperationNotPossibleDueToRfKill => write!(f, "Operation not possible due to RF-kill"), + MemoryPageHasHardwareError => write!(f, "Memory page has hardware error"), + Other(value) => write!(f, "Other error: {}", value), + } + } +} diff --git a/mission_rust/src/fsrc/osal/ffi.rs b/mission_rust/src/fsrc/osal/ffi.rs index f5c2322..6229a2d 100644 --- a/mission_rust/src/fsrc/osal/ffi.rs +++ b/mission_rust/src/fsrc/osal/ffi.rs @@ -3,9 +3,7 @@ type TaskFunction = unsafe extern "C" fn(*mut core::ffi::c_void) -> !; - -pub struct Sizes { -} +pub struct Sizes {} // TODO these should be passed by compile time value #[cfg(target_os = "linux")] @@ -17,7 +15,7 @@ impl Sizes { } // We use none for bare metal, as we only support one architecture so far -#[cfg(target_os = "none")] +#[cfg(target_env = "newlib")] impl Sizes { pub const TASK_DATA_SIZE: usize = 184; pub const MUTEX_DATA_SIZE: usize = 168; @@ -25,12 +23,10 @@ impl Sizes { pub const TASK_MINIMAL_STACK_SIZE: usize = 10240; } - extern "C" { //////////////////////// /// FreeRTOS API /// shimmed in mission/freeRTOS_rust_helper.c - //void *create_task_static(TaskFunction_t taskFunction, void *parameter, // char *task_data, uint32_t task_data_len, char *stack, uint32_t stack_size) pub fn freertos_create_task_static( @@ -49,7 +45,10 @@ extern "C" { pub fn freertos_task_delete(handle: *const core::ffi::c_void) -> !; - pub fn freertos_task_storage_set(handle: *const core::ffi::c_void, data: *const core::ffi::c_void); + pub fn freertos_task_storage_set( + handle: *const core::ffi::c_void, + data: *const core::ffi::c_void, + ); pub fn freertos_task_storage_get(handle: *const core::ffi::c_void) -> *const core::ffi::c_void; @@ -60,7 +59,7 @@ extern "C" { pub fn freertos_task_current() -> *const core::ffi::c_void; // TODO this should be passed by compile time value - pub fn freertos_task_priority_max()-> u32; + pub fn freertos_task_priority_max() -> u32; //void *freertos_queue_create_static(uint32_t depth, uint32_t element_size, // char *queue_data, uint32_t queue_data_len, @@ -70,11 +69,17 @@ extern "C" { element_size: u32, queue_data: *mut core::ffi::c_char, queue_data_len: u32, - queue: *mut u8 + queue: *mut u8, ) -> *const core::ffi::c_void; - pub fn freertos_queue_receive(queue: *const core::ffi::c_void, message: *const core::ffi::c_void) -> u8; - pub fn freertos_queue_send(queue: *const core::ffi::c_void, message: *const core::ffi::c_void) -> u8; + pub fn freertos_queue_receive( + queue: *const core::ffi::c_void, + message: *const core::ffi::c_void, + ) -> u8; + pub fn freertos_queue_send( + queue: *const core::ffi::c_void, + message: *const core::ffi::c_void, + ) -> u8; pub fn freertos_mutex_create_static( mutex_data: *const core::ffi::c_char, @@ -87,17 +92,18 @@ extern "C" { //uint8_t freertos_simple_once(uint8_t *once_data) pub fn freertos_simple_once(once_data: *const u8) -> u8; + //int freertos_get_sys_error() + pub fn freertos_get_sys_error() -> core::ffi::c_int; //////////////////////// /// Harware Abstraction API in common/include/interfaces.h /// Used for access to peripherals to make switching between linux and embedded easier - //int hw_device_open(const char * path, size_t path_len); pub fn hw_device_open(path: *const core::ffi::c_char, path_len: usize) -> core::ffi::c_int; //////////////////////// /// POSIX/UNIX/LINUX API - + //TODO, linux seems to use ssize_t (confirm!), newlib int as return value and count #[cfg(target_os = "linux")] pub fn write(fd: core::ffi::c_int, buffer: *const core::ffi::c_void, count: usize) -> isize; @@ -105,10 +111,17 @@ extern "C" { #[cfg(target_os = "linux")] pub fn read(fd: core::ffi::c_int, buffer: *const core::ffi::c_void, count: usize) -> isize; - #[cfg(target_os = "none")] - pub fn write(fd: core::ffi::c_int, buffer: *const core::ffi::c_void, count: core::ffi::c_int) -> core::ffi::c_int; + #[cfg(target_env = "newlib")] + pub fn write( + fd: core::ffi::c_int, + buffer: *const core::ffi::c_void, + count: core::ffi::c_int, + ) -> core::ffi::c_int; - #[cfg(target_os = "none")] - pub fn read(fd: core::ffi::c_int, buffer: *const core::ffi::c_void, count: core::ffi::c_int) -> core::ffi::c_int; + #[cfg(target_env = "newlib")] + pub fn read( + fd: core::ffi::c_int, + buffer: *const core::ffi::c_void, + count: core::ffi::c_int, + ) -> core::ffi::c_int; } - diff --git a/mission_rust/src/fsrc/osal/io.rs b/mission_rust/src/fsrc/osal/io.rs index 17c0589..f4e1c18 100644 --- a/mission_rust/src/fsrc/osal/io.rs +++ b/mission_rust/src/fsrc/osal/io.rs @@ -1,46 +1,114 @@ -pub use super::error::UnixError; +use core::fmt::Display; + +pub use super::error::OsError; +use super::ffi; #[derive(Debug)] -pub enum Error{ - Unix(UnixError), - AlreadyOpen, +pub enum Error { + Os(OsError), + IncompleteWrite(usize), InvalidPath, - Unknown(i32) + Unknown(i32), + InternalError, } -// TODO error values should be in ffi -impl From for Error { - fn from(value: i32) -> Self { - match value { - -2 => Error::AlreadyOpen, - -3 => Error::InvalidPath, - any => Error::Unknown(any) +impl Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "😢")?; + match self { + Error::Os(os_error) => write!(f, "Os::{}", os_error), + _ => write!(f, "not implemented"), } } } -pub type Result = core::result::Result; +pub type Result = core::result::Result; + +pub trait Read { + fn read<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8]>; +} + +pub trait Write { + fn write(&mut self, data: &[u8]) -> Result<()>; +} #[derive(Debug)] -pub struct Interface{ +pub struct HardwareInterface { fd: core::ffi::c_int, } -impl Interface { +impl HardwareInterface { pub fn new(path: &str) -> Result { // Safe: we trust our own implementation to only read // as we cut our string into bytes, conversion to c_char is correct, as C will compare bit value - let fd = unsafe{ crate::osal::ffi::hw_device_open(path.as_bytes().as_ptr() as *const core::ffi::c_char, path.as_bytes().len())}; + let fd = unsafe { + crate::osal::ffi::hw_device_open( + path.as_bytes().as_ptr() as *const core::ffi::c_char, + path.as_bytes().len(), + ) + }; if fd >= 0 { Ok(Self { fd }) } else { - // revert errno conversion done by hw_device_open - let errno = -fd -1; - Err(Error::Unix(errno.into())) + // Safe: getter on os + let errno = unsafe { ffi::freertos_get_sys_error() }; + Err(Error::Os(errno.into())) } } +} - pub fn write(&mut self, data: &[u8]) { - +impl Write for HardwareInterface { + fn write(&mut self, data: &[u8]) -> Result<()> { + let written = unsafe { + ffi::write( + self.fd, + data.as_ptr() as *const core::ffi::c_void, + data.len().into(), + ) + }; + if written < 0 { + // Safe: getter on os + let errno = unsafe { ffi::freertos_get_sys_error() }; + return Err(Error::Os(errno.into())); + } else { + let Ok(written_unsigned) = written.try_into() else { + // we are in written >=0, this should never happen + return Err(Error::InternalError); + }; + if written_unsigned < data.len() { + return Err(Error::IncompleteWrite(written_unsigned)); + } else { + return Ok(()); + } + } } -} \ No newline at end of file +} + +impl Read for HardwareInterface { + fn read<'a>(&self, buffer: &'a [u8]) -> Result<&'a [u8]> { + // Safe as we trust the OS + let read = unsafe { + ffi::read( + self.fd, + buffer.as_ptr() as *const core::ffi::c_void, + buffer.len().into(), + ) + }; + if read < 0 { + // Safe: getter on os + let errno = unsafe { ffi::freertos_get_sys_error() }; + let error = errno.into(); + if error == OsError::ResourceTemporarilyUnavailable { + // non blocking call returned + return Ok(&[]); + } + return Err(Error::Os(error)); + } else { + let Ok(read_unsigned) = read.try_into() else { + // we are in read >=0, this should never happen + return Err(Error::InternalError); + }; + Ok(&buffer[..read_unsigned]) + } + } +} diff --git a/mission_rust/src/fsrc/sif.rs b/mission_rust/src/fsrc/sif.rs index 0641902..8d9074e 100644 --- a/mission_rust/src/fsrc/sif.rs +++ b/mission_rust/src/fsrc/sif.rs @@ -76,7 +76,7 @@ macro_rules! sifln { writeln!(Stdout {}); ); ($($arg:tt)*) => ( - let _alwaysok = writeln!(crate::fsrc::sif::Stdout {}, $($arg)*); + {let _alwaysok = writeln!(crate::fsrc::sif::Stdout {}, $($arg)*);} ); } diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 18ddc08..aee4dfa 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -16,7 +16,7 @@ use osal::{ thread, }; -use crate::fsrc::osal::io::Interface; +use crate::fsrc::osal::io::{self, HardwareInterface, Read, Write as IoWrite}; @@ -184,8 +184,30 @@ static THREAD_3: StaticReadOnceLock< fn init_task() -> ! { sifln!("Mission enter"); - sifln!("{:?}", Interface::new("debug/uart🚀")); + let interface = HardwareInterface::new("ps/uart_mtg"); + let Ok(mut interface) = interface else { + let error : io::Error = interface.err().unwrap(); + sifln!("open: {error}"); + panic!(); + }; + let buffer = [1,2,3,4]; + interface.write(&buffer).unwrap_or_else(|e| sifln!("write: {e}")); + + let mut buffer = [0u8;30]; + + let res = interface.read(&mut buffer).unwrap_or_else(|e| {sifln!("read: {e}"); &[]}); + sifln!("{res:?}"); + + osal::thread::current().delay(Duration::from_millis(500)); + + let res = interface.read(&mut buffer).unwrap_or_else(|e| {sifln!("read: {e}"); &[]}); + sifln!("{res:?}"); + + osal::thread::current().delay(Duration::from_millis(500)); + + let res = interface.read(&mut buffer).unwrap_or_else(|e| {sifln!("read: {e}"); &[]}); + sifln!("{res:?}"); let test1 = TEST1.take().unwrap(); let test2 = TEST2.take().unwrap();