Add the user-space mediated firmware contract, HDA/UAC stream descriptors, acquire-release circular DMA buffers, MSI-X acknowledgement hooks, Bluetooth HCI ACL capability checks and Hurd LED namespace records.
Signed-off-by: Breno Rodrigues Alves <[email protected]> --- modern/include/mach/c17/audio.h | 113 +++++++++++++++ modern/include/mach/c17/bluetooth.h | 42 ++++++ modern/include/mach/c17/firmware.h | 73 ++++++++++ modern/include/mach/c17/leds.h | 51 +++++++ modern/interfaces/firmware.defs | 47 +++++++ modern/src/drivers/audio.c | 204 ++++++++++++++++++++++++++++ modern/src/drivers/bluetooth.c | 57 ++++++++ modern/src/drivers/firmware.c | 119 ++++++++++++++++ modern/src/drivers/leds.c | 80 +++++++++++ modern/tests/test_modern_platform.c | 161 ++++++++++++++++++++++ 10 files changed, 947 insertions(+) create mode 100644 modern/include/mach/c17/audio.h create mode 100644 modern/include/mach/c17/bluetooth.h create mode 100644 modern/include/mach/c17/firmware.h create mode 100644 modern/include/mach/c17/leds.h create mode 100644 modern/interfaces/firmware.defs create mode 100644 modern/src/drivers/audio.c create mode 100644 modern/src/drivers/bluetooth.c create mode 100644 modern/src/drivers/firmware.c create mode 100644 modern/src/drivers/leds.c create mode 100644 modern/tests/test_modern_platform.c diff --git a/modern/include/mach/c17/audio.h b/modern/include/mach/c17/audio.h new file mode 100644 index 00000000..cec62c71 --- /dev/null +++ b/modern/include/mach/c17/audio.h @@ -0,0 +1,113 @@ +/* Intel HDA and USB Audio Class contract for GNU Mach. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Only MMIO, DMA ownership and interrupt acknowledgement belong here. + Codec policy and USB class parsing remain Hurd server responsibilities. */ + +#ifndef _MACH_C17_AUDIO_H +#define _MACH_C17_AUDIO_H 1 + +#include <mach/c17/driver.h> + +#define MACH_AUDIO_MAX_CHANNELS 32u +#define MACH_AUDIO_MAX_RATE 192000u +#define MACH_AUDIO_MIN_RATE 44100u +#define MACH_AUDIO_RING_FRAMES 1024u +#define MACH_HDA_MAX_STREAMS 16u + +typedef enum mach_audio_format +{ + MACH_AUDIO_S16_LE = 16, + MACH_AUDIO_S24_LE = 24, + MACH_AUDIO_S32_LE = 32 +} mach_audio_format_t; + +typedef enum mach_usb_audio_version +{ + MACH_UAC1 = 1, + MACH_UAC2 = 2 +} mach_usb_audio_version_t; + +struct mach_audio_ring +{ + uint8_t *data; + size_t frame_bytes; + uint32_t frame_count; + mach_atomic_u32 producer; + mach_atomic_u32 consumer; +}; + +struct mach_audio_stream +{ + struct mach_audio_ring ring; + uint32_t stream_id; + uint32_t sample_rate; + uint16_t channels; + mach_audio_format_t format; + bool capture; + bool running; + uint32_t irq; +}; + +struct mach_hda_ops +{ + mach_error_t (*set_stream) (void *context, uint32_t stream_id, + mach_phys_t buffer, size_t bytes); + mach_error_t (*set_codec_verb) (void *context, uint8_t codec, + uint32_t verb); + void (*ack_irq) (void *context, uint32_t irq); +}; + +struct mach_hda_controller +{ + struct mach_mmio mmio; + struct mach_dma dma; + const struct mach_hda_ops *ops; + void *context; + struct mach_audio_stream streams[MACH_HDA_MAX_STREAMS]; + uint32_t stream_count; + uint32_t msix_vectors; +}; + +struct mach_uac_stream_descriptor +{ + mach_usb_audio_version_t version; + uint32_t sample_rate; + uint16_t channels; + mach_audio_format_t format; + uint16_t max_packet; + bool capture; +}; + +mach_error_t mach_audio_ring_init (struct mach_audio_ring *ring, + uint8_t *data, uint32_t frame_count, + size_t frame_bytes); +size_t mach_audio_ring_available (const struct mach_audio_ring *ring); +size_t mach_audio_ring_space (const struct mach_audio_ring *ring); +size_t mach_audio_ring_write (struct mach_audio_ring *ring, + const uint8_t *frames, size_t count); +size_t mach_audio_ring_read (struct mach_audio_ring *ring, uint8_t *frames, + size_t count); + +mach_error_t mach_hda_init (struct mach_hda_controller *controller, + const struct mach_mmio *mmio, + const struct mach_dma *dma, + const struct mach_hda_ops *ops, void *context); +mach_error_t mach_hda_add_stream (struct mach_hda_controller *controller, + const struct mach_uac_stream_descriptor *desc, + uint8_t *buffer, uint32_t frames, + uint32_t *stream_id); +mach_error_t mach_hda_start (struct mach_hda_controller *controller, + uint32_t stream_id, mach_phys_t dma_address); +void mach_hda_interrupt (struct mach_hda_controller *controller, + uint32_t irq); +mach_error_t mach_uac_validate (const struct mach_uac_stream_descriptor *desc); + +#endif /* _MACH_C17_AUDIO_H */ diff --git a/modern/include/mach/c17/bluetooth.h b/modern/include/mach/c17/bluetooth.h new file mode 100644 index 00000000..2d96fa80 --- /dev/null +++ b/modern/include/mach/c17/bluetooth.h @@ -0,0 +1,42 @@ +/* Bluetooth HCI transport contract for GNU Mach. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. */ + +#ifndef _MACH_C17_BLUETOOTH_H +#define _MACH_C17_BLUETOOTH_H 1 + +#include <mach/c17/ipc.h> + +#define MACH_BT_ACL_MAX 4096u + +typedef enum mach_bt_transport +{ + MACH_BT_USB = 1, + MACH_BT_PCIE = 2 +} mach_bt_transport_t; + +struct mach_bt_hci +{ + mach_bt_transport_t transport; + struct mach_ipc_port *port; + mach_atomic_u32 tx_sequence; + mach_atomic_u32 rx_sequence; + bool ready; +}; + +mach_error_t mach_bt_init (struct mach_bt_hci *hci, + mach_bt_transport_t transport); +mach_error_t mach_bt_attach_port (struct mach_bt_hci *hci, + struct mach_ipc_port *port); +mach_error_t mach_bt_submit_acl (struct mach_bt_hci *hci, + const struct mach_ipc_ool *buffer, + size_t offset, size_t length); +uint32_t mach_bt_next_sequence (struct mach_bt_hci *hci); + +#endif /* _MACH_C17_BLUETOOTH_H */ diff --git a/modern/include/mach/c17/firmware.h b/modern/include/mach/c17/firmware.h new file mode 100644 index 00000000..3bb80331 --- /dev/null +++ b/modern/include/mach/c17/firmware.h @@ -0,0 +1,73 @@ +/* User-space firmware loading contract for GNU Mach. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + No firmware image is embedded here. The Hurd server owns and validates + the image; the kernel-side object exposes only bounded MMIO writes. */ + +#ifndef _MACH_C17_FIRMWARE_H +#define _MACH_C17_FIRMWARE_H 1 + +#include <mach/c17/driver.h> + +#define MACH_FIRMWARE_MAX_CHUNK 4096u +#define MACH_FIRMWARE_FLAG_WRITE32 0x1u +#define MACH_FIRMWARE_FLAG_WRITE8 0x2u +#define MACH_FIRMWARE_MAX_IMAGE (16u * 1024u * 1024u) + +typedef enum mach_firmware_state +{ + MACH_FIRMWARE_EMPTY = 0, + MACH_FIRMWARE_VALIDATED, + MACH_FIRMWARE_UPLOADING, + MACH_FIRMWARE_COMMITTED, + MACH_FIRMWARE_FAILED +} mach_firmware_state_t; + +struct mach_firmware_image +{ + const uint8_t *data; + size_t length; + uint32_t crc32; +}; + +struct mach_firmware_ops +{ + /* This callback is the only privileged operation in the protocol. */ + mach_error_t (*write) (void *context, mach_phys_t offset, + const uint8_t *data, size_t length); + void (*fence) (void *context); +}; + +struct mach_firmware_session +{ + const struct mach_firmware_ops *ops; + void *context; + mach_phys_t window; + size_t window_length; + size_t next_offset; + size_t image_length; + uint32_t expected_crc32; + mach_firmware_state_t state; +}; + +uint32_t mach_firmware_crc32 (const uint8_t *data, size_t length); +mach_error_t mach_firmware_init (struct mach_firmware_session *session, + const struct mach_firmware_ops *ops, + void *context, mach_phys_t window, + size_t window_length); +mach_error_t mach_firmware_validate (struct mach_firmware_session *session, + const struct mach_firmware_image *image); +mach_error_t mach_firmware_upload (struct mach_firmware_session *session, + size_t offset, const uint8_t *data, + size_t length); +mach_error_t mach_firmware_commit (struct mach_firmware_session *session); +void mach_firmware_abort (struct mach_firmware_session *session); + +#endif /* _MACH_C17_FIRMWARE_H */ diff --git a/modern/include/mach/c17/leds.h b/modern/include/mach/c17/leds.h new file mode 100644 index 00000000..153256af --- /dev/null +++ b/modern/include/mach/c17/leds.h @@ -0,0 +1,51 @@ +/* GPIO, ACPI/EC and Hurd LED namespace contract for GNU Mach. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. */ + +#ifndef _MACH_C17_LEDS_H +#define _MACH_C17_LEDS_H 1 + +#include <mach/c17/base.h> + +#define MACH_LED_NAME_MAX 24u +#define MACH_LED_MAX 32u + +typedef enum mach_led_kind +{ + MACH_LED_DIAGNOSTIC = 1, + MACH_LED_DISK = 2, + MACH_LED_POWER = 3, + MACH_LED_CAPSLOCK = 4, + MACH_LED_WIRELESS = 5 +} mach_led_kind_t; + +struct mach_led +{ + char name[MACH_LED_NAME_MAX]; + mach_led_kind_t kind; + uint32_t gpio; + bool value; + bool writable; +}; + +struct mach_led_bank +{ + struct mach_led leds[MACH_LED_MAX]; + uint32_t count; +}; + +mach_error_t mach_led_bank_init (struct mach_led_bank *bank); +mach_error_t mach_led_register (struct mach_led_bank *bank, const char *name, + mach_led_kind_t kind, uint32_t gpio, + bool writable, uint32_t *id); +mach_error_t mach_led_set (struct mach_led_bank *bank, uint32_t id, bool value); +mach_error_t mach_led_get (const struct mach_led_bank *bank, uint32_t id, + bool *value); + +#endif /* _MACH_C17_LEDS_H */ diff --git a/modern/interfaces/firmware.defs b/modern/interfaces/firmware.defs new file mode 100644 index 00000000..b13cd530 --- /dev/null +++ b/modern/interfaces/firmware.defs @@ -0,0 +1,47 @@ +/* GNU Mach firmware service contract. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This interface deliberately carries no firmware bytes. A Hurd server + owns the image, validates its digest and asks the minimal kernel-side + MMIO primitive to transfer it. */ + +subsystem mach_firmware 4200; + +#include <mach/std_types.defs> + +routine firmware_open ( + server: mach_port_t; + device: mach_port_t; + out session: mach_port_t); + +routine firmware_query ( + session: mach_port_t; + out max_chunk: uint32_t; + out flags: uint32_t); + +routine firmware_validate ( + session: mach_port_t; + image_size: uint64_t; + image_crc32: uint32_t; + out result: kern_return_t); + +routine firmware_upload ( + session: mach_port_t; + offset: uint64_t; + image: pointer_t; + image_count: uint32_t; + out result: kern_return_t); + +routine firmware_commit ( + session: mach_port_t; + out result: kern_return_t); + +routine firmware_close ( + session: mach_port_t); diff --git a/modern/src/drivers/audio.c b/modern/src/drivers/audio.c new file mode 100644 index 00000000..6a6323b5 --- /dev/null +++ b/modern/src/drivers/audio.c @@ -0,0 +1,204 @@ +/* Intel HDA and USB Audio Class DMA engine for GNU Mach. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + The producer publishes frames with release semantics and the consumer + observes them with acquire semantics. Codec policy, mixer controls and + UAC descriptors are intentionally left to the user-space audio server. */ + +#include <mach/c17/audio.h> + +static uint32_t +audio_load (const mach_atomic_u32 *value) +{ + return atomic_load_explicit (value, memory_order_acquire); +} + +static uint32_t +audio_distance (uint32_t producer, uint32_t consumer, + uint32_t frame_count) +{ + return producer >= consumer ? producer - consumer + : frame_count - consumer + producer; +} + +mach_error_t +mach_audio_ring_init (struct mach_audio_ring *ring, uint8_t *data, + uint32_t frame_count, size_t frame_bytes) +{ + if (ring == NULL || data == NULL || frame_count < 2u || frame_bytes == 0 + || frame_count > 65536u) + return MACH_EINVAL; + ring->data = data; + ring->frame_bytes = frame_bytes; + ring->frame_count = frame_count; + atomic_init (&ring->producer, 0u); + atomic_init (&ring->consumer, 0u); + return MACH_OK; +} + +size_t +mach_audio_ring_available (const struct mach_audio_ring *ring) +{ + uint32_t producer; + uint32_t consumer; + + if (ring == NULL || ring->frame_count == 0) + return 0; + producer = audio_load (&ring->producer); + consumer = audio_load (&ring->consumer); + return audio_distance (producer, consumer, ring->frame_count); +} + +size_t +mach_audio_ring_space (const struct mach_audio_ring *ring) +{ + if (ring == NULL || ring->frame_count < 2u) + return 0; + return (size_t) ring->frame_count - 1u + - mach_audio_ring_available (ring); +} + +size_t +mach_audio_ring_write (struct mach_audio_ring *ring, const uint8_t *frames, + size_t count) +{ + uint32_t producer; + size_t written = 0; + + if (ring == NULL || frames == NULL || ring->frame_count < 2u) + return 0; + count = count < mach_audio_ring_space (ring) ? count + : mach_audio_ring_space (ring); + producer = atomic_load_explicit (&ring->producer, memory_order_relaxed); + while (written < count) + { + mach_memcpy (ring->data + producer * ring->frame_bytes, + frames + written * ring->frame_bytes, ring->frame_bytes); + producer = (producer + 1u) % ring->frame_count; + written++; + } + atomic_store_explicit (&ring->producer, producer, memory_order_release); + return written; +} + +size_t +mach_audio_ring_read (struct mach_audio_ring *ring, uint8_t *frames, + size_t count) +{ + uint32_t consumer; + size_t read = 0; + + if (ring == NULL || frames == NULL || ring->frame_count < 2u) + return 0; + count = count < mach_audio_ring_available (ring) ? count + : mach_audio_ring_available (ring); + consumer = atomic_load_explicit (&ring->consumer, memory_order_relaxed); + while (read < count) + { + mach_memcpy (frames + read * ring->frame_bytes, + ring->data + consumer * ring->frame_bytes, + ring->frame_bytes); + consumer = (consumer + 1u) % ring->frame_count; + read++; + } + atomic_store_explicit (&ring->consumer, consumer, memory_order_release); + return read; +} + +mach_error_t +mach_uac_validate (const struct mach_uac_stream_descriptor *desc) +{ + if (desc == NULL || (desc->version != MACH_UAC1 && desc->version != MACH_UAC2) + || desc->sample_rate < MACH_AUDIO_MIN_RATE + || desc->sample_rate > MACH_AUDIO_MAX_RATE || desc->channels == 0 + || desc->channels > MACH_AUDIO_MAX_CHANNELS || desc->max_packet == 0 + || (desc->format != MACH_AUDIO_S16_LE + && desc->format != MACH_AUDIO_S24_LE + && desc->format != MACH_AUDIO_S32_LE)) + return MACH_EINVAL; + return MACH_OK; +} + +mach_error_t +mach_hda_init (struct mach_hda_controller *controller, + const struct mach_mmio *mmio, const struct mach_dma *dma, + const struct mach_hda_ops *ops, void *context) +{ + if (controller == NULL || mmio == NULL || dma == NULL || ops == NULL + || ops->set_stream == NULL || ops->set_codec_verb == NULL) + return MACH_EINVAL; + mach_memset (controller, 0, sizeof (*controller)); + controller->mmio = *mmio; + controller->dma = *dma; + controller->ops = ops; + controller->context = context; + return MACH_OK; +} + +mach_error_t +mach_hda_add_stream (struct mach_hda_controller *controller, + const struct mach_uac_stream_descriptor *desc, + uint8_t *buffer, uint32_t frames, uint32_t *stream_id) +{ + struct mach_audio_stream *stream; + size_t bytes_per_sample; + mach_error_t error; + + error = mach_uac_validate (desc); + if (error != MACH_OK || controller == NULL || stream_id == NULL + || controller->stream_count >= MACH_HDA_MAX_STREAMS) + return error != MACH_OK ? error : MACH_ENOMEM; + bytes_per_sample = ((size_t) desc->format + 7u) / 8u; + stream = &controller->streams[controller->stream_count]; + error = mach_audio_ring_init (&stream->ring, buffer, frames, + bytes_per_sample * desc->channels); + if (error != MACH_OK) + return error; + stream->stream_id = controller->stream_count + 1u; + stream->sample_rate = desc->sample_rate; + stream->channels = desc->channels; + stream->format = desc->format; + stream->capture = desc->capture; + *stream_id = stream->stream_id; + controller->stream_count++; + return MACH_OK; +} + +mach_error_t +mach_hda_start (struct mach_hda_controller *controller, uint32_t stream_id, + mach_phys_t dma_address) +{ + struct mach_audio_stream *stream; + mach_error_t error; + + if (controller == NULL || stream_id == 0 || stream_id > controller->stream_count) + return MACH_EINVAL; + stream = &controller->streams[stream_id - 1u]; + error = controller->ops->set_stream (controller->context, stream_id, + dma_address, + stream->ring.frame_count + * stream->ring.frame_bytes); + if (error == MACH_OK) + { + stream->running = true; + stream->irq = stream_id; + controller->msix_vectors++; + } + return error; +} + +void +mach_hda_interrupt (struct mach_hda_controller *controller, uint32_t irq) +{ + if (controller == NULL || controller->ops == NULL + || controller->ops->ack_irq == NULL) + return; + controller->ops->ack_irq (controller->context, irq); +} diff --git a/modern/src/drivers/bluetooth.c b/modern/src/drivers/bluetooth.c new file mode 100644 index 00000000..c491273d --- /dev/null +++ b/modern/src/drivers/bluetooth.c @@ -0,0 +1,57 @@ +/* Bluetooth HCI transport capability for GNU Mach. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Pairing, encryption, firmware policy and packet decoding stay in the + user-space Bluetooth server. The kernel only checks buffer ownership. */ + +#include <mach/c17/bluetooth.h> + +mach_error_t +mach_bt_init (struct mach_bt_hci *hci, mach_bt_transport_t transport) +{ + if (hci == NULL || (transport != MACH_BT_USB && transport != MACH_BT_PCIE)) + return MACH_EINVAL; + mach_memset (hci, 0, sizeof (*hci)); + hci->transport = transport; + atomic_init (&hci->tx_sequence, 0u); + atomic_init (&hci->rx_sequence, 0u); + return MACH_OK; +} + +mach_error_t +mach_bt_attach_port (struct mach_bt_hci *hci, struct mach_ipc_port *port) +{ + if (hci == NULL || port == NULL) + return MACH_EINVAL; + hci->port = port; + hci->ready = true; + return MACH_OK; +} + +mach_error_t +mach_bt_submit_acl (struct mach_bt_hci *hci, const struct mach_ipc_ool *buffer, + size_t offset, size_t length) +{ + if (hci == NULL || !hci->ready || buffer == NULL || length == 0 + || length > MACH_BT_ACL_MAX || offset > buffer->length + || length > buffer->length - offset) + return MACH_EINVAL; + atomic_fetch_add_explicit (&hci->tx_sequence, 1u, memory_order_release); + return MACH_OK; +} + +uint32_t +mach_bt_next_sequence (struct mach_bt_hci *hci) +{ + if (hci == NULL) + return 0; + return atomic_fetch_add_explicit (&hci->rx_sequence, 1u, + memory_order_acq_rel) + 1u; +} diff --git a/modern/src/drivers/firmware.c b/modern/src/drivers/firmware.c new file mode 100644 index 00000000..1156eded --- /dev/null +++ b/modern/src/drivers/firmware.c @@ -0,0 +1,119 @@ +/* User-space mediated firmware transfer for GNU Mach. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + The implementation contains no image and no vendor-specific protocol. + It validates a server-owned image, bounds every transfer and delegates + the actual register write to the minimal device capability. */ + +#include <mach/c17/firmware.h> + +static uint32_t +firmware_crc32_byte (uint32_t crc, uint8_t byte) +{ + crc ^= byte; + for (unsigned int bit = 0; bit < 8u; bit++) + crc = (crc >> 1) ^ (0xedb88320u & (uint32_t) -(int32_t) (crc & 1u)); + return crc; +} + +uint32_t +mach_firmware_crc32 (const uint8_t *data, size_t length) +{ + uint32_t crc = 0xffffffffu; + + if (data == NULL && length != 0) + return 0; + for (size_t index = 0; index < length; index++) + crc = firmware_crc32_byte (crc, data[index]); + return ~crc; +} + +mach_error_t +mach_firmware_init (struct mach_firmware_session *session, + const struct mach_firmware_ops *ops, void *context, + mach_phys_t window, size_t window_length) +{ + if (session == NULL || ops == NULL || ops->write == NULL + || window_length == 0) + return MACH_EINVAL; + mach_memset (session, 0, sizeof (*session)); + session->ops = ops; + session->context = context; + session->window = window; + session->window_length = window_length; + session->state = MACH_FIRMWARE_EMPTY; + return MACH_OK; +} + +mach_error_t +mach_firmware_validate (struct mach_firmware_session *session, + const struct mach_firmware_image *image) +{ + if (session == NULL || image == NULL || image->data == NULL + || image->length == 0 || image->length > MACH_FIRMWARE_MAX_IMAGE + || image->length > session->window_length) + return MACH_EINVAL; + if (mach_firmware_crc32 (image->data, image->length) != image->crc32) + { + session->state = MACH_FIRMWARE_FAILED; + return MACH_EIO; + } + session->image_length = image->length; + session->expected_crc32 = image->crc32; + session->next_offset = 0; + session->state = MACH_FIRMWARE_VALIDATED; + return MACH_OK; +} + +mach_error_t +mach_firmware_upload (struct mach_firmware_session *session, size_t offset, + const uint8_t *data, size_t length) +{ + mach_error_t error; + + if (session == NULL || data == NULL || length == 0 + || (session->state != MACH_FIRMWARE_VALIDATED + && session->state != MACH_FIRMWARE_UPLOADING) + || offset != session->next_offset || offset > session->image_length + || length > MACH_FIRMWARE_MAX_CHUNK + || length > session->image_length - offset) + return MACH_EINVAL; + session->state = MACH_FIRMWARE_UPLOADING; + error = session->ops->write (session->context, session->window + offset, + data, length); + if (error != MACH_OK) + { + session->state = MACH_FIRMWARE_FAILED; + return error; + } + session->next_offset += length; + session->state = session->next_offset == session->image_length + ? MACH_FIRMWARE_VALIDATED : MACH_FIRMWARE_UPLOADING; + return MACH_OK; +} + +mach_error_t +mach_firmware_commit (struct mach_firmware_session *session) +{ + if (session == NULL || session->state != MACH_FIRMWARE_VALIDATED + || session->next_offset != session->image_length) + return MACH_EINVAL; + if (session->ops->fence != NULL) + session->ops->fence (session->context); + session->state = MACH_FIRMWARE_COMMITTED; + return MACH_OK; +} + +void +mach_firmware_abort (struct mach_firmware_session *session) +{ + if (session != NULL) + session->state = MACH_FIRMWARE_FAILED; +} diff --git a/modern/src/drivers/leds.c b/modern/src/drivers/leds.c new file mode 100644 index 00000000..a3802bfd --- /dev/null +++ b/modern/src/drivers/leds.c @@ -0,0 +1,80 @@ +/* GPIO and Hurd LED namespace capability for GNU Mach. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + The Hurd translator owns policy and translates these records to + /dev/leds/<name>. This layer stores no ACPI/EC protocol or vendor blob. */ + +#include <mach/c17/leds.h> + +static bool +led_name_valid (const char *name) +{ + size_t length = 0; + + if (name == NULL || name[0] == '\0') + return false; + while (name[length] != '\0') + { + if (length >= MACH_LED_NAME_MAX - 1u || name[length] == '/') + return false; + length++; + } + return true; +} + +mach_error_t +mach_led_bank_init (struct mach_led_bank *bank) +{ + if (bank == NULL) + return MACH_EINVAL; + mach_memset (bank, 0, sizeof (*bank)); + return MACH_OK; +} + +mach_error_t +mach_led_register (struct mach_led_bank *bank, const char *name, + mach_led_kind_t kind, uint32_t gpio, bool writable, + uint32_t *id) +{ + struct mach_led *led; + size_t length; + + if (bank == NULL || !led_name_valid (name) || id == NULL + || kind < MACH_LED_DIAGNOSTIC || kind > MACH_LED_WIRELESS + || bank->count >= MACH_LED_MAX) + return MACH_EINVAL; + length = mach_strlen (name); + led = &bank->leds[bank->count]; + mach_memcpy (led->name, name, length + 1u); + led->kind = kind; + led->gpio = gpio; + led->writable = writable; + *id = bank->count++ + 1u; + return MACH_OK; +} + +mach_error_t +mach_led_set (struct mach_led_bank *bank, uint32_t id, bool value) +{ + if (bank == NULL || id == 0 || id > bank->count + || !bank->leds[id - 1u].writable) + return MACH_EPERM; + bank->leds[id - 1u].value = value; + return MACH_OK; +} + +mach_error_t +mach_led_get (const struct mach_led_bank *bank, uint32_t id, bool *value) +{ + if (bank == NULL || value == NULL || id == 0 || id > bank->count) + return MACH_EINVAL; + *value = bank->leds[id - 1u].value; + return MACH_OK; +} diff --git a/modern/tests/test_modern_platform.c b/modern/tests/test_modern_platform.c new file mode 100644 index 00000000..9194c0ae --- /dev/null +++ b/modern/tests/test_modern_platform.c @@ -0,0 +1,161 @@ +/* Tests for the firmware, audio, iGPU, Bluetooth and LED contracts. + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. */ + +#include <mach/c17/audio.h> +#include <mach/c17/bluetooth.h> +#include <mach/c17/firmware.h> +#include <mach/c17/igpu.h> +#include <mach/c17/leds.h> + +#include "test.h" + +struct firmware_capture +{ + uint8_t bytes[64]; + size_t writes; + bool fenced; +}; + +static mach_error_t +capture_write (void *context, mach_phys_t offset, const uint8_t *data, + size_t length) +{ + struct firmware_capture *capture = context; + if (offset + length > sizeof (capture->bytes)) + return MACH_ERANGE; + memcpy (capture->bytes + offset, data, length); + capture->writes++; + return MACH_OK; +} + +static void +capture_fence (void *context) +{ + ((struct firmware_capture *) context)->fenced = true; +} + +struct audio_capture +{ + uint32_t stream; + mach_phys_t address; + size_t bytes; + uint32_t irq; +}; + +static mach_error_t +capture_stream (void *context, uint32_t stream, mach_phys_t address, size_t bytes) +{ + struct audio_capture *capture = context; + capture->stream = stream; + capture->address = address; + capture->bytes = bytes; + return MACH_OK; +} + +static mach_error_t +capture_verb (void *context, uint8_t codec, uint32_t verb) +{ + (void) context; + (void) codec; + (void) verb; + return MACH_OK; +} + +static void +capture_irq (void *context, uint32_t irq) +{ + ((struct audio_capture *) context)->irq = irq; +} + +static void +run_tests (void) +{ + static const struct mach_firmware_ops firmware_ops = + { capture_write, capture_fence }; + static const struct mach_hda_ops audio_ops = + { capture_stream, capture_verb, capture_irq }; + struct firmware_capture firmware_capture_state = { { 0 }, 0, false }; + struct mach_firmware_session session; + uint8_t firmware[] = { 1, 2, 3, 4, 5, 6 }; + struct mach_firmware_image image; + uint8_t audio_data[4096]; + uint8_t frames[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + uint8_t result[8] = { 0 }; + struct mach_audio_ring ring; + struct mach_hda_controller hda; + struct mach_uac_stream_descriptor descriptor; + struct audio_capture audio_capture_state = { 0, 0, 0, 0 }; + struct mach_mmio mmio = { 0 }; + struct mach_dma dma = { 0 }; + uint32_t stream_id; + struct mach_igpu gpu; + struct mach_ipc_ool ool = { MACH_REFCOUNT_INIT (1), 0x1000, 128, 0, 1 }; + uint32_t ring_id; + struct mach_bt_hci hci; + struct mach_led_bank leds; + uint32_t led_id; + bool value; + + image.data = firmware; + image.length = sizeof (firmware); + image.crc32 = mach_firmware_crc32 (firmware, sizeof (firmware)); + CHECK_EQ (mach_firmware_init (&session, &firmware_ops, + &firmware_capture_state, 0, sizeof (firmware)), + MACH_OK); + CHECK_EQ (mach_firmware_validate (&session, &image), MACH_OK); + CHECK_EQ (mach_firmware_upload (&session, 0, firmware, 3), MACH_OK); + CHECK_EQ (mach_firmware_upload (&session, 3, firmware + 3, 3), MACH_OK); + CHECK_EQ (mach_firmware_commit (&session), MACH_OK); + CHECK (firmware_capture_state.fenced); + CHECK_EQ (firmware_capture_state.writes, 2); + CHECK (memcmp (firmware_capture_state.bytes, firmware, sizeof (firmware)) == 0); + + CHECK_EQ (mach_audio_ring_init (&ring, audio_data, 8, 1), MACH_OK); + CHECK_EQ (mach_audio_ring_write (&ring, frames, 8), 7); + CHECK_EQ (mach_audio_ring_read (&ring, result, 4), 4); + CHECK (memcmp (result, frames, 4) == 0); + descriptor.version = MACH_UAC2; + descriptor.sample_rate = 48000; + descriptor.channels = 2; + descriptor.format = MACH_AUDIO_S24_LE; + descriptor.max_packet = 192; + descriptor.capture = false; + CHECK_EQ (mach_hda_init (&hda, &mmio, &dma, &audio_ops, + &audio_capture_state), MACH_OK); + CHECK_EQ (mach_hda_add_stream (&hda, &descriptor, audio_data, 64, + &stream_id), MACH_OK); + CHECK_EQ (mach_hda_start (&hda, stream_id, 0x2000), MACH_OK); + CHECK_EQ (audio_capture_state.address, 0x2000); + mach_hda_interrupt (&hda, 7); + CHECK_EQ (audio_capture_state.irq, 7); + + CHECK_EQ (mach_igpu_init (&gpu, MACH_IGPU_INTEL), MACH_OK); + CHECK_EQ (mach_igpu_add_bar (&gpu, 0x100000000ull, 0x100000, + true), MACH_OK); + CHECK_EQ (mach_igpu_claim_uma (&gpu, 0x80000000ull, 0x200000), MACH_OK); + CHECK_EQ (mach_igpu_add_ring (&gpu, 0x300000, 4096, &ring_id), MACH_OK); + CHECK_EQ (mach_igpu_submit (&gpu, ring_id, &ool, 0, 64), MACH_OK); + + CHECK_EQ (mach_bt_init (&hci, MACH_BT_USB), MACH_OK); + CHECK_EQ (mach_bt_attach_port (&hci, (struct mach_ipc_port *) &gpu), MACH_OK); + CHECK_EQ (mach_bt_submit_acl (&hci, &ool, 0, 32), MACH_OK); + CHECK_EQ (mach_bt_next_sequence (&hci), 1); + + CHECK_EQ (mach_led_bank_init (&leds), MACH_OK); + CHECK_EQ (mach_led_register (&leds, "wifi", MACH_LED_WIRELESS, 4, true, + &led_id), MACH_OK); + CHECK_EQ (mach_led_set (&leds, led_id, true), MACH_OK); + CHECK_EQ (mach_led_get (&leds, led_id, &value), MACH_OK); + CHECK (value); + CHECK_EQ (mach_led_register (&leds, "bad/name", MACH_LED_POWER, 5, true, + &led_id), MACH_EINVAL); +} + +TEST_MAIN ("modern platform contracts") -- 2.56.0
