Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
jobs:
linux:
runs-on: ubuntu-24.04
container: swift:6.2
container: swift:6.3
steps:
- name: Packages
run: |
Expand Down
23 changes: 19 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
// swift-tools-version: 6.3
// The @c attribute used by the native-Swift HAL requires the Swift 6.3 compiler;
// gate older toolchains here rather than failing mid-build with cryptic errors.

import Foundation
import PackageDescription
Expand Down Expand Up @@ -51,15 +52,27 @@ let package = Package(
.package(url: "https://github.com/apple/swift-system", from: "1.0.0"),
],
targets: [
// Residual C the Swift HAL calls (inline-assembly hwcycle, termios2 UART
// helpers, fs mount point, ioctl/mq/syslog shims) plus the public HAL headers.
.target(
name: "LinuxHalSwiftIO",
name: "CLinuxHalSwiftIO",
dependencies: [
.product(name: "CSwiftIO", package: "SwiftIO"),
],
linkerSettings: [
.linkedLibrary("gpiod", .when(platforms: [.linux])),
]
),
// Native-Swift HAL implementation. Exports the same C ABI via @c and
// re-exports the C module so `import LinuxHalSwiftIO` still surfaces the
// HAL declarations to consumers (AsyncSwiftIO, SwiftIO).
.target(
name: "LinuxHalSwiftIO",
dependencies: [
"CLinuxHalSwiftIO",
.product(name: "CSwiftIO", package: "SwiftIO"),
]
),
.target(
name: "AsyncSwiftIO",
dependencies: [
Expand Down Expand Up @@ -98,5 +111,7 @@ let package = Package(
.target(name: "AsyncSwiftIO"),
]
),
]
],
// Keep the Swift 5 language mode the package built with under tools 5.7.
swiftLanguageModes: [.v5]
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,16 @@
// limitations under the License.
//

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
// Exposes libgpiod to the native-Swift GPIO implementation (GPIO.swift). The
// request-type/flag values are enum constants (not bare macros) and the structs
// import cleanly, so no wrappers are needed; Swift calls gpiod directly.

#include "swift_hal_internal.h"
#pragma once

int swift_eth_setup_mac(const uint8_t *mac) { return -ENOSYS; }
#ifdef __linux__
#include <gpiod.h>
#endif

int swift_eth_tx_register(int (*send)(const uint8_t *, int)) { return -ENOSYS; }

int swift_eth_rx(uint8_t *data, uint16_t len) { return -ENOSYS; }

int swift_eth_event_send(int32_t event_id,
void *event_data,
ssize_t event_data_size,
ssize_t ticks_to_wait) {
return -ENOSYS;
}
// The libgpiod consumer label, as a permanent C string literal (infallible,
// unlike a Swift String or strdup()).
static inline const char *swifthal_gpio__consumer(void) { return "SwiftIO"; }
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@

int swifthal_gpio_get_fd(const void *_Nonnull gpio);

int swifthal_gpio_interrupt_callback_install_block(
void *_Nonnull gpio, void (^_Nonnull)(uint8_t, struct timespec));

///
/// I2C
///
Expand Down
138 changes: 138 additions & 0 deletions Sources/CLinuxHalSwiftIO/include/swift_hal_native.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// Copyright (c) 2026 PADL Software Pty Ltd
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an 'AS IS' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// C helpers for the native-Swift HAL implementation (Sources/LinuxHalSwiftIO/).
//
// Two jobs:
//
// 1. Expose system headers whose declarations the Swift code needs but which
// the Glibc module map does not surface (mqueue, sys/random, sys/sysinfo,
// semaphore, pthread, sched). Once included here they become visible to
// Swift via `import CLinuxHalSwiftIO`.
//
// 2. Wrap the two things Swift's C interop cannot call directly:
// - function-like ioctl request-number macros (SPI_IOC_MESSAGE(n), the
// _IOR/_IOW-expanding SPI_IOC_* constants);
// - the variadic mq_open(), whose trailing attr pointer Swift's importer
// turns into an un-passable `Any`/CVarArg.
// (Plain object-like constants such as SPI_CPOL and `struct
// spi_ioc_transfer` import into Swift natively and need no wrapper.)

#pragma once

#include <inttypes.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <syslog.h>

// syslog() is variadic and thus not callable from Swift; wrap a preformatted
// message. Callers do their own interpolation and pass a plain string.
static inline void swifthal__syslog(int priority, const char *msg) {
syslog(priority, "%s", msg);
}

