| name | embedded-c-patterns |
| description | C coding patterns and best practices for BeagleBone BSP. Covers kernel driver patterns, FreeRTOS idioms, interrupt safety, MMIO access, DMA patterns, and Yocto recipe writing. |
| origin | custom-bsp |
Embedded C Patterns — BeagleBone BSP
Reference patterns for writing correct, safe embedded C code in this project.
Kernel Driver Patterns
Resource-Managed Allocation (devm_*)
struct my_dev *dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
void __iomem *base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
int irq = devm_request_irq(&pdev->dev, irq_num, my_irq_handler,
IRQF_SHARED, "my-driver", dev);
struct my_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
Error Path Pattern (goto cleanup)
static int my_probe(struct platform_device *pdev)
{
struct my_dev *dev;
int ret;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->clk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(dev->clk)) {
ret = PTR_ERR(dev->clk);
goto err_clk;
}
ret = clk_prepare_enable(dev->clk);
if (ret)
goto err_clk;
platform_set_drvdata(pdev, dev);
return 0;
err_clk:
dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
return ret;
}
MMIO Register Access
u32 val = ioread32(dev->base + REG_CTRL);
iowrite32(val | BIT(0), dev->base + REG_CTRL);
#define CTRL_ENABLE BIT(0)
#define CTRL_MODE_MASK GENMASK(3, 1)
#define CTRL_MODE(x) (((x) << 1) & CTRL_MODE_MASK)
*(volatile u32 *)(dev->base + REG_CTRL) = val;
Spinlock for IRQ-shared data
struct my_dev {
spinlock_t lock;
u32 shared_data;
};
static void update_data(struct my_dev *dev, u32 val)
{
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
dev->shared_data = val;
spin_unlock_irqrestore(&dev->lock, flags);
}
static irqreturn_t my_irq_handler(int irq, void *data)
{
struct my_dev *dev = data;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
spin_unlock_irqrestore(&dev->lock, flags);
return IRQ_HANDLED;
}
Device Tree Matching
static const struct of_device_id my_of_match[] = {
{ .compatible = "mycompany,my-device-v1", .data = &my_v1_data },
{ .compatible = "mycompany,my-device-v2", .data = &my_v2_data },
{ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
static int my_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
match = of_match_device(my_of_match, &pdev->dev);
if (!match)
return -ENODEV;
}
FreeRTOS Patterns
Task with static stack (required by coding-standards)
#define MY_TASK_STACK_SIZE (configMINIMAL_STACK_SIZE * 4)
#define MY_TASK_PRIORITY (tskIDLE_PRIORITY + 2)
static StaticTask_t my_task_tcb;
static StackType_t my_task_stack[MY_TASK_STACK_SIZE];
static TaskHandle_t my_task_handle = NULL;
void my_task(void *pvParameters)
{
my_task_config_t *cfg = (my_task_config_t *)pvParameters;
for (;;) {
if (xSemaphoreTake(cfg->sem, pdMS_TO_TICKS(1000)) == pdTRUE) {
} else {
}
}
vTaskDelete(NULL);
}
my_task_handle = xTaskCreateStatic(my_task, "MyTask", MY_TASK_STACK_SIZE,
&task_config, MY_TASK_PRIORITY,
my_task_stack, &my_task_tcb);
Queue from ISR (interrupt-safe)
static void my_hw_irq_handler(void)
{
BaseType_t higher_prio_task_woken = pdFALSE;
uint32_t event = read_hw_event();
xQueueSendFromISR(event_queue, &event, &higher_prio_task_woken);
portYIELD_FROM_ISR(higher_prio_task_woken);
}
Yocto Recipe Patterns
Kernel module recipe
SUMMARY = "BeagleBone custom kernel module"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://COPYING;md5=abc123"
inherit module
SRC_URI = "file://Makefile \
file://my_driver.c \
file://my_driver.h \
"
S = "${WORKDIR}"
KERNEL_MODULE_AUTOLOAD += "my_driver"
KERNEL_MODULE_PROBECONF += "my_driver"
module_conf_my_driver = "options my_driver param=1"
Standalone kernel recipe (project pattern)
# meta-bbb/recipes-kernel/linux/linux-yocto-bbb_5.10.bb
# Project uses a standalone .bb recipe (not .bbappend) to pin kernel SRCREV.
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI += "file://0001-add-my-driver.patch \
file://boneblack-custom.config \
file://am335x-boneblack-custom.dts \
"
Common Anti-Patterns to Avoid
irqreturn_t bad_irq(int irq, void *data) {
msleep(10);
mutex_lock(&m);
kmalloc(sz, GFP_KERNEL);
return IRQ_HANDLED;
}
while (!(ioread32(base + STATUS) & READY_BIT));
int timeout = 1000;
while (!(ioread32(base + STATUS) & READY_BIT) && --timeout)
udelay(1);
if (!timeout)
return -ETIMEDOUT;