device: add base abstraction for all devices

This commit is contained in:
Felix Kopp 2021-02-03 04:01:27 +01:00
parent 56c76d8b4b
commit 256b49b529
No known key found for this signature in database
GPG key ID: C478BA0A85F75728
14 changed files with 221 additions and 85 deletions

View file

@ -16,20 +16,20 @@
#include <errno.h>
#include <stddef.h>
struct arch_serial_interface arch_serial_default_interface = {
struct arch_serial_device arch_serial_default_device = {
.tx_current = NULL,
.tx_next = NULL,
.interface = {
.device = {
.rx = NULL,
.id = 0,
.baud = 0,
},
};
struct serial_interface *serial_default_interface = &arch_serial_default_interface.interface;
struct serial_device *serial_default_device = &arch_serial_default_device.device;
int arch_serial_init(struct serial_interface *interface)
int arch_serial_init(struct serial_device *dev)
{
if (interface->baud <= 0 || interface->id != 0)
if (dev->baud <= 0 || dev->id != 0)
return -1;
/* enable peripheral clock for UART (which has peripheral id 8) */
@ -49,7 +49,7 @@ int arch_serial_init(struct serial_interface *interface)
REG_UART_MR = REG_UART_MR_PAR_NO | REG_UART_MR_CHMODE_NORMAL;
/* From Atmel Datasheet: baud rate = MCK / (REG_UART_BRGR * 16) */
REG_UART_BRGR = (uint16_t)(( sys_core_clock / (uint32_t)interface->baud ) >> 4);
REG_UART_BRGR = (uint16_t)(( sys_core_clock / (uint32_t)dev->baud ) >> 4);
/* choose the events we want an interrupt on */
REG_UART_IDR = 0xFFFFFFFF; /* make sure all interrupts are disabled first */
@ -65,9 +65,9 @@ int arch_serial_init(struct serial_interface *interface)
return 0;
}
void arch_serial_exit(struct serial_interface *interface)
void arch_serial_exit(struct serial_device *dev)
{
if (interface->id != 0)
if (dev->id != 0)
return;
/* disable receiver and transmitter */
@ -78,15 +78,15 @@ void arch_serial_exit(struct serial_interface *interface)
/* disable peripheral clock for UART (PID is taken from Atmel Datasheet, Section 9.1 */
REG_PMC_PCDR0 = REG_PMC_PCDR0_PID(8);
interface->id = -1;
dev->id = -1;
}
ssize_t arch_serial_write(struct serial_interface *interface, const void *buf, size_t len)
ssize_t arch_serial_write(struct serial_device *dev, const void *buf, size_t len)
{
struct arch_serial_buffer *arch_buf = NULL;
struct arch_serial_interface *arch_iface = to_arch_serial_interface(interface);
struct arch_serial_device *arch_dev = to_arch_serial_device(dev);
if (arch_iface->tx_next != NULL)
if (arch_dev->tx_next != NULL)
return -EBUSY;
if (len >= (1 << 16)) /* DMA uses 16-bit counters */
@ -99,14 +99,14 @@ ssize_t arch_serial_write(struct serial_interface *interface, const void *buf, s
memcpy(&arch_buf->data[0], buf, len);
arch_buf->len = (uint16_t)len;
if (arch_iface->tx_current == NULL) {
arch_iface->tx_current = arch_buf;
if (arch_dev->tx_current == NULL) {
arch_dev->tx_current = arch_buf;
REG_UART_PDC_TPR = (uint32_t)&arch_buf->data[0];
REG_UART_PDC_TCR = arch_buf->len;
/* we weren't transmitting, so the interrupt was masked */
REG_UART_IER = REG_UART_IER_ENDTX_MASK;
} else {
arch_iface->tx_next = arch_buf;
arch_dev->tx_next = arch_buf;
REG_UART_PDC_TNPR = (uint32_t)&arch_buf->data[0];
REG_UART_PDC_TNCR = arch_buf->len;
}
@ -122,19 +122,19 @@ void irq_uart(void)
/* RX has received a byte, store it into the ring buffer */
if (state & REG_UART_SR_RXRDY_MASK) {
tmp = REG_UART_RHR;
ringbuf_write(arch_serial_default_interface.interface.rx, &tmp, sizeof(tmp));
ringbuf_write(arch_serial_default_device.device.rx, &tmp, sizeof(tmp));
}
/* REG_UART_PDC_TCR has reached zero */
if (state & REG_UART_SR_ENDTX_MASK) {
/* this might be NULL but that's ok because free() tolerates that */
free(arch_serial_default_interface.tx_current);
free(arch_serial_default_device.tx_current);
/* DMA automatically does this to the actual hardware registers */
arch_serial_default_interface.tx_current = arch_serial_default_interface.tx_next;
arch_serial_default_interface.tx_next = NULL;
arch_serial_default_device.tx_current = arch_serial_default_device.tx_next;
arch_serial_default_device.tx_next = NULL;
if (arch_serial_default_interface.tx_current == NULL)
if (arch_serial_default_device.tx_current == NULL)
REG_UART_IDR = REG_UART_IDR_ENDTX_MASK;
}

View file

@ -18,23 +18,23 @@ struct arch_serial_buffer {
uint8_t data[];
};
/** Architecture-specific extension of `struct serial_interface` */
struct arch_serial_interface {
/** Architecture-specific extension of `struct serial_device` */
struct arch_serial_device {
/** should always match REG_UART_PDC_TPR */
struct arch_serial_buffer *tx_current;
/** should always match REG_UART_PDC_TNPR */
struct arch_serial_buffer *tx_next;
struct serial_interface interface;
struct serial_device device;
};
/**
* Cast a `struct serial_interface` out to a `struct arch_serial_interface`.
* Cast a `struct serial_device` out to a `struct arch_serialdevice`.
*
* @param ptr: The `struct serial_interface *` to cast out from.
* @returns The containing `struct arch_serial_interface *`.
* @param ptr: The `struct serial_device *` to cast out from.
* @returns The containing `struct arch_serialdevice *`.
*/
#define to_arch_serial_interface(ptr) container_of(ptr, struct arch_serial_interface, interface)
#define to_arch_serial_device(ptr) container_of(ptr, struct arch_serial_device, device)
/*
* Copyright (c) 2020 Felix Kopp <sandtler@sandtler.club>

View file

@ -7,19 +7,19 @@
#include <ardix/serial.h>
int arch_serial_init(struct serial_interface *interface);
void arch_serial_exit(struct serial_interface *interface);
int arch_serial_init(struct serial_device *dev);
void arch_serial_exit(struct serial_device *dev);
/**
* Copy `buf` to a hardware buffer in the TX queue.
* The transmission is performed asynchronously.
*
* @param interface: serial interface to enqueue the buffer for
* @param dev: serial device to enqueue the buffer for
* @param buf: raw buffer data
* @param len: length of `buf`
* @returns actual amount of bytes enqueued, or a negative error code on failure
*/
ssize_t arch_serial_write(struct serial_interface *interface, const void *buf, size_t len);
ssize_t arch_serial_write(struct serial_device *dev, const void *buf, size_t len);
#include ARCH_INCLUDE(serial.h)

70
include/ardix/device.h Normal file
View file

@ -0,0 +1,70 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/* See the end of this file for copyright, licensing, and warranty information. */
#pragma once
#include <ardix/kent.h>
#include <ardix/list.h>
#include <ardix/malloc.h>
#include <ardix/types.h>
#include <stddef.h>
/** Top-level abstraction for any device connected to the system. */
struct device {
struct kent kent;
};
/** Cast a kent out to its containing struct device */
#define to_device(ptr) container_of(ptr, struct device, kent)
extern struct kent *devices_kent;
/** Initialize the devices subsystem. */
int devices_init(void);
/**
* Initialize a device and add it to the device tree.
*
* @param dev: device to initialze
* @param parent: parent device (may me `NULL` if unapplicable)
* @returns 0 on success, or a negative error code on failure
*/
int device_init(struct device *dev, struct device *parent);
/** Increment a device's reference counter. */
inline void device_get(struct device *dev)
{
kent_get(&dev->kent);
}
/** Decrement a device's referece counter. */
inline void device_put(struct device *dev)
{
kent_put(&dev->kent);
}
/*
* Copyright (c) 2021 Felix Kopp <sandtler@sandtler.club>
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

View file

@ -3,6 +3,7 @@
#pragma once
#include <ardix/device.h>
#include <ardix/types.h>
#include <ardix/ringbuf.h>
@ -18,50 +19,47 @@
#define SERIAL_BUFSZ 256
#endif
struct serial_interface {
struct serial_device {
struct device device;
struct ringbuf *rx;
long int baud;
int id;
};
/** The default serial console (this is where printk outputs to) */
extern struct serial_interface *serial_default_interface;
extern struct serial_device *serial_default_device;
/**
* Initialize a serial interface.
* Initialize a serial device.
*
* @param interface: The serial interface.
* @param baud: The baud rate (bits/second).
* @param dev: serial device
* @param baud: baud rate (bits/sec)
* @returns 0 on success, a negative number otherwise.
*/
int serial_init(struct serial_interface *interface, long int baud);
int serial_init(struct serial_device *dev, long int baud);
/**
* Flush all buffers (if possible) and close the serial interface.
*
* @param interface: The serial interface.
*/
void serial_exit(struct serial_interface *interface);
/** Flush all buffers (if possible) and close the serial device. */
void serial_exit(struct serial_device *dev);
/**
* Read from the serial buffer.
*
* @param dest: Where to store the received data.
* @param interface: The serial interface to read data from.
* @param len: The maximum amount of bytes to read.
* @returns The actual amount of bytes read.
* @param dest: where to write received data
* @param dev: serial device to read from
* @param len: amount of bytes to read
* @returns actual amount of bytes read
*/
ssize_t serial_read(void *dest, struct serial_interface *interface, size_t len);
ssize_t serial_read(void *dest, struct serial_device *dev, size_t len);
/**
* Write data to the serial buffer.
*
* @param interface: The serial interface to write data to.
* @param data: The data to write.
* @param len: The length of `data`.
* @returns The actual amount of bytes written.
* @param dev: serial device to write to
* @param data: where to read data from
* @param len: amount of bytes to write
* @returns actual amount of bytes written
*/
ssize_t serial_write(struct serial_interface *interface, const void *data, size_t len);
ssize_t serial_write(struct serial_device *dev, const void *data, size_t len);
/*
* Copyright (c) 2020 Felix Kopp <sandtler@sandtler.club>

View file

@ -5,6 +5,8 @@
#include <stddef.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
/**
* Cast a pointer to a member of a struct out to the containing structure.
*

View file

@ -6,10 +6,10 @@
#include <ardix/io.h>
#include <ardix/printk.h>
#include <ardix/sched.h>
#include <ardix/serial.h>
#include <stdint.h>
#include <stddef.h>
#include <unistd.h>
#define REG_PIOB_PER (*(uint32_t *)0x400E1000U)
#define REG_PIOB_PDR (*(uint32_t *)0x400E1004U)
@ -34,9 +34,12 @@ void do_bootstrap(void)
REG_PIOB_CODR = 1 << 27;
sched_init();
devices_init();
io_init();
printk("hello, world\n");
write(1, "hello, world\n", 13);
while (true) {
if (count++ != 1000000)
@ -47,7 +50,6 @@ void do_bootstrap(void)
REG_PIOB_CODR = 1 << 27;
else
REG_PIOB_SODR = 1 << 27;
printk("endless loop iteration #%u\n", print_count);
count = 0;
}

View file

@ -29,6 +29,7 @@ include $(ARDIX_KERNEL_PWD)/fs/Makefile
include $(ARDIX_KERNEL_PWD)/io/Makefile
ARDIX_SOURCES += \
$(ARDIX_KERNEL_PWD)/device.c \
$(ARDIX_KERNEL_PWD)/kent.c \
$(ARDIX_KERNEL_PWD)/printk.c \
$(ARDIX_KERNEL_PWD)/ringbuf.c \

59
kernel/device.c Normal file
View file

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/* See the end of this file for copyright, licensing, and warranty information. */
#include <ardix/device.h>
#include <ardix/kent.h>
#include <ardix/list.h>
#include <ardix/malloc.h>
#include <ardix/types.h>
#include <errno.h>
#include <stddef.h>
struct kent *devices_kent = NULL;
/** Initialize the devices subsystem. */
int devices_init(void)
{
if (devices_kent != NULL)
return -EEXIST;
devices_kent = malloc(sizeof(*devices_kent));
if (devices_kent == NULL)
return -ENOMEM;
return kent_init(NULL, devices_kent);
}
int device_init(struct device *dev, struct device *parent)
{
if (devices_kent == NULL)
return -ENOENT;
return kent_init(devices_kent, &dev->kent);
}
/*
* Copyright (c) 2021 Felix Kopp <sandtler@sandtler.club>
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

View file

@ -22,7 +22,7 @@ ssize_t sys_read(int fd, __user void *buf, size_t len)
if (copy == NULL)
return -ENOMEM;
ret = serial_read(copy, serial_default_interface, len);
ret = serial_read(copy, serial_default_device, len);
if (ret > 0)
copy_to_user(buf, copy, (size_t)ret);

View file

@ -24,7 +24,7 @@ ssize_t sys_write(int fd, __user const void *buf, size_t len)
ret = (ssize_t)copy_from_user(copy, buf, len);
/* TODO: reschedule if blocking */
ret = serial_write(serial_default_interface, copy, (size_t)ret);
ret = serial_write(serial_default_device, copy, (size_t)ret);
free(copy);

View file

@ -8,7 +8,7 @@ int io_init(void)
{
int ret;
ret = serial_init(serial_default_interface, CONFIG_SERIAL_BAUD);
ret = serial_init(serial_default_device, CONFIG_SERIAL_BAUD);
return ret;
}

View file

@ -17,8 +17,8 @@
* TODO: THIS CAUSES A STACK BUFFER OVERFLOW ON SYSTEMS WHERE INT IS 64 BITS
*/
/* 10 decimal digits (2 ** 32 - 1) + ASCII NUL */
#define PRINTK_UINT_BUFSZ 11
/* 10 decimal digits of 4294967295 (2 ** 32 - 1) */
#define PRINTK_UINT_BUFSZ 10
__rodata static const char fmt_hex_table[] = {
'0', '1', '2', '3',
@ -42,7 +42,7 @@ static int fmt_handle_ptr(uintptr_t ptr)
ptr >>= 4;
} while (pos != &buf[2]);
ret = serial_write(serial_default_interface, &buf[0], 2 * sizeof(uintptr_t) + 2);
ret = serial_write(serial_default_device, &buf[0], 2 * sizeof(uintptr_t) + 2);
return ret;
}
@ -59,7 +59,7 @@ static int fmt_handle_uint(unsigned int u)
} while (u != 0);
pos++;
ret = serial_write(serial_default_interface, pos,
ret = serial_write(serial_default_device, pos,
PRINTK_UINT_BUFSZ - ( (size_t)pos - (size_t)&buf[0] ));
return ret;
}
@ -70,7 +70,7 @@ static inline int fmt_handle_int(int i)
char minus = '-';
if (i < 0) {
ret = serial_write(serial_default_interface, &minus, sizeof(minus));
ret = serial_write(serial_default_device, &minus, sizeof(minus));
i = -i;
}
@ -102,12 +102,12 @@ static inline int fmt_handle(const char **pos, va_list args)
switch (**pos) {
case '%': /* literal percent sign */
ret = serial_write(serial_default_interface, *pos, sizeof(**pos));
ret = serial_write(serial_default_device, *pos, sizeof(**pos));
break;
case 'c': /* char */
val.c = va_arg(args, typeof(val.c));
ret = serial_write(serial_default_interface, &val.c, sizeof(val.c));
ret = serial_write(serial_default_device, &val.c, sizeof(val.c));
break;
case 'd': /* int */
@ -123,7 +123,7 @@ static inline int fmt_handle(const char **pos, va_list args)
case 's': /* string */
val.s = va_arg(args, typeof(val.s));
ret = (int)strlen(val.s);
ret = serial_write(serial_default_interface, val.s, (size_t)ret);
ret = serial_write(serial_default_device, val.s, (size_t)ret);
break;
case 'u': /* unsigned int */
@ -149,7 +149,7 @@ int printk(const char *fmt, ...)
while (*tmp != '\0') {
if (*tmp++ == '%') {
/* flush out everything we have so far (minus one char for %) */
ret += (int)serial_write(serial_default_interface, fmt,
ret += (int)serial_write(serial_default_device, fmt,
(size_t)tmp - (size_t)fmt - 1);
tmpret = fmt_handle(&tmp, args);
@ -166,9 +166,10 @@ int printk(const char *fmt, ...)
}
if (tmp != fmt && ret >= 0)
ret += serial_write(serial_default_interface, fmt, (size_t)tmp - (size_t)fmt);
ret += serial_write(serial_default_device, fmt, (size_t)tmp - (size_t)fmt);
va_end(args);
return ret;
}

View file

@ -2,6 +2,7 @@
/* See the end of this file for copyright, licensing, and warranty information. */
#include <ardix/atomic.h>
#include <ardix/device.h>
#include <ardix/ringbuf.h>
#include <ardix/sched.h>
#include <ardix/serial.h>
@ -10,50 +11,52 @@
#include <stddef.h>
int serial_init(struct serial_interface *interface, long int baud)
int serial_init(struct serial_device *dev, long int baud)
{
int err = -1;
if (interface->id < 0)
return -1; /* invalid interface */
if (dev->id < 0)
return -1; /* invalid dev */
interface->baud = baud;
err = device_init(&dev->device, NULL);
interface->rx = ringbuf_create(SERIAL_BUFSZ);
if (interface->rx == NULL)
dev->baud = baud;
dev->rx = ringbuf_create(SERIAL_BUFSZ);
if (dev->rx == NULL)
return -1;
err = arch_serial_init(interface);
err = arch_serial_init(dev);
if (err)
ringbuf_destroy(interface->rx);
ringbuf_destroy(dev->rx);
return err;
}
void serial_exit(struct serial_interface *interface)
void serial_exit(struct serial_device *dev)
{
arch_serial_exit(interface);
ringbuf_destroy(interface->rx);
interface->id = -1;
arch_serial_exit(dev);
ringbuf_destroy(dev->rx);
dev->id = -1;
}
ssize_t serial_read(void *dest, struct serial_interface *interface, size_t len)
ssize_t serial_read(void *dest, struct serial_device *dev, size_t len)
{
ssize_t ret;
atomic_enter();
ret = (ssize_t)ringbuf_read(dest, interface->rx, len);
ret = (ssize_t)ringbuf_read(dest, dev->rx, len);
atomic_leave();
return ret;
}
ssize_t serial_write(struct serial_interface *interface, const void *data, size_t len)
ssize_t serial_write(struct serial_device *dev, const void *data, size_t len)
{
ssize_t ret;
atomic_enter();
ret = arch_serial_write(interface, data, len);
ret = arch_serial_write(dev, data, len);
atomic_leave();
return ret;