#ifdef __linux__
#include <sys/ioctl.h>
#include <sys/random.h>
#include <sys/sysinfo.h>
#include <mqueue.h>
#include <linux/spi/spidev.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <dirent.h>
#include <sys/vfs.h>
#endif

#ifdef __linux__

///
/// SPI mode bits: the SPI_* macros expand via _BITUL(), which Swift's importer
/// reports as "structure not supported", so re-expose them as typed constants.
///

static const uint8_t swifthal_spi__cpha = SPI_CPHA;
static const uint8_t swifthal_spi__cpol = SPI_CPOL;
static const uint8_t swifthal_spi__loop = SPI_LOOP;
static const uint8_t swifthal_spi__lsb_first = SPI_LSB_FIRST;

///
/// Message queue (variadic mq_open cannot be called from Swift)
///

static inline mqd_t swifthal_os__mq_open(const char *name,
int oflag,
mode_t mode,
struct mq_attr *attr) {
return mq_open(name, oflag, mode, attr);
}

///
/// SPI ioctls (request numbers are function-like macros)
///

static inline int swifthal_spi__rd_max_speed(int fd, uint32_t *speed) {
return ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, speed);
}

static inline int swifthal_spi__wr_max_speed(int fd, const uint32_t *speed) {
return ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, speed);
}

static inline int swifthal_spi__rd_mode(int fd, uint8_t *mode) {
return ioctl(fd, SPI_IOC_RD_MODE, mode);
}

static inline int swifthal_spi__wr_mode(int fd, const uint8_t *mode) {
return ioctl(fd, SPI_IOC_WR_MODE, mode);
}

static inline int swifthal_spi__rd_lsb_first(int fd, uint8_t *lsb) {
return ioctl(fd, SPI_IOC_RD_LSB_FIRST, lsb);
}

static inline int swifthal_spi__rd_bits_per_word(int fd, uint8_t *bits) {
return ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, bits);
}

static inline int swifthal_spi__wr_bits_per_word(int fd, const uint8_t *bits) {
return ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, bits);
}

// Issue a two-transfer full-duplex SPI message.
static inline int swifthal_spi__message2(int fd,
struct spi_ioc_transfer *xfer) {
return ioctl(fd, SPI_IOC_MESSAGE(2), xfer);
}

///
/// I2C ioctls (I2C_SLAVE takes a scalar; I2C_RDWR takes a pointer — neither is
/// callable through Swift's variadic ioctl import).
///

static inline int swifthal_i2c__set_slave(int fd, unsigned long addr) {
return ioctl(fd, I2C_SLAVE, addr);
}

static inline int swifthal_i2c__rdwr(int fd,
struct i2c_rdwr_ioctl_data *data) {
return ioctl(fd, I2C_RDWR, data);
}

#endif // __linux__
34 changes: 34 additions & 0 deletions Sources/CLinuxHalSwiftIO/include/swift_uart_native.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Copyright (c) 2026 PADL Software Pty Ltd
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an 'AS IS' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Plain prototypes for the UART termios2 helpers implemented in
// swift_uart_native.c. Deliberately does NOT include <asm/termbits.h> so it can
// be part of the module Swift imports without colliding with Glibc's
// <termios.h> (both define `struct termios`).

#pragma once

#include <inttypes.h>

int swifthal_uart__set_baud(int fd, uint32_t baud);
int swifthal_uart__set_parity(int fd, int parity); // 0 none, 1 odd, 2 even
int swifthal_uart__set_stop_bits(int fd, int two);
int swifthal_uart__set_data_bits_8(int fd);
int swifthal_uart__set_raw(int fd);
int swifthal_uart__get_config(int fd,
uint32_t *baud,
int *parity,
int *stop2);
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,8 @@
// limitations under the License.
//

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
// The filesystem HAL is implemented in Swift (FS.swift); this one entry stays
// in C because it must return a permanent C string. A string literal here is
// infallible, whereas a Swift String has no stable char* and strdup() can fail.

#include "swift_hal_internal.h"

void *swifthal_adc_open(int id) { return NULL; }

int swifthal_adc_close(void *adc) { return -ENOSYS; }

int swifthal_adc_read(void *adc, uint16_t *sample_buffer) { return -ENOSYS; }

int swifthal_adc_info_get(void *adc, swift_adc_info_t *info) { return -ENOSYS; }

int swifthal_adc_dev_number_get(void) { return 0; }
char *swifthal_mount_point_get(void) { return "/"; }
Loading
Loading