diff --git a/arch/at91sam3x8e/serial.c b/arch/at91sam3x8e/serial.c index 84aaa96..aac280a 100644 --- a/arch/at91sam3x8e/serial.c +++ b/arch/at91sam3x8e/serial.c @@ -127,8 +127,8 @@ void irq_uart(void) /* 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 */ - dmabuf_put(arch_serial_default_device.tx_current); + if (arch_serial_default_device.tx_current != NULL) + dmabuf_put(arch_serial_default_device.tx_current); /* DMA automatically does this to the actual hardware registers */ arch_serial_default_device.tx_current = arch_serial_default_device.tx_next; diff --git a/include/ardix/device.h b/include/ardix/device.h index 9975834..e4bb825 100644 --- a/include/ardix/device.h +++ b/include/ardix/device.h @@ -16,7 +16,7 @@ struct device { }; /** Cast a kent out to its containing struct device */ -#define to_device(ptr) container_of(ptr, struct device, kent) +#define kent_to_device(ptr) container_of(ptr, struct device, kent) extern struct kent *devices_kent; @@ -27,10 +27,9 @@ 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); +int device_init(struct device *dev); /** Increment a device's reference counter. */ inline void device_get(struct device *dev) diff --git a/include/ardix/dma.h b/include/ardix/dma.h index 14bd48b..0135bd5 100644 --- a/include/ardix/dma.h +++ b/include/ardix/dma.h @@ -13,8 +13,11 @@ struct dmabuf { uint8_t data[0]; }; +#define kent_to_dmabuf(ptr) container_of(ptr, struct dmabuf, kent) + /** * Create a new DMA buffer and its corresponding kent. + * Use `dmabuf_get` and `dmabuf_put` for refcounting. * * @param dev: device to create the buffer for * @param len: buffer length in bytes diff --git a/include/ardix/kent.h b/include/ardix/kent.h index cb2bcb1..3042bcf 100644 --- a/include/ardix/kent.h +++ b/include/ardix/kent.h @@ -25,7 +25,10 @@ struct kent_ops { /** * struct kent: Kernel Entity * - * This is basically a primitive ripoff of the kobject system in Linux. + * This is basically a primitive ripoff of the kobject system in Linux, except + * there is no representation in a virtual filesystem and it is only really used + * to keeping track of hierarchial reference counting. + * * The main purpose of kents is to provide a basic common abstraction layer for * all modules and submodules of the Ardix kernel. kents are arranged in a tree * structure, and use an atomic reference counter to keep track of when it is @@ -42,13 +45,23 @@ struct kent { extern struct kent *kent_root; /** - * Initialize a kent and increment its refcounter by one. + * Initialize to root kent. + * This should probably be called before bootstrapping anything else hooking + * into the kent hierarchy i guess. * - * @param kent: The kent. - * @param parent: The parent kent. - * @returns A nonzero value on failure + * @returns a nonzero value on failure */ -int kent_init(struct kent *kent, struct kent *parent); +int kent_root_init(void); + +/** + * Initialize a kent and set its refcount to one. + * This will fail unless both the operations and parent members are initialized. + * The parent refcount is incremented. + * + * @param kent: the kent + * @returns a nonzero value on failure + */ +int kent_init(struct kent *kent); /** * Increment the reference counter. @@ -60,7 +73,7 @@ void kent_get(struct kent *kent); /** * Decrement the reference counter. * If it reaches zero, the kent is destroyed by invoking the respective callback - * in the operations field. + * in the operations field and the parent reference counter is also decremented. * * @param kent: The kent. */ diff --git a/kernel/device.c b/kernel/device.c index aa22b4e..724360c 100644 --- a/kernel/device.c +++ b/kernel/device.c @@ -12,14 +12,14 @@ struct kent *devices_kent = NULL; -static void device_destroy(struct kent *kent) +static void devices_destroy(struct kent *kent) { - struct device *dev = to_device(kent); - free(dev); + /* should never be executed because the root devices kent is immortal */ + free(kent); } struct kent_ops devices_kent_ops = { - .destroy = &device_destroy, + .destroy = &devices_destroy, }; /** Initialize the devices subsystem. */ @@ -32,18 +32,33 @@ int devices_init(void) if (devices_kent == NULL) return -ENOMEM; - /* we don't need that because the root device kent lives forever */ - devices_kent->operations = NULL; + devices_kent->parent = kent_root; + devices_kent->operations = &devices_kent_ops; - return kent_init(devices_kent, NULL); + return kent_init(devices_kent); } -int device_init(struct device *dev, struct device *parent) +static void device_destroy(struct kent *kent) +{ + struct device *dev = kent_to_device(kent); + free(dev); +} + +struct kent_ops device_kent_ops = { + .destroy = &device_destroy, +}; + +int device_init(struct device *dev) { if (devices_kent == NULL) return -ENOENT; - return kent_init(&dev->kent, devices_kent); + if (dev->kent.operations == NULL) + dev->kent.operations = &device_kent_ops; + if (dev->kent.parent == NULL) + dev->kent.parent = devices_kent; + + return kent_init(&dev->kent); } /* diff --git a/kernel/dma.c b/kernel/dma.c index 0866e5f..45644b3 100644 --- a/kernel/dma.c +++ b/kernel/dma.c @@ -12,11 +12,11 @@ static void dmabuf_destroy(struct kent *kent) { - struct dmabuf *buf = container_of(kent, struct dmabuf, kent); + struct dmabuf *buf = kent_to_dmabuf(kent); free(buf); } -static struct kent_ops dma_kent_ops = { +static struct kent_ops dmabuf_kent_ops = { .destroy = &dmabuf_destroy, }; @@ -27,10 +27,11 @@ struct dmabuf *dmabuf_create(struct device *dev, size_t len) if (buf == NULL) return NULL; - buf->kent.operations = &dma_kent_ops; + buf->kent.parent = &dev->kent; + buf->kent.operations = &dmabuf_kent_ops; - err = kent_init(&buf->kent, &dev->kent); - if (err != 0) { + err = kent_init(&buf->kent); + if (err) { free(buf); return NULL; } diff --git a/kernel/kent.c b/kernel/kent.c index e408cbd..be0c1c8 100644 --- a/kernel/kent.c +++ b/kernel/kent.c @@ -2,33 +2,57 @@ /* See the end of this file for copyright, licensing, and warranty information. */ #include +#include #include #include #include #include -struct kent *kent_root; +struct kent *kent_root = NULL; -int kent_init(struct kent *kent, struct kent *parent) +static void kent_root_destroy(struct kent *kent) { - int ret = 0; + /* + * this callback should never actually be executed in the first place + * because the kent root lives as long as the kernel is running but hey, + * it's not like our flash memory has a size limit or anything :) + */ + free(kent); + kent_root = NULL; +} - if (kent->operations == NULL) - return -EFAULT; - if (kent->operations->destroy == NULL) +static struct kent_ops kent_root_ops = { + .destroy = &kent_root_destroy, +}; + +int kent_root_init(void) +{ + if (kent_root != NULL) + return -EEXIST; + + kent_root = malloc(sizeof(*kent_root)); + if (kent_root == NULL) + return -ENOMEM; + + kent_root->parent = NULL; + kent_root->operations = &kent_root_ops; + atom_init(&kent_root->refcount); + kent_get(kent_root); + + return 0; +} + +int kent_init(struct kent *kent) +{ + if (kent->parent == NULL || kent->operations == NULL) return -EFAULT; + kent_get(kent->parent); atom_init(&kent->refcount); kent_get(kent); - if (parent == NULL) - parent = kent_root; - - kent_get(parent); - kent->parent = parent; - - return ret; + return 0; } void kent_get(struct kent *kent) @@ -38,16 +62,13 @@ void kent_get(struct kent *kent) void kent_put(struct kent *kent) { - struct kent *parent; - - while (kent != NULL) { - parent = kent->parent; - - if (atom_put(&kent->refcount) != 0) - break; + struct kent *parent = kent->parent; + if (atom_put(&kent->refcount) == 0) { kent->operations->destroy(kent); - kent = parent; + + if (parent != NULL) + kent_put(parent); } } diff --git a/kernel/serial.c b/kernel/serial.c index 697d649..c07f0cc 100644 --- a/kernel/serial.c +++ b/kernel/serial.c @@ -18,7 +18,7 @@ int serial_init(struct serial_device *dev, long int baud) if (dev->id < 0) return -1; /* invalid dev */ - err = device_init(&dev->device, NULL); + err = device_init(&dev->device); dev->baud = baud;